From 096841e98a456effd1b19b5de56a341e82989a39 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Thu, 26 Sep 2024 00:22:10 -0500 Subject: [PATCH] Handling completion keyboard movement --- .../base/notebook/editor_controller.py | 2 + .../base/sourceview/key_input_controller.py | 33 +++--- src/core/widgets/completion_item.py | 7 +- src/core/widgets/completion_view.py | 102 ++++++++++++++++-- 4 files changed, 116 insertions(+), 28 deletions(-) diff --git a/src/core/widgets/base/notebook/editor_controller.py b/src/core/widgets/base/notebook/editor_controller.py index 77a3285..5c2324a 100644 --- a/src/core/widgets/base/notebook/editor_controller.py +++ b/src/core/widgets/base/notebook/editor_controller.py @@ -5,6 +5,7 @@ import gi gi.require_version('Gtk', '3.0') gi.require_version('GtkSource', '4') from gi.repository import Gtk +from gi.repository import GLib from gi.repository import GtkSource # Application imports @@ -68,6 +69,7 @@ class EditorControllerMixin(KeyInputController, EditorEventsMixin): source_view.completion_view.add_completion_item(ci) source_view.completion_view.show_all() + GLib.idle_add( source_view.completion_view.select_first_row ) # completion = source_view.get_completion() diff --git a/src/core/widgets/base/sourceview/key_input_controller.py b/src/core/widgets/base/sourceview/key_input_controller.py index 53233fe..f728690 100644 --- a/src/core/widgets/base/sourceview/key_input_controller.py +++ b/src/core/widgets/base/sourceview/key_input_controller.py @@ -31,9 +31,9 @@ class KeyInputController: return True if keyname in [ "slash", "Up", "Down", "m", "z", "y" ]: - if keyname == "Up": + if keyname == "Up" and not self.completion_view.get_parent(): self.keyboard_move_lines_up() - if keyname == "Down": + if keyname == "Down" and not self.completion_view.get_parent(): self.keyboard_move_lines_down() if keyname == "z": @@ -47,10 +47,22 @@ class KeyInputController: if keyname in [ "Up", "Down", "Left", "Right" ]: return True - if keyname in [ "Return", "Enter", "Up", "Down" ]: + if keyname in [ "Left", "Right" ]: if self.completion_view.get_parent() and self.completion_view.is_visible(): + # Needed to escape our completion widget and get back ibeam + if keyname == "Left": + self.remove( self.completion_view ) + GLib.idle_add(self.grab_focus) + if keyname == "Right": + self.remove( self.completion_view ) + GLib.idle_add(self.grab_focus) + # ^ Needed to escape our completion widget and get back ibeam + return True + if keyname in [ "Return", "Enter" ]: + if self.completion_view.get_parent() and self.completion_view.is_visible(): + return True if len(self._multi_insert_marks) > 0: if keyname == "BackSpace": @@ -129,23 +141,12 @@ class KeyInputController: self.keyboard_clear_marks() - if keyname in [ "Return", "Enter", "Up", "Down", "Left", "Right" ]: + if keyname in [ "Return", "Enter" ]: if self.completion_view.get_parent() and self.completion_view.is_visible(): - if keyname in {"Return", "Enter"}: - self.completion_view.activate_completion() - if keyname == "UP": - self.completion_view.move_selection_up() - if keyname == "Down": - self.completion_view.move_selection_down() - if keyname == "Left": - self.remove( self.completion_view ) - if keyname == "Right": - self.remove( self.completion_view ) + self.completion_view.activate_completion() return True - - if keyname in {"Return", "Enter"}: if len(self._multi_insert_marks) > 0: self.begin_user_action(buffer) with buffer.freeze_notify(): diff --git a/src/core/widgets/completion_item.py b/src/core/widgets/completion_item.py index 150667d..cf81187 100644 --- a/src/core/widgets/completion_item.py +++ b/src/core/widgets/completion_item.py @@ -9,7 +9,7 @@ from gi.repository import Gtk -class CompletionItem(Gtk.Button): +class CompletionItem(Gtk.Label): def __init__(self): super(CompletionItem, self).__init__() @@ -27,7 +27,7 @@ class CompletionItem(Gtk.Button): ctx.add_class("completion-item") def _setup_signals(self): - self.connect("clicked", self._do_completion) + ... def populate_completion_item(self, item): @@ -39,6 +39,3 @@ class CompletionItem(Gtk.Button): if "additionalTextEdits" in keys: self.additionalTextEdits = item["additionalTextEdits"] - - def _do_completion(self, button): - ... \ No newline at end of file diff --git a/src/core/widgets/completion_view.py b/src/core/widgets/completion_view.py index 98fe9b5..bcb0691 100644 --- a/src/core/widgets/completion_view.py +++ b/src/core/widgets/completion_view.py @@ -3,7 +3,10 @@ # Lib imports import gi gi.require_version('Gtk', '3.0') +gi.require_version('Gdk', '3.0') from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import GLib # Application imports from .completion_item import CompletionItem @@ -26,21 +29,58 @@ class CompletionView(Gtk.ScrolledWindow): def _setup_styling(self): ctx = self.get_style_context() ctx.add_class("completion-view") - self.set_size_request(320, 320) + self.set_margin_top(10) + self.set_margin_bottom(10) + self.set_margin_start(10) + self.set_margin_end(10) + self.set_size_request(320, -1) + self.set_min_content_height(120) + self.set_max_content_height(480) + + self.set_overlay_scrolling(False) + self.set_policy( Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC ) # hbar, vbar def _setup_signals(self): ... def _load_widgets(self): viewport = Gtk.Viewport() - self.button_box = Gtk.Box() + self.button_box = Gtk.ListBox() - self.button_box.set_orientation( Gtk.Orientation.VERTICAL ) self.button_box.set_hexpand( True ) + self.button_box.set_placeholder( Gtk.Label(label = "No completion data...") ) + self.button_box.set_selection_mode( Gtk.SelectionMode.BROWSE ) + + self.button_box.connect("key-press-event", self._key_press_event) viewport.add(self.button_box) self.add(viewport) - + + + # This is depressing but only way I can get to scroll with items getting selected. + # Cannot figure out how to just manually scroll widget into view with code. + def _key_press_event(self, widget, eve): + keyname = Gdk.keyval_name(eve.keyval) + modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK) + is_control = True if modifiers & Gdk.ModifierType.CONTROL_MASK else False + is_shift = True if modifiers & Gdk.ModifierType.SHIFT_MASK else False + + if is_control: + return True + + if keyname in [ "Up" ]: + self.move_selection_up() + return True + + if keyname in [ "Down" ]: + self.move_selection_down() + return True + + if keyname in [ "Enter", "Return" ]: + self.activate_completion() + return True + + def add_completion_item(self, item: CompletionItem): self.button_box.add(item) @@ -49,11 +89,59 @@ class CompletionView(Gtk.ScrolledWindow): self.button_box.remove(child) def activate_completion(self): - ... + row = self.button_box.get_selected_row() def move_selection_up(self): - ... + index = -1 + srow = self.button_box.get_selected_row() + + if not srow: + self.select_last_row() + return + + for i, child in enumerate( self.button_box.get_children() ): + if child == srow: + index = i - 1 + break + + if index == -1: + index = len( self.button_box.get_children() ) - 1 + + row = self.button_box.get_row_at_index(index) + self.select_and_scroll_to_view(row) def move_selection_down(self): - ... + index = -1 + srow = self.button_box.get_selected_row() + if not srow: + self.select_first_row() + return + + for i, child in enumerate( self.button_box.get_children() ): + if child == srow: + index = i + 1 + break + + if index > (len( self.button_box.get_children() ) - 1): + index = 0 + + row = self.button_box.get_row_at_index(index) + self.select_and_scroll_to_view(row) + + + def select_first_row(self): + row = self.button_box.get_row_at_y(0) + if not row: return + self.select_and_scroll_to_view(row) + + def select_last_row(self): + row = self.button_box.get_row_at_y( len( self.button_box.get_children() ) - 1) + if not row: return + self.select_and_scroll_to_view(row) + + def select_and_scroll_to_view(self, row): + self.button_box.select_row(row) + GLib.idle_add( row.grab_focus ) + # row.set_focus(True) + # GLib.idle_add( row.set_focus, True ) \ No newline at end of file