~eliasnaur/gio-patches

gio: io: add ReadClipboardOp and WriteClipboardOp v1 PROPOSED

~inkeliz
~inkeliz: 1
 io: add ReadClipboardOp and WriteClipboardOp

 5 files changed, 151 insertions(+), 8 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~eliasnaur/gio-patches/patches/15401/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH gio] io: add ReadClipboardOp and WriteClipboardOp Export this patch

~inkeliz
From: Inkeliz <inkeliz@inkeliz.com>

Previously, the only way to manipulate the clipboard (read or write) is using the `app.Window`, thatis inacessible inside widgets by default.

The new `clipboard.ReadClipboardOp` and `clipboard.WriteClipboardOp` makes possible to read/write inside the widget. The old `system.ClipboardEvent` was removed and replaced by `clipboard.Event`.

Fixes gio#183.

Signed-off-by: Inkeliz <inkeliz@inkeliz.com>
---
 internal/opconst/ops.go   |  8 +++-
 io/clipboard/clipboard.go | 34 ++++++++++++++
 io/router/clipboard.go    | 93 +++++++++++++++++++++++++++++++++++++++
 io/router/router.go       | 17 +++++++
 io/system/system.go       |  7 ---
 5 files changed, 151 insertions(+), 8 deletions(-)
 create mode 100644 io/clipboard/clipboard.go
 create mode 100644 io/router/clipboard.go

diff --git a/internal/opconst/ops.go b/internal/opconst/ops.go
index dba0f54..3634ce9 100644
--- a/internal/opconst/ops.go
+++ b/internal/opconst/ops.go
@@ -20,6 +20,8 @@ const (
	TypeArea
	TypePointerInput
	TypePass
	TypeWriteClipboard
	TypeReadClipboard
	TypeKeyInput
	TypeKeyFocus
	TypeKeySoftKeyboard
@@ -43,6 +45,8 @@ const (
	TypeAreaLen            = 1 + 1 + 4*4
	TypePointerInputLen    = 1 + 1 + 1
	TypePassLen            = 1 + 1
	TypeWriteClipboardLen  = 1
	TypeReadClipboardLen   = 1
	TypeKeyInputLen        = 1
	TypeKeyFocusLen        = 1 + 1
	TypeKeySoftKeyboardLen = 1 + 1
@@ -67,6 +71,8 @@ func (t OpType) Size() int {
		TypeAreaLen,
		TypePointerInputLen,
		TypePassLen,
		TypeWriteClipboardLen,
		TypeReadClipboardLen,
		TypeKeyInputLen,
		TypeKeyFocusLen,
		TypeKeySoftKeyboardLen,
@@ -80,7 +86,7 @@ func (t OpType) Size() int {

func (t OpType) NumRefs() int {
	switch t {
	case TypeKeyInput, TypePointerInput, TypeProfile, TypeCall:
	case TypeKeyInput, TypePointerInput, TypeProfile, TypeCall, TypeReadClipboard, TypeWriteClipboard:
		return 1
	case TypeImage:
		return 2
diff --git a/io/clipboard/clipboard.go b/io/clipboard/clipboard.go
new file mode 100644
index 0000000..46d6b00
--- /dev/null
+++ b/io/clipboard/clipboard.go
@@ -0,0 +1,34 @@
package clipboard

import (
	"gioui.org/internal/opconst"
	"gioui.org/io/event"
	"gioui.org/op"
)

type ReadClipboardOp struct {
	Tag event.Tag
}

type WriteClipboardOp struct {
	Text string
}

// Event is generated when a handler request
// the ReadClipboardOp
type Event struct {
	Text string
}

func (h ReadClipboardOp) Add(o *op.Ops) {
	data := o.Write1(opconst.TypeReadClipboardLen, h.Tag)
	data[0] = byte(opconst.TypeReadClipboard)
}

func (h WriteClipboardOp) Add(o *op.Ops) {
	data := o.Write1(opconst.TypeWriteClipboardLen, &h.Text)
	data[0] = byte(opconst.TypeWriteClipboard)
}


func (Event) ImplementsEvent()  {}
\ No newline at end of file
diff --git a/io/router/clipboard.go b/io/router/clipboard.go
new file mode 100644
index 0000000..06aa768
--- /dev/null
+++ b/io/router/clipboard.go
@@ -0,0 +1,93 @@
package router

import (
	"gioui.org/internal/opconst"
	"gioui.org/internal/ops"
	"gioui.org/io/event"
	"gioui.org/op"
)

type clipboardQueue struct {
	receiver  event.Tag
	requested bool
	text      *string
	reader    ops.Reader
}

// WriteClipboard returns the last text supossed to be
// copied to clipboard as determined in Frame.
func (q *clipboardQueue) WriteClipboard() *string {
	if q.text != nil {
		t := q.text
		q.text = nil
		return t
	}
	return nil
}

// ReadClipboard returns true if there's any request
// to read the clipboard.
func (q *clipboardQueue) ReadClipboard() bool {
	if q.receiver != nil && !q.requested {
		q.requested = true
		return true
	}
	return false
}

func (q *clipboardQueue) Frame(root *op.Ops, events *handlerEvents) {
	q.reader.Reset(root)

	receiver, text := q.resolveClipboard(events)
	if text != nil {
		q.text = text
	}
	if receiver != nil {
		q.receiver = receiver
		q.requested = false
	}
}

func (q *clipboardQueue) Push(e event.Event, events *handlerEvents) {
	if q.receiver != nil {
		events.Add(q.receiver, e)
		q.receiver = nil
	}
}

func (q *clipboardQueue) resolveClipboard(events *handlerEvents) (receiver event.Tag, text *string) {
loop:
	for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
		switch opconst.OpType(encOp.Data[0]) {
		case opconst.TypeWriteClipboard:
			text = decodeWriteClipboard(encOp.Data, encOp.Refs)
		case opconst.TypeReadClipboard:
			receiver = decodeReadClipboard(encOp.Data, encOp.Refs)
		case opconst.TypePush:
			newReceiver, newWrite := q.resolveClipboard(events)
			if newWrite != nil {
				text = newWrite
			}
			if newReceiver != nil {
				receiver = newReceiver
			}
		case opconst.TypePop:
			break loop
		}
	}
	return receiver, text
}

func decodeWriteClipboard(d []byte, refs []interface{}) *string {
	if opconst.OpType(d[0]) != opconst.TypeWriteClipboard {
		panic("invalid op")
	}
	return refs[0].(*string)
}

func decodeReadClipboard(d []byte, refs []interface{}) event.Tag {
	if opconst.OpType(d[0]) != opconst.TypeReadClipboard {
		panic("invalid op")
	}
	return refs[0].(event.Tag)
}
diff --git a/io/router/router.go b/io/router/router.go
index d24d214..7ab212d 100644
--- a/io/router/router.go
+++ b/io/router/router.go
@@ -16,6 +16,7 @@ import (

	"gioui.org/internal/opconst"
	"gioui.org/internal/ops"
	"gioui.org/io/clipboard"
	"gioui.org/io/event"
	"gioui.org/io/key"
	"gioui.org/io/pointer"
@@ -28,6 +29,7 @@ import (
type Router struct {
	pqueue pointerQueue
	kqueue keyQueue
	cqueue clipboardQueue

	handlers handlerEvents

@@ -73,6 +75,7 @@ func (q *Router) Frame(ops *op.Ops) {

	q.pqueue.Frame(ops, &q.handlers)
	q.kqueue.Frame(ops, &q.handlers)
	q.cqueue.Frame(ops, &q.handlers)
	if q.handlers.HadEvents() {
		q.wakeup = true
		q.wakeupTime = time.Time{}
@@ -88,6 +91,8 @@ func (q *Router) Add(events ...event.Event) bool {
			q.pqueue.Push(e, &q.handlers)
		case key.EditEvent, key.Event, key.FocusEvent:
			q.kqueue.Push(e, &q.handlers)
		case clipboard.Event:
			q.cqueue.Push(e, &q.handlers)
		}
	}
	return q.handlers.HadEvents()
@@ -99,6 +104,18 @@ func (q *Router) TextInputState() TextInputState {
	return q.kqueue.InputState()
}

// WriteClipboard returns the most recent text to be copied
// to the clipboard, if any.
func (q *Router) WriteClipboard() *string {
	return q.cqueue.WriteClipboard()
}

// ReadClipboard returns true if some handler is waiting to
// retrieve the clipboard.
func (q *Router) ReadClipboard() bool {
	return q.cqueue.ReadClipboard()
}

func (q *Router) collect() {
	for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
		switch opconst.OpType(encOp.Data[0]) {
diff --git a/io/system/system.go b/io/system/system.go
index 4aa88d3..8a84051 100644
--- a/io/system/system.go
+++ b/io/system/system.go
@@ -60,12 +60,6 @@ type DestroyEvent struct {
	Err error
}

// ClipboardEvent is sent once for each request for the
// clipboard content.
type ClipboardEvent struct {
	Text string
}

// Insets is the space taken up by
// system decoration such as translucent
// system bars and software keyboards.
@@ -122,4 +116,3 @@ func (FrameEvent) ImplementsEvent()     {}
func (StageEvent) ImplementsEvent()     {}
func (*CommandEvent) ImplementsEvent()  {}
func (DestroyEvent) ImplementsEvent()   {}
func (ClipboardEvent) ImplementsEvent() {}
-- 
2.26.2