Fancy Mouse Cursors
The gamepad mouse cursor has become THE mouse cursor. It is always visible and your real cursor is hidden, and this way the game can swap out other cursors for certain scenarios: * The Pencil Tool in the editor will use a pencil cursor over the level canvas. * The Flood Tool has a custom Flood cursor so you don't forget it's selected! Other improvements: * The Palette buttons in the editor now render using their swatch's pattern instead of only using its color. * If you have an ultra HD monitor and open a Bounded level in the editor which is too small to fill your screen, the editor canvas limits its size to fit the level (preferable over showing parts of the level you can't actually play as it's out of bounds). * The "brush size" box is only drawn around the cursor when a relevant tool is selected (Pencil, Line, Rect, Ellipse, Eraser)
This commit is contained in:
parent
9b75f1b039
commit
4efa8d00fc
BIN
assets/sprites/flood-cursor.png
Normal file
BIN
assets/sprites/flood-cursor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
BIN
assets/sprites/pencil.png
Normal file
BIN
assets/sprites/pencil.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
|
@ -199,6 +199,10 @@ func main() {
|
||||||
game := doodle.New(c.Bool("debug"), engine)
|
game := doodle.New(c.Bool("debug"), engine)
|
||||||
game.SetupEngine()
|
game.SetupEngine()
|
||||||
|
|
||||||
|
// Hide the mouse cursor over the window, we draw our own
|
||||||
|
// sprite image for it.
|
||||||
|
engine.ShowCursor(false)
|
||||||
|
|
||||||
// Set the app window icon.
|
// Set the app window icon.
|
||||||
if engine, ok := game.Engine.(*sdl.Renderer); ok {
|
if engine, ok := game.Engine.(*sdl.Renderer); ok {
|
||||||
if icon, err := sprites.LoadImage(game.Engine, balance.WindowIcon); err == nil {
|
if icon, err := sprites.LoadImage(game.Engine, balance.WindowIcon); err == nil {
|
||||||
|
|
|
@ -14,7 +14,11 @@ var (
|
||||||
GoldCoin = "assets/sprites/gold.png"
|
GoldCoin = "assets/sprites/gold.png"
|
||||||
SilverCoin = "assets/sprites/silver.png"
|
SilverCoin = "assets/sprites/silver.png"
|
||||||
LockIcon = "assets/sprites/padlock.png"
|
LockIcon = "assets/sprites/padlock.png"
|
||||||
|
|
||||||
|
// Cursors
|
||||||
CursorIcon = "assets/sprites/pointer.png"
|
CursorIcon = "assets/sprites/pointer.png"
|
||||||
|
PencilIcon = "assets/sprites/pencil.png"
|
||||||
|
FloodCursor = "assets/sprites/flood-cursor.png"
|
||||||
|
|
||||||
// Title Screen Font
|
// Title Screen Font
|
||||||
TitleScreenFont = render.Text{
|
TitleScreenFont = render.Text{
|
||||||
|
|
86
pkg/cursor/cursor.go
Normal file
86
pkg/cursor/cursor.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// Package cursor handles custom mouse cursor sprite images.
|
||||||
|
package cursor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/log"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/shmem"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/sprites"
|
||||||
|
"git.kirsle.net/go/render"
|
||||||
|
"git.kirsle.net/go/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cursor struct {
|
||||||
|
Filename string
|
||||||
|
Sprite *ui.Image
|
||||||
|
Offset render.Point
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current selected cursor to draw on screen.
|
||||||
|
var Current *Cursor
|
||||||
|
|
||||||
|
// NoCursor hides the cursor entirely.
|
||||||
|
var NoCursor = &Cursor{}
|
||||||
|
|
||||||
|
// Draw the cursor on screen.
|
||||||
|
func Draw(e render.Engine) {
|
||||||
|
if Current == nil {
|
||||||
|
Current = NewPointer(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if Current.Sprite != nil {
|
||||||
|
Current.Sprite.Present(e, shmem.Cursor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPointer initializes the default pointer cursor.
|
||||||
|
func NewPointer(e render.Engine) *Cursor {
|
||||||
|
if pointer != nil {
|
||||||
|
return pointer
|
||||||
|
}
|
||||||
|
img, err := sprites.LoadImage(e, balance.CursorIcon)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("NewPointer: %s", err)
|
||||||
|
}
|
||||||
|
return &Cursor{
|
||||||
|
Filename: balance.CursorIcon,
|
||||||
|
Sprite: img,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPencil initializes the pencil cursor.
|
||||||
|
func NewPencil(e render.Engine) *Cursor {
|
||||||
|
if pencil != nil {
|
||||||
|
return pencil
|
||||||
|
}
|
||||||
|
img, err := sprites.LoadImage(e, balance.PencilIcon)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("NewPencil: %s", err)
|
||||||
|
}
|
||||||
|
return &Cursor{
|
||||||
|
Filename: balance.PencilIcon,
|
||||||
|
Sprite: img,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFlood initializes the Flood cursor.
|
||||||
|
func NewFlood(e render.Engine) *Cursor {
|
||||||
|
if pencil != nil {
|
||||||
|
return pencil
|
||||||
|
}
|
||||||
|
img, err := sprites.LoadImage(e, balance.FloodCursor)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("NewFlood: %s", err)
|
||||||
|
}
|
||||||
|
return &Cursor{
|
||||||
|
Filename: balance.FloodCursor,
|
||||||
|
Sprite: img,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cached singletons of the cursors.
|
||||||
|
var (
|
||||||
|
pointer *Cursor
|
||||||
|
pencil *Cursor
|
||||||
|
flood *Cursor
|
||||||
|
)
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
"git.kirsle.net/apps/doodle/pkg/branding"
|
"git.kirsle.net/apps/doodle/pkg/branding"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/cursor"
|
||||||
"git.kirsle.net/apps/doodle/pkg/enum"
|
"git.kirsle.net/apps/doodle/pkg/enum"
|
||||||
"git.kirsle.net/apps/doodle/pkg/gamepad"
|
"git.kirsle.net/apps/doodle/pkg/gamepad"
|
||||||
"git.kirsle.net/apps/doodle/pkg/keybind"
|
"git.kirsle.net/apps/doodle/pkg/keybind"
|
||||||
|
@ -209,8 +210,8 @@ func (d *Doodle) Run() error {
|
||||||
// Draw the debug overlay over all scenes.
|
// Draw the debug overlay over all scenes.
|
||||||
d.DrawDebugOverlay()
|
d.DrawDebugOverlay()
|
||||||
|
|
||||||
// Let the gamepad controller draw in case of MouseMode to show the cursor.
|
// Draw our custom mouse cursor to the screen.
|
||||||
gamepad.Draw(d.Engine)
|
cursor.Draw(d.Engine)
|
||||||
|
|
||||||
// Render the pixels to the screen.
|
// Render the pixels to the screen.
|
||||||
err = d.Engine.Present()
|
err = d.Engine.Present()
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/cursor"
|
||||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
"git.kirsle.net/apps/doodle/pkg/doodads"
|
||||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||||
"git.kirsle.net/apps/doodle/pkg/enum"
|
"git.kirsle.net/apps/doodle/pkg/enum"
|
||||||
|
@ -609,5 +610,8 @@ func (s *EditorScene) Destroy() error {
|
||||||
// their bitmaps cached and will regen the textures as needed.
|
// their bitmaps cached and will regen the textures as needed.
|
||||||
s.UI.Teardown()
|
s.UI.Teardown()
|
||||||
|
|
||||||
|
// Reset the cursor to default.
|
||||||
|
cursor.Current = cursor.NewPointer(s.d.Engine)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -395,6 +395,7 @@ func (u *EditorUI) SetupWorkspace(d *Doodle) *ui.Frame {
|
||||||
func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
||||||
drawing := uix.NewCanvas(balance.ChunkSize, true)
|
drawing := uix.NewCanvas(balance.ChunkSize, true)
|
||||||
drawing.Name = "edit-canvas"
|
drawing.Name = "edit-canvas"
|
||||||
|
drawing.FancyCursors = true
|
||||||
drawing.Palette = level.DefaultPalette()
|
drawing.Palette = level.DefaultPalette()
|
||||||
drawing.SetBackground(render.White)
|
drawing.SetBackground(render.White)
|
||||||
if len(drawing.Palette.Swatches) > 0 {
|
if len(drawing.Palette.Swatches) > 0 {
|
||||||
|
@ -517,7 +518,22 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
||||||
// _wanted_ to be smaller, as in Doodad Editing Mode.
|
// _wanted_ to be smaller, as in Doodad Editing Mode.
|
||||||
func (u *EditorUI) ExpandCanvas(e render.Engine) {
|
func (u *EditorUI) ExpandCanvas(e render.Engine) {
|
||||||
if u.Scene.DrawingType == enum.LevelDrawing {
|
if u.Scene.DrawingType == enum.LevelDrawing {
|
||||||
u.Canvas.Resize(u.Workspace.Size())
|
var (
|
||||||
|
workspaceSize = u.Workspace.Size()
|
||||||
|
maxSize = workspaceSize
|
||||||
|
)
|
||||||
|
|
||||||
|
// If the level is bounded make that the max canvas size.
|
||||||
|
if u.Scene.Level != nil && u.Scene.Level.PageType >= level.Bounded {
|
||||||
|
if u.Scene.Level.MaxWidth < int64(maxSize.W) {
|
||||||
|
maxSize.W = int(u.Scene.Level.MaxWidth)
|
||||||
|
}
|
||||||
|
if u.Scene.Level.MaxHeight < int64(maxSize.H) {
|
||||||
|
maxSize.H = int(u.Scene.Level.MaxHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u.Canvas.Resize(maxSize)
|
||||||
} else {
|
} else {
|
||||||
// Size is managed externally.
|
// Size is managed externally.
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/level"
|
||||||
"git.kirsle.net/apps/doodle/pkg/log"
|
"git.kirsle.net/apps/doodle/pkg/log"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/uix"
|
||||||
"git.kirsle.net/apps/doodle/pkg/usercfg"
|
"git.kirsle.net/apps/doodle/pkg/usercfg"
|
||||||
|
"git.kirsle.net/go/render"
|
||||||
"git.kirsle.net/go/ui"
|
"git.kirsle.net/go/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -94,12 +97,15 @@ func (u *EditorUI) setupPaletteFrame(window *ui.Window) *ui.Frame {
|
||||||
frame.Pack(row, packConfig)
|
frame.Pack(row, packConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
colorbox := ui.NewFrame(swatch.Name)
|
// Fancy colorbox: show the color AND the texture of each swatch.
|
||||||
colorbox.Configure(ui.Config{
|
var (
|
||||||
Width: width,
|
colorbox = uix.NewCanvas(width, false)
|
||||||
Height: width,
|
chunker = level.NewChunker(width)
|
||||||
Background: swatch.Color,
|
size = render.NewRect(width, width)
|
||||||
})
|
)
|
||||||
|
chunker.SetRect(size, swatch)
|
||||||
|
colorbox.Resize(size)
|
||||||
|
colorbox.Load(u.Canvas.Palette, chunker)
|
||||||
|
|
||||||
btn := ui.NewRadioButton("palette", &u.selectedSwatch, swatch.Name, colorbox)
|
btn := ui.NewRadioButton("palette", &u.selectedSwatch, swatch.Name, colorbox)
|
||||||
btn.Configure(ui.Config{
|
btn.Configure(ui.Config{
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
"git.kirsle.net/apps/doodle/pkg/collision"
|
"git.kirsle.net/apps/doodle/pkg/collision"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/cursor"
|
||||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
"git.kirsle.net/apps/doodle/pkg/doodads"
|
||||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||||
"git.kirsle.net/apps/doodle/pkg/filesystem"
|
"git.kirsle.net/apps/doodle/pkg/filesystem"
|
||||||
|
@ -56,6 +57,10 @@ type Canvas struct {
|
||||||
// NoLimitScroll suppresses the scroll limit for bounded levels.
|
// NoLimitScroll suppresses the scroll limit for bounded levels.
|
||||||
NoLimitScroll bool
|
NoLimitScroll bool
|
||||||
|
|
||||||
|
// Show custom mouse cursors over this canvas (eg. editor tools)
|
||||||
|
FancyCursors bool
|
||||||
|
cursor *cursor.Cursor
|
||||||
|
|
||||||
// Underlying chunk data for the drawing.
|
// Underlying chunk data for the drawing.
|
||||||
level *level.Level
|
level *level.Level
|
||||||
chunks *level.Chunker
|
chunks *level.Chunker
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package uix
|
package uix
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/cursor"
|
||||||
|
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||||
"git.kirsle.net/apps/doodle/pkg/shmem"
|
"git.kirsle.net/apps/doodle/pkg/shmem"
|
||||||
"git.kirsle.net/go/render"
|
"git.kirsle.net/go/render"
|
||||||
"git.kirsle.net/go/ui"
|
"git.kirsle.net/go/ui"
|
||||||
|
@ -27,11 +29,34 @@ func (w *Canvas) IsCursorOver() bool {
|
||||||
// brush size, and draws a "preview rect" under the cursor of how big a click
|
// brush size, and draws a "preview rect" under the cursor of how big a click
|
||||||
// will be at that size.
|
// will be at that size.
|
||||||
func (w *Canvas) presentCursor(e render.Engine) {
|
func (w *Canvas) presentCursor(e render.Engine) {
|
||||||
|
// Are we to show a custom mouse cursor?
|
||||||
|
if w.FancyCursors {
|
||||||
|
switch w.Tool {
|
||||||
|
case drawtool.PencilTool:
|
||||||
|
w.cursor = cursor.NewPencil(e)
|
||||||
|
case drawtool.FloodTool:
|
||||||
|
w.cursor = cursor.NewFlood(e)
|
||||||
|
default:
|
||||||
|
w.cursor = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.IsCursorOver() && w.cursor != nil {
|
||||||
|
cursor.Current = w.cursor
|
||||||
|
} else {
|
||||||
|
cursor.Current = cursor.NewPointer(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !w.IsCursorOver() {
|
if !w.IsCursorOver() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we editing with a thick brush?
|
// Are we editing with a thick brush?
|
||||||
|
if w.Tool == drawtool.LineTool || w.Tool == drawtool.RectTool ||
|
||||||
|
w.Tool == drawtool.PencilTool || w.Tool == drawtool.EllipseTool ||
|
||||||
|
w.Tool == drawtool.EraserTool {
|
||||||
|
|
||||||
|
// Draw a box where the brush size is.
|
||||||
if w.BrushSize > 0 {
|
if w.BrushSize > 0 {
|
||||||
var r = w.BrushSize
|
var r = w.BrushSize
|
||||||
rect := render.Rect{
|
rect := render.Rect{
|
||||||
|
@ -47,4 +72,6 @@ func (w *Canvas) presentCursor(e render.Engine) {
|
||||||
rect.H -= 2
|
rect.H -= 2
|
||||||
e.DrawRect(render.RGBA(153, 153, 153, 153), rect)
|
e.DrawRect(render.RGBA(153, 153, 153, 153), rect)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user