Move Editor Canvas Into UI + UI Improvements
* Increase the default window size from 800x600 to 1024x768. * Move the drawing canvas in EditorMode to inside the EditorUI where it can be better managed with the other widgets it shares the screen with. * Slightly fix Frame packing bug (with East orientation) that was causing right-aligned statusbar items to be partially cropped off-screen. Moved a couple statusbar labels in EditorMode to the right. * Add `Parent()` and `Adopt()` methods to widgets for when they're managed by containers like the Frame. * Add utility functions to UI toolkit for computing a widget's Absolute Position and Absolute Rect, by crawling all parent widgets and summing them up. * Add `lib/debugging` package with useful stack tracing utilities. * Add `make guitest` to launch the program into the GUI Test. The command line flag is: `doodle -guitest` * Console: add a `close` command which returns to the MainScene. * Initialize the font cache directory (~/.cache/doodle/fonts) but don't extract the fonts there yet.
This commit is contained in:
parent
cfe26cb964
commit
f18dcf9c2c
5
Makefile
5
Makefile
|
@ -23,6 +23,11 @@ build:
|
||||||
run:
|
run:
|
||||||
go run cmd/doodle/main.go -debug
|
go run cmd/doodle/main.go -debug
|
||||||
|
|
||||||
|
# `make guitest` to run it in guitest mode.
|
||||||
|
.PHONY: guitest
|
||||||
|
guitest:
|
||||||
|
go run cmd/doodle/main.go -debug -guitest
|
||||||
|
|
||||||
# `make test` to run unit tests.
|
# `make test` to run unit tests.
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
|
|
|
@ -2,6 +2,10 @@ package balance
|
||||||
|
|
||||||
// Numbers.
|
// Numbers.
|
||||||
var (
|
var (
|
||||||
|
// Window dimensions.
|
||||||
|
Width = 1024
|
||||||
|
Height = 768
|
||||||
|
|
||||||
// Speed to scroll a canvas with arrow keys in Edit Mode.
|
// Speed to scroll a canvas with arrow keys in Edit Mode.
|
||||||
CanvasScrollSpeed int32 = 8
|
CanvasScrollSpeed int32 = 8
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle"
|
"git.kirsle.net/apps/doodle"
|
||||||
|
"git.kirsle.net/apps/doodle/balance"
|
||||||
"git.kirsle.net/apps/doodle/render/sdl"
|
"git.kirsle.net/apps/doodle/render/sdl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,13 +14,15 @@ var Build string
|
||||||
|
|
||||||
// Command line args
|
// Command line args
|
||||||
var (
|
var (
|
||||||
debug bool
|
debug bool
|
||||||
edit bool
|
edit bool
|
||||||
|
guitest bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.BoolVar(&debug, "debug", false, "Debug mode")
|
flag.BoolVar(&debug, "debug", false, "Debug mode")
|
||||||
flag.BoolVar(&edit, "edit", false, "Edit the map given on the command line. Default is to play the map.")
|
flag.BoolVar(&edit, "edit", false, "Edit the map given on the command line. Default is to play the map.")
|
||||||
|
flag.BoolVar(&guitest, "guitest", false, "Enter the GUI Test scene.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -35,13 +38,15 @@ func main() {
|
||||||
// SDL engine.
|
// SDL engine.
|
||||||
engine := sdl.New(
|
engine := sdl.New(
|
||||||
"Doodle v"+doodle.Version,
|
"Doodle v"+doodle.Version,
|
||||||
800,
|
balance.Width,
|
||||||
600,
|
balance.Height,
|
||||||
)
|
)
|
||||||
|
|
||||||
app := doodle.New(debug, engine)
|
app := doodle.New(debug, engine)
|
||||||
app.SetupEngine()
|
app.SetupEngine()
|
||||||
if filename != "" {
|
if guitest {
|
||||||
|
app.Goto(&doodle.GUITestScene{})
|
||||||
|
} else if filename != "" {
|
||||||
if edit {
|
if edit {
|
||||||
app.EditFile(filename)
|
app.EditFile(filename)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -34,6 +34,8 @@ func (c Command) Run(d *Doodle) error {
|
||||||
return c.Edit(d)
|
return c.Edit(d)
|
||||||
case "play":
|
case "play":
|
||||||
return c.Play(d)
|
return c.Play(d)
|
||||||
|
case "close":
|
||||||
|
return c.Close(d)
|
||||||
case "exit":
|
case "exit":
|
||||||
case "quit":
|
case "quit":
|
||||||
return c.Quit()
|
return c.Quit()
|
||||||
|
@ -65,6 +67,13 @@ func (c Command) New(d *Doodle) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close returns to the Main Scene.
|
||||||
|
func (c Command) Close(d *Doodle) error {
|
||||||
|
main := &MainScene{}
|
||||||
|
d.Goto(main)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Help prints the help info.
|
// Help prints the help info.
|
||||||
func (c Command) Help(d *Doodle) error {
|
func (c Command) Help(d *Doodle) error {
|
||||||
if len(c.Args) == 0 {
|
if len(c.Args) == 0 {
|
||||||
|
|
21
config.go
21
config.go
|
@ -23,9 +23,13 @@ var (
|
||||||
// Profile Directory settings.
|
// Profile Directory settings.
|
||||||
var (
|
var (
|
||||||
ConfigDirectoryName = "doodle"
|
ConfigDirectoryName = "doodle"
|
||||||
ProfileDirectory string
|
|
||||||
LevelDirectory string
|
ProfileDirectory string
|
||||||
DoodadDirectory string
|
LevelDirectory string
|
||||||
|
DoodadDirectory string
|
||||||
|
|
||||||
|
CacheDirectory string
|
||||||
|
FontDirectory string
|
||||||
|
|
||||||
// Regexp to match simple filenames for maps and doodads.
|
// Regexp to match simple filenames for maps and doodads.
|
||||||
reSimpleFilename = regexp.MustCompile(`^([A-Za-z0-9-_.,+ '"\[\](){}]+)$`)
|
reSimpleFilename = regexp.MustCompile(`^([A-Za-z0-9-_.,+ '"\[\](){}]+)$`)
|
||||||
|
@ -38,10 +42,19 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
// Profile directory contains the user's levels and doodads.
|
||||||
ProfileDirectory = configdir.LocalConfig(ConfigDirectoryName)
|
ProfileDirectory = configdir.LocalConfig(ConfigDirectoryName)
|
||||||
LevelDirectory = configdir.LocalConfig(ConfigDirectoryName, "levels")
|
LevelDirectory = configdir.LocalConfig(ConfigDirectoryName, "levels")
|
||||||
DoodadDirectory = configdir.LocalConfig(ConfigDirectoryName, "doodads")
|
DoodadDirectory = configdir.LocalConfig(ConfigDirectoryName, "doodads")
|
||||||
configdir.MakePath(LevelDirectory, DoodadDirectory)
|
|
||||||
|
// Cache directory to extract font files to.
|
||||||
|
CacheDirectory = configdir.LocalCache(ConfigDirectoryName)
|
||||||
|
FontDirectory = configdir.LocalCache(ConfigDirectoryName, "fonts")
|
||||||
|
|
||||||
|
// Ensure all the directories exist.
|
||||||
|
configdir.MakePath(LevelDirectory)
|
||||||
|
configdir.MakePath(DoodadDirectory)
|
||||||
|
configdir.MakePath(FontDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LevelPath will turn a "simple" filename into an absolute path in the user's
|
// LevelPath will turn a "simple" filename into an absolute path in the user's
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.kirsle.net/apps/doodle/balance"
|
||||||
"git.kirsle.net/apps/doodle/enum"
|
"git.kirsle.net/apps/doodle/enum"
|
||||||
"git.kirsle.net/apps/doodle/render"
|
"git.kirsle.net/apps/doodle/render"
|
||||||
"github.com/kirsle/golog"
|
"github.com/kirsle/golog"
|
||||||
|
@ -46,8 +47,8 @@ func New(debug bool, engine render.Engine) *Doodle {
|
||||||
Engine: engine,
|
Engine: engine,
|
||||||
startTime: time.Now(),
|
startTime: time.Now(),
|
||||||
running: true,
|
running: true,
|
||||||
width: 800,
|
width: int32(balance.Width),
|
||||||
height: 600,
|
height: int32(balance.Height),
|
||||||
}
|
}
|
||||||
d.shell = NewShell(d)
|
d.shell = NewShell(d)
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,11 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/balance"
|
|
||||||
"git.kirsle.net/apps/doodle/doodads"
|
"git.kirsle.net/apps/doodle/doodads"
|
||||||
"git.kirsle.net/apps/doodle/enum"
|
"git.kirsle.net/apps/doodle/enum"
|
||||||
"git.kirsle.net/apps/doodle/events"
|
"git.kirsle.net/apps/doodle/events"
|
||||||
"git.kirsle.net/apps/doodle/level"
|
"git.kirsle.net/apps/doodle/level"
|
||||||
"git.kirsle.net/apps/doodle/render"
|
"git.kirsle.net/apps/doodle/render"
|
||||||
"git.kirsle.net/apps/doodle/uix"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// EditorScene manages the "Edit Level" game mode.
|
// EditorScene manages the "Edit Level" game mode.
|
||||||
|
@ -31,10 +29,6 @@ type EditorScene struct {
|
||||||
Level *level.Level
|
Level *level.Level
|
||||||
Doodad *doodads.Doodad
|
Doodad *doodads.Doodad
|
||||||
|
|
||||||
// The canvas widget that contains the map we're working on.
|
|
||||||
// XXX: in dev builds this is available at $ d.Scene.GetDrawing()
|
|
||||||
drawing *uix.Canvas
|
|
||||||
|
|
||||||
// Last saved filename by the user.
|
// Last saved filename by the user.
|
||||||
filename string
|
filename string
|
||||||
}
|
}
|
||||||
|
@ -46,15 +40,9 @@ func (s *EditorScene) Name() string {
|
||||||
|
|
||||||
// Setup the editor scene.
|
// Setup the editor scene.
|
||||||
func (s *EditorScene) Setup(d *Doodle) error {
|
func (s *EditorScene) Setup(d *Doodle) error {
|
||||||
s.drawing = uix.NewCanvas(balance.ChunkSize, true)
|
// Initialize the user interface. It references the palette and such so it
|
||||||
if len(s.drawing.Palette.Swatches) > 0 {
|
// must be initialized after those things.
|
||||||
s.drawing.SetSwatch(s.drawing.Palette.Swatches[0])
|
s.UI = NewEditorUI(d, s)
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: move inside the UI. Just an approximate position for now.
|
|
||||||
s.drawing.MoveTo(render.NewPoint(0, 19))
|
|
||||||
s.drawing.Resize(render.NewRect(d.width-150, d.height-44))
|
|
||||||
s.drawing.Compute(d.Engine)
|
|
||||||
|
|
||||||
// Were we given configuration data?
|
// Were we given configuration data?
|
||||||
if s.Filename != "" {
|
if s.Filename != "" {
|
||||||
|
@ -68,7 +56,7 @@ func (s *EditorScene) Setup(d *Doodle) error {
|
||||||
case enum.LevelDrawing:
|
case enum.LevelDrawing:
|
||||||
if s.Level != nil {
|
if s.Level != nil {
|
||||||
log.Debug("EditorScene.Setup: received level from scene caller")
|
log.Debug("EditorScene.Setup: received level from scene caller")
|
||||||
s.drawing.LoadLevel(s.Level)
|
s.UI.Canvas.LoadLevel(s.Level)
|
||||||
} else if s.filename != "" && s.OpenFile {
|
} else if s.filename != "" && s.OpenFile {
|
||||||
log.Debug("EditorScene.Setup: Loading map from filename at %s", s.filename)
|
log.Debug("EditorScene.Setup: Loading map from filename at %s", s.filename)
|
||||||
if err := s.LoadLevel(s.filename); err != nil {
|
if err := s.LoadLevel(s.filename); err != nil {
|
||||||
|
@ -81,9 +69,9 @@ func (s *EditorScene) Setup(d *Doodle) error {
|
||||||
log.Debug("EditorScene.Setup: initializing a new Level")
|
log.Debug("EditorScene.Setup: initializing a new Level")
|
||||||
s.Level = level.New()
|
s.Level = level.New()
|
||||||
s.Level.Palette = level.DefaultPalette()
|
s.Level.Palette = level.DefaultPalette()
|
||||||
s.drawing.LoadLevel(s.Level)
|
s.UI.Canvas.LoadLevel(s.Level)
|
||||||
s.drawing.ScrollTo(render.Origin)
|
s.UI.Canvas.ScrollTo(render.Origin)
|
||||||
s.drawing.Scrollable = true
|
s.UI.Canvas.Scrollable = true
|
||||||
}
|
}
|
||||||
case enum.DoodadDrawing:
|
case enum.DoodadDrawing:
|
||||||
// No Doodad?
|
// No Doodad?
|
||||||
|
@ -98,20 +86,16 @@ func (s *EditorScene) Setup(d *Doodle) error {
|
||||||
if s.Doodad == nil {
|
if s.Doodad == nil {
|
||||||
log.Debug("EditorScene.Setup: initializing a new Doodad")
|
log.Debug("EditorScene.Setup: initializing a new Doodad")
|
||||||
s.Doodad = doodads.New(s.DoodadSize)
|
s.Doodad = doodads.New(s.DoodadSize)
|
||||||
s.drawing.LoadDoodad(s.Doodad)
|
s.UI.Canvas.LoadDoodad(s.Doodad)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move inside the UI. Just an approximate position for now.
|
// TODO: move inside the UI. Just an approximate position for now.
|
||||||
s.drawing.MoveTo(render.NewPoint(200, 200))
|
s.UI.Canvas.Resize(render.NewRect(int32(s.DoodadSize), int32(s.DoodadSize)))
|
||||||
s.drawing.Resize(render.NewRect(int32(s.DoodadSize), int32(s.DoodadSize)))
|
s.UI.Canvas.ScrollTo(render.Origin)
|
||||||
s.drawing.ScrollTo(render.Origin)
|
s.UI.Canvas.Scrollable = false
|
||||||
s.drawing.Scrollable = false
|
s.UI.Workspace.Compute(d.Engine)
|
||||||
s.drawing.Compute(d.Engine)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the user interface. It references the palette and such so it
|
|
||||||
// must be initialized after those things.
|
|
||||||
s.UI = NewEditorUI(d, s)
|
|
||||||
d.Flash("Editor Mode. Press 'P' to play this map.")
|
d.Flash("Editor Mode. Press 'P' to play this map.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -120,7 +104,6 @@ func (s *EditorScene) Setup(d *Doodle) error {
|
||||||
// Loop the editor scene.
|
// Loop the editor scene.
|
||||||
func (s *EditorScene) Loop(d *Doodle, ev *events.State) error {
|
func (s *EditorScene) Loop(d *Doodle, ev *events.State) error {
|
||||||
s.UI.Loop(ev)
|
s.UI.Loop(ev)
|
||||||
s.drawing.Loop(ev)
|
|
||||||
|
|
||||||
// Switching to Play Mode?
|
// Switching to Play Mode?
|
||||||
if ev.KeyName.Read() == "p" {
|
if ev.KeyName.Read() == "p" {
|
||||||
|
@ -141,7 +124,6 @@ func (s *EditorScene) Draw(d *Doodle) error {
|
||||||
d.Engine.Clear(render.Magenta)
|
d.Engine.Clear(render.Magenta)
|
||||||
|
|
||||||
s.UI.Present(d.Engine)
|
s.UI.Present(d.Engine)
|
||||||
s.drawing.Present(d.Engine, s.drawing.Point())
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -157,7 +139,7 @@ func (s *EditorScene) LoadLevel(filename string) error {
|
||||||
|
|
||||||
s.DrawingType = enum.LevelDrawing
|
s.DrawingType = enum.LevelDrawing
|
||||||
s.Level = level
|
s.Level = level
|
||||||
s.drawing.LoadLevel(s.Level)
|
s.UI.Canvas.LoadLevel(s.Level)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,8 +164,8 @@ func (s *EditorScene) SaveLevel(filename string) error {
|
||||||
m.Author = os.Getenv("USER")
|
m.Author = os.Getenv("USER")
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Palette = s.drawing.Palette
|
m.Palette = s.UI.Canvas.Palette
|
||||||
m.Chunker = s.drawing.Chunker()
|
m.Chunker = s.UI.Canvas.Chunker()
|
||||||
|
|
||||||
json, err := m.ToJSON()
|
json, err := m.ToJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -213,7 +195,7 @@ func (s *EditorScene) LoadDoodad(filename string) error {
|
||||||
s.DrawingType = enum.DoodadDrawing
|
s.DrawingType = enum.DoodadDrawing
|
||||||
s.Doodad = doodad
|
s.Doodad = doodad
|
||||||
s.DoodadSize = doodad.Layers[0].Chunker.Size
|
s.DoodadSize = doodad.Layers[0].Chunker.Size
|
||||||
s.drawing.LoadDoodad(s.Doodad)
|
s.UI.Canvas.LoadDoodad(s.Doodad)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,8 +219,8 @@ func (s *EditorScene) SaveDoodad(filename string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is this copying necessary?
|
// TODO: is this copying necessary?
|
||||||
d.Palette = s.drawing.Palette
|
d.Palette = s.UI.Canvas.Palette
|
||||||
d.Layers[0].Chunker = s.drawing.Chunker()
|
d.Layers[0].Chunker = s.UI.Canvas.Chunker()
|
||||||
|
|
||||||
// Save it to their profile directory.
|
// Save it to their profile directory.
|
||||||
filename = DoodadPath(filename)
|
filename = DoodadPath(filename)
|
||||||
|
|
|
@ -7,5 +7,5 @@ import "git.kirsle.net/apps/doodle/uix"
|
||||||
|
|
||||||
// GetDrawing returns the uix.Canvas
|
// GetDrawing returns the uix.Canvas
|
||||||
func (w *EditorScene) GetDrawing() *uix.Canvas {
|
func (w *EditorScene) GetDrawing() *uix.Canvas {
|
||||||
return w.drawing
|
return w.UI.Canvas
|
||||||
}
|
}
|
||||||
|
|
131
editor_ui.go
131
editor_ui.go
|
@ -7,8 +7,10 @@ import (
|
||||||
"git.kirsle.net/apps/doodle/balance"
|
"git.kirsle.net/apps/doodle/balance"
|
||||||
"git.kirsle.net/apps/doodle/enum"
|
"git.kirsle.net/apps/doodle/enum"
|
||||||
"git.kirsle.net/apps/doodle/events"
|
"git.kirsle.net/apps/doodle/events"
|
||||||
|
"git.kirsle.net/apps/doodle/level"
|
||||||
"git.kirsle.net/apps/doodle/render"
|
"git.kirsle.net/apps/doodle/render"
|
||||||
"git.kirsle.net/apps/doodle/ui"
|
"git.kirsle.net/apps/doodle/ui"
|
||||||
|
"git.kirsle.net/apps/doodle/uix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EditorUI manages the user interface for the Editor Scene.
|
// EditorUI manages the user interface for the Editor Scene.
|
||||||
|
@ -24,6 +26,8 @@ type EditorUI struct {
|
||||||
|
|
||||||
// Widgets
|
// Widgets
|
||||||
Supervisor *ui.Supervisor
|
Supervisor *ui.Supervisor
|
||||||
|
Canvas *uix.Canvas
|
||||||
|
Workspace *ui.Frame
|
||||||
MenuBar *ui.Frame
|
MenuBar *ui.Frame
|
||||||
Palette *ui.Window
|
Palette *ui.Window
|
||||||
StatusBar *ui.Frame
|
StatusBar *ui.Frame
|
||||||
|
@ -40,14 +44,23 @@ func NewEditorUI(d *Doodle, s *EditorScene) *EditorUI {
|
||||||
StatusFilenameText: "Filename: <none>",
|
StatusFilenameText: "Filename: <none>",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select the first swatch of the palette.
|
u.Canvas = u.SetupCanvas(d)
|
||||||
if u.Scene.drawing.Palette.ActiveSwatch != nil {
|
|
||||||
u.selectedSwatch = u.Scene.drawing.Palette.ActiveSwatch.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
u.MenuBar = u.SetupMenuBar(d)
|
u.MenuBar = u.SetupMenuBar(d)
|
||||||
u.StatusBar = u.SetupStatusBar(d)
|
u.StatusBar = u.SetupStatusBar(d)
|
||||||
u.Palette = u.SetupPalette(d)
|
u.Palette = u.SetupPalette(d)
|
||||||
|
u.Workspace = u.SetupWorkspace(d) // important that this is last!
|
||||||
|
|
||||||
|
// Position the Canvas inside the frame.
|
||||||
|
u.Workspace.Pack(u.Canvas, ui.Pack{
|
||||||
|
Anchor: ui.N,
|
||||||
|
})
|
||||||
|
u.Workspace.Compute(d.Engine)
|
||||||
|
u.ExpandCanvas(d.Engine)
|
||||||
|
|
||||||
|
// Select the first swatch of the palette.
|
||||||
|
if u.Canvas.Palette != nil && u.Canvas.Palette.ActiveSwatch != nil {
|
||||||
|
u.selectedSwatch = u.Canvas.Palette.ActiveSwatch.Name
|
||||||
|
}
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +73,7 @@ func (u *EditorUI) Loop(ev *events.State) {
|
||||||
ev.CursorY.Now,
|
ev.CursorY.Now,
|
||||||
)
|
)
|
||||||
u.StatusPaletteText = fmt.Sprintf("Swatch: %s",
|
u.StatusPaletteText = fmt.Sprintf("Swatch: %s",
|
||||||
u.Scene.drawing.Palette.ActiveSwatch,
|
u.Canvas.Palette.ActiveSwatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Statusbar filename label.
|
// Statusbar filename label.
|
||||||
|
@ -80,6 +93,7 @@ func (u *EditorUI) Loop(ev *events.State) {
|
||||||
u.MenuBar.Compute(u.d.Engine)
|
u.MenuBar.Compute(u.d.Engine)
|
||||||
u.StatusBar.Compute(u.d.Engine)
|
u.StatusBar.Compute(u.d.Engine)
|
||||||
u.Palette.Compute(u.d.Engine)
|
u.Palette.Compute(u.d.Engine)
|
||||||
|
u.Canvas.Loop(ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present the UI to the screen.
|
// Present the UI to the screen.
|
||||||
|
@ -93,6 +107,46 @@ func (u *EditorUI) Present(e render.Engine) {
|
||||||
u.Palette.Present(e, u.Palette.Point())
|
u.Palette.Present(e, u.Palette.Point())
|
||||||
u.MenuBar.Present(e, u.MenuBar.Point())
|
u.MenuBar.Present(e, u.MenuBar.Point())
|
||||||
u.StatusBar.Present(e, u.StatusBar.Point())
|
u.StatusBar.Present(e, u.StatusBar.Point())
|
||||||
|
u.Workspace.Present(e, u.Workspace.Point())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWorkspace configures the main Workspace frame that takes up the full
|
||||||
|
// window apart from toolbars. The Workspace has a single child element, the
|
||||||
|
// Canvas, so it can easily full-screen it or center it for Doodad editing.
|
||||||
|
func (u *EditorUI) SetupWorkspace(d *Doodle) *ui.Frame {
|
||||||
|
frame := ui.NewFrame("Workspace")
|
||||||
|
|
||||||
|
// Position and size the frame around the other main widgets.
|
||||||
|
frame.MoveTo(render.NewPoint(
|
||||||
|
0,
|
||||||
|
u.MenuBar.Size().H,
|
||||||
|
))
|
||||||
|
frame.Resize(render.NewRect(
|
||||||
|
d.width-u.Palette.Size().W,
|
||||||
|
d.height-u.MenuBar.Size().H-u.StatusBar.Size().H,
|
||||||
|
))
|
||||||
|
frame.Compute(d.Engine)
|
||||||
|
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupCanvas configures the main drawing canvas in the editor.
|
||||||
|
func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
||||||
|
drawing := uix.NewCanvas(balance.ChunkSize, true)
|
||||||
|
drawing.Palette = level.DefaultPalette()
|
||||||
|
if len(drawing.Palette.Swatches) > 0 {
|
||||||
|
drawing.SetSwatch(drawing.Palette.Swatches[0])
|
||||||
|
}
|
||||||
|
return drawing
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpandCanvas manually expands the Canvas to fill the frame, to work around
|
||||||
|
// UI packing bugs. Ideally I would use `Expand: true` when packing the Canvas
|
||||||
|
// in its frame, but that would artificially expand the Canvas also when it
|
||||||
|
// _wanted_ to be smaller, as in Doodad Editing Mode.
|
||||||
|
func (u *EditorUI) ExpandCanvas(e render.Engine) {
|
||||||
|
u.Canvas.Resize(u.Workspace.Size())
|
||||||
|
u.Workspace.Compute(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupMenuBar sets up the menu bar.
|
// SetupMenuBar sets up the menu bar.
|
||||||
|
@ -214,7 +268,6 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.Frame {
|
||||||
|
|
||||||
// SetupPalette sets up the palette panel.
|
// SetupPalette sets up the palette panel.
|
||||||
func (u *EditorUI) SetupPalette(d *Doodle) *ui.Window {
|
func (u *EditorUI) SetupPalette(d *Doodle) *ui.Window {
|
||||||
log.Error("SetupPalette Window")
|
|
||||||
window := ui.NewWindow("Palette")
|
window := ui.NewWindow("Palette")
|
||||||
window.ConfigureTitle(balance.TitleConfig)
|
window.ConfigureTitle(balance.TitleConfig)
|
||||||
window.TitleBar().Font = balance.TitleFont
|
window.TitleBar().Font = balance.TitleFont
|
||||||
|
@ -232,32 +285,34 @@ func (u *EditorUI) SetupPalette(d *Doodle) *ui.Window {
|
||||||
// Handler function for the radio buttons being clicked.
|
// Handler function for the radio buttons being clicked.
|
||||||
onClick := func(p render.Point) {
|
onClick := func(p render.Point) {
|
||||||
name := u.selectedSwatch
|
name := u.selectedSwatch
|
||||||
swatch, ok := u.Scene.drawing.Palette.Get(name)
|
swatch, ok := u.Canvas.Palette.Get(name)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Error("Palette onClick: couldn't get swatch named '%s' from palette", name)
|
log.Error("Palette onClick: couldn't get swatch named '%s' from palette", name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Info("Set swatch: %s", swatch)
|
log.Info("Set swatch: %s", swatch)
|
||||||
u.Scene.drawing.SetSwatch(swatch)
|
u.Canvas.SetSwatch(swatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the radio buttons for the palette.
|
// Draw the radio buttons for the palette.
|
||||||
for _, swatch := range u.Scene.drawing.Palette.Swatches {
|
if u.Canvas != nil && u.Canvas.Palette != nil {
|
||||||
label := ui.NewLabel(ui.Label{
|
for _, swatch := range u.Canvas.Palette.Swatches {
|
||||||
Text: swatch.Name,
|
label := ui.NewLabel(ui.Label{
|
||||||
Font: balance.StatusFont,
|
Text: swatch.Name,
|
||||||
})
|
Font: balance.StatusFont,
|
||||||
label.Font.Color = swatch.Color.Darken(40)
|
})
|
||||||
|
label.Font.Color = swatch.Color.Darken(40)
|
||||||
|
|
||||||
btn := ui.NewRadioButton("palette", &u.selectedSwatch, swatch.Name, label)
|
btn := ui.NewRadioButton("palette", &u.selectedSwatch, swatch.Name, label)
|
||||||
btn.Handle(ui.Click, onClick)
|
btn.Handle(ui.Click, onClick)
|
||||||
u.Supervisor.Add(btn)
|
u.Supervisor.Add(btn)
|
||||||
|
|
||||||
window.Pack(btn, ui.Pack{
|
window.Pack(btn, ui.Pack{
|
||||||
Anchor: ui.N,
|
Anchor: ui.N,
|
||||||
Fill: true,
|
Fill: true,
|
||||||
PadY: 4,
|
PadY: 4,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return window
|
return window
|
||||||
|
@ -309,25 +364,25 @@ func (u *EditorUI) SetupStatusBar(d *Doodle) *ui.Frame {
|
||||||
filenameLabel.Configure(style)
|
filenameLabel.Configure(style)
|
||||||
filenameLabel.Compute(d.Engine)
|
filenameLabel.Compute(d.Engine)
|
||||||
frame.Pack(filenameLabel, ui.Pack{
|
frame.Pack(filenameLabel, ui.Pack{
|
||||||
Anchor: ui.W,
|
Anchor: ui.E,
|
||||||
PadX: 1,
|
PadX: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: right-aligned labels clip out of bounds
|
// TODO: right-aligned labels clip out of bounds
|
||||||
// extraLabel := ui.NewLabel(ui.Label{
|
extraLabel := ui.NewLabel(ui.Label{
|
||||||
// Text: "blah",
|
Text: "blah",
|
||||||
// Font: balance.StatusFont,
|
Font: balance.StatusFont,
|
||||||
// })
|
})
|
||||||
// extraLabel.Configure(ui.Config{
|
extraLabel.Configure(ui.Config{
|
||||||
// Background: render.Grey,
|
Background: render.Grey,
|
||||||
// BorderStyle: ui.BorderSunken,
|
BorderStyle: ui.BorderSunken,
|
||||||
// BorderColor: render.Grey,
|
BorderColor: render.Grey,
|
||||||
// BorderSize: 1,
|
BorderSize: 1,
|
||||||
// })
|
})
|
||||||
// extraLabel.Compute(d.Engine)
|
extraLabel.Compute(d.Engine)
|
||||||
// frame.Pack(extraLabel, ui.Pack{
|
frame.Pack(extraLabel, ui.Pack{
|
||||||
// Anchor: ui.E,
|
Anchor: ui.E,
|
||||||
// })
|
})
|
||||||
|
|
||||||
frame.Resize(render.Rect{
|
frame.Resize(render.Rect{
|
||||||
W: d.width,
|
W: d.width,
|
||||||
|
|
104
lib/debugging/debugging.go
Normal file
104
lib/debugging/debugging.go
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
// Package debugging contains useful methods for debugging the app, safely
|
||||||
|
// isolated from the rest of the app's packages.
|
||||||
|
package debugging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configurable variables for the stack tracer functions.
|
||||||
|
var (
|
||||||
|
// StackDepth is the depth that Callers() will crawl up the call stack. This
|
||||||
|
// variable is configurable.
|
||||||
|
StackDepth = 20
|
||||||
|
|
||||||
|
// StopAt is the function name to stop the tracebacks at. Set to a blank
|
||||||
|
// string to not stop and trace all the way up to `runtime.goexit` or
|
||||||
|
// wherever.
|
||||||
|
StopAt = "main.main"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Minimum depth given to runtime.Caller() so that the call stacks will exclude
|
||||||
|
// the call to debugging.Caller() itself -- so this debug module won't debug its
|
||||||
|
// own function calls in the tracebacks.
|
||||||
|
const minDepth = 2
|
||||||
|
|
||||||
|
// Caller returns the filename and line number that called the calling
|
||||||
|
// function.
|
||||||
|
func Caller() string {
|
||||||
|
if pc, file, no, ok := runtime.Caller(minDepth); ok {
|
||||||
|
frames := runtime.CallersFrames([]uintptr{pc})
|
||||||
|
frame, _ := frames.Next()
|
||||||
|
if frame.Function != "" {
|
||||||
|
return fmt.Sprintf("%s#%d: %s()",
|
||||||
|
frame.File,
|
||||||
|
frame.Line,
|
||||||
|
frame.Function,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s#%d",
|
||||||
|
file,
|
||||||
|
no,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return "[no caller information]"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callers returns an array of all the callers of the current function.
|
||||||
|
func Callers() []string {
|
||||||
|
var (
|
||||||
|
callers []string
|
||||||
|
pc = make([]uintptr, StackDepth)
|
||||||
|
count = runtime.Callers(minDepth, pc)
|
||||||
|
)
|
||||||
|
pc = pc[:count] // only pass valid program counters to CallersFrames
|
||||||
|
var frames = runtime.CallersFrames(pc)
|
||||||
|
_ = frames
|
||||||
|
|
||||||
|
// Loop to get frames of the call stack.
|
||||||
|
for {
|
||||||
|
frame, more := frames.Next()
|
||||||
|
|
||||||
|
callers = append(callers, fmt.Sprintf("%s#%d: %s()",
|
||||||
|
frame.File,
|
||||||
|
frame.Line,
|
||||||
|
frame.Function,
|
||||||
|
))
|
||||||
|
|
||||||
|
if StopAt != "" && frame.Function == StopAt {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if !more {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return callers
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringifyCallers pretty-prints the Callers as a single string with newlines.
|
||||||
|
func StringifyCallers() string {
|
||||||
|
callers := Callers()
|
||||||
|
var result []string
|
||||||
|
for i, caller := range callers {
|
||||||
|
if i == 0 {
|
||||||
|
continue // StringifyCallers() would be the first row, skip it.
|
||||||
|
}
|
||||||
|
result = append(result, fmt.Sprintf("%d: %s", i, caller))
|
||||||
|
}
|
||||||
|
return strings.Join(result, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintCallers prints the stringified callers directly to STDOUT.
|
||||||
|
func PrintCallers() {
|
||||||
|
fmt.Println("Call stack (most recent/current function first):")
|
||||||
|
for i, caller := range Callers() {
|
||||||
|
if i == 0 {
|
||||||
|
continue // PrintCallers() would be the first row, skip it.
|
||||||
|
}
|
||||||
|
fmt.Printf("%d: %s\n", i, caller)
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,12 +31,12 @@ type Renderer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates the SDL renderer.
|
// New creates the SDL renderer.
|
||||||
func New(title string, width, height int32) *Renderer {
|
func New(title string, width, height int) *Renderer {
|
||||||
return &Renderer{
|
return &Renderer{
|
||||||
events: events.New(),
|
events: events.New(),
|
||||||
title: title,
|
title: title,
|
||||||
width: width,
|
width: int32(width),
|
||||||
height: height,
|
height: int32(height),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,7 @@ func (w *Button) SetText(text string) error {
|
||||||
// Present the button.
|
// Present the button.
|
||||||
func (w *Button) Present(e render.Engine, P render.Point) {
|
func (w *Button) Present(e render.Engine, P render.Point) {
|
||||||
w.Compute(e)
|
w.Compute(e)
|
||||||
|
w.MoveTo(P)
|
||||||
var (
|
var (
|
||||||
S = w.Size()
|
S = w.Size()
|
||||||
ChildSize = w.child.Size()
|
ChildSize = w.child.Size()
|
||||||
|
|
|
@ -69,7 +69,14 @@ func (w *Frame) Present(e render.Engine, P render.Point) {
|
||||||
P.X+p.X+w.BoxThickness(1),
|
P.X+p.X+w.BoxThickness(1),
|
||||||
P.Y+p.Y+w.BoxThickness(1),
|
P.Y+p.Y+w.BoxThickness(1),
|
||||||
)
|
)
|
||||||
child.MoveTo(moveTo)
|
// if child.ID() == "Canvas" {
|
||||||
|
// log.Debug("Frame X=%d Child X=%d Box=%d Point=%s", P.X, p.X, w.BoxThickness(1), p)
|
||||||
|
// log.Debug("Frame Y=%d Child Y=%d Box=%d MoveTo=%s", P.Y, p.Y, w.BoxThickness(1), moveTo)
|
||||||
|
// }
|
||||||
|
// child.MoveTo(moveTo) // TODO: if uncommented the child will creep down the parent each tick
|
||||||
|
// if child.ID() == "Canvas" {
|
||||||
|
// log.Debug("New Point: %s", child.Point())
|
||||||
|
// }
|
||||||
child.Present(e, moveTo)
|
child.Present(e, moveTo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,12 @@ func (w *Frame) computePacked(e render.Engine) {
|
||||||
xDirection int32 = 1
|
xDirection int32 = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
if anchor.IsSouth() {
|
if anchor.IsSouth() { // TODO: these need tuning
|
||||||
y = frameSize.H
|
y = frameSize.H - w.BoxThickness(4)
|
||||||
yDirection = -1 - w.BoxThickness(2) // parent + child BoxThickness(1) = 2
|
yDirection = -1 * w.BoxThickness(4) // parent + child BoxThickness(1) = 2
|
||||||
} else if anchor == E {
|
} else if anchor == E {
|
||||||
x = frameSize.W
|
x = frameSize.W - w.BoxThickness(4)
|
||||||
xDirection = -1 // - w.BoxThickness(2)
|
xDirection = -1 - w.BoxThickness(4) // - w.BoxThickness(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, packedWidget := range w.packs[anchor] {
|
for _, packedWidget := range w.packs[anchor] {
|
||||||
|
@ -64,10 +64,10 @@ func (w *Frame) computePacked(e render.Engine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if anchor.IsSouth() {
|
if anchor.IsSouth() {
|
||||||
y -= size.H + pack.PadY
|
y -= size.H - pack.PadY
|
||||||
}
|
}
|
||||||
if anchor.IsEast() {
|
if anchor.IsEast() {
|
||||||
x -= size.W + pack.PadX
|
x -= size.W - pack.PadX
|
||||||
}
|
}
|
||||||
|
|
||||||
child.MoveTo(render.NewPoint(x, y))
|
child.MoveTo(render.NewPoint(x, y))
|
||||||
|
@ -80,7 +80,7 @@ func (w *Frame) computePacked(e render.Engine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
visited = append(visited, packedWidget)
|
visited = append(visited, packedWidget)
|
||||||
if pack.Expand {
|
if pack.Expand { // TODO: don't fuck with children of fixed size
|
||||||
expanded = append(expanded, packedWidget)
|
expanded = append(expanded, packedWidget)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,10 +131,6 @@ func (w *Frame) computePacked(e render.Engine) {
|
||||||
moved bool
|
moved bool
|
||||||
)
|
)
|
||||||
|
|
||||||
if w.String() == "Frame<Row0; 3 widgets>" {
|
|
||||||
log.Debug("%s>%s: pack.FillX=%d resize=%s innerFrameSize=%s", w, child, pack.FillX, resize, innerFrameSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pack.Anchor.IsNorth() || pack.Anchor.IsSouth() {
|
if pack.Anchor.IsNorth() || pack.Anchor.IsSouth() {
|
||||||
if pack.FillX && resize.W < innerFrameSize.W {
|
if pack.FillX && resize.W < innerFrameSize.W {
|
||||||
resize.W = innerFrameSize.W - w.BoxThickness(2)
|
resize.W = innerFrameSize.W - w.BoxThickness(2)
|
||||||
|
@ -175,7 +171,6 @@ func (w *Frame) computePacked(e render.Engine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if resized && size != resize {
|
if resized && size != resize {
|
||||||
// log.Debug("%s/%s: resize to: %s", w, child, resize)
|
|
||||||
child.Resize(resize)
|
child.Resize(resize)
|
||||||
child.Compute(e)
|
child.Compute(e)
|
||||||
}
|
}
|
||||||
|
@ -288,6 +283,9 @@ func (w *Frame) Pack(child Widget, config ...Pack) {
|
||||||
C.FillY = true
|
C.FillY = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adopt the child widget so it can access the Frame.
|
||||||
|
child.Adopt(w)
|
||||||
|
|
||||||
w.packs[C.Anchor] = append(w.packs[C.Anchor], packedWidget{
|
w.packs[C.Anchor] = append(w.packs[C.Anchor], packedWidget{
|
||||||
widget: child,
|
widget: child,
|
||||||
pack: C,
|
pack: C,
|
||||||
|
|
38
ui/functions.go
Normal file
38
ui/functions.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import "git.kirsle.net/apps/doodle/render"
|
||||||
|
|
||||||
|
// AbsolutePosition computes a widget's absolute X,Y position on the
|
||||||
|
// window on screen by crawling its parent widget tree.
|
||||||
|
func AbsolutePosition(w Widget) render.Point {
|
||||||
|
abs := w.Point()
|
||||||
|
|
||||||
|
var (
|
||||||
|
node = w
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
node, ok = node.Parent()
|
||||||
|
if !ok { // reached the top of the tree
|
||||||
|
return abs
|
||||||
|
}
|
||||||
|
|
||||||
|
abs.Add(node.Point())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsoluteRect returns a Rect() offset with the absolute position.
|
||||||
|
func AbsoluteRect(w Widget) render.Rect {
|
||||||
|
var (
|
||||||
|
P = AbsolutePosition(w)
|
||||||
|
R = w.Rect()
|
||||||
|
)
|
||||||
|
return render.Rect{
|
||||||
|
X: P.X,
|
||||||
|
Y: P.Y,
|
||||||
|
W: R.W + P.X,
|
||||||
|
H: R.H, // TODO: the Canvas in EditMode lets you draw pixels
|
||||||
|
// below the status bar if we do `+ R.Y` here.
|
||||||
|
}
|
||||||
|
}
|
26
ui/widget.go
26
ui/widget.go
|
@ -56,6 +56,11 @@ type Widget interface {
|
||||||
OutlineSize() int32 // Outline size (default 0)
|
OutlineSize() int32 // Outline size (default 0)
|
||||||
SetOutlineSize(int32) //
|
SetOutlineSize(int32) //
|
||||||
|
|
||||||
|
// Container widgets like Frames can wire up associations between the
|
||||||
|
// child widgets and the parent.
|
||||||
|
Parent() (parent Widget, ok bool)
|
||||||
|
Adopt(parent Widget) // for the container to assign itself the parent
|
||||||
|
|
||||||
// Run any render computations; by the end the widget must know its
|
// Run any render computations; by the end the widget must know its
|
||||||
// Width and Height. For example the Label widget will render itself onto
|
// Width and Height. For example the Label widget will render itself onto
|
||||||
// an SDL Surface and then it will know its bounding box, but not before.
|
// an SDL Surface and then it will know its bounding box, but not before.
|
||||||
|
@ -105,6 +110,8 @@ type BaseWidget struct {
|
||||||
outlineColor render.Color
|
outlineColor render.Color
|
||||||
outlineSize int32
|
outlineSize int32
|
||||||
handlers map[Event][]func(render.Point)
|
handlers map[Event][]func(render.Point)
|
||||||
|
hasParent bool
|
||||||
|
parent Widget
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetID sets a string name for your widget, helpful for debugging purposes.
|
// SetID sets a string name for your widget, helpful for debugging purposes.
|
||||||
|
@ -250,6 +257,25 @@ func (w *BaseWidget) BoxThickness(m int32) int32 {
|
||||||
return (w.Margin() * m) + (w.BorderSize() * m) + (w.OutlineSize() * m)
|
return (w.Margin() * m) + (w.BorderSize() * m) + (w.OutlineSize() * m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parent returns the parent widget, like a Frame, and a boolean indicating
|
||||||
|
// whether the widget had a parent.
|
||||||
|
func (w *BaseWidget) Parent() (Widget, bool) {
|
||||||
|
return w.parent, w.hasParent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adopt sets the widget's parent. This function is called by container
|
||||||
|
// widgets like Frame when they add a child widget to their care.
|
||||||
|
// Pass a nil parent to unset the parent.
|
||||||
|
func (w *BaseWidget) Adopt(parent Widget) {
|
||||||
|
if parent == nil {
|
||||||
|
w.hasParent = false
|
||||||
|
w.parent = nil
|
||||||
|
} else {
|
||||||
|
w.hasParent = true
|
||||||
|
w.parent = parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DrawBox draws the border and outline.
|
// DrawBox draws the border and outline.
|
||||||
func (w *BaseWidget) DrawBox(e render.Engine, P render.Point) {
|
func (w *BaseWidget) DrawBox(e render.Engine, P render.Point) {
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -38,6 +38,9 @@ func NewCanvas(size int, editable bool) *Canvas {
|
||||||
chunks: level.NewChunker(size),
|
chunks: level.NewChunker(size),
|
||||||
}
|
}
|
||||||
w.setup()
|
w.setup()
|
||||||
|
w.IDFunc(func() string {
|
||||||
|
return "Canvas"
|
||||||
|
})
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,10 +84,9 @@ func (w *Canvas) setup() {
|
||||||
// Loop is called on the scene's event loop to handle mouse interaction with
|
// Loop is called on the scene's event loop to handle mouse interaction with
|
||||||
// the canvas, i.e. to edit it.
|
// the canvas, i.e. to edit it.
|
||||||
func (w *Canvas) Loop(ev *events.State) error {
|
func (w *Canvas) Loop(ev *events.State) error {
|
||||||
var (
|
// Get the absolute position of the canvas on screen to accurately match
|
||||||
P = w.Point()
|
// it up to mouse clicks.
|
||||||
_ = P
|
var P = ui.AbsolutePosition(w)
|
||||||
)
|
|
||||||
|
|
||||||
if w.Scrollable {
|
if w.Scrollable {
|
||||||
// Arrow keys to scroll the view.
|
// Arrow keys to scroll the view.
|
||||||
|
@ -106,7 +108,7 @@ func (w *Canvas) Loop(ev *events.State) error {
|
||||||
|
|
||||||
// Only care if the cursor is over our space.
|
// Only care if the cursor is over our space.
|
||||||
cursor := render.NewPoint(ev.CursorX.Now, ev.CursorY.Now)
|
cursor := render.NewPoint(ev.CursorX.Now, ev.CursorY.Now)
|
||||||
if !cursor.Inside(w.Rect()) {
|
if !cursor.Inside(ui.AbsoluteRect(w)) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +119,6 @@ func (w *Canvas) Loop(ev *events.State) error {
|
||||||
|
|
||||||
// Clicking? Log all the pixels while doing so.
|
// Clicking? Log all the pixels while doing so.
|
||||||
if ev.Button1.Now {
|
if ev.Button1.Now {
|
||||||
// log.Warn("Button1: %+v", ev.Button1)
|
|
||||||
lastPixel := w.lastPixel
|
lastPixel := w.lastPixel
|
||||||
cursor := render.Point{
|
cursor := render.Point{
|
||||||
X: ev.CursorX.Now - P.X + w.Scroll.X,
|
X: ev.CursorX.Now - P.X + w.Scroll.X,
|
||||||
|
@ -193,7 +194,7 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
|
||||||
S = w.Size()
|
S = w.Size()
|
||||||
Viewport = w.Viewport()
|
Viewport = w.Viewport()
|
||||||
)
|
)
|
||||||
w.MoveTo(p)
|
// w.MoveTo(p) // TODO: when uncommented the canvas will creep down the Workspace frame in EditorMode
|
||||||
w.DrawBox(e, p)
|
w.DrawBox(e, p)
|
||||||
e.DrawBox(w.Background(), render.Rect{
|
e.DrawBox(w.Background(), render.Rect{
|
||||||
X: p.X + w.BoxThickness(1),
|
X: p.X + w.BoxThickness(1),
|
||||||
|
|
14
uix/log.go
Normal file
14
uix/log.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package uix
|
||||||
|
|
||||||
|
import "github.com/kirsle/golog"
|
||||||
|
|
||||||
|
var log *golog.Logger
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log = golog.GetLogger("uix")
|
||||||
|
log.Configure(&golog.Config{
|
||||||
|
Level: golog.DebugLevel,
|
||||||
|
Theme: golog.DarkTheme,
|
||||||
|
Colors: golog.ExtendedColor,
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user