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>
- 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>
- 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>
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>
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>
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>
The Editor will handle CTRL+C and CTRL+V. The CTRL+V will paste the
content on the Editor, and CTRL+C will copy all the Editor content.
Signed-off-by: Inkeliz <inkeliz@inkeliz.com>
Move the replacing to Editor.prepend to fix SetText, and replace with
space instead of nothing to keep lines separated even in single-line
Editors.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
The existing implementation cannot remove the focus of some widget,
doesn't have an option to focus without display the on-screen keyboard
and it automatically focuses the first InputOp, aggressively.
That change aims to make possible: remove focus from any widget. Add
focus without displaying the on-screen-keyboard/soft keyboard. Don't
automatically focus any widget. Don't recover focus when the widget is
visible again.
Fixes gio#180.
Signed-off-by: Inkeliz <inkeliz@inkeliz.com>
Commit https://gioui.org/commit/b331407e81456 added text layout and shaping
based on io.Reader and changed Editor to use it. Unfortunately, as ~inkeliz
discovered, caching of shapes were also lost.
~inkeliz suggested fix,
https://lists.sr.ht/~eliasnaur/gio-patches/patches/15059
adds caching of shapes to Editor to regain lost performance.
This change repairs the cache to work on io.Reader API, in hope that the
already complicated Editor won't need additional caching.
Before this change, text layouts were represented as a slice of (rune, advance)
pairs. Unfortunately, this representation doesn't lend itself to caching of
shaping results, so change the representation of a line of text to be a pair
of text and advances:
package text
type Layout {
Text string
Advances []fixed.Int26_6
}
The Text field can then be used in a cache key, assuming Advances is
consistent with it.
The end result is that the two shaper variants of text.Shaper is reduced to
just one, and the Len field field of text.Line is no longer needed.
The changed representation adds a bit of extra work to package opentype.
Cleaning that up is left as a future TODO.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
PaintOp.Rect is the wrong abstraction; it implies a clip operation
better handled by package clip, and not all paints need it (colors).
Furthermore, it's awkward to specify a PaintOp that fills up the
current clip area, regardless of its size.
Redefine PathOp to mean "fill current clip area".
API change. Replace uses of PaintOp.Rect with a TransformOp applied
before the PaintOp.
Leave a TODO for the PathOp infinity area.
Fixes gio#167
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Use op.Offset instead, or create and manipulate a f32.Affine2D.
API change. Update your code with a gofmt rule:
gofmt -r 'op.TransformOp{}.Offset -> op.Offset'
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This change adds optional password masking to the Editor. To enable
this feature, set the new Mask field to a non-zero rune. Every rune
in the Editor's contents will be replaced by the mask rune in the
visual display, except for newlines. The actual contents of the
editor can still be accessed with Len, Text, and SetText.
Fixes gio#80
Signed-off-by: tainted-bit <sourcehut@taintedbit.com>
In preparation for adding editor masking, Editor can't rely on the
Rune and Len fields of the laid out text.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
The caret x-offset tracks residual horizontal offset for arrow key
movements. Caret movement by the mouse should reset the residual.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
An interface for scaling dp and sp is overkill, at least for all
current uses. Make it a concrete struct type, and rename it to the
shorter and more precise Metric.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Fixes misaligned carets when the Editor text contains code points
represented by multiple UTF-8 bytes. Line lengths should be
measured in bytes instead of glyphs for caret positioning.
Signed-off-by: tainted-bit <sourcehut@taintedbit.com>
Then, make layout.Context.Now a field, copied from FrameEvent.Now.
API change:
gofmt -r 'gtx.Now() -> gtx.Now'
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>
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>
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>
Instead of
type Contraints struct {
Width, Height Constraint
}
use
type Constraints struct {
Min, Max image.Point
}
which leads to simpler use. For example, the Min method is trivally replaced by
the field, and the RigidConstraints constructor is no longer a net win.
API Change. Rewrites:
gofmt -r 'gtx.Constraints.Min() -> gtx.Constraints.Min'
gofmt -r 'gtx.Constraints.Width.Min -> gtx.Constraints.Min.X'
gofmt -r 'gtx.Constraints.Height.Min -> gtx.Constraints.Min.Y'
gofmt -r 'gtx.Constraints.Height.Max -> gtx.Constraints.Max.Y'
gofmt -r 'gtx.Constraints.Width.Max -> gtx.Constraints.Max.X'
Signed-off-by: Elias Naur <mail@eliasnaur.com>
Key had an unfortunate association with keyboard input.
This is an API change. The following rewrites were run to fixup
Gio code:
$ gofmt -r 'pointer.InputOp{Key:a} -> pointer.InputOp{Tag:a}' -w .
$ gofmt -r 'pointer.InputOp{Key:a, Grab:b} -> pointer.InputOp{Tag:a, Grab:b}' -w .
$ gofmt -r 'key.InputOp{Key:a} -> key.InputOp{Tag:a}' -w .
$ gofmt -r 'key.InputOp{Key:a, Focus:b} -> key.InputOp{Tag:a, Focus:b}' -w .
$ gofmt -r 'event.Key -> event.Tag' -w .
Signed-off-by: Elias Naur <mail@eliasnaur.com>
- Focused: returns whether editor is focused
- CaretPos: returns the text line & column numbers of the caret.
- CaretCoords: returns the x & y pixel coordinates of the caret.
- NumLines: returns the number of text lines in the editor
Signed-off-by: Larry Clapp <larry@theclapp.org>