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 only mutable field is "recording", which is used for a sanity
check. THat check is performed (less generally) by Ops.macroStack.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
To match Record, we'd like Push to return a value. To do that and
support the one-line
defer op.Push(ops).Pop()
Pop needs to use a value receiver as well. Drop the active field
and make it so. The field was only a sanity check, a check which is
already done by Ops.stackStack, albeit with a less specific panic.
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>
- Drop pointer.Event.Hit in favour of Enter/Leave events.
- Track enter/leaves for each pointer.ID (updates #122). Add test.
- Resolve grabs once.
- Get rid of scratch slice.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Recent changes to the macOS threading exposed a problem where a
window's display link may fail to start after being started and stopped
in rapid succession.
Introduce a displayLink type that waits a while after the last stop
request before stopping its display link. That seems to be the way
other projects are using display links.
As a bonus, the new implementation avoids the potentially expensive
overhead of frequent starting and stopping the underlying OS thread.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
We're about to move the display link to common Go code. To do that,
we need the redraw logic in Go as well.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
The macOS redraw callback is not invoked on the main thread, so its
access to window fields must be synchronized.
An alternative would be to schedule the asynchronous redraws on the main
thread, but I believe frame callbacks are performance-sensitive enough
to warrant the extra locking complexity.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Only the synchronous draws from the main thread may involve changing
width, height and scale. Introduce cached window.width and window.height
fields and limits updates to main-thread draws.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
An earlier change unexported the Button.Update method that exposed raw pointer
input not available from the boolean Button.Clicked method. Introduce Click
and Button.Clicks to replace it, and implement Clicked in terms of it.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Before this change, events were typically processed twice or more per
widget: once in the Layout method for refreshing the visual state, and
once per method that queries for state changes.
One example is widget.Clickable that processed events in both its Layout
and Clicked method.
This change establishes the convention that events are processed once, in
the Layout method. There are several advantages to that approach:
- Query methods such as Clickable.Clicked no longer need a layout.Context.
- State updates from events only occur in Layout.
- Widgets are simplified because they won't need a separate processEvents
(or similar) method and won't forget to call it from methods other than Layout.
- Useless calls to gtx.Events are avoided (gtx.Events only returns events
for the first call each frame for a given event.Tag).
The disadvantage is that state updates from input events will not appear
before Layout. For example, in the call sequence
var btn *widget.Clickable
if btn.Clicked() {...}
btn.Layout(...)
the Clicked call will not detect an incoming click until the frame after it
happened.
This is ok because
- The Gio event router automatically dispatches an extra frame after events
arrive, bounding the latency from events to queries such as Clicked to
at most one frame (~17 ms).
- The potential extra frame of latency does not apply to Layout methods as long
as they process events before drawing. In other words, the visual feedback
from input events are not delayed because of this change.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Instead of, say,
var th *material.Theme
var btn *widget.Clickable
material.Button(th, "Click me").Layout(gtx, btn)
move the widget state objects to the constructor:
material.Button(th, btn, "Click me").Layout(gtx)
The advatage is that several widgets can now be used without
wrapping them in function literals. For example,
layout.Inset{}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
material.Button(th, "Click me").Layout(gtx, btn)
})
collapses to just
layout.Inset{}.Layout(gtx, material.Button(th, btn, "Click me").Layout)
Signed-off-by: Elias Naur <mail@eliasnaur.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>
Use Inset instead, matching the other buttons.
Redefine Size to apply to the icon size, without padding.
Signed-off-by: Elias Naur <mail@eliasnaur.com>