diff --git a/pkg/editor_ui_menubar.go b/pkg/editor_ui_menubar.go index e84ddb2..4ad8a86 100644 --- a/pkg/editor_ui_menubar.go +++ b/pkg/editor_ui_menubar.go @@ -137,6 +137,18 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar { levelMenu.AddItem("Open screenshot folder", func() { native.OpenLocalURL(userdir.ScreenshotDirectory) }) + + levelMenu.AddSeparator() + levelMenu.AddItem("New viewport", func() { + pip := windows.MakePiPWindow(340, 480, windows.PiP{ + Supervisor: u.Supervisor, + Engine: u.d.Engine, + Level: u.Scene.Level, + Event: u.d.event, + }) + + pip.Show() + }) } //////// diff --git a/pkg/windows/pip_canvas.go b/pkg/windows/pip_canvas.go new file mode 100644 index 0000000..ca9e917 --- /dev/null +++ b/pkg/windows/pip_canvas.go @@ -0,0 +1,208 @@ +package windows + +import ( + "git.kirsle.net/apps/doodle/pkg/balance" + "git.kirsle.net/apps/doodle/pkg/drawtool" + "git.kirsle.net/apps/doodle/pkg/level" + "git.kirsle.net/apps/doodle/pkg/shmem" + "git.kirsle.net/apps/doodle/pkg/uix" + "git.kirsle.net/go/render" + "git.kirsle.net/go/render/event" + "git.kirsle.net/go/ui" +) + +// PiP window. +type PiP struct { + // Settings passed in by doodle + Supervisor *ui.Supervisor + Engine render.Engine + Level *level.Level + Event *event.State + + OnCancel func() +} + +// MakePiPWindow initializes a license window for any scene. +// The window width/height are the actual SDL2 window dimensions. +func MakePiPWindow(windowWidth, windowHeight int, cfg PiP) *ui.Window { + win := NewPiPWindow(cfg) + win.Compute(cfg.Engine) + win.Supervise(cfg.Supervisor) + + // Center the window. + size := win.Size() + win.MoveTo(render.Point{ + X: (windowWidth / 2) - (size.W / 2), + Y: (windowHeight / 2) - (size.H / 2), + }) + + return win +} + +// NewPiPWindow initializes the window. +func NewPiPWindow(cfg PiP) *ui.Window { + var ( + windowWidth = 340 + windowHeight = 320 + ) + + window := ui.NewWindow("Viewport (WORK IN PROGRESS!)") + window.SetButtons(ui.CloseButton) + window.Configure(ui.Config{ + Width: windowWidth, + Height: windowHeight, + Background: render.RGBA(255, 200, 255, 255), + }) + + canvas := uix.NewCanvas(128, true) + canvas.Name = "Viewport" + canvas.LoadLevel(cfg.Level) + canvas.InstallActors(cfg.Level.Actors) + canvas.Scrollable = true + canvas.Editable = true + canvas.Resize(render.NewRect(windowWidth, windowHeight)) + + // NOTE: my UI toolkit calls this every tick, if this is "fixed" + // in the future make one that does. + window.Handle(ui.MouseMove, func(ed ui.EventData) error { + canvas.Loop(cfg.Event) + return nil + }) + + window.Pack(canvas, ui.Pack{ + Side: ui.N, + FillX: true, + }) + + ///////////// + // Buttons at bottom of window + + bottomFrame := ui.NewFrame("Button Frame") + window.Pack(bottomFrame, ui.Pack{ + Side: ui.N, + FillX: true, + }) + + 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) + }}, + {"???", "Load a different drawing", nil, func() { + shmem.Prompt("Filename to open: ", func(answer string) { + if answer == "" { + return + } + + if lvl, err := level.LoadFile(answer); err == nil { + canvas.ClearActors() + canvas.LoadLevel(lvl) + canvas.InstallActors(lvl.Actors) + } else { + shmem.Flash(err.Error()) + } + }) + }}, + } + for _, button := range buttons { + button := button + + btn := ui.NewButton(button.label, ui.NewLabel(ui.Label{ + Text: button.label, + Font: balance.MenuFont, + })) + + 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.Compute(cfg.Engine) + cfg.Supervisor.Add(btn) + + ui.NewTooltip(btn, ui.Tooltip{ + Text: button.tooltip, + Edge: ui.Top, + }) + + frame.Pack(btn, ui.Pack{ + Side: ui.W, + PadX: 4, + 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, + }) + + return window +}