Commit Graph

428 Commits

Author SHA1 Message Date
Elias Naur d7636ea273 widget: remove test dependency on package app
Without the dependency, tests builds much faster.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur be86450ea5 widget/material: drop test dependency on package app
Without the dependency, tests builds much faster.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur 676b670119 io/input,io/clipboard: [API] replace ReadOp with command
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur d51aea553f io/input,io/clipboard: [API] replace WriteOp with command
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur a3c539b3c2 io/input,io/transfer: [API] replace OfferOp with command
Also delete two tests that are no longer relevant.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur eed93aaffe io/input,io/key: [API] replace SnippetOp with command
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur 813d836641 io/input,io/key: [API] replace SelectionOp with command
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur 627e028d3c widget: [API] re-implement Clickable.Focus with a command
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur 9de80749e1 widget: [API] re-implement Selectable.Focus in terms of commands
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur 8334d2abb4 widget: [API] re-implement Editor.Focus in terms of commands
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur 5dd41f74d3 io/input,io/key: [API] replace SoftKeyboardOp with a command
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur be36fc88aa io/input,io/key: [API] introduce Command, replace FocusOp with FocusCmd
Modeling focus change as an operation is awkward, because focus changes
logically happen during event processing, not layout. In particular, you
want to apply focus changes even if a widget is subsequently never laid
out.

Now that input.Source is concrete, it's much more straightforward to
offer focus changes as a command which can be queued through the
Source. A future change may similarly offer a command for directional
focus changes.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur 6027517949 io/input: [API] introduce Source, the interface between a Router and widgets
This change gets rid of the event.Queue interface by replacing it with
input.Source values. Source provides the interface to Router necessary
to implement interface widgets.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur 4fcd96ac4b layout,app: [API] rename FrameEvent.Queue and Context.Queue to Source
We're about to replace the interface Queue with a concrete input.Source.
This change renames the field accordingly.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur d5a0d2cf60 io/input,io/router: [API] rename package io/router to io/input
The input name better matches its purpose, in particular when we
introduce input.Source.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur 99399184ac widget: remove assumption that Context.Queue is an interface
We're about to make Context.Queue a concrete type, and this change
replaces code that relies on Queue being an interface.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
Elias Naur cb1e605203 app,io/system,layout: [API] move FrameEvent and Insets to package app
In the early days of Gio, FrameEvent was part of package app. It was
moved to package system to enable layout.NewContext be a convenient
short-hand for constructing a layout.

However, it seems the better design to leave FrameEvent (and Insets) in
package app, and move layout.NewContext there as well. More importantly,
the move allows us to replace the event.Queue interface with a concrete
type.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 10:59:51 +00:00
James Stanley 7cfd226b57 material: fix documentation of using buttons
Signed-off-by: James Stanley <james@incoherency.co.uk>
2024-02-05 10:59:23 +00:00
James Stanley 40706d3782 material: fix documentation of creating an icon
Signed-off-by: James Stanley <james@incoherency.co.uk>
2024-01-16 15:14:46 +00:00
James Stanley adba14c062 material: fix documentation of changing theme colours
Signed-off-by: James Stanley <james@incoherency.co.uk>
2024-01-16 15:14:46 +00:00
Dominik Honnef 7ea432fa13 widget: don't refer to non-existent method Clickable.Clicks
Signed-off-by: Dominik Honnef <dominik@honnef.co>
2024-01-04 11:56:56 -06:00
sewn a8ec3968d9 widget/material: allow changing height & radius of progressbar
Signed-off-by: sewn <sewn@disroot.org>
2023-12-19 11:32:43 -06:00
Chris Waldon 52987e53f6 widget/material: fix list scrollbar display
This commit fixes a visual misalignment in scrollbars resulting from subtle differences
in the semantics of layout.Stack and layout.Background. layout.Stack will position expanded
children according to their minimum constraint regardless of their returned size, whereas
layout.Background uses their returned size. This means that layout.Expanded widgets returning
zero dimensions are positioned correctly, but they break when converted to use layout.Background.

This commit fixes the problem by returning correct dimensions from the scrollbar track.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-12-08 11:27:00 -05:00
Chris Waldon e32417353a widget: [API] rename scrollbar update method to update
This matches the convention of other state update methods. While here, remove useless
dimensions return. The update doesn't draw anything, so there are no dimensions involved.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-12-08 11:27:00 -05:00
Elias Naur c458eb30f0 widget/material: add missing Update calls
Without the updates, the switch and radiobutton would use stale state
for layout.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-12-05 10:40:45 -06:00
Egon Elbre f39245df99 layout: add Background
It's relatively common to create a widget and then add a background to
it. Using layout.Stack causes bunch of heap allocs, which we would like
to avoid whenever we can.

This adds layout.Background which is roughly the same as:

    layout.Stack{Alignment: layout.C}.Layout(gtx,
    	layout.Expanded(background),
    	layout.Stacked(widget)
    )

goos: windows
goarch: amd64
pkg: gioui.org/layout
cpu: AMD Ryzen Threadripper 2950X 16-Core Processor
     │    Stack     │             Background              │
     │    sec/op    │   sec/op     vs base                │
*-32   203.80n ± 1%   83.36n ± 3%  -59.09% (p=0.000 n=10)

     │   Stack    │             Background             │
     │    B/op    │   B/op     vs base                 │
*-32   48.00 ± 0%   0.00 ± 0%  -100.00% (p=0.000 n=10)

     │   Stack    │             Background              │
     │ allocs/op  │ allocs/op   vs base                 │
*-32   2.000 ± 0%   0.000 ± 0%  -100.00% (p=0.000 n=10)

Signed-off-by: Egon Elbre <egonelbre@gmail.com>
2023-11-25 11:50:25 -06:00
Chris Waldon c8801fe233 widget: test update-only editor logic
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-11-10 16:17:56 -05:00
Chris Waldon 3fde0c0061 widget: [API] split text widget Update from Layout
This commit introduces Update(gtx) functions for both Selectable and Editor, allowing their
state to be updated explicitly prior to layout. This completes the transition that allows all
Gio widgets to have their state updated ahead-of-time, ensuring that there is zero frame lag
between an input event and the widget response to that event.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-11-10 14:59:06 -05:00
Egon Elbre 48bd5952b1 widget: optimize processGlyph
processGlyph does not modify the value, so there's no reason to
return the struct.

Signed-off-by: Egon Elbre <egonelbre@gmail.com>
2023-11-09 15:18:46 -05:00
Egon Elbre df8a8789a3 text: [API] reduce size of Glyph.Runes to uint16
This shrinks text.Glyph from 72B to 58B.

  LabelStatic/1000runes-RTL-arabic-32   63.62µ ± 0%   62.05µ ± 0%  -2.47% (p=0.002 n=6)

Signed-off-by: Egon Elbre <egonelbre@gmail.com>
2023-11-09 15:18:46 -05:00
Larry Clapp ae2b1f42b2 widget: Update Selectable key filter
Selectable was using a key event filter copied directly from editor.go,
but it didn't actually process all those keys. Update the filter to only
ask for the keys that Selectable actually uses.

Signed-off-by: Larry Clapp <larry@theclapp.org>
2023-10-15 10:44:39 -04:00
Elias Naur 63fea3d2bd widget: use local random source to avoid deprecated rand.Seed
This change replace the global rand use with a local source, to avoid
the recently deprecated global rand.Seed function. At the same time, the
time-dependent seeds are replaced with static numbers to ensure
reproducible benchmarks numbers.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-14 15:45:58 -05:00
Elias Naur c756986d9e gesture: [API] rename gesture state update methods to Update
Change the gesture state update methods to align with the convention.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-08 12:37:12 -05:00
Elias Naur d42dae73f0 widget: [API] separate Float state update; remove min, max, invert parameters
This change allows users of Float to determine its state before Layout
by calling Update.

While here, remove the value transformation represented by the min, max,
invert parameters; they're too many arguments for a computation that
may as well be done by the user.

Remove Float.Pos; it is better to compute its value from the dimensions
returned by Float.Layout.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-06 20:04:32 -05:00
Elias Naur 23e44292bb widget: [API] separate state changes from Draggable.Layout to Update
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-06 20:04:32 -05:00
Elias Naur fe85136f99 widget: [API] move Enum state update to Changed, rename it to Update
Similar to an earlier change for other widgets, this change separate
Enum state changes for access earlier than Layout.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-06 20:04:32 -05:00
Elias Naur b9837def5c widget: [API] move Decorations state update to Actions
Similar to a previous change for Clickable and Bool this change separates
state changes from Decorations.Layout to Actions so that access may
happen before Layout.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-06 20:04:13 -05:00
Elias Naur dc97871122 widget: [API] rename Bool.Changed to Update and move state update to it
Similar to a previous change for Clickable, this change separates Bool
state changes to its renamed method Update. This allows access to
the most recent state before calling Layout.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-06 20:03:25 -05:00
Elias Naur 4a4fe5a69b widget: [API] move Clickable state update from Layout to Clicks
Before this change, Clickable state updates would happen in Layout.
However, that is too late in cases where clicks affects layout that
contiains the Clickable.

This change removes state changes from Layout and moves them to Clicks,
to allow users pre-layout access. Note that Layout itself processes
events, which means users can no longer access clicks after Layout.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-06 20:03:22 -05:00
Elias Naur 1686874d07 gesture: [API] rename ClickType to ClickKind
"Kind" is the Go idiomatic name for distinguishing structs outside of
the type system.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-06 19:11:11 -05:00
Elias Naur 650ccea28d io/pointer: [API] rename PointerEvent.Type to Kind
Kind is the idiomatic field name for distinguishing a struct without
using separate types.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-06 19:11:08 -05:00
Elias Naur e1b3928819 io/semantic: [API] replace DisabledOp with EnabledOp
The double-negative DisabledOp is harder to understand than a
straightforward EnabledOp. Note that the absence of an EnabledOp
implies still means that the widget is enabled.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-10-06 18:08:52 -05:00
Veikko Sariola 290b5fe821 widget: click button only if key pressed and released
This commit fixes the non-intuitive behaviour, where hitting return or
space with a button focused, then tabbing to another button and
releasing the key causes the second button to trigger. It feels wrong,
as the "gesture" was never initiated on the second button. The fix makes
widget.Clickable track which key was pressed, in a variable called
pressedKey, and only considers a key release if the released key matches
the pressed key. Finally, if the widget loses focus, pressedKey is
cleared.

Fixes: https://todo.sr.ht/~eliasnaur/gio/525
Signed-off-by: Veikko Sariola <5684185+vsariola@users.noreply.github.com>
2023-08-22 09:07:48 -06:00
Chris Waldon edbf872b44 widget: fix label vertical glyph padding logic
We previously were not handling glyphs that extended vertically beyond the
ascent/descent declared by their font. This is done rarely with text fonts,
but is apparently common among symbol and emoji fonts.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-08-01 09:09:10 +02:00
Chris Waldon fdd102aaf9 widget: simplify and improve cursor position generation
This commit updates the strategy of our cursor positioning index to eliminate
cursor positions *after* trailing whitespace characters on a line. Eliminating
such cursor positions enables us to collapse trailing whitespace visually without
impacting the editability of text (this will be done in a future commit).

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-07-31 12:05:07 +02:00
Chris Waldon 8dc03ed655 text,widget: remove fractional line height
The previous logic kept the y offset of a line as a fractional value
until the last possible moment in an effort to be as true to a fractional
line height as possible (minimize the error), but this interacts pathologically
with multi-line text selections, as the selections may have visibly different
gaps between lines. It's better to always shift lines by a fixed quantity of
whole pixels, even if it is technically less accurate to the desired line height.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-07-31 12:03:18 +02:00
Chris Waldon ddf770b9d5 widget{,/material}: surface line height manipulation
This commit surfaces fields to manipulate the line height of all label and editor
types. It's unfortunate how this spreads through the API, but I don't see a good
way to eliminate that right now.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-07-19 10:05:33 +02:00
Chris Waldon acab582487 widget/material: allow configuring default typeface on theme
This commit introduces the material.Theme.Face field, which will automatically
populate the Font.Typeface in every text widget created using a constructor function
in package material.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-07-19 10:02:18 +02:00
Chris Waldon 43c47f0883 go.*,text,font{,/opentype},app,gpu,widget{,/material}: [API] load system fonts
This commit updates the text package to be able to load system fonts. As a consequence,
application authors may choose to provide no fonts manually, and it's
also possible that the system provides none (WASM, for instance, currently provides no
system fonts). As such, the text stack needed some minor tweaks to handle this case by
displaying blank spaces where text should be rather than crashing when no faces are
available.

Internally, we are dropping the old method of choosing faces and instead relying solely
on the new font matching logic in go-text. I chose to do this because maintaining two
different sets of logic with a hierarchical relationship proved to be really complex,
and also the go-text logic seems to produce higher-quality choices.

The breaking API change from this commit is the new way of constructing a text shaper
using text.ShaperOptions. Providing no options will result in a shaper that uses solely
system fonts. The various options can be used to disable system font loading and to
provide an already-parsed collection of fonts as per Gio's old API.

The material.NewTheme function now accepts no arguments instead of a font collection.
Users wanting to provide a collection can simply provide a new shaper configured how
they would like:

    theme := material.NewTheme()
	theme.Shaper = text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Regular()))

This commit touches many packages to fix up their construction of text shapers, mostly in
test code. The changes to the tests in package widget deserve special note:
Changing our font resolution logic caused the tofu characters within the
test strings to use a different font's tofu. This isn't a problem, but shifted
the layout of the shaped text a little bit. I've updated the numbers to expect
the new glyph positions.

Fixes: https://todo.sr.ht/~eliasnaur/gio/309
Fixes: https://todo.sr.ht/~eliasnaur/gio/184
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-07-19 10:01:51 +02:00
Chris Waldon 6ea4119a3c text,widget: [API] implement consistent, controllable line height
This commit ensures that any given paragraph of text shaped by Gio will use a single
internal line height. This line height is determined (by default) by the text size,
rather than the fonts involved. This is a breaking change, as previously we would
blindly use the largest line height of any font in a line for that line, leading to
lines within the same paragraph with extremely uneven spacing. This commit also
updates some test expectations in package widget.

I thought pretty hard about how to implement line spacing, and consulted a few sources:

[0] https://www.figma.com/blog/line-height-changes/
[1] https://practicaltypography.com/line-spacing.html
[2] https://developer.mozilla.org/en-US/docs/Web/CSS/line-height

There is no single, universal way to think about line spacing. Fonts internally specify
a line height as the sum of their ascent, descent, and gap, but the line height of two
fonts at the same pixel size (say 20 Sp) can vary wildy (especially across writing systems).
There are two strategies we could pursue to establish the line height of a paragraph of text:

- derive the line height from the fonts involved (our old behavior, and the behavior of
  many word processors)
- derive the line height from the requested text size provided by the user (the behavior of the
  web).

The challenge with the first option is that for a given piece of text in the UI, there can
be a silly number of fonts involved. If a label dispays user-generated content, the user can
put an emoji in it, and emoji fonts have different line heights from latin ones. This can cause
unexpected and nasty layout shift. Gio would previously do exactly this, on a line-by-line basis,
resulting in unevenly spaced lines within a paragraph depending on which fonts were used on
which lines. Choosing one of the fonts and enforcing its line height would make things consistent,
but it isn't clear how to choose that canonical font. There is no 1:1 mapping between the input
text.Font provided in the shaping parameters and a single font.Face. Instead, that mapping depends
upon the runes being shaped.

I think the only sane way to implement the first option would be to synthesize some text in the
provided system.Locale (mapping the language to a script and then generating a rune from that
script), shape that single rune, and then enforce the line height of the resulting face on the
entire paragraph. This would require doing a fair bit more work per paragraph than Gio does today,
so I've opted not to do it.

Instead, the second option allows us to choose a line height based on the size of the text that
the user wants to display. While this can potentially interact poorly with unusually tall fonts,
it means that text will always have a consistent line height.

I've provided two knobs to control line height:

- text.Parameters.LineHeight lets you set a specific height in pixels with a default value of
  text.Parameters.PxPerEm.
- text.Parameters.LineHeightScale applies a scaling factor to the LineHeight, allowing you to
  easily space out text without hard-coding a specific pixel size. The default value here
  (drawn from the recommendations of [1]) is 1.2, which looks pretty good across many fonts.

I've chosen this two-value API because many users will want to set one or the other value. I
considered instead a single value field and a "mode" that would specify how it was used, but
that felt uglier. Also, you *can* set both of these two fields and get predictable results.

I'd like to revisit using the line height of the chosen fonts in the future, but it seems a
little too complex to be worthwhile right now. An interesting option would be making the
select-a-face-using-locale strategy described above an opt-in feature, though some users
might instead want to just use the tallest line height among fonts in use. Something like
this Android API might be appropriate:

[3] https://learn.microsoft.com/en-us/dotnet/api/android.widget.textview.fallbacklinespacing?view=xamarin-android-sdk-13

I'd like to thank Dominik Honnef for some good discussion around this feature, and for pointing
me to some good sources on the subject.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-07-17 22:33:02 +02:00