Simple scroll indicator (#37162)

* scroll indicator

* 65%

* calibrate

* margin

* cleaner?

* manual clean up

* clean up

* full scroll bar

* look

* looks

* unlook

* no fade, looks good but "too much"

* clean up

* cmt
This commit is contained in:
Shane Smiskol
2026-02-11 01:15:02 -08:00
committed by GitHub
parent 3f382d6e69
commit 8ba36b76a0
3 changed files with 56 additions and 2 deletions

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:af8d5ecb6468442361462aa838a2d234b1256b8139418be8ef2962e4350cfbef
size 2176

View File

@@ -47,7 +47,7 @@ class MiciMainLayout(Widget):
self._alerts_layout,
self._home_layout,
self._onroad_layout,
], spacing=0, pad_start=0, pad_end=0)
], spacing=0, pad_start=0, pad_end=0, scroll_indicator=False)
self._scroller.set_reset_scroll_at_show(False)
# Disable scrolling when onroad is interacting with bookmark

View File

@@ -32,9 +32,52 @@ class LineSeparator(Widget):
LINE_COLOR)
class ScrollIndicator(Widget):
HORIZONTAL_MARGIN = 4
def __init__(self):
super().__init__()
self._txt_scroll_indicator = gui_app.texture("icons_mici/settings/horizontal_scroll_indicator.png", 96, 48)
self._scroll_offset: float = 0.0
self._content_size: float = 0.0
self._viewport: rl.Rectangle = rl.Rectangle(0, 0, 0, 0)
def update(self, scroll_offset: float, content_size: float, viewport: rl.Rectangle) -> None:
self._scroll_offset = scroll_offset
self._content_size = content_size
self._viewport = viewport
def _render(self, _):
# scale indicator width based on content size
indicator_w = float(np.interp(self._content_size, [1000, 3000], [300, 100]))
# position based on scroll ratio
slide_range = self._viewport.width - indicator_w
max_scroll = self._content_size - self._viewport.width
scroll_ratio = -self._scroll_offset / max_scroll
x = self._viewport.x + scroll_ratio * slide_range
# don't bounce up when NavWidget shows
y = max(self._viewport.y, 0) + self._viewport.height - self._txt_scroll_indicator.height / 2
# squeeze when overscrolling past edges
dest_left = max(x, self._viewport.x)
dest_right = min(x + indicator_w, self._viewport.x + self._viewport.width)
dest_w = max(indicator_w / 2, dest_right - dest_left)
# keep within viewport after applying minimum width
dest_left = min(dest_left, self._viewport.x + self._viewport.width - dest_w)
dest_left = max(dest_left, self._viewport.x)
src_rec = rl.Rectangle(0, 0, self._txt_scroll_indicator.width, self._txt_scroll_indicator.height)
dest_rec = rl.Rectangle(dest_left, y, dest_w, self._txt_scroll_indicator.height)
rl.draw_texture_pro(self._txt_scroll_indicator, src_rec, dest_rec, rl.Vector2(0, 0), 0.0,
rl.Color(255, 255, 255, int(255 * 0.45)))
class Scroller(Widget):
def __init__(self, items: list[Widget], horizontal: bool = True, snap_items: bool = True, spacing: int = ITEM_SPACING,
line_separator: bool = False, pad_start: int = ITEM_SPACING, pad_end: int = ITEM_SPACING):
line_separator: bool = False, pad_start: int = ITEM_SPACING, pad_end: int = ITEM_SPACING,
scroll_indicator: bool = True):
super().__init__()
self._items: list[Widget] = []
self._horizontal = horizontal
@@ -64,6 +107,9 @@ class Scroller(Widget):
self.scroll_panel = GuiScrollPanel2(self._horizontal, handle_out_of_bounds=not self._snap_items)
self._scroll_enabled: bool | Callable[[], bool] = True
self._show_scroll_indicator = scroll_indicator
self._scroll_indicator = ScrollIndicator()
for item in items:
self.add_widget(item)
@@ -240,6 +286,11 @@ class Scroller(Widget):
rl.end_scissor_mode()
# Draw scroll indicator
if self._show_scroll_indicator and self._horizontal and len(self._visible_items) > 0:
self._scroll_indicator.update(self._scroll_offset, self._content_size, self._rect)
self._scroll_indicator.render()
def show_event(self):
super().show_event()
if self._reset_scroll_at_show: