From 2ceaba314befaa32d15cd88b732296249faf21b0 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Thu, 11 Jun 2026 19:58:25 +0200 Subject: [PATCH 1/2] Gnome: pin code view scrollbars to LTR The scrollbars are internal children of the scrolled window and were created before the LTR pin from the RTL mirroring exemption, so they kept the RTL default direction. An RTL scrollbar maps the adjustment mirrored, while the pinned code content stays anchored left: the thumb sat at the right while the view showed the left edge, and dragging felt inverted. --- packages/app-gnome/src/widgets/source-view.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/app-gnome/src/widgets/source-view.ts b/packages/app-gnome/src/widgets/source-view.ts index 5f75ab68..0168c4fb 100644 --- a/packages/app-gnome/src/widgets/source-view.ts +++ b/packages/app-gnome/src/widgets/source-view.ts @@ -550,6 +550,11 @@ export class SourceView extends Adw.Bin implements SourceViewWidget { this._scrolledWindow.set_direction(Gtk.TextDirection.LTR); this._sourceView.set_direction(Gtk.TextDirection.LTR); this._copyButton.set_direction(Gtk.TextDirection.LTR); + // The scrollbars are internal children created before the pin above, so + // they kept the RTL default and mapped the scroll position mirrored to + // the LTR code content (thumb at the right while showing the left edge) + this._scrolledWindow.get_hscrollbar().set_direction(Gtk.TextDirection.LTR); + this._scrolledWindow.get_vscrollbar().set_direction(Gtk.TextDirection.LTR); this.setupScrolledWindow(); // Setup action group and actions From 85643727f350c71a5debaea3493c0d72a0b987f2 Mon Sep 17 00:00:00 2001 From: Pascal Garber Date: Fri, 12 Jun 2026 08:18:53 +0200 Subject: [PATCH 2/2] Gnome: pin code view direction recursively Pinning the scrollbar widgets was not enough: each scrollbar holds an internal GtkRange child that does the actual value-to-thumb mapping, and gtk_widget_set_direction does not propagate to children. The range kept the RTL default, so the horizontal thumb still sat at the right end while the view showed the left edge. Replace the individual pins with one recursive pin over the whole SourceView subtree, verified against the internal scrollbar structure (scrollbar > range > trough > slider). Vertical scroll behavior is direction-independent in GTK and stays unchanged. --- packages/app-gnome/src/widgets/source-view.ts | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/app-gnome/src/widgets/source-view.ts b/packages/app-gnome/src/widgets/source-view.ts index 0168c4fb..c6e999b0 100644 --- a/packages/app-gnome/src/widgets/source-view.ts +++ b/packages/app-gnome/src/widgets/source-view.ts @@ -541,20 +541,10 @@ export class SourceView extends Adw.Bin implements SourceViewWidget { ...rest } = params; super(rest); - // Source code is inherently LTR content, so the widget is exempt from - // RTL mirroring — like in any code editor, the gutter, scrollbar and - // the floating copy button keep their LTR positions. GTK does not - // propagate the direction to existing children, so every widget that - // resolves its own alignment or margins must be pinned individually. - this.set_direction(Gtk.TextDirection.LTR); - this._scrolledWindow.set_direction(Gtk.TextDirection.LTR); - this._sourceView.set_direction(Gtk.TextDirection.LTR); - this._copyButton.set_direction(Gtk.TextDirection.LTR); - // The scrollbars are internal children created before the pin above, so - // they kept the RTL default and mapped the scroll position mirrored to - // the LTR code content (thumb at the right while showing the left edge) - this._scrolledWindow.get_hscrollbar().set_direction(Gtk.TextDirection.LTR); - this._scrolledWindow.get_vscrollbar().set_direction(Gtk.TextDirection.LTR); + // Source code is inherently LTR content, so the whole widget is exempt + // from RTL mirroring — like in any code editor, the gutter, scrollbar + // and the floating copy button keep their LTR positions. + this.pinDirectionLtr(this); this.setupScrolledWindow(); // Setup action group and actions @@ -805,6 +795,22 @@ export class SourceView extends Adw.Bin implements SourceViewWidget { this.events.dispatch("changed", { code: this.code }); } + /** + * Pin the widget and all its current descendants to LTR. + * + * gtk_widget_set_direction does not propagate to children, and some of + * the widgets that resolve a direction themselves are internal — like the + * GtkRange inside each scrollbar, which maps the scroll position: left + * unpinned it keeps the RTL default and shows the thumb mirrored to the + * LTR code content (at the right end while the view shows the left edge). + */ + private pinDirectionLtr(widget: Gtk.Widget): void { + widget.set_direction(Gtk.TextDirection.LTR); + for (let child = widget.get_first_child(); child !== null; child = child.get_next_sibling()) { + this.pinDirectionLtr(child); + } + } + /** * Update the style of the source view. * Used internally to update the style of the source view when the theme changes.