Added Slider.
Refactored Icon related methods & added alpha support for icon/image generation.
This commit is contained in:
parent
4d05127c6d
commit
432281f08d
@ -65,7 +65,7 @@ func (c *Checkbox) checkMark() geom.PointsF32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checkbox) hoverIcon() IconPixelTestFn {
|
func (c *Checkbox) hoverIcon() ImagePixelTestFn {
|
||||||
border := c.iconBorder()
|
border := c.iconBorder()
|
||||||
check := c.checkMark()
|
check := c.checkMark()
|
||||||
return func(pt geom.PointF32) bool {
|
return func(pt geom.PointF32) bool {
|
||||||
@ -73,14 +73,14 @@ func (c *Checkbox) hoverIcon() IconPixelTestFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checkbox) normalIcon() IconPixelTestFn {
|
func (c *Checkbox) normalIcon() ImagePixelTestFn {
|
||||||
border := c.iconBorder()
|
border := c.iconBorder()
|
||||||
return func(pt geom.PointF32) bool {
|
return func(pt geom.PointF32) bool {
|
||||||
return pt.DistanceToPolygon(border) < 48 && !pt.InPolygon(border)
|
return pt.DistanceToPolygon(border) < 48 && !pt.InPolygon(border)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checkbox) selectedIcon() IconPixelTestFn {
|
func (c *Checkbox) selectedIcon() ImagePixelTestFn {
|
||||||
border := c.iconBorder()
|
border := c.iconBorder()
|
||||||
check := c.checkMark()
|
check := c.checkMark()
|
||||||
return func(pt geom.PointF32) bool {
|
return func(pt geom.PointF32) bool {
|
||||||
|
53
ui/icon.go
53
ui/icon.go
@ -7,23 +7,38 @@ import (
|
|||||||
"opslag.de/schobers/geom"
|
"opslag.de/schobers/geom"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IconPixelTestFn func(geom.PointF32) bool
|
type ImagePixelTestFn func(geom.PointF32) bool
|
||||||
|
type ImageAlphaPixelTestFn func(geom.PointF32) uint8
|
||||||
|
|
||||||
func IconSize() geom.Point {
|
func createImage(ctx Context, image image.Image) Image {
|
||||||
return geom.Pt(448, 512)
|
im, err := ctx.Renderer().CreateImage(image)
|
||||||
}
|
|
||||||
|
|
||||||
func CreateIcon(ctx Context, test IconPixelTestFn) Image {
|
|
||||||
icon := DrawIcon(test)
|
|
||||||
im, err := ctx.Renderer().CreateImage(icon)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return im
|
return im
|
||||||
}
|
}
|
||||||
|
|
||||||
func DrawIcon(test IconPixelTestFn) image.Image {
|
func CreateIcon(ctx Context, test ImagePixelTestFn) Image {
|
||||||
|
icon := DrawIcon(test)
|
||||||
|
return createImage(ctx, icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateImage(ctx Context, size geom.Point, test ImagePixelTestFn) Image {
|
||||||
|
image := DrawImage(size, test)
|
||||||
|
return createImage(ctx, image)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateImageAlpha(ctx Context, size geom.Point, test ImageAlphaPixelTestFn) Image {
|
||||||
|
image := DrawImageAlpha(size, test)
|
||||||
|
return createImage(ctx, image)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DrawIcon(test ImagePixelTestFn) image.Image {
|
||||||
size := IconSize()
|
size := IconSize()
|
||||||
|
return DrawImage(size, test)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DrawImage(size geom.Point, test ImagePixelTestFn) image.Image {
|
||||||
r := image.Rect(0, 0, size.X, size.Y)
|
r := image.Rect(0, 0, size.X, size.Y)
|
||||||
icon := image.NewRGBA(r)
|
icon := image.NewRGBA(r)
|
||||||
for y := 0; y < size.Y; y++ {
|
for y := 0; y < size.Y; y++ {
|
||||||
@ -37,7 +52,21 @@ func DrawIcon(test IconPixelTestFn) image.Image {
|
|||||||
return icon
|
return icon
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetOrCreateIcon(ctx Context, name string, testFactory func() IconPixelTestFn) Image {
|
func DrawImageAlpha(size geom.Point, test ImageAlphaPixelTestFn) image.Image {
|
||||||
|
r := image.Rect(0, 0, size.X, size.Y)
|
||||||
|
icon := image.NewAlpha(r)
|
||||||
|
for y := 0; y < size.Y; y++ {
|
||||||
|
for x := 0; x < size.X; x++ {
|
||||||
|
pt := geom.PtF32(float32(x), float32(y))
|
||||||
|
if a := test(pt); a > 0 {
|
||||||
|
icon.Set(x, y, color.Alpha{A: a})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return icon
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOrCreateIcon(ctx Context, name string, testFactory func() ImagePixelTestFn) Image {
|
||||||
im := ctx.Images().Image(name)
|
im := ctx.Images().Image(name)
|
||||||
if im != nil {
|
if im != nil {
|
||||||
return im
|
return im
|
||||||
@ -50,3 +79,7 @@ func GetOrCreateIcon(ctx Context, name string, testFactory func() IconPixelTestF
|
|||||||
ctx.Images().AddImage(name, im)
|
ctx.Images().AddImage(name, im)
|
||||||
return im
|
return im
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IconSize() geom.Point {
|
||||||
|
return geom.Pt(448, 512)
|
||||||
|
}
|
||||||
|
153
ui/slider.go
Normal file
153
ui/slider.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Slider struct {
|
||||||
|
ControlBase
|
||||||
|
|
||||||
|
Orientation Orientation
|
||||||
|
Minimum float32
|
||||||
|
Maximum float32
|
||||||
|
Value float32
|
||||||
|
|
||||||
|
handleWidth float32
|
||||||
|
handle sliderHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildSlider(o Orientation, fn func(s *Slider)) *Slider {
|
||||||
|
s := &Slider{Orientation: o}
|
||||||
|
s.handle.OnDragStart(func(start geom.PointF32) { s.handleDrag(start) })
|
||||||
|
s.handle.OnDragMove(func(_, pos geom.PointF32) { s.handleDrag(pos) })
|
||||||
|
if fn != nil {
|
||||||
|
fn(s)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Slider) handleDrag(pos geom.PointF32) {
|
||||||
|
start, length, _ := s.offsets()
|
||||||
|
var offset float32
|
||||||
|
if s.Orientation == OrientationHorizontal {
|
||||||
|
offset = pos.X
|
||||||
|
} else {
|
||||||
|
offset = pos.Y
|
||||||
|
}
|
||||||
|
s.Value = s.Minimum + ((offset-start)/length)*(s.Maximum-s.Minimum)
|
||||||
|
if s.Minimum < s.Maximum {
|
||||||
|
s.Value = geom.Min32(s.Maximum, geom.Max32(s.Minimum, s.Value))
|
||||||
|
} else {
|
||||||
|
s.Value = geom.Min32(s.Minimum, geom.Max32(s.Maximum, s.Value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Slider) offsets() (start, length, center float32) {
|
||||||
|
bounds := s.Bounds()
|
||||||
|
if s.Orientation == OrientationHorizontal {
|
||||||
|
start = bounds.Min.X
|
||||||
|
length = bounds.Dx()
|
||||||
|
center = .5 * (bounds.Min.Y + bounds.Max.Y)
|
||||||
|
} else {
|
||||||
|
start = bounds.Max.Y
|
||||||
|
length = -bounds.Dy()
|
||||||
|
center = .5 * (bounds.Min.X + bounds.Max.X)
|
||||||
|
}
|
||||||
|
if geom.Abs32(length) < s.handleWidth {
|
||||||
|
start += .5 * length
|
||||||
|
length = 0
|
||||||
|
} else {
|
||||||
|
if length < 0 {
|
||||||
|
start -= .5 * s.handleWidth
|
||||||
|
length += s.handleWidth
|
||||||
|
} else {
|
||||||
|
start += .5 * s.handleWidth
|
||||||
|
length -= s.handleWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Slider) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||||
|
s.ControlBase.Arrange(ctx, bounds, offset, parent)
|
||||||
|
|
||||||
|
s.handleWidth = ctx.Style().Dimensions.ScrollbarWidth
|
||||||
|
start, length, center := s.offsets()
|
||||||
|
w := s.handleWidth
|
||||||
|
w05 := .5 * w
|
||||||
|
handleOffset := (s.Value - s.Minimum) / (s.Maximum - s.Minimum)
|
||||||
|
if !geom.IsNaN32(handleOffset) {
|
||||||
|
start += handleOffset * length
|
||||||
|
}
|
||||||
|
if s.Orientation == OrientationHorizontal {
|
||||||
|
s.handle.Arrange(ctx, geom.RectF32(start-w05, center-w05, start+w05, center+w05), offset, s)
|
||||||
|
} else {
|
||||||
|
s.handle.Arrange(ctx, geom.RectF32(center-w05, start-w05, center+w05, start+w05), offset, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Slider) DesiredSize(ctx Context) geom.PointF32 {
|
||||||
|
w := ctx.Style().Dimensions.ScrollbarWidth
|
||||||
|
if s.Orientation == OrientationHorizontal {
|
||||||
|
return geom.PtF32(geom.NaN32(), w)
|
||||||
|
}
|
||||||
|
return geom.PtF32(w, geom.NaN32())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Slider) Handle(ctx Context, e Event) {
|
||||||
|
s.handle.Handle(ctx, e)
|
||||||
|
s.ControlBase.Handle(ctx, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Slider) Render(ctx Context) {
|
||||||
|
s.RenderBackground(ctx)
|
||||||
|
prim := ctx.Style().Palette.Primary
|
||||||
|
start, length, center := s.offsets()
|
||||||
|
if s.Orientation == OrientationHorizontal {
|
||||||
|
ctx.Renderer().FillRectangle(geom.RectF32(start, center-1, start+length, center+1), prim)
|
||||||
|
} else {
|
||||||
|
ctx.Renderer().FillRectangle(geom.RectF32(center-1, start, center+1, start+length), prim)
|
||||||
|
}
|
||||||
|
s.handle.Render(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliderHandle struct {
|
||||||
|
ControlBase
|
||||||
|
|
||||||
|
handle Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sliderHandle) im(ctx Context) Image {
|
||||||
|
if h.handle != nil {
|
||||||
|
return h.handle
|
||||||
|
}
|
||||||
|
size := h.Bounds().Size()
|
||||||
|
center := geom.PtF32(.5*size.X-.5, .5*size.Y-.5)
|
||||||
|
radius := 0.5 * geom.Min32(size.X, size.Y)
|
||||||
|
h.handle = CreateImageAlpha(ctx, geom.Pt(int(size.X), int(size.Y)), func(p geom.PointF32) uint8 {
|
||||||
|
dist := center.Distance(p)
|
||||||
|
if dist < radius {
|
||||||
|
if dist < (radius - 1) {
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
return uint8(255 * (radius - dist))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
return h.handle
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sliderHandle) Handle(ctx Context, e Event) {
|
||||||
|
h.ControlBase.Handle(ctx, e)
|
||||||
|
if h.IsOver() {
|
||||||
|
ctx.Renderer().SetMouseCursor(MouseCursorPointer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *sliderHandle) Render(ctx Context) {
|
||||||
|
color := ctx.Style().Palette.Primary
|
||||||
|
if h.IsOver() {
|
||||||
|
color = ctx.Style().Palette.PrimaryLight
|
||||||
|
}
|
||||||
|
ctx.Renderer().DrawImageOptions(h.im(ctx), h.Bounds().Min, DrawOptions{Tint: color})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user