Noah Petherbridge
44122d4130
UI improvements specifically for mobile (running the game with the `-w mobile` or `-w landscape` options) screen sizes. * Rework the Settings window to be mobile friendly to landscape oriented screens (`doodle -w landscape`) and migrate Options tab to magicform. * The toolbar in the Editor will be a single column of buttons on small screens, such as `-w mobile` (375x812) portrait mode smartphone. On larger screens the toolbar shows in two columns of buttons. * Fix tooltips not drawing on top. * Centralize the hard-coded references to specific font filenames * Add cheat code: `test load screen` to bring a sample loading screen up for a few seconds. It needs improvement on `-w landscape`
388 lines
8.7 KiB
Go
388 lines
8.7 KiB
Go
package doodle
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"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/sprites"
|
|
"git.kirsle.net/apps/doodle/pkg/usercfg"
|
|
"git.kirsle.net/go/render"
|
|
"git.kirsle.net/go/ui"
|
|
"git.kirsle.net/go/ui/style"
|
|
)
|
|
|
|
// Global toolbarWidth, TODO: editor_ui.go wants it
|
|
var toolbarWidth int
|
|
|
|
// SetupToolbar configures the UI for the Tools panel.
|
|
func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
|
|
// Horizontal toolbar instead of vertical?
|
|
var (
|
|
toolbarSpriteSize = 24 // size of sprite images
|
|
frameSize render.Rect
|
|
isHoz = usercfg.Current.HorizontalToolbars
|
|
buttonsPerRow = 2
|
|
packAlign = ui.N
|
|
tooltipEdge = ui.Right
|
|
btnRowPack = ui.Pack{
|
|
Side: packAlign,
|
|
PadY: 1,
|
|
Fill: true,
|
|
}
|
|
btnPack = ui.Pack{
|
|
Side: ui.W,
|
|
PadX: 1,
|
|
}
|
|
)
|
|
if isHoz {
|
|
packAlign = ui.W
|
|
tooltipEdge = ui.Bottom
|
|
btnRowPack = ui.Pack{
|
|
Side: packAlign,
|
|
PadX: 2,
|
|
}
|
|
btnPack = ui.Pack{
|
|
Side: ui.N,
|
|
PadY: 1,
|
|
}
|
|
}
|
|
|
|
// Button Layout Controls:
|
|
// We can draw 2 buttons per row, but for very small screens
|
|
// e.g. mobile in portrait orientation, draw 1 button per row.
|
|
buttonsPerRow = 1
|
|
if isHoz {
|
|
if d.width < enum.ScreenWidthSmall {
|
|
// Narrow screens
|
|
buttonsPerRow = 2
|
|
}
|
|
} else {
|
|
if d.width >= enum.ScreenWidthSmall {
|
|
// Screen wider than 600px = can spare room for 2 buttons per row.
|
|
buttonsPerRow = 2
|
|
}
|
|
}
|
|
|
|
// Compute toolbar size to accommodate all buttons (+10 for borders/padding)
|
|
toolbarWidth = buttonsPerRow * (toolbarSpriteSize + 10)
|
|
frameSize = render.NewRect(toolbarWidth, 100)
|
|
|
|
frame := ui.NewFrame("Tool Bar")
|
|
frame.Resize(frameSize)
|
|
frame.Configure(ui.Config{
|
|
BorderSize: 2,
|
|
BorderStyle: ui.BorderRaised,
|
|
Background: render.Grey,
|
|
})
|
|
|
|
btnFrame := ui.NewFrame("Tool Buttons")
|
|
frame.Pack(btnFrame, ui.Pack{
|
|
Side: packAlign,
|
|
})
|
|
|
|
// Buttons.
|
|
var buttons = []struct {
|
|
Value string
|
|
Icon string
|
|
Tooltip string
|
|
Style *style.Button
|
|
Click func()
|
|
|
|
// Optional fields.
|
|
NoDoodad bool // tool not available for Doodad editing (Levels only)
|
|
}{
|
|
{
|
|
Value: drawtool.PanTool.String(),
|
|
Icon: "assets/sprites/pan-tool.png",
|
|
Tooltip: "Pan Tool",
|
|
Click: func() {
|
|
u.Canvas.Tool = drawtool.PanTool
|
|
d.Flash("Pan Tool selected.")
|
|
},
|
|
},
|
|
|
|
{
|
|
Value: drawtool.PencilTool.String(),
|
|
Icon: "assets/sprites/pencil-tool.png",
|
|
Tooltip: "Pencil Tool",
|
|
Click: func() {
|
|
u.Canvas.Tool = drawtool.PencilTool
|
|
d.Flash("Pencil Tool selected.")
|
|
},
|
|
},
|
|
|
|
{
|
|
Value: drawtool.LineTool.String(),
|
|
Icon: "assets/sprites/line-tool.png",
|
|
Tooltip: "Line Tool",
|
|
Click: func() {
|
|
u.Canvas.Tool = drawtool.LineTool
|
|
d.Flash("Line Tool selected.")
|
|
},
|
|
},
|
|
|
|
{
|
|
Value: drawtool.RectTool.String(),
|
|
Icon: "assets/sprites/rect-tool.png",
|
|
Tooltip: "Rectangle Tool",
|
|
Click: func() {
|
|
u.Canvas.Tool = drawtool.RectTool
|
|
d.Flash("Rectangle Tool selected.")
|
|
},
|
|
},
|
|
|
|
{
|
|
Value: drawtool.EllipseTool.String(),
|
|
Icon: "assets/sprites/ellipse-tool.png",
|
|
Tooltip: "Ellipse Tool",
|
|
Click: func() {
|
|
u.Canvas.Tool = drawtool.EllipseTool
|
|
d.Flash("Ellipse Tool selected.")
|
|
},
|
|
},
|
|
|
|
{
|
|
Value: drawtool.TextTool.String(),
|
|
Icon: "assets/sprites/text-tool.png",
|
|
Tooltip: "Text Tool",
|
|
Click: func() {
|
|
u.Canvas.Tool = drawtool.TextTool
|
|
u.OpenTextTool()
|
|
d.Flash("Text Tool selected.")
|
|
},
|
|
},
|
|
|
|
{
|
|
Value: drawtool.ActorTool.String(),
|
|
Icon: "assets/sprites/actor-tool.png",
|
|
Tooltip: "Doodad Tool\nDrag-and-drop objects into your map",
|
|
NoDoodad: true,
|
|
Style: &balance.ButtonBabyBlue,
|
|
Click: func() {
|
|
u.Canvas.Tool = drawtool.ActorTool
|
|
u.OpenDoodadDropper()
|
|
d.Flash("Actor Tool selected. Drag a Doodad from the drawer into your level.")
|
|
},
|
|
},
|
|
|
|
{
|
|
Value: drawtool.LinkTool.String(),
|
|
Icon: "assets/sprites/link-tool.png",
|
|
Tooltip: "Link Tool\nConnect doodads to each other",
|
|
Style: &balance.ButtonPink,
|
|
NoDoodad: true,
|
|
Click: func() {
|
|
u.Canvas.Tool = drawtool.LinkTool
|
|
d.Flash("Link Tool selected. Click a doodad in your level to link it to another.")
|
|
},
|
|
},
|
|
|
|
{
|
|
Value: drawtool.EraserTool.String(),
|
|
Icon: "assets/sprites/eraser-tool.png",
|
|
Tooltip: "Eraser Tool",
|
|
Style: &balance.ButtonLightRed,
|
|
Click: func() {
|
|
u.Canvas.Tool = drawtool.EraserTool
|
|
|
|
// Set the brush size within range for the eraser.
|
|
if u.Canvas.BrushSize < balance.DefaultEraserBrushSize {
|
|
u.Canvas.BrushSize = balance.DefaultEraserBrushSize
|
|
} else if u.Canvas.BrushSize > balance.MaxEraserBrushSize {
|
|
u.Canvas.BrushSize = balance.MaxEraserBrushSize
|
|
}
|
|
|
|
d.Flash("Eraser Tool selected.")
|
|
},
|
|
},
|
|
}
|
|
|
|
// Arrange the buttons 2x2.
|
|
var btnRow *ui.Frame
|
|
for i, button := range buttons {
|
|
button := button
|
|
if button.NoDoodad && u.Scene.DrawingType == enum.DoodadDrawing {
|
|
continue
|
|
}
|
|
|
|
if buttonsPerRow == 1 || i%buttonsPerRow == 0 {
|
|
btnRow = ui.NewFrame(fmt.Sprintf("Button Row %d", i))
|
|
btnFrame.Pack(btnRow, btnRowPack)
|
|
}
|
|
|
|
image, err := sprites.LoadImage(d.Engine, button.Icon)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
btn := ui.NewRadioButton(
|
|
button.Value,
|
|
&u.activeTool,
|
|
button.Value,
|
|
image,
|
|
)
|
|
if button.Style != nil {
|
|
btn.SetStyle(button.Style)
|
|
}
|
|
|
|
var btnSize = btn.BoxThickness(2) + toolbarSpriteSize
|
|
btn.SetBorderSize(1)
|
|
btn.Resize(render.NewRect(btnSize, btnSize))
|
|
|
|
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
|
button.Click()
|
|
return nil
|
|
})
|
|
u.Supervisor.Add(btn)
|
|
|
|
tt := ui.NewTooltip(btn, ui.Tooltip{
|
|
Text: button.Tooltip,
|
|
Edge: tooltipEdge,
|
|
})
|
|
tt.Supervise(u.Supervisor)
|
|
|
|
btnRow.Pack(btn, btnPack)
|
|
}
|
|
|
|
// Doodad Editor: show the Layers button.
|
|
if u.Scene.DrawingType == enum.DoodadDrawing {
|
|
btn := ui.NewButton("Layers Button", ui.NewLabel(ui.Label{
|
|
Text: "Lyr.",
|
|
Font: balance.MenuFont,
|
|
}))
|
|
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
|
u.OpenLayersWindow()
|
|
return nil
|
|
})
|
|
u.Supervisor.Add(btn)
|
|
btnFrame.Pack(btn, ui.Pack{
|
|
Side: packAlign,
|
|
PadY: 2,
|
|
})
|
|
}
|
|
|
|
// Spacer frame.
|
|
frame.Pack(ui.NewFrame("spacer"), ui.Pack{
|
|
Side: packAlign,
|
|
PadY: 8,
|
|
})
|
|
|
|
//////////////
|
|
// "Brush Size" label
|
|
bsFrame := ui.NewFrame("Brush Size Frame")
|
|
frame.Pack(bsFrame, ui.Pack{
|
|
Side: packAlign,
|
|
})
|
|
|
|
bsLabel := ui.NewLabel(ui.Label{
|
|
Text: "Size:",
|
|
Font: balance.SmallFont,
|
|
})
|
|
bsFrame.Pack(bsLabel, ui.Pack{
|
|
Side: ui.N,
|
|
})
|
|
|
|
ui.NewTooltip(bsLabel, ui.Tooltip{
|
|
Text: "Set the line thickness for drawing",
|
|
Edge: tooltipEdge,
|
|
})
|
|
u.Supervisor.Add(bsLabel)
|
|
|
|
sizeLabel := ui.NewLabel(ui.Label{
|
|
IntVariable: &u.Canvas.BrushSize,
|
|
Font: balance.SmallFont,
|
|
})
|
|
sizeLabel.Configure(ui.Config{
|
|
BorderSize: 1,
|
|
BorderStyle: ui.BorderSunken,
|
|
Background: render.Grey,
|
|
})
|
|
bsFrame.Pack(sizeLabel, ui.Pack{
|
|
Side: ui.N,
|
|
// FillX: true,
|
|
PadY: 0,
|
|
})
|
|
|
|
// Brush Size widget
|
|
{
|
|
sizeFrame := ui.NewFrame("Brush Size Frame")
|
|
frame.Pack(sizeFrame, ui.Pack{
|
|
Side: packAlign,
|
|
PadY: 0,
|
|
})
|
|
|
|
sizeBtnFrame := ui.NewFrame("Size Increment Button Frame")
|
|
sizeFrame.Pack(sizeBtnFrame, ui.Pack{
|
|
Side: ui.N,
|
|
FillX: true,
|
|
})
|
|
|
|
var incButtons = []struct {
|
|
Label string
|
|
F func()
|
|
}{
|
|
{
|
|
Label: "-",
|
|
F: func() {
|
|
// Select next smaller brush size.
|
|
for i := len(balance.BrushSizeOptions) - 1; i >= 0; i-- {
|
|
if balance.BrushSizeOptions[i] < u.Canvas.BrushSize {
|
|
u.Canvas.BrushSize = balance.BrushSizeOptions[i]
|
|
break
|
|
}
|
|
}
|
|
},
|
|
},
|
|
{
|
|
Label: "+",
|
|
F: func() {
|
|
// Select next bigger brush size.
|
|
for _, size := range balance.BrushSizeOptions {
|
|
if size > u.Canvas.BrushSize {
|
|
u.Canvas.BrushSize = size
|
|
break
|
|
}
|
|
}
|
|
|
|
// Limit the eraser brush size, too big and it's slow because
|
|
// the eraser has to scan and remember pixels to be able to
|
|
// Undo the erase and restore them.
|
|
if u.Canvas.Tool == drawtool.EraserTool && u.Canvas.BrushSize > balance.MaxEraserBrushSize {
|
|
u.Canvas.BrushSize = balance.MaxEraserBrushSize
|
|
}
|
|
},
|
|
},
|
|
}
|
|
for _, button := range incButtons {
|
|
button := button
|
|
btn := ui.NewButton("BrushSize"+button.Label, ui.NewLabel(ui.Label{
|
|
Text: button.Label,
|
|
Font: balance.SmallMonoFont,
|
|
}))
|
|
btn.SetBorderSize(1)
|
|
btn.Handle(ui.Click, func(ed ui.EventData) error {
|
|
button.F()
|
|
return nil
|
|
})
|
|
u.Supervisor.Add(btn)
|
|
|
|
// Which side to pack on?
|
|
var side = ui.W
|
|
if !isHoz && buttonsPerRow == 1 {
|
|
// Vertical layout w/ narrow one-button-per-row, the +-
|
|
// buttons stick out so stack them vertically.
|
|
side = ui.S
|
|
}
|
|
sizeBtnFrame.Pack(btn, ui.Pack{
|
|
Side: side,
|
|
})
|
|
}
|
|
}
|
|
|
|
frame.Compute(d.Engine)
|
|
|
|
return frame
|
|
}
|