From eb725ee7810b24932f19cc1c88447862edf369f0 Mon Sep 17 00:00:00 2001 From: Greg Pomerantz Date: Wed, 19 Aug 2026 22:35:13 -0400 Subject: [PATCH] Fix word-wrap toggle: actually stop wrapping when disabled The bottom-bar 'Wrap: On/Off' toggle flipped State.WordWrap and relabelled itself, but the render path never read the flag: NewTextField hardcoded WordWrap: true and drawWrappedText always shaped at the region width with WrapHeuristically, so lines wrapped in both modes. NewTextField now takes the wordWrap flag, TextField.Draw passes tf.WordWrap to drawWrappedText, and with wrap disabled the shaper gets unlimited width (MaxWidth = maxInt32, as in single-line layout) so over-long lines extend past the region and are clipped instead of wrapping. EditorLayout passes its existing wordWrap parameter through. --- internal/editor/state.go | 1 + internal/ui/element.go | 6 +++--- internal/ui/render.go | 10 ++++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/internal/editor/state.go b/internal/editor/state.go index 1e1473f..21bce43 100644 --- a/internal/editor/state.go +++ b/internal/editor/state.go @@ -2106,6 +2106,7 @@ func EditorLayout(screenWidth, screenHeight ui.Dp, wordWrap bool) []ui.Element { "editor_text", visibleContent, editorRegion, + wordWrap, editorRegion.W, visibleScrollOffset, visibleCursorPos, diff --git a/internal/ui/element.go b/internal/ui/element.go index c94ae23..c175518 100644 --- a/internal/ui/element.go +++ b/internal/ui/element.go @@ -330,7 +330,7 @@ func (tf TextField) Draw(gtx layout.Context, r *Renderer) { r.lastSelCaret = -1 r.lastIMEShowSeq = 0 } - r.drawWrappedText(gtx, tf.Value, tf.region, tf.WrapWidth, tf.ScrollOffset, tf.CursorPosition, tf.SelectionStart, tf.SelectionEnd, tf.CaretDrag) + r.drawWrappedText(gtx, tf.Value, tf.region, tf.WordWrap, tf.WrapWidth, tf.ScrollOffset, tf.CursorPosition, tf.SelectionStart, tf.SelectionEnd, tf.CaretDrag) } // runeCount returns the number of UTF-8 runes in s[:bytePos] (bytePos is a @@ -351,7 +351,7 @@ func runeCount(s string, bytePos int) int { } // NewTextField creates a visible multiline TextField. -func NewTextField(id string, value string, region Region, wrapWidth Dp, scrollOffset Dp, cursorPos int, selStart, selEnd int, interactions []Interaction) TextField { +func NewTextField(id string, value string, region Region, wordWrap bool, wrapWidth Dp, scrollOffset Dp, cursorPos int, selStart, selEnd int, interactions []Interaction) TextField { return TextField{ id: id, region: region, @@ -359,7 +359,7 @@ func NewTextField(id string, value string, region Region, wrapWidth Dp, scrollOf interactions: interactions, Value: value, Multiline: true, - WordWrap: true, + WordWrap: wordWrap, WrapWidth: wrapWidth, ScrollOffset: scrollOffset, CursorPosition: cursorPos, diff --git a/internal/ui/render.go b/internal/ui/render.go index 1745e2f..20bc4ab 100644 --- a/internal/ui/render.go +++ b/internal/ui/render.go @@ -750,7 +750,7 @@ func (r *Renderer) drawLine(gtx layout.Context, line []text.Glyph, x, y Dp, col // detection via WrapHeuristically. Long words overflow the wrap width. // Line spacing is fixed: LineHeight = fontSize × LineHeightScale, independent // of glyph metrics. The shaper's first.Y accounts for line spacing. -func (r *Renderer) drawWrappedText(gtx layout.Context, str string, reg Region, wrapWidth Dp, scrollOffset Dp, cursorPos, selStart, selEnd int, caretDrag bool) { +func (r *Renderer) drawWrappedText(gtx layout.Context, str string, reg Region, wordWrap bool, wrapWidth Dp, scrollOffset Dp, cursorPos, selStart, selEnd int, caretDrag bool) { if str == "" { return } @@ -770,10 +770,16 @@ func (r *Renderer) drawWrappedText(gtx layout.Context, str string, reg Region, w } ascent := Dp(float32(r.theme.FontSize) * fontScale) lineH := Dp(float32(lineHeightSp) * fontScale) + // Wrap disabled: shape with unlimited width so lines extend past the + // region (clipped by textClip below) instead of wrapping. + maxWidthPx := maxInt32 + if wordWrap { + maxWidthPx = int(r.toPx(wrapWidth)) + } params := text.Parameters{ PxPerEm: fixed.I(gtx.Sp(r.theme.FontSize)), MinWidth: 0, - MaxWidth: int(r.toPx(wrapWidth)), + MaxWidth: maxWidthPx, MaxLines: 0, // unlimited - wrap at MaxWidth LineHeight: fixed.I(gtx.Sp(lineHeightSp)), LineHeightScale: 1.0, // use LineHeight directly, don't scale