From 3a18d3adf92b83b81756c1b61d4a79ada941b64e Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Fri, 15 May 2020 16:39:53 +0200 Subject: [PATCH] Added support for tooltips. --- ui/context.go | 24 +++++++++++-- ui/controlbase.go | 6 ++++ ui/examples/01_basic/basic.go | 1 + ui/style.go | 2 ++ ui/tooltip.go | 64 +++++++++++++++++++++++++++++++++++ ui/ui.go | 2 ++ 6 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 ui/tooltip.go diff --git a/ui/context.go b/ui/context.go index a4a41ee..9e50161 100644 --- a/ui/context.go +++ b/ui/context.go @@ -1,12 +1,18 @@ package ui +import ( + "opslag.de/schobers/geom" +) + type Context interface { Animate() Fonts() *Fonts HasQuit() bool + MousePosition() geom.PointF32 Overlays() *Overlays Quit() Renderer() Renderer + ShowTooltip(t string) Style() *Style Textures() *Textures } @@ -19,6 +25,8 @@ type context struct { quit chan struct{} renderer Renderer view Control + mouse geom.PointF32 + tooltip *Tooltip overlays *Overlays fonts *Fonts textures *Textures @@ -26,14 +34,17 @@ type context struct { } func newContext(r Renderer, s *Style, view Control) *context { - return &context{ + ctx := &context{ quit: make(chan struct{}), renderer: r, style: s, view: view, + tooltip: &Tooltip{}, overlays: NewOverlays(view), fonts: NewFonts(r), textures: NewTextures(r)} + ctx.overlays.AddOnTop(uiDefaultTooltipOverlay, ctx.tooltip, false) + return ctx } func (c *context) Animate() { c.animate = true } @@ -54,10 +65,17 @@ func (c *context) HasQuit() bool { } } +func (c *context) MousePosition() geom.PointF32 { return c.mouse } + func (c *context) Overlays() *Overlays { return c.overlays } func (c *context) Renderer() Renderer { return c.renderer } +func (c *context) ShowTooltip(t string) { + c.overlays.Show(uiDefaultTooltipOverlay) + c.tooltip.Text = t +} + func (c *context) Style() *Style { return c.style } func (c *context) Quit() { @@ -71,10 +89,12 @@ func (c *context) Textures() *Textures { return c.textures } // Handle implement EventTarget func (c *context) Handle(e Event) { - switch e.(type) { + switch e := e.(type) { case *DisplayCloseEvent: c.Quit() return + case *MouseMoveEvent: + c.mouse = e.Pos() } c.overlays.Handle(c, e) } diff --git a/ui/controlbase.go b/ui/controlbase.go index 7df85ce..64ea79b 100644 --- a/ui/controlbase.go +++ b/ui/controlbase.go @@ -31,6 +31,8 @@ type ControlBase struct { Background color.Color Font FontStyle TextAlignment HorizontalAlignment + + Tooltip string } func (c *ControlBase) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) { @@ -100,6 +102,10 @@ func (c *ControlBase) Handle(ctx Context, e Event) { c.pressed = false } } + + if c.Tooltip != "" && c.over { + ctx.ShowTooltip(c.Tooltip) + } } func (c *ControlBase) FontColor(ctx Context) color.Color { diff --git a/ui/examples/01_basic/basic.go b/ui/examples/01_basic/basic.go index a7cc762..712376a 100644 --- a/ui/examples/01_basic/basic.go +++ b/ui/examples/01_basic/basic.go @@ -62,6 +62,7 @@ func (b *basic) Init(ctx ui.Context) error { b.OnClick(func(geom.PointF32, ui.MouseButton) { ctx.Quit() }) + b.Tooltip = "Will quit the application" }), 8), ui.BuildLabel("Status...", func(l *ui.Label) { l.Background = style.Palette.PrimaryDark diff --git a/ui/style.go b/ui/style.go index f10ef63..7147ebe 100644 --- a/ui/style.go +++ b/ui/style.go @@ -19,6 +19,7 @@ type Dimensions struct { type FontNames struct { Default string + Tooltip string } type Palette struct { @@ -63,6 +64,7 @@ func DefaultDimensions() *Dimensions { func DefaultFontNames() *FontNames { return &FontNames{ Default: "default", + Tooltip: "default", } } diff --git a/ui/tooltip.go b/ui/tooltip.go new file mode 100644 index 0000000..8417a4a --- /dev/null +++ b/ui/tooltip.go @@ -0,0 +1,64 @@ +package ui + +import ( + "image/color" + + "opslag.de/schobers/geom" + "opslag.de/schobers/zntg" +) + +const tooltipBorderThickness = 1 +const tooltipHorizontalPadding = 6 +const tooltipVerticalPadding = 2 +const tooltipMouseDistance = 12 +const uiDefaultTooltipOverlay = "ui-default-tooltip" + +type Tooltip struct { + ControlBase + + Text string +} + +func (t *Tooltip) FontName(ctx Context) string { + var name = t.Font.Name + if len(name) == 0 { + name = ctx.Style().Fonts.Tooltip + } + return name +} + +func (t *Tooltip) Handle(Context, Event) {} + +func (t *Tooltip) Render(ctx Context) { + if len(t.Text) == 0 { + return + } + + fontName := t.FontName(ctx) + size := ctx.Fonts().Font(fontName).Measure(t.Text) + + offset := t.Offset() + mouse := ctx.MousePosition().Sub(offset) + width := size.Dx() + 2*tooltipBorderThickness + 2*tooltipHorizontalPadding + height := size.Dy() + 2*tooltipBorderThickness + 2*tooltipVerticalPadding + + left := mouse.X + tooltipMouseDistance + top := mouse.Y + tooltipMouseDistance + if left+width > t.bounds.Max.X { + left = mouse.X - tooltipMouseDistance - width + } + if top+height > t.bounds.Max.Y { + top = mouse.Y - tooltipMouseDistance - height + } + + bounds := geom.RectRelF32(left, top, width, height) + + almostBlack := zntg.MustHexColor("#000000bf") + almostWhite := zntg.MustHexColor("ffffffbf") + + ctx.Renderer().FillRectangle(bounds, almostBlack) + ctx.Renderer().Rectangle(bounds, almostWhite, 1) + + bottomLeft := bounds.Min.Add2D(tooltipBorderThickness+tooltipHorizontalPadding, tooltipBorderThickness+tooltipVerticalPadding) + ctx.Fonts().Text(fontName, bottomLeft, color.White, t.Text) +} diff --git a/ui/ui.go b/ui/ui.go index 6a8a103..ac710f4 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -49,6 +49,8 @@ func RunWait(r Renderer, s *Style, view Control, wait bool) error { if ctx.HasQuit() { return nil } + + ctx.overlays.Hide("ui-default-tooltip") r.PushEvents(ctx, wait) } return nil