Skip to content

Commit a3f115d

Browse files
committed
sidebar: Selected object follows cursor when searching
1 parent 5d07cd2 commit a3f115d

2 files changed

Lines changed: 54 additions & 12 deletions

File tree

odbcli/app.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ def __init__(
5757
# Added for efficiency (no need to traverse unless necessary). Updated
5858
# from the main thread always, so no need for locking.
5959
self.obj_list_changed: bool = True
60-
# This field presents the idx of the currently selected object in the
60+
# This field is a list with two elements. The first is the index of
61+
# the currently selected object (0-indexed). The second is the
62+
# index of the currently selected object in the
6163
# list of objects where each object is counted with length of characters
6264
# in name + 1 multiplicity. So for example a list of objects
6365
# A
@@ -68,7 +70,9 @@ def __init__(
6870
# This is used to track the cursor position in the sidebar document
6971
# It is recorded here, rather than elsewhere because we can track it
7072
# here far more efficiently (select_next, and select_previous).
71-
self._selected_obj_idx = 0
73+
# It is important that all methods of this class that manipulate the
74+
# currently selected object also update this index.
75+
self._selected_obj_idx = [0, 0]
7276
dsns = list(datasources().keys())
7377
if len(dsns) < 1:
7478
sys.exit("No datasources found ... exiting.")
@@ -103,16 +107,35 @@ def selected_object(self) -> myDBObject:
103107
def selected_object(self, obj) -> None:
104108
""" Avoid using / computationally expensive.
105109
Instead try using select_next / select_previous if possible.
110+
Will update _selected_obj_idx appropriately.
106111
"""
112+
cursor = 0
107113
idx = 0
108114
o = self.obj_list[0]
109115
self._selected_object = obj
110116
while o is not self._selected_object:
111117
if not o.next_object:
112118
raise IndexError
113-
idx += len(o.name) + 1
119+
cursor += len(o.name) + 1
120+
idx += 1
114121
o = o.next_object
115-
self._selected_obj_idx = idx
122+
self._selected_obj_idx = [idx, cursor]
123+
124+
def select(self, idx) -> None:
125+
""" Select the [i]-th object in the list. Will also update
126+
_selected_obj_idx appropriately.
127+
"""
128+
counter = 0
129+
cursor = 0
130+
o = self.obj_list[0]
131+
while counter < idx:
132+
if not o.next_object:
133+
raise IndexError
134+
counter += 1
135+
cursor += len(o.name) + 1
136+
o = o.next_object
137+
self._selected_object = o
138+
self._selected_obj_idx = [idx, cursor]
116139

117140
@property
118141
def selected_object_idx(self):
@@ -272,14 +295,17 @@ def _(event):
272295
inc = len(self.selected_object.name) + 1 # newline character
273296
if obj is self.obj_list[0]:
274297
idx = 0
298+
cursor = 0
275299
while obj is not self._selected_object:
276300
if not obj.next_object:
277301
raise IndexError
278-
idx += len(obj.name) + 1
302+
cursor += len(obj.name) + 1
303+
idx += 1
279304
obj = obj.next_object
280-
self._selected_obj_idx = idx
305+
self._selected_obj_idx = [idx, cursor]
281306
else:
282-
self._selected_obj_idx -= inc
307+
self._selected_obj_idx[0] -= 1
308+
self._selected_obj_idx[1] -= inc
283309

284310
@kb.add("down", filter=sidebar_visible)
285311
@kb.add("c-n", filter=sidebar_visible)
@@ -289,9 +315,10 @@ def _(event):
289315
inc = len(self.selected_object.name) + 1 # newline character
290316
self.select_next()
291317
if self.selected_object is self.obj_list[0]:
292-
self._selected_obj_idx = 0
318+
self._selected_obj_idx = [0, 0]
293319
else:
294-
self._selected_obj_idx += inc
320+
self._selected_obj_idx[0] += 1
321+
self._selected_obj_idx[1] += inc
295322

296323
@kb.add("enter", filter = sidebar_visible)
297324
def _(event):

odbcli/sidebar.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ def __init__(
4848
self.otype = otype
4949
self.level = level
5050
self.selected: bool = False
51-
self.logger = getLogger(__name__)
5251

5352
def _expand_internal(self) -> None:
5453
"""
@@ -533,9 +532,23 @@ def tokenize_obj(obj: "myDBObject") -> StyleAndTextTuples:
533532
search_buffer = search_buffer,
534533
ignore_case = True
535534
)
535+
def _buffer_pos_changed(buff):
536+
""" This callback gets executed after cursor position changes. Most
537+
of the time we register a key-press (up / down), we change the
538+
selected object and as a result of that the cursor changes. By that
539+
time we don't need to updat the selected object (cursor changed as
540+
a result of the selected object being updated). The one exception
541+
is when searching the sidebar buffer. When this happens the cursor
542+
moves ahead of the selected object. When that happens, here we
543+
update the selected object to follow suit.
544+
"""
545+
if buff.document.cursor_position_row != my_app.selected_object_idx[0]:
546+
my_app.select(buff.document.cursor_position_row)
547+
536548
sidebar_buffer = Buffer(
537549
name = "sidebarbuffer",
538550
read_only = True,
551+
on_cursor_position_changed = _buffer_pos_changed
539552
)
540553

541554
class myLexer(Lexer):
@@ -577,7 +590,7 @@ def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone":
577590
def create_content(self, width: int, height: Optional[int]) -> UIContent:
578591
# Only traverse the obj_list if it has been expanded / collapsed
579592
if not my_app.obj_list_changed:
580-
self.buffer.cursor_position = my_app.selected_object_idx
593+
self.buffer.cursor_position = my_app.selected_object_idx[1]
581594
return super().create_content(width, height)
582595

583596
res = []
@@ -589,12 +602,14 @@ def create_content(self, width: int, height: Optional[int]) -> UIContent:
589602

590603
self.lexer.add_objects(res)
591604
self.buffer.set_document(Document(
592-
text = "\n".join([a.name for a in res]), cursor_position = my_app.selected_object_idx), True)
605+
text = "\n".join([a.name for a in res]), cursor_position = my_app.selected_object_idx[1]), True)
593606
# Reset obj_list_changed flag, now that we have had a chance to
594607
# regenerate the sidebar document content
595608
my_app.obj_list_changed = False
596609
return super().create_content(width, height)
597610

611+
612+
598613
sidebar_control = myControl(
599614
buffer = sidebar_buffer,
600615
lexer = sidebar_lexer,

0 commit comments

Comments
 (0)