This commit adds two helper methods to layout.Contraints that make it easier to
manipulate the constraints while keeping their invariants. In particular, code
manually manipulating constraints usually fails to correctly ensure that the
max does not become smaller than the min, the min does not exceed the max, and
that no value goes below zero.
It's quite a few lines to check these invariants yourself in every custom layout,
so I think it makes sense to offer helpers for this.
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
This commit ensures that the Spacer type doesn't break layouts
by ignoring when its min constraints require it to be larger or
its max constraints require it to be smaller.
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
The unit.Value is a struct and thus more inconvenient to use than its
underlying float32 type. In addition, most uses don't need a general
value, but rather a specific unit given by the context. This change
replaces unit.Value with two float32 units, Dp and Sp. It also changes
variables and parameters of unit.Value to a specific unit type matching
the context. That is, unit.Dp everywhere except for text sizes which are
in Sp.
Switching to typed float32s has multiple advantages
- They can be constants:
const touchSlop = unit.Dp(16)
- Casting untyped constants is no longer necessary:
insets := layout.UniformInset(16)
- Calculation with values is natural:
func (s ScrollbarStyle) Width() unit.Dp {
return s.Indicator.MinorWidth + s.Track.MinorPadding + s.Track.MinorPadding
}
The main API change is that calls to gtx.Px must be replaced with either
gtx.Dp or gtx.Sp depending on the unit.
Idea by Christophe Meessen.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
op.Offset is a convenience function most often used by layouts. Layouts
usually operate in integer coordinates, and the float32 version of op.Offset
needlessly force conversions from int to float32. This change makes op.Offset
take integer coordinates, to better match its intended use.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
The op.Save and Load methods exist to support the need for
transformation, clip, pointer area state to behave as stacks. For
example, layout needs to apply an offset to its children but not
subsequent operations.
Before this change, op.Save and Load were used to save and restore the
state:
ops := new(op.Ops)
// Save state.
state := op.Save(ops)
// Apply offset.
op.Offset(...).Add(ops)
// Draw with offset applied.
draw(ops)
// Restore state.
state.Load()
A drawback with the op.Save mechanism is that there is no direct
connection between the state change and the saving and loading of state.
This causes confusion as to when a Save/Load is needed and who is
responsible for performing them, which leads to subtle bugs and over-use
of Save/Loads.
This change gets rid of the general state stack and replaces it with
per-state stacks. There is now a stack for transformation, clip, pointer
areas, and they can only be restored by the code pushing state to them.
The example above now becomes:
ops := new(op.Ops)
// Push offset to the transformation stack.
stack := op.Offset(...).Push(ops)
// Draw with offset applied.
draw(ops)
// Restore state.
stack.Pop()
For convenience, transformation also be Add'ed if the stack operation is
not required.
Simple state such as the current material no longer has a way to be
restored; it is assumed the client of a PaintOp adds their desired
material operation before it.
API change: replace op.Save/Load with explicit Push/Pop scopes for
op.TransformOps, pointer.AreaOps, clip.Ops.
To ease porting, this change retains a version of op.Save/Load that
saves and restores the transformation and clip stacks. It also retains
an Add method for clip.Op.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This creates a floating-point analog to layout.Axis.Convert
for converting from (x,y) coordinate space to (main,cross)
coordinate space.
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
The semantics were relaxed in a previous commit; this change renames
to operations accordingly.
API change. Use gofmt to adjust your code accordingly:
gofmt -r 'op.Push(a).Pop() -> op.Save(a).Load()'
gofmt -r 'op.Push(a) -> op.Save(a)'
gofmt -r 'v.Pop() -> v.Load()'
gofmt -r 'op.StackOp -> op.StateOp'
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Added comment on the use of Dimension.Baseline and Direction.Layout constraint minimum clearing.
Also, renamed the Direction receiver for consistency and removed unnecessary conversions.
Signed-off-by: Pierre.Curto <pierre.curto@gmail.com>
Use op.Offset instead, or create and manipulate a f32.Affine2D.
API change. Update your code with a gofmt rule:
gofmt -r 'op.TransformOp{}.Offset -> op.Offset'
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Converting
macro := op.Record(ops)
...
macro.Stop()
macro.Add()
to
macro := op.Record(ops)
...
call := macro.Stop()
call.Add(ops)
Which is more general (call.Add can take a different ops than the op.Record
that started it), and enforced the order between Stop and the subsequent Add.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
The funcs replace stack.Push and macro.Record, which become private.
This makes stack and macro faster to write, in particular for stacks
where you can just write the following line to save and restore the
state :
defer op.Push(ops).Pop()
This usage requires Push to return a pointer (since Pop has a pointer
receiver), or else the code doesn't compile.
For consistancy, I tried to do the same for op.Record, but this implied
to turn all the MacroOp fields into pointers, and this caused some
panics. As a result, op.Record doesn't return a pointer.
An other side effect pointed by Larry Clapp: StackOp and MacroOp are not
re-usable any more, you have to allocate a new one for each usage, using
the described funcs above.
Signed-off-by: Thomas Bruyelle <thomas.bruyelle@gmail.com>
Change the definition of Widget from the implicit
type Widget func()
to the explicit functional
type Widget func(gtx layout.Context) layout.Dimensions
The advantages are numerous:
- Clearer connection between the incoming context and the output dimensions.
- Returning the Dimensions are impossible to omit.
- Contexts passed by value, so its fields can be exported
and freely mutated by the program.
The only disadvantage is the longer function literals and the many "returns".
What tipped the scales in favour of the explicit Widget variant is that type
aliases can dramatically shorten the literals:
type (
C = layout.Context
D = layout.Dimensions
)
widget := func(gtx C) D {
...
}
Note that the aliases are not part of the Gio API and it is up to each user
whether they want to use them.
Finally the Go proposal for lightweight function literals,
https://github.com/golang/go/issues/21498, may remove the disadvantage
completely in future.
Context becomes a plain struct with only public fields, and its Reset is
replaced by a NewContext convenience constructor.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
layout.FRect, layout.FPt for converting from integer to floating point,
useful for drawing operations.
f32.Pt is a shorthand that mirrors image.Pt.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Instead of
type Contraints struct {
Width, Height Constraint
}
use
type Constraints struct {
Min, Max image.Point
}
which leads to simpler use. For example, the Min method is trivally replaced by
the field, and the RigidConstraints constructor is no longer a net win.
API Change. Rewrites:
gofmt -r 'gtx.Constraints.Min() -> gtx.Constraints.Min'
gofmt -r 'gtx.Constraints.Width.Min -> gtx.Constraints.Min.X'
gofmt -r 'gtx.Constraints.Height.Min -> gtx.Constraints.Min.Y'
gofmt -r 'gtx.Constraints.Height.Max -> gtx.Constraints.Max.Y'
gofmt -r 'gtx.Constraints.Width.Max -> gtx.Constraints.Max.X'
Signed-off-by: Elias Naur <mail@eliasnaur.com>
It's one less type (Align) and shorter:
Before:
layout.Align(layout.Center).Layout(...)
After
layout.Center.Layout(...)
It is also safer: since `layout.Align(...)` was a casting operation,
the Go compiler would not complain about an incompatible constant.
For example, the widget/material package contained a wrong cast:
layout.Align(layout.Start)
which should have been
layout.Align(layout.W)
After this change, attempting `layout.Start.Layout(...)` result
in a compile error.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
The ability to invoke other operation lists belongs in the new CallOp.
While we're here, make MacroOp.Add use a pointer receiver to match the
other methods.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Package app is the only package that depends on native libraries and
Cgo. Minimize its API, thereby minimizing Gio clients' dependency on
it. In the future, a headless, testing or remote "Window" should be
very easy to replace app.Window.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Taking a constraint in Reset smells too much of a layout operation,
whereas a size is simpler and only serves to set the context constraints
to something sensible.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Package ui is now only about units except for the Config.Now method.
Remove Now and rename Config to Converter. Add layout.Config to
replace the old ui.Config.
Signed-off-by: Elias Naur <mail@eliasnaur.com>