Move most properties from Button to parent Widget
These properties will be globally useful to all sorts of Widgets and have been moved from the Button up into the Common widget, and its interface extended to configure these: * Padding int32 * Background color * Foreground color * Border size, color and style (default solid; raised; sunken) * Outline size and color The button adjusts its border style from "raised" to "sunken" for MouseDown events and its Background color for MouseOver events. Other widgets such as Labels and Frames will be able to have borders, paddings and outlines too, but they will be off by default.
This commit is contained in:
parent
602273aa16
commit
11df6cbda9
|
@ -63,6 +63,32 @@ func (c Color) String() string {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a relative color value to the color.
|
||||||
|
func (c Color) Add(r, g, b, a int32) Color {
|
||||||
|
var (
|
||||||
|
R = int32(c.Red) + r
|
||||||
|
G = int32(c.Green) + g
|
||||||
|
B = int32(c.Blue) + b
|
||||||
|
A = int32(c.Alpha) + a
|
||||||
|
)
|
||||||
|
|
||||||
|
cap8 := func(v int32) uint8 {
|
||||||
|
if v > 255 {
|
||||||
|
v = 255
|
||||||
|
} else if v < 0 {
|
||||||
|
v = 0
|
||||||
|
}
|
||||||
|
return uint8(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Color{
|
||||||
|
Red: cap8(R),
|
||||||
|
Green: cap8(G),
|
||||||
|
Blue: cap8(B),
|
||||||
|
Alpha: cap8(A),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Point holds an X,Y coordinate value.
|
// Point holds an X,Y coordinate value.
|
||||||
type Point struct {
|
type Point struct {
|
||||||
X int32
|
X int32
|
||||||
|
|
|
@ -131,17 +131,7 @@ func (r *Renderer) Poll() (*events.State, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if eventName != "" {
|
if eventName != "" {
|
||||||
log.Debug("tick:%d Mouse Button1 %s BEFORE: %+v",
|
|
||||||
r.ticks,
|
|
||||||
eventName,
|
|
||||||
s.Button1,
|
|
||||||
)
|
|
||||||
s.Button1.Push(eventName == "DOWN")
|
s.Button1.Push(eventName == "DOWN")
|
||||||
log.Debug("tick:%d Mouse Button1 %s AFTER: %+v",
|
|
||||||
r.ticks,
|
|
||||||
eventName,
|
|
||||||
s.Button1,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Return the event immediately.
|
// Return the event immediately.
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|
86
ui/button.go
86
ui/button.go
|
@ -8,16 +8,7 @@ import (
|
||||||
// Button is a clickable button.
|
// Button is a clickable button.
|
||||||
type Button struct {
|
type Button struct {
|
||||||
BaseWidget
|
BaseWidget
|
||||||
Label Label
|
Label Label
|
||||||
Padding int32
|
|
||||||
Border int32
|
|
||||||
Outline int32
|
|
||||||
|
|
||||||
// Color options.
|
|
||||||
Background render.Color
|
|
||||||
HighlightColor render.Color
|
|
||||||
ShadowColor render.Color
|
|
||||||
OutlineColor render.Color
|
|
||||||
|
|
||||||
// Private options.
|
// Private options.
|
||||||
hovering bool
|
hovering bool
|
||||||
|
@ -27,30 +18,32 @@ type Button struct {
|
||||||
// NewButton creates a new Button.
|
// NewButton creates a new Button.
|
||||||
func NewButton(label Label) *Button {
|
func NewButton(label Label) *Button {
|
||||||
w := &Button{
|
w := &Button{
|
||||||
Label: label,
|
Label: label,
|
||||||
Padding: 4, // TODO magic number
|
|
||||||
Border: 2,
|
|
||||||
Outline: 1,
|
|
||||||
|
|
||||||
// Default theme colors.
|
|
||||||
Background: theme.ButtonBackgroundColor,
|
|
||||||
HighlightColor: theme.ButtonHighlightColor,
|
|
||||||
ShadowColor: theme.ButtonShadowColor,
|
|
||||||
OutlineColor: theme.ButtonOutlineColor,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.SetPadding(4)
|
||||||
|
w.SetBorderSize(2)
|
||||||
|
w.SetBorderStyle(BorderRaised)
|
||||||
|
w.SetOutlineSize(1)
|
||||||
|
w.SetOutlineColor(theme.ButtonOutlineColor)
|
||||||
|
w.SetBackground(theme.ButtonBackgroundColor)
|
||||||
|
|
||||||
w.Handle("MouseOver", func(p render.Point) {
|
w.Handle("MouseOver", func(p render.Point) {
|
||||||
w.hovering = true
|
w.hovering = true
|
||||||
|
w.SetBackground(theme.ButtonHoverColor)
|
||||||
})
|
})
|
||||||
w.Handle("MouseOut", func(p render.Point) {
|
w.Handle("MouseOut", func(p render.Point) {
|
||||||
w.hovering = false
|
w.hovering = false
|
||||||
|
w.SetBackground(theme.ButtonBackgroundColor)
|
||||||
})
|
})
|
||||||
|
|
||||||
w.Handle("MouseDown", func(p render.Point) {
|
w.Handle("MouseDown", func(p render.Point) {
|
||||||
w.clicked = true
|
w.clicked = true
|
||||||
|
w.SetBorderStyle(BorderSunken)
|
||||||
})
|
})
|
||||||
w.Handle("MouseUp", func(p render.Point) {
|
w.Handle("MouseUp", func(p render.Point) {
|
||||||
w.clicked = false
|
w.clicked = false
|
||||||
|
w.SetBorderStyle(BorderRaised)
|
||||||
})
|
})
|
||||||
|
|
||||||
return w
|
return w
|
||||||
|
@ -67,8 +60,8 @@ func (w *Button) Compute(e render.Engine) {
|
||||||
w.Label.Compute(e)
|
w.Label.Compute(e)
|
||||||
size := w.Label.Size()
|
size := w.Label.Size()
|
||||||
w.Resize(render.Rect{
|
w.Resize(render.Rect{
|
||||||
W: size.W + (w.Padding * 2) + (w.Border * 2) + (w.Outline * 2),
|
W: size.W + w.BoxThickness(2),
|
||||||
H: size.H + (w.Padding * 2) + (w.Border * 2) + (w.Outline * 2),
|
H: size.H + w.BoxThickness(2),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,55 +69,20 @@ func (w *Button) Compute(e render.Engine) {
|
||||||
func (w *Button) Present(e render.Engine) {
|
func (w *Button) Present(e render.Engine) {
|
||||||
w.Compute(e)
|
w.Compute(e)
|
||||||
P := w.Point()
|
P := w.Point()
|
||||||
S := w.Size()
|
|
||||||
|
|
||||||
box := render.Rect{
|
// Draw the widget's border and everything.
|
||||||
X: P.X,
|
w.DrawBox(e)
|
||||||
Y: P.Y,
|
|
||||||
W: S.W,
|
|
||||||
H: S.H,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the outline layer as the full size of the widget.
|
// Offset further if we are currently sunken.
|
||||||
e.DrawBox(w.OutlineColor, render.Rect{
|
var clickOffset int32
|
||||||
X: P.X - w.Outline,
|
|
||||||
Y: P.Y - w.Outline,
|
|
||||||
W: S.W + (w.Outline * 2),
|
|
||||||
H: S.H + (w.Outline * 2),
|
|
||||||
})
|
|
||||||
|
|
||||||
// Highlight on the top left edge.
|
|
||||||
color := w.HighlightColor
|
|
||||||
if w.clicked {
|
if w.clicked {
|
||||||
color = w.ShadowColor
|
clickOffset++
|
||||||
}
|
|
||||||
e.DrawBox(color, box)
|
|
||||||
box.W = S.W
|
|
||||||
|
|
||||||
// Shadow on the bottom right edge.
|
|
||||||
box.X += w.Border
|
|
||||||
box.Y += w.Border
|
|
||||||
box.W -= w.Border
|
|
||||||
box.H -= w.Border
|
|
||||||
color = w.ShadowColor
|
|
||||||
if w.clicked {
|
|
||||||
color = w.HighlightColor
|
|
||||||
}
|
|
||||||
e.DrawBox(color, box)
|
|
||||||
|
|
||||||
// Background color of the button.
|
|
||||||
box.W -= w.Border
|
|
||||||
box.H -= w.Border
|
|
||||||
if w.hovering {
|
|
||||||
e.DrawBox(render.Yellow, box)
|
|
||||||
} else {
|
|
||||||
e.DrawBox(w.Background, box)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the text label inside.
|
// Draw the text label inside.
|
||||||
w.Label.MoveTo(render.Point{
|
w.Label.MoveTo(render.Point{
|
||||||
X: P.X + w.Padding + w.Border + w.Outline,
|
X: P.X + w.BoxThickness(1) + clickOffset,
|
||||||
Y: P.Y + w.Padding + w.Border + w.Outline,
|
Y: P.Y + w.BoxThickness(1) + clickOffset,
|
||||||
})
|
})
|
||||||
w.Label.Present(e)
|
w.Label.Present(e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,7 @@ import "git.kirsle.net/apps/doodle/render"
|
||||||
|
|
||||||
// Color schemes.
|
// Color schemes.
|
||||||
var (
|
var (
|
||||||
ButtonBackgroundColor = render.RGBA(250, 250, 250, 255)
|
ButtonBackgroundColor = render.RGBA(200, 200, 200, 255)
|
||||||
ButtonHighlightColor = render.RGBA(128, 128, 128, 255)
|
ButtonHoverColor = render.RGBA(200, 255, 255, 255)
|
||||||
ButtonShadowColor = render.RGBA(20, 20, 20, 255)
|
|
||||||
ButtonOutlineColor = render.Black
|
ButtonOutlineColor = render.Black
|
||||||
)
|
)
|
||||||
|
|
205
ui/widget.go
205
ui/widget.go
|
@ -1,6 +1,18 @@
|
||||||
package ui
|
package ui
|
||||||
|
|
||||||
import "git.kirsle.net/apps/doodle/render"
|
import (
|
||||||
|
"git.kirsle.net/apps/doodle/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BorderStyle options for widget.SetBorderStyle()
|
||||||
|
type BorderStyle string
|
||||||
|
|
||||||
|
// Styles for a widget border.
|
||||||
|
const (
|
||||||
|
BorderSolid BorderStyle = "solid"
|
||||||
|
BorderRaised = "raised"
|
||||||
|
BorderSunken = "sunken"
|
||||||
|
)
|
||||||
|
|
||||||
// Widget is a user interface element.
|
// Widget is a user interface element.
|
||||||
type Widget interface {
|
type Widget interface {
|
||||||
|
@ -13,6 +25,28 @@ type Widget interface {
|
||||||
Handle(string, func(render.Point))
|
Handle(string, func(render.Point))
|
||||||
Event(string, render.Point) // called internally to trigger an event
|
Event(string, render.Point) // called internally to trigger an event
|
||||||
|
|
||||||
|
// Thickness of the padding + border + outline.
|
||||||
|
BoxThickness(multiplier int32) int32
|
||||||
|
DrawBox(render.Engine)
|
||||||
|
|
||||||
|
// Widget configuration getters.
|
||||||
|
Padding() int32 // Padding
|
||||||
|
SetPadding(int32) //
|
||||||
|
Background() render.Color // Background color
|
||||||
|
SetBackground(render.Color) //
|
||||||
|
Foreground() render.Color // Foreground color
|
||||||
|
SetForeground(render.Color) //
|
||||||
|
BorderStyle() BorderStyle // Border style: none, raised, sunken
|
||||||
|
SetBorderStyle(BorderStyle) //
|
||||||
|
BorderColor() render.Color // Border color (default is Background)
|
||||||
|
SetBorderColor(render.Color) //
|
||||||
|
BorderSize() int32 // Border size (default 0)
|
||||||
|
SetBorderSize(int32) //
|
||||||
|
OutlineColor() render.Color // Outline color (default Invisible)
|
||||||
|
SetOutlineColor(render.Color) //
|
||||||
|
OutlineSize() int32 // Outline size (default 0)
|
||||||
|
SetOutlineSize(int32) //
|
||||||
|
|
||||||
// Run any render computations; by the end the widget must know its
|
// Run any render computations; by the end the widget must know its
|
||||||
// Width and Height. For example the Label widget will render itself onto
|
// Width and Height. For example the Label widget will render itself onto
|
||||||
// an SDL Surface and then it will know its bounding box, but not before.
|
// an SDL Surface and then it will know its bounding box, but not before.
|
||||||
|
@ -25,10 +59,18 @@ type Widget interface {
|
||||||
// BaseWidget holds common functionality for all widgets, such as managing
|
// BaseWidget holds common functionality for all widgets, such as managing
|
||||||
// their widths and heights.
|
// their widths and heights.
|
||||||
type BaseWidget struct {
|
type BaseWidget struct {
|
||||||
width int32
|
width int32
|
||||||
height int32
|
height int32
|
||||||
point render.Point
|
point render.Point
|
||||||
handlers map[string][]func(render.Point)
|
padding int32
|
||||||
|
background render.Color
|
||||||
|
foreground render.Color
|
||||||
|
borderStyle BorderStyle
|
||||||
|
borderColor render.Color
|
||||||
|
borderSize int32
|
||||||
|
outlineColor render.Color
|
||||||
|
outlineSize int32
|
||||||
|
handlers map[string][]func(render.Point)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Point returns the X,Y position of the widget on the window.
|
// Point returns the X,Y position of the widget on the window.
|
||||||
|
@ -62,6 +104,159 @@ func (w *BaseWidget) Resize(v render.Rect) {
|
||||||
w.height = v.H
|
w.height = v.H
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BoxThickness returns the full sum of the padding, border and outline.
|
||||||
|
// m = multiplier, i.e., 1 or 2
|
||||||
|
func (w *BaseWidget) BoxThickness(m int32) int32 {
|
||||||
|
if m == 0 {
|
||||||
|
m = 1
|
||||||
|
}
|
||||||
|
return (w.Padding() * m) + (w.BorderSize() * m) + (w.OutlineSize() * m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DrawBox draws the border and outline.
|
||||||
|
func (w *BaseWidget) DrawBox(e render.Engine) {
|
||||||
|
var (
|
||||||
|
P = w.Point()
|
||||||
|
S = w.Size()
|
||||||
|
outline = w.OutlineSize()
|
||||||
|
border = w.BorderSize()
|
||||||
|
borderColor = w.BorderColor()
|
||||||
|
highlight = borderColor.Add(20, 20, 20, 0)
|
||||||
|
shadow = borderColor.Add(-20, -20, -20, 0)
|
||||||
|
color render.Color
|
||||||
|
box = render.Rect{
|
||||||
|
X: P.X,
|
||||||
|
Y: P.Y,
|
||||||
|
W: S.W,
|
||||||
|
H: S.H,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Highlight on the top left edge.
|
||||||
|
if w.BorderStyle() == BorderRaised {
|
||||||
|
color = highlight
|
||||||
|
} else if w.BorderStyle() == BorderSunken {
|
||||||
|
color = shadow
|
||||||
|
} else {
|
||||||
|
color = borderColor
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Padding returns the padding width.
|
||||||
|
func (w *BaseWidget) Padding() int32 {
|
||||||
|
return w.padding
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPadding sets the padding width.
|
||||||
|
func (w *BaseWidget) SetPadding(v int32) {
|
||||||
|
w.padding = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background returns the background color.
|
||||||
|
func (w *BaseWidget) Background() render.Color {
|
||||||
|
return w.background
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBackground sets the color.
|
||||||
|
func (w *BaseWidget) SetBackground(c render.Color) {
|
||||||
|
w.background = c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foreground returns the foreground color.
|
||||||
|
func (w *BaseWidget) Foreground() render.Color {
|
||||||
|
return w.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetForeground sets the color.
|
||||||
|
func (w *BaseWidget) SetForeground(c render.Color) {
|
||||||
|
w.foreground = c
|
||||||
|
}
|
||||||
|
|
||||||
|
// BorderStyle returns the border style.
|
||||||
|
func (w *BaseWidget) BorderStyle() BorderStyle {
|
||||||
|
return w.borderStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBorderStyle sets the border style.
|
||||||
|
func (w *BaseWidget) SetBorderStyle(v BorderStyle) {
|
||||||
|
w.borderStyle = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// BorderColor returns the border color, or defaults to the background color.
|
||||||
|
func (w *BaseWidget) BorderColor() render.Color {
|
||||||
|
if w.borderColor == render.Invisible {
|
||||||
|
return w.Background()
|
||||||
|
}
|
||||||
|
return w.borderColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBorderColor sets the border color.
|
||||||
|
func (w *BaseWidget) SetBorderColor(c render.Color) {
|
||||||
|
w.borderColor = c
|
||||||
|
}
|
||||||
|
|
||||||
|
// BorderSize returns the border thickness.
|
||||||
|
func (w *BaseWidget) BorderSize() int32 {
|
||||||
|
return w.borderSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBorderSize sets the border thickness.
|
||||||
|
func (w *BaseWidget) SetBorderSize(v int32) {
|
||||||
|
w.borderSize = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutlineColor returns the background color.
|
||||||
|
func (w *BaseWidget) OutlineColor() render.Color {
|
||||||
|
return w.outlineColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutlineColor sets the color.
|
||||||
|
func (w *BaseWidget) SetOutlineColor(c render.Color) {
|
||||||
|
w.outlineColor = c
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutlineSize returns the outline thickness.
|
||||||
|
func (w *BaseWidget) OutlineSize() int32 {
|
||||||
|
return w.outlineSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutlineSize sets the outline thickness.
|
||||||
|
func (w *BaseWidget) SetOutlineSize(v int32) {
|
||||||
|
w.outlineSize = v
|
||||||
|
}
|
||||||
|
|
||||||
// Event is called internally by Doodle to trigger an event.
|
// Event is called internally by Doodle to trigger an event.
|
||||||
func (w *BaseWidget) Event(name string, p render.Point) {
|
func (w *BaseWidget) Event(name string, p render.Point) {
|
||||||
if handlers, ok := w.handlers[name]; ok {
|
if handlers, ok := w.handlers[name]; ok {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user