github.com/Seikaijyu/gio@v0.0.1/io/pointer/pointer.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package pointer 4 5 import ( 6 "encoding/binary" 7 "fmt" 8 "image" 9 "strings" 10 "time" 11 12 "github.com/Seikaijyu/gio/f32" 13 "github.com/Seikaijyu/gio/internal/ops" 14 "github.com/Seikaijyu/gio/io/event" 15 "github.com/Seikaijyu/gio/io/key" 16 "github.com/Seikaijyu/gio/op" 17 ) 18 19 // Event is a pointer event. 20 type Event struct { 21 Kind Kind 22 Source Source 23 // PointerID is the id for the pointer and can be used 24 // to track a particular pointer from Press to 25 // Release or Cancel. 26 PointerID ID 27 // Priority is the priority of the receiving handler 28 // for this event. 29 Priority Priority 30 // Time is when the event was received. The 31 // timestamp is relative to an undefined base. 32 Time time.Duration 33 // Buttons are the set of pressed mouse buttons for this event. 34 Buttons Buttons 35 // Position is the coordinates of the event in the local coordinate 36 // system of the receiving tag. The transformation from global window 37 // coordinates to local coordinates is performed by the inverse of 38 // the effective transformation of the tag. 39 Position f32.Point 40 // Scroll is the scroll amount, if any. 41 Scroll f32.Point 42 // Modifiers is the set of active modifiers when 43 // the mouse button was pressed. 44 Modifiers key.Modifiers 45 } 46 47 // PassOp sets the pass-through mode. InputOps added while the pass-through 48 // mode is set don't block events to siblings. 49 type PassOp struct { 50 } 51 52 // PassStack represents a PassOp on the pass stack. 53 type PassStack struct { 54 ops *ops.Ops 55 id ops.StackID 56 macroID uint32 57 } 58 59 // InputOp declares an input handler ready for pointer 60 // events. 61 type InputOp struct { 62 Tag event.Tag 63 // Grab, if set, request that the handler get 64 // Grabbed priority. 65 Grab bool 66 // Kinds is a bitwise-or of event types to receive. 67 Kinds Kind 68 // ScrollBounds describe the maximum scrollable distances in both 69 // axes. Specifically, any Event e delivered to Tag will satisfy 70 // 71 // ScrollBounds.Min.X <= e.Scroll.X <= ScrollBounds.Max.X (horizontal axis) 72 // ScrollBounds.Min.Y <= e.Scroll.Y <= ScrollBounds.Max.Y (vertical axis) 73 ScrollBounds image.Rectangle 74 } 75 76 type ID uint16 77 78 // Kind of an Event. 79 type Kind uint 80 81 // Priority of an Event. 82 type Priority uint8 83 84 // Source of an Event. 85 type Source uint8 86 87 // Buttons is a set of mouse buttons 88 type Buttons uint8 89 90 // Cursor denotes a pre-defined cursor shape. Its Add method adds an 91 // operation that sets the cursor shape for the current clip area. 92 type Cursor byte 93 94 // The cursors correspond to CSS pointer naming. 95 const ( 96 // CursorDefault is the default cursor. 97 CursorDefault Cursor = iota 98 // CursorNone hides the cursor. To show it again, use any other cursor. 99 CursorNone 100 // CursorText is for selecting and inserting text. 101 CursorText 102 // CursorVerticalText is for selecting and inserting vertical text. 103 CursorVerticalText 104 // CursorPointer is for a link. 105 // Usually displayed as a pointing hand. 106 CursorPointer 107 // CursorCrosshair is for a precise location. 108 CursorCrosshair 109 // CursorAllScroll is for indicating scrolling in all directions. 110 // Usually displayed as arrows to all four directions. 111 CursorAllScroll 112 // CursorColResize is for vertical resize. 113 // Usually displayed as a vertical bar with arrows pointing east and west. 114 CursorColResize 115 // CursorRowResize is for horizontal resize. 116 // Usually displayed as a horizontal bar with arrows pointing north and south. 117 CursorRowResize 118 // CursorGrab is for content that can be grabbed (dragged to be moved). 119 // Usually displayed as an open hand. 120 CursorGrab 121 // CursorGrabbing is for content that is being grabbed (dragged to be moved). 122 // Usually displayed as a closed hand. 123 CursorGrabbing 124 // CursorNotAllowed is shown when the request action cannot be carried out. 125 // Usually displayed as a circle with a line through. 126 CursorNotAllowed 127 // CursorWait is shown when the program is busy and user cannot interact. 128 // Usually displayed as a hourglass or the system equivalent. 129 CursorWait 130 // CursorProgress is shown when the program is busy, but the user can still interact. 131 // Usually displayed as a default cursor with a hourglass. 132 CursorProgress 133 // CursorNorthWestResize is for top-left corner resizing. 134 // Usually displayed as an arrow towards north-west. 135 CursorNorthWestResize 136 // CursorNorthEastResize is for top-right corner resizing. 137 // Usually displayed as an arrow towards north-east. 138 CursorNorthEastResize 139 // CursorSouthWestResize is for bottom-left corner resizing. 140 // Usually displayed as an arrow towards south-west. 141 CursorSouthWestResize 142 // CursorSouthEastResize is for bottom-right corner resizing. 143 // Usually displayed as an arrow towards south-east. 144 CursorSouthEastResize 145 // CursorNorthSouth is for top-bottom resizing. 146 // Usually displayed as a bi-directional arrow towards north-south. 147 CursorNorthSouthResize 148 // CursorEastWestResize is for left-right resizing. 149 // Usually displayed as a bi-directional arrow towards east-west. 150 CursorEastWestResize 151 // CursorWestResize is for left resizing. 152 // Usually displayed as an arrow towards west. 153 CursorWestResize 154 // CursorEastResize is for right resizing. 155 // Usually displayed as an arrow towards east. 156 CursorEastResize 157 // CursorNorthResize is for top resizing. 158 // Usually displayed as an arrow towards north. 159 CursorNorthResize 160 // CursorSouthResize is for bottom resizing. 161 // Usually displayed as an arrow towards south. 162 CursorSouthResize 163 // CursorNorthEastSouthWestResize is for top-right to bottom-left diagonal resizing. 164 // Usually displayed as a double ended arrow on the corresponding diagonal. 165 CursorNorthEastSouthWestResize 166 // CursorNorthWestSouthEastResize is for top-left to bottom-right diagonal resizing. 167 // Usually displayed as a double ended arrow on the corresponding diagonal. 168 CursorNorthWestSouthEastResize 169 ) 170 171 const ( 172 // A Cancel event is generated when the current gesture is 173 // interrupted by other handlers or the system. 174 Cancel Kind = (1 << iota) >> 1 175 // Press of a pointer. 176 Press 177 // Release of a pointer. 178 Release 179 // Move of a pointer. 180 Move 181 // Drag of a pointer. 182 Drag 183 // Pointer enters an area watching for pointer input 184 Enter 185 // Pointer leaves an area watching for pointer input 186 Leave 187 // Scroll of a pointer. 188 Scroll 189 ) 190 191 const ( 192 // Mouse generated event. 193 Mouse Source = iota 194 // Touch generated event. 195 Touch 196 ) 197 198 const ( 199 // Shared priority is for handlers that 200 // are part of a matching set larger than 1. 201 Shared Priority = iota 202 // Foremost priority is like Shared, but the 203 // handler is the foremost of the matching set. 204 Foremost 205 // Grabbed is used for matching sets of size 1. 206 Grabbed 207 ) 208 209 const ( 210 // ButtonPrimary is the primary button, usually the left button for a 211 // right-handed user. 212 ButtonPrimary Buttons = 1 << iota 213 // ButtonSecondary is the secondary button, usually the right button for a 214 // right-handed user. 215 ButtonSecondary 216 // ButtonTertiary is the tertiary button, usually the middle button. 217 ButtonTertiary 218 ) 219 220 // Push the current pass mode to the pass stack and set the pass mode. 221 func (p PassOp) Push(o *op.Ops) PassStack { 222 id, mid := ops.PushOp(&o.Internal, ops.PassStack) 223 data := ops.Write(&o.Internal, ops.TypePassLen) 224 data[0] = byte(ops.TypePass) 225 return PassStack{ops: &o.Internal, id: id, macroID: mid} 226 } 227 228 func (p PassStack) Pop() { 229 ops.PopOp(p.ops, ops.PassStack, p.id, p.macroID) 230 data := ops.Write(p.ops, ops.TypePopPassLen) 231 data[0] = byte(ops.TypePopPass) 232 } 233 234 func (op Cursor) Add(o *op.Ops) { 235 data := ops.Write(&o.Internal, ops.TypeCursorLen) 236 data[0] = byte(ops.TypeCursor) 237 data[1] = byte(op) 238 } 239 240 // Add panics if the scroll range does not contain zero. 241 func (op InputOp) Add(o *op.Ops) { 242 if op.Tag == nil { 243 panic("Tag must be non-nil") 244 } 245 if b := op.ScrollBounds; b.Min.X > 0 || b.Max.X < 0 || b.Min.Y > 0 || b.Max.Y < 0 { 246 panic(fmt.Errorf("invalid scroll range value %v", b)) 247 } 248 if op.Kinds>>16 > 0 { 249 panic(fmt.Errorf("value in Types overflows uint16")) 250 } 251 data := ops.Write1(&o.Internal, ops.TypePointerInputLen, op.Tag) 252 data[0] = byte(ops.TypePointerInput) 253 if op.Grab { 254 data[1] = 1 255 } 256 bo := binary.LittleEndian 257 bo.PutUint16(data[2:], uint16(op.Kinds)) 258 bo.PutUint32(data[4:], uint32(op.ScrollBounds.Min.X)) 259 bo.PutUint32(data[8:], uint32(op.ScrollBounds.Min.Y)) 260 bo.PutUint32(data[12:], uint32(op.ScrollBounds.Max.X)) 261 bo.PutUint32(data[16:], uint32(op.ScrollBounds.Max.Y)) 262 } 263 264 func (t Kind) String() string { 265 if t == Cancel { 266 return "Cancel" 267 } 268 var buf strings.Builder 269 for tt := Kind(1); tt > 0; tt <<= 1 { 270 if t&tt > 0 { 271 if buf.Len() > 0 { 272 buf.WriteByte('|') 273 } 274 buf.WriteString((t & tt).string()) 275 } 276 } 277 return buf.String() 278 } 279 280 func (t Kind) string() string { 281 switch t { 282 case Press: 283 return "Press" 284 case Release: 285 return "Release" 286 case Cancel: 287 return "Cancel" 288 case Move: 289 return "Move" 290 case Drag: 291 return "Drag" 292 case Enter: 293 return "Enter" 294 case Leave: 295 return "Leave" 296 case Scroll: 297 return "Scroll" 298 default: 299 panic("unknown Type") 300 } 301 } 302 303 func (p Priority) String() string { 304 switch p { 305 case Shared: 306 return "Shared" 307 case Foremost: 308 return "Foremost" 309 case Grabbed: 310 return "Grabbed" 311 default: 312 panic("unknown priority") 313 } 314 } 315 316 func (s Source) String() string { 317 switch s { 318 case Mouse: 319 return "Mouse" 320 case Touch: 321 return "Touch" 322 default: 323 panic("unknown source") 324 } 325 } 326 327 // Contain reports whether the set b contains 328 // all of the buttons. 329 func (b Buttons) Contain(buttons Buttons) bool { 330 return b&buttons == buttons 331 } 332 333 func (b Buttons) String() string { 334 var strs []string 335 if b.Contain(ButtonPrimary) { 336 strs = append(strs, "ButtonPrimary") 337 } 338 if b.Contain(ButtonSecondary) { 339 strs = append(strs, "ButtonSecondary") 340 } 341 if b.Contain(ButtonTertiary) { 342 strs = append(strs, "ButtonTertiary") 343 } 344 return strings.Join(strs, "|") 345 } 346 347 func (c Cursor) String() string { 348 switch c { 349 case CursorDefault: 350 return "Default" 351 case CursorNone: 352 return "None" 353 case CursorText: 354 return "Text" 355 case CursorVerticalText: 356 return "VerticalText" 357 case CursorPointer: 358 return "Pointer" 359 case CursorCrosshair: 360 return "Crosshair" 361 case CursorAllScroll: 362 return "AllScroll" 363 case CursorColResize: 364 return "ColResize" 365 case CursorRowResize: 366 return "RowResize" 367 case CursorGrab: 368 return "Grab" 369 case CursorGrabbing: 370 return "Grabbing" 371 case CursorNotAllowed: 372 return "NotAllowed" 373 case CursorWait: 374 return "Wait" 375 case CursorProgress: 376 return "Progress" 377 case CursorNorthWestResize: 378 return "NorthWestResize" 379 case CursorNorthEastResize: 380 return "NorthEastResize" 381 case CursorSouthWestResize: 382 return "SouthWestResize" 383 case CursorSouthEastResize: 384 return "SouthEastResize" 385 case CursorNorthSouthResize: 386 return "NorthSouthResize" 387 case CursorEastWestResize: 388 return "EastWestResize" 389 case CursorWestResize: 390 return "WestResize" 391 case CursorEastResize: 392 return "EastResize" 393 case CursorNorthResize: 394 return "NorthResize" 395 case CursorSouthResize: 396 return "SouthResize" 397 case CursorNorthEastSouthWestResize: 398 return "NorthEastSouthWestResize" 399 case CursorNorthWestSouthEastResize: 400 return "NorthWestSouthEastResize" 401 default: 402 panic("unknown Type") 403 } 404 } 405 406 func (Event) ImplementsEvent() {}