forked from joejulian/gio
io/system: add ActionInputOp to register window move gesture areas
The app.Window.Perform(ActionMove) is the wrong abstraction for initiating a move gesture: Windows needs to know the move gesture area at pointer move, and macOS needs to know the pointer button down event that triggers the move gesture. This change replaces Perform(ActionMove) with a new system.ActionInputOp that marks an area movable. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -113,6 +113,7 @@ const (
|
|||||||
|
|
||||||
HWND_TOPMOST = ^(uint32(1) - 1) // -1
|
HWND_TOPMOST = ^(uint32(1) - 1) // -1
|
||||||
|
|
||||||
|
HTCAPTION = 2
|
||||||
HTCLIENT = 1
|
HTCLIENT = 1
|
||||||
HTLEFT = 10
|
HTLEFT = 10
|
||||||
HTRIGHT = 11
|
HTRIGHT = 11
|
||||||
|
|||||||
+14
-21
@@ -232,9 +232,8 @@ type window struct {
|
|||||||
displayLink *displayLink
|
displayLink *displayLink
|
||||||
// redraw is a single entry channel for making sure only one
|
// redraw is a single entry channel for making sure only one
|
||||||
// display link redraw request is in flight.
|
// display link redraw request is in flight.
|
||||||
redraw chan struct{}
|
redraw chan struct{}
|
||||||
cursor pointer.Cursor
|
cursor pointer.Cursor
|
||||||
lastPress C.CFTypeRef
|
|
||||||
|
|
||||||
scale float32
|
scale float32
|
||||||
config Config
|
config Config
|
||||||
@@ -413,12 +412,6 @@ func (w *window) Perform(acts system.Action) {
|
|||||||
C.setScreenFrame(w.window, C.CGFloat(x), C.CGFloat(y), C.CGFloat(sz.X), C.CGFloat(sz.Y))
|
C.setScreenFrame(w.window, C.CGFloat(x), C.CGFloat(y), C.CGFloat(sz.X), C.CGFloat(sz.Y))
|
||||||
case system.ActionRaise:
|
case system.ActionRaise:
|
||||||
C.raiseWindow(w.window)
|
C.raiseWindow(w.window)
|
||||||
case system.ActionMove:
|
|
||||||
if w.lastPress != 0 {
|
|
||||||
C.performWindowDragWithEvent(w.window, w.lastPress)
|
|
||||||
C.CFRelease(w.lastPress)
|
|
||||||
w.lastPress = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if acts&system.ActionClose != 0 {
|
if acts&system.ActionClose != 0 {
|
||||||
@@ -500,6 +493,10 @@ func gio_onText(view, cstr C.CFTypeRef) {
|
|||||||
//export gio_onMouse
|
//export gio_onMouse
|
||||||
func gio_onMouse(view, evt C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx, dy C.CGFloat, ti C.double, mods C.NSUInteger) {
|
func gio_onMouse(view, evt C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx, dy C.CGFloat, ti C.double, mods C.NSUInteger) {
|
||||||
w := mustView(view)
|
w := mustView(view)
|
||||||
|
t := time.Duration(float64(ti)*float64(time.Second) + .5)
|
||||||
|
xf, yf := float32(x)*w.scale, float32(y)*w.scale
|
||||||
|
dxf, dyf := float32(dx)*w.scale, float32(dy)*w.scale
|
||||||
|
pos := f32.Point{X: xf, Y: yf}
|
||||||
var typ pointer.Type
|
var typ pointer.Type
|
||||||
switch cdir {
|
switch cdir {
|
||||||
case C.MOUSE_MOVE:
|
case C.MOUSE_MOVE:
|
||||||
@@ -508,11 +505,14 @@ func gio_onMouse(view, evt C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx
|
|||||||
typ = pointer.Release
|
typ = pointer.Release
|
||||||
case C.MOUSE_DOWN:
|
case C.MOUSE_DOWN:
|
||||||
typ = pointer.Press
|
typ = pointer.Press
|
||||||
if w.lastPress != 0 {
|
act, ok := w.w.ActionAt(pos)
|
||||||
C.CFRelease(w.lastPress)
|
if ok && w.config.Mode != Fullscreen {
|
||||||
w.lastPress = 0
|
switch act {
|
||||||
|
case system.ActionMove:
|
||||||
|
C.performWindowDragWithEvent(w.window, evt)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
w.lastPress = C.CFRetain(evt)
|
|
||||||
case C.MOUSE_SCROLL:
|
case C.MOUSE_SCROLL:
|
||||||
typ = pointer.Scroll
|
typ = pointer.Scroll
|
||||||
default:
|
default:
|
||||||
@@ -528,15 +528,12 @@ func gio_onMouse(view, evt C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx
|
|||||||
if cbtns&(1<<2) != 0 {
|
if cbtns&(1<<2) != 0 {
|
||||||
btns |= pointer.ButtonTertiary
|
btns |= pointer.ButtonTertiary
|
||||||
}
|
}
|
||||||
t := time.Duration(float64(ti)*float64(time.Second) + .5)
|
|
||||||
xf, yf := float32(x)*w.scale, float32(y)*w.scale
|
|
||||||
dxf, dyf := float32(dx)*w.scale, float32(dy)*w.scale
|
|
||||||
w.w.Event(pointer.Event{
|
w.w.Event(pointer.Event{
|
||||||
Type: typ,
|
Type: typ,
|
||||||
Source: pointer.Mouse,
|
Source: pointer.Mouse,
|
||||||
Time: t,
|
Time: t,
|
||||||
Buttons: btns,
|
Buttons: btns,
|
||||||
Position: f32.Point{X: xf, Y: yf},
|
Position: pos,
|
||||||
Scroll: f32.Point{X: dxf, Y: dyf},
|
Scroll: f32.Point{X: dxf, Y: dyf},
|
||||||
Modifiers: convertMods(mods),
|
Modifiers: convertMods(mods),
|
||||||
})
|
})
|
||||||
@@ -768,10 +765,6 @@ func gio_onClose(view C.CFTypeRef) {
|
|||||||
w.w.Event(ViewEvent{})
|
w.w.Event(ViewEvent{})
|
||||||
deleteView(view)
|
deleteView(view)
|
||||||
w.w.Event(system.DestroyEvent{})
|
w.w.Event(system.DestroyEvent{})
|
||||||
if w.lastPress != 0 {
|
|
||||||
C.CFRelease(w.lastPress)
|
|
||||||
w.lastPress = 0
|
|
||||||
}
|
|
||||||
w.displayLink.Close()
|
w.displayLink.Close()
|
||||||
C.CFRelease(w.view)
|
C.CFRelease(w.view)
|
||||||
C.CFRelease(w.window)
|
C.CFRelease(w.window)
|
||||||
|
|||||||
+12
-6
@@ -879,6 +879,14 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
|
|||||||
w.resize(serial, edge)
|
w.resize(serial, edge)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
act, ok := w.w.ActionAt(w.lastPos)
|
||||||
|
if ok && w.config.Mode == Windowed {
|
||||||
|
switch act {
|
||||||
|
case system.ActionMove:
|
||||||
|
w.move(serial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
case BTN_RIGHT:
|
case BTN_RIGHT:
|
||||||
btn = pointer.ButtonSecondary
|
btn = pointer.ButtonSecondary
|
||||||
case BTN_MIDDLE:
|
case BTN_MIDDLE:
|
||||||
@@ -1073,19 +1081,17 @@ func (w *window) Perform(actions system.Action) {
|
|||||||
// https://wayland.app/protocols/xdg-shell#xdg_toplevel:request:set_minimized
|
// https://wayland.app/protocols/xdg-shell#xdg_toplevel:request:set_minimized
|
||||||
walkActions(actions, func(action system.Action) {
|
walkActions(actions, func(action system.Action) {
|
||||||
switch action {
|
switch action {
|
||||||
case system.ActionMove:
|
|
||||||
w.move()
|
|
||||||
case system.ActionClose:
|
case system.ActionClose:
|
||||||
w.dead = true
|
w.dead = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) move() {
|
func (w *window) move(serial C.uint32_t) {
|
||||||
if !w.inCompositor && w.seat != nil {
|
s := w.seat
|
||||||
|
if !w.inCompositor && s != nil {
|
||||||
w.inCompositor = true
|
w.inCompositor = true
|
||||||
s := w.seat
|
C.xdg_toplevel_move(w.topLvl, s.seat, serial)
|
||||||
C.xdg_toplevel_move(w.topLvl, s.seat, s.serial)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -437,6 +437,11 @@ func (w *window) hitTest(x, y int) uintptr {
|
|||||||
default:
|
default:
|
||||||
fallthrough
|
fallthrough
|
||||||
case !top && !bottom && !left && !right:
|
case !top && !bottom && !left && !right:
|
||||||
|
p := f32.Pt(float32(x), float32(y))
|
||||||
|
switch a, _ := w.w.ActionAt(p); a {
|
||||||
|
case system.ActionMove:
|
||||||
|
return windows.HTCAPTION
|
||||||
|
}
|
||||||
return windows.HTCLIENT
|
return windows.HTCLIENT
|
||||||
case top && left:
|
case top && left:
|
||||||
return windows.HTTOPLEFT
|
return windows.HTTOPLEFT
|
||||||
|
|||||||
@@ -567,6 +567,10 @@ func (c *callbacks) ClickFocus() {
|
|||||||
c.w.updateAnimation(c.d)
|
c.w.updateAnimation(c.d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *callbacks) ActionAt(p f32.Point) (system.Action, bool) {
|
||||||
|
return c.w.queue.q.ActionAt(p)
|
||||||
|
}
|
||||||
|
|
||||||
func (e *editorState) Replace(r key.Range, text string) {
|
func (e *editorState) Replace(r key.Range, text string) {
|
||||||
if r.Start > r.End {
|
if r.Start > r.End {
|
||||||
r.Start, r.End = r.End, r.Start
|
r.Start, r.End = r.End, r.Start
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ const (
|
|||||||
TypeSemanticDisabled
|
TypeSemanticDisabled
|
||||||
TypeSnippet
|
TypeSnippet
|
||||||
TypeSelection
|
TypeSelection
|
||||||
|
TypeActionInput
|
||||||
)
|
)
|
||||||
|
|
||||||
type StackID struct {
|
type StackID struct {
|
||||||
@@ -158,6 +159,7 @@ const (
|
|||||||
TypeSemanticDisabledLen = 2
|
TypeSemanticDisabledLen = 2
|
||||||
TypeSnippetLen = 1 + 4 + 4
|
TypeSnippetLen = 1 + 4 + 4
|
||||||
TypeSelectionLen = 1 + 2*4 + 2*4 + 4 + 4
|
TypeSelectionLen = 1 + 2*4 + 2*4 + 4 + 4
|
||||||
|
TypeActionInputLen = 1 + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
func (op *ClipOp) Decode(data []byte) {
|
func (op *ClipOp) Decode(data []byte) {
|
||||||
@@ -418,6 +420,7 @@ func (t OpType) Size() int {
|
|||||||
TypeSemanticDisabledLen,
|
TypeSemanticDisabledLen,
|
||||||
TypeSnippetLen,
|
TypeSnippetLen,
|
||||||
TypeSelectionLen,
|
TypeSelectionLen,
|
||||||
|
TypeActionInputLen,
|
||||||
}[t-firstOpIndex]
|
}[t-firstOpIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"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/transfer"
|
"gioui.org/io/transfer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -97,6 +98,7 @@ type areaNode struct {
|
|||||||
id SemanticID
|
id SemanticID
|
||||||
content semanticContent
|
content semanticContent
|
||||||
}
|
}
|
||||||
|
action system.Action
|
||||||
}
|
}
|
||||||
|
|
||||||
type areaKind uint8
|
type areaKind uint8
|
||||||
@@ -256,6 +258,12 @@ func (c *pointerCollector) keyInputOp(op key.InputOp) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *pointerCollector) actionInputOp(act system.Action) {
|
||||||
|
areaID := c.currentArea()
|
||||||
|
area := &c.q.areas[areaID]
|
||||||
|
area.action = act
|
||||||
|
}
|
||||||
|
|
||||||
func (c *pointerCollector) inputOp(op pointer.InputOp, events *handlerEvents) {
|
func (c *pointerCollector) inputOp(op pointer.InputOp, events *handlerEvents) {
|
||||||
areaID := c.currentArea()
|
areaID := c.currentArea()
|
||||||
area := &c.q.areas[areaID]
|
area := &c.q.areas[areaID]
|
||||||
@@ -424,6 +432,19 @@ func (q *pointerQueue) semanticIDFor(content semanticContent) SemanticID {
|
|||||||
return id.id
|
return id.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *pointerQueue) ActionAt(pos f32.Point) (system.Action, bool) {
|
||||||
|
for i := len(q.hitTree) - 1; i >= 0; i-- {
|
||||||
|
n := &q.hitTree[i]
|
||||||
|
hit, _ := q.hit(n.area, pos)
|
||||||
|
if !hit {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
area := q.areas[n.area]
|
||||||
|
return area.action, area.action != 0
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
func (q *pointerQueue) SemanticAt(pos f32.Point) (SemanticID, bool) {
|
func (q *pointerQueue) SemanticAt(pos f32.Point) (SemanticID, bool) {
|
||||||
q.assignSemIDs()
|
q.assignSemIDs()
|
||||||
for i := len(q.hitTree) - 1; i >= 0; i-- {
|
for i := len(q.hitTree) - 1; i >= 0; i-- {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
"gioui.org/io/profile"
|
"gioui.org/io/profile"
|
||||||
"gioui.org/io/semantic"
|
"gioui.org/io/semantic"
|
||||||
|
"gioui.org/io/system"
|
||||||
"gioui.org/io/transfer"
|
"gioui.org/io/transfer"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
)
|
)
|
||||||
@@ -276,6 +277,10 @@ func min(p1, p2 image.Point) image.Point {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Router) ActionAt(p f32.Point) (system.Action, bool) {
|
||||||
|
return q.pointer.queue.ActionAt(p)
|
||||||
|
}
|
||||||
|
|
||||||
func (q *Router) ClickFocus() {
|
func (q *Router) ClickFocus() {
|
||||||
focus := q.key.queue.focus
|
focus := q.key.queue.focus
|
||||||
if focus == nil {
|
if focus == nil {
|
||||||
@@ -444,6 +449,9 @@ func (q *Router) collect() {
|
|||||||
Data: encOp.Refs[2].(io.ReadCloser),
|
Data: encOp.Refs[2].(io.ReadCloser),
|
||||||
}
|
}
|
||||||
pc.offerOp(op, &q.handlers)
|
pc.offerOp(op, &q.handlers)
|
||||||
|
case ops.TypeActionInput:
|
||||||
|
act := system.Action(encOp.Data[1])
|
||||||
|
pc.actionInputOp(act)
|
||||||
|
|
||||||
// Key ops.
|
// Key ops.
|
||||||
case ops.TypeKeyFocus:
|
case ops.TypeKeyFocus:
|
||||||
|
|||||||
@@ -2,8 +2,17 @@ package system
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gioui.org/internal/ops"
|
||||||
|
"gioui.org/op"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ActionAreaOp makes the current clip area available for
|
||||||
|
// system gestures.
|
||||||
|
//
|
||||||
|
// Note: only ActionMove is supported.
|
||||||
|
type ActionInputOp Action
|
||||||
|
|
||||||
// Action is a set of window decoration actions.
|
// Action is a set of window decoration actions.
|
||||||
type Action uint
|
type Action uint
|
||||||
|
|
||||||
@@ -31,6 +40,12 @@ const (
|
|||||||
ActionMove
|
ActionMove
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (op ActionInputOp) Add(o *op.Ops) {
|
||||||
|
data := ops.Write(&o.Internal, ops.TypeActionInputLen)
|
||||||
|
data[0] = byte(ops.TypeActionInput)
|
||||||
|
data[1] = byte(op)
|
||||||
|
}
|
||||||
|
|
||||||
func (a Action) String() string {
|
func (a Action) String() string {
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
for b := Action(1); a != 0; b <<= 1 {
|
for b := Action(1); a != 0; b <<= 1 {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
// Decorations handles the states of window decorations.
|
// Decorations handles the states of window decorations.
|
||||||
type Decorations struct {
|
type Decorations struct {
|
||||||
move gesture.Drag
|
|
||||||
clicks []Clickable
|
clicks []Clickable
|
||||||
resize [8]struct {
|
resize [8]struct {
|
||||||
gesture.Hover
|
gesture.Hover
|
||||||
@@ -25,13 +24,8 @@ type Decorations struct {
|
|||||||
// LayoutMove lays out the widget that makes a window movable.
|
// LayoutMove lays out the widget that makes a window movable.
|
||||||
func (d *Decorations) LayoutMove(gtx layout.Context, w layout.Widget) layout.Dimensions {
|
func (d *Decorations) LayoutMove(gtx layout.Context, w layout.Widget) layout.Dimensions {
|
||||||
dims := w(gtx)
|
dims := w(gtx)
|
||||||
d.move.Events(gtx.Metric, gtx, gesture.Both)
|
defer clip.Rect{Max: dims.Size}.Push(gtx.Ops).Pop()
|
||||||
st := clip.Rect{Max: dims.Size}.Push(gtx.Ops)
|
system.ActionInputOp(system.ActionMove).Add(gtx.Ops)
|
||||||
d.move.Add(gtx.Ops)
|
|
||||||
if d.move.Pressed() {
|
|
||||||
d.actions |= system.ActionMove
|
|
||||||
}
|
|
||||||
st.Pop()
|
|
||||||
return dims
|
return dims
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user