Add Tool Bar to Editor Mode

* Toolbar has icon buttons for the Pencil Tool, Line Tool, Rect Tool,
  Actor Tool and Link Tool.
* Remove the tab buttons from the top of the Palette window. The palette
  tab is now toggled between Swatches and Doodads by the tool selected
  on the tool bar, instead of the tab buttons setting the tool.
* Remove the "Link Doodads" button from the Doodad Palette. The Link
  Tool has its own dedicated toolbar button with the others.
This commit is contained in:
Noah 2019-07-03 20:24:04 -07:00
parent 5a1ec156ca
commit 12d34517e9
13 changed files with 225 additions and 75 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

View File

@ -41,6 +41,13 @@ func NewImage(c Image) *Image {
return w return w
} }
// ImageFromTexture creates an Image from a texture.
func ImageFromTexture(tex render.Texturer) *Image {
return &Image{
texture: tex,
}
}
// OpenImage initializes an Image with a given file name. // OpenImage initializes an Image with a given file name.
// //
// The file extension is important and should be a supported ImageType. // The file extension is important and should be a supported ImageType.

View File

@ -41,6 +41,7 @@ type EditorUI struct {
Workspace *ui.Frame Workspace *ui.Frame
MenuBar *ui.Frame MenuBar *ui.Frame
StatusBar *ui.Frame StatusBar *ui.Frame
ToolBar *ui.Frame
PlayButton *ui.Button PlayButton *ui.Button
// Palette window. // Palette window.
@ -48,6 +49,9 @@ type EditorUI struct {
PaletteTab *ui.Frame PaletteTab *ui.Frame
DoodadTab *ui.Frame DoodadTab *ui.Frame
// ToolBar window.
activeTool *string
// Draggable Doodad canvas. // Draggable Doodad canvas.
DraggableActor *DraggableActor DraggableActor *DraggableActor
@ -67,6 +71,10 @@ func NewEditorUI(d *Doodle, s *EditorScene) *EditorUI {
StatusScrollText: "Hello world", StatusScrollText: "Hello world",
} }
// Default tool in the toolbox.
activeTool := drawtool.PencilTool.String()
u.activeTool = &activeTool
// Bind the StatusBoxes arrays to the text variables. // Bind the StatusBoxes arrays to the text variables.
u.StatusBoxes = []*string{ u.StatusBoxes = []*string{
&u.StatusMouseText, &u.StatusMouseText,
@ -78,6 +86,7 @@ func NewEditorUI(d *Doodle, s *EditorScene) *EditorUI {
u.Canvas = u.SetupCanvas(d) u.Canvas = u.SetupCanvas(d)
u.MenuBar = u.SetupMenuBar(d) u.MenuBar = u.SetupMenuBar(d)
u.StatusBar = u.SetupStatusBar(d) u.StatusBar = u.SetupStatusBar(d)
u.ToolBar = u.SetupToolbar(d)
u.Workspace = u.SetupWorkspace(d) // important that this is last! u.Workspace = u.SetupWorkspace(d) // important that this is last!
u.PlayButton = ui.NewButton("Play", ui.NewLabel(ui.Label{ u.PlayButton = ui.NewButton("Play", ui.NewLabel(ui.Label{
@ -147,16 +156,31 @@ func (u *EditorUI) Resized(d *Doodle) {
u.Palette.Compute(d.Engine) u.Palette.Compute(d.Engine)
} }
var innerHeight = int32(u.d.height) - u.MenuBar.Size().H - u.StatusBar.Size().H
// Tool Bar.
{
u.ToolBar.Configure(ui.Config{
Width: toolbarWidth,
Height: innerHeight,
})
u.ToolBar.MoveTo(render.NewPoint(
0,
u.MenuBar.BoxSize().H,
))
u.ToolBar.Compute(d.Engine)
}
// Position the workspace around with the other widgets. // Position the workspace around with the other widgets.
{ {
frame := u.Workspace frame := u.Workspace
frame.MoveTo(render.NewPoint( frame.MoveTo(render.NewPoint(
0, u.ToolBar.Size().W,
u.MenuBar.Size().H, u.MenuBar.Size().H,
)) ))
frame.Resize(render.NewRect( frame.Resize(render.NewRect(
int32(d.width)-u.Palette.Size().W, int32(d.width)-u.Palette.Size().W-u.ToolBar.Size().W,
int32(d.height)-u.MenuBar.Size().H-u.StatusBar.Size().H, int32(d.height)-u.MenuBar.Size().H-u.StatusBar.Size().H,
)) ))
frame.Compute(d.Engine) frame.Compute(d.Engine)
@ -227,9 +251,14 @@ func (u *EditorUI) Loop(ev *events.State) error {
} }
// Recompute widgets. // Recompute widgets.
// Explanation: if I don't, the UI packing algorithm somehow causes widgets
// to creep away every frame and fly right off the screen. For example the
// ToolBar's buttons would start packed at the top of the bar but then just
// move themselves every frame downward and away.
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.ToolBar.Compute(u.d.Engine)
// Only forward events to the Canvas if the UI hasn't stopped them. // Only forward events to the Canvas if the UI hasn't stopped them.
if !stopPropagation { if !stopPropagation {
@ -249,6 +278,7 @@ 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.ToolBar.Present(e, u.ToolBar.Point())
u.Workspace.Present(e, u.Workspace.Point()) u.Workspace.Present(e, u.Workspace.Point())
u.PlayButton.Present(e, u.PlayButton.Point()) u.PlayButton.Present(e, u.PlayButton.Point())

View File

@ -51,35 +51,6 @@ func (u *EditorUI) setupDoodadFrame(e render.Engine, window *ui.Window) (*ui.Fra
frame.SetBackground(render.RGBA(0, 153, 255, 153)) frame.SetBackground(render.RGBA(0, 153, 255, 153))
// Toolbar on top of the Doodad panel.
toolbar := ui.NewFrame("Doodad Palette Toolbar")
toolbar.Configure(ui.Config{
Background: render.Grey,
BorderSize: 2,
BorderStyle: ui.BorderRaised,
Height: 24,
})
{
// Link button.
linkButton := ui.NewButton("Link", ui.NewLabel(ui.Label{
Text: "Link Doodads",
}))
linkButton.Handle(ui.Click, func(p render.Point) {
u.Canvas.LinkStart()
u.d.Flash("Click on the first Doodad to link to another one.")
})
u.Supervisor.Add(linkButton)
toolbar.Pack(linkButton, ui.Pack{
Anchor: ui.N,
FillX: true,
})
}
frame.Pack(toolbar, ui.Pack{
Anchor: ui.N,
Fill: true,
})
// Pager buttons on top of the doodad list. // Pager buttons on top of the doodad list.
pager := ui.NewFrame("Doodad Pager") pager := ui.NewFrame("Doodad Pager")
pager.SetBackground(render.RGBA(255, 0, 0, 20)) // TODO: if I don't set a background color, pager.SetBackground(render.RGBA(255, 0, 0, 20)) // TODO: if I don't set a background color,

View File

@ -4,8 +4,6 @@ import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/apps/doodle/lib/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/drawtool"
"git.kirsle.net/apps/doodle/pkg/enum"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"
) )
@ -19,46 +17,6 @@ func (u *EditorUI) SetupPalette(d *Doodle) *ui.Window {
BorderColor: balance.WindowBorder, BorderColor: balance.WindowBorder,
}) })
// Frame that holds the tab buttons in Level Edit mode.
tabFrame := ui.NewFrame("Palette Tabs")
for _, name := range []string{"Palette", "Doodads"} {
if u.paletteTab == "" {
u.paletteTab = name
}
tab := ui.NewRadioButton("Palette Tab", &u.paletteTab, name, ui.NewLabel(ui.Label{
Text: name,
}))
tab.Handle(ui.Click, func(p render.Point) {
if u.paletteTab == "Palette" {
u.Canvas.Tool = drawtool.PencilTool
u.PaletteTab.Show()
u.DoodadTab.Hide()
} else {
u.Canvas.Tool = drawtool.ActorTool
u.PaletteTab.Hide()
u.DoodadTab.Show()
}
window.Compute(d.Engine)
})
u.Supervisor.Add(tab)
tabFrame.Pack(tab, ui.Pack{
Anchor: ui.W,
Fill: true,
Expand: true,
})
}
window.Pack(tabFrame, ui.Pack{
Anchor: ui.N,
Fill: true,
PadY: 4,
})
// Only show the tab frame in Level drawing mode!
if u.Scene.DrawingType != enum.LevelDrawing {
tabFrame.Hide()
}
// Doodad frame. // Doodad frame.
{ {
frame, err := u.setupDoodadFrame(d.Engine, window) frame, err := u.setupDoodadFrame(d.Engine, window)

123
pkg/editor_ui_toolbar.go Normal file
View File

@ -0,0 +1,123 @@
package doodle
import (
"git.kirsle.net/apps/doodle/lib/render"
"git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/drawtool"
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/apps/doodle/pkg/sprites"
)
// Width of the toolbar frame.
var toolbarWidth int32 = 44 // 38px button (32px sprite + borders) + padding
var toolbarSpriteSize int32 = 32 // 32x32 sprites.
// SetupToolbar configures the UI for the Tools panel.
func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
frame := ui.NewFrame("Tool Bar")
frame.Resize(render.NewRect(toolbarWidth, 100))
frame.Configure(ui.Config{
BorderSize: 2,
BorderStyle: ui.BorderRaised,
Background: render.Grey,
})
btnFrame := ui.NewFrame("Tool Buttons")
frame.Pack(btnFrame, ui.Pack{
Anchor: ui.N,
})
// Buttons.
var buttons = []struct {
Value string
Icon string
Click func()
}{
{
Value: drawtool.PencilTool.String(),
Icon: "assets/sprites/pencil-tool.png",
Click: func() {
u.Canvas.Tool = drawtool.PencilTool
u.DoodadTab.Hide()
u.PaletteTab.Show()
d.Flash("Pencil Tool selected.")
},
},
{
Value: drawtool.LineTool.String(),
Icon: "assets/sprites/line-tool.png",
Click: func() {
u.Canvas.Tool = drawtool.LineTool
u.DoodadTab.Hide()
u.PaletteTab.Show()
d.Flash("Line Tool selected.")
},
},
{
Value: drawtool.RectTool.String(),
Icon: "assets/sprites/rect-tool.png",
Click: func() {
u.Canvas.Tool = drawtool.RectTool
u.DoodadTab.Hide()
u.PaletteTab.Show()
d.Flash("Rectangle Tool selected.")
},
},
{
Value: drawtool.ActorTool.String(),
Icon: "assets/sprites/actor-tool.png",
Click: func() {
u.Canvas.Tool = drawtool.ActorTool
u.PaletteTab.Hide()
u.DoodadTab.Show()
d.Flash("Actor Tool selected. Drag a Doodad from the drawer into your level.")
},
},
{
Value: drawtool.LinkTool.String(),
Icon: "assets/sprites/link-tool.png",
Click: func() {
u.Canvas.Tool = drawtool.LinkTool
u.PaletteTab.Hide()
u.DoodadTab.Show()
d.Flash("Link Tool selected. Click a doodad in your level to link it to another.")
},
},
}
for _, button := range buttons {
button := button
image, err := sprites.LoadImage(d.Engine, button.Icon)
if err != nil {
panic(err)
}
btn := ui.NewRadioButton(
button.Value,
u.activeTool,
button.Value,
image,
)
var btnSize int32 = btn.BoxThickness(2) + toolbarSpriteSize
log.Info("BtnSize: %d", btnSize)
btn.Resize(render.NewRect(btnSize, btnSize))
btn.Handle(ui.Click, func(p render.Point) {
button.Click()
})
u.Supervisor.Add(btn)
btnFrame.Pack(btn, ui.Pack{
Anchor: ui.N,
PadY: 2,
})
}
frame.Compute(d.Engine)
return frame
}

View File

@ -326,7 +326,7 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
// Otherwise, just draw flashed messages. // Otherwise, just draw flashed messages.
valid := false // Did we actually draw any? valid := false // Did we actually draw any?
outputY := int32(d.height - (lineHeight * 2)) outputY := int32(d.height - (lineHeight * 2) - 16)
for i := len(s.Flashes); i > 0; i-- { for i := len(s.Flashes); i > 0; i-- {
flash := s.Flashes[i-1] flash := s.Flashes[i-1]
if d.ticks >= flash.Expires { if d.ticks >= flash.Expires {
@ -342,7 +342,7 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
Shadow: render.Black, Shadow: render.Black,
}, },
render.Point{ render.Point{
X: balance.ShellPadding, X: balance.ShellPadding + toolbarWidth,
Y: outputY, Y: outputY,
}, },
) )

61
pkg/sprites/sprites.go Normal file
View File

@ -0,0 +1,61 @@
package sprites
import (
"bytes"
"errors"
"image/png"
"io/ioutil"
"os"
"git.kirsle.net/apps/doodle/lib/render"
"git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/bindata"
"git.kirsle.net/apps/doodle/pkg/log"
)
// LoadImage loads a sprite as a ui.Image object. It checks Doodle's embedded
// bindata, then the filesystem before erroring out.
//
// NOTE: only .png images supported as of now. TODO
func LoadImage(e render.Engine, filename string) (*ui.Image, error) {
// Try the bindata first.
if data, err := bindata.Asset(filename); err == nil {
log.Debug("sprites.LoadImage: %s from bindata", filename)
img, err := png.Decode(bytes.NewBuffer(data))
if err != nil {
return nil, err
}
tex, err := e.StoreTexture(filename, img)
if err != nil {
return nil, err
}
return ui.ImageFromTexture(tex), nil
}
// Then try the file system.
if _, err := os.Stat(filename); !os.IsNotExist(err) {
log.Debug("sprites.LoadImage: %s from filesystem", filename)
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
img, err := png.Decode(bytes.NewBuffer(data))
if err != nil {
return nil, err
}
tex, err := e.StoreTexture(filename, img)
if err != nil {
return nil, err
}
return ui.ImageFromTexture(tex), nil
}
return nil, errors.New("no such sprite found")
}