mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 09:55:40 +00:00
ui/layout: add common state to Context
Almost every layout and widget need the ui.Config for its environment, an ui.Ops to store operations. Stateful widgets need an input.Queue for events. Add all these common objects to Context, greatly simplifying the function signatures for Gio programs. Fixes gio#33 Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+7
-7
@@ -13,17 +13,17 @@ in an implicit Context to keep the Widget declaration short.
|
|||||||
|
|
||||||
For example, to add space above a widget:
|
For example, to add space above a widget:
|
||||||
|
|
||||||
ctx := new(layout.Context)
|
c := &layout.Context{...}
|
||||||
ctx.Constraints = ...
|
c.Reset(...)
|
||||||
|
|
||||||
// Configure a top inset.
|
// Configure a top inset.
|
||||||
inset := layout.Inset{Top: ui.Dp(8), ...}
|
inset := layout.Inset{Top: ui.Dp(8), ...}
|
||||||
// Use the inset to lay out a widget.
|
// Use the inset to lay out a widget.
|
||||||
inset.Layout(..., ctx, func() {
|
inset.Layout(c, func() {
|
||||||
// Lay out widget and determine its size given the constraints.
|
// Lay out widget and determine its size given the constraints.
|
||||||
...
|
...
|
||||||
dims := layout.Dimensions{...}
|
dims := layout.Dimensions{...}
|
||||||
ctx.Dimensions = dims
|
c.Dimensions = dims
|
||||||
})
|
})
|
||||||
|
|
||||||
Note that the example does not generate any garbage even though the
|
Note that the example does not generate any garbage even though the
|
||||||
@@ -37,10 +37,10 @@ be created from a few generic layouts.
|
|||||||
This example both aligns and insets a child:
|
This example both aligns and insets a child:
|
||||||
|
|
||||||
inset := layout.Inset{...}
|
inset := layout.Inset{...}
|
||||||
inset.Layout(..., ctx, func() {
|
inset.Layout(c, func() {
|
||||||
align := layout.Align(...)
|
align := layout.Align(...)
|
||||||
align.Layout(..., ctx, func() {
|
align.Layout(c, func() {
|
||||||
widget.Layout(..., ctx)
|
widget.Layout(c, ...)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+6
-8
@@ -22,7 +22,6 @@ type Flex struct {
|
|||||||
|
|
||||||
ctx *Context
|
ctx *Context
|
||||||
macro ui.MacroOp
|
macro ui.MacroOp
|
||||||
ops *ui.Ops
|
|
||||||
mode flexMode
|
mode flexMode
|
||||||
size int
|
size int
|
||||||
rigidSize int
|
rigidSize int
|
||||||
@@ -69,13 +68,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Init must be called before Rigid or Flexible.
|
// Init must be called before Rigid or Flexible.
|
||||||
func (f *Flex) Init(ops *ui.Ops, ctx *Context) *Flex {
|
func (f *Flex) Init(c *Context) *Flex {
|
||||||
if f.mode > modeBegun {
|
if f.mode > modeBegun {
|
||||||
panic("must End the current child before calling Init again")
|
panic("must End the current child before calling Init again")
|
||||||
}
|
}
|
||||||
f.mode = modeBegun
|
f.mode = modeBegun
|
||||||
f.ops = ops
|
f.ctx = c
|
||||||
f.ctx = ctx
|
|
||||||
f.size = 0
|
f.size = 0
|
||||||
f.rigidSize = 0
|
f.rigidSize = 0
|
||||||
f.maxCross = 0
|
f.maxCross = 0
|
||||||
@@ -91,7 +89,7 @@ func (f *Flex) begin(mode flexMode) {
|
|||||||
panic("must End before adding a child")
|
panic("must End before adding a child")
|
||||||
}
|
}
|
||||||
f.mode = mode
|
f.mode = mode
|
||||||
f.macro.Record(f.ops)
|
f.macro.Record(f.ctx.Ops)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rigid lays out a widget with the main axis constrained to the range
|
// Rigid lays out a widget with the main axis constrained to the range
|
||||||
@@ -192,9 +190,9 @@ func (f *Flex) Layout(children ...FlexChild) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var stack ui.StackOp
|
var stack ui.StackOp
|
||||||
stack.Push(f.ops)
|
stack.Push(f.ctx.Ops)
|
||||||
ui.TransformOp{}.Offset(toPointF(axisPoint(f.Axis, mainSize, cross))).Add(f.ops)
|
ui.TransformOp{}.Offset(toPointF(axisPoint(f.Axis, mainSize, cross))).Add(f.ctx.Ops)
|
||||||
child.macro.Add(f.ops)
|
child.macro.Add(f.ctx.Ops)
|
||||||
stack.Pop()
|
stack.Pop()
|
||||||
mainSize += axisMain(f.Axis, dims.Size)
|
mainSize += axisMain(f.Axis, dims.Size)
|
||||||
if i < len(children)-1 {
|
if i < len(children)-1 {
|
||||||
|
|||||||
+36
-18
@@ -6,6 +6,7 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
|
|
||||||
"gioui.org/ui"
|
"gioui.org/ui"
|
||||||
|
"gioui.org/ui/input"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Constraints represent a set of acceptable ranges for
|
// Constraints represent a set of acceptable ranges for
|
||||||
@@ -41,11 +42,18 @@ type Direction uint8
|
|||||||
// computing dimensions for a user interface element.
|
// computing dimensions for a user interface element.
|
||||||
type Widget func()
|
type Widget func()
|
||||||
|
|
||||||
// Context tracks the current constraints and dimensions during
|
// Context carry the state needed by almost all layouts and widgets.
|
||||||
// layout.
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
|
// Constraints track the constraints for the active widget or
|
||||||
|
// layout.
|
||||||
Constraints Constraints
|
Constraints Constraints
|
||||||
Dimensions Dimensions
|
// Dimensions track the result of the most recent layout
|
||||||
|
// operation.
|
||||||
|
Dimensions Dimensions
|
||||||
|
|
||||||
|
ui.Config
|
||||||
|
input.Queue
|
||||||
|
*ui.Ops
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -83,6 +91,16 @@ func (s *Context) Layout(cs Constraints, w Widget) Dimensions {
|
|||||||
return s.Dimensions
|
return s.Dimensions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset the context.
|
||||||
|
func (c *Context) Reset(cfg ui.Config, cs Constraints) {
|
||||||
|
c.Constraints = cs
|
||||||
|
c.Config = cfg
|
||||||
|
if c.Ops == nil {
|
||||||
|
c.Ops = new(ui.Ops)
|
||||||
|
}
|
||||||
|
c.Ops.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
// Constrain a value to the range [Min; Max].
|
// Constrain a value to the range [Min; Max].
|
||||||
func (c Constraint) Constrain(v int) int {
|
func (c Constraint) Constrain(v int) int {
|
||||||
if v < c.Min {
|
if v < c.Min {
|
||||||
@@ -116,12 +134,12 @@ type Inset struct {
|
|||||||
type Align Direction
|
type Align Direction
|
||||||
|
|
||||||
// Layout a widget.
|
// Layout a widget.
|
||||||
func (in Inset) Layout(c ui.Config, ops *ui.Ops, ctx *Context, w Widget) {
|
func (in Inset) Layout(c *Context, w Widget) {
|
||||||
top := c.Px(in.Top)
|
top := c.Px(in.Top)
|
||||||
right := c.Px(in.Right)
|
right := c.Px(in.Right)
|
||||||
bottom := c.Px(in.Bottom)
|
bottom := c.Px(in.Bottom)
|
||||||
left := c.Px(in.Left)
|
left := c.Px(in.Left)
|
||||||
mcs := ctx.Constraints
|
mcs := c.Constraints
|
||||||
mcs.Width.Min -= left + right
|
mcs.Width.Min -= left + right
|
||||||
mcs.Width.Max -= left + right
|
mcs.Width.Max -= left + right
|
||||||
if mcs.Width.Min < 0 {
|
if mcs.Width.Min < 0 {
|
||||||
@@ -139,12 +157,12 @@ func (in Inset) Layout(c ui.Config, ops *ui.Ops, ctx *Context, w Widget) {
|
|||||||
mcs.Height.Max = mcs.Height.Min
|
mcs.Height.Max = mcs.Height.Min
|
||||||
}
|
}
|
||||||
var stack ui.StackOp
|
var stack ui.StackOp
|
||||||
stack.Push(ops)
|
stack.Push(c.Ops)
|
||||||
ui.TransformOp{}.Offset(toPointF(image.Point{X: left, Y: top})).Add(ops)
|
ui.TransformOp{}.Offset(toPointF(image.Point{X: left, Y: top})).Add(c.Ops)
|
||||||
dims := ctx.Layout(mcs, w)
|
dims := c.Layout(mcs, w)
|
||||||
stack.Pop()
|
stack.Pop()
|
||||||
ctx.Dimensions = Dimensions{
|
c.Dimensions = Dimensions{
|
||||||
Size: ctx.Constraints.Constrain(dims.Size.Add(image.Point{X: right + left, Y: top + bottom})),
|
Size: c.Constraints.Constrain(dims.Size.Add(image.Point{X: right + left, Y: top + bottom})),
|
||||||
Baseline: dims.Baseline + top,
|
Baseline: dims.Baseline + top,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,14 +174,14 @@ func UniformInset(v ui.Value) Inset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Layout a widget.
|
// Layout a widget.
|
||||||
func (a Align) Layout(ops *ui.Ops, st *Context, w Widget) {
|
func (a Align) Layout(c *Context, w Widget) {
|
||||||
var macro ui.MacroOp
|
var macro ui.MacroOp
|
||||||
macro.Record(ops)
|
macro.Record(c.Ops)
|
||||||
cs := st.Constraints
|
cs := c.Constraints
|
||||||
mcs := cs
|
mcs := cs
|
||||||
mcs.Width.Min = 0
|
mcs.Width.Min = 0
|
||||||
mcs.Height.Min = 0
|
mcs.Height.Min = 0
|
||||||
dims := st.Layout(mcs, w)
|
dims := c.Layout(mcs, w)
|
||||||
macro.Stop()
|
macro.Stop()
|
||||||
sz := dims.Size
|
sz := dims.Size
|
||||||
if sz.X < cs.Width.Min {
|
if sz.X < cs.Width.Min {
|
||||||
@@ -186,11 +204,11 @@ func (a Align) Layout(ops *ui.Ops, st *Context, w Widget) {
|
|||||||
p.Y = sz.Y - dims.Size.Y
|
p.Y = sz.Y - dims.Size.Y
|
||||||
}
|
}
|
||||||
var stack ui.StackOp
|
var stack ui.StackOp
|
||||||
stack.Push(ops)
|
stack.Push(c.Ops)
|
||||||
ui.TransformOp{}.Offset(toPointF(p)).Add(ops)
|
ui.TransformOp{}.Offset(toPointF(p)).Add(c.Ops)
|
||||||
macro.Add(ops)
|
macro.Add(c.Ops)
|
||||||
stack.Pop()
|
stack.Pop()
|
||||||
st.Dimensions = Dimensions{
|
c.Dimensions = Dimensions{
|
||||||
Size: sz,
|
Size: sz,
|
||||||
Baseline: dims.Baseline,
|
Baseline: dims.Baseline,
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-40
@@ -18,22 +18,22 @@ var q queue
|
|||||||
var cfg = new(config)
|
var cfg = new(config)
|
||||||
|
|
||||||
func ExampleInset() {
|
func ExampleInset() {
|
||||||
ops := new(ui.Ops)
|
c := &layout.Context{Queue: q}
|
||||||
ctx := new(layout.Context)
|
|
||||||
|
|
||||||
// Loose constraints with no minimal size.
|
// Loose constraints with no minimal size.
|
||||||
ctx.Constraints.Width.Max = 100
|
var cs layout.Constraints
|
||||||
ctx.Constraints.Height.Max = 100
|
cs.Width.Max = 100
|
||||||
|
cs.Height.Max = 100
|
||||||
|
c.Reset(cfg, cs)
|
||||||
|
|
||||||
// Inset all edges by 10.
|
// Inset all edges by 10.
|
||||||
inset := layout.UniformInset(ui.Dp(10))
|
inset := layout.UniformInset(ui.Dp(10))
|
||||||
inset.Layout(cfg, ops, ctx, func() {
|
inset.Layout(c, func() {
|
||||||
// Lay out a 50x50 sized widget.
|
// Lay out a 50x50 sized widget.
|
||||||
layoutWidget(ctx, 50, 50)
|
layoutWidget(c, 50, 50)
|
||||||
fmt.Println(ctx.Dimensions.Size)
|
fmt.Println(c.Dimensions.Size)
|
||||||
})
|
})
|
||||||
|
|
||||||
fmt.Println(ctx.Dimensions.Size)
|
fmt.Println(c.Dimensions.Size)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// (50,50)
|
// (50,50)
|
||||||
@@ -41,20 +41,19 @@ func ExampleInset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExampleAlign() {
|
func ExampleAlign() {
|
||||||
ops := new(ui.Ops)
|
c := &layout.Context{Queue: q}
|
||||||
ctx := new(layout.Context)
|
|
||||||
|
|
||||||
// Rigid constraints with both minimum and maximum set.
|
// Rigid constraints with both minimum and maximum set.
|
||||||
ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
cs := layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||||
|
c.Reset(cfg, cs)
|
||||||
|
|
||||||
align := layout.Align(layout.Center)
|
align := layout.Align(layout.Center)
|
||||||
align.Layout(ops, ctx, func() {
|
align.Layout(c, func() {
|
||||||
// Lay out a 50x50 sized widget.
|
// Lay out a 50x50 sized widget.
|
||||||
layoutWidget(ctx, 50, 50)
|
layoutWidget(c, 50, 50)
|
||||||
fmt.Println(ctx.Dimensions.Size)
|
fmt.Println(c.Dimensions.Size)
|
||||||
})
|
})
|
||||||
|
|
||||||
fmt.Println(ctx.Dimensions.Size)
|
fmt.Println(c.Dimensions.Size)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// (50,50)
|
// (50,50)
|
||||||
@@ -62,24 +61,23 @@ func ExampleAlign() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExampleFlex() {
|
func ExampleFlex() {
|
||||||
ops := new(ui.Ops)
|
c := &layout.Context{Queue: q}
|
||||||
ctx := new(layout.Context)
|
cs := layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||||
|
c.Reset(cfg, cs)
|
||||||
ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
|
||||||
|
|
||||||
flex := layout.Flex{}
|
flex := layout.Flex{}
|
||||||
flex.Init(ops, ctx)
|
flex.Init(c)
|
||||||
|
|
||||||
// Rigid 10x10 widget.
|
// Rigid 10x10 widget.
|
||||||
child1 := flex.Rigid(func() {
|
child1 := flex.Rigid(func() {
|
||||||
fmt.Printf("Rigid: %v\n", ctx.Constraints.Width)
|
fmt.Printf("Rigid: %v\n", c.Constraints.Width)
|
||||||
layoutWidget(ctx, 10, 10)
|
layoutWidget(c, 10, 10)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Child with 50% space allowance.
|
// Child with 50% space allowance.
|
||||||
child2 := flex.Flexible(0.5, func() {
|
child2 := flex.Flexible(0.5, func() {
|
||||||
fmt.Printf("50%%: %v\n", ctx.Constraints.Width)
|
fmt.Printf("50%%: %v\n", c.Constraints.Width)
|
||||||
layoutWidget(ctx, 10, 10)
|
layoutWidget(c, 10, 10)
|
||||||
})
|
})
|
||||||
|
|
||||||
flex.Layout(child1, child2)
|
flex.Layout(child1, child2)
|
||||||
@@ -90,23 +88,22 @@ func ExampleFlex() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExampleStack() {
|
func ExampleStack() {
|
||||||
ops := new(ui.Ops)
|
c := &layout.Context{Queue: q}
|
||||||
ctx := new(layout.Context)
|
cs := layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||||
|
c.Reset(cfg, cs)
|
||||||
ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
|
||||||
|
|
||||||
stack := layout.Stack{}
|
stack := layout.Stack{}
|
||||||
stack.Init(ops, ctx)
|
stack.Init(c)
|
||||||
|
|
||||||
// Rigid 50x50 widget.
|
// Rigid 50x50 widget.
|
||||||
child1 := stack.Rigid(func() {
|
child1 := stack.Rigid(func() {
|
||||||
layoutWidget(ctx, 50, 50)
|
layoutWidget(c, 50, 50)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Force widget to the same size as the first.
|
// Force widget to the same size as the first.
|
||||||
child2 := stack.Expand(func() {
|
child2 := stack.Expand(func() {
|
||||||
fmt.Printf("Expand: %v\n", ctx.Constraints)
|
fmt.Printf("Expand: %v\n", c.Constraints)
|
||||||
layoutWidget(ctx, 10, 10)
|
layoutWidget(c, 10, 10)
|
||||||
})
|
})
|
||||||
|
|
||||||
stack.Layout(child1, child2)
|
stack.Layout(child1, child2)
|
||||||
@@ -116,19 +113,18 @@ func ExampleStack() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExampleList() {
|
func ExampleList() {
|
||||||
ops := new(ui.Ops)
|
c := &layout.Context{Queue: q}
|
||||||
ctx := new(layout.Context)
|
cs := layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
||||||
|
c.Reset(cfg, cs)
|
||||||
ctx.Constraints = layout.RigidConstraints(image.Point{X: 100, Y: 100})
|
|
||||||
|
|
||||||
// The list is 1e6 elements, but only 5 fit the constraints.
|
// The list is 1e6 elements, but only 5 fit the constraints.
|
||||||
const listLen = 1e6
|
const listLen = 1e6
|
||||||
|
|
||||||
var list layout.List
|
var list layout.List
|
||||||
count := 0
|
count := 0
|
||||||
list.Layout(cfg, q, ops, ctx, listLen, func(i int) {
|
list.Layout(c, listLen, func(i int) {
|
||||||
count++
|
count++
|
||||||
layoutWidget(ctx, 20, 20)
|
layoutWidget(c, 20, 20)
|
||||||
})
|
})
|
||||||
|
|
||||||
fmt.Println(count)
|
fmt.Println(count)
|
||||||
|
|||||||
+15
-23
@@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"gioui.org/ui"
|
"gioui.org/ui"
|
||||||
"gioui.org/ui/gesture"
|
"gioui.org/ui/gesture"
|
||||||
"gioui.org/ui/input"
|
|
||||||
"gioui.org/ui/paint"
|
"gioui.org/ui/paint"
|
||||||
"gioui.org/ui/pointer"
|
"gioui.org/ui/pointer"
|
||||||
)
|
)
|
||||||
@@ -33,9 +32,7 @@ type List struct {
|
|||||||
// the very end.
|
// the very end.
|
||||||
beforeEnd bool
|
beforeEnd bool
|
||||||
|
|
||||||
config ui.Config
|
ctx *Context
|
||||||
ops *ui.Ops
|
|
||||||
queue input.Queue
|
|
||||||
macro ui.MacroOp
|
macro ui.MacroOp
|
||||||
child ui.MacroOp
|
child ui.MacroOp
|
||||||
scroll gesture.Scroll
|
scroll gesture.Scroll
|
||||||
@@ -47,7 +44,6 @@ type List struct {
|
|||||||
// to the child with index first.
|
// to the child with index first.
|
||||||
offset int
|
offset int
|
||||||
|
|
||||||
cs Constraints
|
|
||||||
len int
|
len int
|
||||||
|
|
||||||
// maxSize is the total size of visible children.
|
// maxSize is the total size of visible children.
|
||||||
@@ -71,18 +67,15 @@ const (
|
|||||||
const inf = 1e6
|
const inf = 1e6
|
||||||
|
|
||||||
// Init prepares the list for iterating through its children with Next.
|
// Init prepares the list for iterating through its children with Next.
|
||||||
func (l *List) init(cfg ui.Config, q input.Queue, ops *ui.Ops, cs Constraints, len int) {
|
func (l *List) init(c *Context, len int) {
|
||||||
if l.more() {
|
if l.more() {
|
||||||
panic("unfinished child")
|
panic("unfinished child")
|
||||||
}
|
}
|
||||||
l.config = cfg
|
l.ctx = c
|
||||||
l.queue = q
|
|
||||||
l.update()
|
|
||||||
l.ops = ops
|
|
||||||
l.maxSize = 0
|
l.maxSize = 0
|
||||||
l.children = l.children[:0]
|
l.children = l.children[:0]
|
||||||
l.cs = cs
|
|
||||||
l.len = len
|
l.len = len
|
||||||
|
l.update()
|
||||||
if l.scrollToEnd() {
|
if l.scrollToEnd() {
|
||||||
l.offset = 0
|
l.offset = 0
|
||||||
l.first = len
|
l.first = len
|
||||||
@@ -91,21 +84,20 @@ func (l *List) init(cfg ui.Config, q input.Queue, ops *ui.Ops, cs Constraints, l
|
|||||||
l.offset = 0
|
l.offset = 0
|
||||||
l.first = len
|
l.first = len
|
||||||
}
|
}
|
||||||
l.macro.Record(ops)
|
l.macro.Record(c.Ops)
|
||||||
l.next()
|
l.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layout the List and return its dimensions.
|
// Layout the List and return its dimensions.
|
||||||
func (l *List) Layout(c ui.Config, q input.Queue, ops *ui.Ops, ctx *Context, len int, w ListElement) {
|
func (l *List) Layout(c *Context, len int, w ListElement) {
|
||||||
cs := ctx.Constraints
|
for l.init(c, len); l.more(); l.next() {
|
||||||
for l.init(c, q, ops, cs, len); l.more(); l.next() {
|
cs := axisConstraints(l.Axis, Constraint{Max: inf}, axisCrossConstraint(l.Axis, l.ctx.Constraints))
|
||||||
cs := axisConstraints(l.Axis, Constraint{Max: inf}, axisCrossConstraint(l.Axis, l.cs))
|
|
||||||
i := l.index()
|
i := l.index()
|
||||||
l.end(ctx.Layout(cs, func() {
|
l.end(c.Layout(cs, func() {
|
||||||
w(i)
|
w(i)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
ctx.Dimensions = l.layout()
|
c.Dimensions = l.layout()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *List) scrollToEnd() bool {
|
func (l *List) scrollToEnd() bool {
|
||||||
@@ -118,7 +110,7 @@ func (l *List) Dragging() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *List) update() {
|
func (l *List) update() {
|
||||||
d := l.scroll.Scroll(l.config, l.queue, gesture.Axis(l.Axis))
|
d := l.scroll.Scroll(l.ctx.Config, l.ctx.Queue, gesture.Axis(l.Axis))
|
||||||
l.scrollDelta = d
|
l.scrollDelta = d
|
||||||
l.offset += d
|
l.offset += d
|
||||||
}
|
}
|
||||||
@@ -134,7 +126,7 @@ func (l *List) next() {
|
|||||||
l.dir = l.nextDir()
|
l.dir = l.nextDir()
|
||||||
}
|
}
|
||||||
if l.more() {
|
if l.more() {
|
||||||
l.child.Record(l.ops)
|
l.child.Record(l.ctx.Ops)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +148,7 @@ func (l *List) more() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *List) nextDir() iterationDir {
|
func (l *List) nextDir() iterationDir {
|
||||||
vsize := axisMainConstraint(l.Axis, l.cs).Max
|
vsize := axisMainConstraint(l.Axis, l.ctx.Constraints).Max
|
||||||
last := l.first + len(l.children)
|
last := l.first + len(l.children)
|
||||||
// Clamp offset.
|
// Clamp offset.
|
||||||
if l.maxSize-l.offset < vsize && last == l.len {
|
if l.maxSize-l.offset < vsize && last == l.len {
|
||||||
@@ -200,7 +192,7 @@ func (l *List) layout() Dimensions {
|
|||||||
if l.more() {
|
if l.more() {
|
||||||
panic("unfinished child")
|
panic("unfinished child")
|
||||||
}
|
}
|
||||||
mainc := axisMainConstraint(l.Axis, l.cs)
|
mainc := axisMainConstraint(l.Axis, l.ctx.Constraints)
|
||||||
children := l.children
|
children := l.children
|
||||||
// Skip invisible children
|
// Skip invisible children
|
||||||
for len(children) > 0 {
|
for len(children) > 0 {
|
||||||
@@ -226,7 +218,7 @@ func (l *List) layout() Dimensions {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ops := l.ops
|
ops := l.ctx.Ops
|
||||||
pos := -l.offset
|
pos := -l.offset
|
||||||
// ScrollToEnd lists lists are end aligned.
|
// ScrollToEnd lists lists are end aligned.
|
||||||
if space := mainc.Max - size; l.ScrollToEnd && space > 0 {
|
if space := mainc.Max - size; l.ScrollToEnd && space > 0 {
|
||||||
|
|||||||
+6
-8
@@ -16,7 +16,6 @@ type Stack struct {
|
|||||||
Alignment Direction
|
Alignment Direction
|
||||||
|
|
||||||
macro ui.MacroOp
|
macro ui.MacroOp
|
||||||
ops *ui.Ops
|
|
||||||
constrained bool
|
constrained bool
|
||||||
ctx *Context
|
ctx *Context
|
||||||
maxSZ image.Point
|
maxSZ image.Point
|
||||||
@@ -30,9 +29,8 @@ type StackChild struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init a stack before calling Rigid or Expand.
|
// Init a stack before calling Rigid or Expand.
|
||||||
func (s *Stack) Init(ops *ui.Ops, ctx *Context) *Stack {
|
func (s *Stack) Init(c *Context) *Stack {
|
||||||
s.ops = ops
|
s.ctx = c
|
||||||
s.ctx = ctx
|
|
||||||
s.constrained = true
|
s.constrained = true
|
||||||
s.maxSZ = image.Point{}
|
s.maxSZ = image.Point{}
|
||||||
s.baseline = 0
|
s.baseline = 0
|
||||||
@@ -43,7 +41,7 @@ func (s *Stack) begin() {
|
|||||||
if !s.constrained {
|
if !s.constrained {
|
||||||
panic("must Init before adding a child")
|
panic("must Init before adding a child")
|
||||||
}
|
}
|
||||||
s.macro.Record(s.ops)
|
s.macro.Record(s.ctx.Ops)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rigid lays out a widget with the same constraints that were
|
// Rigid lays out a widget with the same constraints that were
|
||||||
@@ -102,9 +100,9 @@ func (s *Stack) Layout(children ...StackChild) {
|
|||||||
p.Y = s.maxSZ.Y - sz.Y
|
p.Y = s.maxSZ.Y - sz.Y
|
||||||
}
|
}
|
||||||
var stack ui.StackOp
|
var stack ui.StackOp
|
||||||
stack.Push(s.ops)
|
stack.Push(s.ctx.Ops)
|
||||||
ui.TransformOp{}.Offset(toPointF(p)).Add(s.ops)
|
ui.TransformOp{}.Offset(toPointF(p)).Add(s.ctx.Ops)
|
||||||
ch.macro.Add(s.ops)
|
ch.macro.Add(s.ctx.Ops)
|
||||||
stack.Pop()
|
stack.Pop()
|
||||||
}
|
}
|
||||||
b := s.baseline
|
b := s.baseline
|
||||||
|
|||||||
+37
-38
@@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"gioui.org/ui"
|
"gioui.org/ui"
|
||||||
"gioui.org/ui/gesture"
|
"gioui.org/ui/gesture"
|
||||||
"gioui.org/ui/input"
|
|
||||||
"gioui.org/ui/key"
|
"gioui.org/ui/key"
|
||||||
"gioui.org/ui/layout"
|
"gioui.org/ui/layout"
|
||||||
"gioui.org/ui/paint"
|
"gioui.org/ui/paint"
|
||||||
@@ -82,9 +81,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Next returns the next available editor event, or false if none are available.
|
// Next returns the next available editor event, or false if none are available.
|
||||||
func (e *Editor) Next(cfg ui.Config, queue input.Queue) (EditorEvent, bool) {
|
func (e *Editor) Next(c *layout.Context) (EditorEvent, bool) {
|
||||||
// Crude configuration change detection.
|
// Crude configuration change detection.
|
||||||
if scale := cfg.Px(ui.Sp(100)); scale != e.oldScale {
|
if scale := c.Px(ui.Sp(100)); scale != e.oldScale {
|
||||||
e.invalidate()
|
e.invalidate()
|
||||||
e.oldScale = scale
|
e.oldScale = scale
|
||||||
}
|
}
|
||||||
@@ -98,7 +97,7 @@ func (e *Editor) Next(cfg ui.Config, queue input.Queue) (EditorEvent, bool) {
|
|||||||
axis = gesture.Vertical
|
axis = gesture.Vertical
|
||||||
smin, smax = sbounds.Min.Y, sbounds.Max.Y
|
smin, smax = sbounds.Min.Y, sbounds.Max.Y
|
||||||
}
|
}
|
||||||
sdist := e.scroller.Scroll(cfg, queue, axis)
|
sdist := e.scroller.Scroll(c.Config, c.Queue, axis)
|
||||||
var soff int
|
var soff int
|
||||||
if e.SingleLine {
|
if e.SingleLine {
|
||||||
e.scrollOff.X += sdist
|
e.scrollOff.X += sdist
|
||||||
@@ -107,26 +106,26 @@ func (e *Editor) Next(cfg ui.Config, queue input.Queue) (EditorEvent, bool) {
|
|||||||
e.scrollOff.Y += sdist
|
e.scrollOff.Y += sdist
|
||||||
soff = e.scrollOff.Y
|
soff = e.scrollOff.Y
|
||||||
}
|
}
|
||||||
for evt, ok := e.clicker.Next(queue); ok; evt, ok = e.clicker.Next(queue) {
|
for evt, ok := e.clicker.Next(c.Queue); ok; evt, ok = e.clicker.Next(c.Queue) {
|
||||||
switch {
|
switch {
|
||||||
case evt.Type == gesture.TypePress && evt.Source == pointer.Mouse,
|
case evt.Type == gesture.TypePress && evt.Source == pointer.Mouse,
|
||||||
evt.Type == gesture.TypeClick && evt.Source == pointer.Touch:
|
evt.Type == gesture.TypeClick && evt.Source == pointer.Touch:
|
||||||
e.blinkStart = cfg.Now()
|
e.blinkStart = c.Now()
|
||||||
e.moveCoord(image.Point{
|
e.moveCoord(image.Point{
|
||||||
X: int(math.Round(float64(evt.Position.X))),
|
X: int(math.Round(float64(evt.Position.X))),
|
||||||
Y: int(math.Round(float64(evt.Position.Y))),
|
Y: int(math.Round(float64(evt.Position.Y))),
|
||||||
})
|
})
|
||||||
e.requestFocus = true
|
e.requestFocus = true
|
||||||
if e.scroller.State() != gesture.StateFlinging {
|
if e.scroller.State() != gesture.StateFlinging {
|
||||||
e.scrollToCaret(cfg)
|
e.scrollToCaret(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sdist > 0 && soff >= smax) || (sdist < 0 && soff <= smin) {
|
if (sdist > 0 && soff >= smax) || (sdist < 0 && soff <= smin) {
|
||||||
e.scroller.Stop()
|
e.scroller.Stop()
|
||||||
}
|
}
|
||||||
for ke, ok := queue.Next(e); ok; ke, ok = queue.Next(e) {
|
for ke, ok := c.Queue.Next(e); ok; ke, ok = c.Queue.Next(e) {
|
||||||
e.blinkStart = cfg.Now()
|
e.blinkStart = c.Now()
|
||||||
switch ke := ke.(type) {
|
switch ke := ke.(type) {
|
||||||
case key.FocusEvent:
|
case key.FocusEvent:
|
||||||
e.focused = ke.Focus
|
e.focused = ke.Focus
|
||||||
@@ -140,11 +139,11 @@ func (e *Editor) Next(cfg ui.Config, queue input.Queue) (EditorEvent, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if e.command(ke) {
|
if e.command(ke) {
|
||||||
e.scrollToCaret(cfg)
|
e.scrollToCaret(c.Config)
|
||||||
e.scroller.Stop()
|
e.scroller.Stop()
|
||||||
}
|
}
|
||||||
case key.EditEvent:
|
case key.EditEvent:
|
||||||
e.scrollToCaret(cfg)
|
e.scrollToCaret(c)
|
||||||
e.scroller.Stop()
|
e.scroller.Stop()
|
||||||
e.append(ke.Text)
|
e.append(ke.Text)
|
||||||
}
|
}
|
||||||
@@ -165,11 +164,11 @@ func (e *Editor) Focus() {
|
|||||||
e.requestFocus = true
|
e.requestFocus = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, ctx *layout.Context) {
|
func (e *Editor) Layout(c *layout.Context) {
|
||||||
cs := ctx.Constraints
|
cs := c.Constraints
|
||||||
for _, ok := e.Next(cfg, queue); ok; _, ok = e.Next(cfg, queue) {
|
for _, ok := e.Next(c); ok; _, ok = e.Next(c) {
|
||||||
}
|
}
|
||||||
twoDp := cfg.Px(ui.Dp(2))
|
twoDp := c.Px(ui.Dp(2))
|
||||||
e.padLeft, e.padRight = twoDp, twoDp
|
e.padLeft, e.padRight = twoDp, twoDp
|
||||||
maxWidth := cs.Width.Max
|
maxWidth := cs.Width.Max
|
||||||
if e.SingleLine {
|
if e.SingleLine {
|
||||||
@@ -197,7 +196,7 @@ func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, ctx *layo
|
|||||||
Min: image.Point{X: 0, Y: 0},
|
Min: image.Point{X: 0, Y: 0},
|
||||||
Max: image.Point{X: e.viewSize.X, Y: e.viewSize.Y},
|
Max: image.Point{X: e.viewSize.X, Y: e.viewSize.Y},
|
||||||
}
|
}
|
||||||
key.InputOp{Key: e, Focus: e.requestFocus}.Add(ops)
|
key.InputOp{Key: e, Focus: e.requestFocus}.Add(c.Ops)
|
||||||
e.requestFocus = false
|
e.requestFocus = false
|
||||||
e.it = lineIterator{
|
e.it = lineIterator{
|
||||||
Lines: lines,
|
Lines: lines,
|
||||||
@@ -207,14 +206,14 @@ func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, ctx *layo
|
|||||||
Offset: off,
|
Offset: off,
|
||||||
}
|
}
|
||||||
var stack ui.StackOp
|
var stack ui.StackOp
|
||||||
stack.Push(ops)
|
stack.Push(c.Ops)
|
||||||
// Apply material. Set a default color in case the material is empty.
|
// Apply material. Set a default color in case the material is empty.
|
||||||
if e.rr.len() > 0 {
|
if e.rr.len() > 0 {
|
||||||
paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(ops)
|
paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(c.Ops)
|
||||||
e.Material.Add(ops)
|
e.Material.Add(c.Ops)
|
||||||
} else {
|
} else {
|
||||||
paint.ColorOp{Color: color.RGBA{A: 0xaa}}.Add(ops)
|
paint.ColorOp{Color: color.RGBA{A: 0xaa}}.Add(c.Ops)
|
||||||
e.HintMaterial.Add(ops)
|
e.HintMaterial.Add(c.Ops)
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
str, lineOff, ok := e.it.Next()
|
str, lineOff, ok := e.it.Next()
|
||||||
@@ -222,21 +221,21 @@ func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, ctx *layo
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
var stack ui.StackOp
|
var stack ui.StackOp
|
||||||
stack.Push(ops)
|
stack.Push(c.Ops)
|
||||||
ui.TransformOp{}.Offset(lineOff).Add(ops)
|
ui.TransformOp{}.Offset(lineOff).Add(c.Ops)
|
||||||
e.Face.Path(str).Add(ops)
|
e.Face.Path(str).Add(c.Ops)
|
||||||
paint.PaintOp{Rect: toRectF(clip).Sub(lineOff)}.Add(ops)
|
paint.PaintOp{Rect: toRectF(clip).Sub(lineOff)}.Add(c.Ops)
|
||||||
stack.Pop()
|
stack.Pop()
|
||||||
}
|
}
|
||||||
if e.focused {
|
if e.focused {
|
||||||
now := cfg.Now()
|
now := c.Now()
|
||||||
dt := now.Sub(e.blinkStart)
|
dt := now.Sub(e.blinkStart)
|
||||||
blinking := dt < maxBlinkDuration
|
blinking := dt < maxBlinkDuration
|
||||||
const timePerBlink = time.Second / blinksPerSecond
|
const timePerBlink = time.Second / blinksPerSecond
|
||||||
nextBlink := now.Add(timePerBlink/2 - dt%(timePerBlink/2))
|
nextBlink := now.Add(timePerBlink/2 - dt%(timePerBlink/2))
|
||||||
on := !blinking || dt%timePerBlink < timePerBlink/2
|
on := !blinking || dt%timePerBlink < timePerBlink/2
|
||||||
if on {
|
if on {
|
||||||
carWidth := e.caretWidth(cfg)
|
carWidth := e.caretWidth(c)
|
||||||
carX -= carWidth / 2
|
carX -= carWidth / 2
|
||||||
carAsc, carDesc := -lines[carLine].Bounds.Min.Y, lines[carLine].Bounds.Max.Y
|
carAsc, carDesc := -lines[carLine].Bounds.Min.Y, lines[carLine].Bounds.Max.Y
|
||||||
carRect := image.Rectangle{
|
carRect := image.Rectangle{
|
||||||
@@ -249,29 +248,29 @@ func (e *Editor) Layout(cfg ui.Config, queue input.Queue, ops *ui.Ops, ctx *layo
|
|||||||
})
|
})
|
||||||
carRect = clip.Intersect(carRect)
|
carRect = clip.Intersect(carRect)
|
||||||
if !carRect.Empty() {
|
if !carRect.Empty() {
|
||||||
paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(ops)
|
paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(c.Ops)
|
||||||
e.Material.Add(ops)
|
e.Material.Add(c.Ops)
|
||||||
paint.PaintOp{Rect: toRectF(carRect)}.Add(ops)
|
paint.PaintOp{Rect: toRectF(carRect)}.Add(c.Ops)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if blinking {
|
if blinking {
|
||||||
redraw := ui.InvalidateOp{At: nextBlink}
|
redraw := ui.InvalidateOp{At: nextBlink}
|
||||||
redraw.Add(ops)
|
redraw.Add(c.Ops)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stack.Pop()
|
stack.Pop()
|
||||||
|
|
||||||
baseline := e.padTop + e.dims.Baseline
|
baseline := e.padTop + e.dims.Baseline
|
||||||
pointerPadding := cfg.Px(ui.Dp(4))
|
pointerPadding := c.Px(ui.Dp(4))
|
||||||
r := image.Rectangle{Max: e.viewSize}
|
r := image.Rectangle{Max: e.viewSize}
|
||||||
r.Min.X -= pointerPadding
|
r.Min.X -= pointerPadding
|
||||||
r.Min.Y -= pointerPadding
|
r.Min.Y -= pointerPadding
|
||||||
r.Max.X += pointerPadding
|
r.Max.X += pointerPadding
|
||||||
r.Max.X += pointerPadding
|
r.Max.X += pointerPadding
|
||||||
pointer.RectAreaOp{Rect: r}.Add(ops)
|
pointer.RectAreaOp{Rect: r}.Add(c.Ops)
|
||||||
e.scroller.Add(ops)
|
e.scroller.Add(c.Ops)
|
||||||
e.clicker.Add(ops)
|
e.clicker.Add(c.Ops)
|
||||||
ctx.Dimensions = layout.Dimensions{Size: e.viewSize, Baseline: baseline}
|
c.Dimensions = layout.Dimensions{Size: e.viewSize, Baseline: baseline}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Text returns the contents of the editor.
|
// Text returns the contents of the editor.
|
||||||
@@ -550,8 +549,8 @@ func (e *Editor) moveEnd() {
|
|||||||
e.carXOff = l.Width + a - x
|
e.carXOff = l.Width + a - x
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Editor) scrollToCaret(cfg ui.Config) {
|
func (e *Editor) scrollToCaret(c ui.Config) {
|
||||||
carWidth := e.caretWidth(cfg)
|
carWidth := e.caretWidth(c)
|
||||||
carLine, _, x, y := e.layoutCaret()
|
carLine, _, x, y := e.layoutCaret()
|
||||||
l := e.lines[carLine]
|
l := e.lines[carLine]
|
||||||
if e.SingleLine {
|
if e.SingleLine {
|
||||||
|
|||||||
+9
-9
@@ -92,8 +92,8 @@ func (l *lineIterator) Next() (String, f32.Point, bool) {
|
|||||||
return String{}, f32.Point{}, false
|
return String{}, f32.Point{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Label) Layout(ops *ui.Ops, ctx *layout.Context) {
|
func (l Label) Layout(c *layout.Context) {
|
||||||
cs := ctx.Constraints
|
cs := c.Constraints
|
||||||
textLayout := l.Face.Layout(l.Text, LayoutOptions{MaxWidth: cs.Width.Max})
|
textLayout := l.Face.Layout(l.Text, LayoutOptions{MaxWidth: cs.Width.Max})
|
||||||
lines := textLayout.Lines
|
lines := textLayout.Lines
|
||||||
if max := l.MaxLines; max > 0 && len(lines) > max {
|
if max := l.MaxLines; max > 0 && len(lines) > max {
|
||||||
@@ -119,16 +119,16 @@ func (l Label) Layout(ops *ui.Ops, ctx *layout.Context) {
|
|||||||
}
|
}
|
||||||
lclip := toRectF(clip).Sub(off)
|
lclip := toRectF(clip).Sub(off)
|
||||||
var stack ui.StackOp
|
var stack ui.StackOp
|
||||||
stack.Push(ops)
|
stack.Push(c.Ops)
|
||||||
ui.TransformOp{}.Offset(off).Add(ops)
|
ui.TransformOp{}.Offset(off).Add(c.Ops)
|
||||||
l.Face.Path(str).Add(ops)
|
l.Face.Path(str).Add(c.Ops)
|
||||||
// Set a default color in case the material is empty.
|
// Set a default color in case the material is empty.
|
||||||
paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(ops)
|
paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(c.Ops)
|
||||||
l.Material.Add(ops)
|
l.Material.Add(c.Ops)
|
||||||
paint.PaintOp{Rect: lclip}.Add(ops)
|
paint.PaintOp{Rect: lclip}.Add(c.Ops)
|
||||||
stack.Pop()
|
stack.Pop()
|
||||||
}
|
}
|
||||||
ctx.Dimensions = dims
|
c.Dimensions = dims
|
||||||
}
|
}
|
||||||
|
|
||||||
func toRectF(r image.Rectangle) f32.Rectangle {
|
func toRectF(r image.Rectangle) f32.Rectangle {
|
||||||
|
|||||||
+5
-5
@@ -25,7 +25,7 @@ type Image struct {
|
|||||||
Scale float32
|
Scale float32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im Image) Layout(c ui.Config, ops *ui.Ops, ctx *layout.Context) {
|
func (im Image) Layout(c *layout.Context) {
|
||||||
size := im.Src.Bounds()
|
size := im.Src.Bounds()
|
||||||
wf, hf := float32(size.Dx()), float32(size.Dy())
|
wf, hf := float32(size.Dx()), float32(size.Dy())
|
||||||
var w, h int
|
var w, h int
|
||||||
@@ -35,7 +35,7 @@ func (im Image) Layout(c ui.Config, ops *ui.Ops, ctx *layout.Context) {
|
|||||||
} else {
|
} else {
|
||||||
w, h = int(wf*im.Scale+.5), int(hf*im.Scale+.5)
|
w, h = int(wf*im.Scale+.5), int(hf*im.Scale+.5)
|
||||||
}
|
}
|
||||||
cs := ctx.Constraints
|
cs := c.Constraints
|
||||||
d := image.Point{X: cs.Width.Constrain(w), Y: cs.Height.Constrain(h)}
|
d := image.Point{X: cs.Width.Constrain(w), Y: cs.Height.Constrain(h)}
|
||||||
aspect := float32(w) / float32(h)
|
aspect := float32(w) / float32(h)
|
||||||
dw, dh := float32(d.X), float32(d.Y)
|
dw, dh := float32(d.X), float32(d.Y)
|
||||||
@@ -48,7 +48,7 @@ func (im Image) Layout(c ui.Config, ops *ui.Ops, ctx *layout.Context) {
|
|||||||
dr := f32.Rectangle{
|
dr := f32.Rectangle{
|
||||||
Max: f32.Point{X: float32(d.X), Y: float32(d.Y)},
|
Max: f32.Point{X: float32(d.X), Y: float32(d.Y)},
|
||||||
}
|
}
|
||||||
paint.ImageOp{Src: im.Src, Rect: im.Rect}.Add(ops)
|
paint.ImageOp{Src: im.Src, Rect: im.Rect}.Add(c.Ops)
|
||||||
paint.PaintOp{Rect: dr}.Add(ops)
|
paint.PaintOp{Rect: dr}.Add(c.Ops)
|
||||||
ctx.Dimensions = layout.Dimensions{Size: d, Baseline: d.Y}
|
c.Dimensions = layout.Dimensions{Size: d, Baseline: d.Y}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user