Skip to content

Commit

Permalink
scroll_x() methods of ScrollView made public.
Browse files Browse the repository at this point in the history
Added `scroll_to_rect()` method to ScrollView.
Horizontal cursor movement on text input  resets `_last_x`.
  • Loading branch information
salt-die committed Feb 24, 2024
1 parent 416eda2 commit 2af9a7f
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 60 deletions.
28 changes: 13 additions & 15 deletions src/batgrl/gadgets/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,7 @@ def cursor(self, cursor: int):
if self.parent is None:
return

console = self.console
rel_x = console._container.x + console._prompt.width + cursor
port_width = console._scroll_view.port_width

if cursor == 0:
console._scroll_view.horizontal_proportion = 0
elif rel_x < 0:
console._scroll_view._scroll_right(rel_x)
elif rel_x >= port_width:
console._scroll_view._scroll_right(rel_x - port_width + 1)
self.console._scroll_view.scroll_to_rect((0, cursor + self.x), (1, 1))

if self.is_selecting:
self._selection_end = self.cursor
Expand Down Expand Up @@ -193,6 +184,9 @@ def on_key(self, key_event: KeyEvent) -> bool | None:
if key_event == KeyEvent(Key.Tab, Mods.NO_MODS):
self._tab()
return True
if key_event == KeyEvent(Key.Left, Mods.NO_MODS) and self.cursor == 0:
self.console._scroll_view.horizontal_proportion = 0
return True
return super().on_key(key_event)

def on_paste(self, paste_event: PasteEvent) -> bool | None:
Expand Down Expand Up @@ -471,12 +465,16 @@ def fix_input_pos():
self._input.left = self._prompt.right

def update_bars():
self._scroll_view.show_vertical_bar = (
self._container.height > self._scroll_view.port_height
)
self._scroll_view.show_horizontal_bar = (
self._container.width > self._scroll_view.port_width
show_vertical_bar = self._container.height > self._scroll_view.port_height
show_horizontal_bar = self._container.width > self._scroll_view.port_width
update_cursor = (
show_vertical_bar != self._scroll_view.show_vertical_bar
or show_horizontal_bar != self._scroll_view.show_horizontal_bar
)
self._scroll_view.show_vertical_bar = show_vertical_bar
self._scroll_view.show_horizontal_bar = show_horizontal_bar
if update_cursor:
self._input.cursor = self._input.cursor

self._prompt.bind("size", fix_input_pos)
self._prompt.bind("pos", fix_input_pos)
Expand Down
6 changes: 1 addition & 5 deletions src/batgrl/gadgets/file_chooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,7 @@ def on_key(self, key_event):
return super().on_key(key_event)

if self.selected_node is not None:
top = self.selected_node.top + self.top
if top < 0:
self.parent._scroll_up(-top)
elif top >= self.parent.height - 1:
self.parent._scroll_down(self.parent.height - top)
self.parent.scroll_to_rect(self.selected_node.pos)

return True

Expand Down
97 changes: 72 additions & 25 deletions src/batgrl/gadgets/scroll_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,16 @@ class ScrollView(Themable, Grabbable, Gadget):
Methods
-------
scroll_left(n=1)
Scroll the view left `n` characters.
scroll_right(n=1)
Scroll the view right `n` characters.
scroll_up(n=1)
Scroll the view up `n` lines.
scroll_down(n=1)
Scroll the view down `n` lines.
scroll_to_rect(pos, size=(1, 1))
Scroll the view so that a given rect is visible.
update_theme()
Paint the gadget with current theme.
grab(mouse_event)
Expand Down Expand Up @@ -634,24 +644,38 @@ def on_key(self, key_event: KeyEvent) -> bool | None:

match key_event.key:
case "up":
self._scroll_up()
self.scroll_up()
case "down":
self._scroll_down()
self.scroll_down()
case "left":
self._scroll_left()
self.scroll_left()
case "right":
self._scroll_right()
self.scroll_right()
case _:
return super().on_key(key_event)

return True

def grab_update(self, mouse_event: MouseEvent):
"""Scroll on grab update."""
self._scroll_up(self.mouse_dy)
self._scroll_left(self.mouse_dx)
self.scroll_up(self.mouse_dy)
self.scroll_left(self.mouse_dx)

def _scroll_left(self, n=1):
def on_mouse(self, mouse_event: MouseEvent) -> bool | None:
"""Scroll on mouse wheel."""
if self.scrollwheel_enabled and self.collides_point(mouse_event.position):
match mouse_event.event_type:
case MouseEventType.SCROLL_UP:
self.scroll_up()
return True
case MouseEventType.SCROLL_DOWN:
self.scroll_down()
return True

return super().on_mouse(mouse_event)

def scroll_left(self, n=1):
"""Scroll the view left `n` characters."""
if self._view is not None:
if self.total_horizontal_distance == 0:
self.horizontal_proportion = 0
Expand All @@ -660,10 +684,12 @@ def _scroll_left(self, n=1):
(-self.view.left - n) / self.total_horizontal_distance, 0, 1
)

def _scroll_right(self, n=1):
self._scroll_left(-n)
def scroll_right(self, n=1):
"""Scroll the view right `n` characters."""
self.scroll_left(-n)

def _scroll_up(self, n=1):
def scroll_up(self, n=1):
"""Scroll the view up `n` lines."""
if self._view is not None:
if self.total_vertical_distance == 0:
self.vertical_proportion = 0
Expand All @@ -672,18 +698,39 @@ def _scroll_up(self, n=1):
(-self.view.top - n) / self.total_vertical_distance, 0, 1
)

def _scroll_down(self, n=1):
self._scroll_up(-n)

def on_mouse(self, mouse_event: MouseEvent) -> bool | None:
"""Scroll on mouse wheel."""
if self.scrollwheel_enabled and self.collides_point(mouse_event.position):
match mouse_event.event_type:
case MouseEventType.SCROLL_UP:
self._scroll_up()
return True
case MouseEventType.SCROLL_DOWN:
self._scroll_down()
return True

return super().on_mouse(mouse_event)
def scroll_down(self, n=1):
"""Scroll the view down `n` lines."""
self.scroll_up(-n)

def scroll_to_rect(self, pos: Point, size: Size = Size(1, 1)):
"""
Scroll the view so that a given rect is visible.
The rect is assumed to be within the view's bounding box.
Parameters
----------
pos : Point
Position of rect.
size : Size, default: Size(1, 1)
Size of rect.
"""
if self.view is None:
return

y, x = pos
h, w = size
h = clamp(h, 1, None)
w = clamp(w, 1, None)
gy, gx = self.view.pos
ay, ax = gy + y, gx + x
if ay < 0:
self.scroll_up(-ay)
elif ay + h >= self.port_height:
self.scroll_down(ay + h - self.port_height)

if ax < 0:
self.scroll_left(-ax)
elif ax + w >= self.port_width:
self.scroll_right(ax + w - self.port_width)
22 changes: 7 additions & 15 deletions src/batgrl/gadgets/text_pad.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,23 +361,9 @@ def cursor(self, cursor: Point):
"""After setting cursor position, move pad so that cursor is visible."""
y, x = cursor
self._cursor.pos = Point(y, x)

max_y = self._scroll_view.port_height - 1
if (rel_y := y + self._pad.y) > max_y:
self._scroll_view._scroll_down(rel_y - max_y)
elif rel_y < 0:
self._scroll_view._scroll_up(-rel_y)

max_x = self._scroll_view.port_width - 1
rel_x = x + self._pad.x
if rel_x > max_x:
self._scroll_view._scroll_right(rel_x - max_x)
elif rel_x < 0:
self._scroll_view._scroll_right(rel_x)

self._scroll_view.scroll_to_rect(cursor)
if self.is_selecting:
self._selection_end = self.cursor

self._highlight_selection()

def _highlight_selection(self):
Expand Down Expand Up @@ -624,6 +610,7 @@ def move_cursor_down(self, n: int = 1):

def move_word_left(self):
"""Move cursor a word left."""
self._last_x = None
last_x = self.cursor.x
first_char_found = False
while True:
Expand All @@ -646,6 +633,7 @@ def move_word_left(self):

def move_word_right(self):
"""Move cursor a word right."""
self._last_x = None
last_x = self.cursor.x
first_char_found = False
while True:
Expand Down Expand Up @@ -798,10 +786,12 @@ def _pgdn(self):

def _home(self):
self.unselect()
self._last_x = None
self.cursor = self.cursor.y, 0

def _end(self):
self.unselect()
self._last_x = None
y = self.cursor.y
self.cursor = y, self._line_lengths[y]

Expand Down Expand Up @@ -839,10 +829,12 @@ def _shift_pgdn(self):

def _shift_home(self):
self.select()
self._last_x = None
self.cursor = self.cursor.y, 0

def _shift_end(self):
self.select()
self._last_x = None
y = self.cursor.y
self.cursor = y, self._line_lengths[y]

Expand Down

0 comments on commit 2af9a7f

Please sign in to comment.