From 2e36d9ca85c6ac9dac532938d3ec4a004a72b505 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Tue, 31 Jul 2018 17:18:13 -0700 Subject: [PATCH] WIP Labels --- doodle.go | 3 +- guitest_scene.go | 159 ++++++++++++++++++++++ main_scene.go | 49 +++++-- render/interface.go | 10 ++ ui/button.go | 20 ++- ui/frame.go | 325 ++++++++++++++++++++++++++++++++++++++++++++ ui/label.go | 38 +++++- ui/log.go | 14 ++ ui/supervisor.go | 3 +- ui/theme/theme.go | 2 + ui/widget.go | 178 ++++++++++++++++++++---- 11 files changed, 750 insertions(+), 51 deletions(-) create mode 100644 guitest_scene.go create mode 100644 ui/frame.go create mode 100644 ui/log.go diff --git a/doodle.go b/doodle.go index d487100..e6dd44b 100644 --- a/doodle.go +++ b/doodle.go @@ -63,7 +63,8 @@ func (d *Doodle) Run() error { // Set up the default scene. if d.Scene == nil { - d.Goto(&MainScene{}) + d.Goto(&GUITestScene{}) + // d.Goto(&MainScene{}) } log.Info("Enter Main Loop") diff --git a/guitest_scene.go b/guitest_scene.go new file mode 100644 index 0000000..61b0224 --- /dev/null +++ b/guitest_scene.go @@ -0,0 +1,159 @@ +package doodle + +import ( + "git.kirsle.net/apps/doodle/events" + "git.kirsle.net/apps/doodle/render" + "git.kirsle.net/apps/doodle/ui" +) + +// GUITestScene implements the main menu of Doodle. +type GUITestScene struct { + Supervisor *ui.Supervisor + frame *ui.Frame + window *ui.Frame +} + +// Name of the scene. +func (s *GUITestScene) Name() string { + return "Main" +} + +// Setup the scene. +func (s *GUITestScene) Setup(d *Doodle) error { + s.Supervisor = ui.NewSupervisor() + + window := ui.NewFrame() + s.window = window + window.Configure(ui.Config{ + Width: 400, + Height: 400, + Background: render.Grey, + BorderStyle: ui.BorderRaised, + BorderSize: 2, + }) + + titleBar := ui.NewLabel(render.Text{ + Text: "Alert", + Size: 12, + Color: render.White, + Stroke: render.Black, + }) + titleBar.Configure(ui.Config{ + Background: render.Blue, + OutlineSize: 1, + OutlineColor: render.Black, + }) + window.Pack(titleBar, ui.Pack{ + Anchor: ui.N, + FillX: true, + }) + + msgFrame := ui.NewFrame() + msgFrame.Configure(ui.Config{ + Background: render.Grey, + BorderStyle: ui.BorderRaised, + BorderSize: 1, + }) + window.Pack(msgFrame, ui.Pack{ + Anchor: ui.N, + Fill: true, + Padding: 4, + }) + + btnFrame := ui.NewFrame() + btnFrame.Configure(ui.Config{ + Background: render.DarkRed, + }) + window.Pack(btnFrame, ui.Pack{ + Anchor: ui.N, + Padding: 4, + }) + + msg := ui.NewLabel(render.Text{ + Text: "Hello World!", + Size: 14, + Color: render.Black, + }) + msgFrame.Pack(msg, ui.Pack{ + Anchor: ui.NW, + Padding: 2, + }) + + button1 := ui.NewButton(*ui.NewLabel(render.Text{ + Text: "New Map", + Size: 14, + Color: render.Black, + })) + button1.SetBackground(render.Blue) + button1.Handle("Click", func(p render.Point) { + d.NewMap() + }) + + log.Info("Button1 bg: %s", button1.Background()) + + button2 := ui.NewButton(*ui.NewLabel(render.Text{ + Text: "New Map", + Size: 14, + Color: render.Black, + })) + button2.SetText("Load Map") + + var align = ui.W + btnFrame.Pack(button1, ui.Pack{ + Anchor: align, + Padding: 20, + Fill: true, + }) + btnFrame.Pack(button2, ui.Pack{ + Anchor: align, + Padding: 20, + Fill: true, + }) + + s.Supervisor.Add(button1) + s.Supervisor.Add(button2) + + return nil +} + +// Loop the editor scene. +func (s *GUITestScene) Loop(d *Doodle, ev *events.State) error { + s.Supervisor.Loop(ev) + return nil +} + +// Draw the pixels on this frame. +func (s *GUITestScene) Draw(d *Doodle) error { + // Clear the canvas and fill it with white. + d.Engine.Clear(render.White) + + label := ui.NewLabel(render.Text{ + Text: "GUITest Doodle v" + Version, + Size: 26, + Color: render.Pink, + Stroke: render.SkyBlue, + Shadow: render.Black, + }) + label.Compute(d.Engine) + label.MoveTo(render.Point{ + X: (d.width / 2) - (label.Size().W / 2), + Y: 40, + }) + label.Present(d.Engine) + + s.window.Compute(d.Engine) + s.window.MoveTo(render.Point{ + X: (d.width / 2) - (s.window.Size().W / 2), + Y: 100, + }) + s.window.Present(d.Engine) + + s.Supervisor.Present(d.Engine) + + return nil +} + +// Destroy the scene. +func (s *GUITestScene) Destroy() error { + return nil +} diff --git a/main_scene.go b/main_scene.go index b4bb589..073be25 100644 --- a/main_scene.go +++ b/main_scene.go @@ -9,6 +9,7 @@ import ( // MainScene implements the main menu of Doodle. type MainScene struct { Supervisor *ui.Supervisor + frame *ui.Frame } // Name of the scene. @@ -20,16 +21,27 @@ func (s *MainScene) Name() string { func (s *MainScene) Setup(d *Doodle) error { s.Supervisor = ui.NewSupervisor() + frame := ui.NewFrame() + s.frame = frame + s.frame.Configure(ui.Config{ + // Width: 400, + // Height: 200, + Background: render.Purple, + BorderStyle: ui.BorderSolid, + BorderSize: 1, + BorderColor: render.Blue, + }) + button1 := ui.NewButton(*ui.NewLabel(render.Text{ Text: "New Map", Size: 14, Color: render.Black, })) - button1.Compute(d.Engine) - button1.MoveTo(render.Point{ - X: (d.width / 2) - (button1.Size().W / 2), - Y: 200, - }) + // button1.Compute(d.Engine) + // button1.MoveTo(render.Point{ + // X: (d.width / 2) - (button1.Size().W / 2), + // Y: 200, + // }) button1.Handle("Click", func(p render.Point) { d.NewMap() }) @@ -40,10 +52,22 @@ func (s *MainScene) Setup(d *Doodle) error { Color: render.Black, })) button2.SetText("Load Map") - button2.Compute(d.Engine) - button2.MoveTo(render.Point{ - X: (d.width / 2) - (button2.Size().W / 2), - Y: 260, + // button2.Compute(d.Engine) + // button2.MoveTo(render.Point{ + // X: (d.width / 2) - (button2.Size().W / 2), + // Y: 260, + // }) + + var align = ui.E + frame.Pack(button1, ui.Pack{ + Anchor: align, + Padding: 12, + Fill: true, + }) + frame.Pack(button2, ui.Pack{ + Anchor: align, + Padding: 12, + Fill: true, }) s.Supervisor.Add(button1) @@ -77,6 +101,13 @@ func (s *MainScene) Draw(d *Doodle) error { }) label.Present(d.Engine) + s.frame.Compute(d.Engine) + s.frame.MoveTo(render.Point{ + X: (d.width / 2) - (s.frame.Size().W / 2), + Y: 200, + }) + s.frame.Present(d.Engine) + s.Supervisor.Present(d.Engine) return nil diff --git a/render/interface.go b/render/interface.go index 72fd530..8ae222d 100644 --- a/render/interface.go +++ b/render/interface.go @@ -89,6 +89,16 @@ func (c Color) Add(r, g, b, a int32) Color { } } +// Lighten a color value. +func (c Color) Lighten(v int32) Color { + return c.Add(v, v, v, 0) +} + +// Darken a color value. +func (c Color) Darken(v int32) Color { + return c.Add(-v, -v, -v, 0) +} + // Point holds an X,Y coordinate value. type Point struct { X int32 diff --git a/ui/button.go b/ui/button.go index b19486b..f8ab5c1 100644 --- a/ui/button.go +++ b/ui/button.go @@ -1,6 +1,8 @@ package ui import ( + "fmt" + "git.kirsle.net/apps/doodle/render" "git.kirsle.net/apps/doodle/ui/theme" ) @@ -21,12 +23,14 @@ func NewButton(label Label) *Button { Label: label, } - w.SetPadding(4) - w.SetBorderSize(2) - w.SetBorderStyle(BorderRaised) - w.SetOutlineSize(1) - w.SetOutlineColor(theme.ButtonOutlineColor) - w.SetBackground(theme.ButtonBackgroundColor) + w.Configure(Config{ + Padding: 4, + BorderSize: 2, + BorderStyle: BorderRaised, + OutlineSize: 1, + OutlineColor: theme.ButtonOutlineColor, + Background: theme.ButtonBackgroundColor, + }) w.Handle("MouseOver", func(p render.Point) { w.hovering = true @@ -46,6 +50,10 @@ func NewButton(label Label) *Button { w.SetBorderStyle(BorderRaised) }) + w.IDFunc(func() string { + return fmt.Sprintf("Button<%s>", w.Label.Text.Text) + }) + return w } diff --git a/ui/frame.go b/ui/frame.go new file mode 100644 index 0000000..66f19ee --- /dev/null +++ b/ui/frame.go @@ -0,0 +1,325 @@ +package ui + +import ( + "fmt" + "time" + + "git.kirsle.net/apps/doodle/render" +) + +// Frame is a widget that contains other widgets. +type Frame struct { + BaseWidget + packs map[Anchor][]packedWidget + widgets []Widget +} + +// NewFrame creates a new Frame. +func NewFrame() *Frame { + return &Frame{ + packs: map[Anchor][]packedWidget{}, + widgets: []Widget{}, + } +} + +func (w *Frame) String() string { + return fmt.Sprintf("Frame<%d widgets>", + len(w.widgets), + ) +} + +// Compute the size of the Frame. +func (w *Frame) Compute(e render.Engine) { + var ( + frameSize = w.Size() + maxWidth int32 + maxHeight int32 + visited = []packedWidget{} + ) + + // Initialize the dimensions? + if w.FixedSize() { + maxWidth = frameSize.W + maxHeight = frameSize.H + } + + for anchor := AnchorMin; anchor <= AnchorMax; anchor++ { + if _, ok := w.packs[anchor]; !ok { + continue + } + + var ( + x int32 + y int32 + yDirection int32 = 1 + xDirection int32 = 1 + ) + + if anchor.IsSouth() { + y = frameSize.H + yDirection = -1 - w.BoxThickness(1) + } else if anchor == E { + x = frameSize.W + xDirection = -1 + } + + for _, packedWidget := range w.packs[anchor] { + child := packedWidget.widget + pack := packedWidget.pack + child.Compute(e) + var ( + // point = child.Point() + size = child.Size() + yStep = y * yDirection + xStep = x * xDirection + ) + + if !w.FixedSize() { + log.Warn("not fixed") + if xStep+size.W+(pack.PadX*2) > maxWidth { + var old = maxWidth + maxWidth = xStep + size.W + (pack.PadX * 2) + log.Error("%s %s Upgrading maxWidth %d -> %d (size %s) xstep %d", + w, + child, + old, + maxWidth, + size, + xStep, + ) + time.Sleep(5) + } + if yStep+size.H+(pack.PadY*2) > maxHeight { + maxHeight = yStep + size.H + (pack.PadY * 2) + } + } + + if anchor.IsSouth() { + y -= size.H + (pack.PadY * 2) + } + if anchor.IsEast() { + x -= size.W + (pack.PadX * 2) + } + + child.MoveTo(render.Point{ + X: x + pack.PadX, + Y: y + pack.PadY, + }) + + if anchor.IsNorth() { + y += size.H + (pack.PadY * 2) + } + if anchor == W { + x += size.W + (pack.PadX * 2) + } + + visited = append(visited, packedWidget) + } + } + + // Rescan all the widgets in this anchor to re-center them + // in their space. + for _, pw := range visited { + var ( + child = pw.widget + pack = pw.pack + point = child.Point() + size = child.Size() + resized bool + moved bool + ) + + if pack.Anchor.IsNorth() || pack.Anchor.IsSouth() { + if pack.FillX && size.W < maxWidth { + size.W = maxWidth - w.BoxThickness(2) + resized = true + } + if size.W < maxWidth { + if pack.Anchor.IsCenter() { + point.X = (maxWidth / 2) - (size.W / 2) + } else if pack.Anchor.IsWest() { + point.X = pack.PadX + } else if pack.Anchor.IsEast() { + point.X = maxWidth - size.W - pack.PadX + } + + moved = true + } + } else if pack.Anchor.IsWest() || pack.Anchor.IsEast() { + if pack.FillY && size.H < maxHeight { + size.H = maxHeight - w.BoxThickness(2) + resized = true + } + if size.H < maxHeight { + if pack.Anchor.IsMiddle() { + point.Y = (maxHeight / 2) - (size.H / 2) + } else if pack.Anchor.IsNorth() { + point.Y = pack.PadY + } else if pack.Anchor.IsSouth() { + point.Y = maxHeight - size.H - pack.PadY + } + moved = true + } + } else { + log.Error("unsupported pack.Anchor") + } + + if resized { + child.Resize(size) + } + if moved { + child.MoveTo(point) + } + } + + if !w.FixedSize() { + w.Resize(render.Rect{ + W: maxWidth + w.BoxThickness(2), + H: maxHeight + w.BoxThickness(2), + }) + } +} + +// Present the Frame. +func (w *Frame) Present(e render.Engine) { + var ( + P = w.Point() + S = w.Size() + ) + + // Draw the widget's border and everything. + w.DrawBox(e) + + // Draw the background color. + e.DrawBox(w.Background(), render.Rect{ + X: P.X + w.BoxThickness(1), + Y: P.Y + w.BoxThickness(1), + W: S.W - w.BoxThickness(2), + H: S.H - w.BoxThickness(2), + }) + + // Draw the widgets. + for _, child := range w.widgets { + p := child.Point() + child.MoveTo(render.NewPoint( + P.X+p.X+w.BoxThickness(1), + P.Y+p.Y+w.BoxThickness(1), + )) + child.Present(e) + } +} + +// 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 to fill any remaining space. +} + +// Anchor is a cardinal direction. +type Anchor uint8 + +// Anchor values. +const ( + Center Anchor = iota + N + NE + E + SE + S + SW + W + NW +) + +// Range of Anchor values. +const ( + AnchorMin = Center + AnchorMax = NW +) + +// IsNorth returns if the anchor is N, NE or NW. +func (a Anchor) IsNorth() bool { + return a == N || a == NE || a == NW +} + +// IsSouth returns if the anchor is S, SE or SW. +func (a Anchor) IsSouth() bool { + return a == S || a == SE || a == SW +} + +// IsEast returns if the anchor is E, NE or SE. +func (a Anchor) IsEast() bool { + return a == E || a == NE || a == SE +} + +// IsWest returns if the anchor is W, NW or SW. +func (a Anchor) IsWest() bool { + return a == W || a == NW || a == SW +} + +// IsCenter returns if the anchor is Center, N or S, to determine +// whether to align text as centered for North/South anchors. +func (a Anchor) IsCenter() bool { + return a == Center || a == N || a == S +} + +// IsMiddle returns if the anchor is Center, E or W, to determine +// whether to align text as middled for East/West anchors. +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 + + 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 +} + +type packedWidget struct { + widget Widget + pack Pack + fill uint8 +} + +// packedWidget.fill values +const ( + fillNone uint8 = iota + fillX + fillY + fillBoth +) diff --git a/ui/label.go b/ui/label.go index 76bb6bf..2c7cc4c 100644 --- a/ui/label.go +++ b/ui/label.go @@ -1,6 +1,10 @@ package ui -import "git.kirsle.net/apps/doodle/render" +import ( + "fmt" + + "git.kirsle.net/apps/doodle/render" +) // Label is a simple text label widget. type Label struct { @@ -12,20 +16,40 @@ type Label struct { // NewLabel creates a new label. func NewLabel(t render.Text) *Label { - return &Label{ + w := &Label{ Text: t, } + w.Configure(Config{ + Padding: 4, + }) + w.IDFunc(func() string { + return fmt.Sprintf("Label<%s>", w.Text.Text) + }) + return w } // Compute the size of the label widget. func (w *Label) Compute(e render.Engine) { - rect, err := e.ComputeTextRect(w.Text) - w.Resize(rect) - _ = rect - _ = err + rect, _ := e.ComputeTextRect(w.Text) + w.Resize(render.Rect{ + W: rect.W + w.Padding(), + H: rect.H + w.Padding(), + }) + w.MoveTo(render.Point{ + X: rect.X + w.BoxThickness(1), + Y: rect.Y + w.BoxThickness(1), + }) } // Present the label widget. func (w *Label) Present(e render.Engine) { - e.DrawText(w.Text, w.Point()) + var ( + P = w.Point() + border = w.BoxThickness(1) + ) + w.DrawBox(e) + e.DrawText(w.Text, render.Point{ + X: P.X + border, + Y: P.Y + border, + }) } diff --git a/ui/log.go b/ui/log.go new file mode 100644 index 0000000..4ddc06b --- /dev/null +++ b/ui/log.go @@ -0,0 +1,14 @@ +package ui + +import "github.com/kirsle/golog" + +var log *golog.Logger + +func init() { + log = golog.GetLogger("ui") + log.Configure(&golog.Config{ + Level: golog.DebugLevel, + Theme: golog.DarkTheme, + Colors: golog.ExtendedColor, + }) +} diff --git a/ui/supervisor.go b/ui/supervisor.go index 0f6d93f..4c6e295 100644 --- a/ui/supervisor.go +++ b/ui/supervisor.go @@ -84,7 +84,8 @@ func (s *Supervisor) Present(e render.Engine) { defer s.lock.RUnlock() for _, w := range s.widgets { - w.Present(e) + // w.Present(e) + _ = w } } diff --git a/ui/theme/theme.go b/ui/theme/theme.go index 729a2c4..23c3186 100644 --- a/ui/theme/theme.go +++ b/ui/theme/theme.go @@ -7,4 +7,6 @@ var ( ButtonBackgroundColor = render.RGBA(200, 200, 200, 255) ButtonHoverColor = render.RGBA(200, 255, 255, 255) ButtonOutlineColor = render.Black + + BorderColorOffset int32 = 40 ) diff --git a/ui/widget.go b/ui/widget.go index 475e80f..ac44592 100644 --- a/ui/widget.go +++ b/ui/widget.go @@ -2,6 +2,7 @@ package ui import ( "git.kirsle.net/apps/doodle/render" + "git.kirsle.net/apps/doodle/ui/theme" ) // BorderStyle options for widget.SetBorderStyle() @@ -16,10 +17,14 @@ const ( // Widget is a user interface element. type Widget interface { + ID() string // Get the widget's string ID. + IDFunc(func() string) // Set a function that returns the widget's ID. + String() string Point() render.Point MoveTo(render.Point) MoveBy(render.Point) Size() render.Rect // Return the Width and Height of the widget. + FixedSize() bool // Return whether the size is fixed (true) or automatic (false) Resize(render.Rect) Handle(string, func(render.Point)) @@ -56,9 +61,34 @@ type Widget interface { Present(render.Engine) } +// Config holds common base widget configs for quick configuration. +type Config struct { + // Size management. If you provide a non-zero value for Width and Height, + // the widget will be resized and the "fixedSize" flag is set, meaning it + // will not re-compute its size dynamically. To set the size while also + // keeping the auto-resize property, pass AutoResize=true too. This is + // mainly used internally when widgets are calculating their automatic sizes. + AutoResize bool + Width int32 + Height int32 + Padding int32 + PadX int32 + PadY int32 + Background render.Color + Foreground render.Color + BorderSize int32 + BorderStyle BorderStyle + BorderColor render.Color + OutlineSize int32 + OutlineColor render.Color +} + // BaseWidget holds common functionality for all widgets, such as managing // their widths and heights. type BaseWidget struct { + id string + idFunc func() string + fixedSize bool width int32 height int32 point render.Point @@ -73,6 +103,66 @@ type BaseWidget struct { handlers map[string][]func(render.Point) } +// SetID sets a string name for your widget, helpful for debugging purposes. +func (w *BaseWidget) SetID(id string) { + w.id = id +} + +// ID returns the ID that the widget calls itself by. +func (w *BaseWidget) ID() string { + if w.idFunc == nil { + w.IDFunc(func() string { + return "Widget" + }) + } + return w.idFunc() +} + +// IDFunc sets an ID function. +func (w *BaseWidget) IDFunc(fn func() string) { + w.idFunc = fn +} + +func (w *BaseWidget) String() string { + return w.ID() +} + +// Configure the base widget with all the common properties at once. Any +// property left as the zero value will not update the widget. +func (w *BaseWidget) Configure(c Config) { + if c.Width != 0 && c.Height != 0 { + w.fixedSize = !c.AutoResize + w.width = c.Width + w.height = c.Height + } + + if c.Padding != 0 { + w.padding = c.Padding + } + if c.Background != render.Invisible { + w.background = c.Background + } + if c.Foreground != render.Invisible { + w.foreground = c.Foreground + } + if c.BorderColor != render.Invisible { + w.borderColor = c.BorderColor + } + if c.OutlineColor != render.Invisible { + w.outlineColor = c.OutlineColor + } + + if c.BorderSize != 0 { + w.borderSize = c.BorderSize + } + if c.BorderStyle != BorderSolid { + w.borderStyle = c.BorderStyle + } + if c.OutlineSize != 0 { + w.outlineSize = c.OutlineSize + } +} + // Point returns the X,Y position of the widget on the window. func (w *BaseWidget) Point() render.Point { return w.point @@ -98,8 +188,21 @@ func (w *BaseWidget) Size() render.Rect { } } +// FixedSize returns whether the widget's size has been hard-coded by the user +// (true) or if it automatically resizes based on its contents (false). +func (w *BaseWidget) FixedSize() bool { + return w.fixedSize +} + // Resize sets the size of the widget to the .W and .H attributes of a rect. func (w *BaseWidget) Resize(v render.Rect) { + w.fixedSize = true + w.width = v.W + w.height = v.H +} + +// resizeAuto sets the size of the widget but doesn't set the fixedSize flag. +func (w *BaseWidget) resizeAuto(v render.Rect) { w.width = v.W w.height = v.H } @@ -121,8 +224,8 @@ func (w *BaseWidget) DrawBox(e render.Engine) { outline = w.OutlineSize() border = w.BorderSize() borderColor = w.BorderColor() - highlight = borderColor.Add(20, 20, 20, 0) - shadow = borderColor.Add(-20, -20, -20, 0) + highlight = borderColor.Lighten(theme.BorderColorOffset) + shadow = borderColor.Darken(theme.BorderColorOffset) color render.Color box = render.Rect{ X: P.X, @@ -132,46 +235,67 @@ func (w *BaseWidget) DrawBox(e render.Engine) { } ) + if borderColor == render.Invisible { + borderColor = render.Red + } + // Draw the outline layer as the full size of the widget. - e.DrawBox(w.OutlineColor(), render.Rect{ - X: P.X - outline, - Y: P.Y - outline, - W: S.W + (outline * 2), - H: S.H + (outline * 2), - }) + if outline > 0 && w.OutlineColor() != render.Invisible { + e.DrawBox(w.OutlineColor(), render.Rect{ + X: P.X, + Y: P.Y, + W: S.W, + H: S.H, + }) + } + box.X += outline + box.Y += outline + box.W -= outline * 2 + box.H -= outline * 2 // Highlight on the top left edge. - if w.BorderStyle() == BorderRaised { - color = highlight - } else if w.BorderStyle() == BorderSunken { - color = shadow - } else { - color = borderColor + if border > 0 { + if w.BorderStyle() == BorderRaised { + color = highlight + } else if w.BorderStyle() == BorderSunken { + color = shadow + } else { + color = borderColor + } + e.DrawBox(color, box) } - e.DrawBox(color, box) - box.W = S.W // Shadow on the bottom right edge. box.X += border box.Y += border box.W -= border box.H -= border - if w.BorderStyle() == BorderRaised { - color = shadow - } else if w.BorderStyle() == BorderSunken { - color = highlight - } else { - color = borderColor + if w.BorderSize() > 0 { + if w.BorderStyle() == BorderRaised { + color = shadow + } else if w.BorderStyle() == BorderSunken { + color = highlight + } else { + color = borderColor + } + e.DrawBox(color, box) } - e.DrawBox(color.Add(-20, -20, -20, 0), box) // Background color of the button. box.W -= border box.H -= border - // if w.hovering { - // e.DrawBox(render.Yellow, box) - // } else { - e.DrawBox(color, box) + if w.Background() != render.Invisible { + e.DrawBox(w.Background(), box) + } + + // log.Info("Widget %s background color: %s", w, w.Background()) + + // XXX: color effective area + // box.X += w.Padding() + // box.Y += w.Padding() + // box.W -= w.Padding() * 2 + // box.H -= w.Padding() * 2 + // e.DrawBox(render.RGBA(0, 255, 255, 153), box) } // Padding returns the padding width.