Add Scrolling in the Doodad Palette Window

* Rudimentary scrolling shows a Left and Right button at the top of the
  Doodad Palette if your window is deemed not tall enough to contain all
  of the doodads.
* A "progress bar" is shown between the buttons indicating the
  percentage of your scroll down the doodad list. When you're able to
  see the final row of doodads, the progress bar is at 100%.
physics
Noah 2019-07-03 21:55:15 -07:00
parent 12d34517e9
commit 22440f436b
5 changed files with 145 additions and 47 deletions

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"runtime" "runtime"
"sort"
"strings" "strings"
"git.kirsle.net/apps/doodle/pkg/bindata" "git.kirsle.net/apps/doodle/pkg/bindata"
@ -57,6 +58,8 @@ func ListDoodads() ([]string, error) {
} }
} }
sort.Strings(result)
return result, err return result, err
} }

View File

@ -174,12 +174,15 @@ func (s *EditorScene) Loop(d *Doodle, ev *events.State) error {
case "l": case "l":
d.Flash("Line Tool selected.") d.Flash("Line Tool selected.")
s.UI.Canvas.Tool = drawtool.LineTool s.UI.Canvas.Tool = drawtool.LineTool
s.UI.activeTool = s.UI.Canvas.Tool.String()
case "f": case "f":
d.Flash("Pencil Tool selected.") d.Flash("Pencil Tool selected.")
s.UI.Canvas.Tool = drawtool.PencilTool s.UI.Canvas.Tool = drawtool.PencilTool
s.UI.activeTool = s.UI.Canvas.Tool.String()
case "r": case "r":
d.Flash("Rectangle Tool selected.") d.Flash("Rectangle Tool selected.")
s.UI.Canvas.Tool = drawtool.RectTool s.UI.Canvas.Tool = drawtool.RectTool
s.UI.activeTool = s.UI.Canvas.Tool.String()
} }
return nil return nil

View File

@ -49,8 +49,15 @@ type EditorUI struct {
PaletteTab *ui.Frame PaletteTab *ui.Frame
DoodadTab *ui.Frame DoodadTab *ui.Frame
// Doodad Palette window variables.
doodadSkip int
doodadRows []*ui.Frame
doodadPager *ui.Frame
doodadButtonSize int32
doodadScroller *ui.Frame
// ToolBar window. // ToolBar window.
activeTool *string activeTool string
// Draggable Doodad canvas. // Draggable Doodad canvas.
DraggableActor *DraggableActor DraggableActor *DraggableActor
@ -72,8 +79,7 @@ func NewEditorUI(d *Doodle, s *EditorScene) *EditorUI {
} }
// Default tool in the toolbox. // Default tool in the toolbox.
activeTool := drawtool.PencilTool.String() u.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{
@ -154,6 +160,8 @@ func (u *EditorUI) Resized(d *Doodle) {
u.MenuBar.BoxSize().H, u.MenuBar.BoxSize().H,
)) ))
u.Palette.Compute(d.Engine) u.Palette.Compute(d.Engine)
u.scrollDoodadFrame(0)
} }
var innerHeight = int32(u.d.height) - u.MenuBar.Size().H - u.StatusBar.Size().H var innerHeight = int32(u.d.height) - u.MenuBar.Size().H - u.StatusBar.Size().H

View File

@ -49,8 +49,6 @@ func (u *EditorUI) setupDoodadFrame(e render.Engine, window *ui.Window) (*ui.Fra
perRow = balance.UIDoodadsPerRow perRow = balance.UIDoodadsPerRow
) )
frame.SetBackground(render.RGBA(0, 153, 255, 153))
// 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,
@ -59,28 +57,40 @@ func (u *EditorUI) setupDoodadFrame(e render.Engine, window *ui.Window) (*ui.Fra
{ {
leftBtn := ui.NewButton("Prev Page", ui.NewLabel(ui.Label{ leftBtn := ui.NewButton("Prev Page", ui.NewLabel(ui.Label{
Text: "<", Text: "<",
Font: balance.MenuFont,
})) }))
leftBtn.Handle(ui.Click, func(p render.Point) {
u.scrollDoodadFrame(-1)
})
u.Supervisor.Add(leftBtn) u.Supervisor.Add(leftBtn)
pager.Pack(leftBtn, ui.Pack{ pager.Pack(leftBtn, ui.Pack{
Anchor: ui.W, Anchor: ui.W,
}) })
pageLabel := ui.NewLabel(ui.Label{ scroller := ui.NewFrame("Doodad Scroll Progressbar")
Text: " Page 1 of 20", scroller.Configure(ui.Config{
Width: 20,
Height: 20,
Background: render.RGBA(128, 128, 128, 128),
}) })
pager.Pack(pageLabel, ui.Pack{ pager.Pack(scroller, ui.Pack{
Anchor: ui.W, Anchor: ui.W,
Expand: true,
}) })
u.doodadScroller = scroller
rightBtn := ui.NewButton("Next Page", ui.NewLabel(ui.Label{ rightBtn := ui.NewButton("Next Page", ui.NewLabel(ui.Label{
Text: ">", Text: ">",
Font: balance.MenuFont,
})) }))
rightBtn.Handle(ui.Click, func(p render.Point) {
u.scrollDoodadFrame(1)
})
u.Supervisor.Add(rightBtn) u.Supervisor.Add(rightBtn)
pager.Pack(rightBtn, ui.Pack{ pager.Pack(rightBtn, ui.Pack{
Anchor: ui.W, Anchor: ui.E,
}) })
} }
u.doodadPager = pager
frame.Pack(pager, ui.Pack{ frame.Pack(pager, ui.Pack{
Anchor: ui.N, Anchor: ui.N,
Fill: true, Fill: true,
@ -96,64 +106,138 @@ func (u *EditorUI) setupDoodadFrame(e render.Engine, window *ui.Window) (*ui.Fra
} }
var buttonSize = (paletteWidth - window.BoxThickness(2)) / int32(perRow) var buttonSize = (paletteWidth - window.BoxThickness(2)) / int32(perRow)
u.doodadButtonSize = buttonSize
// Draw the doodad buttons in a grid `perRow` buttons wide. // Draw the doodad buttons in a grid `perRow` buttons wide.
var ( var (
row *ui.Frame row *ui.Frame
rowCount int // for labeling the ui.Frame for each row rowCount int // for labeling the ui.Frame for each row
btnRows = []*ui.Frame{} // Collect the row frames for the buttons.
) )
for i, filename := range doodadsAvailable { for i, filename := range doodadsAvailable {
filename := filename
if row == nil || i%perRow == 0 { if row == nil || i%perRow == 0 {
rowCount++ rowCount++
row = ui.NewFrame(fmt.Sprintf("Doodad Row %d", rowCount)) row = ui.NewFrame(fmt.Sprintf("Doodad Row %d", rowCount))
row.SetBackground(balance.WindowBackground) row.SetBackground(balance.WindowBackground)
btnRows = append(btnRows, row)
frame.Pack(row, ui.Pack{ frame.Pack(row, ui.Pack{
Anchor: ui.N, Anchor: ui.N,
Fill: true, Fill: true,
}) })
} }
func(filename string) { doodad, err := doodads.LoadFile(filename)
doodad, err := doodads.LoadFile(filename) if err != nil {
if err != nil { log.Error(err.Error())
log.Error(err.Error()) doodad = doodads.New(balance.DoodadSize)
doodad = doodads.New(balance.DoodadSize) }
}
can := uix.NewCanvas(int(buttonSize), true) can := uix.NewCanvas(int(buttonSize), true)
can.Name = filename can.Name = filename
can.SetBackground(render.White) can.SetBackground(render.White)
can.LoadDoodad(doodad) can.LoadDoodad(doodad)
btn := ui.NewButton(filename, can) btn := ui.NewButton(filename, can)
btn.Resize(render.NewRect( btn.Resize(render.NewRect(
buttonSize-2, // TODO: without the -2 the button border buttonSize-2, // TODO: without the -2 the button border
buttonSize-2, // rests on top of the window border. buttonSize-2, // rests on top of the window border.
)) ))
row.Pack(btn, ui.Pack{ row.Pack(btn, ui.Pack{
Anchor: ui.W, Anchor: ui.W,
}) })
// Begin the drag event to grab this Doodad. // Begin the drag event to grab this Doodad.
// NOTE: The drag target is the EditorUI.Canvas in // NOTE: The drag target is the EditorUI.Canvas in
// editor_ui.go#SetupCanvas() // editor_ui.go#SetupCanvas()
btn.Handle(ui.MouseDown, func(e render.Point) { btn.Handle(ui.MouseDown, func(e render.Point) {
log.Warn("MouseDown on doodad %s (%s)", doodad.Filename, doodad.Title) log.Warn("MouseDown on doodad %s (%s)", doodad.Filename, doodad.Title)
u.startDragActor(doodad) u.startDragActor(doodad)
}) })
u.Supervisor.Add(btn) u.Supervisor.Add(btn)
// Resize the canvas to fill the button interior. // Resize the canvas to fill the button interior.
btnSize := btn.Size() btnSize := btn.Size()
can.Resize(render.NewRect( can.Resize(render.NewRect(
btnSize.W-btn.BoxThickness(2), btnSize.W-btn.BoxThickness(2),
btnSize.H-btn.BoxThickness(2), btnSize.H-btn.BoxThickness(2),
), ),
) )
btn.Compute(e) btn.Compute(e)
}(filename)
} }
u.doodadRows = btnRows
u.scrollDoodadFrame(0)
return frame, nil return frame, nil
} }
// scrollDoodadFrame handles the Page Up/Down buttons to adjust the number of
// Doodads visible on screen.
//
// rows is the number of rows to scroll. Positive values mean scroll *down*
// the list.
func (u *EditorUI) scrollDoodadFrame(rows int) {
u.doodadSkip += rows
if u.doodadSkip < 0 {
u.doodadSkip = 0
}
log.Info("scrollDoodadFrame(%d): skip=%d", rows, u.doodadSkip)
// Calculate about how many rows we can see given our current window size.
var (
maxVisibleHeight = int32(u.d.height - 86)
calculatedHeight int32
rowsBefore int // count of rows hidden before
rowsVisible int
rowsAfter int // count of rows hidden after
rowsEstimated = maxVisibleHeight / u.doodadButtonSize // estimated number rows shown
maxSkip = ((len(u.doodadRows) * int(u.doodadButtonSize)) - int(u.doodadButtonSize*rowsEstimated)) / int(u.doodadButtonSize)
)
if maxSkip < 0 {
maxSkip = 0
}
log.Info("maxSkip = (%d * %d) - (%d * %d) = %d", len(u.doodadRows), u.doodadButtonSize, u.doodadButtonSize, rowsEstimated, maxSkip)
// log.Info("maxSkip: estimate=%d rows=%d - visible=%d => %d", rowsEstimated, len(u.doodadRows), rowsVisible, maxSkip)
if u.doodadSkip > maxSkip {
u.doodadSkip = maxSkip
}
// If the window is big enough to encompass all the doodads, don't show
// the pager toolbar, its just confusing.
if maxSkip == 0 {
u.doodadPager.Hide()
} else {
u.doodadPager.Show()
}
// Comb through the doodads and show/hide the relevant buttons.
for i, row := range u.doodadRows {
if i < u.doodadSkip {
row.Hide()
rowsBefore++
continue
}
calculatedHeight += u.doodadButtonSize
if calculatedHeight > maxVisibleHeight {
row.Hide()
rowsAfter++
} else {
row.Show()
rowsVisible++
}
}
var viewPercent = float64(rowsBefore+rowsVisible) / float64(len(u.doodadRows))
u.doodadScroller.Configure(ui.Config{
Width: int32(float64(paletteWidth-50) * viewPercent), // TODO: hacky magic number
})
log.Info("v%% = (%d + %d) / %d = %f", rowsBefore, rowsVisible, len(u.doodadRows), viewPercent)
}

View File

@ -97,7 +97,7 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame {
btn := ui.NewRadioButton( btn := ui.NewRadioButton(
button.Value, button.Value,
u.activeTool, &u.activeTool,
button.Value, button.Value,
image, image,
) )