Commit Graph

15 Commits

Author SHA1 Message Date
Elias Naur 936c266b03 all: [API] split operation stack into per-state stacks
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>
2021-10-08 17:21:56 +02:00
Elias Naur b39d1bdac7 widget: handle hovering of the empty Enum key
Before this change, a radio button with the empty key ("") would be
displayed as hovering if no other button were.

It's still not possible to have no radio buttons selected when one of
them is the empty key. If that's becomes necessary, Enum.Value can be
converted to a *string.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-03-03 18:12:39 +01:00
Egon Elbre bc6798c4ca widget/material: add hovered to RadioButton
Signed-off-by: Egon Elbre <egonelbre@gmail.com>
2021-01-18 10:57:06 +01:00
Elias Naur d331dd2de8 op: rename StackOp/Push/Pop to StateOp/Save/Load
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>
2021-01-12 21:28:59 +01:00
Walter Werner SCHNEIDER fd2d96adfc all: fix spelling errors
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2020-12-17 08:55:23 +01:00
Elias Naur 7eff3f561f widget: report Enum.Changed only when actually changed
Fixes gio#159

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2020-08-24 18:49:54 +02:00
Elias Naur 8761550839 widget: fix Enum.Changed to reset changed status
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2020-06-07 17:06:03 +02:00
Thomas Bruyelle ae8a377cda op: add op.Push and op.Record funcs
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>
2020-06-02 10:39:56 +02:00
Elias Naur ad93e32128 widget: redefine Enum.Changed and Bool.Changed to consider only user interaction
Ignore programmatic value changes to avoid feedback loops.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2020-05-24 19:48:33 +02:00
Elias Naur f2df7c1458 widget: change Enum.Layout to follow layout protocol
Respect constraints and return dimensions.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2020-05-24 13:52:48 +02:00
Elias Naur d017c722f5 widget,widget/material: only process events in Layout methods
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>
2020-05-24 13:03:23 +02:00
Elias Naur 3af01a3f43 layout: change Widget to take explicit Context and return explicit Dimensions
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>
2020-05-23 22:28:49 +02:00
Elias Naur f08568a6df widget: report whether Value changed after Enum.Update and Bool.Update
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2020-05-05 11:33:41 +02:00
Elias Naur f1e266a9e7 widget,widget/material: export Enum.Value
The Value method both updated the enum value and returned it.

In order to access the current value withoutm, expose the Value
field of the enum and rename the method to Update. As a bonus we
can get rid of the SetValue method as well.

Updates gio#96

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2020-05-03 21:28:33 +02:00
Alexander Arin 089ae31f0c widgets, widgets/material: add RadioButton & Enum
Signed-off-by: Alexander Arin <fralx@yandex.ru>
2019-11-06 11:02:28 +01:00