Added Fonts() to context similarly as Textures().
- Fonts are now managed by context instead of the implementation specific renderers.
This commit is contained in:
parent
02ee819a99
commit
3591e22c97
@ -22,8 +22,9 @@ func newFont(f *allg5.Font) *font {
|
|||||||
return &font{f}
|
return &font{f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *font) Destroy() {
|
func (f *font) Destroy() error {
|
||||||
f.Font.Destroy()
|
f.Font.Destroy()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *font) Height() float32 {
|
func (f *font) Height() float32 {
|
||||||
|
@ -47,7 +47,7 @@ func NewRenderer(w, h int, opts allg5.NewDisplayOptions) (*Renderer, error) {
|
|||||||
})
|
})
|
||||||
clean = nil
|
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.
|
// Renderer implements ui.Renderer using Allegro 5.
|
||||||
@ -55,7 +55,6 @@ type Renderer struct {
|
|||||||
disp *allg5.Display
|
disp *allg5.Display
|
||||||
eq *allg5.EventQueue
|
eq *allg5.EventQueue
|
||||||
unh func(allg5.Event)
|
unh func(allg5.Event)
|
||||||
ft map[string]*font
|
|
||||||
user *allg5.UserEventSource
|
user *allg5.UserEventSource
|
||||||
res ui.Resources
|
res ui.Resources
|
||||||
|
|
||||||
@ -149,10 +148,6 @@ func (r *Renderer) Refresh() {
|
|||||||
func (r *Renderer) Destroy() error {
|
func (r *Renderer) Destroy() error {
|
||||||
r.user.Destroy()
|
r.user.Destroy()
|
||||||
r.eq.Destroy()
|
r.eq.Destroy()
|
||||||
for _, f := range r.ft {
|
|
||||||
f.Destroy()
|
|
||||||
}
|
|
||||||
r.ft = nil
|
|
||||||
r.disp.Destroy()
|
r.disp.Destroy()
|
||||||
r.res.Destroy()
|
r.res.Destroy()
|
||||||
return nil
|
return nil
|
||||||
@ -164,6 +159,18 @@ func (r *Renderer) Clear(c color.Color) {
|
|||||||
allg5.ClearToColor(newColor(c))
|
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) {
|
func (r *Renderer) createTexture(source ui.ImageSource, keepSource bool) (ui.Texture, error) {
|
||||||
im, err := source.CreateImage()
|
im, err := source.CreateImage()
|
||||||
if err != nil {
|
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))
|
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 {
|
func (r *Renderer) mustGetBitmap(t ui.Texture) *allg5.Bitmap {
|
||||||
texture, ok := t.(*texture)
|
texture, ok := t.(*texture)
|
||||||
if !ok {
|
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)
|
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) {
|
func (r *Renderer) RenderTo(texture ui.Texture) {
|
||||||
bmp := r.mustGetBitmap(texture)
|
bmp := r.mustGetBitmap(texture)
|
||||||
bmp.SetAsTarget()
|
bmp.SetAsTarget()
|
||||||
@ -325,20 +301,20 @@ func (r *Renderer) Target() ui.Texture {
|
|||||||
return &texture{allg5.CurrentTarget(), nil}
|
return &texture{allg5.CurrentTarget(), nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Renderer) text(p geom.PointF32, font string, c color.Color, t string, align allg5.HorizontalAlignment) {
|
func (r *Renderer) text(f ui.Font, p geom.PointF32, c color.Color, t string, align allg5.HorizontalAlignment) {
|
||||||
var f = r.ft[font]
|
font, ok := f.(*font)
|
||||||
if f == nil {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
x, y := snap(p)
|
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) {
|
func (r *Renderer) Text(font ui.Font, p geom.PointF32, c color.Color, t string) {
|
||||||
r.text(p, font, c, t, allg5.AlignLeft)
|
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
|
var alignment = allg5.AlignLeft
|
||||||
switch align {
|
switch align {
|
||||||
case ui.AlignCenter:
|
case ui.AlignCenter:
|
||||||
@ -346,7 +322,7 @@ func (r *Renderer) TextAlign(p geom.PointF32, font string, c color.Color, t stri
|
|||||||
case ui.AlignRight:
|
case ui.AlignRight:
|
||||||
alignment = allg5.AlignRight
|
alignment = allg5.AlignRight
|
||||||
}
|
}
|
||||||
r.text(p, font, c, t, alignment)
|
r.text(font, p, c, t, alignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
|
10
colors.go
10
colors.go
@ -22,7 +22,7 @@ func HexColor(s string) (color.RGBA, error) {
|
|||||||
if len(match[4]) > 0 {
|
if len(match[4]) > 0 {
|
||||||
a = values[3]
|
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.
|
// HexToInt tries to convert a string with hexadecimal characters to an integer.
|
||||||
@ -64,3 +64,11 @@ func MustHexColor(s string) color.RGBA {
|
|||||||
}
|
}
|
||||||
return color
|
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}
|
||||||
|
}
|
||||||
|
@ -9,6 +9,11 @@ type Font struct {
|
|||||||
*ttf.Font
|
*ttf.Font
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Font) Destroy() error {
|
||||||
|
f.Font.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Font) Height() float32 {
|
func (f *Font) Height() float32 {
|
||||||
return float32(f.Font.Height())
|
return float32(f.Font.Height())
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ type Renderer struct {
|
|||||||
window *sdl.Window
|
window *sdl.Window
|
||||||
renderer *sdl.Renderer
|
renderer *sdl.Renderer
|
||||||
refresh uint32
|
refresh uint32
|
||||||
fonts map[string]*Font
|
|
||||||
resources ui.Resources
|
resources ui.Resources
|
||||||
|
|
||||||
mouse geom.PointF32
|
mouse geom.PointF32
|
||||||
@ -88,7 +87,6 @@ func NewRenderer(title string, width, height int32, opts NewRendererOptions) (*R
|
|||||||
window: window,
|
window: window,
|
||||||
renderer: renderer,
|
renderer: renderer,
|
||||||
refresh: refresh,
|
refresh: refresh,
|
||||||
fonts: map[string]*Font{},
|
|
||||||
resources: &ui.OSResources{},
|
resources: &ui.OSResources{},
|
||||||
cursors: map[sdl.SystemCursor]*sdl.Cursor{},
|
cursors: map[sdl.SystemCursor]*sdl.Cursor{},
|
||||||
}, nil
|
}, nil
|
||||||
@ -205,9 +203,6 @@ func (r *Renderer) Refresh() {
|
|||||||
// Lifetime
|
// Lifetime
|
||||||
|
|
||||||
func (r *Renderer) Destroy() error {
|
func (r *Renderer) Destroy() error {
|
||||||
for _, f := range r.fonts {
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
r.renderer.Destroy()
|
r.renderer.Destroy()
|
||||||
r.window.Destroy()
|
r.window.Destroy()
|
||||||
ttf.Quit()
|
ttf.Quit()
|
||||||
@ -226,6 +221,18 @@ func (r *Renderer) Clear(c color.Color) {
|
|||||||
r.renderer.Clear()
|
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) {
|
func (r *Renderer) createTexture(source ui.ImageSource, keepSource bool) (ui.Texture, error) {
|
||||||
m, err := source.CreateImage()
|
m, err := source.CreateImage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -307,10 +314,6 @@ func (r *Renderer) FillRectangle(rect geom.RectangleF32, c color.Color) {
|
|||||||
r.renderer.FillRect(SDLRectanglePtr(rect))
|
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) {
|
func (r *Renderer) Rectangle(rect geom.RectangleF32, c color.Color, thickness float32) {
|
||||||
r.SetDrawColorGo(c)
|
r.SetDrawColorGo(c)
|
||||||
if rect.Dx() == 0 { // SDL doesn't draw a 1 px wide line when Dx() == 0 && thickness == 1
|
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) {
|
func (r *Renderer) RenderTo(t ui.Texture) {
|
||||||
texture, ok := t.(sdlTexture)
|
texture, ok := t.(sdlTexture)
|
||||||
if ok {
|
if ok {
|
||||||
@ -397,8 +387,8 @@ func (r *Renderer) Target() ui.Texture {
|
|||||||
return &Texture{target}
|
return &Texture{target}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Renderer) Text(p geom.PointF32, font string, color color.Color, text string) {
|
func (r *Renderer) Text(font ui.Font, p geom.PointF32, color color.Color, text string) {
|
||||||
f := r.Font(font).(*Font)
|
f := font.(*Font)
|
||||||
|
|
||||||
surface, err := f.RenderUTF8Blended(text, ColorSDL(color))
|
surface, err := f.RenderUTF8Blended(text, ColorSDL(color))
|
||||||
if err != nil {
|
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)
|
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 {
|
switch align {
|
||||||
case ui.AlignLeft:
|
case ui.AlignLeft:
|
||||||
r.Text(p, font, color, text)
|
r.Text(font, p, color, text)
|
||||||
case ui.AlignCenter:
|
case ui.AlignCenter:
|
||||||
width := r.Font(font).(*Font).WidthOf(text)
|
width := font.WidthOf(text)
|
||||||
r.Text(p.Add2D(-.5*width, 0), font, color, text)
|
r.Text(font, p.Add2D(-.5*width, 0), color, text)
|
||||||
case ui.AlignRight:
|
case ui.AlignRight:
|
||||||
width := r.Font(font).(*Font).WidthOf(text)
|
width := font.WidthOf(text)
|
||||||
r.Text(p.Add2D(-width, 0), font, color, text)
|
r.Text(font, p.Add2D(-width, 0), color, text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ func BuildIconButton(icon, text string, fn func(b *Button)) *Button {
|
|||||||
|
|
||||||
func (b *Button) desiredSize(ctx Context) geom.PointF32 {
|
func (b *Button) desiredSize(ctx Context) geom.PointF32 {
|
||||||
var pad = ctx.Style().Dimensions.TextPadding
|
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()
|
var w, h float32 = 0, font.Height()
|
||||||
if len(b.Text) != 0 {
|
if len(b.Text) != 0 {
|
||||||
w += pad + font.WidthOf(b.Text)
|
w += pad + font.WidthOf(b.Text)
|
||||||
@ -144,9 +144,9 @@ func (b *Button) Render(ctx Context) {
|
|||||||
pos.X += iconWidth + pad
|
pos.X += iconWidth + pad
|
||||||
}
|
}
|
||||||
if len(b.Text) != 0 {
|
if len(b.Text) != 0 {
|
||||||
var fontName = b.FontName(ctx)
|
fontName := b.FontName(ctx)
|
||||||
var font = ctx.Renderer().Font(fontName)
|
font := ctx.Fonts().Font(fontName)
|
||||||
ctx.Renderer().Text(geom.PtF32(pos.X, pos.Y+.5*(boundsH-font.Height())), fontName, textColor, b.Text)
|
ctx.Renderer().Text(font, geom.PtF32(pos.X, pos.Y+.5*(boundsH-font.Height())), textColor, b.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.Type == ButtonTypeOutlined {
|
if b.Type == ButtonTypeOutlined {
|
||||||
|
@ -25,7 +25,7 @@ func BuildCheckbox(text string, fn func(c *Checkbox)) *Checkbox {
|
|||||||
|
|
||||||
func (c *Checkbox) desiredSize(ctx Context) geom.PointF32 {
|
func (c *Checkbox) desiredSize(ctx Context) geom.PointF32 {
|
||||||
var pad = ctx.Style().Dimensions.TextPadding
|
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()
|
var w, h float32 = 0, font.Height()
|
||||||
if len(c.Text) != 0 {
|
if len(c.Text) != 0 {
|
||||||
w += pad + font.WidthOf(c.Text)
|
w += pad + font.WidthOf(c.Text)
|
||||||
@ -128,7 +128,7 @@ func (c *Checkbox) Render(ctx Context) {
|
|||||||
}
|
}
|
||||||
if len(c.Text) != 0 {
|
if len(c.Text) != 0 {
|
||||||
var fontName = c.FontName(ctx)
|
var fontName = c.FontName(ctx)
|
||||||
var font = ctx.Renderer().Font(fontName)
|
var font = ctx.Fonts().Font(fontName)
|
||||||
ctx.Renderer().Text(geom.PtF32(pos.X, pos.Y+.5*(boundsH-font.Height())), fontName, fore, c.Text)
|
ctx.Renderer().Text(font, geom.PtF32(pos.X, pos.Y+.5*(boundsH-font.Height())), fore, c.Text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package ui
|
|||||||
|
|
||||||
type Context interface {
|
type Context interface {
|
||||||
Animate()
|
Animate()
|
||||||
|
Fonts() *Fonts
|
||||||
HasQuit() bool
|
HasQuit() bool
|
||||||
Quit()
|
Quit()
|
||||||
Renderer() Renderer
|
Renderer() Renderer
|
||||||
@ -15,14 +16,32 @@ var _ EventTarget = &context{}
|
|||||||
type context struct {
|
type context struct {
|
||||||
animate bool
|
animate bool
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
r Renderer
|
renderer Renderer
|
||||||
view Control
|
view Control
|
||||||
|
fonts *Fonts
|
||||||
textures *Textures
|
textures *Textures
|
||||||
style *Style
|
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) 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 {
|
func (c *context) HasQuit() bool {
|
||||||
select {
|
select {
|
||||||
case <-c.quit:
|
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 }
|
func (c *context) Style() *Style { return c.style }
|
||||||
|
|
||||||
|
@ -16,7 +16,12 @@ type basic struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *basic) Init(ctx ui.Context) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -73,11 +78,6 @@ func run() error {
|
|||||||
}
|
}
|
||||||
defer render.Destroy()
|
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)
|
return ui.RunWait(render, ui.DefaultStyle(), &basic{}, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Font interface {
|
type Font interface {
|
||||||
|
Destroy() error
|
||||||
Height() float32
|
Height() float32
|
||||||
Measure(t string) geom.RectangleF32
|
Measure(t string) geom.RectangleF32
|
||||||
WidthOf(t string) float32
|
WidthOf(t string) float32
|
||||||
|
70
ui/fonts.go
Normal file
70
ui/fonts.go
Normal 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)
|
||||||
|
}
|
12
ui/label.go
12
ui/label.go
@ -35,7 +35,7 @@ func (l *Label) hashContent(ctx Context) string {
|
|||||||
|
|
||||||
func (l *Label) desiredSize(ctx Context) interface{} {
|
func (l *Label) desiredSize(ctx Context) interface{} {
|
||||||
fontName := l.FontName(ctx)
|
fontName := l.FontName(ctx)
|
||||||
font := ctx.Renderer().Font(fontName)
|
font := ctx.Fonts().Font(fontName)
|
||||||
width := font.WidthOf(l.Text)
|
width := font.WidthOf(l.Text)
|
||||||
height := font.Height()
|
height := font.Height()
|
||||||
pad := ctx.Style().Dimensions.TextPadding
|
pad := ctx.Style().Dimensions.TextPadding
|
||||||
@ -49,16 +49,16 @@ func (l *Label) DesiredSize(ctx Context) geom.PointF32 {
|
|||||||
|
|
||||||
func (l *Label) Render(ctx Context) {
|
func (l *Label) Render(ctx Context) {
|
||||||
l.RenderBackground(ctx)
|
l.RenderBackground(ctx)
|
||||||
c := l.FontColor(ctx)
|
fontColor := l.FontColor(ctx)
|
||||||
f := l.FontName(ctx)
|
fontName := l.FontName(ctx)
|
||||||
pad := ctx.Style().Dimensions.TextPadding
|
pad := ctx.Style().Dimensions.TextPadding
|
||||||
bounds := l.bounds.Inset(pad)
|
bounds := l.bounds.Inset(pad)
|
||||||
switch l.TextAlignment {
|
switch l.TextAlignment {
|
||||||
case AlignLeft:
|
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:
|
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:
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ type Renderer interface {
|
|||||||
|
|
||||||
// Drawing
|
// Drawing
|
||||||
Clear(c color.Color)
|
Clear(c color.Color)
|
||||||
|
CreateFontPath(path string, size int) (Font, error)
|
||||||
CreateTexture(m ImageSource) (Texture, error)
|
CreateTexture(m ImageSource) (Texture, error)
|
||||||
CreateTextureGo(m image.Image, source bool) (Texture, error)
|
CreateTextureGo(m image.Image, source bool) (Texture, error)
|
||||||
CreateTexturePath(path string, source bool) (Texture, error)
|
CreateTexturePath(path string, source bool) (Texture, error)
|
||||||
@ -25,16 +26,14 @@ type Renderer interface {
|
|||||||
DrawTexture(t Texture, p geom.PointF32)
|
DrawTexture(t Texture, p geom.PointF32)
|
||||||
DrawTextureOptions(t Texture, p geom.PointF32, opts DrawOptions)
|
DrawTextureOptions(t Texture, p geom.PointF32, opts DrawOptions)
|
||||||
FillRectangle(r geom.RectangleF32, c color.Color)
|
FillRectangle(r geom.RectangleF32, c color.Color)
|
||||||
Font(name string) Font
|
|
||||||
Rectangle(r geom.RectangleF32, c color.Color, thickness float32)
|
Rectangle(r geom.RectangleF32, c color.Color, thickness float32)
|
||||||
RegisterFont(name, path string, size int) error
|
|
||||||
RenderTo(Texture)
|
RenderTo(Texture)
|
||||||
RenderToDisplay()
|
RenderToDisplay()
|
||||||
SetMouseCursor(c MouseCursor)
|
SetMouseCursor(c MouseCursor)
|
||||||
Size() geom.PointF32
|
Size() geom.PointF32
|
||||||
Target() Texture
|
Target() Texture
|
||||||
Text(p geom.PointF32, font string, color color.Color, text string)
|
Text(font Font, p geom.PointF32, color color.Color, text string)
|
||||||
TextAlign(p geom.PointF32, font string, color color.Color, text string, align HorizontalAlignment)
|
TextAlign(font Font, p geom.PointF32, color color.Color, text string, align HorizontalAlignment)
|
||||||
|
|
||||||
// Resources
|
// Resources
|
||||||
Resources() Resources
|
Resources() Resources
|
||||||
|
@ -2,6 +2,7 @@ package ui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"opslag.de/schobers/geom"
|
"opslag.de/schobers/geom"
|
||||||
|
"opslag.de/schobers/zntg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Scrollbar struct {
|
type Scrollbar struct {
|
||||||
@ -55,7 +56,7 @@ func (s *Scrollbar) Handle(ctx Context, e Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scrollbar) Render(ctx Context) {
|
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)
|
s.handle.Render(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
72
ui/style.go
72
ui/style.go
@ -1,6 +1,10 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import "image/color"
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"opslag.de/schobers/zntg"
|
||||||
|
)
|
||||||
|
|
||||||
var defaultDimensions *Dimensions
|
var defaultDimensions *Dimensions
|
||||||
var defaultFonts *Fonts
|
var defaultFonts *Fonts
|
||||||
@ -13,7 +17,7 @@ type Dimensions struct {
|
|||||||
TextPadding float32
|
TextPadding float32
|
||||||
}
|
}
|
||||||
|
|
||||||
type Fonts struct {
|
type FontNames struct {
|
||||||
Default string
|
Default string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,60 +48,44 @@ type Palette struct {
|
|||||||
|
|
||||||
type Style struct {
|
type Style struct {
|
||||||
Dimensions *Dimensions
|
Dimensions *Dimensions
|
||||||
Fonts *Fonts
|
Fonts *FontNames
|
||||||
Palette *Palette
|
Palette *Palette
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultDimensions() *Dimensions {
|
func DefaultDimensions() *Dimensions {
|
||||||
if defaultDimensions == nil {
|
return &Dimensions{
|
||||||
defaultDimensions = &Dimensions{
|
OutlineWidth: 2.,
|
||||||
OutlineWidth: 2.,
|
ScrollbarWidth: 16.,
|
||||||
ScrollbarWidth: 16.,
|
TextPadding: 8.,
|
||||||
TextPadding: 8.,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return defaultDimensions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultFonts() *Fonts {
|
func DefaultFontNames() *FontNames {
|
||||||
if defaultFonts == nil {
|
return &FontNames{
|
||||||
defaultFonts = &Fonts{
|
Default: "default",
|
||||||
Default: "default",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return defaultFonts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultPalette() *Palette {
|
func DefaultPalette() *Palette {
|
||||||
if defaultPalette == nil {
|
return &Palette{
|
||||||
defaultPalette = &Palette{
|
Background: color.White,
|
||||||
Background: color.White,
|
Primary: zntg.MustHexColor(`#3F51B5`),
|
||||||
Primary: RGBA(0x3F, 0x51, 0xB5, 0xFF),
|
PrimaryDark: zntg.MustHexColor(`#002884`),
|
||||||
PrimaryDark: RGBA(0x00, 0x28, 0x84, 0xFF),
|
PrimaryHighlight: zntg.MustHexColor(`#E8EAF6`),
|
||||||
PrimaryHighlight: RGBA(0xE8, 0xEA, 0xF6, 0xFF),
|
PrimaryLight: zntg.MustHexColor(`#757CE8`),
|
||||||
PrimaryLight: RGBA(0x75, 0x7C, 0xE8, 0xFF),
|
ShadedBackground: zntg.MustHexColor(`#FAFAFA`),
|
||||||
ShadedBackground: RGBA(0xFA, 0xFA, 0xFA, 0xFF),
|
Text: color.Black,
|
||||||
Text: color.Black,
|
TextDisabled: zntg.MustHexColor(`#BDBDBD`),
|
||||||
TextDisabled: RGBA(0xBD, 0xBD, 0xBD, 0xFF),
|
TextNegative: zntg.MustHexColor(`#FF4336`),
|
||||||
TextNegative: RGBA(0xFF, 0x43, 0x36, 0xFF),
|
TextOnPrimary: color.White,
|
||||||
TextOnPrimary: color.White,
|
TextPositive: zntg.MustHexColor(`#4CAF50`),
|
||||||
TextPositive: RGBA(0x4C, 0xAF, 0x50, 0xFF),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return defaultPalette
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultStyle() *Style {
|
func DefaultStyle() *Style {
|
||||||
if defaultStyle == nil {
|
return &Style{
|
||||||
defaultStyle = &Style{
|
Dimensions: DefaultDimensions(),
|
||||||
Dimensions: DefaultDimensions(),
|
Fonts: DefaultFontNames(),
|
||||||
Fonts: DefaultFonts(),
|
Palette: DefaultPalette(),
|
||||||
Palette: DefaultPalette(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return defaultStyle
|
|
||||||
}
|
|
||||||
|
|
||||||
func RGBA(r, g, b, a byte) *color.RGBA {
|
|
||||||
return &color.RGBA{R: r, G: g, B: b, A: a}
|
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ func (b *TextBox) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.Poi
|
|||||||
|
|
||||||
func (b *TextBox) DesiredSize(ctx Context) geom.PointF32 {
|
func (b *TextBox) DesiredSize(ctx Context) geom.PointF32 {
|
||||||
var fontName = b.FontName(ctx)
|
var fontName = b.FontName(ctx)
|
||||||
var font = ctx.Renderer().Font(fontName)
|
var font = ctx.Fonts().Font(fontName)
|
||||||
var width = font.WidthOf(b.Text)
|
var width = font.WidthOf(b.Text)
|
||||||
var height = font.Height()
|
var height = font.Height()
|
||||||
var pad = b.pad(ctx)
|
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 {
|
func (b *TextBox) mousePosToCaretPos(ctx Context, e MouseEvent) int {
|
||||||
p := b.ToControlPosition(e.Pos())
|
p := b.ToControlPosition(e.Pos())
|
||||||
offset := p.X - b.box.bounds.Min.X
|
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 carets = [3]int{0, 0, len(b.Text)}
|
||||||
var offsets = [3]float32{0, 0, f.WidthOf(b.Text)}
|
var offsets = [3]float32{0, 0, f.WidthOf(b.Text)}
|
||||||
var updateCenter = func() {
|
var updateCenter = func() {
|
||||||
@ -234,7 +234,6 @@ func (b *TextBox) Render(ctx Context) {
|
|||||||
b.RenderOutline(ctx)
|
b.RenderOutline(ctx)
|
||||||
|
|
||||||
c := b.FontColor(ctx)
|
c := b.FontColor(ctx)
|
||||||
f := b.FontName(ctx)
|
|
||||||
style := ctx.Style()
|
style := ctx.Style()
|
||||||
var caretWidth float32 = 1
|
var caretWidth float32 = 1
|
||||||
b.box.RenderFn(ctx, func(_ Context, size geom.PointF32) {
|
b.box.RenderFn(ctx, func(_ Context, size geom.PointF32) {
|
||||||
@ -244,14 +243,16 @@ func (b *TextBox) Render(ctx Context) {
|
|||||||
back = ctx.Style().Palette.Background
|
back = ctx.Style().Palette.Background
|
||||||
}
|
}
|
||||||
renderer.Clear(back)
|
renderer.Clear(back)
|
||||||
|
fontName := b.FontName(ctx)
|
||||||
|
font := ctx.Fonts().Font(fontName)
|
||||||
if b.Selection.Start != b.Selection.End {
|
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.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
|
const interval = 500 * time.Millisecond
|
||||||
if b.Focus && (time.Since(b.blink)/interval)%2 == 0 {
|
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
|
var caret = w + .5*caretWidth
|
||||||
renderer.Rectangle(geom.RectF32(caret, 0, caret, size.Y), c, caretWidth)
|
renderer.Rectangle(geom.RectF32(caret, 0, caret, size.Y), c, caretWidth)
|
||||||
}
|
}
|
||||||
|
4
ui/ui.go
4
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.
|
// RunWait runs the application loop and conditionally waits on events before rendering.
|
||||||
func RunWait(r Renderer, s *Style, view Control, wait bool) error {
|
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)
|
root, ok := view.(RootControl)
|
||||||
if ok {
|
if ok {
|
||||||
err := root.Init(ctx)
|
err := root.Init(ctx)
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"opslag.de/schobers/geom"
|
"opslag.de/schobers/geom"
|
||||||
|
"opslag.de/schobers/zntg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Background(content Control, c color.Color) Control {
|
func Background(content Control, c color.Color) Control {
|
||||||
@ -37,7 +38,7 @@ type shadow struct {
|
|||||||
func (s *shadow) Render(ctx Context) {
|
func (s *shadow) Render(ctx Context) {
|
||||||
s.Proxy.Render(ctx)
|
s.Proxy.Render(ctx)
|
||||||
b := s.Bounds()
|
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+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+2), shadow)
|
||||||
ctx.Renderer().FillRectangle(geom.RectF32(b.Min.X, b.Min.Y, b.Max.X, b.Min.Y+1), shadow)
|
ctx.Renderer().FillRectangle(geom.RectF32(b.Min.X, b.Min.Y, b.Max.X, b.Min.Y+1), shadow)
|
||||||
|
Loading…
Reference in New Issue
Block a user