Viewport Windows, Quality of Life, Spit and Polish
* New keybind: 'v' to open a new Viewport in the Level Editor. * New keybind: Backspace to close the topmost UI window, and Shift+Backspace to close them all. * Zoom has graduated out of experimental feature status. Still a bit buggy but workable. * Viewport windows now copy the Tool and BrushSize of the toplevel editor, so drawing in and out of viewports works well. * Viewport window UI improved: buttons to grow or shrink the window size, refresh the actors, etc.
This commit is contained in:
parent
a24c94a161
commit
0b0af70a62
|
@ -141,7 +141,7 @@ var (
|
|||
// Small font
|
||||
SmallFont = render.Text{
|
||||
Size: 10,
|
||||
Padding: 4,
|
||||
Padding: 2,
|
||||
Color: render.Black,
|
||||
}
|
||||
|
||||
|
|
|
@ -11,4 +11,5 @@ const (
|
|||
|
||||
// Update check URL
|
||||
UpdateCheckJSON = "https://download.sketchymaze.com/version.json"
|
||||
GuidebookURL = "https://www.sketchymaze.com/guidebook/"
|
||||
)
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||
"git.kirsle.net/apps/doodle/pkg/enum"
|
||||
|
@ -18,6 +17,7 @@ import (
|
|||
"git.kirsle.net/apps/doodle/pkg/modal/loadscreen"
|
||||
"git.kirsle.net/apps/doodle/pkg/usercfg"
|
||||
"git.kirsle.net/apps/doodle/pkg/userdir"
|
||||
"git.kirsle.net/apps/doodle/pkg/windows"
|
||||
"git.kirsle.net/go/render"
|
||||
"git.kirsle.net/go/render/event"
|
||||
)
|
||||
|
@ -288,54 +288,111 @@ func (s *EditorScene) Loop(d *Doodle, ev *event.State) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Menu key bindings.
|
||||
if keybind.NewLevel(ev) {
|
||||
// Ctrl-N, New Level
|
||||
s.MenuNewLevel()
|
||||
} else if keybind.SaveAs(ev) {
|
||||
// Shift-Ctrl-S, Save As
|
||||
s.MenuSave(true)()
|
||||
} else if keybind.Save(ev) {
|
||||
// Ctrl-S, Save
|
||||
s.MenuSave(false)()
|
||||
} else if keybind.Open(ev) {
|
||||
// Ctrl-O, Open
|
||||
s.MenuOpen()
|
||||
}
|
||||
// Run all of the keybinds.
|
||||
binders := []struct {
|
||||
v bool
|
||||
f func()
|
||||
}{
|
||||
{
|
||||
keybind.NewLevel(ev), func() {
|
||||
// Ctrl-N, New Level
|
||||
s.MenuNewLevel()
|
||||
},
|
||||
},
|
||||
{
|
||||
keybind.SaveAs(ev), func() {
|
||||
// Shift-Ctrl-S, Save As
|
||||
s.MenuSave(true)()
|
||||
},
|
||||
},
|
||||
{
|
||||
keybind.Save(ev), func() {
|
||||
// Ctrl-S, Save
|
||||
s.MenuSave(false)()
|
||||
},
|
||||
},
|
||||
{
|
||||
keybind.Open(ev), func() {
|
||||
// Ctrl-O, Open
|
||||
s.MenuOpen()
|
||||
},
|
||||
},
|
||||
{
|
||||
keybind.Undo(ev), func() {
|
||||
// Ctrl-Z, Undo
|
||||
s.UI.Canvas.UndoStroke()
|
||||
ev.ResetKeyDown()
|
||||
},
|
||||
},
|
||||
{
|
||||
keybind.Redo(ev), func() {
|
||||
// Ctrl-Y, Undo
|
||||
s.UI.Canvas.RedoStroke()
|
||||
ev.ResetKeyDown()
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
// Undo/Redo key bindings.
|
||||
if keybind.Undo(ev) {
|
||||
s.UI.Canvas.UndoStroke()
|
||||
ev.ResetKeyDown()
|
||||
} else if keybind.Redo(ev) {
|
||||
s.UI.Canvas.RedoStroke()
|
||||
ev.ResetKeyDown()
|
||||
}
|
||||
keybind.ZoomIn(ev), func() {
|
||||
s.UI.Canvas.Zoom++
|
||||
ev.ResetKeyDown()
|
||||
},
|
||||
},
|
||||
{
|
||||
keybind.ZoomOut(ev), func() {
|
||||
s.UI.Canvas.Zoom--
|
||||
ev.ResetKeyDown()
|
||||
},
|
||||
},
|
||||
{
|
||||
keybind.ZoomReset(ev), func() {
|
||||
s.UI.Canvas.Zoom = 0
|
||||
ev.ResetKeyDown()
|
||||
},
|
||||
},
|
||||
{
|
||||
keybind.Origin(ev), func() {
|
||||
d.Flash("Scrolled back to level origin (0,0)")
|
||||
s.UI.Canvas.ScrollTo(render.Origin)
|
||||
ev.ResetKeyDown()
|
||||
},
|
||||
},
|
||||
{
|
||||
keybind.CloseAllWindows(ev), func() {
|
||||
s.UI.Supervisor.CloseAllWindows()
|
||||
},
|
||||
},
|
||||
{
|
||||
keybind.CloseTopmostWindow(ev), func() {
|
||||
s.UI.Supervisor.CloseActiveWindow()
|
||||
},
|
||||
},
|
||||
{
|
||||
keybind.NewViewport(ev), func() {
|
||||
if s.DrawingType != enum.LevelDrawing {
|
||||
return
|
||||
}
|
||||
|
||||
// Zoom in/out.
|
||||
if balance.Feature.Zoom {
|
||||
if keybind.ZoomIn(ev) {
|
||||
d.Flash("Zoom in")
|
||||
s.UI.Canvas.Zoom++
|
||||
ev.ResetKeyDown()
|
||||
} else if keybind.ZoomOut(ev) {
|
||||
d.Flash("Zoom out")
|
||||
s.UI.Canvas.Zoom--
|
||||
ev.ResetKeyDown()
|
||||
} else if keybind.ZoomReset(ev) {
|
||||
d.Flash("Reset zoom")
|
||||
s.UI.Canvas.Zoom = 0
|
||||
ev.ResetKeyDown()
|
||||
pip := windows.MakePiPWindow(d.width, d.height, windows.PiP{
|
||||
Supervisor: s.UI.Supervisor,
|
||||
Engine: s.d.Engine,
|
||||
Level: s.Level,
|
||||
Event: s.d.event,
|
||||
|
||||
Tool: &s.UI.Canvas.Tool,
|
||||
BrushSize: &s.UI.Canvas.BrushSize,
|
||||
})
|
||||
|
||||
pip.Show()
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, bind := range binders {
|
||||
if bind.v {
|
||||
bind.f()
|
||||
}
|
||||
}
|
||||
|
||||
// More keybinds
|
||||
if keybind.Origin(ev) {
|
||||
d.Flash("Scrolled back to level origin (0,0)")
|
||||
s.UI.Canvas.ScrollTo(render.Origin)
|
||||
ev.ResetKeyDown()
|
||||
}
|
||||
|
||||
// s.UI.Loop(ev)
|
||||
|
||||
// Switching to Play Mode?
|
||||
|
|
|
@ -6,6 +6,7 @@ package doodle
|
|||
|
||||
import (
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/branding"
|
||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||
"git.kirsle.net/apps/doodle/pkg/enum"
|
||||
"git.kirsle.net/apps/doodle/pkg/level/giant_screenshot"
|
||||
|
@ -139,12 +140,15 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
})
|
||||
|
||||
levelMenu.AddSeparator()
|
||||
levelMenu.AddItem("New viewport", func() {
|
||||
pip := windows.MakePiPWindow(340, 480, windows.PiP{
|
||||
levelMenu.AddItemAccel("New viewport", "v", func() {
|
||||
pip := windows.MakePiPWindow(d.width, d.height, windows.PiP{
|
||||
Supervisor: u.Supervisor,
|
||||
Engine: u.d.Engine,
|
||||
Level: u.Scene.Level,
|
||||
Event: u.d.event,
|
||||
|
||||
Tool: &u.Scene.UI.Canvas.Tool,
|
||||
BrushSize: &u.Scene.UI.Canvas.BrushSize,
|
||||
})
|
||||
|
||||
pip.Show()
|
||||
|
@ -172,21 +176,28 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
|
||||
////////
|
||||
// View menu
|
||||
if balance.Feature.Zoom {
|
||||
viewMenu := menu.AddMenu("View")
|
||||
viewMenu.AddItemAccel("Zoom in", "+", func() {
|
||||
u.Canvas.Zoom++
|
||||
})
|
||||
viewMenu.AddItemAccel("Zoom out", "-", func() {
|
||||
u.Canvas.Zoom--
|
||||
})
|
||||
viewMenu.AddItemAccel("Reset zoom", "1", func() {
|
||||
u.Canvas.Zoom = 0
|
||||
})
|
||||
viewMenu.AddItemAccel("Scroll drawing to origin", "0", func() {
|
||||
u.Canvas.ScrollTo(render.Origin)
|
||||
})
|
||||
}
|
||||
viewMenu := menu.AddMenu("View")
|
||||
viewMenu.AddItemAccel("Zoom in", "+", func() {
|
||||
u.Canvas.Zoom++
|
||||
})
|
||||
viewMenu.AddItemAccel("Zoom out", "-", func() {
|
||||
u.Canvas.Zoom--
|
||||
})
|
||||
viewMenu.AddItemAccel("Reset zoom", "1", func() {
|
||||
u.Canvas.Zoom = 0
|
||||
})
|
||||
viewMenu.AddItemAccel("Scroll drawing to origin", "0", func() {
|
||||
u.Canvas.ScrollTo(render.Origin)
|
||||
})
|
||||
|
||||
viewMenu.AddSeparator()
|
||||
|
||||
viewMenu.AddItemAccel("Close window", "←", func() {
|
||||
u.Supervisor.CloseActiveWindow()
|
||||
})
|
||||
viewMenu.AddItemAccel("Close all windows", "Shift-←", func() {
|
||||
u.Supervisor.CloseAllWindows()
|
||||
})
|
||||
|
||||
////////
|
||||
// Tools menu
|
||||
|
@ -270,6 +281,13 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
}
|
||||
u.aboutWindow.Show()
|
||||
})
|
||||
helpMenu.AddSeparator()
|
||||
helpMenu.AddItem("Go to Website", func() {
|
||||
native.OpenURL(branding.Website)
|
||||
})
|
||||
helpMenu.AddItem("Guidebook Online", func() {
|
||||
native.OpenURL(branding.GuidebookURL)
|
||||
})
|
||||
|
||||
menu.Supervise(u.Supervisor)
|
||||
menu.Compute(d.Engine)
|
||||
|
|
|
@ -109,6 +109,31 @@ func DebugCollision(ev *event.State) bool {
|
|||
return result
|
||||
}
|
||||
|
||||
// CloseTopmostWindow (Backspace)
|
||||
func CloseTopmostWindow(ev *event.State) bool {
|
||||
result := ev.KeyDown(`\b`)
|
||||
ev.SetKeyDown(`\b`, false)
|
||||
return result
|
||||
}
|
||||
|
||||
// CloseAllWindows (Shift+Backspace)
|
||||
func CloseAllWindows(ev *event.State) bool {
|
||||
result := ev.KeyDown(`\b`) && ev.Shift
|
||||
if result {
|
||||
ev.SetKeyDown(`\b`, false)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// NewViewport (V)
|
||||
func NewViewport(ev *event.State) bool {
|
||||
result := ev.KeyDown("v")
|
||||
if result {
|
||||
ev.SetKeyDown("v", false)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Undo (Ctrl-Z)
|
||||
func Undo(ev *event.State) bool {
|
||||
return ev.Ctrl && ev.KeyDown("z")
|
||||
|
|
|
@ -18,6 +18,12 @@ type PiP struct {
|
|||
Engine render.Engine
|
||||
Level *level.Level
|
||||
Event *event.State
|
||||
Tool *drawtool.Tool
|
||||
BrushSize *int
|
||||
|
||||
// Or sensible defaults:
|
||||
Width int
|
||||
Height int
|
||||
|
||||
OnCancel func()
|
||||
}
|
||||
|
@ -43,29 +49,53 @@ func MakePiPWindow(windowWidth, windowHeight int, cfg PiP) *ui.Window {
|
|||
func NewPiPWindow(cfg PiP) *ui.Window {
|
||||
var (
|
||||
windowWidth = 340
|
||||
windowHeight = 320
|
||||
windowHeight = 300
|
||||
)
|
||||
|
||||
window := ui.NewWindow("Viewport (WORK IN PROGRESS!)")
|
||||
if cfg.Width+cfg.Height > 0 {
|
||||
windowWidth = cfg.Width
|
||||
windowHeight = cfg.Height
|
||||
}
|
||||
|
||||
var (
|
||||
canvasWidth = windowWidth - 8
|
||||
canvasHeight = windowHeight - 4 - 48 // for the titlebar?
|
||||
)
|
||||
|
||||
window := ui.NewWindow("Viewport")
|
||||
window.SetButtons(ui.CloseButton)
|
||||
window.Configure(ui.Config{
|
||||
Width: windowWidth,
|
||||
Height: windowHeight,
|
||||
Background: render.RGBA(255, 200, 255, 255),
|
||||
Width: windowWidth,
|
||||
Height: windowHeight,
|
||||
})
|
||||
|
||||
canvas := uix.NewCanvas(128, true)
|
||||
canvas.Name = "Viewport"
|
||||
canvas.Name = "Viewport (WIP)"
|
||||
canvas.LoadLevel(cfg.Level)
|
||||
canvas.InstallActors(cfg.Level.Actors)
|
||||
canvas.Scrollable = true
|
||||
canvas.Editable = true
|
||||
canvas.Resize(render.NewRect(windowWidth, windowHeight))
|
||||
canvas.Resize(render.NewRect(canvasWidth, canvasHeight))
|
||||
|
||||
// NOTE: my UI toolkit calls this every tick, if this is "fixed"
|
||||
// in the future make one that does.
|
||||
var (
|
||||
curTool = *cfg.Tool
|
||||
curThicc = *cfg.BrushSize
|
||||
)
|
||||
canvas.Tool = curTool
|
||||
window.Handle(ui.MouseMove, func(ed ui.EventData) error {
|
||||
canvas.Loop(cfg.Event)
|
||||
|
||||
// Check if bound values have modified.
|
||||
if *cfg.Tool != curTool {
|
||||
curTool = *cfg.Tool
|
||||
canvas.Tool = curTool
|
||||
}
|
||||
if *cfg.BrushSize != curThicc {
|
||||
curThicc = *cfg.BrushSize
|
||||
canvas.BrushSize = curThicc
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -81,31 +111,46 @@ func NewPiPWindow(cfg PiP) *ui.Window {
|
|||
window.Pack(bottomFrame, ui.Pack{
|
||||
Side: ui.N,
|
||||
FillX: true,
|
||||
PadY: 4,
|
||||
})
|
||||
|
||||
frame := ui.NewFrame("Button frame")
|
||||
buttons := []struct {
|
||||
label string
|
||||
tooltip string
|
||||
down func()
|
||||
f func()
|
||||
}{
|
||||
{"^", "Scroll up", func() {
|
||||
canvas.ScrollBy(render.NewPoint(0, 64))
|
||||
}, nil},
|
||||
{"v", "Scroll down", func() {
|
||||
canvas.ScrollBy(render.NewPoint(0, -64))
|
||||
}, nil},
|
||||
{"<", "Scroll left", func() {
|
||||
canvas.ScrollBy(render.NewPoint(64, 0))
|
||||
}, nil},
|
||||
{">", "Scroll right", func() {
|
||||
canvas.ScrollBy(render.NewPoint(-64, 0))
|
||||
}, nil},
|
||||
{"0", "Reset to origin", nil, func() {
|
||||
canvas.ScrollTo(render.Origin)
|
||||
{"Smaller", "Shrink this viewport window by 20%", func() {
|
||||
// Make a smaller version of the same window, and close.
|
||||
cfg.Width = int(float64(windowWidth) * 0.8)
|
||||
cfg.Height = int(float64(windowHeight) * 0.8)
|
||||
pip := MakePiPWindow(cfg.Width, cfg.Height, cfg)
|
||||
pip.MoveTo(window.Point())
|
||||
window.Close()
|
||||
pip.Show()
|
||||
}},
|
||||
{"???", "Load a different drawing", nil, func() {
|
||||
{"Larger", "Grow this viewport window by 20%", func() {
|
||||
// Make a smaller version of the same window, and close.
|
||||
cfg.Width = int(float64(windowWidth) * 1.2)
|
||||
cfg.Height = int(float64(windowHeight) * 1.2)
|
||||
pip := MakePiPWindow(cfg.Width, cfg.Height, cfg)
|
||||
pip.MoveTo(window.Point())
|
||||
window.Close()
|
||||
pip.Show()
|
||||
}},
|
||||
{"Refresh", "Update the state of doodads placed in this level", func() {
|
||||
canvas.ClearActors()
|
||||
canvas.InstallActors(cfg.Level.Actors)
|
||||
}},
|
||||
{"Rename", "Give this viewport window a custom name", func() {
|
||||
shmem.Prompt("Give this viewport a name: ", func(answer string) {
|
||||
if answer == "" {
|
||||
return
|
||||
}
|
||||
window.Title = answer
|
||||
})
|
||||
}},
|
||||
{"???", "Load a different drawing (experimental!)", func() {
|
||||
shmem.Prompt("Filename to open: ", func(answer string) {
|
||||
if answer == "" {
|
||||
return
|
||||
|
@ -121,27 +166,25 @@ func NewPiPWindow(cfg PiP) *ui.Window {
|
|||
})
|
||||
}},
|
||||
}
|
||||
for _, button := range buttons {
|
||||
for i, button := range buttons {
|
||||
// Start axing buttons if window size is too small.
|
||||
if windowWidth < 150 && i > 1 {
|
||||
break
|
||||
} else if windowWidth < 250 && i > 2 {
|
||||
break
|
||||
}
|
||||
|
||||
button := button
|
||||
|
||||
btn := ui.NewButton(button.label, ui.NewLabel(ui.Label{
|
||||
Text: button.label,
|
||||
Font: balance.MenuFont,
|
||||
Font: balance.SmallFont,
|
||||
}))
|
||||
|
||||
if button.down != nil {
|
||||
btn.Handle(ui.MouseDown, func(ed ui.EventData) error {
|
||||
button.down()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if button.f != nil {
|
||||
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
button.f()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
button.f()
|
||||
return nil
|
||||
})
|
||||
|
||||
btn.Compute(cfg.Engine)
|
||||
cfg.Supervisor.Add(btn)
|
||||
|
@ -153,55 +196,16 @@ func NewPiPWindow(cfg PiP) *ui.Window {
|
|||
|
||||
frame.Pack(btn, ui.Pack{
|
||||
Side: ui.W,
|
||||
PadX: 4,
|
||||
PadX: 1,
|
||||
Expand: true,
|
||||
Fill: true,
|
||||
})
|
||||
}
|
||||
|
||||
// Tool selector.
|
||||
toolBtn := ui.NewSelectBox("Tool Select", ui.Label{
|
||||
Font: ui.MenuFont,
|
||||
})
|
||||
toolBtn.AlwaysChange = true
|
||||
frame.Pack(toolBtn, ui.Pack{
|
||||
Side: ui.W,
|
||||
Expand: true,
|
||||
})
|
||||
|
||||
toolBtn.AddItem("Pencil", drawtool.PencilTool, func() {})
|
||||
toolBtn.AddItem("Line", drawtool.LineTool, func() {})
|
||||
toolBtn.AddItem("Rectangle", drawtool.RectTool, func() {})
|
||||
toolBtn.AddItem("Ellipse", drawtool.EllipseTool, func() {})
|
||||
|
||||
// TODO: Actor and Link Tools don't work as the canvas needs
|
||||
// hooks for their events. The code in EditorUI#SetupCanvas should
|
||||
// be made reusable here.
|
||||
// toolBtn.AddItem("Link", drawtool.LinkTool, func() {})
|
||||
// toolBtn.AddItem("Actor", drawtool.ActorTool, func() {})
|
||||
|
||||
toolBtn.Handle(ui.Change, func(ed ui.EventData) error {
|
||||
selection, _ := toolBtn.GetValue()
|
||||
tool, _ := selection.Value.(drawtool.Tool)
|
||||
|
||||
// log.Error("Change: %d, b4: %s", value, canvas.Tool)
|
||||
canvas.Tool = tool
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
ui.NewTooltip(toolBtn, ui.Tooltip{
|
||||
Text: "Draw tool (viewport only)",
|
||||
Edge: ui.Top,
|
||||
})
|
||||
|
||||
toolBtn.Supervise(cfg.Supervisor)
|
||||
cfg.Supervisor.Add(toolBtn)
|
||||
|
||||
bottomFrame.Pack(frame, ui.Pack{
|
||||
Side: ui.N,
|
||||
PadX: 8,
|
||||
PadY: 12,
|
||||
PadY: 0,
|
||||
})
|
||||
|
||||
return window
|
||||
|
|
Loading…
Reference in New Issue
Block a user