From 22440f436b8a2fdd6aed193cdb5577000a35771b Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Wed, 3 Jul 2019 21:55:15 -0700 Subject: [PATCH] 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%. --- pkg/doodads/fmt_readwrite.go | 3 + pkg/editor_scene.go | 3 + pkg/editor_ui.go | 14 ++- pkg/editor_ui_doodad.go | 170 ++++++++++++++++++++++++++--------- pkg/editor_ui_toolbar.go | 2 +- 5 files changed, 145 insertions(+), 47 deletions(-) diff --git a/pkg/doodads/fmt_readwrite.go b/pkg/doodads/fmt_readwrite.go index 1666350..6406e7f 100644 --- a/pkg/doodads/fmt_readwrite.go +++ b/pkg/doodads/fmt_readwrite.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "runtime" + "sort" "strings" "git.kirsle.net/apps/doodle/pkg/bindata" @@ -57,6 +58,8 @@ func ListDoodads() ([]string, error) { } } + sort.Strings(result) + return result, err } diff --git a/pkg/editor_scene.go b/pkg/editor_scene.go index 3daab4b..c50f8f0 100644 --- a/pkg/editor_scene.go +++ b/pkg/editor_scene.go @@ -174,12 +174,15 @@ func (s *EditorScene) Loop(d *Doodle, ev *events.State) error { case "l": d.Flash("Line Tool selected.") s.UI.Canvas.Tool = drawtool.LineTool + s.UI.activeTool = s.UI.Canvas.Tool.String() case "f": d.Flash("Pencil Tool selected.") s.UI.Canvas.Tool = drawtool.PencilTool + s.UI.activeTool = s.UI.Canvas.Tool.String() case "r": d.Flash("Rectangle Tool selected.") s.UI.Canvas.Tool = drawtool.RectTool + s.UI.activeTool = s.UI.Canvas.Tool.String() } return nil diff --git a/pkg/editor_ui.go b/pkg/editor_ui.go index 04e9d22..258437d 100644 --- a/pkg/editor_ui.go +++ b/pkg/editor_ui.go @@ -49,8 +49,15 @@ type EditorUI struct { PaletteTab *ui.Frame DoodadTab *ui.Frame + // Doodad Palette window variables. + doodadSkip int + doodadRows []*ui.Frame + doodadPager *ui.Frame + doodadButtonSize int32 + doodadScroller *ui.Frame + // ToolBar window. - activeTool *string + activeTool string // Draggable Doodad canvas. DraggableActor *DraggableActor @@ -72,8 +79,7 @@ func NewEditorUI(d *Doodle, s *EditorScene) *EditorUI { } // Default tool in the toolbox. - activeTool := drawtool.PencilTool.String() - u.activeTool = &activeTool + u.activeTool = drawtool.PencilTool.String() // Bind the StatusBoxes arrays to the text variables. u.StatusBoxes = []*string{ @@ -154,6 +160,8 @@ func (u *EditorUI) Resized(d *Doodle) { u.MenuBar.BoxSize().H, )) u.Palette.Compute(d.Engine) + + u.scrollDoodadFrame(0) } var innerHeight = int32(u.d.height) - u.MenuBar.Size().H - u.StatusBar.Size().H diff --git a/pkg/editor_ui_doodad.go b/pkg/editor_ui_doodad.go index 790b628..29fd806 100644 --- a/pkg/editor_ui_doodad.go +++ b/pkg/editor_ui_doodad.go @@ -49,8 +49,6 @@ func (u *EditorUI) setupDoodadFrame(e render.Engine, window *ui.Window) (*ui.Fra perRow = balance.UIDoodadsPerRow ) - frame.SetBackground(render.RGBA(0, 153, 255, 153)) - // Pager buttons on top of the doodad list. pager := ui.NewFrame("Doodad Pager") 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{ Text: "<", + Font: balance.MenuFont, })) + leftBtn.Handle(ui.Click, func(p render.Point) { + u.scrollDoodadFrame(-1) + }) u.Supervisor.Add(leftBtn) pager.Pack(leftBtn, ui.Pack{ Anchor: ui.W, }) - pageLabel := ui.NewLabel(ui.Label{ - Text: " Page 1 of 20", + scroller := ui.NewFrame("Doodad Scroll Progressbar") + 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, - Expand: true, }) + u.doodadScroller = scroller rightBtn := ui.NewButton("Next Page", ui.NewLabel(ui.Label{ Text: ">", + Font: balance.MenuFont, })) + rightBtn.Handle(ui.Click, func(p render.Point) { + u.scrollDoodadFrame(1) + }) u.Supervisor.Add(rightBtn) pager.Pack(rightBtn, ui.Pack{ - Anchor: ui.W, + Anchor: ui.E, }) } + u.doodadPager = pager frame.Pack(pager, ui.Pack{ Anchor: ui.N, 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) + u.doodadButtonSize = buttonSize // Draw the doodad buttons in a grid `perRow` buttons wide. var ( 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 { + filename := filename + if row == nil || i%perRow == 0 { rowCount++ row = ui.NewFrame(fmt.Sprintf("Doodad Row %d", rowCount)) row.SetBackground(balance.WindowBackground) + btnRows = append(btnRows, row) frame.Pack(row, ui.Pack{ Anchor: ui.N, Fill: true, }) } - func(filename string) { - doodad, err := doodads.LoadFile(filename) - if err != nil { - log.Error(err.Error()) - doodad = doodads.New(balance.DoodadSize) - } + doodad, err := doodads.LoadFile(filename) + if err != nil { + log.Error(err.Error()) + doodad = doodads.New(balance.DoodadSize) + } - can := uix.NewCanvas(int(buttonSize), true) - can.Name = filename - can.SetBackground(render.White) - can.LoadDoodad(doodad) + can := uix.NewCanvas(int(buttonSize), true) + can.Name = filename + can.SetBackground(render.White) + can.LoadDoodad(doodad) - btn := ui.NewButton(filename, can) - btn.Resize(render.NewRect( - buttonSize-2, // TODO: without the -2 the button border - buttonSize-2, // rests on top of the window border. - )) - row.Pack(btn, ui.Pack{ - Anchor: ui.W, - }) + btn := ui.NewButton(filename, can) + btn.Resize(render.NewRect( + buttonSize-2, // TODO: without the -2 the button border + buttonSize-2, // rests on top of the window border. + )) + row.Pack(btn, ui.Pack{ + Anchor: ui.W, + }) - // Begin the drag event to grab this Doodad. - // NOTE: The drag target is the EditorUI.Canvas in - // editor_ui.go#SetupCanvas() - btn.Handle(ui.MouseDown, func(e render.Point) { - log.Warn("MouseDown on doodad %s (%s)", doodad.Filename, doodad.Title) - u.startDragActor(doodad) - }) - u.Supervisor.Add(btn) + // Begin the drag event to grab this Doodad. + // NOTE: The drag target is the EditorUI.Canvas in + // editor_ui.go#SetupCanvas() + btn.Handle(ui.MouseDown, func(e render.Point) { + log.Warn("MouseDown on doodad %s (%s)", doodad.Filename, doodad.Title) + u.startDragActor(doodad) + }) + u.Supervisor.Add(btn) - // Resize the canvas to fill the button interior. - btnSize := btn.Size() - can.Resize(render.NewRect( - btnSize.W-btn.BoxThickness(2), - btnSize.H-btn.BoxThickness(2), - ), - ) + // Resize the canvas to fill the button interior. + btnSize := btn.Size() + can.Resize(render.NewRect( + btnSize.W-btn.BoxThickness(2), + btnSize.H-btn.BoxThickness(2), + ), + ) - btn.Compute(e) - }(filename) + btn.Compute(e) } + u.doodadRows = btnRows + u.scrollDoodadFrame(0) + 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) + +} diff --git a/pkg/editor_ui_toolbar.go b/pkg/editor_ui_toolbar.go index 5fe59d1..05f5f94 100644 --- a/pkg/editor_ui_toolbar.go +++ b/pkg/editor_ui_toolbar.go @@ -97,7 +97,7 @@ func (u *EditorUI) SetupToolbar(d *Doodle) *ui.Frame { btn := ui.NewRadioButton( button.Value, - u.activeTool, + &u.activeTool, button.Value, image, )