mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
text,widget{,/material}: [API] move all shaping parameters into text.Parameters
This commit moves the min/max width of shaped text and the text's Locale into
text.Parameters. They were previously passed as separate function parameters to
the shaper, but this made little sense and added visual noise. This is a breaking
change, but only if you previously invoked the shaping API directly.
Callers of text.(*Shaper).LayoutString should change:
shaper.LayoutString(params, minWidth, maxWidth, locale, "string")
to
params.MinWidth=minWidth
params.MaxWidth=maxWidth
params.Locale=locale
shaper.LayoutString(params, "string")
Callers of text.(*Shaper).Layout should do likewise.
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
This commit is contained in:
+31
-8
@@ -32,17 +32,30 @@ func makePosTestText(fontSize, lineWidth int, alignOpposite bool) (source string
|
||||
// bidiSource is crafted to contain multiple consecutive RTL runs (by
|
||||
// changing scripts within the RTL).
|
||||
bidiSource := "The quick سماء שלום لا fox تمط שלום غير the lazy dog."
|
||||
ltrParams := text.Parameters{Font: text.Font{Typeface: "LTR"}, PxPerEm: fixed.I(fontSize)}
|
||||
rtlParams := text.Parameters{Alignment: text.End, Font: text.Font{Typeface: "RTL"}, PxPerEm: fixed.I(fontSize)}
|
||||
ltrParams := text.Parameters{
|
||||
Font: text.Font{Typeface: "LTR"},
|
||||
PxPerEm: fixed.I(fontSize),
|
||||
MaxWidth: lineWidth,
|
||||
MinWidth: lineWidth,
|
||||
Locale: english,
|
||||
}
|
||||
rtlParams := text.Parameters{
|
||||
Alignment: text.End,
|
||||
Font: text.Font{Typeface: "RTL"},
|
||||
PxPerEm: fixed.I(fontSize),
|
||||
MaxWidth: lineWidth,
|
||||
MinWidth: lineWidth,
|
||||
Locale: arabic,
|
||||
}
|
||||
if alignOpposite {
|
||||
ltrParams.Alignment = text.End
|
||||
rtlParams.Alignment = text.Start
|
||||
}
|
||||
shaper.LayoutString(ltrParams, lineWidth, lineWidth, english, bidiSource)
|
||||
shaper.LayoutString(ltrParams, bidiSource)
|
||||
for g, ok := shaper.NextGlyph(); ok; g, ok = shaper.NextGlyph() {
|
||||
bidiLTR = append(bidiLTR, g)
|
||||
}
|
||||
shaper.LayoutString(rtlParams, lineWidth, lineWidth, arabic, bidiSource)
|
||||
shaper.LayoutString(rtlParams, bidiSource)
|
||||
for g, ok := shaper.NextGlyph(); ok; g, ok = shaper.NextGlyph() {
|
||||
bidiRTL = append(bidiRTL, g)
|
||||
}
|
||||
@@ -64,8 +77,12 @@ func makeAccountingTestText(str string, fontSize, lineWidth int) (txt []text.Gly
|
||||
Face: rtlFace,
|
||||
},
|
||||
})
|
||||
params := text.Parameters{PxPerEm: fixed.I(fontSize)}
|
||||
shaper.LayoutString(params, 0, lineWidth, english, str)
|
||||
params := text.Parameters{
|
||||
PxPerEm: fixed.I(fontSize),
|
||||
MaxWidth: lineWidth,
|
||||
Locale: english,
|
||||
}
|
||||
shaper.LayoutString(params, str)
|
||||
for g, ok := shaper.NextGlyph(); ok; g, ok = shaper.NextGlyph() {
|
||||
txt = append(txt, g)
|
||||
}
|
||||
@@ -86,8 +103,14 @@ func getGlyphs(fontSize, minWidth, lineWidth int, align text.Alignment, str stri
|
||||
Face: rtlFace,
|
||||
},
|
||||
})
|
||||
params := text.Parameters{PxPerEm: fixed.I(fontSize), Alignment: align}
|
||||
shaper.LayoutString(params, minWidth, lineWidth, english, str)
|
||||
params := text.Parameters{
|
||||
PxPerEm: fixed.I(fontSize),
|
||||
Alignment: align,
|
||||
MinWidth: minWidth,
|
||||
MaxWidth: lineWidth,
|
||||
Locale: english,
|
||||
}
|
||||
shaper.LayoutString(params, str)
|
||||
for g, ok := shaper.NextGlyph(); ok; g, ok = shaper.NextGlyph() {
|
||||
txt = append(txt, g)
|
||||
}
|
||||
|
||||
+4
-1
@@ -38,7 +38,10 @@ func (l Label) Layout(gtx layout.Context, lt *text.Shaper, font text.Font, size
|
||||
MaxLines: l.MaxLines,
|
||||
Truncator: l.Truncator,
|
||||
Alignment: l.Alignment,
|
||||
}, cs.Min.X, cs.Max.X, gtx.Locale, txt)
|
||||
MaxWidth: cs.Max.X,
|
||||
MinWidth: cs.Min.X,
|
||||
Locale: gtx.Locale,
|
||||
}, txt)
|
||||
m := op.Record(gtx.Ops)
|
||||
viewport := image.Rectangle{Max: cs.Max}
|
||||
it := textIterator{
|
||||
|
||||
@@ -116,8 +116,15 @@ func (l LabelStyle) Layout(gtx layout.Context) layout.Dimensions {
|
||||
if l.State.Text() != l.Text {
|
||||
l.State.SetText(l.Text)
|
||||
}
|
||||
l.State.Alignment = l.Alignment
|
||||
l.State.MaxLines = l.MaxLines
|
||||
l.State.Truncator = l.Truncator
|
||||
return l.State.Layout(gtx, l.shaper, l.Font, l.TextSize, textColor, selectColor)
|
||||
}
|
||||
tl := widget.Label{Alignment: l.Alignment, MaxLines: l.MaxLines}
|
||||
tl := widget.Label{
|
||||
Alignment: l.Alignment,
|
||||
MaxLines: l.MaxLines,
|
||||
Truncator: l.Truncator,
|
||||
}
|
||||
return tl.Layout(gtx, l.shaper, l.Font, l.TextSize, l.Text, textColor)
|
||||
}
|
||||
|
||||
+11
-1
@@ -48,8 +48,15 @@ func (s stringSource) ReadAt(b []byte, offset int64) (int, error) {
|
||||
func (s stringSource) ReplaceRunes(byteOffset, runeCount int64, str string) {
|
||||
}
|
||||
|
||||
// Selectable holds text selection state.
|
||||
// Selectable displays selectable text.
|
||||
type Selectable struct {
|
||||
// Alignment controls the alignment of the text.
|
||||
Alignment text.Alignment
|
||||
// MaxLines is the maximum number of lines of text to be displayed.
|
||||
MaxLines int
|
||||
// Truncator is the symbol to use at the end of the final line of text
|
||||
// if text was cut off. Defaults to "…" if left empty.
|
||||
Truncator string
|
||||
initialized bool
|
||||
source stringSource
|
||||
// scratch is a buffer reused to efficiently read text out of the
|
||||
@@ -171,6 +178,9 @@ func (l *Selectable) Truncated() bool {
|
||||
// paint material for the text and selection rectangles, respectively.
|
||||
func (l *Selectable) Layout(gtx layout.Context, lt *text.Shaper, font text.Font, size unit.Sp, textMaterial, selectionMaterial op.CallOp) layout.Dimensions {
|
||||
l.initialize()
|
||||
l.text.Alignment = l.Alignment
|
||||
l.text.MaxLines = l.MaxLines
|
||||
l.text.Truncator = l.Truncator
|
||||
l.text.Update(gtx, lt, font, size, l.handleEvents)
|
||||
dims := l.text.Dimensions()
|
||||
defer clip.Rect(image.Rectangle{Max: dims.Size}).Push(gtx.Ops).Pop()
|
||||
|
||||
@@ -100,7 +100,7 @@ func TestSelectableConfigurations(t *testing.T) {
|
||||
gtx.Constraints.Min = gtx.Constraints.Max
|
||||
}
|
||||
s := new(Selectable)
|
||||
s.text.Alignment = alignment
|
||||
s.Alignment = alignment
|
||||
s.SetText(sentence)
|
||||
interactiveDims := s.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
||||
staticDims := Label{Alignment: alignment}.Layout(gtx, cache, font, fontSize, sentence, op.CallOp{})
|
||||
|
||||
+30
-29
@@ -10,7 +10,6 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/io/system"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
"gioui.org/op/clip"
|
||||
@@ -58,22 +57,20 @@ type textView struct {
|
||||
// are accessed by Len, Text, and SetText.
|
||||
Mask rune
|
||||
|
||||
font text.Font
|
||||
params text.Parameters
|
||||
shaper *text.Shaper
|
||||
textSize fixed.Int26_6
|
||||
seekCursor int64
|
||||
rr textSource
|
||||
maskReader maskReader
|
||||
// graphemes tracks the indices of grapheme cluster boundaries within rr.
|
||||
graphemes []int
|
||||
// paragraphReader is used to populate graphemes.
|
||||
paragraphReader graphemeReader
|
||||
lastMask rune
|
||||
maxWidth, minWidth int
|
||||
viewSize image.Point
|
||||
valid bool
|
||||
regions []Region
|
||||
dims layout.Dimensions
|
||||
paragraphReader graphemeReader
|
||||
lastMask rune
|
||||
viewSize image.Point
|
||||
valid bool
|
||||
regions []Region
|
||||
dims layout.Dimensions
|
||||
|
||||
// offIndex is an index of rune index to byte offsets.
|
||||
offIndex []offEntry
|
||||
@@ -93,8 +90,6 @@ type textView struct {
|
||||
}
|
||||
|
||||
scrollOff image.Point
|
||||
|
||||
locale system.Locale
|
||||
}
|
||||
|
||||
func (e *textView) Changed() bool {
|
||||
@@ -228,27 +223,27 @@ func (e *textView) calculateViewSize(gtx layout.Context) image.Point {
|
||||
// allow parent widgets to adapt to any changes in text content or positioning. If eventHandling modifies the contents
|
||||
// of the textView, it is guaranteed to be reshaped (and ready for painting) before Update returns.
|
||||
func (e *textView) Update(gtx layout.Context, lt *text.Shaper, font text.Font, size unit.Sp, eventHandling func(gtx layout.Context)) {
|
||||
if e.locale != gtx.Locale {
|
||||
e.locale = gtx.Locale
|
||||
if e.params.Locale != gtx.Locale {
|
||||
e.params.Locale = gtx.Locale
|
||||
e.invalidate()
|
||||
}
|
||||
textSize := fixed.I(gtx.Sp(size))
|
||||
if e.font != font || e.textSize != textSize {
|
||||
if e.params.Font != font || e.params.PxPerEm != textSize {
|
||||
e.invalidate()
|
||||
e.font = font
|
||||
e.textSize = textSize
|
||||
e.params.Font = font
|
||||
e.params.PxPerEm = textSize
|
||||
}
|
||||
maxWidth := gtx.Constraints.Max.X
|
||||
if e.SingleLine {
|
||||
maxWidth = math.MaxInt
|
||||
}
|
||||
minWidth := gtx.Constraints.Min.X
|
||||
if maxWidth != e.maxWidth {
|
||||
e.maxWidth = maxWidth
|
||||
if maxWidth != e.params.MaxWidth {
|
||||
e.params.MaxWidth = maxWidth
|
||||
e.invalidate()
|
||||
}
|
||||
if minWidth != e.minWidth {
|
||||
e.minWidth = minWidth
|
||||
if minWidth != e.params.MinWidth {
|
||||
e.params.MinWidth = minWidth
|
||||
e.invalidate()
|
||||
}
|
||||
if lt != e.shaper {
|
||||
@@ -259,6 +254,18 @@ func (e *textView) Update(gtx layout.Context, lt *text.Shaper, font text.Font, s
|
||||
e.lastMask = e.Mask
|
||||
e.invalidate()
|
||||
}
|
||||
if e.Alignment != e.params.Alignment {
|
||||
e.params.Alignment = e.Alignment
|
||||
e.invalidate()
|
||||
}
|
||||
if e.Truncator != e.params.Truncator {
|
||||
e.params.Truncator = e.Truncator
|
||||
e.invalidate()
|
||||
}
|
||||
if e.MaxLines != e.params.MaxLines {
|
||||
e.params.MaxLines = e.MaxLines
|
||||
e.invalidate()
|
||||
}
|
||||
|
||||
e.makeValid()
|
||||
if eventHandling != nil {
|
||||
@@ -463,13 +470,7 @@ func (e *textView) layoutText(lt *text.Shaper) {
|
||||
e.index.reset()
|
||||
it := textIterator{viewport: image.Rectangle{Max: image.Point{X: math.MaxInt, Y: math.MaxInt}}}
|
||||
if lt != nil {
|
||||
lt.Layout(text.Parameters{
|
||||
Font: e.font,
|
||||
PxPerEm: e.textSize,
|
||||
Alignment: e.Alignment,
|
||||
MaxLines: e.MaxLines,
|
||||
Truncator: e.Truncator,
|
||||
}, e.minWidth, e.maxWidth, e.locale, r)
|
||||
lt.Layout(e.params, r)
|
||||
for glyph, ok := it.processGlyph(lt.NextGlyph()); ok; glyph, ok = it.processGlyph(lt.NextGlyph()) {
|
||||
e.index.Glyph(glyph)
|
||||
}
|
||||
@@ -635,7 +636,7 @@ func (e *textView) MoveEnd(selAct selectionAction) {
|
||||
caret := e.closestToRune(e.caret.start)
|
||||
caret = e.closestToLineCol(caret.lineCol.line, math.MaxInt)
|
||||
e.caret.start = caret.runes
|
||||
e.caret.xoff = fixed.I(e.maxWidth) - caret.x
|
||||
e.caret.xoff = fixed.I(e.params.MaxWidth) - caret.x
|
||||
e.updateSelection(selAct)
|
||||
e.clampCursorToGraphemes()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user