diff --git a/assets/sprites/flood-cursor.png b/assets/sprites/flood-cursor.png new file mode 100644 index 0000000..74df929 Binary files /dev/null and b/assets/sprites/flood-cursor.png differ diff --git a/assets/sprites/pencil.png b/assets/sprites/pencil.png new file mode 100644 index 0000000..7811e10 Binary files /dev/null and b/assets/sprites/pencil.png differ diff --git a/cmd/doodle/main.go b/cmd/doodle/main.go index ef2efc5..eb2a0ff 100644 --- a/cmd/doodle/main.go +++ b/cmd/doodle/main.go @@ -199,6 +199,10 @@ func main() { game := doodle.New(c.Bool("debug"), engine) 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. if engine, ok := game.Engine.(*sdl.Renderer); ok { if icon, err := sprites.LoadImage(game.Engine, balance.WindowIcon); err == nil { diff --git a/pkg/balance/theme.go b/pkg/balance/theme.go index cffefb2..c0e0070 100644 --- a/pkg/balance/theme.go +++ b/pkg/balance/theme.go @@ -14,7 +14,11 @@ var ( GoldCoin = "assets/sprites/gold.png" SilverCoin = "assets/sprites/silver.png" LockIcon = "assets/sprites/padlock.png" - CursorIcon = "assets/sprites/pointer.png" + + // Cursors + CursorIcon = "assets/sprites/pointer.png" + PencilIcon = "assets/sprites/pencil.png" + FloodCursor = "assets/sprites/flood-cursor.png" // Title Screen Font TitleScreenFont = render.Text{ diff --git a/pkg/cursor/cursor.go b/pkg/cursor/cursor.go new file mode 100644 index 0000000..a7d9e7c --- /dev/null +++ b/pkg/cursor/cursor.go @@ -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 +) diff --git a/pkg/doodle.go b/pkg/doodle.go index 3b7ec3a..09dfe55 100644 --- a/pkg/doodle.go +++ b/pkg/doodle.go @@ -9,6 +9,7 @@ import ( "git.kirsle.net/apps/doodle/pkg/balance" "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/gamepad" "git.kirsle.net/apps/doodle/pkg/keybind" @@ -209,8 +210,8 @@ func (d *Doodle) Run() error { // Draw the debug overlay over all scenes. d.DrawDebugOverlay() - // Let the gamepad controller draw in case of MouseMode to show the cursor. - gamepad.Draw(d.Engine) + // Draw our custom mouse cursor to the screen. + cursor.Draw(d.Engine) // Render the pixels to the screen. err = d.Engine.Present() diff --git a/pkg/editor_scene.go b/pkg/editor_scene.go index 9849596..80daa11 100644 --- a/pkg/editor_scene.go +++ b/pkg/editor_scene.go @@ -8,6 +8,7 @@ import ( "time" "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/drawtool" "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. s.UI.Teardown() + // Reset the cursor to default. + cursor.Current = cursor.NewPointer(s.d.Engine) + return nil } diff --git a/pkg/editor_ui.go b/pkg/editor_ui.go index 140e330..a79f3d9 100644 --- a/pkg/editor_ui.go +++ b/pkg/editor_ui.go @@ -395,6 +395,7 @@ func (u *EditorUI) SetupWorkspace(d *Doodle) *ui.Frame { func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas { drawing := uix.NewCanvas(balance.ChunkSize, true) drawing.Name = "edit-canvas" + drawing.FancyCursors = true drawing.Palette = level.DefaultPalette() drawing.SetBackground(render.White) 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. func (u *EditorUI) ExpandCanvas(e render.Engine) { 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 { // Size is managed externally. } diff --git a/pkg/editor_ui_palette.go b/pkg/editor_ui_palette.go index 4c1c76d..d0fc456 100644 --- a/pkg/editor_ui_palette.go +++ b/pkg/editor_ui_palette.go @@ -4,8 +4,11 @@ import ( "fmt" "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/uix" "git.kirsle.net/apps/doodle/pkg/usercfg" + "git.kirsle.net/go/render" "git.kirsle.net/go/ui" ) @@ -94,12 +97,15 @@ func (u *EditorUI) setupPaletteFrame(window *ui.Window) *ui.Frame { frame.Pack(row, packConfig) } - colorbox := ui.NewFrame(swatch.Name) - colorbox.Configure(ui.Config{ - Width: width, - Height: width, - Background: swatch.Color, - }) + // Fancy colorbox: show the color AND the texture of each swatch. + var ( + colorbox = uix.NewCanvas(width, false) + chunker = level.NewChunker(width) + 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.Configure(ui.Config{ diff --git a/pkg/uix/canvas.go b/pkg/uix/canvas.go index ab3b4b4..0560711 100644 --- a/pkg/uix/canvas.go +++ b/pkg/uix/canvas.go @@ -7,6 +7,7 @@ import ( "git.kirsle.net/apps/doodle/pkg/balance" "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/drawtool" "git.kirsle.net/apps/doodle/pkg/filesystem" @@ -56,6 +57,10 @@ type Canvas struct { // NoLimitScroll suppresses the scroll limit for bounded levels. NoLimitScroll bool + // Show custom mouse cursors over this canvas (eg. editor tools) + FancyCursors bool + cursor *cursor.Cursor + // Underlying chunk data for the drawing. level *level.Level chunks *level.Chunker diff --git a/pkg/uix/canvas_cursor.go b/pkg/uix/canvas_cursor.go index 547b015..1dfdd5a 100644 --- a/pkg/uix/canvas_cursor.go +++ b/pkg/uix/canvas_cursor.go @@ -1,6 +1,8 @@ package uix 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/go/render" "git.kirsle.net/go/ui" @@ -27,24 +29,49 @@ func (w *Canvas) IsCursorOver() bool { // brush size, and draws a "preview rect" under the cursor of how big a click // will be at that size. 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() { return } // Are we editing with a thick brush? - if w.BrushSize > 0 { - var r = w.BrushSize - rect := render.Rect{ - X: shmem.Cursor.X - r, - Y: shmem.Cursor.Y - r, - W: r * 2, - H: r * 2, + 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 { + var r = w.BrushSize + rect := render.Rect{ + X: shmem.Cursor.X - r, + Y: shmem.Cursor.Y - r, + W: r * 2, + H: r * 2, + } + e.DrawRect(render.Black, rect) + rect.X++ + rect.Y++ + rect.W -= 2 + rect.H -= 2 + e.DrawRect(render.RGBA(153, 153, 153, 153), rect) } - e.DrawRect(render.Black, rect) - rect.X++ - rect.Y++ - rect.W -= 2 - rect.H -= 2 - e.DrawRect(render.RGBA(153, 153, 153, 153), rect) } + }