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>
Android doesn't distinguish between the arrow keys on a keyboard and the
directional keys on a remote control, so there's no way to move the caret
in an Editor with arrow keys. This change updates the Android port to map
Android's DPAD_* key codes to the arrow key names, fixing caret movement.
The change also updates Editor to only request arrow keys that actually move
the caret, to keep directional focus movement working.
Fixes: https://todo.sr.ht/~eliasnaur/gio/410
Signed-off-by: Mearaj <mearajbhagad@gmail.com>
Prior to this change an editor with no content and a zero minimum
constraint would return itself has having width zero. This
prevented users from being able to see the editor when they
moved focus to it, as it could not display its caret. This
simple change ensures that, at minimum, the editor returns
its dimensions to include the width of a caret.
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
Before this change, every Event would be passed to the focused InputOp
tag, making it impossible to implement, say, program-wide shortcuts.
This change implements key.Event routing similar to how pointer.Events
are routed: every InputOp describes the set of keys it can handle, and
the router use that information to deliver an Event to the matching
handler.
This is an API change, because every InputOp must now include a filter
matching the keys it wants to handle.
Fixes: https://todo.sr.ht/~eliasnaur/gio/395
Signed-off-by: Elias Naur <mail@eliasnaur.com>
A meaningful clip area for a key handler will matter when we start
auto-scrolling to move focused handlers into view.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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 change avoids a macro wrapping every text shape, and prepares text
shaping for scoped clip operations.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
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>