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