diff --git a/internal/ui/render.go b/internal/ui/render.go index 02675ab..631ffb6 100644 --- a/internal/ui/render.go +++ b/internal/ui/render.go @@ -97,7 +97,12 @@ func (r *Renderer) drawStatusBar(gtx layout.Context, sb StatusBar) { availableWidth := int(reg.W - unit.Dp(8) - unit.Dp(40)) // left margin + right icons in DP // Layout ellipsis once and get its width - th.Shaper.LayoutString(text.Parameters{PxPerEm: fixed.I(gtx.Sp(r.theme.FontSize))}, "...") + th.Shaper.LayoutString(text.Parameters{ + PxPerEm: fixed.I(gtx.Sp(r.theme.FontSize)), + MinWidth: 0, + MaxWidth: 1000, + MaxLines: 1, + }, "...") var ellipsisWidth int for { g, ok := th.Shaper.NextGlyph() @@ -197,8 +202,13 @@ func boolToString(b bool) string { // drawTruncatedText layouts text, measures widths, truncates if needed, and draws in a single pass. func (r *Renderer) drawTruncatedText(gtx layout.Context, shp *text.Shaper, str string, size unit.Sp, x, y unit.Dp, availableWidth, spaceForText int, col color.NRGBA) { - // Layout text - shp.LayoutString(text.Parameters{PxPerEm: fixed.I(gtx.Sp(size))}, str) + // Layout text with width constraints so the shaper doesn't wrap every character. + shp.LayoutString(text.Parameters{ + PxPerEm: fixed.I(gtx.Sp(size)), + MinWidth: 0, + MaxWidth: availableWidth, + MaxLines: 1, + }, str) // Measure widths and find truncation point var widths []int @@ -226,15 +236,25 @@ func (r *Renderer) drawTruncatedText(gtx layout.Context, shp *text.Shaper, str s } // Re-layout truncated text and draw glyphs directly - shp.LayoutString(text.Parameters{PxPerEm: fixed.I(gtx.Sp(size))}, fullStr) + shp.LayoutString(text.Parameters{ + PxPerEm: fixed.I(gtx.Sp(size)), + MinWidth: 0, + MaxWidth: availableWidth, + MaxLines: 1, + }, fullStr) r.drawLineText(gtx, shp, x, y, col) } // drawText draws text using the same approach as Gio's textView: // layout text, iterate glyphs, buffer into lines, and draw using shaper.Shape(). func (r *Renderer) drawText(gtx layout.Context, shp *text.Shaper, str string, size unit.Sp, x, y unit.Dp, col color.NRGBA) { - // Layout text - shp.LayoutString(text.Parameters{PxPerEm: fixed.I(gtx.Sp(size))}, str) + // Layout text with width constraints so the shaper doesn't wrap every character. + shp.LayoutString(text.Parameters{ + PxPerEm: fixed.I(gtx.Sp(size)), + MinWidth: 0, + MaxWidth: 1000, + MaxLines: 1, + }, str) r.drawLineText(gtx, shp, x, y, col) } @@ -292,7 +312,13 @@ func (r *Renderer) drawLine(gtx layout.Context, shp *text.Shaper, line []text.Gl // charWidths returns the cumulative width after each rune in str. func charWidths(gtx layout.Context, shp *text.Shaper, str string, size unit.Sp) []int { // Match Gio's textView: PxPerEm = font size in device pixels - shp.LayoutString(text.Parameters{PxPerEm: fixed.I(gtx.Sp(size))}, str) + // Must set MinWidth/MaxWidth/MaxLines or shaper wraps every character. + shp.LayoutString(text.Parameters{ + PxPerEm: fixed.I(gtx.Sp(size)), + MinWidth: 0, + MaxWidth: 1000, + MaxLines: 1, + }, str) var widths []int var cumWidth fixed.Int26_6 = 0 for {