mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
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>
This commit is contained in:
+60
-38
@@ -103,7 +103,10 @@ type Editor struct {
|
||||
}
|
||||
|
||||
type imeState struct {
|
||||
selection key.Range
|
||||
selection struct {
|
||||
rng key.Range
|
||||
caret key.Caret
|
||||
}
|
||||
snippet key.Snippet
|
||||
start, end int
|
||||
}
|
||||
@@ -524,30 +527,40 @@ func (e *Editor) Layout(gtx layout.Context, sh text.Shaper, font text.Font, size
|
||||
e.processEvents(gtx)
|
||||
e.makeValid()
|
||||
|
||||
if e.focused {
|
||||
// Notify IME of selection if it changed.
|
||||
newSel := key.Range{
|
||||
Start: e.caret.start,
|
||||
End: e.caret.end,
|
||||
}
|
||||
if newSel != e.ime.selection {
|
||||
e.ime.selection = newSel
|
||||
key.SelectionOp{
|
||||
Tag: &e.eventKey,
|
||||
Range: newSel,
|
||||
}.Add(gtx.Ops)
|
||||
}
|
||||
|
||||
e.updateSnippet(gtx, e.ime.start, e.ime.end)
|
||||
}
|
||||
|
||||
if viewSize := gtx.Constraints.Constrain(e.dims.Size); viewSize != e.viewSize {
|
||||
e.viewSize = viewSize
|
||||
e.invalidate()
|
||||
}
|
||||
e.makeValid()
|
||||
|
||||
return e.layout(gtx, content)
|
||||
dims := e.layout(gtx, content)
|
||||
|
||||
if e.focused {
|
||||
// Notify IME of selection if it changed.
|
||||
newSel := e.ime.selection
|
||||
newSel.rng = key.Range{
|
||||
Start: e.caret.start,
|
||||
End: e.caret.end,
|
||||
}
|
||||
caretPos, carAsc, carDesc := e.caretInfo()
|
||||
newSel.caret = key.Caret{
|
||||
Pos: layout.FPt(caretPos),
|
||||
Ascent: float32(carAsc),
|
||||
Descent: float32(carDesc),
|
||||
}
|
||||
if newSel != e.ime.selection {
|
||||
e.ime.selection = newSel
|
||||
key.SelectionOp{
|
||||
Tag: &e.eventKey,
|
||||
Range: newSel.rng,
|
||||
Caret: newSel.caret,
|
||||
}.Add(gtx.Ops)
|
||||
}
|
||||
|
||||
e.updateSnippet(gtx, e.ime.start, e.ime.end)
|
||||
}
|
||||
|
||||
return dims
|
||||
}
|
||||
|
||||
// updateSnippet adds a key.SnippetOp if the snippet content or position
|
||||
@@ -710,29 +723,23 @@ func (e *Editor) PaintCaret(gtx layout.Context) {
|
||||
if !e.caret.on {
|
||||
return
|
||||
}
|
||||
carWidth := fixed.I(gtx.Px(unit.Dp(1)))
|
||||
caretStart := e.closestPosition(combinedPos{runes: e.caret.start})
|
||||
carX := caretStart.x
|
||||
carY := caretStart.y
|
||||
|
||||
carX -= carWidth / 2
|
||||
carAsc, carDesc := -e.lines[caretStart.lineCol.Y].Bounds.Min.Y, e.lines[caretStart.lineCol.Y].Bounds.Max.Y
|
||||
carRect := image.Rectangle{
|
||||
Min: image.Point{X: carX.Ceil(), Y: carY - carAsc.Ceil()},
|
||||
Max: image.Point{X: carX.Ceil() + carWidth.Ceil(), Y: carY + carDesc.Ceil()},
|
||||
carWidth2 := gtx.Px(unit.Dp(1)) / 2
|
||||
if carWidth2 < 1 {
|
||||
carWidth2 = 1
|
||||
}
|
||||
caretPos, carAsc, carDesc := e.caretInfo()
|
||||
|
||||
carRect := image.Rectangle{
|
||||
Min: caretPos.Sub(image.Pt(carWidth2, carAsc)),
|
||||
Max: caretPos.Add(image.Pt(carWidth2, carDesc)),
|
||||
}
|
||||
carRect = carRect.Add(image.Point{
|
||||
X: -e.scrollOff.X,
|
||||
Y: -e.scrollOff.Y,
|
||||
})
|
||||
cl := textPadding(e.lines)
|
||||
// Account for caret width to each side.
|
||||
whalf := (carWidth / 2).Ceil()
|
||||
if cl.Max.X < whalf {
|
||||
cl.Max.X = whalf
|
||||
if cl.Max.X < carWidth2 {
|
||||
cl.Max.X = carWidth2
|
||||
}
|
||||
if cl.Min.X > -whalf {
|
||||
cl.Min.X = -whalf
|
||||
if cl.Min.X > -carWidth2 {
|
||||
cl.Min.X = -carWidth2
|
||||
}
|
||||
cl.Max = cl.Max.Add(e.viewSize)
|
||||
carRect = cl.Intersect(carRect)
|
||||
@@ -742,6 +749,21 @@ func (e *Editor) PaintCaret(gtx layout.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Editor) caretInfo() (pos image.Point, ascent, descent int) {
|
||||
caretStart := e.closestPosition(combinedPos{runes: e.caret.start})
|
||||
carX := caretStart.x
|
||||
carY := caretStart.y
|
||||
|
||||
ascent = -e.lines[caretStart.lineCol.Y].Bounds.Min.Y.Ceil()
|
||||
descent = e.lines[caretStart.lineCol.Y].Bounds.Max.Y.Ceil()
|
||||
pos = image.Point{
|
||||
X: carX.Round(),
|
||||
Y: carY,
|
||||
}
|
||||
pos = pos.Sub(e.scrollOff)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: copied from package math. Remove when Go 1.18 is minimum.
|
||||
const (
|
||||
intSize = 32 << (^uint(0) >> 63) // 32 or 64
|
||||
|
||||
Reference in New Issue
Block a user