Added Fonts() to context similarly as Textures().

- Fonts are now managed by context instead of the implementation specific renderers.
This commit is contained in:
Sander Schobers 2020-05-15 15:42:24 +02:00
parent 02ee819a99
commit 3591e22c97
18 changed files with 215 additions and 153 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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}
}

View File

@ -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())
}

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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 }

View File

@ -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)
}

View File

@ -7,6 +7,7 @@ import (
)
type Font interface {
Destroy() error
Height() float32
Measure(t string) geom.RectangleF32
WidthOf(t string) float32

70
ui/fonts.go Normal file
View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -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)
}

View File

@ -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}
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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)