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
|
||||
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
|
||||
|
||||
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})$`)
|
||||
)
|
||||
|
||||
// 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.
|
||||
type Color struct {
|
||||
Red uint8
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package event
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// State holds the current state of key/mouse events.
|
||||
type State struct {
|
||||
|
@ -30,6 +32,14 @@ type State struct {
|
|||
|
||||
// Window resized
|
||||
WindowResized bool
|
||||
|
||||
// Touch state
|
||||
Touching bool
|
||||
TouchNumFingers int
|
||||
TouchCenterX int
|
||||
TouchCenterY int
|
||||
GestureRotated float64
|
||||
GesturePinched float64
|
||||
}
|
||||
|
||||
// NewState creates a new event.State.
|
||||
|
|
146
interface.go
146
interface.go
|
@ -1,7 +1,6 @@
|
|||
package render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
|
||||
"git.kirsle.net/go/render/event"
|
||||
|
@ -51,148 +50,3 @@ type Texturer interface {
|
|||
Size() Rect
|
||||
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
|
||||
var (
|
||||
DebugWindowEvents = false
|
||||
DebugTouchEvents = false
|
||||
DebugMouseEvents = false
|
||||
DebugClickEvents = false
|
||||
DebugKeyEvents = false
|
||||
|
@ -22,6 +23,7 @@ func (r *Renderer) Poll() (*event.State, error) {
|
|||
|
||||
// Reset some events.
|
||||
s.WindowResized = false
|
||||
// s.Touching = false
|
||||
|
||||
// helper function to push keyboard key names on keyDown events only.
|
||||
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,
|
||||
)
|
||||
}
|
||||
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:
|
||||
if DebugKeyEvents {
|
||||
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