mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
ui/layout: introduce Context
Context keeps the current Constraints and Dimensions so the layout
function scopes don't have to.
With
ctx := new(layout.Context)
a label with margins and alignment goes from
return al.Layout(ops, cs, func(cs layout.Constraints) layout.Dimensions {
in := layout.Inset{...}
return in.Layout(c, ops, cs, func(cs layout.Constraints) layout.Dimensions {
return text.Label{...}.Layout(ops, cs)
})
})
to
al.Layout(ops, ctx, func() {
in := layout.Inset{...}
in.Layout(c, ops, ctx, func() {
text.Label{...}.Layout(ops, ctx)
})
})
It was a difficult trade-off between the verbose functional approach
and the shorter but more complex Context.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+15
-13
@@ -5,28 +5,30 @@ Package layout implements layouts common to GUI programs.
|
||||
|
||||
Constraints and dimensions
|
||||
|
||||
Constraints and dimensions form the interface between
|
||||
layouts and interface child elements. Every layout operation
|
||||
start with a set of constraints for acceptable widths and heights
|
||||
of a child. The operation ends with the child computing and returning
|
||||
its size and baseline (if any).
|
||||
Constraints and dimensions form the interface between layouts and
|
||||
interface child elements. This package operates on Widgets, functions
|
||||
that compute Dimensions from a a set of constraints for acceptable
|
||||
widths and heights. Both the constraints and dimensions are maintained
|
||||
in an implicit Context to keep the Widget declaration short.
|
||||
|
||||
For example, to add space above a widget:
|
||||
|
||||
var cs layout.Constraints = ...
|
||||
ctx := new(layout.Context)
|
||||
ctx.Constraints = ...
|
||||
|
||||
// Configure a top inset.
|
||||
inset := layout.Inset{Top: ui.Dp(8), ...}
|
||||
// Use the inset to lay out a widget.
|
||||
inset.Layout(..., cs, func(cs layout.Constraints) layout.Dimensions {
|
||||
inset.Layout(..., ctx, func() {
|
||||
// Lay out widget and determine its size given the constraints.
|
||||
dimensions := widget.Layout(..., cs)
|
||||
return dimensions
|
||||
...
|
||||
dims := layout.Dimensions{...}
|
||||
ctx.Dimensions = dims
|
||||
})
|
||||
|
||||
Note that the example does not generate any garbage even though the
|
||||
Inset is transient. Layouts that don't accept user input are designed
|
||||
to escape to the heap during their use.
|
||||
to not escape to the heap during their use.
|
||||
|
||||
Layout operations are recursive: a child in a layout operation can
|
||||
itself be another layout. That way, complex user interfaces can
|
||||
@@ -35,10 +37,10 @@ be created from a few generic layouts.
|
||||
This example both aligns and insets a child:
|
||||
|
||||
inset := layout.Inset{...}
|
||||
inset.Layout(..., cs, func(cs layout.Constraints) layout.Dimensions {
|
||||
inset.Layout(..., ctx, func() {
|
||||
align := layout.Align{...}
|
||||
return align.Layout(..., cs, func(cs layout.Constraints) layout.Dimensions {
|
||||
return widget.Layout(..., cs)
|
||||
align.Layout(..., ctx, func() {
|
||||
widget.Layout(..., ctx)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
+18
-13
@@ -20,9 +20,9 @@ type Flex struct {
|
||||
// Alignment is the alignment in the cross axis.
|
||||
Alignment Alignment
|
||||
|
||||
ctx *Context
|
||||
macro ui.MacroOp
|
||||
ops *ui.Ops
|
||||
cs Constraints
|
||||
mode flexMode
|
||||
size int
|
||||
rigidSize int
|
||||
@@ -69,13 +69,13 @@ const (
|
||||
)
|
||||
|
||||
// Init must be called before Rigid or Flexible.
|
||||
func (f *Flex) Init(ops *ui.Ops, cs Constraints) *Flex {
|
||||
func (f *Flex) Init(ops *ui.Ops, ctx *Context) *Flex {
|
||||
if f.mode > modeBegun {
|
||||
panic("must End the current child before calling Init again")
|
||||
}
|
||||
f.mode = modeBegun
|
||||
f.ops = ops
|
||||
f.cs = cs
|
||||
f.ctx = ctx
|
||||
f.size = 0
|
||||
f.rigidSize = 0
|
||||
f.maxCross = 0
|
||||
@@ -98,20 +98,23 @@ func (f *Flex) begin(mode flexMode) {
|
||||
// from 0 to the remaining space.
|
||||
func (f *Flex) Rigid(w Widget) FlexChild {
|
||||
f.begin(modeRigid)
|
||||
mainc := axisMainConstraint(f.Axis, f.cs)
|
||||
cs := f.ctx.Constraints
|
||||
mainc := axisMainConstraint(f.Axis, cs)
|
||||
mainMax := mainc.Max - f.size
|
||||
if mainMax < 0 {
|
||||
mainMax = 0
|
||||
}
|
||||
cs := axisConstraints(f.Axis, Constraint{Max: mainMax}, axisCrossConstraint(f.Axis, f.cs))
|
||||
return f.end(w(cs))
|
||||
cs = axisConstraints(f.Axis, Constraint{Max: mainMax}, axisCrossConstraint(f.Axis, cs))
|
||||
dims := f.ctx.Layout(cs, w)
|
||||
return f.end(dims)
|
||||
}
|
||||
|
||||
// Flexible is like Rigid, where the main axis size is also constrained to a
|
||||
// fraction of the space not taken up by Rigid children.
|
||||
func (f *Flex) Flexible(weight float32, w Widget) FlexChild {
|
||||
f.begin(modeFlex)
|
||||
mainc := axisMainConstraint(f.Axis, f.cs)
|
||||
cs := f.ctx.Constraints
|
||||
mainc := axisMainConstraint(f.Axis, cs)
|
||||
var flexSize int
|
||||
if mainc.Max > f.size {
|
||||
flexSize = mainc.Max - f.rigidSize
|
||||
@@ -125,8 +128,9 @@ func (f *Flex) Flexible(weight float32, w Widget) FlexChild {
|
||||
}
|
||||
}
|
||||
submainc := Constraint{Max: flexSize}
|
||||
cs := axisConstraints(f.Axis, submainc, axisCrossConstraint(f.Axis, f.cs))
|
||||
return f.end(w(cs))
|
||||
cs = axisConstraints(f.Axis, submainc, axisCrossConstraint(f.Axis, cs))
|
||||
dims := f.ctx.Layout(cs, w)
|
||||
return f.end(dims)
|
||||
}
|
||||
|
||||
// End a child by specifying its dimensions. Pass the returned layout result
|
||||
@@ -153,9 +157,10 @@ func (f *Flex) end(dims Dimensions) FlexChild {
|
||||
|
||||
// Layout a list of children. The order of the children determines their laid
|
||||
// out order.
|
||||
func (f *Flex) Layout(children ...FlexChild) Dimensions {
|
||||
mainc := axisMainConstraint(f.Axis, f.cs)
|
||||
crossSize := axisCrossConstraint(f.Axis, f.cs).Constrain(f.maxCross)
|
||||
func (f *Flex) Layout(children ...FlexChild) {
|
||||
cs := f.ctx.Constraints
|
||||
mainc := axisMainConstraint(f.Axis, cs)
|
||||
crossSize := axisCrossConstraint(f.Axis, cs).Constrain(f.maxCross)
|
||||
var space int
|
||||
if mainc.Min > f.size {
|
||||
space = mainc.Min - f.size
|
||||
@@ -220,7 +225,7 @@ func (f *Flex) Layout(children ...FlexChild) Dimensions {
|
||||
if baseline == 0 {
|
||||
baseline = sz.Y
|
||||
}
|
||||
return Dimensions{Size: sz, Baseline: baseline}
|
||||
f.ctx.Dimensions = Dimensions{Size: sz, Baseline: baseline}
|
||||
}
|
||||
|
||||
func axisPoint(a Axis, main, cross int) image.Point {
|
||||
|
||||
+31
-12
@@ -37,9 +37,16 @@ type Alignment uint8
|
||||
// space.
|
||||
type Direction uint8
|
||||
|
||||
// Widget is a function that computes a set of dimensions that
|
||||
// satisfies the given constraints.
|
||||
type Widget func(cs Constraints) Dimensions
|
||||
// Widget is a function scope for drawing, processing input and
|
||||
// computing dimensions for a user interface element.
|
||||
type Widget func()
|
||||
|
||||
// Context tracks the current constraints and dimensions during
|
||||
// layout.
|
||||
type Context struct {
|
||||
Constraints Constraints
|
||||
Dimensions Dimensions
|
||||
}
|
||||
|
||||
const (
|
||||
Start Alignment = iota
|
||||
@@ -65,6 +72,17 @@ const (
|
||||
Vertical
|
||||
)
|
||||
|
||||
// Layout a widget with a set of constraints and return its
|
||||
// dimensions. The previous constraints are restored after layout.
|
||||
func (s *Context) Layout(cs Constraints, w Widget) Dimensions {
|
||||
saved := s.Constraints
|
||||
s.Constraints = cs
|
||||
s.Dimensions = Dimensions{}
|
||||
w()
|
||||
s.Constraints = saved
|
||||
return s.Dimensions
|
||||
}
|
||||
|
||||
// Constrain a value to the range [Min; Max].
|
||||
func (c Constraint) Constrain(v int) int {
|
||||
if v < c.Min {
|
||||
@@ -100,12 +118,12 @@ type Align struct {
|
||||
}
|
||||
|
||||
// Layout a widget.
|
||||
func (in *Inset) Layout(c ui.Config, ops *ui.Ops, cs Constraints, w Widget) Dimensions {
|
||||
func (in Inset) Layout(c ui.Config, ops *ui.Ops, ctx *Context, w Widget) {
|
||||
top := c.Px(in.Top)
|
||||
right := c.Px(in.Right)
|
||||
bottom := c.Px(in.Bottom)
|
||||
left := c.Px(in.Left)
|
||||
mcs := cs
|
||||
mcs := ctx.Constraints
|
||||
mcs.Width.Min -= left + right
|
||||
mcs.Width.Max -= left + right
|
||||
if mcs.Width.Min < 0 {
|
||||
@@ -125,10 +143,10 @@ func (in *Inset) Layout(c ui.Config, ops *ui.Ops, cs Constraints, w Widget) Dime
|
||||
var stack ui.StackOp
|
||||
stack.Push(ops)
|
||||
ui.TransformOp{}.Offset(toPointF(image.Point{X: left, Y: top})).Add(ops)
|
||||
dims := w(mcs)
|
||||
dims := ctx.Layout(mcs, w)
|
||||
stack.Pop()
|
||||
return Dimensions{
|
||||
Size: cs.Constrain(dims.Size.Add(image.Point{X: right + left, Y: top + bottom})),
|
||||
ctx.Dimensions = Dimensions{
|
||||
Size: ctx.Constraints.Constrain(dims.Size.Add(image.Point{X: right + left, Y: top + bottom})),
|
||||
Baseline: dims.Baseline + top,
|
||||
}
|
||||
}
|
||||
@@ -140,13 +158,14 @@ func UniformInset(v ui.Value) Inset {
|
||||
}
|
||||
|
||||
// Layout a widget.
|
||||
func (a *Align) Layout(ops *ui.Ops, cs Constraints, w Widget) Dimensions {
|
||||
func (a Align) Layout(ops *ui.Ops, st *Context, w Widget) {
|
||||
var macro ui.MacroOp
|
||||
macro.Record(ops)
|
||||
cs := st.Constraints
|
||||
mcs := cs
|
||||
mcs.Width.Min = 0
|
||||
mcs.Height.Min = 0
|
||||
macro.Record(ops)
|
||||
dims := w(mcs)
|
||||
dims := st.Layout(mcs, w)
|
||||
macro.Stop()
|
||||
sz := dims.Size
|
||||
if sz.X < cs.Width.Min {
|
||||
@@ -173,7 +192,7 @@ func (a *Align) Layout(ops *ui.Ops, cs Constraints, w Widget) Dimensions {
|
||||
ui.TransformOp{}.Offset(toPointF(p)).Add(ops)
|
||||
macro.Add(ops)
|
||||
stack.Pop()
|
||||
return Dimensions{
|
||||
st.Dimensions = Dimensions{
|
||||
Size: sz,
|
||||
Baseline: dims.Baseline,
|
||||
}
|
||||
|
||||
+36
-34
@@ -19,22 +19,21 @@ var cfg = new(config)
|
||||
|
||||
func ExampleInset() {
|
||||
ops := new(ui.Ops)
|
||||
ctx := new(layout.Context)
|
||||
|
||||
// Loose constraints with no minimal size.
|
||||
var cs layout.Constraints
|
||||
cs.Width.Max = 100
|
||||
cs.Height.Max = 100
|
||||
ctx.Constraints.Width.Max = 100
|
||||
ctx.Constraints.Height.Max = 100
|
||||
|
||||
// Inset all edges by 10.
|
||||
inset := layout.UniformInset(ui.Dp(10))
|
||||
dims := inset.Layout(cfg, ops, cs, func(cs layout.Constraints) layout.Dimensions {
|
||||
inset.Layout(cfg, ops, ctx, func() {
|
||||
// Lay out a 50x50 sized widget.
|
||||
dims := layoutWidget(50, 50, cs)
|
||||
fmt.Println(dims.Size)
|
||||
return dims
|
||||
layoutWidget(ctx, 50, 50)
|
||||
fmt.Println(ctx.Dimensions.Size)
|
||||
})
|
||||
|
||||
fmt.Println(dims.Size)
|
||||
fmt.Println(ctx.Dimensions.Size)
|
||||
|
||||
// Output:
|
||||
// (50,50)
|
||||
@@ -43,19 +42,19 @@ func ExampleInset() {
|
||||
|
||||
func ExampleAlign() {
|
||||
ops := new(ui.Ops)
|
||||
ctx := new(layout.Context)
|
||||
|
||||
// Rigid constraints with both minimum and maximum set.
|
||||
cs := layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||
ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||
|
||||
align := layout.Align{Alignment: layout.Center}
|
||||
dims := align.Layout(ops, cs, func(cs layout.Constraints) layout.Dimensions {
|
||||
align.Layout(ops, ctx, func() {
|
||||
// Lay out a 50x50 sized widget.
|
||||
dims := layoutWidget(50, 50, cs)
|
||||
fmt.Println(dims.Size)
|
||||
return dims
|
||||
layoutWidget(ctx, 50, 50)
|
||||
fmt.Println(ctx.Dimensions.Size)
|
||||
})
|
||||
|
||||
fmt.Println(dims.Size)
|
||||
fmt.Println(ctx.Dimensions.Size)
|
||||
|
||||
// Output:
|
||||
// (50,50)
|
||||
@@ -64,22 +63,23 @@ func ExampleAlign() {
|
||||
|
||||
func ExampleFlex() {
|
||||
ops := new(ui.Ops)
|
||||
ctx := new(layout.Context)
|
||||
|
||||
cs := layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||
ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||
|
||||
flex := layout.Flex{}
|
||||
flex.Init(ops, cs)
|
||||
flex.Init(ops, ctx)
|
||||
|
||||
// Rigid 10x10 widget.
|
||||
child1 := flex.Rigid(func(cs layout.Constraints) layout.Dimensions {
|
||||
fmt.Printf("Rigid: %v\n", cs.Width)
|
||||
return layoutWidget(10, 10, cs)
|
||||
child1 := flex.Rigid(func() {
|
||||
fmt.Printf("Rigid: %v\n", ctx.Constraints.Width)
|
||||
layoutWidget(ctx, 10, 10)
|
||||
})
|
||||
|
||||
// Child with 50% space allowance.
|
||||
child2 := flex.Flexible(0.5, func(cs layout.Constraints) layout.Dimensions {
|
||||
fmt.Printf("50%%: %v\n", cs.Width)
|
||||
return layoutWidget(10, 10, cs)
|
||||
child2 := flex.Flexible(0.5, func() {
|
||||
fmt.Printf("50%%: %v\n", ctx.Constraints.Width)
|
||||
layoutWidget(ctx, 10, 10)
|
||||
})
|
||||
|
||||
flex.Layout(child1, child2)
|
||||
@@ -91,21 +91,22 @@ func ExampleFlex() {
|
||||
|
||||
func ExampleStack() {
|
||||
ops := new(ui.Ops)
|
||||
ctx := new(layout.Context)
|
||||
|
||||
cs := layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||
ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||
|
||||
stack := layout.Stack{}
|
||||
stack.Init(ops, cs)
|
||||
stack.Init(ops, ctx)
|
||||
|
||||
// Rigid 50x50 widget.
|
||||
child1 := stack.Rigid(func(cs layout.Constraints) layout.Dimensions {
|
||||
return layoutWidget(50, 50, cs)
|
||||
child1 := stack.Rigid(func() {
|
||||
layoutWidget(ctx, 50, 50)
|
||||
})
|
||||
|
||||
// Force widget to the same size as the first.
|
||||
child2 := stack.Expand(func(cs layout.Constraints) layout.Dimensions {
|
||||
fmt.Printf("Expand: %v\n", cs)
|
||||
return layoutWidget(10, 10, cs)
|
||||
child2 := stack.Expand(func() {
|
||||
fmt.Printf("Expand: %v\n", ctx.Constraints)
|
||||
layoutWidget(ctx, 10, 10)
|
||||
})
|
||||
|
||||
stack.Layout(child1, child2)
|
||||
@@ -116,17 +117,18 @@ func ExampleStack() {
|
||||
|
||||
func ExampleList() {
|
||||
ops := new(ui.Ops)
|
||||
ctx := new(layout.Context)
|
||||
|
||||
cs := layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||
ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||
|
||||
// The list is 1e6 elements, but only 5 fit the constraints.
|
||||
const listLen = 1e6
|
||||
|
||||
var list layout.List
|
||||
count := 0
|
||||
list.Layout(cfg, q, ops, cs, listLen, func(cs layout.Constraints, i int) layout.Dimensions {
|
||||
list.Layout(cfg, q, ops, ctx, listLen, func(i int) {
|
||||
count++
|
||||
return layoutWidget(20, 20, cs)
|
||||
layoutWidget(ctx, 20, 20)
|
||||
})
|
||||
|
||||
fmt.Println(count)
|
||||
@@ -135,8 +137,8 @@ func ExampleList() {
|
||||
// 5
|
||||
}
|
||||
|
||||
func layoutWidget(width, height int, cs layout.Constraints) layout.Dimensions {
|
||||
return layout.Dimensions{
|
||||
func layoutWidget(ctx *layout.Context, width, height int) {
|
||||
ctx.Dimensions = layout.Dimensions{
|
||||
Size: image.Point{
|
||||
X: width,
|
||||
Y: height,
|
||||
|
||||
+8
-4
@@ -58,7 +58,7 @@ type List struct {
|
||||
|
||||
// ListElement is a function that computes the dimensions of
|
||||
// a list element.
|
||||
type ListElement func(cs Constraints, index int) Dimensions
|
||||
type ListElement func(index int)
|
||||
|
||||
type iterationDir uint8
|
||||
|
||||
@@ -96,12 +96,16 @@ func (l *List) init(cfg ui.Config, q input.Queue, ops *ui.Ops, cs Constraints, l
|
||||
}
|
||||
|
||||
// Layout the List and return its dimensions.
|
||||
func (l *List) Layout(c ui.Config, q input.Queue, ops *ui.Ops, cs Constraints, len int, w ListElement) Dimensions {
|
||||
func (l *List) Layout(c ui.Config, q input.Queue, ops *ui.Ops, ctx *Context, len int, w ListElement) {
|
||||
cs := ctx.Constraints
|
||||
for l.init(c, q, ops, cs, len); l.more(); l.next() {
|
||||
cs := axisConstraints(l.Axis, Constraint{Max: inf}, axisCrossConstraint(l.Axis, l.cs))
|
||||
l.end(w(cs, l.index()))
|
||||
i := l.index()
|
||||
l.end(ctx.Layout(cs, func() {
|
||||
w(i)
|
||||
}))
|
||||
}
|
||||
return l.layout()
|
||||
ctx.Dimensions = l.layout()
|
||||
}
|
||||
|
||||
func (l *List) scrollToEnd() bool {
|
||||
|
||||
+9
-7
@@ -18,7 +18,7 @@ type Stack struct {
|
||||
macro ui.MacroOp
|
||||
ops *ui.Ops
|
||||
constrained bool
|
||||
cs Constraints
|
||||
ctx *Context
|
||||
maxSZ image.Point
|
||||
baseline int
|
||||
}
|
||||
@@ -30,9 +30,9 @@ type StackChild struct {
|
||||
}
|
||||
|
||||
// Init a stack before calling Rigid or Expand.
|
||||
func (s *Stack) Init(ops *ui.Ops, cs Constraints) *Stack {
|
||||
func (s *Stack) Init(ops *ui.Ops, ctx *Context) *Stack {
|
||||
s.ops = ops
|
||||
s.cs = cs
|
||||
s.ctx = ctx
|
||||
s.constrained = true
|
||||
s.maxSZ = image.Point{}
|
||||
s.baseline = 0
|
||||
@@ -50,7 +50,8 @@ func (s *Stack) begin() {
|
||||
// passed to Init.
|
||||
func (s *Stack) Rigid(w Widget) StackChild {
|
||||
s.begin()
|
||||
return s.end(w(s.cs))
|
||||
dims := s.ctx.Layout(s.ctx.Constraints, w)
|
||||
return s.end(dims)
|
||||
}
|
||||
|
||||
// Expand lays out a widget with constraints that exactly match
|
||||
@@ -61,7 +62,8 @@ func (s *Stack) Expand(w Widget) StackChild {
|
||||
Width: Constraint{Min: s.maxSZ.X, Max: s.maxSZ.X},
|
||||
Height: Constraint{Min: s.maxSZ.Y, Max: s.maxSZ.Y},
|
||||
}
|
||||
return s.end(w(cs))
|
||||
dims := s.ctx.Layout(cs, w)
|
||||
return s.end(dims)
|
||||
}
|
||||
|
||||
// End a child by specifying its dimensions.
|
||||
@@ -83,7 +85,7 @@ func (s *Stack) end(dims Dimensions) StackChild {
|
||||
|
||||
// Layout a list of children. The order of the children determines their laid
|
||||
// out order.
|
||||
func (s *Stack) Layout(children ...StackChild) Dimensions {
|
||||
func (s *Stack) Layout(children ...StackChild) {
|
||||
for _, ch := range children {
|
||||
sz := ch.dims.Size
|
||||
var p image.Point
|
||||
@@ -109,7 +111,7 @@ func (s *Stack) Layout(children ...StackChild) Dimensions {
|
||||
if b == 0 {
|
||||
b = s.maxSZ.Y
|
||||
}
|
||||
return Dimensions{
|
||||
s.ctx.Dimensions = Dimensions{
|
||||
Size: s.maxSZ,
|
||||
Baseline: b,
|
||||
}
|
||||
|
||||
+3
-2
@@ -165,7 +165,8 @@ func (e *Editor) Focus() {
|
||||
e.requestFocus = true
|
||||
}
|
||||
|
||||
func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, cs layout.Constraints) layout.Dimensions {
|
||||
func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, ctx *layout.Context) {
|
||||
cs := ctx.Constraints
|
||||
for _, ok := e.Next(cfg, queue); ok; _, ok = e.Next(cfg, queue) {
|
||||
}
|
||||
twoDp := cfg.Px(ui.Dp(2))
|
||||
@@ -270,7 +271,7 @@ func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, cs layout
|
||||
pointer.RectAreaOp{Rect: r}.Add(ops)
|
||||
e.scroller.Add(ops)
|
||||
e.clicker.Add(ops)
|
||||
return layout.Dimensions{Size: e.viewSize, Baseline: baseline}
|
||||
ctx.Dimensions = layout.Dimensions{Size: e.viewSize, Baseline: baseline}
|
||||
}
|
||||
|
||||
// Text returns the contents of the editor.
|
||||
|
||||
+3
-2
@@ -92,7 +92,8 @@ func (l *lineIterator) Next() (String, f32.Point, bool) {
|
||||
return String{}, f32.Point{}, false
|
||||
}
|
||||
|
||||
func (l Label) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimensions {
|
||||
func (l Label) Layout(ops *ui.Ops, ctx *layout.Context) {
|
||||
cs := ctx.Constraints
|
||||
textLayout := l.Face.Layout(l.Text, LayoutOptions{MaxWidth: cs.Width.Max})
|
||||
lines := textLayout.Lines
|
||||
if max := l.MaxLines; max > 0 && len(lines) > max {
|
||||
@@ -127,7 +128,7 @@ func (l Label) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimensions {
|
||||
paint.PaintOp{Rect: lclip}.Add(ops)
|
||||
stack.Pop()
|
||||
}
|
||||
return dims
|
||||
ctx.Dimensions = dims
|
||||
}
|
||||
|
||||
func toRectF(r image.Rectangle) f32.Rectangle {
|
||||
|
||||
+3
-2
@@ -25,7 +25,7 @@ type Image struct {
|
||||
Scale float32
|
||||
}
|
||||
|
||||
func (im Image) Layout(c ui.Config, ops *ui.Ops, cs layout.Constraints) layout.Dimensions {
|
||||
func (im Image) Layout(c ui.Config, ops *ui.Ops, ctx *layout.Context) {
|
||||
size := im.Src.Bounds()
|
||||
wf, hf := float32(size.Dx()), float32(size.Dy())
|
||||
var w, h int
|
||||
@@ -35,6 +35,7 @@ func (im Image) Layout(c ui.Config, ops *ui.Ops, cs layout.Constraints) layout.D
|
||||
} else {
|
||||
w, h = int(wf*im.Scale+.5), int(hf*im.Scale+.5)
|
||||
}
|
||||
cs := ctx.Constraints
|
||||
d := image.Point{X: cs.Width.Constrain(w), Y: cs.Height.Constrain(h)}
|
||||
aspect := float32(w) / float32(h)
|
||||
dw, dh := float32(d.X), float32(d.Y)
|
||||
@@ -49,5 +50,5 @@ func (im Image) Layout(c ui.Config, ops *ui.Ops, cs layout.Constraints) layout.D
|
||||
}
|
||||
paint.ImageOp{Src: im.Src, Rect: im.Rect}.Add(ops)
|
||||
paint.PaintOp{Rect: dr}.Add(ops)
|
||||
return layout.Dimensions{Size: d, Baseline: d.Y}
|
||||
ctx.Dimensions = layout.Dimensions{Size: d, Baseline: d.Y}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user