Add support for SDL2 MultiGestureEvents
This commit is contained in:
parent
a9231fc038
commit
09a6c1db52
18
README.md
18
README.md
|
@ -135,6 +135,24 @@ for pt := range render.IterLine(A, B) {
|
||||||
* IterEllipse(A Point, B Point): draw an elipse fitting inside the
|
* IterEllipse(A Point, B Point): draw an elipse fitting inside the
|
||||||
rectangle bounded by points A and B.
|
rectangle bounded by points A and B.
|
||||||
|
|
||||||
|
## Multitouch Gesture Notes
|
||||||
|
|
||||||
|
Support for SDL2's MultiGestureEvent is added on October 6 2021.
|
||||||
|
The event.State will have the property `Touching=true` while the engine
|
||||||
|
believes multitouch gestures are afoot. This begins when the user touches
|
||||||
|
the screen with two fingers, and _then_ motion is detected.
|
||||||
|
|
||||||
|
SDL2 spams us with gesture events for each tiny change detected, and then
|
||||||
|
just stops. The SDL driver in this repo doesn't set ev.State.Touching=false.
|
||||||
|
One heuristic you may use in your program to detect when multitouch has
|
||||||
|
ended is this:
|
||||||
|
|
||||||
|
SDL2 always emulates the mouse Button1 click for one of the fingers.
|
||||||
|
Record the position at the first Touching=true event, and monitor for
|
||||||
|
delta changes in position as the "mouse cursor" moves. When delta
|
||||||
|
movements become stale and don't update, you can set State.Touching=false
|
||||||
|
in your program.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT.
|
MIT.
|
||||||
|
|
24
color.go
24
color.go
|
@ -19,6 +19,30 @@ var (
|
||||||
reHexColor8 = regexp.MustCompile(`^([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})$`)
|
reHexColor8 = regexp.MustCompile(`^([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Common color names.
|
||||||
|
var (
|
||||||
|
Invisible = Color{}
|
||||||
|
White = RGBA(255, 255, 255, 255)
|
||||||
|
Grey = RGBA(153, 153, 153, 255)
|
||||||
|
DarkGrey = RGBA(64, 64, 64, 255)
|
||||||
|
Black = RGBA(0, 0, 0, 255)
|
||||||
|
SkyBlue = RGBA(0, 153, 255, 255)
|
||||||
|
Blue = RGBA(0, 0, 255, 255)
|
||||||
|
DarkBlue = RGBA(0, 0, 153, 255)
|
||||||
|
Red = RGBA(255, 0, 0, 255)
|
||||||
|
DarkRed = RGBA(153, 0, 0, 255)
|
||||||
|
Green = RGBA(0, 255, 0, 255)
|
||||||
|
DarkGreen = RGBA(0, 153, 0, 255)
|
||||||
|
Cyan = RGBA(0, 255, 255, 255)
|
||||||
|
DarkCyan = RGBA(0, 153, 153, 255)
|
||||||
|
Yellow = RGBA(255, 255, 0, 255)
|
||||||
|
Orange = RGBA(255, 153, 0, 255)
|
||||||
|
DarkYellow = RGBA(153, 153, 0, 255)
|
||||||
|
Magenta = RGBA(255, 0, 255, 255)
|
||||||
|
Purple = RGBA(153, 0, 153, 255)
|
||||||
|
Pink = RGBA(255, 153, 255, 255)
|
||||||
|
)
|
||||||
|
|
||||||
// Color holds an RGBA color value.
|
// Color holds an RGBA color value.
|
||||||
type Color struct {
|
type Color struct {
|
||||||
Red uint8
|
Red uint8
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package event
|
package event
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// State holds the current state of key/mouse events.
|
// State holds the current state of key/mouse events.
|
||||||
type State struct {
|
type State struct {
|
||||||
|
@ -30,6 +32,14 @@ type State struct {
|
||||||
|
|
||||||
// Window resized
|
// Window resized
|
||||||
WindowResized bool
|
WindowResized bool
|
||||||
|
|
||||||
|
// Touch state
|
||||||
|
Touching bool
|
||||||
|
TouchNumFingers int
|
||||||
|
TouchCenterX int
|
||||||
|
TouchCenterY int
|
||||||
|
GestureRotated float64
|
||||||
|
GesturePinched float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewState creates a new event.State.
|
// NewState creates a new event.State.
|
||||||
|
|
146
interface.go
146
interface.go
|
@ -1,7 +1,6 @@
|
||||||
package render
|
package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"image"
|
"image"
|
||||||
|
|
||||||
"git.kirsle.net/go/render/event"
|
"git.kirsle.net/go/render/event"
|
||||||
|
@ -51,148 +50,3 @@ type Texturer interface {
|
||||||
Size() Rect
|
Size() Rect
|
||||||
Image() image.Image
|
Image() image.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rect has a coordinate and a width and height.
|
|
||||||
type Rect struct {
|
|
||||||
X int
|
|
||||||
Y int
|
|
||||||
W int
|
|
||||||
H int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRect creates a rectangle of size `width` and `height`. The X,Y values
|
|
||||||
// are initialized to zero.
|
|
||||||
func NewRect(width, height int) Rect {
|
|
||||||
return Rect{
|
|
||||||
W: width,
|
|
||||||
H: height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Rect) String() string {
|
|
||||||
return fmt.Sprintf("Rect<%d,%d,%d,%d>",
|
|
||||||
r.X, r.Y, r.W, r.H,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point returns the rectangle's X,Y values as a Point.
|
|
||||||
func (r Rect) Point() Point {
|
|
||||||
return Point{
|
|
||||||
X: r.X,
|
|
||||||
Y: r.Y,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bigger returns if the given rect is larger than the current one.
|
|
||||||
func (r Rect) Bigger(other Rect) bool {
|
|
||||||
// TODO: don't know why this is !
|
|
||||||
return !(other.X < r.X || // Lefter
|
|
||||||
other.Y < r.Y || // Higher
|
|
||||||
other.W > r.W || // Wider
|
|
||||||
other.H > r.H) // Taller
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intersects with the other rectangle in any way.
|
|
||||||
func (r Rect) Intersects(other Rect) bool {
|
|
||||||
// Do a bidirectional compare.
|
|
||||||
compare := func(a, b Rect) bool {
|
|
||||||
var corners = []Point{
|
|
||||||
NewPoint(b.X, b.Y),
|
|
||||||
NewPoint(b.X, b.Y+b.H),
|
|
||||||
NewPoint(b.X+b.W, b.Y),
|
|
||||||
NewPoint(b.X+b.W, b.Y+b.H),
|
|
||||||
}
|
|
||||||
for _, pt := range corners {
|
|
||||||
if pt.Inside(a) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return compare(r, other) || compare(other, r) || false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsZero returns if the Rect is uninitialized.
|
|
||||||
func (r Rect) IsZero() bool {
|
|
||||||
return r.X == 0 && r.Y == 0 && r.W == 0 && r.H == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add another rect.
|
|
||||||
func (r Rect) Add(other Rect) Rect {
|
|
||||||
return Rect{
|
|
||||||
X: r.X + other.X,
|
|
||||||
Y: r.Y + other.Y,
|
|
||||||
W: r.W + other.W,
|
|
||||||
H: r.H + other.H,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a point to move the rect.
|
|
||||||
func (r Rect) AddPoint(other Point) Rect {
|
|
||||||
return Rect{
|
|
||||||
X: r.X + other.X,
|
|
||||||
Y: r.Y + other.Y,
|
|
||||||
W: r.W,
|
|
||||||
H: r.H,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubtractPoint is the inverse of AddPoint. Use this only if you need to invert
|
|
||||||
// the Point being added.
|
|
||||||
//
|
|
||||||
// This does r.X - other.X, r.Y - other.Y and keeps the width/height the same.
|
|
||||||
func (r Rect) SubtractPoint(other Point) Rect {
|
|
||||||
return Rect{
|
|
||||||
X: r.X - other.X,
|
|
||||||
Y: r.Y - other.Y,
|
|
||||||
W: r.W,
|
|
||||||
H: r.H,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text holds information for drawing text.
|
|
||||||
type Text struct {
|
|
||||||
Text string
|
|
||||||
Size int
|
|
||||||
Color Color
|
|
||||||
Padding int
|
|
||||||
PadX int
|
|
||||||
PadY int
|
|
||||||
Stroke Color // Stroke color (if not zero)
|
|
||||||
Shadow Color // Drop shadow color (if not zero)
|
|
||||||
FontFilename string // Path to *.ttf file on disk
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t Text) String() string {
|
|
||||||
return fmt.Sprintf(`Text<"%s" %dpx %s>`, t.Text, t.Size, t.Color)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsZero returns if the Text is the zero value.
|
|
||||||
func (t Text) IsZero() bool {
|
|
||||||
return t.Text == "" && t.Size == 0 && t.Color == Invisible && t.Padding == 0 && t.PadX == 0 && t.PadY == 0 && t.Stroke == Invisible && t.Shadow == Invisible
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common color names.
|
|
||||||
var (
|
|
||||||
Invisible = Color{}
|
|
||||||
White = RGBA(255, 255, 255, 255)
|
|
||||||
Grey = RGBA(153, 153, 153, 255)
|
|
||||||
DarkGrey = RGBA(64, 64, 64, 255)
|
|
||||||
Black = RGBA(0, 0, 0, 255)
|
|
||||||
SkyBlue = RGBA(0, 153, 255, 255)
|
|
||||||
Blue = RGBA(0, 0, 255, 255)
|
|
||||||
DarkBlue = RGBA(0, 0, 153, 255)
|
|
||||||
Red = RGBA(255, 0, 0, 255)
|
|
||||||
DarkRed = RGBA(153, 0, 0, 255)
|
|
||||||
Green = RGBA(0, 255, 0, 255)
|
|
||||||
DarkGreen = RGBA(0, 153, 0, 255)
|
|
||||||
Cyan = RGBA(0, 255, 255, 255)
|
|
||||||
DarkCyan = RGBA(0, 153, 153, 255)
|
|
||||||
Yellow = RGBA(255, 255, 0, 255)
|
|
||||||
Orange = RGBA(255, 153, 0, 255)
|
|
||||||
DarkYellow = RGBA(153, 153, 0, 255)
|
|
||||||
Magenta = RGBA(255, 0, 255, 255)
|
|
||||||
Purple = RGBA(153, 0, 153, 255)
|
|
||||||
Pink = RGBA(255, 153, 255, 255)
|
|
||||||
)
|
|
||||||
|
|
102
rect.go
Normal file
102
rect.go
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package render
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Rect has a coordinate and a width and height.
|
||||||
|
type Rect struct {
|
||||||
|
X int
|
||||||
|
Y int
|
||||||
|
W int
|
||||||
|
H int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRect creates a rectangle of size `width` and `height`. The X,Y values
|
||||||
|
// are initialized to zero.
|
||||||
|
func NewRect(width, height int) Rect {
|
||||||
|
return Rect{
|
||||||
|
W: width,
|
||||||
|
H: height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Rect) String() string {
|
||||||
|
return fmt.Sprintf("Rect<%d,%d,%d,%d>",
|
||||||
|
r.X, r.Y, r.W, r.H,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point returns the rectangle's X,Y values as a Point.
|
||||||
|
func (r Rect) Point() Point {
|
||||||
|
return Point{
|
||||||
|
X: r.X,
|
||||||
|
Y: r.Y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bigger returns if the given rect is larger than the current one.
|
||||||
|
func (r Rect) Bigger(other Rect) bool {
|
||||||
|
// TODO: don't know why this is !
|
||||||
|
return !(other.X < r.X || // Lefter
|
||||||
|
other.Y < r.Y || // Higher
|
||||||
|
other.W > r.W || // Wider
|
||||||
|
other.H > r.H) // Taller
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersects with the other rectangle in any way.
|
||||||
|
func (r Rect) Intersects(other Rect) bool {
|
||||||
|
// Do a bidirectional compare.
|
||||||
|
compare := func(a, b Rect) bool {
|
||||||
|
var corners = []Point{
|
||||||
|
NewPoint(b.X, b.Y),
|
||||||
|
NewPoint(b.X, b.Y+b.H),
|
||||||
|
NewPoint(b.X+b.W, b.Y),
|
||||||
|
NewPoint(b.X+b.W, b.Y+b.H),
|
||||||
|
}
|
||||||
|
for _, pt := range corners {
|
||||||
|
if pt.Inside(a) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return compare(r, other) || compare(other, r) || false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns if the Rect is uninitialized.
|
||||||
|
func (r Rect) IsZero() bool {
|
||||||
|
return r.X == 0 && r.Y == 0 && r.W == 0 && r.H == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add another rect.
|
||||||
|
func (r Rect) Add(other Rect) Rect {
|
||||||
|
return Rect{
|
||||||
|
X: r.X + other.X,
|
||||||
|
Y: r.Y + other.Y,
|
||||||
|
W: r.W + other.W,
|
||||||
|
H: r.H + other.H,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a point to move the rect.
|
||||||
|
func (r Rect) AddPoint(other Point) Rect {
|
||||||
|
return Rect{
|
||||||
|
X: r.X + other.X,
|
||||||
|
Y: r.Y + other.Y,
|
||||||
|
W: r.W,
|
||||||
|
H: r.H,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubtractPoint is the inverse of AddPoint. Use this only if you need to invert
|
||||||
|
// the Point being added.
|
||||||
|
//
|
||||||
|
// This does r.X - other.X, r.Y - other.Y and keeps the width/height the same.
|
||||||
|
func (r Rect) SubtractPoint(other Point) Rect {
|
||||||
|
return Rect{
|
||||||
|
X: r.X - other.X,
|
||||||
|
Y: r.Y - other.Y,
|
||||||
|
W: r.W,
|
||||||
|
H: r.H,
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import (
|
||||||
// Debug certain SDL events
|
// Debug certain SDL events
|
||||||
var (
|
var (
|
||||||
DebugWindowEvents = false
|
DebugWindowEvents = false
|
||||||
|
DebugTouchEvents = false
|
||||||
DebugMouseEvents = false
|
DebugMouseEvents = false
|
||||||
DebugClickEvents = false
|
DebugClickEvents = false
|
||||||
DebugKeyEvents = false
|
DebugKeyEvents = false
|
||||||
|
@ -22,6 +23,7 @@ func (r *Renderer) Poll() (*event.State, error) {
|
||||||
|
|
||||||
// Reset some events.
|
// Reset some events.
|
||||||
s.WindowResized = false
|
s.WindowResized = false
|
||||||
|
// s.Touching = false
|
||||||
|
|
||||||
// helper function to push keyboard key names on keyDown events only.
|
// helper function to push keyboard key names on keyDown events only.
|
||||||
pushKey := func(name string, state uint8) {
|
pushKey := func(name string, state uint8) {
|
||||||
|
@ -105,6 +107,18 @@ func (r *Renderer) Poll() (*event.State, error) {
|
||||||
t.Timestamp, r.ticks, t.Type, t.Which, t.X, t.Y,
|
t.Timestamp, r.ticks, t.Type, t.Which, t.X, t.Y,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
case *sdl.MultiGestureEvent:
|
||||||
|
if DebugTouchEvents {
|
||||||
|
fmt.Printf("[%d ms] tick:%d MultiGesture type:%d Num=%d TouchID=%+v Dt=%f Dd=%f XY=%f,%f\n",
|
||||||
|
t.Timestamp, r.ticks, t.Type, t.NumFingers, t.TouchID, t.DTheta, t.DDist, t.X, t.Y,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
s.Touching = true
|
||||||
|
s.TouchNumFingers = int(t.NumFingers)
|
||||||
|
s.TouchCenterX = int(t.X)
|
||||||
|
s.TouchCenterY = int(t.Y)
|
||||||
|
s.GesturePinched = float64(t.DDist)
|
||||||
|
s.GestureRotated = float64(t.DTheta)
|
||||||
case *sdl.KeyboardEvent:
|
case *sdl.KeyboardEvent:
|
||||||
if DebugKeyEvents {
|
if DebugKeyEvents {
|
||||||
fmt.Printf("[%d ms] tick:%d Keyboard type:%d sym:%c modifiers:%d state:%d repeat:%d\n",
|
fmt.Printf("[%d ms] tick:%d Keyboard type:%d sym:%c modifiers:%d state:%d repeat:%d\n",
|
||||||
|
|
25
text.go
Normal file
25
text.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package render
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Text holds information for drawing text.
|
||||||
|
type Text struct {
|
||||||
|
Text string
|
||||||
|
Size int
|
||||||
|
Color Color
|
||||||
|
Padding int
|
||||||
|
PadX int
|
||||||
|
PadY int
|
||||||
|
Stroke Color // Stroke color (if not zero)
|
||||||
|
Shadow Color // Drop shadow color (if not zero)
|
||||||
|
FontFilename string // Path to *.ttf file on disk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Text) String() string {
|
||||||
|
return fmt.Sprintf(`Text<"%s" %dpx %s>`, t.Text, t.Size, t.Color)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZero returns if the Text is the zero value.
|
||||||
|
func (t Text) IsZero() bool {
|
||||||
|
return t.Text == "" && t.Size == 0 && t.Color == Invisible && t.Padding == 0 && t.PadX == 0 && t.PadY == 0 && t.Stroke == Invisible && t.Shadow == Invisible
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user