Commit Graph

99 Commits

Author SHA1 Message Date
Chris Waldon 7daab97fab widget: [API] make text.Alignment direction-sensitive
This commit ensures that text.Alignment is intuitive for
the direction of the text being aligned. RTL text with
Alignment Start will be aligned to the right edge of the area,
whereas LTR text with Alignment Start will continue to be
aligned to the left edge. Vice versa for the End alignment.

References: https://todo.sr.ht/~eliasnaur/gio/146
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2022-03-18 08:05:06 +01:00
Chris Waldon 9576b659d7 text: [API] remove Text and Advances from Layout
These fields are no longer needed with the new text shaper.
Advances is redundant to the glyph information, and Text
should never be used during layout, as you should
traverse the cluster list instead. This commit also removed
the now-unused string field from the path LRU cache key.

References: https://todo.sr.ht/~eliasnaur/gio/146
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2022-03-18 08:04:27 +01:00
Chris Waldon 42c99a5cb2 widget{,/material}: [API] update editor to support complex scripts
This commit updates material.Editor and material.Label to support the
new text shaper. This requires breaking their assumption that glyphs
of font data map 1:1 to runes of text data.

References: https://todo.sr.ht/~eliasnaur/gio/146
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2022-03-18 08:03:46 +01:00
Chris Waldon 1e5a3696f5 deps,text,widget,font/opentype: [API] add harfbuzz-powered text shaper
This commit introduces a new text shaping infrastructure
powered by Benoit Kugler's Go source-port of harfbuzz.
This shaper can properly display complex scripts and RTL
text. This commit changes the signature of the text.Shaper
function, which is a breaking API change.

The new functionality is available via opentype.ParseHarfbuzz,
which configures a text.Shaper leveraging the new backend.

References: https://todo.sr.ht/~eliasnaur/gio/146
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2022-03-18 08:01:44 +01:00
Chris Waldon b0ab5ae06e widget: remove unneeded editor flicker logic
We cannot find a way to trigger this flickering
condition anymore, and so we're removing the logic
guarding against it.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2022-03-17 09:05:35 +01:00
Chris Waldon 1ad785658e widget: optimize painting editor selection
This commit introduces logic to skip painting the
selection rectangle on lines prior to the line
containing the beginning of the selection.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2022-03-17 08:15:31 +01:00
Egon Elbre cdb288d1f9 app,io/pointer: [API] remove CursorNameOp and rename CursorName -> Cursor
It's now possible to directly user pointer.Cursor to add to the ops.

   pointer.CursorText.Add(gtx.Ops)

This is an API change. Use pointer.Cursor directly instead of CursorNameOp.

Signed-off-by: Egon Elbre <egonelbre@gmail.com>
2022-03-01 14:05:46 +01:00
Chris Waldon a401d7aaff widget: fix Editor.CaretCoords when scrolled
This commit fixes the position returned by Editor.CaretCoords
to account for the scroll position of the editor. Without this
change, the returned coordinates can easily overflow the boundaries
of the editor widget when it has been scrolled on either axis.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2022-03-01 10:25:38 +01:00
Elias Naur 8ff10a2068 widget: only ask for software keyboard once per click
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-26 17:27:51 +01:00
Chris Waldon a7afa4d6d1 widget: fix editor's io.Seeker implementation
For some reason, widget.Editor had a Seek method that ignored
the supplied offset and always seeked to offset zero. This
made it impossible to use it like any other io.Seeker. This
commit simply honors the requested offset.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2022-02-26 10:00:27 +01:00
Elias Naur bed5902476 widget: remove pointer area padding from Editor
Pointer padding was introduced in bfece0beba.
I don't remember why, and its commit message doesn't say. Regardless, adding
padding outside a widget's reported dimensions doesn't seem like a good idea
(see #365), and this change removes it.

Fixes: https://todo.sr.ht/~eliasnaur/gio/365
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-19 12:15:48 +01:00
Elias Naur 41489fb732 widget: replace segmentIterator with simpler functions
The replacement functions all use the single seeking function, seekPosition.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-17 18:55:30 +01:00
Elias Naur b7341672e3 widget: extract seeking logic from Editor.closestPosition
We'd like to re-use the Editor.closestPosition seeking for
segmentIterator.Next; this change extracts the state-less logic
into functions.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-17 18:53:50 +01:00
Elias Naur 2df3db361f widget: fix moveLines residual x offset calculation
Commit c22138f5f broke it, this change fixes it.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-16 19:50:02 +01:00
Elias Naur 31f55232bf app,widget,io: implement IME positioning
This change implements reporting of the caret position from Editor, as well
as Windows, macOS, Android support. As a result, the IME composition window
on Windows and macOS is now positioned correctly.

References: https://todo.sr.ht/~eliasnaur/gio/246
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-13 20:09:06 +01:00
Chris Waldon 9e23412a01 widget: test cursor motion in all editor permutations
This commit adds a testcase to catch unexpected panics in the
editor's scroll offset logic introduced by using different
setting combinations that affect editor layout. It also fixes
a panic for single-line editors with alignments other than
text.Start.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2022-02-11 13:42:13 +01:00
Elias Naur 912ddb95f8 widget: fix build on Go 1.17
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-09 21:04:18 +01:00
Elias Naur e32925d6a3 widget: push calls to Editor.makeValid to lower-level indexPosition
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-09 13:00:41 +01:00
Elias Naur 430aee39be widget: don't adjust selection in Editor.makeValid
Only rune positions are tracked for carets, and they only need adjusting
when changing Editor content, not just for re-layout.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-09 12:54:50 +01:00
Elias Naur 58cdb3e1da app,widget: implement Editor IME support, add Android implementation
Fixes: https://todo.sr.ht/~eliasnaur/gio/116
References: https://todo.sr.ht/~eliasnaur/gio/246
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-06 10:31:53 +01:00
Elias Naur c22138f5f8 widget: track only Editor caret start in runes
The other information can be queries at use.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-05 22:58:55 +01:00
Elias Naur e323afa822 widget: track only rune offset in Editor caret end
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-05 22:36:12 +01:00
Elias Naur ff245361a4 widget: replace Editor.makeValidCaret
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-05 22:34:52 +01:00
Elias Naur 45078813b3 widget: replace Editor.movePosToEnd, movePosToStart
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-05 22:34:52 +01:00
Elias Naur 3ce403f851 widget: replace Editor.seek
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-05 22:34:52 +01:00
Elias Naur c071750ed9 widget: replace Editor.movePosToLine
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-05 22:34:52 +01:00
Elias Naur 3a70eaa9c1 widget: replace Editor.movePos
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-05 22:34:52 +01:00
Elias Naur 44e0196173 widget: [API] change Editor.SelectionLen, Selection, SetCaret, Len to operate in runes
This change uncovered and fixes a bug in nullLayout.

This is an API change; the methods operated in bytes before.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-05 22:34:32 +01:00
Elias Naur 2babf3b997 widget: replace a call to Editor.movePosToLine
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-05 21:58:12 +01:00
Elias Naur b9e8c4eda8 widget: introduce caret indexing to Editor
An efficient index replaces all other ad-hoc caret positioning methods.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-05 21:57:49 +01:00
Elias Naur 3614782e0d widget: simplify Editor.offsetToScreenPos
It used to return an iterator function, now it just takes the result
of a previous call.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-04 16:54:14 +01:00
Elias Naur d0869ef457 widget: track rune positions for Editor carets
Needed for efficient implementation of the upcoming IME interface.

Also introduce Editor.replace, seek methods for easier caret navigation
and editing.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-04 15:45:52 +01:00
Elias Naur 212c3cc126 widget: move xoff field out of caret position type
Editor is only interested in one xoff value, this change makes it so
it's the only one tracked.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-04 15:41:34 +01:00
Fabien Jansem 9b7ec167bc delete unicode chars with length > 1 correctly
When there were non ASCII characters (for exemple éèàçîï) in a deleted
selection or word, more characters were deleted because there was a
mismatch between runes and bytes in Delete and deleteWord

Fixes: https://todo.sr.ht/~eliasnaur/gio/330
Signed-off-by: Fabien Jansem <fabien@jansem.eu.org>
2022-01-04 17:37:00 +01:00
Christophe Meessen a34e239c04 text,widget,opentype: change text.Face.Shape to return a clip.PathSpec
With this change, the Shape function returns a clip.PathSpec
instead of a clip.Outline op. It is then possible to create
a clip.Outline or clip.Stroke op to fill the text path or
draw its stroke.

Signed-off-by: Christophe Meessen <meessen@cppm.in2p3.fr>
2021-12-19 13:30:45 +01:00
Elias Naur ac97b9d6e1 widget: [API] add content widget argument to Editor.Layout
To make the semantic relation between the editor and its content clear,
the editor clip operation must cover the content. This change adds an
explicit widget argument to editor, and lays it out inside the clip
rect.

This is an API change. Users of Editor.Layout must provide a content
widget.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-12-01 17:23:54 +01:00
Elias Naur 3e0b72304a all: replace deprecated pointer.Rect with clip.Rect
Converted with

gofmt -w -r 'pointer.Rect(r) -> clip.Rect(r)' .
gofmt -w -r 'pointer.Ellipse(r) -> clip.Ellipse(layout.FRect(r))' .

combined with 'goimports -w .' to clean up imports.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-11-03 14:12:31 +01:00
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 c1298cd755 font/opentype,text,widget: use clip.Op for text shapes, not a macro
This change avoids a macro wrapping every text shape, and prepares text
shaping for scoped clip operations.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-10-07 15:01:17 +02:00
Inkeliz dd86c9706f widget: add key.InputHint to widget.Editor
Signed-off-by: Inkeliz <inkeliz@inkeliz.com>
2021-06-07 17:00:40 +02:00
pierre b6e9c0324d widget: make Editor implement io.Seeker, io.Reader and io.WriterTo
The WriteTo, Seek, Read methods implement a more efficient access to
the Editor content than Text.

Signed-off-by: pierre <pierre.curto@gmail.com>
2021-05-19 17:25:42 +02:00
pierre 5e1a662b94 io/pointer: support nested scrollables
Fixes #185.

Signed-off-by: pierre <pierre.curto@gmail.com>
2021-03-31 09:57:13 +02:00
Larry Clapp f88a8216e9 widget: fix Editor panic
If you created an Editor and immediately SetCaret, it panicked because
e.lines was nil and it looked at e.lines[0].

- Add e.makeValid at the top of SetCaret.
- Add a test case for this situation.

Signed-off-by: Larry Clapp <larry@theclapp.org>
2021-01-24 11:06:55 -05:00
Larry Clapp 34273940a0 widget,widget/material: add selection to the editor
- Allow dragging to be on both horizontal and vertical axes at once.
- Split Editor.caret.pos into caret.start and caret.stop. caret.start is
  the old caret.pos, and is both the position of the caret, and also the
  start of selected text. caret.end is the end of the selected text.
  Start can be after end, e.g. after after Shift-DownArrow.
- Update caret.end after a mouse drag, and various shifted keys
  (Shift-UpArrow, Shift-DownArrow, etc).
- Change Shortcut-C to copy only the selected text, not the whole editor
  text.
- Add Shortcut-X to copy and delete selected text, and Shortcut-A to
  select all text.
- The various Insert/Delete/etc functions now overwrite or delete the
  selection, as appropriate.
- Change MoveCaret to accept a distance for selection end, as well.
  Change SetCaret to accept a selection end offset.
- Add SelectionLen to get the selection length, Selection to get
  selection offsets, SelectedText to get the selected text, and
  ClearSelection to clear the selection.
- Add a rudimentary selection unit test, and extend the deleteWord unit
  test with some text selection cases.
- Add SelectionColor to material.EditorStyle, which defaults to
  Theme.Palette.ContrastBg.

Signed-off-by: Larry Clapp <larry@theclapp.org>
2021-01-24 09:44:52 +01:00
Larry Clapp e78bd15564 widget: refactoring to prep for editor selection
- Move caret from editBuffer.caret to Editor.caret.pos.ofs and related
  refactoring. Move other fields in Editor.caret into Editor.caret.pos.
- Refactor several functions to change a position passed into them,
  rather than changing e.rr.caret directly.
- Add editBuffer.Seek().
- Remove editBuffer.dump().
- Change Editor.Move to MoveCaret.
- Add Editor.SetCaret.
- Updated tests.

Signed-off-by: Larry Clapp <larry@theclapp.org>
2021-01-24 09:44:41 +01:00
Elias Naur e70a16c345 io/router/key: add explicit tag to FocusOp; make last SoftKeyboardOp apply
The target of FocusOp is too subtle; be explicit instead and remove
any doubt.

Multiple SoftKeyboardOp in a single frame is rare, but if they do occur,
they should behave as if they were from separate frames: the last one
applies.

As a side-effect the key event router can be much simplified.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-01-22 16:34:51 +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
pierre 578c226278 io/pointer: CursorNameOp no longer needs an InputOp with Leave and Enter events
Signed-off-by: pierre <pierre.curto@gmail.com>
2020-12-15 18:11:48 +01:00
pierre 7c5bcd3db8 io/pointer: added CursorNameOp
The cursor can now be customized for a given area.

Signed-off-by: pierre <pierre.curto@gmail.com>
2020-12-09 09:38:31 +01:00
Elias Naur ede632b265 widget: fix Editor and Label clipping
Commit gioui.org/commit/94d242d18c9245 broke Editor and Label clipping,
most visible for single-line Editors. Restore the correct clipping.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2020-12-06 22:57:11 +01:00