Add Initial "Doodad Palette" UX
* Add a tab bar to the top of the Palette window that has two radiobuttons for "Palette" and "Doodads" * UI: add the concept of a Hidden() widget and the corresponding Hide() and Show() methods. Hidden widgets are skipped over when evaluating Frame packing, rendering, and event supervision. * The Palette Window in editor mode now displays one of two tabs: * Palette: the old color swatch palette now lives here. * Doodads: the new Doodad palette. * The Doodad Palette shows a grid of buttons (2 per row) showing the available Doodad drawings in the user's config folder. * The Doodad buttons act as radiobuttons for now and have no other effect. TODO will be making them react to drag-drop events. * UI: added a `Children()` method as the inverse of `Parent()` for container widgets (like Frame, Window and Button) to expose their children. The BaseWidget just returns an empty []Widget. * Console: added a `repl` command that keeps the dev console open and prefixes every command with `$` filled out -- for rapid JavaScript console evaluation.
This commit is contained in:
parent
f18dcf9c2c
commit
b67c4b67b2
|
@ -52,6 +52,9 @@ func (c Command) Run(d *Doodle) error {
|
|||
out, err := d.shell.js.Run(c.ArgsLiteral)
|
||||
d.Flash("%+v", out)
|
||||
return err
|
||||
case "repl":
|
||||
d.shell.Repl = true
|
||||
d.shell.Text = "$ "
|
||||
case "boolProp":
|
||||
return c.BoolProp(d)
|
||||
default:
|
||||
|
|
20
config.go
20
config.go
|
@ -2,6 +2,7 @@ package doodle
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
@ -69,6 +70,25 @@ func DoodadPath(filename string) string {
|
|||
return resolvePath(DoodadDirectory, filename, extDoodad)
|
||||
}
|
||||
|
||||
// ListDoodads returns a listing of all available doodads.
|
||||
func ListDoodads() ([]string, error) {
|
||||
var names []string
|
||||
|
||||
files, err := ioutil.ReadDir(DoodadDirectory)
|
||||
if err != nil {
|
||||
return names, err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
name := file.Name()
|
||||
if strings.HasSuffix(strings.ToLower(name), extDoodad) {
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// resolvePath is the inner logic for LevelPath and DoodadPath.
|
||||
func resolvePath(directory, filename, extension string) string {
|
||||
if strings.Contains(filename, "/") {
|
||||
|
|
123
editor_ui.go
123
editor_ui.go
|
@ -5,6 +5,7 @@ import (
|
|||
"strconv"
|
||||
|
||||
"git.kirsle.net/apps/doodle/balance"
|
||||
"git.kirsle.net/apps/doodle/doodads"
|
||||
"git.kirsle.net/apps/doodle/enum"
|
||||
"git.kirsle.net/apps/doodle/events"
|
||||
"git.kirsle.net/apps/doodle/level"
|
||||
|
@ -23,14 +24,22 @@ type EditorUI struct {
|
|||
StatusPaletteText string
|
||||
StatusFilenameText string
|
||||
selectedSwatch string // name of selected swatch in palette
|
||||
selectedDoodad string
|
||||
|
||||
// Widgets
|
||||
Supervisor *ui.Supervisor
|
||||
Canvas *uix.Canvas
|
||||
Workspace *ui.Frame
|
||||
MenuBar *ui.Frame
|
||||
Palette *ui.Window
|
||||
StatusBar *ui.Frame
|
||||
|
||||
// Palette window.
|
||||
Palette *ui.Window
|
||||
PaletteTab *ui.Frame
|
||||
DoodadTab *ui.Frame
|
||||
|
||||
// Palette variables.
|
||||
paletteTab string // selected tab, Palette or Doodads
|
||||
}
|
||||
|
||||
// NewEditorUI initializes the Editor UI.
|
||||
|
@ -268,11 +277,13 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.Frame {
|
|||
|
||||
// SetupPalette sets up the palette panel.
|
||||
func (u *EditorUI) SetupPalette(d *Doodle) *ui.Window {
|
||||
var paletteWidth int32 = 150
|
||||
|
||||
window := ui.NewWindow("Palette")
|
||||
window.ConfigureTitle(balance.TitleConfig)
|
||||
window.TitleBar().Font = balance.TitleFont
|
||||
window.Configure(ui.Config{
|
||||
Width: 150,
|
||||
Width: paletteWidth,
|
||||
Height: u.d.height - u.StatusBar.Size().H,
|
||||
Background: balance.WindowBackground,
|
||||
BorderColor: balance.WindowBorder,
|
||||
|
@ -282,6 +293,111 @@ func (u *EditorUI) SetupPalette(d *Doodle) *ui.Window {
|
|||
u.MenuBar.BoxSize().H,
|
||||
))
|
||||
|
||||
// Frame that holds the tab buttons in Level Edit mode.
|
||||
tabFrame := ui.NewFrame("Palette Tabs")
|
||||
if u.Scene.DrawingType != enum.LevelDrawing {
|
||||
// Don't show the tab bar except in Level Edit mode.
|
||||
tabFrame.Hide()
|
||||
}
|
||||
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.PaletteTab.Show()
|
||||
u.DoodadTab.Hide()
|
||||
} else {
|
||||
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,
|
||||
})
|
||||
|
||||
// Doodad frame.
|
||||
{
|
||||
u.DoodadTab = ui.NewFrame("Doodad Tab")
|
||||
u.DoodadTab.Hide()
|
||||
window.Pack(u.DoodadTab, ui.Pack{
|
||||
Anchor: ui.N,
|
||||
Fill: true,
|
||||
})
|
||||
|
||||
doodadsAvailable, err := ListDoodads()
|
||||
if err != nil {
|
||||
d.Flash("ListDoodads: %s", err)
|
||||
}
|
||||
|
||||
var buttonSize = (paletteWidth - window.BoxThickness(2)) / 2
|
||||
|
||||
// Draw the doodad buttons in a grid 2 wide.
|
||||
var row *ui.Frame
|
||||
for i, filename := range doodadsAvailable {
|
||||
si := fmt.Sprintf("%d", i)
|
||||
if row == nil || i%2 == 0 {
|
||||
row = ui.NewFrame("Doodad Row " + si)
|
||||
row.SetBackground(balance.WindowBackground)
|
||||
u.DoodadTab.Pack(row, ui.Pack{
|
||||
Anchor: ui.N,
|
||||
Fill: true,
|
||||
// Expand: true,
|
||||
})
|
||||
}
|
||||
|
||||
doodad, err := doodads.LoadJSON(DoodadPath(filename))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
doodad = doodads.New(balance.DoodadSize)
|
||||
}
|
||||
|
||||
can := uix.NewCanvas(int(buttonSize), true)
|
||||
can.LoadDoodad(doodad)
|
||||
btn := ui.NewRadioButton(filename, &u.selectedDoodad, si, can)
|
||||
btn.Resize(render.NewRect(
|
||||
buttonSize-2, // TODO: without the -2 the button border
|
||||
buttonSize-2, // rests on top of the window border.
|
||||
))
|
||||
u.Supervisor.Add(btn)
|
||||
row.Pack(btn, ui.Pack{
|
||||
Anchor: ui.W,
|
||||
})
|
||||
|
||||
// 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(d.Engine)
|
||||
}
|
||||
}
|
||||
|
||||
// Color Palette Frame.
|
||||
{
|
||||
u.PaletteTab = ui.NewFrame("Palette Tab")
|
||||
u.PaletteTab.SetBackground(balance.WindowBackground)
|
||||
window.Pack(u.PaletteTab, ui.Pack{
|
||||
Anchor: ui.N,
|
||||
Fill: true,
|
||||
})
|
||||
|
||||
// Handler function for the radio buttons being clicked.
|
||||
onClick := func(p render.Point) {
|
||||
name := u.selectedSwatch
|
||||
|
@ -307,13 +423,14 @@ func (u *EditorUI) SetupPalette(d *Doodle) *ui.Window {
|
|||
btn.Handle(ui.Click, onClick)
|
||||
u.Supervisor.Add(btn)
|
||||
|
||||
window.Pack(btn, ui.Pack{
|
||||
u.PaletteTab.Pack(btn, ui.Pack{
|
||||
Anchor: ui.N,
|
||||
Fill: true,
|
||||
PadY: 4,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return window
|
||||
}
|
||||
|
|
19
shell.go
19
shell.go
|
@ -8,6 +8,7 @@ import (
|
|||
"git.kirsle.net/apps/doodle/balance"
|
||||
"git.kirsle.net/apps/doodle/events"
|
||||
"git.kirsle.net/apps/doodle/render"
|
||||
"git.kirsle.net/apps/doodle/ui"
|
||||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
||||
|
@ -30,6 +31,7 @@ type Shell struct {
|
|||
|
||||
Open bool
|
||||
Prompt string
|
||||
Repl bool
|
||||
callback func(string) // for prompt answers only
|
||||
Text string
|
||||
History []string
|
||||
|
@ -75,6 +77,12 @@ func NewShell(d *Doodle) Shell {
|
|||
"RGBA": render.RGBA,
|
||||
"Point": render.NewPoint,
|
||||
"Rect": render.NewRect,
|
||||
"Tree": func(w ui.Widget) string {
|
||||
for _, row := range ui.WidgetTree(w) {
|
||||
d.Flash(row)
|
||||
}
|
||||
return ""
|
||||
},
|
||||
}
|
||||
for name, v := range bindings {
|
||||
err := s.js.Set(name, v)
|
||||
|
@ -90,6 +98,7 @@ func NewShell(d *Doodle) Shell {
|
|||
func (s *Shell) Close() {
|
||||
log.Debug("Shell: closing shell")
|
||||
s.Open = false
|
||||
s.Repl = false
|
||||
s.Prompt = ">"
|
||||
s.callback = nil
|
||||
s.Text = ""
|
||||
|
@ -100,6 +109,7 @@ func (s *Shell) Close() {
|
|||
// Execute a command in the shell.
|
||||
func (s *Shell) Execute(input string) {
|
||||
command := s.Parse(input)
|
||||
|
||||
if command.Raw != "" {
|
||||
s.Output = append(s.Output, s.Prompt+command.Raw)
|
||||
s.History = append(s.History, command.Raw)
|
||||
|
@ -123,7 +133,11 @@ func (s *Shell) Execute(input string) {
|
|||
}
|
||||
|
||||
// Reset the text buffer in the shell.
|
||||
if s.Repl {
|
||||
s.Text = "$ "
|
||||
} else {
|
||||
s.Text = ""
|
||||
}
|
||||
}
|
||||
|
||||
// Write a line of output text to the console.
|
||||
|
@ -197,7 +211,12 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
|
|||
return nil
|
||||
} else if ev.EnterKey.Read() || ev.EscapeKey.Read() {
|
||||
s.Execute(s.Text)
|
||||
|
||||
// Auto-close the console unless in REPL mode.
|
||||
if !s.Repl {
|
||||
s.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
} else if (ev.Up.Now || ev.Down.Now) && len(s.History) > 0 {
|
||||
// Paging through history.
|
||||
|
|
|
@ -56,6 +56,11 @@ func NewButton(name string, child Widget) *Button {
|
|||
return w
|
||||
}
|
||||
|
||||
// Children returns the button's child widget.
|
||||
func (w *Button) Children() []Widget {
|
||||
return []Widget{w.child}
|
||||
}
|
||||
|
||||
// Compute the size of the button.
|
||||
func (w *Button) Compute(e render.Engine) {
|
||||
// Compute the size of the inner widget first.
|
||||
|
@ -81,6 +86,10 @@ func (w *Button) SetText(text string) error {
|
|||
|
||||
// Present the button.
|
||||
func (w *Button) Present(e render.Engine, P render.Point) {
|
||||
if w.Hidden() {
|
||||
return
|
||||
}
|
||||
|
||||
w.Compute(e)
|
||||
w.MoveTo(P)
|
||||
var (
|
||||
|
|
23
ui/debug.go
Normal file
23
ui/debug.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package ui
|
||||
|
||||
import "strings"
|
||||
|
||||
// WidgetTree returns a string representing the tree of widgets starting
|
||||
// at a given widget.
|
||||
func WidgetTree(root Widget) []string {
|
||||
var crawl func(int, Widget) []string
|
||||
crawl = func(depth int, node Widget) []string {
|
||||
var (
|
||||
prefix = strings.Repeat(" ", depth)
|
||||
lines = []string{prefix + node.ID()}
|
||||
)
|
||||
|
||||
for _, child := range node.Children() {
|
||||
lines = append(lines, crawl(depth+1, child)...)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
return crawl(0, root)
|
||||
}
|
|
@ -39,6 +39,11 @@ func (w *Frame) Setup() {
|
|||
}
|
||||
}
|
||||
|
||||
// Children returns all of the child widgets.
|
||||
func (w *Frame) Children() []Widget {
|
||||
return w.widgets
|
||||
}
|
||||
|
||||
// Compute the size of the Frame.
|
||||
func (w *Frame) Compute(e render.Engine) {
|
||||
w.computePacked(e)
|
||||
|
@ -46,6 +51,10 @@ func (w *Frame) Compute(e render.Engine) {
|
|||
|
||||
// Present the Frame.
|
||||
func (w *Frame) Present(e render.Engine, P render.Point) {
|
||||
if w.Hidden() {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
S = w.Size()
|
||||
)
|
||||
|
|
108
ui/frame_pack.go
108
ui/frame_pack.go
|
@ -2,6 +2,58 @@ package ui
|
|||
|
||||
import "git.kirsle.net/apps/doodle/render"
|
||||
|
||||
// Pack provides configuration fields for Frame.Pack().
|
||||
type Pack struct {
|
||||
// Side of the parent to anchor the position to, like N, SE, W. Default
|
||||
// is Center.
|
||||
Anchor Anchor
|
||||
|
||||
// If the widget is smaller than its allocated space, grow the widget
|
||||
// to fill its space in the Frame.
|
||||
Fill bool
|
||||
FillX bool
|
||||
FillY bool
|
||||
|
||||
Padding int32 // Equal padding on X and Y.
|
||||
PadX int32
|
||||
PadY int32
|
||||
Expand bool // Widget should grow its allocated space to better fill the parent.
|
||||
}
|
||||
|
||||
// Pack a widget along a side of the frame.
|
||||
func (w *Frame) Pack(child Widget, config ...Pack) {
|
||||
var C Pack
|
||||
if len(config) > 0 {
|
||||
C = config[0]
|
||||
}
|
||||
|
||||
// Initialize the pack list for this anchor?
|
||||
if _, ok := w.packs[C.Anchor]; !ok {
|
||||
w.packs[C.Anchor] = []packedWidget{}
|
||||
}
|
||||
|
||||
// Padding: if the user only provided Padding add it to both
|
||||
// the X and Y value. If the user additionally provided the X
|
||||
// and Y value, it will add to the base padding as you'd expect.
|
||||
C.PadX += C.Padding
|
||||
C.PadY += C.Padding
|
||||
|
||||
// Fill: true implies both directions.
|
||||
if C.Fill {
|
||||
C.FillX = 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{
|
||||
widget: child,
|
||||
pack: C,
|
||||
})
|
||||
w.widgets = append(w.widgets, child)
|
||||
}
|
||||
|
||||
// computePacked processes all the Pack layout widgets in the Frame.
|
||||
func (w *Frame) computePacked(e render.Engine) {
|
||||
var (
|
||||
|
@ -46,6 +98,10 @@ func (w *Frame) computePacked(e render.Engine) {
|
|||
pack := packedWidget.pack
|
||||
child.Compute(e)
|
||||
|
||||
if child.Hidden() {
|
||||
continue
|
||||
}
|
||||
|
||||
x += pack.PadX
|
||||
y += pack.PadY
|
||||
|
||||
|
@ -187,24 +243,6 @@ func (w *Frame) computePacked(e render.Engine) {
|
|||
// }
|
||||
}
|
||||
|
||||
// Pack provides configuration fields for Frame.Pack().
|
||||
type Pack struct {
|
||||
// Side of the parent to anchor the position to, like N, SE, W. Default
|
||||
// is Center.
|
||||
Anchor Anchor
|
||||
|
||||
// If the widget is smaller than its allocated space, grow the widget
|
||||
// to fill its space in the Frame.
|
||||
Fill bool
|
||||
FillX bool
|
||||
FillY bool
|
||||
|
||||
Padding int32 // Equal padding on X and Y.
|
||||
PadX int32
|
||||
PadY int32
|
||||
Expand bool // Widget should grow its allocated space to better fill the parent.
|
||||
}
|
||||
|
||||
// Anchor is a cardinal direction.
|
||||
type Anchor uint8
|
||||
|
||||
|
@ -259,40 +297,6 @@ func (a Anchor) IsMiddle() bool {
|
|||
return a == Center || a == W || a == E
|
||||
}
|
||||
|
||||
// Pack a widget along a side of the frame.
|
||||
func (w *Frame) Pack(child Widget, config ...Pack) {
|
||||
var C Pack
|
||||
if len(config) > 0 {
|
||||
C = config[0]
|
||||
}
|
||||
|
||||
// Initialize the pack list for this anchor?
|
||||
if _, ok := w.packs[C.Anchor]; !ok {
|
||||
w.packs[C.Anchor] = []packedWidget{}
|
||||
}
|
||||
|
||||
// Padding: if the user only provided Padding add it to both
|
||||
// the X and Y value. If the user additionally provided the X
|
||||
// and Y value, it will add to the base padding as you'd expect.
|
||||
C.PadX += C.Padding
|
||||
C.PadY += C.Padding
|
||||
|
||||
// Fill: true implies both directions.
|
||||
if C.Fill {
|
||||
C.FillX = 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{
|
||||
widget: child,
|
||||
pack: C,
|
||||
})
|
||||
w.widgets = append(w.widgets, child)
|
||||
}
|
||||
|
||||
type packLayout struct {
|
||||
widgets []packedWidget
|
||||
}
|
||||
|
|
|
@ -86,6 +86,10 @@ func (w *Label) Compute(e render.Engine) {
|
|||
|
||||
// Present the label widget.
|
||||
func (w *Label) Present(e render.Engine, P render.Point) {
|
||||
if w.Hidden() {
|
||||
return
|
||||
}
|
||||
|
||||
border := w.BoxThickness(1)
|
||||
|
||||
var (
|
||||
|
|
|
@ -53,6 +53,12 @@ func (s *Supervisor) Loop(ev *events.State) {
|
|||
|
||||
// See if we are hovering over any widgets.
|
||||
for id, w := range s.widgets {
|
||||
if w.Hidden() {
|
||||
// TODO: somehow the Supervisor wasn't triggering hidden widgets
|
||||
// anyway, but I don't know why. Adding this check for safety.
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
P = w.Point()
|
||||
S = w.Size()
|
||||
|
|
38
ui/widget.go
38
ui/widget.go
|
@ -56,10 +56,16 @@ type Widget interface {
|
|||
OutlineSize() int32 // Outline size (default 0)
|
||||
SetOutlineSize(int32) //
|
||||
|
||||
// Visibility
|
||||
Hide()
|
||||
Show()
|
||||
Hidden() bool
|
||||
|
||||
// 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
|
||||
Children() []Widget // for containers to return their children
|
||||
|
||||
// Run any render computations; by the end the widget must know its
|
||||
// Width and Height. For example the Label widget will render itself onto
|
||||
|
@ -98,6 +104,7 @@ type BaseWidget struct {
|
|||
id string
|
||||
idFunc func() string
|
||||
fixedSize bool
|
||||
hidden bool
|
||||
width int32
|
||||
height int32
|
||||
point render.Point
|
||||
|
@ -276,6 +283,37 @@ func (w *BaseWidget) Adopt(parent Widget) {
|
|||
}
|
||||
}
|
||||
|
||||
// Children returns the widget's children, to be implemented by containers.
|
||||
// The default implementation returns an empty slice.
|
||||
func (w *BaseWidget) Children() []Widget {
|
||||
return []Widget{}
|
||||
}
|
||||
|
||||
// Hide the widget from being rendered.
|
||||
func (w *BaseWidget) Hide() {
|
||||
w.hidden = true
|
||||
}
|
||||
|
||||
// Show the widget.
|
||||
func (w *BaseWidget) Show() {
|
||||
w.hidden = false
|
||||
}
|
||||
|
||||
// Hidden returns whether the widget is hidden. If this widget is not hidden,
|
||||
// but it has a parent, this will recursively crawl the parents to see if any
|
||||
// of them are hidden.
|
||||
func (w *BaseWidget) Hidden() bool {
|
||||
if w.hidden {
|
||||
return true
|
||||
}
|
||||
|
||||
if parent, ok := w.Parent(); ok {
|
||||
return parent.Hidden()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// DrawBox draws the border and outline.
|
||||
func (w *BaseWidget) DrawBox(e render.Engine, P render.Point) {
|
||||
var (
|
||||
|
|
|
@ -69,6 +69,13 @@ func NewWindow(title string) *Window {
|
|||
return w
|
||||
}
|
||||
|
||||
// Children returns the window's child widgets.
|
||||
func (w *Window) Children() []Widget {
|
||||
return []Widget{
|
||||
w.body,
|
||||
}
|
||||
}
|
||||
|
||||
// TitleBar returns the title bar widget.
|
||||
func (w *Window) TitleBar() *Label {
|
||||
return w.titleBar
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package uix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.kirsle.net/apps/doodle/balance"
|
||||
"git.kirsle.net/apps/doodle/doodads"
|
||||
"git.kirsle.net/apps/doodle/events"
|
||||
|
@ -39,7 +42,19 @@ func NewCanvas(size int, editable bool) *Canvas {
|
|||
}
|
||||
w.setup()
|
||||
w.IDFunc(func() string {
|
||||
return "Canvas"
|
||||
var attrs []string
|
||||
|
||||
if w.Editable {
|
||||
attrs = append(attrs, "editable")
|
||||
} else {
|
||||
attrs = append(attrs, "read-only")
|
||||
}
|
||||
|
||||
if w.Scrollable {
|
||||
attrs = append(attrs, "scrollable")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Canvas<%d; %s>", size, strings.Join(attrs, "; "))
|
||||
})
|
||||
return w
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user