Some changes to UI elements.

- Added colors to palette and renamed white/black to lightest/darkest.
- Extracted drawing code.
- Restyled checkbox, spinner and scrollbar.
This commit is contained in:
Sander Schobers 2018-08-07 07:03:52 +02:00
parent 115f9877c1
commit 239c24533d
9 changed files with 223 additions and 153 deletions

View File

@ -2,7 +2,6 @@ package ui
import (
"fmt"
"image"
"image/color"
"github.com/llgcode/draw2d/draw2dimg"
@ -13,50 +12,34 @@ import (
type CheckboxValueChangedFn func(bool)
func createOnBitmap(fill, stroke color.Color) *allegro5.Bitmap {
var sz = float64(checkboxSize)
on := image.NewRGBA(image.Rect(0, 0, checkboxSize, checkboxSize))
func drawCheckedBitmap(fill, stroke color.Color) *allegro5.Bitmap {
return drawBitmap(checkboxSize, checkboxSize, func(gc *draw2dimg.GraphicContext) {
var size = float64(checkboxSize)
var margin = float64(checkboxMargin)
gc := draw2dimg.NewGraphicContext(on)
gc.SetFillColor(color.Transparent)
gc.Clear()
gc.SetFillColor(fill)
draw2dkit.RoundedRectangle(gc, 0, 0, sz, sz, 3, 3)
draw2dkit.RoundedRectangle(gc, margin, margin, size-margin, size-margin, margin, margin)
gc.Fill()
gc.SetStrokeColor(stroke)
gc.SetLineWidth(2)
gc.MoveTo(2.5, 5.5)
gc.LineTo(5.5, 8.5)
gc.LineTo(10, 3.5)
gc.SetLineWidth(1.5)
gc.MoveTo(7, 12)
gc.LineTo(10.5, 15.5)
gc.LineTo(17, 9)
gc.Stroke()
bmp, err := allegro5.NewBitmapFromImage(on, false)
if nil != err {
return nil
}
return bmp
})
}
func createOffBitmap(fill, stroke color.Color) *allegro5.Bitmap {
var sz = float64(checkboxSize)
off := image.NewRGBA(image.Rect(0, 0, checkboxSize, checkboxSize))
func drawUncheckedBitmap(fill, stroke color.Color) *allegro5.Bitmap {
return drawBitmap(checkboxSize, checkboxSize, func(gc *draw2dimg.GraphicContext) {
var size = float64(checkboxSize)
var margin = float64(checkboxMargin)
gc := draw2dimg.NewGraphicContext(off)
gc.SetFillColor(color.Transparent)
gc.Clear()
gc.SetFillColor(stroke)
draw2dkit.RoundedRectangle(gc, 0, 0, sz, sz, 4, 4)
gc.Fill()
gc.SetFillColor(fill)
draw2dkit.RoundedRectangle(gc, 1, 1, sz-1, sz-1, 3, 3)
gc.Fill()
bmp, err := allegro5.NewBitmapFromImage(off, false)
if nil != err {
return nil
}
return bmp
gc.SetLineWidth(2)
gc.SetStrokeColor(stroke)
draw2dkit.RoundedRectangle(gc, margin+1, margin+1, size-margin-1, size-margin-1, margin-1, margin-1)
gc.Stroke()
})
}
type Checkbox struct {
@ -64,8 +47,8 @@ type Checkbox struct {
Value bool
Text string
OnChanged CheckboxValueChangedFn
on *allegro5.Bitmap
off *allegro5.Bitmap
checked *allegro5.Bitmap
unchecked *allegro5.Bitmap
}
func (c *Checkbox) Created(ctx Context, p Container) error {
@ -74,9 +57,9 @@ func (c *Checkbox) Created(ctx Context, p Container) error {
return err
}
var plt = ctx.Palette()
c.on = createOnBitmap(plt.Primary(), plt.White())
c.off = createOffBitmap(plt.White(), plt.Black())
if nil == c.on || nil == c.off {
c.checked = drawCheckedBitmap(plt.Primary(), plt.Lightest())
c.unchecked = drawUncheckedBitmap(plt.Lightest(), plt.Darkest())
if nil == c.checked || nil == c.unchecked {
return fmt.Errorf("error creating checkboxes")
}
return nil
@ -101,32 +84,28 @@ func (c *Checkbox) DesiredSize(ctx Context) geom.PointF {
var fonts = ctx.Fonts()
var fnt = fonts.Get("default")
var w = fnt.TextWidth(c.Text)
return geom.PtF(float64(w+2*leftMargin+checkboxSize), 24)
return geom.PtF(float64(w+checkboxSize+leftMargin), checkboxSize)
}
func (c *Checkbox) box() *allegro5.Bitmap {
if c.Value {
return c.on
return c.checked
}
return c.off
return c.unchecked
}
func (c *Checkbox) Render(ctx Context) {
var fonts = ctx.Fonts()
var min = c.Bounds.Min.To32()
min = geom.PointF32{X: min.X + leftMargin, Y: min.Y + topMargin}
var fnt = fonts.Get("default")
var _, textY, _, textH = fnt.TextDims(c.Text)
fnt.Draw(min.X+leftMargin+checkboxSize, min.Y-textY, ctx.Palette().Black(), allegro5.AlignLeft, c.Text)
var checkboxTop = min.Y + textH - checkboxSize
fnt.Draw(min.X+checkboxSize, min.Y-.67*fnt.Ascent()+.5*checkboxSize, ctx.Palette().Darkest(), allegro5.AlignLeft, c.Text)
if c.Disabled {
var disabled = ctx.Palette().Disabled()
c.box().DrawOptions(min.X, checkboxTop, allegro5.DrawOptions{Tint: &disabled})
c.box().DrawOptions(min.X, min.Y, allegro5.DrawOptions{Tint: &disabled})
} else {
c.box().Draw(min.X, checkboxTop)
c.box().Draw(min.X, min.Y)
}
c.ControlBase.Render(ctx)

View File

@ -32,7 +32,7 @@ type debug struct {
func rainbow() []allegro5.Color {
var colors = make([]allegro5.Color, len(Colors500))
for i, c := range Colors500 {
colors[i] = NewColorAlpha(c, 0xbf)
colors[i] = NewColorAlpha(c, 0x7f)
}
return colors
}

View File

@ -2,4 +2,6 @@ package ui
const topMargin = 4
const leftMargin = 8
const checkboxSize = 12
const lineHeight = 16
const checkboxMargin = 4
const checkboxSize = 24

38
ui/draw.go Normal file
View File

@ -0,0 +1,38 @@
package ui
import (
"image"
"image/color"
"math"
"github.com/llgcode/draw2d/draw2dimg"
"opslag.de/schobers/galleg/allegro5"
)
func drawBitmap(w, h int, draw func(*draw2dimg.GraphicContext)) *allegro5.Bitmap {
dest := image.NewRGBA(image.Rect(0, 0, w, h))
gc := draw2dimg.NewGraphicContext(dest)
gc.SetFillColor(color.Transparent)
gc.Clear()
draw(gc)
bmp, err := allegro5.NewBitmapFromImage(dest, false)
if nil != err {
return nil
}
return bmp
}
func drawCircle(r, w int, startAngle, a float64, c color.Color) *allegro5.Bitmap {
var width = 2*r + w
return drawBitmap(width, width, func(gc *draw2dimg.GraphicContext) {
gc.SetFillColor(c)
gc.SetStrokeColor(c)
gc.SetLineWidth(float64(w))
var rad = float64(r)
var cnt = float64(width) * .5
var dx1, dy1 = cnt + math.Cos(startAngle)*rad, cnt + math.Sin(startAngle)*rad
gc.MoveTo(dx1, dy1)
gc.ArcTo(cnt, cnt, rad, rad, startAngle, a)
gc.Stroke()
})
}

View File

@ -13,7 +13,7 @@ func (l *Label) Render(ctx Context) {
var min = l.Bounds.Min.To32()
var fnt = fonts.Get("default")
fnt.Draw(min.X+leftMargin, min.Y+topMargin, ctx.Palette().Black(), allegro5.AlignLeft, l.Text)
fnt.Draw(min.X+leftMargin, min.Y+lineHeight-fnt.Ascent(), ctx.Palette().Darkest(), allegro5.AlignLeft, l.Text)
l.ControlBase.Render(ctx)
}

View File

@ -8,27 +8,42 @@ import (
type Palette interface {
Primary() allegro5.Color
White() allegro5.Color
Black() allegro5.Color
PrimaryTransparent() allegro5.Color
Lightest() allegro5.Color
Darker() allegro5.Color
Darkest() allegro5.Color
Disabled() allegro5.Color
}
type palette struct {
primary allegro5.Color
white allegro5.Color
black allegro5.Color
primaryT allegro5.Color
lightest allegro5.Color
darker allegro5.Color
darkest allegro5.Color
disabled allegro5.Color
}
func (p *palette) Primary() allegro5.Color {
return p.primary
}
func (p *palette) White() allegro5.Color {
return p.white
func (p *palette) PrimaryTransparent() allegro5.Color {
return p.primaryT
}
func (p *palette) Black() allegro5.Color {
return p.black
func (p *palette) Lightest() allegro5.Color {
return p.lightest
}
func (p *palette) Darker() allegro5.Color {
return p.darker
}
func (p *palette) Darkest() allegro5.Color {
return p.darkest
}
func (p *palette) Disabled() allegro5.Color {
return p.disabled
}
@ -42,10 +57,13 @@ func NewColorAlpha(c *color.RGBA, a uint8) allegro5.Color {
}
func DefaultPalette() Palette {
var primary = Blue500
return &palette{
primary: NewColor(Blue500),
white: allegro5.NewColor(0xff, 0xff, 0xff),
black: allegro5.NewColor(0, 0, 0),
primary: NewColor(primary),
primaryT: NewColorAlpha(primary, 96),
lightest: allegro5.NewColor(0xff, 0xff, 0xff),
darker: allegro5.NewColorAlpha(0, 0, 0, 188),
darkest: allegro5.NewColorAlpha(0, 0, 0, 222),
disabled: allegro5.NewColorAlpha(0x1f, 0x1f, 0x1f, 0x1f),
}
}

View File

@ -3,13 +3,15 @@ package ui
import (
"math"
"github.com/llgcode/draw2d/draw2dimg"
"opslag.de/schobers/galleg/allegro5"
"opslag.de/schobers/geom"
)
var _ Control = &Scrollbar{}
const ScrollbarWidth = 26
const ScrollbarWidth = 12
const ScrollbarHandleThickness = 4
const ScrollbarHandlePadding = 1
@ -23,27 +25,57 @@ type Scrollbar struct {
Orientation Orientation
OnChanged ScrollbarValueChangedFn
handle *ControlBase
handles []*allegro5.Bitmap
normal *allegro5.Bitmap
hover *allegro5.Bitmap
pressed *allegro5.Bitmap
}
func (s *Scrollbar) Created(ctx Context, p Container) error {
s.ControlBase.Created(ctx, p)
s.handles = []*allegro5.Bitmap{
createCirle((ScrollbarWidth/2)-ScrollbarHandleThickness-2*ScrollbarHandlePadding, ScrollbarHandleThickness, 0, math.Pi*2, Blue500),
createCirle((ScrollbarWidth/2)-ScrollbarHandleThickness-2*ScrollbarHandlePadding, ScrollbarHandleThickness, 0, math.Pi*2, Blue300),
createCirle((ScrollbarWidth/2)-ScrollbarHandleThickness-2*ScrollbarHandlePadding, ScrollbarHandleThickness, 0, math.Pi*2, Blue700),
var center = float64(ScrollbarWidth) * 1.5
var rad = float64(ScrollbarWidth) * .5
s.normal = drawBitmap(3*ScrollbarWidth, 3*ScrollbarWidth, func(gc *draw2dimg.GraphicContext) {
gc.SetFillColor(ctx.Palette().Primary())
gc.MoveTo(center, center)
gc.ArcTo(center, center, rad, rad, 0, 2*math.Pi)
gc.Fill()
})
s.hover = drawBitmap(3*ScrollbarWidth, 3*ScrollbarWidth, func(gc *draw2dimg.GraphicContext) {
gc.SetFillColor(ctx.Palette().PrimaryTransparent())
gc.MoveTo(center, center)
gc.ArcTo(center, center, center, center, 0, 2*math.Pi)
gc.Fill()
gc.SetFillColor(ctx.Palette().Primary())
gc.MoveTo(center, center)
gc.ArcTo(center, center, rad, rad, 0, 2*math.Pi)
gc.Fill()
})
s.pressed = drawBitmap(3*ScrollbarWidth, 3*ScrollbarWidth, func(gc *draw2dimg.GraphicContext) {
gc.SetFillColor(ctx.Palette().PrimaryTransparent())
for i := 0; i < 2; i++ {
gc.MoveTo(center, center)
gc.ArcTo(center, center, center, center, 0, 2*math.Pi)
gc.Fill()
}
gc.SetFillColor(ctx.Palette().Primary())
gc.MoveTo(center, center)
gc.ArcTo(center, center, rad, rad, 0, 2*math.Pi)
gc.Fill()
})
s.handle = &ControlBase{}
s.handle.Created(ctx, nil)
return nil
}
func (s *Scrollbar) Destroyed(ctx Context) {
if nil != s.handles {
for _, h := range s.handles {
h.Destroy()
var d = func(b *allegro5.Bitmap) {
if nil != b {
b.Destroy()
}
}
d(s.normal)
d(s.hover)
d(s.pressed)
s.handle.Destroyed(ctx)
}
@ -52,12 +84,12 @@ func (s *Scrollbar) barViewRange() (float64, float64) {
var min, max float64
switch s.Orientation {
case OrientationHorizontal:
min = s.Bounds.Min.X + ScrollbarWidth*.5
max = s.Bounds.Max.X - ScrollbarWidth*.5
min = s.Bounds.Min.X + ScrollbarWidth
max = s.Bounds.Max.X - ScrollbarWidth
small = min > max
default:
min = s.Bounds.Max.Y - ScrollbarWidth*.5
max = s.Bounds.Min.Y + ScrollbarWidth*.5
min = s.Bounds.Max.Y - ScrollbarWidth
max = s.Bounds.Min.Y + ScrollbarWidth
small = max > min
}
if small {
@ -143,22 +175,24 @@ func (s *Scrollbar) Handle(ctx Context, ev allegro5.Event) {
}
func (s *Scrollbar) DesiredSize(Context) geom.PointF {
var width = float64(2 * ScrollbarWidth)
switch s.Orientation {
case OrientationHorizontal:
return geom.PtF(math.NaN(), ScrollbarWidth)
return geom.PtF(math.NaN(), width)
}
return geom.PtF(ScrollbarWidth, math.NaN())
return geom.PtF(width, math.NaN())
}
func (s *Scrollbar) SetRect(rect geom.RectangleF) {
var width = float64(2 * ScrollbarWidth)
switch s.Orientation {
case OrientationHorizontal:
if rect.Dy() > ScrollbarWidth {
rect.Min.Y = rect.Max.Y - ScrollbarWidth
if rect.Dy() > width {
rect.Min.Y = rect.Max.Y - width
}
default:
if rect.Dx() > ScrollbarWidth {
rect.Min.X = rect.Max.X - ScrollbarWidth
if rect.Dx() > width {
rect.Min.X = rect.Max.X - width
}
}
s.ControlBase.SetRect(rect)
@ -166,7 +200,7 @@ func (s *Scrollbar) SetRect(rect geom.RectangleF) {
var min, max = s.barViewRange()
var off = float64(s.Value-s.Minimum) / float64(s.Maximum-s.Minimum)
var centerH = min + (max-min)*off
var r = 0.5 * float64(s.handles[0].Width())
var r = 0.5 * float64(s.normal.Width())
var center = s.barViewCenter()
switch s.Orientation {
@ -182,32 +216,28 @@ func (s *Scrollbar) Render(ctx Context) {
var min64, max64 = s.barViewRange()
var min, max = float32(min64), float32(max64)
var minH = s.handle.Bounds.Min.To32()
var stateH = 0
if s.handle.IsOver {
stateH = 1
if s.handle.IsPressed {
stateH = 2
}
}
s.handles[stateH].Draw(minH.X, minH.Y)
var maxH = s.handle.Bounds.Max.To32()
var centerH = s.handle.Bounds.Center().To32()
switch s.Orientation {
case OrientationHorizontal:
if min < minH.X-1 { // Top line
allegro5.DrawLine(min, center, minH.X-1, center, ctx.Palette().Black(), 1)
}
if max > maxH.X+1 { // Bottom line
allegro5.DrawLine(maxH.X+1, center, max, center, ctx.Palette().Black(), 1)
}
// Left line
allegro5.DrawLine(min, center, centerH.X, center, ctx.Palette().Primary(), 2)
allegro5.DrawLine(centerH.X, center, max, center, ctx.Palette().PrimaryTransparent(), 2)
default:
if max < minH.Y-1 { // Top line
allegro5.DrawLine(center, max, center, minH.Y-1, ctx.Palette().Black(), 1)
allegro5.DrawLine(center, max, center, centerH.Y, ctx.Palette().PrimaryTransparent(), 2)
allegro5.DrawLine(center, centerH.Y, center, min, ctx.Palette().Primary(), 2)
}
if min > maxH.Y+1 { // Bottom line
allegro5.DrawLine(center, maxH.Y+1, center, min, ctx.Palette().Black(), 1)
var minH = s.handle.Bounds.Min.To32()
var state = s.normal
if s.handle.IsOver {
if s.handle.IsPressed {
state = s.pressed
} else {
state = s.hover
}
}
state.Draw(minH.X, minH.Y)
s.ControlBase.Render(ctx)
}

View File

@ -1,12 +1,11 @@
package ui
import (
"image"
"image/color"
"math"
"time"
"github.com/llgcode/draw2d/draw2dimg"
"opslag.de/schobers/galleg/allegro5"
)
@ -16,53 +15,56 @@ type Spinner struct {
ControlBase
Text string
spin float32
circs []*allegro5.Bitmap
}
func createCirle(r, w int, startAngle, a float64, c color.Color) *allegro5.Bitmap {
var width = 2*r + w
dest := image.NewRGBA(image.Rect(0, 0, width, width))
gc := draw2dimg.NewGraphicContext(dest)
gc.SetFillColor(color.Transparent)
gc.Clear()
gc.SetFillColor(c)
gc.SetStrokeColor(c)
gc.SetLineWidth(float64(w))
var rad = float64(r)
var cnt = float64(width) * .5
var dx1, dy1 = cnt + math.Cos(startAngle)*rad, cnt + math.Sin(startAngle)*rad
gc.MoveTo(dx1, dy1)
gc.ArcTo(cnt, cnt, rad, rad, startAngle, a)
gc.Stroke()
bmp, err := allegro5.NewBitmapFromImage(dest, false)
if nil != err {
return nil
}
return bmp
circs *allegro5.Bitmap
}
func (s *Spinner) Created(ctx Context, p Container) error {
s.ControlBase.Created(ctx, p)
const numCircs = 32
s.circs = make([]*allegro5.Bitmap, numCircs)
var am = math.Pi * 2 / float64(numCircs)
for i := range s.circs {
s.circs[i] = createCirle(8, 3, float64(i)*am, math.Pi, Blue500)
const row = 6
const numCircs = row * row
const width = 48
const center = width * .5
const full = 2 * math.Pi
s.circs = drawBitmap(row*width, row*width, func(gc *draw2dimg.GraphicContext) {
var start, a float64 = 0, .2 * math.Pi
var inc = true
gc.SetLineWidth(4)
gc.SetStrokeColor(ctx.Palette().Primary())
for i := 0; i < numCircs; i++ {
var left = float64(i%row) * width
var top = float64(i/row) * width
gc.ArcTo(left+center, top+center, center-8, center-8, start, a)
gc.Stroke()
if inc {
start += full / numCircs
a += 1.90 * full / numCircs
if a >= full {
inc = false
}
} else {
start += 2.90 * full / numCircs
a -= 1.90 * full / numCircs
// if a <= .1*math.Pi {
// inc = true
// }
}
}
})
for i := 0; i < numCircs; i++ {
var left = (i % row) * width
var top = (i / row) * width
s.circs.Sub(left, top, width, width)
}
return nil
}
func (s *Spinner) Destroyed(ctx Context) {
for _, circ := range s.circs {
circ.Destroy()
}
s.circs.Destroy()
}
func (s *Spinner) Update(ctx Context, dt time.Duration) {
var spin = float64(s.spin)
spin += dt.Seconds()
spin += dt.Seconds() * .5
for spin > 1. {
spin -= 1.
}
@ -85,13 +87,13 @@ func (s *Spinner) Render(ctx Context) {
const textH float32 = 12
var rectW, rectH float32 = textW + 2*marginH, 3*marginV + textH + 32
var rectX, rectY = (width - rectW) * .5, (height - rectH) * .5
allegro5.DrawFilledRectangle(rectX, rectY, rectX+rectW, rectY+rectH, ctx.Palette().White())
allegro5.DrawFilledRectangle(rectX, rectY, rectX+rectW, rectY+rectH, ctx.Palette().Lightest())
DropShadow(rectX, rectY, rectX+rectW, rectY+rectH)
fnt.Draw(rectX+marginH, rectY+marginV, ctx.Palette().Black(), allegro5.AlignLeft, s.Text)
fnt.Draw(rectX+marginH, rectY+marginV, ctx.Palette().Darkest(), allegro5.AlignLeft, s.Text)
const numCircs = 32
const numCircs = 36
var i = int(math.Floor(float64(s.spin) * numCircs))
s.circs[i].DrawOptions(width*.5, rectY+2*marginV+2*textH, allegro5.DrawOptions{Center: true})
s.circs.Subs()[i].DrawOptions(width*.5, rectY+2*marginV+2*textH, allegro5.DrawOptions{Center: true})
s.ControlBase.Render(ctx)
}

View File

@ -29,8 +29,9 @@ func (b *StatusBar) Render(ctx Context) {
allegro5.DrawFilledRectangle(min.X, min.Y, max.X, max.Y, ctx.Palette().Primary())
var fnt = fonts.Get("default")
fnt.Draw(min.X+leftMargin, min.Y+topMargin, ctx.Palette().White(), allegro5.AlignLeft, b.Text)
fnt.Draw(max.X-leftMargin, min.Y+topMargin, ctx.Palette().White(), allegro5.AlignRight, b.RightText)
var y = min.Y + float32(.5*b.Bounds.Dy()) - .67*fnt.Ascent()
fnt.Draw(min.X+leftMargin, y, ctx.Palette().Lightest(), allegro5.AlignLeft, b.Text)
fnt.Draw(max.X-leftMargin, y, ctx.Palette().Lightest(), allegro5.AlignRight, b.RightText)
b.ControlBase.Render(ctx)
}