From 9f18dfe7a89be9929d2decd79ae2718ba4bdb3f0 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 19 Nov 2024 14:41:38 +0000 Subject: [PATCH 1/8] smooth scrolling --- src/textual/_compositor.py | 2 +- src/textual/_xterm_parser.py | 29 +++++++-- src/textual/driver.py | 8 +-- src/textual/drivers/linux_driver.py | 10 ++- src/textual/events.py | 96 ++++++++++++++++++----------- src/textual/screen.py | 12 ++-- src/textual/scrollbar.py | 19 +++--- src/textual/widget.py | 4 +- 8 files changed, 113 insertions(+), 67 deletions(-) diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index 7aac04393c..ce926209d5 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -840,7 +840,7 @@ def get_widget_at(self, x: int, y: int) -> tuple[Widget, Region]: contains = Region.contains if len(self.layers_visible) > y >= 0: - for widget, cropped_region, region in self.layers_visible[y]: + for widget, cropped_region, region in self.layers_visible[int(y)]: if contains(cropped_region, x, y) and widget.visible: return widget, region raise errors.NoWidget(f"No widget under screen coordinate ({x}, {y})") diff --git a/src/textual/_xterm_parser.py b/src/textual/_xterm_parser.py index 6382413c77..5331c3742b 100644 --- a/src/textual/_xterm_parser.py +++ b/src/textual/_xterm_parser.py @@ -50,8 +50,11 @@ class XTermParser(Parser[Message]): _re_sgr_mouse = re.compile(r"\x1b\[<(\d+);(\d+);(\d+)([Mm])") def __init__(self, debug: bool = False) -> None: - self.last_x = 0 - self.last_y = 0 + self.last_x = 0.0 + self.last_y = 0.0 + self.mouse_pixels = False + self.terminal_size: tuple[int, int] | None = None + self.terminal_pixel_size: tuple[int, int] | None = None self._debug_log_file = open("keys.log", "at") if debug else None super().__init__() self.debug_log("---") @@ -70,8 +73,18 @@ def parse_mouse_code(self, code: str) -> Message | None: if sgr_match: _buttons, _x, _y, state = sgr_match.groups() buttons = int(_buttons) - x = int(_x) - 1 - y = int(_y) - 1 + x = float(int(_x) - 1) + y = float(int(_y) - 1) + if ( + self.mouse_pixels + and self.terminal_pixel_size is not None + and self.terminal_size is not None + ): + x_ratio = self.terminal_pixel_size[0] / self.terminal_size[0] + y_ratio = self.terminal_pixel_size[1] / self.terminal_size[1] + x /= x_ratio + y /= y_ratio + delta_x = x - self.last_x delta_y = y - self.last_y self.last_x = x @@ -120,6 +133,9 @@ def parse( def on_token(token: Message) -> None: """Hook to log events.""" self.debug_log(str(token)) + if isinstance(token, events.Resize): + self.terminal_size = token.size + self.terminal_pixel_size = token.pixel_size token_callback(token) def on_key_token(event: events.Key) -> None: @@ -228,6 +244,10 @@ def send_escape() -> None: (int(width), int(height)), (int(pixel_width), int(pixel_height)), ) + + self.terminal_size = resize_event.size + self.terminal_pixel_size = resize_event.pixel_size + self.mouse_pixels = True on_token(resize_event) break @@ -268,7 +288,6 @@ def send_escape() -> None: if mode_id == "2026" and setting_parameter > 0: on_token(messages.TerminalSupportsSynchronizedOutput()) elif mode_id == "2048" and not IS_ITERM: - # TODO: remove "and not IS_ITERM" when https://gitlab.com/gnachman/iterm2/-/issues/11961 is fixed in_band_event = messages.TerminalSupportInBandWindowResize.from_setting_parameter( setting_parameter ) diff --git a/src/textual/driver.py b/src/textual/driver.py index 1fa57c406b..6197893413 100644 --- a/src/textual/driver.py +++ b/src/textual/driver.py @@ -89,10 +89,10 @@ def process_message(self, message: messages.Message) -> None: else: offset_x, offset_y = self.cursor_origin if isinstance(message, events.MouseEvent): - message.x -= offset_x - message.y -= offset_y - message.screen_x -= offset_x - message.screen_y -= offset_y + message._x -= offset_x + message._y -= offset_y + message._screen_x -= offset_x + message._screen_y -= offset_y if isinstance(message, events.MouseDown): if message.button: diff --git a/src/textual/drivers/linux_driver.py b/src/textual/drivers/linux_driver.py index 510b0992f0..a247692b29 100644 --- a/src/textual/drivers/linux_driver.py +++ b/src/textual/drivers/linux_driver.py @@ -62,6 +62,7 @@ def __init__( # keep track of this. self._must_signal_resume = False self._in_band_window_resize = False + self._mouse_pixels = False # Put handlers for SIGTSTP and SIGCONT in place. These are necessary # to support the user pressing Ctrl+Z (or whatever the dev might @@ -134,6 +135,12 @@ def _enable_mouse_support(self) -> None: # Note: E.g. lxterminal understands 1000h, but not the urxvt or sgr # extensions. + def _enable_mouse_pixels(self) -> None: + if not self._mouse: + return + self.write("\x1b[?1016h") + self._mouse_pixels = True + def _enable_bracketed_paste(self) -> None: """Enable bracketed paste mode.""" self.write("\x1b[?2004h") @@ -441,7 +448,7 @@ def process_selector_events( try: for event in feed(""): pass - except ParseError: + except (EOFError, ParseError): pass def process_message(self, message: Message) -> None: @@ -453,6 +460,7 @@ def process_message(self, message: Message) -> None: self._in_band_window_resize = message.supported elif message.enabled: self._in_band_window_resize = message.supported + self._enable_mouse_pixels() # Send up-to-date message super().process_message( TerminalSupportInBandWindowResize( diff --git a/src/textual/events.py b/src/textual/events.py index 6e7a21e8da..897612c268 100644 --- a/src/textual/events.py +++ b/src/textual/events.py @@ -343,44 +343,44 @@ class MouseEvent(InputEvent, bubble=True): __slots__ = [ "widget", - "x", - "y", - "delta_x", - "delta_y", + "_x", + "_y", + "_delta_x", + "_delta_y", "button", "shift", "meta", "ctrl", - "screen_x", - "screen_y", + "_screen_x", + "_screen_y", "_style", ] def __init__( self, widget: Widget | None, - x: int, - y: int, - delta_x: int, - delta_y: int, + x: float, + y: float, + delta_x: float, + delta_y: float, button: int, shift: bool, meta: bool, ctrl: bool, - screen_x: int | None = None, - screen_y: int | None = None, + screen_x: float | None = None, + screen_y: float | None = None, style: Style | None = None, ) -> None: super().__init__() self.widget: Widget | None = widget """The widget under the mouse at the time of a click.""" - self.x = x + self._x = x """The relative x coordinate.""" - self.y = y + self._y = y """The relative y coordinate.""" - self.delta_x = delta_x + self._delta_x = delta_x """Change in x since the last message.""" - self.delta_y = delta_y + self._delta_y = delta_y """Change in y since the last message.""" self.button = button """Indexed of the pressed button.""" @@ -390,42 +390,66 @@ def __init__( """`True` if the meta key is pressed.""" self.ctrl = ctrl """`True` if the ctrl key is pressed.""" - self.screen_x = x if screen_x is None else screen_x + self._screen_x = x if screen_x is None else screen_x """The absolute x coordinate.""" - self.screen_y = y if screen_y is None else screen_y + self._screen_y = y if screen_y is None else screen_y """The absolute y coordinate.""" self._style = style or Style() + @property + def x(self) -> int: + return int(self._x) + + @property + def y(self) -> int: + return int(self._y) + + @property + def delta_x(self) -> int: + return int(self._delta_x) + + @property + def delta_y(self) -> int: + return int(self._delta_y) + + @property + def screen_x(self) -> int: + return int(self._screen_x) + + @property + def screen_y(self) -> int: + return int(self._screen_y) + @classmethod def from_event( cls: Type[MouseEventT], widget: Widget, event: MouseEvent ) -> MouseEventT: new_event = cls( widget, - event.x, - event.y, - event.delta_x, - event.delta_y, + event._x, + event._y, + event._delta_x, + event._delta_y, event.button, event.shift, event.meta, event.ctrl, - event.screen_x, - event.screen_y, + event._screen_x, + event._screen_y, event._style, ) return new_event def __rich_repr__(self) -> rich.repr.Result: yield self.widget - yield "x", self.x - yield "y", self.y - yield "delta_x", self.delta_x, 0 - yield "delta_y", self.delta_y, 0 + yield "x", self._x + yield "y", self._y + yield "delta_x", self._delta_x, 0 + yield "delta_y", self._delta_y, 0 if self.screen_x != self.x: - yield "screen_x", self.screen_x + yield "screen_x", self._screen_x if self.screen_y != self.y: - yield "screen_y", self.screen_y + yield "screen_y", self._screen_y yield "button", self.button, 0 yield "shift", self.shift, False yield "meta", self.meta, False @@ -492,16 +516,16 @@ def get_content_offset_capture(self, widget: Widget) -> Offset: def _apply_offset(self, x: int, y: int) -> MouseEvent: return self.__class__( self.widget, - x=self.x + x, - y=self.y + y, - delta_x=self.delta_x, - delta_y=self.delta_y, + x=self._x + x, + y=self._y + y, + delta_x=self._delta_x, + delta_y=self._delta_y, button=self.button, shift=self.shift, meta=self.meta, ctrl=self.ctrl, - screen_x=self.screen_x, - screen_y=self.screen_y, + screen_x=self._screen_x, + screen_y=self._screen_y, style=self.style, ) diff --git a/src/textual/screen.py b/src/textual/screen.py index 81cc3c733a..85dd7f0ee1 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -1377,16 +1377,16 @@ def _translate_mouse_move_event( """ return events.MouseMove( event.widget, - event.x - region.x, - event.y - region.y, - event.delta_x, - event.delta_y, + event._x - region.x, + event._y - region.y, + event._delta_x, + event._delta_y, event.button, event.shift, event.meta, event.ctrl, - screen_x=event.screen_x, - screen_y=event.screen_y, + screen_x=event._screen_x, + screen_y=event._screen_y, style=event.style, ) diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index 78aa968d00..68165aca61 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -363,22 +363,17 @@ async def _on_mouse_move(self, event: events.MouseMove) -> None: y: float | None = None if self.vertical: virtual_size = self.window_virtual_size - y = round( - self.grabbed_position - + ( - (event.screen_y - self.grabbed.y) - * (virtual_size / self.window_size) - ) + y = self.grabbed_position + ( + (event._screen_y - self.grabbed.y) + * (virtual_size / self.window_size) ) else: virtual_size = self.window_virtual_size - x = round( - self.grabbed_position - + ( - (event.screen_x - self.grabbed.x) - * (virtual_size / self.window_size) - ) + x = self.grabbed_position + ( + (event._screen_x - self.grabbed.x) + * (virtual_size / self.window_size) ) + print(event) self.post_message(ScrollTo(x=x, y=y)) event.stop() diff --git a/src/textual/widget.py b/src/textual/widget.py index 5427109b7b..6ded8bd50a 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -1662,12 +1662,12 @@ def watch_hover_style( def watch_scroll_x(self, old_value: float, new_value: float) -> None: self.horizontal_scrollbar.position = round(new_value) - if round(old_value) != round(new_value): + if (old_value) != (new_value): self._refresh_scroll() def watch_scroll_y(self, old_value: float, new_value: float) -> None: self.vertical_scrollbar.position = round(new_value) - if round(old_value) != round(new_value): + if (old_value) != (new_value): self._refresh_scroll() def validate_scroll_x(self, value: float) -> float: From 70dc4f232a53ce1f2cb914dd10b2826f5e8ec965 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 19 Nov 2024 16:42:28 +0000 Subject: [PATCH 2/8] smooth scroll view --- src/textual/scroll_view.py | 8 ++++---- src/textual/scrollbar.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/textual/scroll_view.py b/src/textual/scroll_view.py index 42c89ebb23..1725fe9bde 100644 --- a/src/textual/scroll_view.py +++ b/src/textual/scroll_view.py @@ -33,13 +33,13 @@ def is_scrollable(self) -> bool: return True def watch_scroll_x(self, old_value: float, new_value: float) -> None: - if self.show_horizontal_scrollbar and round(old_value) != round(new_value): - self.horizontal_scrollbar.position = round(new_value) + if self.show_horizontal_scrollbar and old_value != new_value: + self.horizontal_scrollbar.position = new_value self.refresh() def watch_scroll_y(self, old_value: float, new_value: float) -> None: - if self.show_vertical_scrollbar and round(old_value) != round(new_value): - self.vertical_scrollbar.position = round(new_value) + if self.show_vertical_scrollbar and (old_value) != (new_value): + self.vertical_scrollbar.position = new_value self.refresh() def on_mount(self): diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index 68165aca61..eedbdf0a50 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -259,7 +259,7 @@ def __init__( window_virtual_size: Reactive[int] = Reactive(100) window_size: Reactive[int] = Reactive(0) - position: Reactive[int] = Reactive(0) + position: Reactive[float] = Reactive(0) mouse_over: Reactive[bool] = Reactive(False) grabbed: Reactive[Offset | None] = Reactive(None) From 8e378fa5044a2e3d2fc06f967a9bc918fa484198 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Sat, 30 Nov 2024 09:04:11 +0000 Subject: [PATCH 3/8] docs(digits): fix update docstring raises type Fix the docstring for `Digits.update`, which raises a TypeError rather than a ValueError if the value is not a str. --- src/textual/widgets/_digits.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/textual/widgets/_digits.py b/src/textual/widgets/_digits.py index b85fe27525..86492ebe8f 100644 --- a/src/textual/widgets/_digits.py +++ b/src/textual/widgets/_digits.py @@ -6,6 +6,7 @@ if TYPE_CHECKING: from textual.app import RenderResult + from textual.geometry import Size from textual.renderables.digits import Digits as DigitsRenderable from textual.widget import Widget @@ -59,7 +60,7 @@ def update(self, value: str) -> None: value: New value to display. Raises: - ValueError: If the value isn't a `str`. + TypeError: If the value isn't a `str`. """ if not isinstance(value, str): raise TypeError("value must be a str") From 4bae33ea3480caf1218e10825d47b306d474d28a Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Nov 2024 11:05:53 +0000 Subject: [PATCH 4/8] optimize scroll --- src/textual/scroll_view.py | 9 +++------ src/textual/widget.py | 3 ++- src/textual/widgets/_option_list.py | 1 - 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/textual/scroll_view.py b/src/textual/scroll_view.py index 1725fe9bde..716be3e5ed 100644 --- a/src/textual/scroll_view.py +++ b/src/textual/scroll_view.py @@ -82,10 +82,8 @@ def _size_updated( layout: Perform layout if required. Returns: - True if anything changed, or False if nothing changed. + True if a resize event should be sent, otherwise False. """ - if self._size != size or self._container_size != container_size: - self.refresh() if ( self._size != size or virtual_size != self.virtual_size @@ -96,9 +94,8 @@ def _size_updated( virtual_size = self.virtual_size self._container_size = size - self.styles.gutter.totals self._scroll_update(virtual_size) - return True - else: - return False + + return self._size != size or self._container_size != container_size def render(self) -> RenderableType: """Render the scrollable region (if `render_lines` is not implemented). diff --git a/src/textual/widget.py b/src/textual/widget.py index 046d2f2e73..537060d479 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -3695,8 +3695,9 @@ def _size_updated( layout: Perform layout if required. Returns: - True if anything changed, or False if nothing changed. + True if a resize event should be sent, otherwise False. """ + self._layout_cache.clear() if ( self._size != size diff --git a/src/textual/widgets/_option_list.py b/src/textual/widgets/_option_list.py index cf99b8bf6e..2863f60aa7 100644 --- a/src/textual/widgets/_option_list.py +++ b/src/textual/widgets/_option_list.py @@ -824,7 +824,6 @@ def get_option_index(self, option_id: str) -> int: ) from None def render_line(self, y: int) -> Strip: - self._populate() assert self._lines is not None _scroll_x, scroll_y = self.scroll_offset From dcb9229e4e1159662ad8a1026cfff507b01c8698 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Nov 2024 14:14:12 +0000 Subject: [PATCH 5/8] Refresh auto dimensions --- src/textual/widgets/_option_list.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/textual/widgets/_option_list.py b/src/textual/widgets/_option_list.py index 2863f60aa7..9a0e0b66f6 100644 --- a/src/textual/widgets/_option_list.py +++ b/src/textual/widgets/_option_list.py @@ -362,6 +362,7 @@ def _add_lines( self._lines.append(OptionLineSpan(-1, 0)) self.virtual_size = Size(width, len(self._lines)) + self.refresh(layout=self.styles.auto_dimensions) def _populate(self) -> None: """Populate the lines data-structure.""" @@ -374,7 +375,6 @@ def _populate(self) -> None: self._contents, self.scrollable_content_region.width - self._left_gutter_width(), ) - self.refresh(layout=True) def get_content_width(self, container: Size, viewport: Size) -> int: """Get maximum width of options.""" @@ -952,7 +952,6 @@ def _page(self, direction: Direction) -> None: # If we find ourselves in a position where we don't know where we're # going, we need a fallback location. Where we go will depend on the # direction. - self._populate() assert self._spans is not None assert self._lines is not None From faac1a3207e7d1e55f671f796d82f6f27e112bc4 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Nov 2024 14:53:51 +0000 Subject: [PATCH 6/8] fix refresh --- src/textual/widgets/_option_list.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/textual/widgets/_option_list.py b/src/textual/widgets/_option_list.py index 9a0e0b66f6..5356e0fe94 100644 --- a/src/textual/widgets/_option_list.py +++ b/src/textual/widgets/_option_list.py @@ -366,8 +366,7 @@ def _add_lines( def _populate(self) -> None: """Populate the lines data-structure.""" - if self._lines is not None: - return + self._lines = [] self._spans = [] @@ -825,6 +824,8 @@ def get_option_index(self, option_id: str) -> int: def render_line(self, y: int) -> Strip: assert self._lines is not None + if not self._lines: + self._populate() _scroll_x, scroll_y = self.scroll_offset line_number = scroll_y + y From a27210576ce2bcb0eebe3959c58637ba337277f6 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Nov 2024 15:10:28 +0000 Subject: [PATCH 7/8] fix select --- src/textual/widgets/_option_list.py | 7 ++++++- src/textual/widgets/_select.py | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/textual/widgets/_option_list.py b/src/textual/widgets/_option_list.py index 5356e0fe94..b921d69aaa 100644 --- a/src/textual/widgets/_option_list.py +++ b/src/textual/widgets/_option_list.py @@ -363,6 +363,7 @@ def _add_lines( self.virtual_size = Size(width, len(self._lines)) self.refresh(layout=self.styles.auto_dimensions) + self._scroll_update(self.virtual_size) def _populate(self) -> None: """Populate the lines data-structure.""" @@ -879,9 +880,12 @@ def scroll_to_highlight(self, top: bool = False) -> None: top: Scroll highlight to top of the list. """ highlighted = self.highlighted - if highlighted is None or self._spans is None: + if highlighted is None or not self.is_mounted: return + if not self._spans: + self._populate() + try: y, height = self._spans[highlighted] except IndexError: @@ -894,6 +898,7 @@ def scroll_to_highlight(self, top: bool = False) -> None: force=True, animate=False, top=top, + immediate=True, ) def validate_highlighted(self, highlighted: int | None) -> int | None: diff --git a/src/textual/widgets/_select.py b/src/textual/widgets/_select.py index c12d882eb9..853696a2db 100644 --- a/src/textual/widgets/_select.py +++ b/src/textual/widgets/_select.py @@ -66,7 +66,7 @@ def select(self, index: int | None) -> None: index: Index of new selection. """ self.highlighted = index - self.scroll_to_highlight(top=True) + self.scroll_to_highlight() def action_dismiss(self) -> None: """Dismiss the overlay.""" @@ -520,7 +520,7 @@ def _watch_expanded(self, expanded: bool) -> None: value = self.value for index, (_prompt, prompt_value) in enumerate(self._options): if value == prompt_value: - overlay.select(index) + self.call_after_refresh(overlay.select, index) break self.query_one(SelectCurrent).has_value = True From 44cddcadf12fe0881af00523d3ddd63ec4f687f7 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 30 Nov 2024 15:12:26 +0000 Subject: [PATCH 8/8] snapshots --- ...test_loading_indicator_disables_widget.svg | 123 ++++++++--------- .../test_missing_vertical_scroll.svg | 123 ++++++++--------- .../test_option_list_tables.svg | 130 +++++++++--------- .../test_snapshots/test_select_width_auto.svg | 126 ++++++++--------- 4 files changed, 250 insertions(+), 252 deletions(-) diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_loading_indicator_disables_widget.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_loading_indicator_disables_widget.svg index a1b1fbd6d1..5f9c6a1a20 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_loading_indicator_disables_widget.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_loading_indicator_disables_widget.svg @@ -19,139 +19,138 @@ font-weight: 700; } - .terminal-79819609-matrix { + .terminal-2779116593-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-79819609-title { + .terminal-2779116593-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-79819609-r1 { fill: #121212 } -.terminal-79819609-r2 { fill: #0178d4 } -.terminal-79819609-r3 { fill: #191919 } -.terminal-79819609-r4 { fill: #c5c8c6 } -.terminal-79819609-r5 { fill: #ddedf9;font-weight: bold } -.terminal-79819609-r6 { fill: #272727 } -.terminal-79819609-r7 { fill: #e0e0e0 } -.terminal-79819609-r8 { fill: #1e1e1e } -.terminal-79819609-r9 { fill: #000000 } + .terminal-2779116593-r1 { fill: #121212 } +.terminal-2779116593-r2 { fill: #0178d4 } +.terminal-2779116593-r3 { fill: #191919 } +.terminal-2779116593-r4 { fill: #c5c8c6 } +.terminal-2779116593-r5 { fill: #ddedf9;font-weight: bold } +.terminal-2779116593-r6 { fill: #1e1e1e } +.terminal-2779116593-r7 { fill: #e0e0e0 } +.terminal-2779116593-r8 { fill: #000000 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - LoadingOverlayRedux + LoadingOverlayRedux - + - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -hello world hello world hello     foo barfoo barfoo barfoo barfoo    -world hello world hello world     bar                                -hello world hello world hello     ▄▄foo barfoo barfoo barfoo barfoo   ▄▄ -world hello world hello world     bar                                -hello world hello world hello     foo barfoo barfoo barfoo barfoo    -world hello world hello world     bar                                -hello world hello world hello     foo barfoo barfoo barfoo barfoo    -world hello world hello world     bar                                -hello world hello world hello     foo barfoo barfoo barfoo barfoo    -world hello world hello world     bar                                -hello world hello world hello     foo barfoo barfoo barfoo barfoo    -world hello world hello world     bar                                -hello world hello world hello     foo barfoo barfoo barfoo barfoo    -world hello world hello world     bar                                -hello world hello world hello     foo barfoo barfoo barfoo barfoo    -world hello world hello world     bar                                -hello world hello world hello     foo barfoo barfoo barfoo barfoo    -world hello world hello world     bar                                -hello world hello world hello     foo barfoo barfoo barfoo barfoo    -world hello world hello world     bar                                -hello world hello world hello     foo barfoo barfoo barfoo barfoo    -world hello world hello world     bar                                -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +hello world hello world hello     foo barfoo barfoo barfoo barfoo    +world hello world hello world     bar                                +hello world hello world hello     ▄▄foo barfoo barfoo barfoo barfoo   ▄▄ +world hello world hello world     bar                                +hello world hello world hello     foo barfoo barfoo barfoo barfoo    +world hello world hello world     bar                                +hello world hello world hello     foo barfoo barfoo barfoo barfoo    +world hello world hello world     bar                                +hello world hello world hello     foo barfoo barfoo barfoo barfoo    +world hello world hello world     bar                                +hello world hello world hello     foo barfoo barfoo barfoo barfoo    +world hello world hello world     bar                                +hello world hello world hello     foo barfoo barfoo barfoo barfoo    +world hello world hello world     bar                                +hello world hello world hello     foo barfoo barfoo barfoo barfoo    +world hello world hello world     bar                                +hello world hello world hello     foo barfoo barfoo barfoo barfoo    +world hello world hello world     bar                                +hello world hello world hello     foo barfoo barfoo barfoo barfoo    +world hello world hello world     bar                                +hello world hello world hello     foo barfoo barfoo barfoo barfoo    +world hello world hello world     bar                                +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_missing_vertical_scroll.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_missing_vertical_scroll.svg index 9c0d16eda1..0f086aa6f6 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_missing_vertical_scroll.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_missing_vertical_scroll.svg @@ -19,139 +19,138 @@ font-weight: 700; } - .terminal-2554343736-matrix { + .terminal-208679440-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2554343736-title { + .terminal-208679440-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2554343736-r1 { fill: #121212 } -.terminal-2554343736-r2 { fill: #0178d4 } -.terminal-2554343736-r3 { fill: #191919 } -.terminal-2554343736-r4 { fill: #c5c8c6 } -.terminal-2554343736-r5 { fill: #ddedf9;font-weight: bold } -.terminal-2554343736-r6 { fill: #272727 } -.terminal-2554343736-r7 { fill: #e0e0e0 } -.terminal-2554343736-r8 { fill: #1e1e1e } -.terminal-2554343736-r9 { fill: #000000 } + .terminal-208679440-r1 { fill: #121212 } +.terminal-208679440-r2 { fill: #0178d4 } +.terminal-208679440-r3 { fill: #191919 } +.terminal-208679440-r4 { fill: #c5c8c6 } +.terminal-208679440-r5 { fill: #ddedf9;font-weight: bold } +.terminal-208679440-r6 { fill: #1e1e1e } +.terminal-208679440-r7 { fill: #e0e0e0 } +.terminal-208679440-r8 { fill: #000000 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - MissingScrollbarApp + MissingScrollbarApp - + - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -0                  0                  0                        -1                  1                  1                        -2                  ▄▄2                  ▄▄2                       ▄▄ -3                  3                  3                        -4                  4                  4                        -5                  5                  5                        -6                  6                  6                        -7                  7                  7                        -8                  8                  8                        -9                  9                  9                        -10                 10                 10                       -11                 11                 11                       -12                 12                 12                       -13                 13                 13                       -14                 14                 14                       -15                 15                 15                       -16                 16                 16                       -17                 17                 17                       -18                 18                 18                       -19                 19                 19                       -20                 20                 20                       -21                 21                 21                       -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +0                  0                  0                        +1                  1                  1                        +2                  ▄▄2                  ▄▄2                       ▄▄ +3                  3                  3                        +4                  4                  4                        +5                  5                  5                        +6                  6                  6                        +7                  7                  7                        +8                  8                  8                        +9                  9                  9                        +10                 10                 10                       +11                 11                 11                       +12                 12                 12                       +13                 13                 13                       +14                 14                 14                       +15                 15                 15                       +16                 16                 16                       +17                 17                 17                       +18                 18                 18                       +19                 19                 19                       +20                 20                 20                       +21                 21                 21                       +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_option_list_tables.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_option_list_tables.svg index 7cb9147edb..9da70495de 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_option_list_tables.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_option_list_tables.svg @@ -19,142 +19,142 @@ font-weight: 700; } - .terminal-1064844851-matrix { + .terminal-1503018871-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1064844851-title { + .terminal-1503018871-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1064844851-r1 { fill: #c5c8c6 } -.terminal-1064844851-r2 { fill: #e0e0e0 } -.terminal-1064844851-r3 { fill: #121212 } -.terminal-1064844851-r4 { fill: #0178d4 } -.terminal-1064844851-r5 { fill: #ddedf9;font-weight: bold;font-style: italic; } -.terminal-1064844851-r6 { fill: #272727 } -.terminal-1064844851-r7 { fill: #ddedf9;font-weight: bold } -.terminal-1064844851-r8 { fill: #000000 } -.terminal-1064844851-r9 { fill: #e0e0e0;font-style: italic; } -.terminal-1064844851-r10 { fill: #e0e0e0;font-weight: bold } -.terminal-1064844851-r11 { fill: #495259 } -.terminal-1064844851-r12 { fill: #ffa62b;font-weight: bold } + .terminal-1503018871-r1 { fill: #c5c8c6 } +.terminal-1503018871-r2 { fill: #e0e0e0 } +.terminal-1503018871-r3 { fill: #121212 } +.terminal-1503018871-r4 { fill: #0178d4 } +.terminal-1503018871-r5 { fill: #ddedf9;font-weight: bold;font-style: italic; } +.terminal-1503018871-r6 { fill: #1e1e1e } +.terminal-1503018871-r7 { fill: #ddedf9;font-weight: bold } +.terminal-1503018871-r8 { fill: #000000 } +.terminal-1503018871-r9 { fill: #e0e0e0;font-style: italic; } +.terminal-1503018871-r10 { fill: #e0e0e0;font-weight: bold } +.terminal-1503018871-r11 { fill: #495259 } +.terminal-1503018871-r12 { fill: #ffa62b;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - OptionListApp + OptionListApp - + - - ⭘                             OptionListApp                          - - -▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -                 Data for Aerilon                  -┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ -┃ Patron God    ┃ Population    ┃ Capital City   ┃ -┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩▇▇ -│ Demeter       │ 1.2 Billion   │ Gaoth          │ -└───────────────┴───────────────┴────────────────┘ -                 Data for Aquaria                  -┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ - Patron God     Population     Capital City    -┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ -│ Hermes        │ 75,000        │ None           │ -└───────────────┴───────────────┴────────────────┘ -                Data for Canceron                  -┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ - Patron God     Population     Capital City    -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - -^p palette + + ⭘                             OptionListApp                          + + +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +                 Data for Aerilon                  +┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ +┃ Patron God    ┃ Population    ┃ Capital City   ┃ +┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩▇▇ +│ Demeter       │ 1.2 Billion   │ Gaoth          │ +└───────────────┴───────────────┴────────────────┘ +                 Data for Aquaria                  +┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ + Patron God     Population     Capital City    +┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ +│ Hermes        │ 75,000        │ None           │ +└───────────────┴───────────────┴────────────────┘ +                Data for Canceron                  +┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ + Patron God     Population     Capital City    +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + +^p palette diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_select_width_auto.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_select_width_auto.svg index 8450289ab9..c4cc494b42 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_select_width_auto.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_select_width_auto.svg @@ -19,139 +19,139 @@ font-weight: 700; } - .terminal-1900810876-matrix { + .terminal-2894297115-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1900810876-title { + .terminal-2894297115-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1900810876-r1 { fill: #e0e0e0 } -.terminal-1900810876-r2 { fill: #121212 } -.terminal-1900810876-r3 { fill: #0178d4 } -.terminal-1900810876-r4 { fill: #c5c8c6 } -.terminal-1900810876-r5 { fill: #969696 } -.terminal-1900810876-r6 { fill: #272727 } -.terminal-1900810876-r7 { fill: #000000 } -.terminal-1900810876-r8 { fill: #191919 } -.terminal-1900810876-r9 { fill: #7f7f7f } + .terminal-2894297115-r1 { fill: #e0e0e0 } +.terminal-2894297115-r2 { fill: #121212 } +.terminal-2894297115-r3 { fill: #0178d4 } +.terminal-2894297115-r4 { fill: #c5c8c6 } +.terminal-2894297115-r5 { fill: #003054 } +.terminal-2894297115-r6 { fill: #272727 } +.terminal-2894297115-r7 { fill: #191919 } +.terminal-2894297115-r8 { fill: #7f7f7f } +.terminal-2894297115-r9 { fill: #ddedf9;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TallSelectApp + TallSelectApp - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -Select - Extra long option here  - Option 1                - Option 2                - Option 3               ▂▂ - Option 4                - Option 5                - Option 6                - Option 7                - Option 8               ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Option 9                - Option 10              ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - Option 11               - Option 12               - Option 13               - Option 14               - Option 15               - Option 16               - Option 17               - Option 18               - Option 19               - Option 20               -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Option 5                + Option 6               ▆▆ + Option 7                + Option 8                + Option 9                + Option 10               + Option 11               + Option 12               + Option 13               + Option 14              ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Option 15               + Option 16              ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + Option 17               + Option 18               + Option 19               + Option 20               + Option 21               + Option 22               + Option 23               + Option 24               + Option 25               + Option 26               +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁