From 3591e22c971ed21e4c3ba08ebae6d04afc53c4d0 Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Fri, 15 May 2020 15:42:24 +0200 Subject: [PATCH] Added Fonts() to context similarly as Textures(). - Fonts are now managed by context instead of the implementation specific renderers. --- allg5ui/font.go | 3 +- allg5ui/renderer.go | 66 ++++++++++---------------------- colors.go | 10 ++++- sdlui/font.go | 5 +++ sdlui/renderer.go | 50 ++++++++++-------------- ui/button.go | 8 ++-- ui/checkbox.go | 6 +-- ui/context.go | 23 ++++++++++- ui/examples/01_basic/basic.go | 12 +++--- ui/font.go | 1 + ui/fonts.go | 70 ++++++++++++++++++++++++++++++++++ ui/label.go | 12 +++--- ui/renderer.go | 7 ++-- ui/scrollbar.go | 3 +- ui/style.go | 72 +++++++++++++++-------------------- ui/textbox.go | 13 ++++--- ui/ui.go | 4 +- ui/visual.go | 3 +- 18 files changed, 215 insertions(+), 153 deletions(-) create mode 100644 ui/fonts.go diff --git a/allg5ui/font.go b/allg5ui/font.go index 1f9c63a..fb502ed 100644 --- a/allg5ui/font.go +++ b/allg5ui/font.go @@ -22,8 +22,9 @@ func newFont(f *allg5.Font) *font { return &font{f} } -func (f *font) Destroy() { +func (f *font) Destroy() error { f.Font.Destroy() + return nil } func (f *font) Height() float32 { diff --git a/allg5ui/renderer.go b/allg5ui/renderer.go index f1f631d..a3c4df7 100644 --- a/allg5ui/renderer.go +++ b/allg5ui/renderer.go @@ -47,7 +47,7 @@ func NewRenderer(w, h int, opts allg5.NewDisplayOptions) (*Renderer, error) { }) clean = nil - return &Renderer{disp, eq, nil, map[string]*font{}, user, &ui.OSResources{}, ui.KeyState{}, ui.KeyModifierNone, ui.MouseCursorDefault}, nil + return &Renderer{disp, eq, nil, user, &ui.OSResources{}, ui.KeyState{}, ui.KeyModifierNone, ui.MouseCursorDefault}, nil } // Renderer implements ui.Renderer using Allegro 5. @@ -55,7 +55,6 @@ type Renderer struct { disp *allg5.Display eq *allg5.EventQueue unh func(allg5.Event) - ft map[string]*font user *allg5.UserEventSource res ui.Resources @@ -149,10 +148,6 @@ func (r *Renderer) Refresh() { func (r *Renderer) Destroy() error { r.user.Destroy() r.eq.Destroy() - for _, f := range r.ft { - f.Destroy() - } - r.ft = nil r.disp.Destroy() r.res.Destroy() return nil @@ -164,6 +159,18 @@ func (r *Renderer) Clear(c color.Color) { allg5.ClearToColor(newColor(c)) } +func (r *Renderer) CreateFontPath(path string, size int) (ui.Font, error) { + path, err := r.res.FetchResource(path) + if err != nil { + return nil, err + } + f, err := allg5.LoadTTFFont(path, size) + if err != nil { + return nil, err + } + return &font{f}, nil +} + func (r *Renderer) createTexture(source ui.ImageSource, keepSource bool) (ui.Texture, error) { im, err := source.CreateImage() if err != nil { @@ -237,10 +244,6 @@ func (r *Renderer) FillRectangle(rect geom.RectangleF32, c color.Color) { allg5.DrawFilledRectangle(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Max.Y, newColor(c)) } -func (r *Renderer) Font(name string) ui.Font { - return r.ft[name] -} - func (r *Renderer) mustGetBitmap(t ui.Texture) *allg5.Bitmap { texture, ok := t.(*texture) if !ok { @@ -255,33 +258,6 @@ func (r *Renderer) Rectangle(rect geom.RectangleF32, c color.Color, thickness fl allg5.DrawRectangle(minX, minY, maxX, maxY, newColor(c), thickness) } -func (r *Renderer) RegisterFont(name, path string, size int) error { - path, err := r.res.FetchResource(path) - if err != nil { - return err - } - font, err := allg5.LoadTTFFont(path, size) - if err != nil { - return err - } - var prev = r.ft[name] - if prev != nil { - prev.Destroy() - } - r.ft[name] = newFont(font) - return nil -} - -func (r *Renderer) RegisterFonts(path string, fonts ...FontDefinition) error { - for _, f := range fonts { - err := r.RegisterFont(path, f.Name, f.Size) - if err != nil { - return err - } - } - return nil -} - func (r *Renderer) RenderTo(texture ui.Texture) { bmp := r.mustGetBitmap(texture) bmp.SetAsTarget() @@ -325,20 +301,20 @@ func (r *Renderer) Target() ui.Texture { return &texture{allg5.CurrentTarget(), nil} } -func (r *Renderer) text(p geom.PointF32, font string, c color.Color, t string, align allg5.HorizontalAlignment) { - var f = r.ft[font] - if f == nil { +func (r *Renderer) text(f ui.Font, p geom.PointF32, c color.Color, t string, align allg5.HorizontalAlignment) { + font, ok := f.(*font) + if !ok { return } x, y := snap(p) - f.Draw(x, y, newColor(c), align, t) + font.Draw(x, y, newColor(c), align, t) } -func (r *Renderer) Text(p geom.PointF32, font string, c color.Color, t string) { - r.text(p, font, c, t, allg5.AlignLeft) +func (r *Renderer) Text(font ui.Font, p geom.PointF32, c color.Color, t string) { + r.text(font, p, c, t, allg5.AlignLeft) } -func (r *Renderer) TextAlign(p geom.PointF32, font string, c color.Color, t string, align ui.HorizontalAlignment) { +func (r *Renderer) TextAlign(font ui.Font, p geom.PointF32, c color.Color, t string, align ui.HorizontalAlignment) { var alignment = allg5.AlignLeft switch align { case ui.AlignCenter: @@ -346,7 +322,7 @@ func (r *Renderer) TextAlign(p geom.PointF32, font string, c color.Color, t stri case ui.AlignRight: alignment = allg5.AlignRight } - r.text(p, font, c, t, alignment) + r.text(font, p, c, t, alignment) } // Utility functions diff --git a/colors.go b/colors.go index 9e12107..c05a690 100644 --- a/colors.go +++ b/colors.go @@ -22,7 +22,7 @@ func HexColor(s string) (color.RGBA, error) { if len(match[4]) > 0 { a = values[3] } - return color.RGBA{R: uint8(values[0]), G: uint8(values[1]), B: uint8(values[2]), A: uint8(a)}, nil + return RGBA(uint8(values[0]), uint8(values[1]), uint8(values[2]), uint8(a)), nil } // HexToInt tries to convert a string with hexadecimal characters to an integer. @@ -64,3 +64,11 @@ func MustHexColor(s string) color.RGBA { } return color } + +// RGB creates an opaque color with the specified red, green and red values. +func RGB(r, g, b byte) color.RGBA { return RGBA(r, g, b, 0xff) } + +// RGB creates a color with the specified red, green, red and alpha values. +func RGBA(r, g, b, a byte) color.RGBA { + return color.RGBA{R: r, G: g, B: b, A: a} +} diff --git a/sdlui/font.go b/sdlui/font.go index e81b374..4848f7f 100644 --- a/sdlui/font.go +++ b/sdlui/font.go @@ -9,6 +9,11 @@ type Font struct { *ttf.Font } +func (f *Font) Destroy() error { + f.Font.Close() + return nil +} + func (f *Font) Height() float32 { return float32(f.Font.Height()) } diff --git a/sdlui/renderer.go b/sdlui/renderer.go index 3b2e580..bbdceac 100644 --- a/sdlui/renderer.go +++ b/sdlui/renderer.go @@ -22,7 +22,6 @@ type Renderer struct { window *sdl.Window renderer *sdl.Renderer refresh uint32 - fonts map[string]*Font resources ui.Resources mouse geom.PointF32 @@ -88,7 +87,6 @@ func NewRenderer(title string, width, height int32, opts NewRendererOptions) (*R window: window, renderer: renderer, refresh: refresh, - fonts: map[string]*Font{}, resources: &ui.OSResources{}, cursors: map[sdl.SystemCursor]*sdl.Cursor{}, }, nil @@ -205,9 +203,6 @@ func (r *Renderer) Refresh() { // Lifetime func (r *Renderer) Destroy() error { - for _, f := range r.fonts { - f.Close() - } r.renderer.Destroy() r.window.Destroy() ttf.Quit() @@ -226,6 +221,18 @@ func (r *Renderer) Clear(c color.Color) { r.renderer.Clear() } +func (r *Renderer) CreateFontPath(path string, size int) (ui.Font, error) { + path, err := r.resources.FetchResource(path) + if err != nil { + return nil, err + } + font, err := ttf.OpenFont(path, size) + if err != nil { + return nil, err + } + return &Font{font}, nil +} + func (r *Renderer) createTexture(source ui.ImageSource, keepSource bool) (ui.Texture, error) { m, err := source.CreateImage() if err != nil { @@ -307,10 +314,6 @@ func (r *Renderer) FillRectangle(rect geom.RectangleF32, c color.Color) { r.renderer.FillRect(SDLRectanglePtr(rect)) } -func (r *Renderer) Font(name string) ui.Font { - return r.fonts[name] -} - func (r *Renderer) Rectangle(rect geom.RectangleF32, c color.Color, thickness float32) { r.SetDrawColorGo(c) if rect.Dx() == 0 { // SDL doesn't draw a 1 px wide line when Dx() == 0 && thickness == 1 @@ -333,19 +336,6 @@ func (r *Renderer) Rectangle(rect geom.RectangleF32, c color.Color, thickness fl } } -func (r *Renderer) RegisterFont(name, path string, size int) error { - path, err := r.resources.FetchResource(path) - if err != nil { - return err - } - font, err := ttf.OpenFont(path, size) - if err != nil { - return err - } - r.fonts[name] = &Font{font} - return nil -} - func (r *Renderer) RenderTo(t ui.Texture) { texture, ok := t.(sdlTexture) if ok { @@ -397,8 +387,8 @@ func (r *Renderer) Target() ui.Texture { return &Texture{target} } -func (r *Renderer) Text(p geom.PointF32, font string, color color.Color, text string) { - f := r.Font(font).(*Font) +func (r *Renderer) Text(font ui.Font, p geom.PointF32, color color.Color, text string) { + f := font.(*Font) surface, err := f.RenderUTF8Blended(text, ColorSDL(color)) if err != nil { @@ -414,16 +404,16 @@ func (r *Renderer) Text(p geom.PointF32, font string, color color.Color, text st r.DrawTexture(&Texture{texture}, p) } -func (r *Renderer) TextAlign(p geom.PointF32, font string, color color.Color, text string, align ui.HorizontalAlignment) { +func (r *Renderer) TextAlign(font ui.Font, p geom.PointF32, color color.Color, text string, align ui.HorizontalAlignment) { switch align { case ui.AlignLeft: - r.Text(p, font, color, text) + r.Text(font, p, color, text) case ui.AlignCenter: - width := r.Font(font).(*Font).WidthOf(text) - r.Text(p.Add2D(-.5*width, 0), font, color, text) + width := font.WidthOf(text) + r.Text(font, p.Add2D(-.5*width, 0), color, text) case ui.AlignRight: - width := r.Font(font).(*Font).WidthOf(text) - r.Text(p.Add2D(-width, 0), font, color, text) + width := font.WidthOf(text) + r.Text(font, p.Add2D(-width, 0), color, text) } } diff --git a/ui/button.go b/ui/button.go index 83eb956..049426b 100644 --- a/ui/button.go +++ b/ui/button.go @@ -36,7 +36,7 @@ func BuildIconButton(icon, text string, fn func(b *Button)) *Button { func (b *Button) desiredSize(ctx Context) geom.PointF32 { var pad = ctx.Style().Dimensions.TextPadding - var font = ctx.Renderer().Font(b.FontName(ctx)) + var font = ctx.Fonts().Font(b.FontName(ctx)) var w, h float32 = 0, font.Height() if len(b.Text) != 0 { w += pad + font.WidthOf(b.Text) @@ -144,9 +144,9 @@ func (b *Button) Render(ctx Context) { pos.X += iconWidth + 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*(boundsH-font.Height())), fontName, textColor, b.Text) + fontName := b.FontName(ctx) + font := ctx.Fonts().Font(fontName) + ctx.Renderer().Text(font, geom.PtF32(pos.X, pos.Y+.5*(boundsH-font.Height())), textColor, b.Text) } if b.Type == ButtonTypeOutlined { diff --git a/ui/checkbox.go b/ui/checkbox.go index 105fcc8..d68b4f1 100644 --- a/ui/checkbox.go +++ b/ui/checkbox.go @@ -25,7 +25,7 @@ func BuildCheckbox(text string, fn func(c *Checkbox)) *Checkbox { func (c *Checkbox) desiredSize(ctx Context) geom.PointF32 { var pad = ctx.Style().Dimensions.TextPadding - var font = ctx.Renderer().Font(c.FontName(ctx)) + var font = ctx.Fonts().Font(c.FontName(ctx)) var w, h float32 = 0, font.Height() if len(c.Text) != 0 { w += pad + font.WidthOf(c.Text) @@ -128,7 +128,7 @@ func (c *Checkbox) Render(ctx Context) { } if len(c.Text) != 0 { var fontName = c.FontName(ctx) - var font = ctx.Renderer().Font(fontName) - ctx.Renderer().Text(geom.PtF32(pos.X, pos.Y+.5*(boundsH-font.Height())), fontName, fore, c.Text) + var font = ctx.Fonts().Font(fontName) + ctx.Renderer().Text(font, geom.PtF32(pos.X, pos.Y+.5*(boundsH-font.Height())), fore, c.Text) } } diff --git a/ui/context.go b/ui/context.go index 29096d0..2282b52 100644 --- a/ui/context.go +++ b/ui/context.go @@ -2,6 +2,7 @@ package ui type Context interface { Animate() + Fonts() *Fonts HasQuit() bool Quit() Renderer() Renderer @@ -15,14 +16,32 @@ var _ EventTarget = &context{} type context struct { animate bool quit chan struct{} - r Renderer + renderer Renderer view Control + fonts *Fonts textures *Textures style *Style } +func newContext(r Renderer, s *Style, view Control) *context { + return &context{ + quit: make(chan struct{}), + renderer: r, + style: s, + view: view, + fonts: NewFonts(r), + textures: NewTextures(r)} +} + func (c *context) Animate() { c.animate = true } +func (c *context) Destroy() { + c.fonts.Destroy() + c.textures.Destroy() +} + +func (c *context) Fonts() *Fonts { return c.fonts } + func (c *context) HasQuit() bool { select { case <-c.quit: @@ -32,7 +51,7 @@ func (c *context) HasQuit() bool { } } -func (c *context) Renderer() Renderer { return c.r } +func (c *context) Renderer() Renderer { return c.renderer } func (c *context) Style() *Style { return c.style } diff --git a/ui/examples/01_basic/basic.go b/ui/examples/01_basic/basic.go index d2549bb..a7cc762 100644 --- a/ui/examples/01_basic/basic.go +++ b/ui/examples/01_basic/basic.go @@ -16,7 +16,12 @@ type basic struct { } func (b *basic) Init(ctx ui.Context) error { - _, err := ctx.Textures().CreateTexturePath("plus", "../resources/images/plus.png", true) + _, err := ctx.Fonts().CreateFontPath("default", "../resources/font/OpenSans-Regular.ttf", 14) + if err != nil { + return err + } + + _, err = ctx.Textures().CreateTexturePath("plus", "../resources/images/plus.png", true) if err != nil { return err } @@ -73,11 +78,6 @@ func run() error { } defer render.Destroy() - err = render.RegisterFont("default", "../resources/font/OpenSans-Regular.ttf", 14) - if err != nil { - return err - } - return ui.RunWait(render, ui.DefaultStyle(), &basic{}, true) } diff --git a/ui/font.go b/ui/font.go index 226fc38..7fb98c7 100644 --- a/ui/font.go +++ b/ui/font.go @@ -7,6 +7,7 @@ import ( ) type Font interface { + Destroy() error Height() float32 Measure(t string) geom.RectangleF32 WidthOf(t string) float32 diff --git a/ui/fonts.go b/ui/fonts.go new file mode 100644 index 0000000..23140f5 --- /dev/null +++ b/ui/fonts.go @@ -0,0 +1,70 @@ +package ui + +import ( + "image/color" + + "opslag.de/schobers/geom" +) + +type Fonts struct { + render Renderer + fonts map[string]Font +} + +func NewFonts(render Renderer) *Fonts { + return &Fonts{render, map[string]Font{}} +} + +func (f *Fonts) AddFont(name string, font Font) { + curr := f.fonts[name] + if curr != nil { + curr.Destroy() + } + f.fonts[name] = font +} + +func (f *Fonts) createFont(name string, create func() (Font, error)) (Font, error) { + font, err := create() + if err != nil { + return nil, err + } + f.AddFont(name, font) + return font, nil +} + +func (f *Fonts) CreateFontPath(name, path string, size int) (Font, error) { + return f.createFont(name, func() (Font, error) { + return f.render.CreateFontPath(path, size) + }) +} + +func (f *Fonts) Destroy() { + for _, font := range f.fonts { + font.Destroy() + } + f.fonts = nil +} + +func (f *Fonts) Font(name string) Font { + font, ok := f.fonts[name] + if ok { + return font + } + return nil +} + +func (f *Fonts) Text(fontName string, p geom.PointF32, color color.Color, text string) { + font := f.Font(fontName) + if font == nil { + return + } + f.render.Text(font, p, color, text) +} + +func (f *Fonts) TextAlign(fontName string, p geom.PointF32, color color.Color, text string, align HorizontalAlignment) { + font := f.Font(fontName) + if font == nil { + return + } + f.render.TextAlign(font, p, color, text, align) +} diff --git a/ui/label.go b/ui/label.go index 4077799..89d718f 100644 --- a/ui/label.go +++ b/ui/label.go @@ -35,7 +35,7 @@ func (l *Label) hashContent(ctx Context) string { func (l *Label) desiredSize(ctx Context) interface{} { fontName := l.FontName(ctx) - font := ctx.Renderer().Font(fontName) + font := ctx.Fonts().Font(fontName) width := font.WidthOf(l.Text) height := font.Height() pad := ctx.Style().Dimensions.TextPadding @@ -49,16 +49,16 @@ func (l *Label) DesiredSize(ctx Context) geom.PointF32 { func (l *Label) Render(ctx Context) { l.RenderBackground(ctx) - c := l.FontColor(ctx) - f := l.FontName(ctx) + fontColor := l.FontColor(ctx) + fontName := l.FontName(ctx) pad := ctx.Style().Dimensions.TextPadding bounds := l.bounds.Inset(pad) switch l.TextAlignment { case AlignLeft: - ctx.Renderer().TextAlign(bounds.Min, f, c, l.Text, l.TextAlignment) + ctx.Fonts().TextAlign(fontName, bounds.Min, fontColor, l.Text, l.TextAlignment) case AlignRight: - ctx.Renderer().TextAlign(geom.PtF32(bounds.Max.X, bounds.Min.Y), f, c, l.Text, l.TextAlignment) + ctx.Fonts().TextAlign(fontName, geom.PtF32(bounds.Max.X, bounds.Min.Y), fontColor, l.Text, l.TextAlignment) case AlignCenter: - ctx.Renderer().TextAlign(geom.PtF32(.5*(bounds.Min.X+bounds.Max.X), bounds.Min.Y), f, c, l.Text, l.TextAlignment) + ctx.Fonts().TextAlign(fontName, geom.PtF32(.5*(bounds.Min.X+bounds.Max.X), bounds.Min.Y), fontColor, l.Text, l.TextAlignment) } } diff --git a/ui/renderer.go b/ui/renderer.go index 85dc636..fa38353 100644 --- a/ui/renderer.go +++ b/ui/renderer.go @@ -17,6 +17,7 @@ type Renderer interface { // Drawing Clear(c color.Color) + CreateFontPath(path string, size int) (Font, error) CreateTexture(m ImageSource) (Texture, error) CreateTextureGo(m image.Image, source bool) (Texture, error) CreateTexturePath(path string, source bool) (Texture, error) @@ -25,16 +26,14 @@ type Renderer interface { DrawTexture(t Texture, p geom.PointF32) DrawTextureOptions(t Texture, p geom.PointF32, opts DrawOptions) FillRectangle(r geom.RectangleF32, c color.Color) - Font(name string) Font Rectangle(r geom.RectangleF32, c color.Color, thickness float32) - RegisterFont(name, path string, size int) error RenderTo(Texture) RenderToDisplay() SetMouseCursor(c MouseCursor) Size() geom.PointF32 Target() Texture - Text(p geom.PointF32, font string, color color.Color, text string) - TextAlign(p geom.PointF32, font string, color color.Color, text string, align HorizontalAlignment) + Text(font Font, p geom.PointF32, color color.Color, text string) + TextAlign(font Font, p geom.PointF32, color color.Color, text string, align HorizontalAlignment) // Resources Resources() Resources diff --git a/ui/scrollbar.go b/ui/scrollbar.go index 1c9b3fc..487c088 100644 --- a/ui/scrollbar.go +++ b/ui/scrollbar.go @@ -2,6 +2,7 @@ package ui import ( "opslag.de/schobers/geom" + "opslag.de/schobers/zntg" ) type Scrollbar struct { @@ -55,7 +56,7 @@ func (s *Scrollbar) Handle(ctx Context, e Event) { } func (s *Scrollbar) Render(ctx Context) { - ctx.Renderer().FillRectangle(s.bounds, RGBA(0, 0, 0, 1)) + ctx.Renderer().FillRectangle(s.bounds, zntg.RGBA(0, 0, 0, 1)) s.handle.Render(ctx) } diff --git a/ui/style.go b/ui/style.go index 41953e6..f10ef63 100644 --- a/ui/style.go +++ b/ui/style.go @@ -1,6 +1,10 @@ package ui -import "image/color" +import ( + "image/color" + + "opslag.de/schobers/zntg" +) var defaultDimensions *Dimensions var defaultFonts *Fonts @@ -13,7 +17,7 @@ type Dimensions struct { TextPadding float32 } -type Fonts struct { +type FontNames struct { Default string } @@ -44,60 +48,44 @@ type Palette struct { type Style struct { Dimensions *Dimensions - Fonts *Fonts + Fonts *FontNames Palette *Palette } func DefaultDimensions() *Dimensions { - if defaultDimensions == nil { - defaultDimensions = &Dimensions{ - OutlineWidth: 2., - ScrollbarWidth: 16., - TextPadding: 8., - } + return &Dimensions{ + OutlineWidth: 2., + ScrollbarWidth: 16., + TextPadding: 8., } - return defaultDimensions } -func DefaultFonts() *Fonts { - if defaultFonts == nil { - defaultFonts = &Fonts{ - Default: "default", - } +func DefaultFontNames() *FontNames { + return &FontNames{ + Default: "default", } - return defaultFonts } func DefaultPalette() *Palette { - if defaultPalette == nil { - defaultPalette = &Palette{ - Background: color.White, - Primary: RGBA(0x3F, 0x51, 0xB5, 0xFF), - PrimaryDark: RGBA(0x00, 0x28, 0x84, 0xFF), - PrimaryHighlight: RGBA(0xE8, 0xEA, 0xF6, 0xFF), - PrimaryLight: RGBA(0x75, 0x7C, 0xE8, 0xFF), - ShadedBackground: RGBA(0xFA, 0xFA, 0xFA, 0xFF), - Text: color.Black, - TextDisabled: RGBA(0xBD, 0xBD, 0xBD, 0xFF), - TextNegative: RGBA(0xFF, 0x43, 0x36, 0xFF), - TextOnPrimary: color.White, - TextPositive: RGBA(0x4C, 0xAF, 0x50, 0xFF), - } + return &Palette{ + Background: color.White, + Primary: zntg.MustHexColor(`#3F51B5`), + PrimaryDark: zntg.MustHexColor(`#002884`), + PrimaryHighlight: zntg.MustHexColor(`#E8EAF6`), + PrimaryLight: zntg.MustHexColor(`#757CE8`), + ShadedBackground: zntg.MustHexColor(`#FAFAFA`), + Text: color.Black, + TextDisabled: zntg.MustHexColor(`#BDBDBD`), + TextNegative: zntg.MustHexColor(`#FF4336`), + TextOnPrimary: color.White, + TextPositive: zntg.MustHexColor(`#4CAF50`), } - return defaultPalette } func DefaultStyle() *Style { - if defaultStyle == nil { - defaultStyle = &Style{ - Dimensions: DefaultDimensions(), - Fonts: DefaultFonts(), - Palette: DefaultPalette(), - } + return &Style{ + Dimensions: DefaultDimensions(), + Fonts: DefaultFontNames(), + Palette: DefaultPalette(), } - return defaultStyle -} - -func RGBA(r, g, b, a byte) *color.RGBA { - return &color.RGBA{R: r, G: g, B: b, A: a} } diff --git a/ui/textbox.go b/ui/textbox.go index c20a019..c441f32 100644 --- a/ui/textbox.go +++ b/ui/textbox.go @@ -58,7 +58,7 @@ func (b *TextBox) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.Poi func (b *TextBox) DesiredSize(ctx Context) geom.PointF32 { var fontName = b.FontName(ctx) - var font = ctx.Renderer().Font(fontName) + var font = ctx.Fonts().Font(fontName) var width = font.WidthOf(b.Text) var height = font.Height() var pad = b.pad(ctx) @@ -68,7 +68,7 @@ func (b *TextBox) DesiredSize(ctx Context) geom.PointF32 { func (b *TextBox) mousePosToCaretPos(ctx Context, e MouseEvent) int { p := b.ToControlPosition(e.Pos()) offset := p.X - b.box.bounds.Min.X - f := ctx.Renderer().Font(b.FontName(ctx)) + f := ctx.Fonts().Font(b.FontName(ctx)) var carets = [3]int{0, 0, len(b.Text)} var offsets = [3]float32{0, 0, f.WidthOf(b.Text)} var updateCenter = func() { @@ -234,7 +234,6 @@ func (b *TextBox) Render(ctx Context) { b.RenderOutline(ctx) c := b.FontColor(ctx) - f := b.FontName(ctx) style := ctx.Style() var caretWidth float32 = 1 b.box.RenderFn(ctx, func(_ Context, size geom.PointF32) { @@ -244,14 +243,16 @@ func (b *TextBox) Render(ctx Context) { back = ctx.Style().Palette.Background } renderer.Clear(back) + fontName := b.FontName(ctx) + font := ctx.Fonts().Font(fontName) if b.Selection.Start != b.Selection.End { - left, right := renderer.Font(f).WidthOf(b.Text[:b.Selection.Start]), renderer.Font(f).WidthOf(b.Text[:b.Selection.End]) + left, right := font.WidthOf(b.Text[:b.Selection.Start]), font.WidthOf(b.Text[:b.Selection.End]) renderer.FillRectangle(geom.RectF32(left, 0, right, size.Y), style.Palette.PrimaryHighlight) } - renderer.Text(geom.ZeroPtF32, f, c, b.Text) + renderer.Text(font, geom.ZeroPtF32, c, b.Text) const interval = 500 * time.Millisecond if b.Focus && (time.Since(b.blink)/interval)%2 == 0 { - var w = renderer.Font(f).WidthOf(b.Text[:b.Selection.Caret]) + var w = font.WidthOf(b.Text[:b.Selection.Caret]) var caret = w + .5*caretWidth renderer.Rectangle(geom.RectF32(caret, 0, caret, size.Y), c, caretWidth) } diff --git a/ui/ui.go b/ui/ui.go index a1b3fca..42039f7 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -13,7 +13,9 @@ func Run(r Renderer, s *Style, view Control) error { // RunWait runs the application loop and conditionally waits on events before rendering. func RunWait(r Renderer, s *Style, view Control, wait bool) error { - ctx := &context{quit: make(chan struct{}), r: r, style: s, view: view, textures: NewTextures(r)} + ctx := newContext(r, s, view) + defer ctx.Destroy() + root, ok := view.(RootControl) if ok { err := root.Init(ctx) diff --git a/ui/visual.go b/ui/visual.go index afe3b87..1a8ba7c 100644 --- a/ui/visual.go +++ b/ui/visual.go @@ -4,6 +4,7 @@ import ( "image/color" "opslag.de/schobers/geom" + "opslag.de/schobers/zntg" ) func Background(content Control, c color.Color) Control { @@ -37,7 +38,7 @@ type shadow struct { func (s *shadow) Render(ctx Context) { s.Proxy.Render(ctx) b := s.Bounds() - shadow := RGBA(0xBD, 0xBD, 0xBD, 0x2F) + shadow := zntg.RGBA(0xBD, 0xBD, 0xBD, 0x2F) ctx.Renderer().FillRectangle(geom.RectF32(b.Min.X, b.Min.Y, b.Max.X, b.Min.Y+3), shadow) ctx.Renderer().FillRectangle(geom.RectF32(b.Min.X, b.Min.Y, b.Max.X, b.Min.Y+2), shadow) ctx.Renderer().FillRectangle(geom.RectF32(b.Min.X, b.Min.Y, b.Max.X, b.Min.Y+1), shadow)