fix(cef): debounce selection updates and drop old bridge code
- commit
- 521444082436df743bc7ca30d0392726fa19a533
- author
- brice · · 2026-04-17 15:24 UTC
- parent
- 2a3f82d
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +1 -130
M internal/application/usecase/clipboard_text_orchestrator.go +0 -25
| @@ -9,7 +9,6 @@ import ( | |||
| 9 | 9 | "unicode/utf8" | |
| 10 | 10 | ||
| 11 | 11 | "github.com/bnema/dumber/internal/application/port" | |
| 12 | - | "github.com/bnema/dumber/internal/logging" | |
| 13 | 12 | ) | |
| 14 | 13 | ||
| 15 | 14 | const explicitClipboardDedupWindow = 250 * time.Millisecond | |
| @@ -109,11 +108,6 @@ func (uc *ClipboardTextOrchestratorUseCase) HandleSelectionUpdate( | |||
| 109 | 108 | } | |
| 110 | 109 | if err := uc.clipboard.WriteText(ctx, input.Text); err != nil { | |
| 111 | 110 | uc.mu.Unlock() | |
| 112 | - | logging.FromContext(ctx).Debug(). | |
| 113 | - | Err(err). | |
| 114 | - | Int("text_len", textLen). | |
| 115 | - | Str("source_engine", string(input.SourceEngine)). | |
| 116 | - | Msg("clipboard selection write failed") | |
| 117 | 111 | return fmt.Errorf("clipboard write failed: %w", err) | |
| 118 | 112 | } | |
| 119 | 113 | ||
| @@ -131,10 +125,6 @@ func (uc *ClipboardTextOrchestratorUseCase) HandleSelectionUpdate( | |||
| 131 | 125 | if uc.toast != nil { | |
| 132 | 126 | uc.toast(textLen) | |
| 133 | 127 | } | |
| 134 | - | logging.FromContext(ctx).Debug(). | |
| 135 | - | Int("text_len", textLen). | |
| 136 | - | Str("source_engine", string(input.SourceEngine)). | |
| 137 | - | Msg("clipboard selection copied") | |
| 138 | 128 | return nil | |
| 139 | 129 | } | |
| 140 | 130 | ||
| @@ -172,12 +162,6 @@ func (uc *ClipboardTextOrchestratorUseCase) HandleExplicitCopy( | |||
| 172 | 162 | if !input.NativeHandled { | |
| 173 | 163 | if err := uc.clipboard.WriteText(ctx, input.Text); err != nil { | |
| 174 | 164 | uc.mu.Unlock() | |
| 175 | - | logging.FromContext(ctx).Debug(). | |
| 176 | - | Err(err). | |
| 177 | - | Int("text_len", textLen). | |
| 178 | - | Str("source_engine", string(input.SourceEngine)). | |
| 179 | - | Str("action", input.Action). | |
| 180 | - | Msg("clipboard explicit write failed") | |
| 181 | 165 | return fmt.Errorf("clipboard write failed: %w", err) | |
| 182 | 166 | } | |
| 183 | 167 | } | |
| @@ -188,15 +172,6 @@ func (uc *ClipboardTextOrchestratorUseCase) HandleExplicitCopy( | |||
| 188 | 172 | if uc.toast != nil { | |
| 189 | 173 | uc.toast(textLen) | |
| 190 | 174 | } | |
| 191 | - | logEntry := logging.FromContext(ctx).Debug(). | |
| 192 | - | Int("text_len", textLen). | |
| 193 | - | Str("source_engine", string(input.SourceEngine)). | |
| 194 | - | Str("action", input.Action) | |
| 195 | - | if input.NativeHandled { | |
| 196 | - | logEntry.Bool("native_handled", true).Msg("clipboard explicit copied (native handled)") | |
| 197 | - | return nil | |
| 198 | - | } | |
| 199 | - | logEntry.Msg("clipboard explicit copied") | |
| 200 | 175 | return nil | |
| 201 | 176 | } | |
| 202 | 177 | ||
| @@ -9,7 +9,6 @@ import ( | |||||
| 9 | "unicode/utf8" | 9 | "unicode/utf8" | ||
| 10 | 10 | ||||
| 11 | "github.com/bnema/dumber/internal/application/port" | 11 | "github.com/bnema/dumber/internal/application/port" | ||
| 12 | - | "github.com/bnema/dumber/internal/logging" | |||
| 13 | ) | 12 | ) | ||
| 14 | 13 | ||||
| 15 | const explicitClipboardDedupWindow = 250 * time.Millisecond | 14 | const explicitClipboardDedupWindow = 250 * time.Millisecond | ||
| @@ -109,11 +108,6 @@ func (uc *ClipboardTextOrchestratorUseCase) HandleSelectionUpdate( | |||||
| 109 | } | 108 | } | ||
| 110 | if err := uc.clipboard.WriteText(ctx, input.Text); err != nil { | 109 | if err := uc.clipboard.WriteText(ctx, input.Text); err != nil { | ||
| 111 | uc.mu.Unlock() | 110 | uc.mu.Unlock() | ||
| 112 | - | logging.FromContext(ctx).Debug(). | |||
| 113 | - | Err(err). | |||
| 114 | - | Int("text_len", textLen). | |||
| 115 | - | Str("source_engine", string(input.SourceEngine)). | |||
| 116 | - | Msg("clipboard selection write failed") | |||
| 117 | return fmt.Errorf("clipboard write failed: %w", err) | 111 | return fmt.Errorf("clipboard write failed: %w", err) | ||
| 118 | } | 112 | } | ||
| 119 | 113 | ||||
| @@ -131,10 +125,6 @@ func (uc *ClipboardTextOrchestratorUseCase) HandleSelectionUpdate( | |||||
| 131 | if uc.toast != nil { | 125 | if uc.toast != nil { | ||
| 132 | uc.toast(textLen) | 126 | uc.toast(textLen) | ||
| 133 | } | 127 | } | ||
| 134 | - | logging.FromContext(ctx).Debug(). | |||
| 135 | - | Int("text_len", textLen). | |||
| 136 | - | Str("source_engine", string(input.SourceEngine)). | |||
| 137 | - | Msg("clipboard selection copied") | |||
| 138 | return nil | 128 | return nil | ||
| 139 | } | 129 | } | ||
| 140 | 130 | ||||
| @@ -172,12 +162,6 @@ func (uc *ClipboardTextOrchestratorUseCase) HandleExplicitCopy( | |||||
| 172 | if !input.NativeHandled { | 162 | if !input.NativeHandled { | ||
| 173 | if err := uc.clipboard.WriteText(ctx, input.Text); err != nil { | 163 | if err := uc.clipboard.WriteText(ctx, input.Text); err != nil { | ||
| 174 | uc.mu.Unlock() | 164 | uc.mu.Unlock() | ||
| 175 | - | logging.FromContext(ctx).Debug(). | |||
| 176 | - | Err(err). | |||
| 177 | - | Int("text_len", textLen). | |||
| 178 | - | Str("source_engine", string(input.SourceEngine)). | |||
| 179 | - | Str("action", input.Action). | |||
| 180 | - | Msg("clipboard explicit write failed") | |||
| 181 | return fmt.Errorf("clipboard write failed: %w", err) | 165 | return fmt.Errorf("clipboard write failed: %w", err) | ||
| 182 | } | 166 | } | ||
| 183 | } | 167 | } | ||
| @@ -188,15 +172,6 @@ func (uc *ClipboardTextOrchestratorUseCase) HandleExplicitCopy( | |||||
| 188 | if uc.toast != nil { | 172 | if uc.toast != nil { | ||
| 189 | uc.toast(textLen) | 173 | uc.toast(textLen) | ||
| 190 | } | 174 | } | ||
| 191 | - | logEntry := logging.FromContext(ctx).Debug(). | |||
| 192 | - | Int("text_len", textLen). | |||
| 193 | - | Str("source_engine", string(input.SourceEngine)). | |||
| 194 | - | Str("action", input.Action) | |||
| 195 | - | if input.NativeHandled { | |||
| 196 | - | logEntry.Bool("native_handled", true).Msg("clipboard explicit copied (native handled)") | |||
| 197 | - | return nil | |||
| 198 | - | } | |||
| 199 | - | logEntry.Msg("clipboard explicit copied") | |||
| 200 | return nil | 175 | return nil | ||
| 201 | } | 176 | } | ||
| 202 | 177 | ||||
M internal/infrastructure/cef/factory.go +0 -6
| @@ -103,12 +103,6 @@ func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) { | |||
| 103 | 103 | wv.findCtrl = newFindController() | |
| 104 | 104 | ||
| 105 | 105 | input := newInputBridge(ctx, f.scale) | |
| 106 | - | input.selectionText = wv.selectedTextSnapshot | |
| 107 | - | input.explicitCopyText = func(action, text string) { | |
| 108 | - | if f.engine != nil { | |
| 109 | - | f.engine.handleExplicitClipboardBridgeText(wv.id, action, text) | |
| 110 | - | } | |
| 111 | - | } | |
| 112 | 106 | input.attachTo(pipeline.glArea) | |
| 113 | 107 | wv.input = input | |
| 114 | 108 | ||
| @@ -103,12 +103,6 @@ func (f *WebViewFactory) Create(ctx context.Context) (port.WebView, error) { | |||||
| 103 | wv.findCtrl = newFindController() | 103 | wv.findCtrl = newFindController() | ||
| 104 | 104 | ||||
| 105 | input := newInputBridge(ctx, f.scale) | 105 | input := newInputBridge(ctx, f.scale) | ||
| 106 | - | input.selectionText = wv.selectedTextSnapshot | |||
| 107 | - | input.explicitCopyText = func(action, text string) { | |||
| 108 | - | if f.engine != nil { | |||
| 109 | - | f.engine.handleExplicitClipboardBridgeText(wv.id, action, text) | |||
| 110 | - | } | |||
| 111 | - | } | |||
| 112 | input.attachTo(pipeline.glArea) | 106 | input.attachTo(pipeline.glArea) | ||
| 113 | wv.input = input | 107 | wv.input = input | ||
| 114 | 108 | ||||
M internal/infrastructure/cef/input_bridge.go +1 -48
| @@ -57,14 +57,6 @@ type inputBridge struct { | |||
| 57 | 57 | // ImeCommitText. Set once during attachTo, read-only afterwards. | |
| 58 | 58 | clipboard *gdk.Clipboard | |
| 59 | 59 | ||
| 60 | - | // selectionText returns the browser's latest known text selection so Ctrl+C | |
| 61 | - | // / Ctrl+X can route the selection through the shared explicit-copy path. | |
| 62 | - | selectionText func() string | |
| 63 | - | ||
| 64 | - | // explicitCopyText routes explicit copy/cut text through the shared | |
| 65 | - | // clipboard orchestration path. | |
| 66 | - | explicitCopyText func(action, text string) | |
| 67 | - | ||
| 68 | 60 | // IMContext for dead key / compose sequence support. Held as a field | |
| 69 | 61 | // to prevent garbage collection while the controller is alive. | |
| 70 | 62 | imContext *gtk.IMContextSimple | |
| @@ -231,8 +223,6 @@ func (ib *inputBridge) attachFocusAndKeyboard(glArea *gtk.GLArea) { | |||
| 231 | 223 | keyPressCb := func(_ gtk.EventControllerKey, keyval, keycode uint, state gdk.ModifierType) bool { | |
| 232 | 224 | mods := uint(state) | |
| 233 | 225 | ||
| 234 | - | ib.maybeMirrorClipboardShortcut(keyval, mods) | |
| 235 | - | ||
| 236 | 226 | // Intercept Ctrl+V / Ctrl+Shift+V: CEF OSR cannot access the | |
| 237 | 227 | // Wayland/X11 clipboard, so we read from GDK and inject via | |
| 238 | 228 | // ImeCommitText instead of letting CEF handle the paste. | |
| @@ -381,39 +371,6 @@ func (ib *inputBridge) hasGTKFocus() bool { | |||
| 381 | 371 | return ib.gtkFocused.Load() | |
| 382 | 372 | } | |
| 383 | 373 | ||
| 384 | - | func (ib *inputBridge) maybeMirrorClipboardShortcut(keyval, mods uint) { | |
| 385 | - | action, ok := clipboardShortcutAction(keyval, mods) | |
| 386 | - | if !ok || ib == nil || ib.selectionText == nil || ib.explicitCopyText == nil { | |
| 387 | - | return | |
| 388 | - | } | |
| 389 | - | text := ib.selectionText() | |
| 390 | - | if text == "" { | |
| 391 | - | return | |
| 392 | - | } | |
| 393 | - | logging.FromContext(ib.ctx).Debug(). | |
| 394 | - | Str("action", action). | |
| 395 | - | Int("text_len", len(text)). | |
| 396 | - | Msg("cef: clipboard shortcut routed through explicit copy handler") | |
| 397 | - | ib.explicitCopyText(action, text) | |
| 398 | - | } | |
| 399 | - | ||
| 400 | - | func clipboardShortcutAction(keyval, mods uint) (string, bool) { | |
| 401 | - | if mods&uint(gdk.ControlMaskValue) == 0 { | |
| 402 | - | return "", false | |
| 403 | - | } | |
| 404 | - | if mods&uint(gdk.ShiftMaskValue) != 0 || mods&uint(gdk.AltMaskValue) != 0 { | |
| 405 | - | return "", false | |
| 406 | - | } | |
| 407 | - | switch keyval { | |
| 408 | - | case gdkKeyLowercaseC, gdkKeyUppercaseC: | |
| 409 | - | return "copy", true | |
| 410 | - | case gdkKeyLowercaseX, gdkKeyUppercaseX: | |
| 411 | - | return "cut", true | |
| 412 | - | default: | |
| 413 | - | return "", false | |
| 414 | - | } | |
| 415 | - | } | |
| 416 | - | ||
| 417 | 374 | func (ib *inputBridge) onKeyPress(keyval, keycode, mods uint) { | |
| 418 | 375 | ib.mu.Lock() | |
| 419 | 376 | host := ib.host | |
| @@ -634,13 +591,9 @@ const ( | |||
| 634 | 591 | gdkKeyPageUp = 0xff55 | |
| 635 | 592 | gdkKeyPageDown = 0xff56 | |
| 636 | 593 | ||
| 637 | - | // Clipboard shortcut keyvals. | |
| 638 | - | gdkKeyLowercaseC = 0x063 | |
| 639 | - | gdkKeyUppercaseC = 0x043 | |
| 594 | + | // Paste shortcut keyvals. | |
| 640 | 595 | gdkKeyLowercaseV = 0x076 | |
| 641 | 596 | gdkKeyUppercaseV = 0x056 | |
| 642 | - | gdkKeyLowercaseX = 0x078 | |
| 643 | - | gdkKeyUppercaseX = 0x058 | |
| 644 | 597 | ) | |
| 645 | 598 | ||
| 646 | 599 | // keyvalToChar converts a GDK keyval to a UTF-16 character for CEF CHAR events. | |
| @@ -57,14 +57,6 @@ type inputBridge struct { | |||||
| 57 | // ImeCommitText. Set once during attachTo, read-only afterwards. | 57 | // ImeCommitText. Set once during attachTo, read-only afterwards. | ||
| 58 | clipboard *gdk.Clipboard | 58 | clipboard *gdk.Clipboard | ||
| 59 | 59 | ||||
| 60 | - | // selectionText returns the browser's latest known text selection so Ctrl+C | |||
| 61 | - | // / Ctrl+X can route the selection through the shared explicit-copy path. | |||
| 62 | - | selectionText func() string | |||
| 63 | - | ||||
| 64 | - | // explicitCopyText routes explicit copy/cut text through the shared | |||
| 65 | - | // clipboard orchestration path. | |||
| 66 | - | explicitCopyText func(action, text string) | |||
| 67 | - | ||||
| 68 | // IMContext for dead key / compose sequence support. Held as a field | 60 | // IMContext for dead key / compose sequence support. Held as a field | ||
| 69 | // to prevent garbage collection while the controller is alive. | 61 | // to prevent garbage collection while the controller is alive. | ||
| 70 | imContext *gtk.IMContextSimple | 62 | imContext *gtk.IMContextSimple | ||
| @@ -231,8 +223,6 @@ func (ib *inputBridge) attachFocusAndKeyboard(glArea *gtk.GLArea) { | |||||
| 231 | keyPressCb := func(_ gtk.EventControllerKey, keyval, keycode uint, state gdk.ModifierType) bool { | 223 | keyPressCb := func(_ gtk.EventControllerKey, keyval, keycode uint, state gdk.ModifierType) bool { | ||
| 232 | mods := uint(state) | 224 | mods := uint(state) | ||
| 233 | 225 | ||||
| 234 | - | ib.maybeMirrorClipboardShortcut(keyval, mods) | |||
| 235 | - | ||||
| 236 | // Intercept Ctrl+V / Ctrl+Shift+V: CEF OSR cannot access the | 226 | // Intercept Ctrl+V / Ctrl+Shift+V: CEF OSR cannot access the | ||
| 237 | // Wayland/X11 clipboard, so we read from GDK and inject via | 227 | // Wayland/X11 clipboard, so we read from GDK and inject via | ||
| 238 | // ImeCommitText instead of letting CEF handle the paste. | 228 | // ImeCommitText instead of letting CEF handle the paste. | ||
| @@ -381,39 +371,6 @@ func (ib *inputBridge) hasGTKFocus() bool { | |||||
| 381 | return ib.gtkFocused.Load() | 371 | return ib.gtkFocused.Load() | ||
| 382 | } | 372 | } | ||
| 383 | 373 | ||||
| 384 | - | func (ib *inputBridge) maybeMirrorClipboardShortcut(keyval, mods uint) { | |||
| 385 | - | action, ok := clipboardShortcutAction(keyval, mods) | |||
| 386 | - | if !ok || ib == nil || ib.selectionText == nil || ib.explicitCopyText == nil { | |||
| 387 | - | return | |||
| 388 | - | } | |||
| 389 | - | text := ib.selectionText() | |||
| 390 | - | if text == "" { | |||
| 391 | - | return | |||
| 392 | - | } | |||
| 393 | - | logging.FromContext(ib.ctx).Debug(). | |||
| 394 | - | Str("action", action). | |||
| 395 | - | Int("text_len", len(text)). | |||
| 396 | - | Msg("cef: clipboard shortcut routed through explicit copy handler") | |||
| 397 | - | ib.explicitCopyText(action, text) | |||
| 398 | - | } | |||
| 399 | - | ||||
| 400 | - | func clipboardShortcutAction(keyval, mods uint) (string, bool) { | |||
| 401 | - | if mods&uint(gdk.ControlMaskValue) == 0 { | |||
| 402 | - | return "", false | |||
| 403 | - | } | |||
| 404 | - | if mods&uint(gdk.ShiftMaskValue) != 0 || mods&uint(gdk.AltMaskValue) != 0 { | |||
| 405 | - | return "", false | |||
| 406 | - | } | |||
| 407 | - | switch keyval { | |||
| 408 | - | case gdkKeyLowercaseC, gdkKeyUppercaseC: | |||
| 409 | - | return "copy", true | |||
| 410 | - | case gdkKeyLowercaseX, gdkKeyUppercaseX: | |||
| 411 | - | return "cut", true | |||
| 412 | - | default: | |||
| 413 | - | return "", false | |||
| 414 | - | } | |||
| 415 | - | } | |||
| 416 | - | ||||
| 417 | func (ib *inputBridge) onKeyPress(keyval, keycode, mods uint) { | 374 | func (ib *inputBridge) onKeyPress(keyval, keycode, mods uint) { | ||
| 418 | ib.mu.Lock() | 375 | ib.mu.Lock() | ||
| 419 | host := ib.host | 376 | host := ib.host | ||
| @@ -634,13 +591,9 @@ const ( | |||||
| 634 | gdkKeyPageUp = 0xff55 | 591 | gdkKeyPageUp = 0xff55 | ||
| 635 | gdkKeyPageDown = 0xff56 | 592 | gdkKeyPageDown = 0xff56 | ||
| 636 | 593 | ||||
| 637 | - | // Clipboard shortcut keyvals. | 594 | + | // Paste shortcut keyvals. |
| 638 | - | gdkKeyLowercaseC = 0x063 | |||
| 639 | - | gdkKeyUppercaseC = 0x043 | |||
| 640 | gdkKeyLowercaseV = 0x076 | 595 | gdkKeyLowercaseV = 0x076 | ||
| 641 | gdkKeyUppercaseV = 0x056 | 596 | gdkKeyUppercaseV = 0x056 | ||
| 642 | - | gdkKeyLowercaseX = 0x078 | |||
| 643 | - | gdkKeyUppercaseX = 0x058 | |||
| 644 | ) | 597 | ) | ||
| 645 | 598 | ||||
| 646 | // keyvalToChar converts a GDK keyval to a UTF-16 character for CEF CHAR events. | 599 | // keyvalToChar converts a GDK keyval to a UTF-16 character for CEF CHAR events. | ||
M internal/infrastructure/cef/input_bridge_test.go +0 -51
| @@ -1,11 +1,9 @@ | |||
| 1 | 1 | package cef | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "context" | |
| 5 | 4 | "testing" | |
| 6 | 5 | ||
| 7 | 6 | purecef "github.com/bnema/purego-cef/cef" | |
| 8 | - | "github.com/bnema/puregotk/v4/gdk" | |
| 9 | 7 | "github.com/stretchr/testify/require" | |
| 10 | 8 | ) | |
| 11 | 9 | ||
| @@ -41,52 +39,3 @@ func TestSyncWindowlessBrowserFocus_ReassertsVisibleFocusedAndInvalidates(t *tes | |||
| 41 | 39 | require.Equal(t, []int32{1}, host.focusValues) | |
| 42 | 40 | require.Equal(t, []purecef.PaintElementType{purecef.PaintElementTypePetView}, host.invalidations) | |
| 43 | 41 | } | |
| 44 | - | ||
| 45 | - | func TestClipboardShortcutAction_RecognizesCopyAndCut(t *testing.T) { | |
| 46 | - | action, ok := clipboardShortcutAction(gdkKeyLowercaseC, uint(gdk.ControlMaskValue)) | |
| 47 | - | require.True(t, ok) | |
| 48 | - | require.Equal(t, "copy", action) | |
| 49 | - | ||
| 50 | - | action, ok = clipboardShortcutAction(gdkKeyUppercaseX, uint(gdk.ControlMaskValue)) | |
| 51 | - | require.True(t, ok) | |
| 52 | - | require.Equal(t, "cut", action) | |
| 53 | - | } | |
| 54 | - | ||
| 55 | - | func TestClipboardShortcutAction_IgnoresCtrlShiftC(t *testing.T) { | |
| 56 | - | action, ok := clipboardShortcutAction(gdkKeyUppercaseC, uint(gdk.ControlMaskValue|gdk.ShiftMaskValue)) | |
| 57 | - | require.False(t, ok) | |
| 58 | - | require.Empty(t, action) | |
| 59 | - | } | |
| 60 | - | ||
| 61 | - | func TestMaybeMirrorClipboardShortcut_DelegatesExplicitCopyAction(t *testing.T) { | |
| 62 | - | var gotAction string | |
| 63 | - | var gotText string | |
| 64 | - | ib := &inputBridge{ | |
| 65 | - | ctx: context.Background(), | |
| 66 | - | selectionText: func() string { return "selected text" }, | |
| 67 | - | explicitCopyText: func(action, text string) { | |
| 68 | - | gotAction = action | |
| 69 | - | gotText = text | |
| 70 | - | }, | |
| 71 | - | } | |
| 72 | - | ||
| 73 | - | ib.maybeMirrorClipboardShortcut(gdkKeyLowercaseC, uint(gdk.ControlMaskValue)) | |
| 74 | - | ||
| 75 | - | require.Equal(t, "copy", gotAction) | |
| 76 | - | require.Equal(t, "selected text", gotText) | |
| 77 | - | } | |
| 78 | - | ||
| 79 | - | func TestMaybeMirrorClipboardShortcut_SkipsWhenSelectionEmpty(t *testing.T) { | |
| 80 | - | called := false | |
| 81 | - | ib := &inputBridge{ | |
| 82 | - | ctx: context.Background(), | |
| 83 | - | selectionText: func() string { return "" }, | |
| 84 | - | explicitCopyText: func(string, string) { | |
| 85 | - | called = true | |
| 86 | - | }, | |
| 87 | - | } | |
| 88 | - | ||
| 89 | - | ib.maybeMirrorClipboardShortcut(gdkKeyLowercaseC, uint(gdk.ControlMaskValue)) | |
| 90 | - | ||
| 91 | - | require.False(t, called) | |
| 92 | - | } | |
| @@ -1,11 +1,9 @@ | |||||
| 1 | package cef | 1 | package cef | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "context" | |||
| 5 | "testing" | 4 | "testing" | ||
| 6 | 5 | ||||
| 7 | purecef "github.com/bnema/purego-cef/cef" | 6 | purecef "github.com/bnema/purego-cef/cef" | ||
| 8 | - | "github.com/bnema/puregotk/v4/gdk" | |||
| 9 | "github.com/stretchr/testify/require" | 7 | "github.com/stretchr/testify/require" | ||
| 10 | ) | 8 | ) | ||
| 11 | 9 | ||||
| @@ -41,52 +39,3 @@ func TestSyncWindowlessBrowserFocus_ReassertsVisibleFocusedAndInvalidates(t *tes | |||||
| 41 | require.Equal(t, []int32{1}, host.focusValues) | 39 | require.Equal(t, []int32{1}, host.focusValues) | ||
| 42 | require.Equal(t, []purecef.PaintElementType{purecef.PaintElementTypePetView}, host.invalidations) | 40 | require.Equal(t, []purecef.PaintElementType{purecef.PaintElementTypePetView}, host.invalidations) | ||
| 43 | } | 41 | } | ||
| 44 | - | ||||
| 45 | - | func TestClipboardShortcutAction_RecognizesCopyAndCut(t *testing.T) { | |||
| 46 | - | action, ok := clipboardShortcutAction(gdkKeyLowercaseC, uint(gdk.ControlMaskValue)) | |||
| 47 | - | require.True(t, ok) | |||
| 48 | - | require.Equal(t, "copy", action) | |||
| 49 | - | ||||
| 50 | - | action, ok = clipboardShortcutAction(gdkKeyUppercaseX, uint(gdk.ControlMaskValue)) | |||
| 51 | - | require.True(t, ok) | |||
| 52 | - | require.Equal(t, "cut", action) | |||
| 53 | - | } | |||
| 54 | - | ||||
| 55 | - | func TestClipboardShortcutAction_IgnoresCtrlShiftC(t *testing.T) { | |||
| 56 | - | action, ok := clipboardShortcutAction(gdkKeyUppercaseC, uint(gdk.ControlMaskValue|gdk.ShiftMaskValue)) | |||
| 57 | - | require.False(t, ok) | |||
| 58 | - | require.Empty(t, action) | |||
| 59 | - | } | |||
| 60 | - | ||||
| 61 | - | func TestMaybeMirrorClipboardShortcut_DelegatesExplicitCopyAction(t *testing.T) { | |||
| 62 | - | var gotAction string | |||
| 63 | - | var gotText string | |||
| 64 | - | ib := &inputBridge{ | |||
| 65 | - | ctx: context.Background(), | |||
| 66 | - | selectionText: func() string { return "selected text" }, | |||
| 67 | - | explicitCopyText: func(action, text string) { | |||
| 68 | - | gotAction = action | |||
| 69 | - | gotText = text | |||
| 70 | - | }, | |||
| 71 | - | } | |||
| 72 | - | ||||
| 73 | - | ib.maybeMirrorClipboardShortcut(gdkKeyLowercaseC, uint(gdk.ControlMaskValue)) | |||
| 74 | - | ||||
| 75 | - | require.Equal(t, "copy", gotAction) | |||
| 76 | - | require.Equal(t, "selected text", gotText) | |||
| 77 | - | } | |||
| 78 | - | ||||
| 79 | - | func TestMaybeMirrorClipboardShortcut_SkipsWhenSelectionEmpty(t *testing.T) { | |||
| 80 | - | called := false | |||
| 81 | - | ib := &inputBridge{ | |||
| 82 | - | ctx: context.Background(), | |||
| 83 | - | selectionText: func() string { return "" }, | |||
| 84 | - | explicitCopyText: func(string, string) { | |||
| 85 | - | called = true | |||
| 86 | - | }, | |||
| 87 | - | } | |||
| 88 | - | ||||
| 89 | - | ib.maybeMirrorClipboardShortcut(gdkKeyLowercaseC, uint(gdk.ControlMaskValue)) | |||
| 90 | - | ||||
| 91 | - | require.False(t, called) | |||
| 92 | - | } | |||