Added button types.
Go image can be reconstructed from ui.Image/allg5.Bitmap. Changed allg5.Scale from interface to struct.
This commit is contained in:
parent
a3aa398909
commit
ff51378aff
@ -7,7 +7,6 @@ import "C"
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,30 +20,22 @@ type Bitmap struct {
|
|||||||
|
|
||||||
type DrawOptions struct {
|
type DrawOptions struct {
|
||||||
Center bool
|
Center bool
|
||||||
Scale Scale
|
Scale *Scale
|
||||||
Tint *Color
|
Tint *Color
|
||||||
Rotation *Rotation
|
Rotation *Rotation
|
||||||
}
|
}
|
||||||
|
|
||||||
type Scale interface {
|
type Scale struct {
|
||||||
Horizontal() float32
|
Horizontal float32
|
||||||
Vertical() float32
|
Vertical float32
|
||||||
}
|
}
|
||||||
|
|
||||||
type scale struct {
|
func NewScale(hor, ver float32) *Scale {
|
||||||
horizontal float32
|
return &Scale{hor, ver}
|
||||||
vertical float32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scale) Horizontal() float32 { return s.horizontal }
|
func NewUniformScale(s float32) *Scale {
|
||||||
func (s *scale) Vertical() float32 { return s.vertical }
|
return &Scale{s, s}
|
||||||
|
|
||||||
func NewScale(horizontal, vertical float32) Scale {
|
|
||||||
return &scale{horizontal, vertical}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUniformScale(s float32) Scale {
|
|
||||||
return &scale{s, s}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Rotation struct {
|
type Rotation struct {
|
||||||
@ -92,7 +83,7 @@ func NewMemoryBitmap(width, height int, flags ...NewBitmapFlag) (*Bitmap, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewBitmapFromImage creates a new bitmap starting from a Go native image (image.Image)
|
// NewBitmapFromImage creates a new bitmap starting from a Go native image (image.Image)
|
||||||
func NewBitmapFromImage(im image.Image, video bool) (*Bitmap, error) {
|
func NewBitmapFromImage(src image.Image, video bool) (*Bitmap, error) {
|
||||||
var newBmpFlags = CaptureNewBitmapFlags()
|
var newBmpFlags = CaptureNewBitmapFlags()
|
||||||
defer newBmpFlags.Revert()
|
defer newBmpFlags.Revert()
|
||||||
newBmpFlags.Mutate(func(m FlagMutation) {
|
newBmpFlags.Mutate(func(m FlagMutation) {
|
||||||
@ -100,30 +91,27 @@ func NewBitmapFromImage(im image.Image, video bool) (*Bitmap, error) {
|
|||||||
m.Set(NewBitmapFlagMemoryBitmap)
|
m.Set(NewBitmapFlagMemoryBitmap)
|
||||||
m.Set(NewBitmapFlagMinLinear)
|
m.Set(NewBitmapFlagMinLinear)
|
||||||
})
|
})
|
||||||
var bnd = im.Bounds()
|
var bnd = src.Bounds()
|
||||||
width, height := bnd.Dx(), bnd.Dy()
|
width, height := bnd.Dx(), bnd.Dy()
|
||||||
var b = C.al_create_bitmap(C.int(width), C.int(height))
|
var b = C.al_create_bitmap(C.int(width), C.int(height))
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return nil, errors.New("error creating memory bitmap")
|
return nil, errors.New("error creating memory bitmap")
|
||||||
}
|
}
|
||||||
row := make([]uint8, width*4)
|
region := C.al_lock_bitmap(b, C.ALLEGRO_PIXEL_FORMAT_ABGR_8888, C.ALLEGRO_LOCK_WRITEONLY)
|
||||||
rgn := C.al_lock_bitmap(b, C.ALLEGRO_PIXEL_FORMAT_ABGR_8888, C.ALLEGRO_LOCK_WRITEONLY)
|
if region == nil {
|
||||||
if rgn == nil {
|
|
||||||
C.al_destroy_bitmap(b)
|
C.al_destroy_bitmap(b)
|
||||||
return nil, errors.New("unable to lock bitmap")
|
return nil, errors.New("unable to lock bitmap")
|
||||||
}
|
}
|
||||||
data := (*[1 << 30]uint8)(rgn.data)
|
dst := (*[1 << 30]uint8)(region.data)
|
||||||
offset := 0
|
|
||||||
for y := 0; y < height; y++ {
|
for y := 0; y < height; y++ {
|
||||||
|
row := dst[y*int(region.pitch):]
|
||||||
for x := 0; x < width; x++ {
|
for x := 0; x < width; x++ {
|
||||||
pix := color.RGBAModel.Convert(im.At(x, y)).(color.RGBA)
|
r, g, b, a := src.At(x, y).RGBA()
|
||||||
row[x*4] = pix.R
|
row[x*4] = uint8(r >> 8)
|
||||||
row[x*4+1] = pix.G
|
row[x*4+1] = uint8(g >> 8)
|
||||||
row[x*4+2] = pix.B
|
row[x*4+2] = uint8(b >> 8)
|
||||||
row[x*4+3] = pix.A
|
row[x*4+3] = uint8(a >> 8)
|
||||||
}
|
}
|
||||||
copy(data[offset:], row)
|
|
||||||
offset += int(rgn.pitch)
|
|
||||||
}
|
}
|
||||||
C.al_unlock_bitmap(b)
|
C.al_unlock_bitmap(b)
|
||||||
if video {
|
if video {
|
||||||
@ -159,16 +147,16 @@ func (b *Bitmap) DrawOptions(left, top float32, options DrawOptions) {
|
|||||||
width := float32(b.width)
|
width := float32(b.width)
|
||||||
height := float32(b.height)
|
height := float32(b.height)
|
||||||
|
|
||||||
scale := nil != options.Scale
|
scale := options.Scale != nil
|
||||||
if scale {
|
if scale {
|
||||||
width *= options.Scale.Horizontal()
|
width *= options.Scale.Horizontal
|
||||||
height *= options.Scale.Vertical()
|
height *= options.Scale.Vertical
|
||||||
}
|
}
|
||||||
if options.Center {
|
if options.Center {
|
||||||
left -= width * 0.5
|
left -= width * 0.5
|
||||||
top -= height * 0.5
|
top -= height * 0.5
|
||||||
}
|
}
|
||||||
rotated := nil != options.Rotation
|
rotated := options.Rotation != nil
|
||||||
var centerX C.float
|
var centerX C.float
|
||||||
var centerY C.float
|
var centerY C.float
|
||||||
if rotated && options.Rotation.Center {
|
if rotated && options.Rotation.Center {
|
||||||
@ -179,13 +167,13 @@ func (b *Bitmap) DrawOptions(left, top float32, options DrawOptions) {
|
|||||||
if scale {
|
if scale {
|
||||||
if options.Tint == nil { // scaled
|
if options.Tint == nil { // scaled
|
||||||
if rotated { // scaled & rotated
|
if rotated { // scaled & rotated
|
||||||
C.al_draw_scaled_rotated_bitmap(b.bitmap, centerX, centerY, C.float(left), C.float(top), C.float(options.Scale.Horizontal()), C.float(options.Scale.Vertical()), C.float(options.Rotation.Angle), 0)
|
C.al_draw_scaled_rotated_bitmap(b.bitmap, centerX, centerY, C.float(left), C.float(top), C.float(options.Scale.Horizontal), C.float(options.Scale.Vertical), C.float(options.Rotation.Angle), 0)
|
||||||
} else { // scaled
|
} else { // scaled
|
||||||
C.al_draw_scaled_bitmap(b.bitmap, 0, 0, C.float(b.width), C.float(b.height), C.float(left), C.float(top), C.float(width), C.float(height), 0)
|
C.al_draw_scaled_bitmap(b.bitmap, 0, 0, C.float(b.width), C.float(b.height), C.float(left), C.float(top), C.float(width), C.float(height), 0)
|
||||||
}
|
}
|
||||||
} else { // tinted & scaled
|
} else { // tinted & scaled
|
||||||
if rotated { // scaled, tinted & rotated
|
if rotated { // scaled, tinted & rotated
|
||||||
C.al_draw_tinted_scaled_rotated_bitmap(b.bitmap, options.Tint.color, centerX, centerY, C.float(left), C.float(top), C.float(options.Scale.Horizontal()), C.float(options.Scale.Vertical()), C.float(options.Rotation.Angle), 0)
|
C.al_draw_tinted_scaled_rotated_bitmap(b.bitmap, options.Tint.color, centerX, centerY, C.float(left), C.float(top), C.float(options.Scale.Horizontal), C.float(options.Scale.Vertical), C.float(options.Rotation.Angle), 0)
|
||||||
} else { // tinted, scaled
|
} else { // tinted, scaled
|
||||||
C.al_draw_tinted_scaled_bitmap(b.bitmap, options.Tint.color, 0, 0, C.float(b.width), C.float(b.height), C.float(left), C.float(top), C.float(width), C.float(height), 0)
|
C.al_draw_tinted_scaled_bitmap(b.bitmap, options.Tint.color, 0, 0, C.float(b.width), C.float(b.height), C.float(left), C.float(top), C.float(width), C.float(height), 0)
|
||||||
}
|
}
|
||||||
@ -231,6 +219,24 @@ func (b *Bitmap) Height() int {
|
|||||||
return b.height
|
return b.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Bitmap) Image() image.Image {
|
||||||
|
im := image.NewRGBA(image.Rect(0, 0, b.width, b.height))
|
||||||
|
region := C.al_lock_bitmap(b.bitmap, C.ALLEGRO_PIXEL_FORMAT_ABGR_8888, C.ALLEGRO_LOCK_READONLY)
|
||||||
|
if region == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer C.al_unlock_bitmap(b.bitmap)
|
||||||
|
src := (*[1 << 30]uint8)(region.data)
|
||||||
|
dst := im.Pix
|
||||||
|
var srcOff, dstOff int
|
||||||
|
for y := 0; y < b.height; y++ {
|
||||||
|
copy(dst[dstOff:], src[srcOff:srcOff+b.width*4])
|
||||||
|
srcOff += int(region.pitch)
|
||||||
|
dstOff += im.Stride
|
||||||
|
}
|
||||||
|
return im
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Bitmap) SetAsTarget() {
|
func (b *Bitmap) SetAsTarget() {
|
||||||
C.al_set_target_bitmap(b.bitmap)
|
C.al_set_target_bitmap(b.bitmap)
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,11 @@ func NewColorAlpha(r, g, b, a byte) Color {
|
|||||||
return Color{C.al_map_rgba(C.uchar(r), C.uchar(g), C.uchar(b), C.uchar(a))}
|
return Color{C.al_map_rgba(C.uchar(r), C.uchar(g), C.uchar(b), C.uchar(a))}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewColorGo(c color.Color) Color {
|
||||||
|
r, g, b, a := c.RGBA()
|
||||||
|
return Color{C.al_premul_rgba(C.uchar(r>>8), C.uchar(g>>8), C.uchar(b>>8), C.uchar(a>>8))}
|
||||||
|
}
|
||||||
|
|
||||||
// RGBA implements the color.Color interface.
|
// RGBA implements the color.Color interface.
|
||||||
func (c Color) RGBA() (r, g, b, a uint32) {
|
func (c Color) RGBA() (r, g, b, a uint32) {
|
||||||
var cr, cg, cb, ca C.uchar
|
var cr, cg, cb, ca C.uchar
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package allg5ui
|
package allg5ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image"
|
||||||
|
|
||||||
"opslag.de/schobers/zntg/allg5"
|
"opslag.de/schobers/zntg/allg5"
|
||||||
"opslag.de/schobers/zntg/ui"
|
"opslag.de/schobers/zntg/ui"
|
||||||
)
|
)
|
||||||
@ -11,14 +13,18 @@ type uiImage struct {
|
|||||||
bmp *allg5.Bitmap
|
bmp *allg5.Bitmap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *uiImage) Destroy() {
|
||||||
|
i.bmp.Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
func (i *uiImage) Height() float32 {
|
func (i *uiImage) Height() float32 {
|
||||||
return float32(i.bmp.Height())
|
return float32(i.bmp.Height())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *uiImage) Image() image.Image {
|
||||||
|
return i.bmp.Image()
|
||||||
|
}
|
||||||
|
|
||||||
func (i *uiImage) Width() float32 {
|
func (i *uiImage) Width() float32 {
|
||||||
return float32(i.bmp.Width())
|
return float32(i.bmp.Width())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *uiImage) Destroy() {
|
|
||||||
i.bmp.Destroy()
|
|
||||||
}
|
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
|
|
||||||
"opslag.de/schobers/geom"
|
"opslag.de/schobers/geom"
|
||||||
|
|
||||||
"opslag.de/schobers/zntg/allg5"
|
"opslag.de/schobers/zntg/allg5"
|
||||||
"opslag.de/schobers/zntg/ui"
|
"opslag.de/schobers/zntg/ui"
|
||||||
)
|
)
|
||||||
@ -141,9 +140,24 @@ func (r *Renderer) DefaultTarget() ui.Image {
|
|||||||
return &uiImage{r.disp.Target()}
|
return &uiImage{r.disp.Target()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Renderer) DrawImage(p geom.PointF32, im ui.Image) {
|
func (r *Renderer) DrawImage(im ui.Image, p geom.PointF32) {
|
||||||
bmp := r.mustGetBitmap(im)
|
bmp := r.mustGetBitmap(im)
|
||||||
bmp.Draw(p.X, p.Y)
|
x, y := snap(p)
|
||||||
|
bmp.Draw(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Renderer) DrawImageOptions(im ui.Image, p geom.PointF32, opts ui.DrawOptions) {
|
||||||
|
bmp := r.mustGetBitmap(im)
|
||||||
|
var o allg5.DrawOptions
|
||||||
|
if opts.Tint != nil {
|
||||||
|
tint := newColor(opts.Tint)
|
||||||
|
o.Tint = &tint
|
||||||
|
}
|
||||||
|
if opts.Scale != nil {
|
||||||
|
o.Scale = &allg5.Scale{Horizontal: opts.Scale.X, Vertical: opts.Scale.Y}
|
||||||
|
}
|
||||||
|
x, y := snap(p)
|
||||||
|
bmp.DrawOptions(x, y, o)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Renderer) FillRectangle(rect geom.RectangleF32, c color.Color) {
|
func (r *Renderer) FillRectangle(rect geom.RectangleF32, c color.Color) {
|
||||||
@ -163,7 +177,9 @@ func (r *Renderer) mustGetBitmap(im ui.Image) *allg5.Bitmap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Renderer) Rectangle(rect geom.RectangleF32, c color.Color, thickness float32) {
|
func (r *Renderer) Rectangle(rect geom.RectangleF32, c color.Color, thickness float32) {
|
||||||
allg5.DrawRectangle(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Max.Y, newColor(c), thickness)
|
minX, minY := snap(rect.Min)
|
||||||
|
maxX, maxY := snap(rect.Max)
|
||||||
|
allg5.DrawRectangle(minX, minY, maxX, maxY, newColor(c), thickness)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Renderer) RegisterFont(path, name string, size int) error {
|
func (r *Renderer) RegisterFont(path, name string, size int) error {
|
||||||
@ -224,8 +240,7 @@ func (r *Renderer) Text(p geom.PointF32, font string, c color.Color, t string) {
|
|||||||
if f == nil {
|
if f == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
x := float32(math.Round(float64(p.X)))
|
x, y := snap(p)
|
||||||
y := float32(math.Round(float64(p.Y)))
|
|
||||||
f.f.Draw(x, y, newColor(c), allg5.AlignLeft, t)
|
f.f.Draw(x, y, newColor(c), allg5.AlignLeft, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +265,9 @@ func newColor(c color.Color) allg5.Color {
|
|||||||
if c == nil {
|
if c == nil {
|
||||||
return newColor(color.Black)
|
return newColor(color.Black)
|
||||||
}
|
}
|
||||||
var r, g, b, a = c.RGBA()
|
return allg5.NewColorGo(c)
|
||||||
var r8, g8, b8, a8 = byte(r >> 8), byte(g >> 8), byte(b >> 8), byte(a >> 8)
|
}
|
||||||
return allg5.NewColorAlpha(r8, g8, b8, a8)
|
|
||||||
|
func snap(p geom.PointF32) (float32, float32) {
|
||||||
|
return float32(math.Round(float64(p.X))), float32(math.Round(float64(p.Y)))
|
||||||
}
|
}
|
||||||
|
147
ui/button.go
147
ui/button.go
@ -1,30 +1,60 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/nfnt/resize"
|
||||||
"opslag.de/schobers/geom"
|
"opslag.de/schobers/geom"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Button struct {
|
type Button struct {
|
||||||
ControlBase
|
ControlBase
|
||||||
|
|
||||||
|
Type ButtonType
|
||||||
Text string
|
Text string
|
||||||
|
Icon Image
|
||||||
|
|
||||||
|
scale float32
|
||||||
|
icon Image
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildButton(text string, fn func(b *Button)) *Button {
|
type ButtonType int
|
||||||
var b = &Button{Text: text}
|
|
||||||
|
const (
|
||||||
|
ButtonTypeContained ButtonType = iota
|
||||||
|
ButtonTypeIcon
|
||||||
|
ButtonTypeOutlined
|
||||||
|
ButtonTypeText
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildButton(text string, fn func(b *Button)) *Button { return BuildIconButton(nil, text, fn) }
|
||||||
|
|
||||||
|
func BuildIconButton(i Image, text string, fn func(b *Button)) *Button {
|
||||||
|
var b = &Button{Text: text, Icon: i}
|
||||||
if fn != nil {
|
if fn != nil {
|
||||||
fn(b)
|
fn(b)
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Button) DesiredSize(ctx Context) geom.PointF32 {
|
func (b *Button) desiredSize(ctx Context) geom.PointF32 {
|
||||||
var fontName = b.FontName(ctx)
|
|
||||||
var font = ctx.Renderer().Font(fontName)
|
|
||||||
var width = font.WidthOf(b.Text)
|
|
||||||
var height = font.Height()
|
|
||||||
var pad = ctx.Style().Dimensions.TextPadding
|
var pad = ctx.Style().Dimensions.TextPadding
|
||||||
return geom.PtF32(width+pad*2, height+pad*2)
|
var font = ctx.Renderer().Font(b.FontName(ctx))
|
||||||
|
var w, h float32 = 0, font.Height()
|
||||||
|
if len(b.Text) != 0 {
|
||||||
|
w += font.WidthOf(b.Text) + pad
|
||||||
|
}
|
||||||
|
if b.Icon != nil && b.Icon.Height() > 0 {
|
||||||
|
w += b.Icon.Width()*h/b.Icon.Height() + pad
|
||||||
|
}
|
||||||
|
if w == 0 {
|
||||||
|
return geom.ZeroPtF32
|
||||||
|
}
|
||||||
|
return geom.PtF32(pad+w, pad+h+pad)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Button) DesiredSize(ctx Context) geom.PointF32 {
|
||||||
|
return b.desiredSize(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Button) Handle(ctx Context, e Event) {
|
func (b *Button) Handle(ctx Context, e Event) {
|
||||||
@ -34,18 +64,97 @@ func (b *Button) Handle(ctx Context, e Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Button) Render(ctx Context) {
|
func (b *Button) fillColor(p *Palette) color.Color {
|
||||||
var fore = b.Font.Color
|
if b.Background != nil {
|
||||||
var style = ctx.Style()
|
return b.Background
|
||||||
if fore == nil {
|
|
||||||
fore = style.Palette.TextOnPrimary
|
|
||||||
}
|
}
|
||||||
var fill = style.Palette.Primary
|
|
||||||
if b.over {
|
if b.over {
|
||||||
fill = style.Palette.PrimaryHighlight
|
switch b.Type {
|
||||||
|
case ButtonTypeContained:
|
||||||
|
return p.PrimaryHighlight
|
||||||
|
case ButtonTypeIcon:
|
||||||
|
default:
|
||||||
|
return p.PrimaryLight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch b.Type {
|
||||||
|
case ButtonTypeContained:
|
||||||
|
return p.Primary
|
||||||
|
case ButtonTypeIcon:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Button) scaledIcon(ctx Context, height float32) Image {
|
||||||
|
scale := height / b.Icon.Height()
|
||||||
|
if scale == 1 {
|
||||||
|
b.scale = 1
|
||||||
|
return b.Icon
|
||||||
|
}
|
||||||
|
if b.icon == nil || b.scale != scale {
|
||||||
|
if b.icon != nil {
|
||||||
|
b.icon.Destroy()
|
||||||
|
b.icon = nil
|
||||||
|
}
|
||||||
|
im := resize.Resize(uint(b.Icon.Width()*scale), 0, b.Icon.Image(), resize.Bilinear)
|
||||||
|
icon, err := ctx.Renderer().CreateImage(im)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
b.icon = icon
|
||||||
|
b.scale = scale
|
||||||
|
}
|
||||||
|
return b.icon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Button) textColor(p *Palette) color.Color {
|
||||||
|
if b.Font.Color != nil {
|
||||||
|
return b.Font.Color
|
||||||
|
}
|
||||||
|
switch b.Type {
|
||||||
|
case ButtonTypeContained:
|
||||||
|
return p.TextOnPrimary
|
||||||
|
case ButtonTypeIcon:
|
||||||
|
if b.over {
|
||||||
|
return p.Primary
|
||||||
|
}
|
||||||
|
return p.Text
|
||||||
|
default:
|
||||||
|
return p.Primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Button) Render(ctx Context) {
|
||||||
|
var style = ctx.Style()
|
||||||
|
var palette = style.Palette
|
||||||
|
textColor := b.textColor(palette)
|
||||||
|
fillColor := b.fillColor(palette)
|
||||||
|
if fillColor != nil {
|
||||||
|
ctx.Renderer().FillRectangle(b.bounds, fillColor)
|
||||||
|
}
|
||||||
|
size := b.desiredSize(ctx)
|
||||||
|
bounds := b.bounds
|
||||||
|
deltaX, deltaY := bounds.Dx()-size.X, bounds.Dy()-size.Y
|
||||||
|
bounds.Min.X += .5 * deltaX
|
||||||
|
bounds.Min.Y += .5 * deltaY
|
||||||
|
|
||||||
|
var pad = style.Dimensions.TextPadding
|
||||||
|
bounds = bounds.Inset(pad)
|
||||||
|
pos := bounds.Min
|
||||||
|
if b.Icon != nil && b.Icon.Height() > 0 {
|
||||||
|
icon := b.scaledIcon(ctx, bounds.Dy())
|
||||||
|
if icon != nil {
|
||||||
|
ctx.Renderer().DrawImageOptions(icon, pos, DrawOptions{Tint: textColor})
|
||||||
|
pos.X += icon.Width() + pad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(b.Text) != 0 {
|
||||||
|
var fontName = b.FontName(ctx)
|
||||||
|
var font = ctx.Renderer().Font(fontName)
|
||||||
|
ctx.Renderer().Text(geom.PtF32(pos.X, pos.Y+.5*(bounds.Dy()-font.Height())), fontName, textColor, b.Text)
|
||||||
|
}
|
||||||
|
if b.Type == ButtonTypeOutlined {
|
||||||
|
ctx.Renderer().Rectangle(b.bounds, palette.TextDisabled, 1)
|
||||||
}
|
}
|
||||||
var pad = style.Dimensions.TextPadding
|
|
||||||
var font = b.FontName(ctx)
|
|
||||||
ctx.Renderer().FillRectangle(b.bounds, fill)
|
|
||||||
ctx.Renderer().Text(b.bounds.Min.Add(geom.PtF32(pad, pad)), font, fore, b.Text)
|
|
||||||
}
|
}
|
||||||
|
12
ui/drawoptions.go
Normal file
12
ui/drawoptions.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DrawOptions struct {
|
||||||
|
Tint color.Color
|
||||||
|
Scale *geom.PointF32
|
||||||
|
}
|
@ -22,16 +22,25 @@ func run() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
plus, err := render.CreateImagePath("../resources/images/plus.png")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer plus.Destroy()
|
||||||
|
|
||||||
var view = &ui.StackPanel{ContainerBase: ui.ContainerBase{
|
var view = &ui.StackPanel{ContainerBase: ui.ContainerBase{
|
||||||
ControlBase: ui.ControlBase{Background: color.White},
|
ControlBase: ui.ControlBase{Background: color.White},
|
||||||
Children: []ui.Control{
|
Children: []ui.Control{
|
||||||
&ui.Label{Text: "Hello, world!"},
|
&ui.Label{Text: "Hello, world!"},
|
||||||
ui.BuildButton("Quit", func(b *ui.Button) {
|
ui.Margin(ui.BuildIconButton(plus, "Contained", func(b *ui.Button) { b.Type = ui.ButtonTypeContained }), 8),
|
||||||
|
ui.Margin(ui.BuildIconButton(plus, "Icon", func(b *ui.Button) { b.Type = ui.ButtonTypeIcon }), 8),
|
||||||
|
ui.Margin(ui.BuildIconButton(plus, "Outlined", func(b *ui.Button) { b.Type = ui.ButtonTypeOutlined }), 8),
|
||||||
|
ui.Margin(ui.BuildIconButton(plus, "Text", func(b *ui.Button) { b.Type = ui.ButtonTypeText }), 8),
|
||||||
|
ui.Margin(ui.BuildButton("Quit", func(b *ui.Button) {
|
||||||
b.OnClick(func(ctx ui.Context, _ ui.Control, _ geom.PointF32, _ ui.MouseButton) {
|
b.OnClick(func(ctx ui.Context, _ ui.Control, _ geom.PointF32, _ ui.MouseButton) {
|
||||||
ctx.Quit()
|
ctx.Quit()
|
||||||
})
|
})
|
||||||
}),
|
}), 8),
|
||||||
ui.Stretch(&ui.Label{Text: "Content"}),
|
ui.Stretch(&ui.Label{Text: "Content"}),
|
||||||
&ui.Label{Text: "Status"},
|
&ui.Label{Text: "Status"},
|
||||||
},
|
},
|
||||||
|
BIN
ui/examples/resources/images/plus.png
Normal file
BIN
ui/examples/resources/images/plus.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
@ -1,7 +1,10 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
|
import "image"
|
||||||
|
|
||||||
type Image interface {
|
type Image interface {
|
||||||
Height() float32
|
|
||||||
Width() float32
|
|
||||||
Destroy()
|
Destroy()
|
||||||
|
Height() float32
|
||||||
|
Image() image.Image
|
||||||
|
Width() float32
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ func (o *overflow) Render(ctx Context) {
|
|||||||
renderer.Clear(color.Transparent)
|
renderer.Clear(color.Transparent)
|
||||||
o.Content.Render(ctx)
|
o.Content.Render(ctx)
|
||||||
renderer.RenderTo(target)
|
renderer.RenderTo(target)
|
||||||
renderer.DrawImage(o.bounds.Min, o.buffer)
|
renderer.DrawImage(o.buffer, o.bounds.Min)
|
||||||
|
|
||||||
o.doOnVisibleBars(func(bar *Scrollbar) {
|
o.doOnVisibleBars(func(bar *Scrollbar) {
|
||||||
bar.Render(ctx)
|
bar.Render(ctx)
|
||||||
|
@ -21,7 +21,8 @@ type Renderer interface {
|
|||||||
CreateImagePath(path string) (Image, error)
|
CreateImagePath(path string) (Image, error)
|
||||||
CreateImageSize(w, h float32) (Image, error)
|
CreateImageSize(w, h float32) (Image, error)
|
||||||
DefaultTarget() Image
|
DefaultTarget() Image
|
||||||
DrawImage(p geom.PointF32, im Image)
|
DrawImage(im Image, p geom.PointF32)
|
||||||
|
DrawImageOptions(im Image, p geom.PointF32, opts DrawOptions)
|
||||||
FillRectangle(r geom.RectangleF32, c color.Color)
|
FillRectangle(r geom.RectangleF32, c color.Color)
|
||||||
Font(font string) Font
|
Font(font string) Font
|
||||||
Rectangle(r geom.RectangleF32, c color.Color, thickness float32)
|
Rectangle(r geom.RectangleF32, c color.Color, thickness float32)
|
||||||
|
20
ui/style.go
20
ui/style.go
@ -23,8 +23,8 @@ type Palette struct {
|
|||||||
Primary color.Color
|
Primary color.Color
|
||||||
// PrimaryHighlight is a highlighted version of the main contrast color.
|
// PrimaryHighlight is a highlighted version of the main contrast color.
|
||||||
PrimaryHighlight color.Color
|
PrimaryHighlight color.Color
|
||||||
// PrimaryBackground is a background version of the main contrast color.
|
// PrimaryLight is a background version of the main contrast color.
|
||||||
PrimaryBackground color.Color
|
PrimaryLight color.Color
|
||||||
// ShadedBackground is a darker version of the background color.
|
// ShadedBackground is a darker version of the background color.
|
||||||
ShadedBackground color.Color
|
ShadedBackground color.Color
|
||||||
// Text is the default text color.
|
// Text is the default text color.
|
||||||
@ -63,14 +63,14 @@ func DefaultFonts() *Fonts {
|
|||||||
func DefaultPalette() *Palette {
|
func DefaultPalette() *Palette {
|
||||||
if defaultPalette == nil {
|
if defaultPalette == nil {
|
||||||
defaultPalette = &Palette{
|
defaultPalette = &Palette{
|
||||||
Background: color.White,
|
Background: color.White,
|
||||||
Primary: RGBA(0x3F, 0x51, 0xB5, 0xFF),
|
Primary: RGBA(0x3F, 0x51, 0xB5, 0xFF),
|
||||||
PrimaryHighlight: RGBA(0x5C, 0x6B, 0xC0, 0xFF),
|
PrimaryHighlight: RGBA(0x5C, 0x6B, 0xC0, 0xFF),
|
||||||
PrimaryBackground: RGBA(0x9F, 0xA8, 0xDA, 0xFF),
|
PrimaryLight: RGBA(0xE8, 0xEA, 0xF6, 0xFF),
|
||||||
ShadedBackground: RGBA(0xFA, 0xFA, 0xFA, 0xFF),
|
ShadedBackground: RGBA(0xFA, 0xFA, 0xFA, 0xFF),
|
||||||
Text: color.Black,
|
Text: color.Black,
|
||||||
TextDisabled: RGBA(0xBD, 0xBD, 0xBD, 0xFF),
|
TextDisabled: RGBA(0xBD, 0xBD, 0xBD, 0xFF),
|
||||||
TextOnPrimary: color.White,
|
TextOnPrimary: color.White,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return defaultPalette
|
return defaultPalette
|
||||||
|
Loading…
Reference in New Issue
Block a user