forked from joejulian/gio
go.*,text,widget{,/material}: enable configurable line wrapping within words
This commit enables consumers of the text shaper to select a policy for how line breaking candidates will be chosen. The new default policy can break lines within "words" (UAX#14 segments) when words do not fit by themselves on a line. This ensures that text does not horizontally overflow its bounding box unless the available width is insufficient to display a single UAX#29 grapheme cluster. Fixes: https://todo.sr.ht/~eliasnaur/gio/467 Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
This commit is contained in:
@@ -57,6 +57,8 @@ type Editor struct {
|
||||
// Filter is the list of characters allowed in the Editor. If Filter is empty,
|
||||
// all characters are allowed.
|
||||
Filter string
|
||||
// WrapPolicy configures how displayed text will be broken into lines.
|
||||
WrapPolicy text.WrapPolicy
|
||||
|
||||
buffer *editBuffer
|
||||
// scratch is a byte buffer that is reused to efficiently read portions of text
|
||||
@@ -504,6 +506,7 @@ func (e *Editor) initBuffer() {
|
||||
e.text.Alignment = e.Alignment
|
||||
e.text.SingleLine = e.SingleLine
|
||||
e.text.Mask = e.Mask
|
||||
e.text.WrapPolicy = e.WrapPolicy
|
||||
}
|
||||
|
||||
// Layout lays out the editor using the provided textMaterial as the paint material
|
||||
|
||||
@@ -409,6 +409,7 @@ func TestEditorRTL(t *testing.T) {
|
||||
|
||||
func TestEditorLigature(t *testing.T) {
|
||||
e := new(Editor)
|
||||
e.WrapPolicy = text.WrapWords
|
||||
gtx := layout.Context{
|
||||
Ops: new(op.Ops),
|
||||
Constraints: layout.Exact(image.Pt(100, 100)),
|
||||
|
||||
+11
-8
@@ -28,6 +28,8 @@ type Label struct {
|
||||
// Truncator is the text that will be shown at the end of the final
|
||||
// line if MaxLines is exceeded. Defaults to "…" if empty.
|
||||
Truncator string
|
||||
// WrapPolicy configures how displayed text will be broken into lines.
|
||||
WrapPolicy text.WrapPolicy
|
||||
}
|
||||
|
||||
// Layout the label with the given shaper, font, size, text, and material.
|
||||
@@ -35,14 +37,15 @@ func (l Label) Layout(gtx layout.Context, lt *text.Shaper, font font.Font, size
|
||||
cs := gtx.Constraints
|
||||
textSize := fixed.I(gtx.Sp(size))
|
||||
lt.LayoutString(text.Parameters{
|
||||
Font: font,
|
||||
PxPerEm: textSize,
|
||||
MaxLines: l.MaxLines,
|
||||
Truncator: l.Truncator,
|
||||
Alignment: l.Alignment,
|
||||
MaxWidth: cs.Max.X,
|
||||
MinWidth: cs.Min.X,
|
||||
Locale: gtx.Locale,
|
||||
Font: font,
|
||||
PxPerEm: textSize,
|
||||
MaxLines: l.MaxLines,
|
||||
Truncator: l.Truncator,
|
||||
Alignment: l.Alignment,
|
||||
WrapPolicy: l.WrapPolicy,
|
||||
MaxWidth: cs.Max.X,
|
||||
MinWidth: cs.Min.X,
|
||||
Locale: gtx.Locale,
|
||||
}, txt)
|
||||
m := op.Record(gtx.Ops)
|
||||
viewport := image.Rectangle{Max: cs.Max}
|
||||
|
||||
@@ -29,6 +29,8 @@ type LabelStyle struct {
|
||||
Alignment text.Alignment
|
||||
// MaxLines limits the number of lines. Zero means no limit.
|
||||
MaxLines int
|
||||
// WrapPolicy configures how displayed text will be broken into lines.
|
||||
WrapPolicy text.WrapPolicy
|
||||
// Truncator is the text that will be shown at the end of the final
|
||||
// line if MaxLines is exceeded. Defaults to "…" if empty.
|
||||
Truncator string
|
||||
@@ -127,12 +129,14 @@ func (l LabelStyle) Layout(gtx layout.Context) layout.Dimensions {
|
||||
l.State.Alignment = l.Alignment
|
||||
l.State.MaxLines = l.MaxLines
|
||||
l.State.Truncator = l.Truncator
|
||||
l.State.WrapPolicy = l.WrapPolicy
|
||||
return l.State.Layout(gtx, l.Shaper, l.Font, l.TextSize, textColor, selectColor)
|
||||
}
|
||||
tl := widget.Label{
|
||||
Alignment: l.Alignment,
|
||||
MaxLines: l.MaxLines,
|
||||
Truncator: l.Truncator,
|
||||
Alignment: l.Alignment,
|
||||
MaxLines: l.MaxLines,
|
||||
Truncator: l.Truncator,
|
||||
WrapPolicy: l.WrapPolicy,
|
||||
}
|
||||
return tl.Layout(gtx, l.Shaper, l.Font, l.TextSize, l.Text, textColor)
|
||||
}
|
||||
|
||||
@@ -57,7 +57,9 @@ type Selectable struct {
|
||||
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
|
||||
Truncator string
|
||||
// WrapPolicy configures how displayed text will be broken into lines.
|
||||
WrapPolicy text.WrapPolicy
|
||||
initialized bool
|
||||
source stringSource
|
||||
// scratch is a buffer reused to efficiently read text out of the
|
||||
@@ -182,6 +184,7 @@ func (l *Selectable) Layout(gtx layout.Context, lt *text.Shaper, font font.Font,
|
||||
l.text.Alignment = l.Alignment
|
||||
l.text.MaxLines = l.MaxLines
|
||||
l.text.Truncator = l.Truncator
|
||||
l.text.WrapPolicy = l.WrapPolicy
|
||||
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()
|
||||
|
||||
@@ -53,6 +53,8 @@ type textView struct {
|
||||
// Truncator is the text that will be shown at the end of the final
|
||||
// line if MaxLines is exceeded. Defaults to "…" if empty.
|
||||
Truncator string
|
||||
// WrapPolicy configures how displayed text will be broken into lines.
|
||||
WrapPolicy text.WrapPolicy
|
||||
// Mask replaces the visual display of each rune in the contents with the given rune.
|
||||
// Newline characters are not masked. When non-zero, the unmasked contents
|
||||
// are accessed by Len, Text, and SetText.
|
||||
@@ -267,6 +269,10 @@ func (e *textView) Update(gtx layout.Context, lt *text.Shaper, font font.Font, s
|
||||
e.params.MaxLines = e.MaxLines
|
||||
e.invalidate()
|
||||
}
|
||||
if e.WrapPolicy != e.params.WrapPolicy {
|
||||
e.params.WrapPolicy = e.WrapPolicy
|
||||
e.invalidate()
|
||||
}
|
||||
|
||||
e.makeValid()
|
||||
if eventHandling != nil {
|
||||
|
||||
Reference in New Issue
Block a user