From b0ceb0efea67a535d6737e0099c18f372de22d97 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Tue, 16 Jun 2026 17:07:24 +0100 Subject: [PATCH 01/12] Handle searchbar key events in MainWindow EventControllerKey --- src/MainWindow.vala | 40 +++++++++++++++++++- src/Widgets/SearchBar.vala | 76 ++++++++++---------------------------- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 78b9b1d1e..3e724e3b5 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -709,7 +709,12 @@ namespace Scratch { } private bool on_key_pressed (uint keyval, uint keycode, Gdk.ModifierType state) { - switch (Gdk.keyval_name (keyval)) { + string key = Gdk.keyval_name (keyval); + if (Gdk.ModifierType.SHIFT_MASK in state) { + key = "" + key; + } + + switch (key) { case "Escape": if (search_bar.is_revealed) { var action = Utils.action_from_group (ACTION_TOGGLE_SHOW_FIND, actions); @@ -720,6 +725,39 @@ namespace Scratch { break; } + // Handle searchbar key_press events here else consumed by Gtk.Entry widgets + if (search_bar.search_is_focused && search_bar.has_search_term) { + switch (key) { + case "Return": + case "Up": + search_bar.search_previous (); + return true; + case "Return": + case "Down": + search_bar.search_next (); + return true; + case "Tab": + search_bar.focus_replace_entry (); + return true; + default: + return false; + } + } else if (search_bar.replace_is_focused && search_bar.has_search_term) { + switch (Gdk.keyval_name (keyval)) { + case "Up": + search_bar.search_previous (); + return true; + case "Down": + search_bar.search_next (); + return true; + case "Tab": + search_bar.focus_search_entry (); + + return true; + } + + return false; + } // propagate this event to child widgets return false; } diff --git a/src/Widgets/SearchBar.vala b/src/Widgets/SearchBar.vala index 8fb50588d..c82a53c16 100644 --- a/src/Widgets/SearchBar.vala +++ b/src/Widgets/SearchBar.vala @@ -56,7 +56,19 @@ namespace Scratch.Widgets { public bool is_focused { get { - return search_entry.has_focus || replace_entry.has_focus; + return search_is_focused || replace_is_focused; + } + } + + public bool search_is_focused { + get { + return search_entry.has_focus; + } + } + + public bool replace_is_focused { + get { + return replace_entry.has_focus; } } @@ -72,6 +84,12 @@ namespace Scratch.Widgets { } } + public bool has_search_term { + get { + return search_entry.text != ""; + } + } + public uint search_occurrences { get { if (search_context == null || @@ -226,7 +244,6 @@ namespace Scratch.Widgets { // Connecting to some signals search_entry.changed.connect (on_search_parameters_changed); - search_entry.key_press_event.connect (on_search_entry_key_press); search_entry.focus_in_event.connect (on_search_entry_focused_in); search_entry.icon_release.connect ((p0, p1) => { if (p0 == Gtk.EntryIconPosition.PRIMARY) { @@ -234,7 +251,6 @@ namespace Scratch.Widgets { } }); replace_entry.activate.connect (on_replace_entry_activate); - replace_entry.key_press_event.connect (on_replace_entry_key_press); var entry_path = new Gtk.WidgetPath (); entry_path.append_type (typeof (Gtk.Widget)); @@ -522,60 +538,6 @@ namespace Scratch.Widgets { search_entry.text = text; } - private bool on_search_entry_key_press (Gdk.EventKey event) { - /* We don't need to perform search if there is nothing to search... */ - if (search_entry.text == "") { - return false; - } - - string key = Gdk.keyval_name (event.keyval); - if (Gdk.ModifierType.SHIFT_MASK in event.state) { - key = "" + key; - } - - switch (key) { - case "Return": - case "Up": - search_previous (); - return true; - case "Return": - case "Down": - search_next (); - return true; - case "Tab": - if (search_entry.is_focus) { - replace_entry.grab_focus (); - } - - return true; - } - - return false; - } - - private bool on_replace_entry_key_press (Gdk.EventKey event) { - /* We don't need to perform search if there is nothing to search… */ - if (search_entry.text == "") { - return false; - } - - switch (Gdk.keyval_name (event.keyval)) { - case "Up": - search_previous (); - return true; - case "Down": - search_next (); - return true; - case "Tab": - if (replace_entry.is_focus) { - search_entry.grab_focus (); - } - - return true; - } - - return false; - } private void cancel_update_search_widgets () { if (update_search_label_timeout_id > 0) { From f9988c25809d982fd9639789a8250747e364a39f Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 17 Jun 2026 12:14:10 +0100 Subject: [PATCH 02/12] Use key controller in word completion plugin --- .../word-completion/completion-provider.vala | 1 - plugins/word-completion/plugin.vala | 20 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/word-completion/completion-provider.vala b/plugins/word-completion/completion-provider.vala index 4f2430c8b..d7878a1ed 100644 --- a/plugins/word-completion/completion-provider.vala +++ b/plugins/word-completion/completion-provider.vala @@ -111,7 +111,6 @@ public class Scratch.Plugins.CompletionProvider : Gtk.SourceCompletionProvider, return true; } - private bool get_proposals (out GLib.List? props, bool no_minimum) { string to_find = ""; Gtk.TextBuffer temp_buffer = buffer; diff --git a/plugins/word-completion/plugin.vala b/plugins/word-completion/plugin.vala index 7719a5c2f..900673a42 100644 --- a/plugins/word-completion/plugin.vala +++ b/plugins/word-completion/plugin.vala @@ -24,6 +24,7 @@ public class Scratch.Plugins.Completion : Peas.ExtensionBase, Scratch.Services.A public Object object { owned get; set construct; } private List text_view_list = new List (); + private Gtk.EventControllerKey key_controller; public Euclide.Completion.Parser parser {get; private set;} public Gtk.SourceView? current_view {get; private set;} public Scratch.Services.Document current_document {get; private set;} @@ -53,6 +54,13 @@ public class Scratch.Plugins.Completion : Peas.ExtensionBase, Scratch.Services.A }); plugins.hook_document.connect (on_new_source_view); + plugins.hook_window.connect ((w) => { + key_controller = new Gtk.EventControllerKey (w) { + propagation_phase = CAPTURE + }; + + key_controller.key_pressed.connect (on_key_press); + }); } public void deactivate () { @@ -78,7 +86,7 @@ public class Scratch.Plugins.Completion : Peas.ExtensionBase, Scratch.Services.A current_document = doc; current_view = doc.source_view; - current_view.key_press_event.connect (on_key_press); + current_view.completion.show.connect (() => { completion_in_progress = true; }); @@ -120,10 +128,12 @@ public class Scratch.Plugins.Completion : Peas.ExtensionBase, Scratch.Services.A return false; } - private bool on_key_press (Gtk.Widget view, Gdk.EventKey event) { - var kv = event.keyval; + // private bool on_key_press (Gtk.Widget view, Gdk.EventKey event) { + private bool on_key_press (uint keyval, uint keycode, Gdk.ModifierType state) requires (current_view != null) { + var kv = keyval; + warning ("key"); /* Pass through any modified keypress except Shift or Capslock */ - Gdk.ModifierType mods = event.state & Gdk.ModifierType.MODIFIER_MASK + Gdk.ModifierType mods = state & Gdk.ModifierType.MODIFIER_MASK & ~Gdk.ModifierType.SHIFT_MASK & ~Gdk.ModifierType.LOCK_MASK; if (mods > 0 ) { @@ -166,7 +176,7 @@ public class Scratch.Plugins.Completion : Peas.ExtensionBase, Scratch.Services.A } private void cleanup (Gtk.SourceView view) { - current_view.key_press_event.disconnect (on_key_press); + // current_view.key_press_event.disconnect (on_key_press); current_view.completion.get_providers ().foreach ((p) => { try { From b554944dd19598ab812d74dfe483afa3342fe861 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 17 Jun 2026 16:22:00 +0100 Subject: [PATCH 03/12] Cleanup --- plugins/word-completion/plugin.vala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/word-completion/plugin.vala b/plugins/word-completion/plugin.vala index 900673a42..f7c8e467b 100644 --- a/plugins/word-completion/plugin.vala +++ b/plugins/word-completion/plugin.vala @@ -128,10 +128,8 @@ public class Scratch.Plugins.Completion : Peas.ExtensionBase, Scratch.Services.A return false; } - // private bool on_key_press (Gtk.Widget view, Gdk.EventKey event) { private bool on_key_press (uint keyval, uint keycode, Gdk.ModifierType state) requires (current_view != null) { var kv = keyval; - warning ("key"); /* Pass through any modified keypress except Shift or Capslock */ Gdk.ModifierType mods = state & Gdk.ModifierType.MODIFIER_MASK & ~Gdk.ModifierType.SHIFT_MASK @@ -176,8 +174,6 @@ public class Scratch.Plugins.Completion : Peas.ExtensionBase, Scratch.Services.A } private void cleanup (Gtk.SourceView view) { - // current_view.key_press_event.disconnect (on_key_press); - current_view.completion.get_providers ().foreach ((p) => { try { /* Only remove provider added by this plug in */ From 0fcd6b2425c0887912b24190e541e2619289642b Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 17 Jun 2026 16:33:07 +0100 Subject: [PATCH 04/12] Word completion plugin: Only process key presses when view is focused --- plugins/word-completion/plugin.vala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/word-completion/plugin.vala b/plugins/word-completion/plugin.vala index f7c8e467b..2eec8521f 100644 --- a/plugins/word-completion/plugin.vala +++ b/plugins/word-completion/plugin.vala @@ -130,6 +130,9 @@ public class Scratch.Plugins.Completion : Peas.ExtensionBase, Scratch.Services.A private bool on_key_press (uint keyval, uint keycode, Gdk.ModifierType state) requires (current_view != null) { var kv = keyval; + if (!current_view.is_focus) { + return false; + } /* Pass through any modified keypress except Shift or Capslock */ Gdk.ModifierType mods = state & Gdk.ModifierType.MODIFIER_MASK & ~Gdk.ModifierType.SHIFT_MASK From d7d5cfd1883d214e3d50bafc14eadf8dc3c85ed5 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 17 Jun 2026 16:35:59 +0100 Subject: [PATCH 05/12] Use EventControllerKey in brackets completion plugin --- .../brackets-completion.vala | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/plugins/brackets-completion/brackets-completion.vala b/plugins/brackets-completion/brackets-completion.vala index ba3727e80..5e4d3a125 100644 --- a/plugins/brackets-completion/brackets-completion.vala +++ b/plugins/brackets-completion/brackets-completion.vala @@ -11,6 +11,7 @@ public class Scratch.Plugins.BracketsCompletion : Peas.ExtensionBase, Scratch.Se public Object object { owned get; set construct; } + private Gtk.EventControllerKey key_controller; private Gee.HashMap brackets; private Gee.HashMap keys; private Gtk.TextBuffer current_buffer; @@ -40,6 +41,12 @@ public class Scratch.Plugins.BracketsCompletion : Peas.ExtensionBase, Scratch.Se plugins = (Scratch.Services.Interface) object; plugins.hook_document.connect (on_hook_document); + plugins.hook_window.connect ((w) => { + key_controller = new Gtk.EventControllerKey (w) { + propagation_phase = CAPTURE + }; + key_controller.key_pressed.connect (on_key_down); + }); } public void deactivate () { @@ -52,14 +59,12 @@ public class Scratch.Plugins.BracketsCompletion : Peas.ExtensionBase, Scratch.Se current_buffer = doc.source_view.buffer; if (current_source_view != null) { - current_source_view.key_press_event.disconnect (on_key_down); current_source_view.event_after.disconnect (on_event_after); current_source_view.backspace.disconnect (on_backspace); } current_source_view = doc.source_view; - current_source_view.key_press_event.connect (on_key_down); current_source_view.event_after.connect (on_event_after); current_source_view.backspace.connect (on_backspace); } @@ -163,8 +168,12 @@ public class Scratch.Plugins.BracketsCompletion : Peas.ExtensionBase, Scratch.Se current_buffer.end_user_action (); } - private bool on_key_down (Gdk.EventKey event) { - if (Gdk.ModifierType.MOD1_MASK in event.state || Gdk.ModifierType.CONTROL_MASK in event.state) { + private bool on_key_down (uint keyval, uint keycode, Gdk.ModifierType state) requires (current_buffer != null) { + if (!current_source_view.is_focus) { + return false; + } + + if (Gdk.ModifierType.MOD1_MASK in state || Gdk.ModifierType.CONTROL_MASK in state) { return false; } @@ -173,7 +182,7 @@ public class Scratch.Plugins.BracketsCompletion : Peas.ExtensionBase, Scratch.Se return false; } - if (keys.has_key (event.keyval) && current_buffer.has_selection) { + if (keys.has_key (keyval) && current_buffer.has_selection) { Gtk.TextIter start, end; current_buffer.get_selection_bounds (out start, out end); From b6748dfc8944158884a47763697b8723abbf7816 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 17 Jun 2026 18:03:13 +0100 Subject: [PATCH 06/12] Use EventControllerKey in markdown plugin --- .../markdown-actions/markdown-actions.vala | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/plugins/markdown-actions/markdown-actions.vala b/plugins/markdown-actions/markdown-actions.vala index 65b712da7..6bdbb7b50 100644 --- a/plugins/markdown-actions/markdown-actions.vala +++ b/plugins/markdown-actions/markdown-actions.vala @@ -26,11 +26,13 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services public void update_state () {} + private Gtk.EventControllerKey key_controller; + public void activate () { plugins = (Scratch.Services.Interface) object; plugins.hook_document.connect ((doc) => { if (current_source != null) { - current_source.key_press_event.disconnect (shortcut_handler); + // current_source.key_press_event.disconnect (shortcut_handler); current_source.notify["language"].disconnect (configure_shortcuts); } @@ -39,30 +41,43 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services current_source.notify["language"].connect (configure_shortcuts); }); + plugins.hook_window.connect ((w) => { + key_controller = new Gtk.EventControllerKey (w) { + propagation_phase = CAPTURE + }; + key_controller.key_pressed.connect (shortcut_handler); + }); } private void configure_shortcuts () { var lang = current_source.language; if (lang != null && lang.id == "markdown") { - current_source.key_press_event.connect (shortcut_handler); + // current_source.key_press_event.connect (shortcut_handler); } else { - current_source.key_press_event.disconnect (shortcut_handler); + // current_source.key_press_event.disconnect (shortcut_handler); } } - private bool shortcut_handler (Gdk.EventKey evt) { - var control = (evt.state & Gdk.ModifierType.CONTROL_MASK) != 0; - var shift = (evt.state & Gdk.ModifierType.SHIFT_MASK) != 0; - var other_mods = (evt.state & Gtk.accelerator_get_default_mod_mask () & - ~Gdk.ModifierType.SHIFT_MASK & - ~Gdk.ModifierType.CONTROL_MASK) != 0; + private bool shortcut_handler ( + Gtk.EventController controller, + uint keyval, + uint keycode, + Gdk.ModifierType state + ) requires (current_source != null) { + if (!Gtk.accelerator_valid (keyval, state)) { + return false; + } - if (evt.is_modifier == 1 || other_mods == true) { + var mods = (state & Gtk.accelerator_get_default_mod_mask ()); + if (((mods & ~Gdk.ModifierType.SHIFT_MASK) & ~Gdk.ModifierType.CONTROL_MASK) != 0) { + // A modifier other than Control or Shift is down return false; } + var control = (mods & Gdk.ModifierType.CONTROL_MASK) != 0; + var shift = (mods & Gdk.ModifierType.SHIFT_MASK) != 0; if (control && shift) { - switch (evt.keyval) { + switch (keyval) { case Gdk.Key.B: add_markdown_tag ("**"); return true; @@ -75,7 +90,7 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services } } - if (evt.keyval == Gdk.Key.Return) { + if (keyval == Gdk.Key.Return) { char ul_marker; int ol_number = 1; string item_text; @@ -99,6 +114,7 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services return true; } } + return false; } @@ -233,7 +249,6 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services public void deactivate () { if (current_source != null) { - current_source.key_press_event.disconnect (shortcut_handler); current_source.notify["language"].disconnect (configure_shortcuts); } } From 3b479f4410052bdbc470e5a824bd7ae2656b25ab Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 17 Jun 2026 18:13:54 +0100 Subject: [PATCH 07/12] Rework language tracking in markdown plugin --- plugins/markdown-actions/markdown-actions.vala | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/plugins/markdown-actions/markdown-actions.vala b/plugins/markdown-actions/markdown-actions.vala index 6bdbb7b50..03754c2f4 100644 --- a/plugins/markdown-actions/markdown-actions.vala +++ b/plugins/markdown-actions/markdown-actions.vala @@ -27,12 +27,12 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services public void update_state () {} private Gtk.EventControllerKey key_controller; + private bool is_markdown = false; public void activate () { plugins = (Scratch.Services.Interface) object; plugins.hook_document.connect ((doc) => { if (current_source != null) { - // current_source.key_press_event.disconnect (shortcut_handler); current_source.notify["language"].disconnect (configure_shortcuts); } @@ -51,11 +51,7 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services private void configure_shortcuts () { var lang = current_source.language; - if (lang != null && lang.id == "markdown") { - // current_source.key_press_event.connect (shortcut_handler); - } else { - // current_source.key_press_event.disconnect (shortcut_handler); - } + is_markdown = (lang != null && lang.id == "markdown"); } private bool shortcut_handler ( @@ -64,7 +60,7 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services uint keycode, Gdk.ModifierType state ) requires (current_source != null) { - if (!Gtk.accelerator_valid (keyval, state)) { + if (!is_markdown || !Gtk.accelerator_valid (keyval, state)) { return false; } From 518dfd139077bcc961b36715a5f0ce6506c33230 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 17 Jun 2026 19:12:55 +0100 Subject: [PATCH 08/12] Use EventControllerKey in vim plugin --- plugins/vim-emulation/vim-emulation.vala | 41 ++++++++++++++++-------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/plugins/vim-emulation/vim-emulation.vala b/plugins/vim-emulation/vim-emulation.vala index 394c5e5c8..480ae311e 100644 --- a/plugins/vim-emulation/vim-emulation.vala +++ b/plugins/vim-emulation/vim-emulation.vala @@ -34,6 +34,8 @@ public class Scratch.Plugins.VimEmulation : Peas.ExtensionBase, Scratch.Services Scratch.Widgets.SourceView? view = null; Scratch.Services.Interface plugins; + private Gtk.EventControllerKey key_controller; + public Object object { owned get; set construct; } construct { @@ -48,36 +50,44 @@ public class Scratch.Plugins.VimEmulation : Peas.ExtensionBase, Scratch.Services plugins = (Scratch.Services.Interface) object; plugins.hook_document.connect ((doc) => { this.view = doc.source_view; - this.view.key_press_event.disconnect (handle_key_press); - this.view.key_press_event.connect (handle_key_press); this.views.add (view); }); + plugins.hook_window.connect ((w) => { + key_controller = new Gtk.EventControllerKey (w) { + propagation_phase = CAPTURE + }; + + key_controller.key_pressed.connect (handle_key_press); + }); } public void deactivate () { - foreach (var v in views) { - v.key_press_event.disconnect (handle_key_press); - } + key_controller = null; } - private bool handle_key_press (Gdk.EventKey event) { + private bool handle_key_press ( + Gtk.EventController controller, + uint keyval, + uint keycode, + Gdk.ModifierType state + ) { //some extensions to the default navigating - bool ctrl = (event.state & Gdk.ModifierType.CONTROL_MASK) != 0; - bool shift = (event.state & Gdk.ModifierType.SHIFT_MASK) != 0; + bool ctrl = (state & Gdk.ModifierType.CONTROL_MASK) != 0; + bool shift = (state & Gdk.ModifierType.SHIFT_MASK) != 0; - if (ctrl && event.keyval == Gdk.Key.Up) { + if (ctrl && (keyval == Gdk.Key.Up)) { move_paragraph (true, shift); return true; } - if (ctrl && event.keyval == Gdk.Key.Down) { + if (ctrl && (keyval == Gdk.Key.Down)) { move_paragraph (false, shift); return true; } int old_len = number.length; // Firstly let's set the mode - switch (event.keyval) { + switch (keyval) { //mode changing case Gdk.Key.i: if (mode == Mode.INSERT) { @@ -97,12 +107,17 @@ public class Scratch.Plugins.VimEmulation : Peas.ExtensionBase, Scratch.Services } if (mode == Mode.INSERT) { - action += event.str; + //NOTE event.str` is gone in Gtk4 so use a different method + var uc = (unichar) (Gdk.keyval_to_unicode (keyval)); + if (uc.isprint ()) { + action += uc.to_string (); + } + return false; } // Parse commands - switch (event.keyval) { + switch (keyval) { //numbers case Gdk.Key.@1: number += "1"; From 34c21635ce6ecd55a8edabc1f569e0f388094be3 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 17 Jun 2026 19:28:44 +0100 Subject: [PATCH 09/12] No need to nullify controller on deactivate - plugin is destroyed --- plugins/vim-emulation/vim-emulation.vala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/vim-emulation/vim-emulation.vala b/plugins/vim-emulation/vim-emulation.vala index 480ae311e..18a6cfe4f 100644 --- a/plugins/vim-emulation/vim-emulation.vala +++ b/plugins/vim-emulation/vim-emulation.vala @@ -61,10 +61,6 @@ public class Scratch.Plugins.VimEmulation : Peas.ExtensionBase, Scratch.Services }); } - public void deactivate () { - key_controller = null; - } - private bool handle_key_press ( Gtk.EventController controller, uint keyval, From 618c97b970d8f158adb03513aee4aada59ba6d02 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 17 Jun 2026 19:45:28 +0100 Subject: [PATCH 10/12] Ensure only process sourceview key presses; cleanup --- plugins/brackets-completion/brackets-completion.vala | 7 ++++++- plugins/markdown-actions/markdown-actions.vala | 5 +++++ plugins/vim-emulation/vim-emulation.vala | 11 ++++++++++- plugins/word-completion/plugin.vala | 7 ++++++- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/plugins/brackets-completion/brackets-completion.vala b/plugins/brackets-completion/brackets-completion.vala index 5e4d3a125..9a86edac0 100644 --- a/plugins/brackets-completion/brackets-completion.vala +++ b/plugins/brackets-completion/brackets-completion.vala @@ -168,7 +168,12 @@ public class Scratch.Plugins.BracketsCompletion : Peas.ExtensionBase, Scratch.Se current_buffer.end_user_action (); } - private bool on_key_down (uint keyval, uint keycode, Gdk.ModifierType state) requires (current_buffer != null) { + private bool on_key_down ( + uint keyval, + uint keycode, + Gdk.ModifierType state + ) requires (current_source_view != null && current_buffer != null) { + if (!current_source_view.is_focus) { return false; } diff --git a/plugins/markdown-actions/markdown-actions.vala b/plugins/markdown-actions/markdown-actions.vala index 03754c2f4..aac82f871 100644 --- a/plugins/markdown-actions/markdown-actions.vala +++ b/plugins/markdown-actions/markdown-actions.vala @@ -60,6 +60,11 @@ public class Code.Plugins.MarkdownActions : Peas.ExtensionBase, Scratch.Services uint keycode, Gdk.ModifierType state ) requires (current_source != null) { + + if (!current_source.is_focus) { + return false; + } + if (!is_markdown || !Gtk.accelerator_valid (keyval, state)) { return false; } diff --git a/plugins/vim-emulation/vim-emulation.vala b/plugins/vim-emulation/vim-emulation.vala index 18a6cfe4f..b20a6cdac 100644 --- a/plugins/vim-emulation/vim-emulation.vala +++ b/plugins/vim-emulation/vim-emulation.vala @@ -61,12 +61,21 @@ public class Scratch.Plugins.VimEmulation : Peas.ExtensionBase, Scratch.Services }); } + public void deactivate () { + + } + private bool handle_key_press ( Gtk.EventController controller, uint keyval, uint keycode, Gdk.ModifierType state - ) { + ) requires (view != null) { + + if (!view.is_focus) { + return false; + } + //some extensions to the default navigating bool ctrl = (state & Gdk.ModifierType.CONTROL_MASK) != 0; bool shift = (state & Gdk.ModifierType.SHIFT_MASK) != 0; diff --git a/plugins/word-completion/plugin.vala b/plugins/word-completion/plugin.vala index 2eec8521f..0a3872629 100644 --- a/plugins/word-completion/plugin.vala +++ b/plugins/word-completion/plugin.vala @@ -128,7 +128,12 @@ public class Scratch.Plugins.Completion : Peas.ExtensionBase, Scratch.Services.A return false; } - private bool on_key_press (uint keyval, uint keycode, Gdk.ModifierType state) requires (current_view != null) { + private bool on_key_press ( + uint keyval, + uint keycode, + Gdk.ModifierType state + ) requires (current_view != null) { + var kv = keyval; if (!current_view.is_focus) { return false; From e4415a2344da6bd1f581722023ba2021b019a679 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 17 Jun 2026 19:58:42 +0100 Subject: [PATCH 11/12] Revert searchbar keypress handler to SearchBar class --- src/MainWindow.vala | 40 +------------------------------ src/Widgets/SearchBar.vala | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 3e724e3b5..78b9b1d1e 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -709,12 +709,7 @@ namespace Scratch { } private bool on_key_pressed (uint keyval, uint keycode, Gdk.ModifierType state) { - string key = Gdk.keyval_name (keyval); - if (Gdk.ModifierType.SHIFT_MASK in state) { - key = "" + key; - } - - switch (key) { + switch (Gdk.keyval_name (keyval)) { case "Escape": if (search_bar.is_revealed) { var action = Utils.action_from_group (ACTION_TOGGLE_SHOW_FIND, actions); @@ -725,39 +720,6 @@ namespace Scratch { break; } - // Handle searchbar key_press events here else consumed by Gtk.Entry widgets - if (search_bar.search_is_focused && search_bar.has_search_term) { - switch (key) { - case "Return": - case "Up": - search_bar.search_previous (); - return true; - case "Return": - case "Down": - search_bar.search_next (); - return true; - case "Tab": - search_bar.focus_replace_entry (); - return true; - default: - return false; - } - } else if (search_bar.replace_is_focused && search_bar.has_search_term) { - switch (Gdk.keyval_name (keyval)) { - case "Up": - search_bar.search_previous (); - return true; - case "Down": - search_bar.search_next (); - return true; - case "Tab": - search_bar.focus_search_entry (); - - return true; - } - - return false; - } // propagate this event to child widgets return false; } diff --git a/src/Widgets/SearchBar.vala b/src/Widgets/SearchBar.vala index c82a53c16..9ecccbd01 100644 --- a/src/Widgets/SearchBar.vala +++ b/src/Widgets/SearchBar.vala @@ -53,6 +53,7 @@ namespace Scratch.Widgets { private Gtk.SourceSearchContext? search_context; private uint update_search_label_timeout_id = 0; private Gtk.Revealer revealer; + private Gtk.EventControllerKey key_controller; public bool is_focused { get { @@ -275,6 +276,11 @@ namespace Scratch.Widgets { add (revealer); update_search_widgets (); + + key_controller = new Gtk.EventControllerKey (window) { + propagation_phase = CAPTURE + }; + key_controller.key_pressed.connect (on_key_pressed); } public void set_text_view (Scratch.Widgets.SourceView? text_view) { @@ -538,6 +544,49 @@ namespace Scratch.Widgets { search_entry.text = text; } + private bool on_key_pressed (uint keyval, uint keycode, Gdk.ModifierType state) { + /* We don't need to perform search if there is nothing to search... */ + if (search_entry.text == "") { + return false; + } + + string key = Gdk.keyval_name (keyval); + if (Gdk.ModifierType.SHIFT_MASK in state) { + key = "" + key; + } + + if (search_is_focused && has_search_term) { + switch (key) { + case "Return": + case "Up": + search_previous (); + return true; + case "Return": + case "Down": + search_next (); + return true; + case "Tab": + focus_replace_entry (); + return true; + } + } else if (replace_is_focused && has_search_term) { + switch (Gdk.keyval_name (keyval)) { + case "Up": + search_previous (); + return true; + case "Down": + search_next (); + return true; + case "Tab": + focus_search_entry (); + return true; + } + + return false; + } + + return false; + } private void cancel_update_search_widgets () { if (update_search_label_timeout_id > 0) { From 07c8429fda8eaa30c801e35393be9cb81f573274 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Wed, 17 Jun 2026 20:04:51 +0100 Subject: [PATCH 12/12] Simplify; bump copyright --- src/Widgets/SearchBar.vala | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Widgets/SearchBar.vala b/src/Widgets/SearchBar.vala index 9ecccbd01..630c96d17 100644 --- a/src/Widgets/SearchBar.vala +++ b/src/Widgets/SearchBar.vala @@ -1,7 +1,7 @@ /* * Copyright (C) 2011-2012 Lucas Baudin * 2013 Mario Guerriero - 2014-2023 elementary, Inc. (https://elementary.io) + 2014-2026 elementary, Inc. (https://elementary.io) * * This file is part of Code. * @@ -545,8 +545,11 @@ namespace Scratch.Widgets { } private bool on_key_pressed (uint keyval, uint keycode, Gdk.ModifierType state) { + if (!(search_is_focused || replace_is_focused)) { + return false; + } /* We don't need to perform search if there is nothing to search... */ - if (search_entry.text == "") { + if (!has_search_term) { return false; } @@ -555,7 +558,7 @@ namespace Scratch.Widgets { key = "" + key; } - if (search_is_focused && has_search_term) { + if (search_is_focused) { switch (key) { case "Return": case "Up": @@ -569,7 +572,7 @@ namespace Scratch.Widgets { focus_replace_entry (); return true; } - } else if (replace_is_focused && has_search_term) { + } else { switch (Gdk.keyval_name (keyval)) { case "Up": search_previous ();