mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
Compare commits
6 Commits
a8fe27488f
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a5fa17a39 | |||
| 5191409708 | |||
| 3eab806940 | |||
| 30dc7ff294 | |||
| 9e18cb93fb | |||
| e4932e163e |
@@ -207,7 +207,9 @@ const (
|
||||
CFS_POINT = 0x0002
|
||||
CFS_CANDIDATEPOS = 0x0040
|
||||
|
||||
HWND_TOPMOST = ^(uint32(1) - 1) // -1
|
||||
HWND_TOP = syscall.Handle(0)
|
||||
HWND_TOPMOST = ^(syscall.Handle(1) - 1) // -1
|
||||
HWND_NOTOPMOST = ^(syscall.Handle(2) - 1) // -2
|
||||
|
||||
HTCAPTION = 2
|
||||
HTCLIENT = 1
|
||||
@@ -782,7 +784,7 @@ func SetWindowPlacement(hwnd syscall.Handle, wp *WindowPlacement) {
|
||||
_SetWindowPlacement.Call(uintptr(hwnd), uintptr(unsafe.Pointer(wp)))
|
||||
}
|
||||
|
||||
func SetWindowPos(hwnd syscall.Handle, hwndInsertAfter uint32, x, y, dx, dy int32, style uintptr) {
|
||||
func SetWindowPos(hwnd, hwndInsertAfter syscall.Handle, x, y, dx, dy int32, style uintptr) {
|
||||
_SetWindowPos.Call(uintptr(hwnd), uintptr(hwndInsertAfter),
|
||||
uintptr(x), uintptr(y),
|
||||
uintptr(dx), uintptr(dy),
|
||||
|
||||
+26
-11
@@ -699,7 +699,13 @@ func (w *window) ReadClipboard() {
|
||||
w.readClipboard()
|
||||
}
|
||||
|
||||
func (w *window) readClipboard() error {
|
||||
func (w *window) readClipboard() (cerr error) {
|
||||
defer func() {
|
||||
if cerr != nil {
|
||||
w.processDataEvent("")
|
||||
}
|
||||
}()
|
||||
|
||||
if err := windows.OpenClipboard(w.hwnd); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -714,13 +720,17 @@ func (w *window) readClipboard() error {
|
||||
}
|
||||
defer windows.GlobalUnlock(mem)
|
||||
content := gowindows.UTF16PtrToString((*uint16)(unsafe.Pointer(ptr)))
|
||||
w.processDataEvent(content)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *window) processDataEvent(content string) {
|
||||
w.ProcessEvent(transfer.DataEvent{
|
||||
Type: "application/text",
|
||||
Open: func() io.ReadCloser {
|
||||
return io.NopCloser(strings.NewReader(content))
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *window) Configure(options []Option) {
|
||||
@@ -737,7 +747,16 @@ func (w *window) Configure(options []Option) {
|
||||
style := windows.GetWindowLong(w.hwnd, windows.GWL_STYLE)
|
||||
var showMode int32
|
||||
var x, y, width, height int32
|
||||
swpStyle := uintptr(windows.SWP_NOZORDER | windows.SWP_FRAMECHANGED)
|
||||
swpStyle := uintptr(windows.SWP_FRAMECHANGED)
|
||||
if cnf.TopMost == w.config.TopMost {
|
||||
// Don't change the z-order if TopMost didn't change.
|
||||
swpStyle |= windows.SWP_NOZORDER
|
||||
}
|
||||
hwndAfter := windows.HWND_NOTOPMOST
|
||||
if cnf.TopMost {
|
||||
hwndAfter = windows.HWND_TOPMOST
|
||||
}
|
||||
w.config.TopMost = cnf.TopMost
|
||||
winStyle := uintptr(windows.WS_OVERLAPPEDWINDOW)
|
||||
style &^= winStyle
|
||||
switch cnf.Mode {
|
||||
@@ -789,7 +808,7 @@ func (w *window) Configure(options []Option) {
|
||||
|
||||
// Note: these invocation all trigger the windows callback method which may process a pending system.ActionCenter
|
||||
// action, so SetWindowPos should come first so as to not "overwrite" system.ActionCenter.
|
||||
windows.SetWindowPos(w.hwnd, 0, x, y, width, height, swpStyle)
|
||||
windows.SetWindowPos(w.hwnd, hwndAfter, x, y, width, height, swpStyle)
|
||||
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style)
|
||||
windows.ShowWindow(w.hwnd, showMode)
|
||||
}
|
||||
@@ -910,19 +929,15 @@ func (w *window) Perform(acts system.Action) {
|
||||
y := (mi.Bottom - mi.Top - dy) / 2
|
||||
windows.SetWindowPos(w.hwnd, 0, x, y, dx, dy, windows.SWP_NOZORDER|windows.SWP_FRAMECHANGED)
|
||||
case system.ActionRaise:
|
||||
w.raise()
|
||||
windows.SetForegroundWindow(w.hwnd)
|
||||
windows.SetWindowPos(w.hwnd, windows.HWND_TOP, 0, 0, 0, 0,
|
||||
windows.SWP_NOMOVE|windows.SWP_NOSIZE|windows.SWP_SHOWWINDOW)
|
||||
case system.ActionClose:
|
||||
windows.PostMessage(w.hwnd, windows.WM_CLOSE, 0, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (w *window) raise() {
|
||||
windows.SetForegroundWindow(w.hwnd)
|
||||
windows.SetWindowPos(w.hwnd, windows.HWND_TOPMOST, 0, 0, 0, 0,
|
||||
windows.SWP_NOMOVE|windows.SWP_NOSIZE|windows.SWP_SHOWWINDOW)
|
||||
}
|
||||
|
||||
func convertKeyCode(code uintptr) (key.Name, bool) {
|
||||
if '0' <= code && code <= '9' || 'A' <= code && code <= 'Z' {
|
||||
return key.Name(rune(code)), true
|
||||
|
||||
+2
-1
@@ -439,6 +439,7 @@ func (c *callbacks) EditorState() editorState {
|
||||
|
||||
func (c *callbacks) SetComposingRegion(r key.Range) {
|
||||
c.w.imeState.compose = r
|
||||
c.w.driver.ProcessEvent(key.CompositionEvent(r))
|
||||
}
|
||||
|
||||
func (c *callbacks) EditorInsert(text string) {
|
||||
@@ -971,7 +972,7 @@ func Decorated(enabled bool) Option {
|
||||
|
||||
// TopMost windows will be rendered above all other non-top-most windows.
|
||||
//
|
||||
// TopMost windows are only supported on MacOS currently.
|
||||
// TopMost windows are supported on macOS, Windows.
|
||||
func TopMost(enabled bool) Option {
|
||||
return func(_ unit.Metric, cnf *Config) {
|
||||
cnf.TopMost = enabled
|
||||
|
||||
+8
-1
@@ -419,7 +419,7 @@ func (f *filter) Merge(f2 filter) {
|
||||
|
||||
func (f *filter) Matches(e event.Event) bool {
|
||||
switch e.(type) {
|
||||
case key.FocusEvent, key.SnippetEvent, key.EditEvent, key.SelectionEvent:
|
||||
case key.FocusEvent, key.SnippetEvent, key.EditEvent, key.SelectionEvent, key.CompositionEvent:
|
||||
return f.focusable
|
||||
default:
|
||||
return f.pointer.Matches(e)
|
||||
@@ -463,6 +463,13 @@ func (q *Router) processEvent(e event.Event, system bool) {
|
||||
evts = append(evts, taggedEvent{tag: f, event: e})
|
||||
}
|
||||
q.changeState(e, state, evts)
|
||||
case key.CompositionEvent:
|
||||
e = key.CompositionEvent(rangeNorm(key.Range(e)))
|
||||
var evts []taggedEvent
|
||||
if f := state.focus; f != nil {
|
||||
evts = append(evts, taggedEvent{tag: f, event: e})
|
||||
}
|
||||
q.changeState(e, state, evts)
|
||||
case key.EditEvent, key.FocusEvent, key.SelectionEvent:
|
||||
var evts []taggedEvent
|
||||
if f := state.focus; f != nil {
|
||||
|
||||
+9
-5
@@ -77,6 +77,9 @@ type Caret struct {
|
||||
// SelectionEvent is generated when an input method changes the selection.
|
||||
type SelectionEvent Range
|
||||
|
||||
// CompositionEvent is generated when an input method changes the composing range.
|
||||
type CompositionEvent Range
|
||||
|
||||
// SnippetEvent is generated when the snippet range is updated by an
|
||||
// input method.
|
||||
type SnippetEvent Range
|
||||
@@ -243,11 +246,12 @@ func (h InputHintOp) Add(o *op.Ops) {
|
||||
data[1] = byte(h.Hint)
|
||||
}
|
||||
|
||||
func (EditEvent) ImplementsEvent() {}
|
||||
func (Event) ImplementsEvent() {}
|
||||
func (FocusEvent) ImplementsEvent() {}
|
||||
func (SnippetEvent) ImplementsEvent() {}
|
||||
func (SelectionEvent) ImplementsEvent() {}
|
||||
func (EditEvent) ImplementsEvent() {}
|
||||
func (Event) ImplementsEvent() {}
|
||||
func (FocusEvent) ImplementsEvent() {}
|
||||
func (CompositionEvent) ImplementsEvent() {}
|
||||
func (SnippetEvent) ImplementsEvent() {}
|
||||
func (SelectionEvent) ImplementsEvent() {}
|
||||
|
||||
func (FocusCmd) ImplementsCommand() {}
|
||||
func (SoftKeyboardCmd) ImplementsCommand() {}
|
||||
|
||||
+30
-2
@@ -25,6 +25,7 @@ import (
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
"gioui.org/op/clip"
|
||||
"gioui.org/op/paint"
|
||||
"gioui.org/text"
|
||||
"gioui.org/unit"
|
||||
)
|
||||
@@ -107,8 +108,9 @@ type imeState struct {
|
||||
rng key.Range
|
||||
caret key.Caret
|
||||
}
|
||||
snippet key.Snippet
|
||||
start, end int
|
||||
snippet key.Snippet
|
||||
composition key.Range
|
||||
start, end int
|
||||
}
|
||||
|
||||
type maskReader struct {
|
||||
@@ -398,9 +400,12 @@ func (e *Editor) processKey(gtx layout.Context) (EditorEvent, bool) {
|
||||
case key.FocusEvent:
|
||||
// Reset IME state.
|
||||
e.ime.imeState = imeState{}
|
||||
e.ime.composition = key.Range{Start: -1, End: -1}
|
||||
if ke.Focus && !e.ReadOnly {
|
||||
gtx.Execute(key.SoftKeyboardCmd{Show: true})
|
||||
}
|
||||
case key.CompositionEvent:
|
||||
e.ime.composition = key.Range(ke)
|
||||
case key.Event:
|
||||
if !gtx.Focused(e) || ke.State != key.Press {
|
||||
break
|
||||
@@ -735,6 +740,7 @@ func (e *Editor) layout(gtx layout.Context, textMaterial, selectMaterial op.Call
|
||||
if e.Len() > 0 {
|
||||
e.paintSelection(gtx, selectMaterial)
|
||||
e.paintText(gtx, textMaterial)
|
||||
e.paintComposition(gtx, textMaterial)
|
||||
}
|
||||
if gtx.Enabled() {
|
||||
e.paintCaret(gtx, textMaterial)
|
||||
@@ -759,6 +765,28 @@ func (e *Editor) paintText(gtx layout.Context, material op.CallOp) {
|
||||
e.text.PaintText(gtx, material)
|
||||
}
|
||||
|
||||
func (e *Editor) paintComposition(gtx layout.Context, material op.CallOp) {
|
||||
e.initBuffer()
|
||||
r := e.ime.composition
|
||||
if r.Start == -1 || r.Start == r.End {
|
||||
return
|
||||
}
|
||||
e.text.regions = e.text.Regions(r.Start, r.End, e.text.regions)
|
||||
thickness := max(gtx.Dp(unit.Dp(1)), 1)
|
||||
for _, region := range e.text.regions {
|
||||
y := region.Bounds.Max.Y - max(region.Baseline/3, thickness)
|
||||
underline := image.Rect(region.Bounds.Min.X, y, region.Bounds.Max.X, y+thickness)
|
||||
underline = underline.Intersect(image.Rectangle{Max: e.text.viewSize})
|
||||
if underline.Empty() {
|
||||
continue
|
||||
}
|
||||
stack := clip.Rect(underline).Push(gtx.Ops)
|
||||
material.Add(gtx.Ops)
|
||||
paint.PaintOp{}.Add(gtx.Ops)
|
||||
stack.Pop()
|
||||
}
|
||||
}
|
||||
|
||||
// paintCaret paints the text glyphs using the provided material to set the fill material
|
||||
// of the caret rectangle.
|
||||
func (e *Editor) paintCaret(gtx layout.Context, material op.CallOp) {
|
||||
|
||||
Reference in New Issue
Block a user