mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
io/input,io/clipboard: [API] replace ReadOp with command
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+9
-2
@@ -123,12 +123,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/cgo"
|
"runtime/cgo"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
@@ -137,12 +139,12 @@ import (
|
|||||||
"gioui.org/internal/f32color"
|
"gioui.org/internal/f32color"
|
||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
"gioui.org/io/clipboard"
|
|
||||||
"gioui.org/io/input"
|
"gioui.org/io/input"
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
"gioui.org/io/semantic"
|
"gioui.org/io/semantic"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
|
"gioui.org/io/transfer"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1311,7 +1313,12 @@ func (w *window) ReadClipboard() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
content := goString(env, C.jstring(c))
|
content := goString(env, C.jstring(c))
|
||||||
w.callbacks.Event(clipboard.Event{Text: content})
|
w.callbacks.Event(transfer.DataEvent{
|
||||||
|
Type: "application/text",
|
||||||
|
Open: func() io.ReadCloser {
|
||||||
|
return io.NopCloser(strings.NewReader(content))
|
||||||
|
},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-2
@@ -72,17 +72,19 @@ import "C"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
|
"io"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
"gioui.org/io/clipboard"
|
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
|
"gioui.org/io/transfer"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -265,7 +267,12 @@ func (w *window) ReadClipboard() {
|
|||||||
cstr := C.readClipboard()
|
cstr := C.readClipboard()
|
||||||
defer C.CFRelease(cstr)
|
defer C.CFRelease(cstr)
|
||||||
content := nsstringToString(cstr)
|
content := nsstringToString(cstr)
|
||||||
w.w.Event(clipboard.Event{Text: content})
|
w.w.Event(transfer.DataEvent{
|
||||||
|
Type: "application/text",
|
||||||
|
Open: func() io.ReadCloser {
|
||||||
|
return io.NopCloser(strings.NewReader(content))
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) WriteClipboard(mime string, s []byte) {
|
func (w *window) WriteClipboard(mime string, s []byte) {
|
||||||
|
|||||||
+8
-2
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
"time"
|
"time"
|
||||||
@@ -15,10 +16,10 @@ import (
|
|||||||
"gioui.org/internal/f32color"
|
"gioui.org/internal/f32color"
|
||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
"gioui.org/io/clipboard"
|
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
|
"gioui.org/io/transfer"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -101,7 +102,12 @@ func newWindow(win *callbacks, options []Option) error {
|
|||||||
})
|
})
|
||||||
w.clipboardCallback = w.funcOf(func(this js.Value, args []js.Value) interface{} {
|
w.clipboardCallback = w.funcOf(func(this js.Value, args []js.Value) interface{} {
|
||||||
content := args[0].String()
|
content := args[0].String()
|
||||||
go win.Event(clipboard.Event{Text: content})
|
go win.Event(transfer.DataEvent{
|
||||||
|
Type: "application/text",
|
||||||
|
Open: func() io.ReadCloser {
|
||||||
|
return io.NopCloser(strings.NewReader(content))
|
||||||
|
},
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
w.addEventListeners()
|
w.addEventListeners()
|
||||||
|
|||||||
+9
-2
@@ -8,16 +8,18 @@ package app
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"image"
|
"image"
|
||||||
|
"io"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"gioui.org/internal/f32"
|
"gioui.org/internal/f32"
|
||||||
"gioui.org/io/clipboard"
|
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
|
"gioui.org/io/transfer"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
|
|
||||||
_ "gioui.org/internal/cocoainit"
|
_ "gioui.org/internal/cocoainit"
|
||||||
@@ -305,7 +307,12 @@ func (w *window) ReadClipboard() {
|
|||||||
defer C.CFRelease(cstr)
|
defer C.CFRelease(cstr)
|
||||||
}
|
}
|
||||||
content := nsstringToString(cstr)
|
content := nsstringToString(cstr)
|
||||||
w.w.Event(clipboard.Event{Text: content})
|
w.w.Event(transfer.DataEvent{
|
||||||
|
Type: "application/text",
|
||||||
|
Open: func() io.ReadCloser {
|
||||||
|
return io.NopCloser(strings.NewReader(content))
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) WriteClipboard(mime string, s []byte) {
|
func (w *window) WriteClipboard(mime string, s []byte) {
|
||||||
|
|||||||
+9
-5
@@ -25,10 +25,10 @@ import (
|
|||||||
"gioui.org/app/internal/xkb"
|
"gioui.org/app/internal/xkb"
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
"gioui.org/internal/fling"
|
"gioui.org/internal/fling"
|
||||||
"gioui.org/io/clipboard"
|
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
|
"gioui.org/io/transfer"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -209,7 +209,7 @@ type window struct {
|
|||||||
wsize image.Point // window config size before going fullscreen or maximized
|
wsize image.Point // window config size before going fullscreen or maximized
|
||||||
inCompositor bool // window is moving or being resized
|
inCompositor bool // window is moving or being resized
|
||||||
|
|
||||||
clipReads chan clipboard.Event
|
clipReads chan transfer.DataEvent
|
||||||
|
|
||||||
wakeups chan struct{}
|
wakeups chan struct{}
|
||||||
}
|
}
|
||||||
@@ -354,7 +354,7 @@ func (d *wlDisplay) createNativeWindow(options []Option) (*window, error) {
|
|||||||
ppdp: ppdp,
|
ppdp: ppdp,
|
||||||
ppsp: ppdp,
|
ppsp: ppdp,
|
||||||
wakeups: make(chan struct{}, 1),
|
wakeups: make(chan struct{}, 1),
|
||||||
clipReads: make(chan clipboard.Event, 1),
|
clipReads: make(chan transfer.DataEvent, 1),
|
||||||
}
|
}
|
||||||
w.surf = C.wl_compositor_create_surface(d.compositor)
|
w.surf = C.wl_compositor_create_surface(d.compositor)
|
||||||
if w.surf == nil {
|
if w.surf == nil {
|
||||||
@@ -1021,14 +1021,18 @@ func (w *window) ReadClipboard() {
|
|||||||
r, err := w.disp.readClipboard()
|
r, err := w.disp.readClipboard()
|
||||||
// Send empty responses on unavailable clipboards or errors.
|
// Send empty responses on unavailable clipboards or errors.
|
||||||
if r == nil || err != nil {
|
if r == nil || err != nil {
|
||||||
w.w.Event(clipboard.Event{})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Don't let slow clipboard transfers block event loop.
|
// Don't let slow clipboard transfers block event loop.
|
||||||
go func() {
|
go func() {
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
data, _ := io.ReadAll(r)
|
data, _ := io.ReadAll(r)
|
||||||
w.clipReads <- clipboard.Event{Text: string(data)}
|
w.clipReads <- transfer.DataEvent{
|
||||||
|
Type: "application/text",
|
||||||
|
Open: func() io.ReadCloser {
|
||||||
|
return io.NopCloser(bytes.NewReader(data))
|
||||||
|
},
|
||||||
|
}
|
||||||
w.Wakeup()
|
w.Wakeup()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-2
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
"io"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -22,10 +23,10 @@ import (
|
|||||||
gowindows "golang.org/x/sys/windows"
|
gowindows "golang.org/x/sys/windows"
|
||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
"gioui.org/io/clipboard"
|
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
|
"gioui.org/io/transfer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ViewEvent struct {
|
type ViewEvent struct {
|
||||||
@@ -667,7 +668,12 @@ func (w *window) readClipboard() error {
|
|||||||
}
|
}
|
||||||
defer windows.GlobalUnlock(mem)
|
defer windows.GlobalUnlock(mem)
|
||||||
content := gowindows.UTF16PtrToString((*uint16)(unsafe.Pointer(ptr)))
|
content := gowindows.UTF16PtrToString((*uint16)(unsafe.Pointer(ptr)))
|
||||||
w.w.Event(clipboard.Event{Text: content})
|
w.w.Event(transfer.DataEvent{
|
||||||
|
Type: "application/text",
|
||||||
|
Open: func() io.ReadCloser {
|
||||||
|
return io.NopCloser(strings.NewReader(content))
|
||||||
|
},
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-2
@@ -30,16 +30,18 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
"gioui.org/io/clipboard"
|
|
||||||
"gioui.org/io/key"
|
"gioui.org/io/key"
|
||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
|
"gioui.org/io/transfer"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
|
|
||||||
syscall "golang.org/x/sys/unix"
|
syscall "golang.org/x/sys/unix"
|
||||||
@@ -650,7 +652,12 @@ func (h *x11EventHandler) handleEvents() bool {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
str := C.GoStringN((*C.char)(unsafe.Pointer(text.value)), C.int(text.nitems))
|
str := C.GoStringN((*C.char)(unsafe.Pointer(text.value)), C.int(text.nitems))
|
||||||
w.w.Event(clipboard.Event{Text: str})
|
w.w.Event(transfer.DataEvent{
|
||||||
|
Type: "application/text",
|
||||||
|
Open: func() io.ReadCloser {
|
||||||
|
return io.NopCloser(strings.NewReader(str))
|
||||||
|
},
|
||||||
|
})
|
||||||
case C.SelectionRequest:
|
case C.SelectionRequest:
|
||||||
cevt := (*C.XSelectionRequestEvent)(unsafe.Pointer(xev))
|
cevt := (*C.XSelectionRequestEvent)(unsafe.Pointer(xev))
|
||||||
if (cevt.selection != w.atoms.clipboard && cevt.selection != w.atoms.primary) || cevt.property == C.None {
|
if (cevt.selection != w.atoms.clipboard && cevt.selection != w.atoms.primary) || cevt.property == C.None {
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ const (
|
|||||||
TypePass
|
TypePass
|
||||||
TypePopPass
|
TypePopPass
|
||||||
TypePointerInput
|
TypePointerInput
|
||||||
TypeClipboardRead
|
|
||||||
TypeSource
|
TypeSource
|
||||||
TypeTarget
|
TypeTarget
|
||||||
TypeKeyInput
|
TypeKeyInput
|
||||||
@@ -142,7 +141,6 @@ const (
|
|||||||
TypePassLen = 1
|
TypePassLen = 1
|
||||||
TypePopPassLen = 1
|
TypePopPassLen = 1
|
||||||
TypePointerInputLen = 1 + 1 + 1*2 + 2*4 + 2*4
|
TypePointerInputLen = 1 + 1 + 1*2 + 2*4 + 2*4
|
||||||
TypeClipboardReadLen = 1
|
|
||||||
TypeSourceLen = 1
|
TypeSourceLen = 1
|
||||||
TypeTargetLen = 1
|
TypeTargetLen = 1
|
||||||
TypeKeyInputLen = 1 + 1
|
TypeKeyInputLen = 1 + 1
|
||||||
@@ -419,7 +417,6 @@ var opProps = [0x100]opProp{
|
|||||||
TypePass: {Size: TypePassLen, NumRefs: 0},
|
TypePass: {Size: TypePassLen, NumRefs: 0},
|
||||||
TypePopPass: {Size: TypePopPassLen, NumRefs: 0},
|
TypePopPass: {Size: TypePopPassLen, NumRefs: 0},
|
||||||
TypePointerInput: {Size: TypePointerInputLen, NumRefs: 1},
|
TypePointerInput: {Size: TypePointerInputLen, NumRefs: 1},
|
||||||
TypeClipboardRead: {Size: TypeClipboardReadLen, NumRefs: 1},
|
|
||||||
TypeSource: {Size: TypeSourceLen, NumRefs: 2},
|
TypeSource: {Size: TypeSourceLen, NumRefs: 2},
|
||||||
TypeTarget: {Size: TypeTargetLen, NumRefs: 2},
|
TypeTarget: {Size: TypeTargetLen, NumRefs: 2},
|
||||||
TypeKeyInput: {Size: TypeKeyInputLen, NumRefs: 2},
|
TypeKeyInput: {Size: TypeKeyInputLen, NumRefs: 2},
|
||||||
@@ -484,8 +481,6 @@ func (t OpType) String() string {
|
|||||||
return "PopPass"
|
return "PopPass"
|
||||||
case TypePointerInput:
|
case TypePointerInput:
|
||||||
return "PointerInput"
|
return "PointerInput"
|
||||||
case TypeClipboardRead:
|
|
||||||
return "ClipboardRead"
|
|
||||||
case TypeSource:
|
case TypeSource:
|
||||||
return "Source"
|
return "Source"
|
||||||
case TypeTarget:
|
case TypeTarget:
|
||||||
|
|||||||
@@ -5,33 +5,20 @@ package clipboard
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"gioui.org/internal/ops"
|
|
||||||
"gioui.org/io/event"
|
"gioui.org/io/event"
|
||||||
"gioui.org/op"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Event is generated when the clipboard content is requested.
|
|
||||||
type Event struct {
|
|
||||||
Text string
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteCmd copies Text to the clipboard.
|
// WriteCmd copies Text to the clipboard.
|
||||||
type WriteCmd struct {
|
type WriteCmd struct {
|
||||||
Type string
|
Type string
|
||||||
Data io.ReadCloser
|
Data io.ReadCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadOp requests the text of the clipboard, delivered to
|
// ReadCmd requests the text of the clipboard, delivered to
|
||||||
// the current handler through an Event.
|
// the handler through an [io/transfer.DataEvent].
|
||||||
type ReadOp struct {
|
type ReadCmd struct {
|
||||||
Tag event.Tag
|
Tag event.Tag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h ReadOp) Add(o *op.Ops) {
|
|
||||||
data := ops.Write1(&o.Internal, ops.TypeClipboardReadLen, h.Tag)
|
|
||||||
data[0] = byte(ops.TypeClipboardRead)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Event) ImplementsEvent() {}
|
|
||||||
|
|
||||||
func (WriteCmd) ImplementsCommand() {}
|
func (WriteCmd) ImplementsCommand() {}
|
||||||
|
func (ReadCmd) ImplementsCommand() {}
|
||||||
|
|||||||
@@ -55,11 +55,10 @@ func (q *clipboardQueue) ProcessWriteClipboard(req clipboard.WriteCmd) {
|
|||||||
q.text = content
|
q.text = content
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *clipboardQueue) ProcessReadClipboard(refs []interface{}) {
|
func (q *clipboardQueue) ProcessReadClipboard(tag event.Tag) {
|
||||||
if q.receivers == nil {
|
if q.receivers == nil {
|
||||||
q.receivers = make(map[event.Tag]struct{})
|
q.receivers = make(map[event.Tag]struct{})
|
||||||
}
|
}
|
||||||
tag := refs[0].(event.Tag)
|
|
||||||
if _, ok := q.receivers[tag]; !ok {
|
if _, ok := q.receivers[tag]; !ok {
|
||||||
q.receivers[tag] = struct{}{}
|
q.receivers[tag] = struct{}{}
|
||||||
q.requested = false
|
q.requested = false
|
||||||
|
|||||||
+31
-20
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gioui.org/io/clipboard"
|
"gioui.org/io/clipboard"
|
||||||
"gioui.org/io/event"
|
"gioui.org/io/event"
|
||||||
|
"gioui.org/io/transfer"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,30 +17,35 @@ func TestClipboardDuplicateEvent(t *testing.T) {
|
|||||||
ops, router, handler := new(op.Ops), new(Router), make([]int, 2)
|
ops, router, handler := new(op.Ops), new(Router), make([]int, 2)
|
||||||
|
|
||||||
// Both must receive the event once
|
// Both must receive the event once
|
||||||
clipboard.ReadOp{Tag: &handler[0]}.Add(ops)
|
router.Source().Queue(clipboard.ReadCmd{Tag: &handler[0]})
|
||||||
clipboard.ReadOp{Tag: &handler[1]}.Add(ops)
|
router.Source().Queue(clipboard.ReadCmd{Tag: &handler[1]})
|
||||||
|
|
||||||
router.Frame(ops)
|
router.Frame(ops)
|
||||||
event := clipboard.Event{Text: "Test"}
|
event := transfer.DataEvent{
|
||||||
|
Type: "application/text",
|
||||||
|
Open: func() io.ReadCloser {
|
||||||
|
return io.NopCloser(strings.NewReader("Test"))
|
||||||
|
},
|
||||||
|
}
|
||||||
router.Queue(event)
|
router.Queue(event)
|
||||||
assertClipboardReadOp(t, router, 0)
|
assertClipboardReadCmd(t, router, 0)
|
||||||
assertClipboardEvent(t, router.Events(&handler[0]), true)
|
assertClipboardEvent(t, router.Events(&handler[0]), true)
|
||||||
assertClipboardEvent(t, router.Events(&handler[1]), true)
|
assertClipboardEvent(t, router.Events(&handler[1]), true)
|
||||||
ops.Reset()
|
ops.Reset()
|
||||||
|
|
||||||
// No ReadOp
|
// No ReadCmd
|
||||||
|
|
||||||
router.Frame(ops)
|
router.Frame(ops)
|
||||||
assertClipboardReadOp(t, router, 0)
|
assertClipboardReadCmd(t, router, 0)
|
||||||
assertClipboardEvent(t, router.Events(&handler[0]), false)
|
assertClipboardEvent(t, router.Events(&handler[0]), false)
|
||||||
assertClipboardEvent(t, router.Events(&handler[1]), false)
|
assertClipboardEvent(t, router.Events(&handler[1]), false)
|
||||||
ops.Reset()
|
ops.Reset()
|
||||||
|
|
||||||
clipboard.ReadOp{Tag: &handler[0]}.Add(ops)
|
router.Source().Queue(clipboard.ReadCmd{Tag: &handler[0]})
|
||||||
|
|
||||||
router.Frame(ops)
|
router.Frame(ops)
|
||||||
// No ClipboardEvent sent
|
// No ClipboardEvent sent
|
||||||
assertClipboardReadOp(t, router, 1)
|
assertClipboardReadCmd(t, router, 1)
|
||||||
assertClipboardEvent(t, router.Events(&handler[0]), false)
|
assertClipboardEvent(t, router.Events(&handler[0]), false)
|
||||||
assertClipboardEvent(t, router.Events(&handler[1]), false)
|
assertClipboardEvent(t, router.Events(&handler[1]), false)
|
||||||
ops.Reset()
|
ops.Reset()
|
||||||
@@ -50,34 +56,39 @@ func TestQueueProcessReadClipboard(t *testing.T) {
|
|||||||
ops.Reset()
|
ops.Reset()
|
||||||
|
|
||||||
// Request read
|
// Request read
|
||||||
clipboard.ReadOp{Tag: &handler[0]}.Add(ops)
|
router.Source().Queue(clipboard.ReadCmd{Tag: &handler[0]})
|
||||||
|
|
||||||
router.Frame(ops)
|
router.Frame(ops)
|
||||||
assertClipboardReadOp(t, router, 1)
|
assertClipboardReadCmd(t, router, 1)
|
||||||
ops.Reset()
|
ops.Reset()
|
||||||
|
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
// No ReadOp
|
// No ReadCmd
|
||||||
// One receiver must still wait for response
|
// One receiver must still wait for response
|
||||||
|
|
||||||
router.Frame(ops)
|
router.Frame(ops)
|
||||||
assertClipboardReadOpDuplicated(t, router, 1)
|
assertClipboardReadDuplicated(t, router, 1)
|
||||||
ops.Reset()
|
ops.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
router.Frame(ops)
|
router.Frame(ops)
|
||||||
// Send the clipboard event
|
// Send the clipboard event
|
||||||
event := clipboard.Event{Text: "Text 2"}
|
event := transfer.DataEvent{
|
||||||
|
Type: "application/text",
|
||||||
|
Open: func() io.ReadCloser {
|
||||||
|
return io.NopCloser(strings.NewReader("Text 2"))
|
||||||
|
},
|
||||||
|
}
|
||||||
router.Queue(event)
|
router.Queue(event)
|
||||||
assertClipboardReadOp(t, router, 0)
|
assertClipboardReadCmd(t, router, 0)
|
||||||
assertClipboardEvent(t, router.Events(&handler[0]), true)
|
assertClipboardEvent(t, router.Events(&handler[0]), true)
|
||||||
ops.Reset()
|
ops.Reset()
|
||||||
|
|
||||||
// No ReadOp
|
// No ReadCmd
|
||||||
// There's no receiver waiting
|
// There's no receiver waiting
|
||||||
|
|
||||||
router.Frame(ops)
|
router.Frame(ops)
|
||||||
assertClipboardReadOp(t, router, 0)
|
assertClipboardReadCmd(t, router, 0)
|
||||||
assertClipboardEvent(t, router.Events(&handler[0]), false)
|
assertClipboardEvent(t, router.Events(&handler[0]), false)
|
||||||
ops.Reset()
|
ops.Reset()
|
||||||
}
|
}
|
||||||
@@ -102,7 +113,7 @@ func TestQueueProcessWriteClipboard(t *testing.T) {
|
|||||||
router.Source().Queue(clipboard.WriteCmd{Type: mime, Data: io.NopCloser(strings.NewReader("Write 2"))})
|
router.Source().Queue(clipboard.WriteCmd{Type: mime, Data: io.NopCloser(strings.NewReader("Write 2"))})
|
||||||
|
|
||||||
router.Frame(ops)
|
router.Frame(ops)
|
||||||
assertClipboardReadOp(t, router, 0)
|
assertClipboardReadCmd(t, router, 0)
|
||||||
assertClipboardWriteCmd(t, router, mime, "Write 2")
|
assertClipboardWriteCmd(t, router, mime, "Write 2")
|
||||||
ops.Reset()
|
ops.Reset()
|
||||||
}
|
}
|
||||||
@@ -112,7 +123,7 @@ func assertClipboardEvent(t *testing.T, events []event.Event, expected bool) {
|
|||||||
var evtClipboard int
|
var evtClipboard int
|
||||||
for _, e := range events {
|
for _, e := range events {
|
||||||
switch e.(type) {
|
switch e.(type) {
|
||||||
case clipboard.Event:
|
case transfer.DataEvent:
|
||||||
evtClipboard++
|
evtClipboard++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,7 +135,7 @@ func assertClipboardEvent(t *testing.T, events []event.Event, expected bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertClipboardReadOp(t *testing.T, router *Router, expected int) {
|
func assertClipboardReadCmd(t *testing.T, router *Router, expected int) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if len(router.cqueue.receivers) != expected {
|
if len(router.cqueue.receivers) != expected {
|
||||||
t.Error("unexpected number of receivers")
|
t.Error("unexpected number of receivers")
|
||||||
@@ -134,7 +145,7 @@ func assertClipboardReadOp(t *testing.T, router *Router, expected int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertClipboardReadOpDuplicated(t *testing.T, router *Router, expected int) {
|
func assertClipboardReadDuplicated(t *testing.T, router *Router, expected int) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if len(router.cqueue.receivers) != expected {
|
if len(router.cqueue.receivers) != expected {
|
||||||
t.Error("receivers removed")
|
t.Error("receivers removed")
|
||||||
|
|||||||
+3
-3
@@ -198,7 +198,7 @@ func (q *Router) Queue(events ...event.Event) bool {
|
|||||||
if f := q.key.queue.focus; f != nil {
|
if f := q.key.queue.focus; f != nil {
|
||||||
q.handlers.Add(f, e)
|
q.handlers.Add(f, e)
|
||||||
}
|
}
|
||||||
case clipboard.Event:
|
case transfer.DataEvent:
|
||||||
q.cqueue.Push(e, &q.handlers)
|
q.cqueue.Push(e, &q.handlers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,6 +224,8 @@ func (q *Router) executeCommands() {
|
|||||||
q.pointer.queue.offerData(req, &q.handlers)
|
q.pointer.queue.offerData(req, &q.handlers)
|
||||||
case clipboard.WriteCmd:
|
case clipboard.WriteCmd:
|
||||||
q.cqueue.ProcessWriteClipboard(req)
|
q.cqueue.ProcessWriteClipboard(req)
|
||||||
|
case clipboard.ReadCmd:
|
||||||
|
q.cqueue.ProcessReadClipboard(req.Tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
q.commands = nil
|
q.commands = nil
|
||||||
@@ -429,8 +431,6 @@ func (q *Router) collect() {
|
|||||||
q.wakeup = true
|
q.wakeup = true
|
||||||
q.wakeupTime = op.At
|
q.wakeupTime = op.At
|
||||||
}
|
}
|
||||||
case ops.TypeClipboardRead:
|
|
||||||
q.cqueue.ProcessReadClipboard(encOp.Refs)
|
|
||||||
case ops.TypeSave:
|
case ops.TypeSave:
|
||||||
id := ops.DecodeSave(encOp.Data)
|
id := ops.DecodeSave(encOp.Data)
|
||||||
if extra := id - len(q.savedTrans) + 1; extra > 0 {
|
if extra := id - len(q.savedTrans) + 1; extra > 0 {
|
||||||
|
|||||||
+7
-3
@@ -21,6 +21,7 @@ import (
|
|||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
"gioui.org/io/semantic"
|
"gioui.org/io/semantic"
|
||||||
"gioui.org/io/system"
|
"gioui.org/io/system"
|
||||||
|
"gioui.org/io/transfer"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
"gioui.org/op/clip"
|
"gioui.org/op/clip"
|
||||||
@@ -377,10 +378,13 @@ func (e *Editor) processKey(gtx layout.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Complete a paste event, initiated by Shortcut-V in Editor.command().
|
// Complete a paste event, initiated by Shortcut-V in Editor.command().
|
||||||
case clipboard.Event:
|
case transfer.DataEvent:
|
||||||
e.scrollCaret = true
|
e.scrollCaret = true
|
||||||
e.scroller.Stop()
|
e.scroller.Stop()
|
||||||
e.Insert(ke.Text)
|
content, err := io.ReadAll(ke.Open())
|
||||||
|
if err == nil {
|
||||||
|
e.Insert(string(content))
|
||||||
|
}
|
||||||
case key.SelectionEvent:
|
case key.SelectionEvent:
|
||||||
e.scrollCaret = true
|
e.scrollCaret = true
|
||||||
e.scroller.Stop()
|
e.scroller.Stop()
|
||||||
@@ -411,7 +415,7 @@ func (e *Editor) command(gtx layout.Context, k key.Event) {
|
|||||||
// half is in Editor.processKey() under clipboard.Event.
|
// half is in Editor.processKey() under clipboard.Event.
|
||||||
case "V":
|
case "V":
|
||||||
if !e.ReadOnly {
|
if !e.ReadOnly {
|
||||||
clipboard.ReadOp{Tag: &e.eventKey}.Add(gtx.Ops)
|
gtx.Queue(clipboard.ReadCmd{Tag: &e.eventKey})
|
||||||
}
|
}
|
||||||
// Copy or Cut selection -- ignored if nothing selected.
|
// Copy or Cut selection -- ignored if nothing selected.
|
||||||
case "C", "X":
|
case "C", "X":
|
||||||
|
|||||||
Reference in New Issue
Block a user