Added Parent to Controls.
Fixed issue where events of partially and invisible controls where incorrectly handled.
This commit is contained in:
parent
3e7e2ab682
commit
dbc017507c
@ -45,8 +45,8 @@ type BufferControl struct {
|
|||||||
buffer Buffer
|
buffer Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BufferControl) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32) {
|
func (c *BufferControl) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||||
c.ControlBase.Arrange(ctx, bounds, offset)
|
c.ControlBase.Arrange(ctx, bounds, offset, parent)
|
||||||
c.buffer.Update(ctx, bounds.Size())
|
c.buffer.Update(ctx, bounds.Size())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,18 +19,18 @@ func (c *ContainerBase) AddChild(child ...Control) {
|
|||||||
c.Children = append(c.Children, child...)
|
c.Children = append(c.Children, child...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ContainerBase) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32) {
|
func (c *ContainerBase) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||||
for _, child := range c.Children {
|
for _, child := range c.Children {
|
||||||
child.Arrange(ctx, bounds, offset)
|
child.Arrange(ctx, bounds, offset, c)
|
||||||
}
|
}
|
||||||
c.ControlBase.Arrange(ctx, bounds, offset)
|
c.ControlBase.Arrange(ctx, bounds, offset, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ContainerBase) Handle(ctx Context, e Event) {
|
func (c *ContainerBase) Handle(ctx Context, e Event) {
|
||||||
|
c.ControlBase.Handle(ctx, e)
|
||||||
for _, child := range c.Children {
|
for _, child := range c.Children {
|
||||||
child.Handle(ctx, e)
|
child.Handle(ctx, e)
|
||||||
}
|
}
|
||||||
c.ControlBase.Handle(ctx, e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ContainerBase) Render(ctx Context) {
|
func (c *ContainerBase) Render(ctx Context) {
|
||||||
|
@ -5,17 +5,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Control interface {
|
type Control interface {
|
||||||
Arrange(Context, geom.RectangleF32, geom.PointF32)
|
Arrange(Context, geom.RectangleF32, geom.PointF32, Control)
|
||||||
DesiredSize(Context) geom.PointF32
|
DesiredSize(Context) geom.PointF32
|
||||||
Handle(Context, Event)
|
Handle(Context, Event)
|
||||||
Render(Context)
|
Render(Context)
|
||||||
|
|
||||||
Bounds() geom.RectangleF32
|
Bounds() geom.RectangleF32
|
||||||
|
IsInBounds(p geom.PointF32) bool
|
||||||
|
IsOver() bool
|
||||||
Offset() geom.PointF32
|
Offset() geom.PointF32
|
||||||
OnClick(ClickFn)
|
OnClick(ClickFn)
|
||||||
OnDragStart(DragStartFn)
|
OnDragStart(DragStartFn)
|
||||||
OnDragMove(DragMoveFn)
|
OnDragMove(DragMoveFn)
|
||||||
OnDragEnd(DragEndFn)
|
OnDragEnd(DragEndFn)
|
||||||
|
Parent() Control
|
||||||
}
|
}
|
||||||
|
|
||||||
type RootControl interface {
|
type RootControl interface {
|
||||||
|
@ -18,6 +18,7 @@ var _ Control = &ControlBase{}
|
|||||||
type ControlBase struct {
|
type ControlBase struct {
|
||||||
bounds geom.RectangleF32
|
bounds geom.RectangleF32
|
||||||
offset geom.PointF32
|
offset geom.PointF32
|
||||||
|
parent Control
|
||||||
dragStart *geom.PointF32
|
dragStart *geom.PointF32
|
||||||
over bool
|
over bool
|
||||||
pressed bool
|
pressed bool
|
||||||
@ -31,55 +32,59 @@ type ControlBase struct {
|
|||||||
Font FontStyle
|
Font FontStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ControlBase) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32) {
|
func (c *ControlBase) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||||
c.bounds = bounds
|
c.bounds = bounds
|
||||||
c.offset = offset
|
c.offset = offset
|
||||||
|
c.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ControlBase) Bounds() geom.RectangleF32 {
|
func (c *ControlBase) Bounds() geom.RectangleF32 { return c.bounds }
|
||||||
return c.bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ControlBase) DesiredSize(Context) geom.PointF32 {
|
func (c *ControlBase) DesiredSize(Context) geom.PointF32 { return geom.ZeroPtF32 }
|
||||||
return geom.ZeroPtF32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ControlBase) mousePos(e MouseEvent) geom.PointF32 {
|
|
||||||
return e.Pos().Sub(c.offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ControlBase) Handle(ctx Context, e Event) {
|
func (c *ControlBase) Handle(ctx Context, e Event) {
|
||||||
var over = func(e MouseEvent) bool {
|
var over = func(e MouseEvent) bool {
|
||||||
c.over = c.mousePos(e).In(c.bounds)
|
pos := e.Pos()
|
||||||
return c.over
|
if !c.IsInBounds(pos) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
parent := c.Parent()
|
||||||
|
for parent != nil {
|
||||||
|
if !parent.IsInBounds(pos) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
parent = parent.Parent()
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case *MouseMoveEvent:
|
case *MouseMoveEvent:
|
||||||
over(e.MouseEvent)
|
c.over = over(e.MouseEvent)
|
||||||
if c.pressed {
|
if c.pressed {
|
||||||
if c.dragStart == nil {
|
if c.dragStart == nil {
|
||||||
var start = c.mousePos(e.MouseEvent)
|
var start = c.ToControlPosition(e.Pos())
|
||||||
c.dragStart = &start
|
c.dragStart = &start
|
||||||
if c.onDragStart != nil {
|
if c.onDragStart != nil {
|
||||||
c.onDragStart(ctx, c, start)
|
c.onDragStart(ctx, c, start)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var start = *c.dragStart
|
var start = *c.dragStart
|
||||||
var move = c.mousePos(e.MouseEvent)
|
var move = c.ToControlPosition(e.Pos())
|
||||||
if c.onDragMove != nil {
|
if c.onDragMove != nil {
|
||||||
c.onDragMove(ctx, c, start, move)
|
c.onDragMove(ctx, c, start, move)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *MouseButtonDownEvent:
|
case *MouseButtonDownEvent:
|
||||||
if over(e.MouseEvent) && e.Button == MouseButtonLeft {
|
c.over = over(e.MouseEvent)
|
||||||
|
if c.over && e.Button == MouseButtonLeft {
|
||||||
c.pressed = true
|
c.pressed = true
|
||||||
}
|
}
|
||||||
case *MouseButtonUpEvent:
|
case *MouseButtonUpEvent:
|
||||||
if e.Button == MouseButtonLeft {
|
if e.Button == MouseButtonLeft {
|
||||||
if c.dragStart != nil {
|
if c.dragStart != nil {
|
||||||
var start = *c.dragStart
|
var start = *c.dragStart
|
||||||
var end = c.mousePos(e.MouseEvent)
|
var end = c.ToControlPosition(e.Pos())
|
||||||
c.dragStart = nil
|
c.dragStart = nil
|
||||||
if c.onDragEnd != nil {
|
if c.onDragEnd != nil {
|
||||||
c.onDragEnd(ctx, c, start, end)
|
c.onDragEnd(ctx, c, start, end)
|
||||||
@ -87,7 +92,7 @@ func (c *ControlBase) Handle(ctx Context, e Event) {
|
|||||||
}
|
}
|
||||||
if c.pressed {
|
if c.pressed {
|
||||||
if c.onClick != nil {
|
if c.onClick != nil {
|
||||||
c.onClick(ctx, c, c.mousePos(e.MouseEvent), e.Button)
|
c.onClick(ctx, c, c.ToControlPosition(e.Pos()), e.Button)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.pressed = false
|
c.pressed = false
|
||||||
@ -111,15 +116,26 @@ func (c *ControlBase) FontName(ctx Context) string {
|
|||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ControlBase) IsInBounds(p geom.PointF32) bool {
|
||||||
|
bounds := c.bounds
|
||||||
|
if bounds.Min.X < 0 {
|
||||||
|
bounds.Min.X = 0
|
||||||
|
}
|
||||||
|
if bounds.Min.Y < 0 {
|
||||||
|
bounds.Min.Y = 0
|
||||||
|
}
|
||||||
|
return c.ToControlPosition(p).In(c.bounds)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ControlBase) IsOver() bool { return c.over }
|
func (c *ControlBase) IsOver() bool { return c.over }
|
||||||
|
|
||||||
|
func (c *ControlBase) Parent() Control { return c.parent }
|
||||||
|
|
||||||
func (c *ControlBase) IsPressed() bool { return c.pressed }
|
func (c *ControlBase) IsPressed() bool { return c.pressed }
|
||||||
|
|
||||||
func (c *ControlBase) Offset() geom.PointF32 { return c.offset }
|
func (c *ControlBase) Offset() geom.PointF32 { return c.offset }
|
||||||
|
|
||||||
func (c *ControlBase) OnClick(fn ClickFn) {
|
func (c *ControlBase) OnClick(fn ClickFn) { c.onClick = fn }
|
||||||
c.onClick = fn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ControlBase) OnDragStart(fn DragStartFn) {
|
func (c *ControlBase) OnDragStart(fn DragStartFn) {
|
||||||
c.onDragStart = fn
|
c.onDragStart = fn
|
||||||
@ -129,6 +145,8 @@ func (c *ControlBase) OnDragMove(fn DragMoveFn) {
|
|||||||
c.onDragMove = fn
|
c.onDragMove = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ControlBase) OnDragEnd(fn DragEndFn) { c.onDragEnd = fn }
|
||||||
|
|
||||||
func (c *ControlBase) Render(Context) {}
|
func (c *ControlBase) Render(Context) {}
|
||||||
|
|
||||||
func (c *ControlBase) RenderBackground(ctx Context) {
|
func (c *ControlBase) RenderBackground(ctx Context) {
|
||||||
@ -146,3 +164,5 @@ func (c *ControlBase) RenderOutline(ctx Context) {
|
|||||||
}
|
}
|
||||||
ctx.Renderer().Rectangle(c.bounds.Inset(.5*width), color, width)
|
ctx.Renderer().Rectangle(c.bounds.Inset(.5*width), color, width)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ControlBase) ToControlPosition(p geom.PointF32) geom.PointF32 { return p.Sub(c.offset) }
|
||||||
|
@ -15,6 +15,7 @@ type overflow struct {
|
|||||||
desired geom.PointF32
|
desired geom.PointF32
|
||||||
bounds geom.RectangleF32
|
bounds geom.RectangleF32
|
||||||
offset geom.PointF32
|
offset geom.PointF32
|
||||||
|
parent Control
|
||||||
content Buffer
|
content Buffer
|
||||||
|
|
||||||
hor *Scrollbar
|
hor *Scrollbar
|
||||||
@ -58,31 +59,32 @@ func (o *overflow) doOnVisibleBars(fn func(bar *Scrollbar)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *overflow) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32) {
|
func (o *overflow) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||||
o.barWidth = ctx.Style().Dimensions.ScrollbarWidth
|
o.barWidth = ctx.Style().Dimensions.ScrollbarWidth
|
||||||
o.desired = o.Content.DesiredSize(ctx)
|
o.desired = o.Content.DesiredSize(ctx)
|
||||||
o.bounds = bounds
|
o.bounds = bounds
|
||||||
o.offset = offset
|
o.offset = offset
|
||||||
|
o.parent = parent
|
||||||
|
|
||||||
var hor, ver = o.shouldScroll(bounds)
|
var hor, ver = o.shouldScroll(bounds)
|
||||||
var contentX, contentY float32 = 0, 0
|
var contentX, contentY float32 = 0, 0
|
||||||
var contentW, contentH = bounds.Dx(), bounds.Dy()
|
var contentW, contentH = bounds.Dx(), bounds.Dy()
|
||||||
if hor {
|
if hor {
|
||||||
contentX -= o.hor.Offset
|
contentX -= o.hor.ContentOffset
|
||||||
contentH = geom.Max32(0, contentH-o.barWidth)
|
contentH = geom.Max32(0, contentH-o.barWidth)
|
||||||
}
|
}
|
||||||
if ver {
|
if ver {
|
||||||
contentY -= o.ver.Offset
|
contentY -= o.ver.ContentOffset
|
||||||
contentW = geom.Max32(0, contentW-o.barWidth)
|
contentW = geom.Max32(0, contentW-o.barWidth)
|
||||||
}
|
}
|
||||||
o.Content.Arrange(ctx, geom.RectF32(contentX, contentY, contentW, contentH), offset.Add(bounds.Min))
|
o.Content.Arrange(ctx, geom.RectF32(contentX, contentY, contentW, contentH), offset.Add(bounds.Min), o)
|
||||||
if hor {
|
if hor {
|
||||||
o.hor.Content = o.desired.X
|
o.hor.ContentLength = o.desired.X
|
||||||
o.hor.Arrange(ctx, geom.RectF32(bounds.Min.X, bounds.Min.Y+contentH, bounds.Min.X+contentW, bounds.Max.Y), offset)
|
o.hor.Arrange(ctx, geom.RectF32(bounds.Min.X, bounds.Min.Y+contentH, bounds.Min.X+contentW, bounds.Max.Y), offset, o)
|
||||||
}
|
}
|
||||||
if ver {
|
if ver {
|
||||||
o.ver.Content = o.desired.Y
|
o.ver.ContentLength = o.desired.Y
|
||||||
o.ver.Arrange(ctx, geom.RectF32(bounds.Min.X+contentW, bounds.Min.Y, bounds.Max.X, bounds.Max.Y), offset)
|
o.ver.Arrange(ctx, geom.RectF32(bounds.Min.X+contentW, bounds.Min.Y, bounds.Max.X, bounds.Max.Y), offset, o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,15 +110,19 @@ func (o *overflow) Handle(ctx Context, e Event) {
|
|||||||
content.Min = contentO
|
content.Min = contentO
|
||||||
content.Max = content.Max.Add(contentO)
|
content.Max = content.Max.Add(contentO)
|
||||||
if e.MouseWheel != 0 && e.Pos().In(content) {
|
if e.MouseWheel != 0 && e.Pos().In(content) {
|
||||||
o.ver.Offset = geom.Max32(0, geom.Min32(o.ver.Content-content.Dy(), o.ver.Offset-36*e.MouseWheel))
|
o.ver.ContentOffset = geom.Max32(0, geom.Min32(o.ver.ContentLength-content.Dy(), o.ver.ContentOffset-36*e.MouseWheel))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o.Content.Handle(ctx, e)
|
o.Content.Handle(ctx, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *overflow) IsInBounds(p geom.PointF32) bool { return p.Sub(o.offset).In(o.bounds) }
|
||||||
|
|
||||||
func (o *overflow) Offset() geom.PointF32 { return o.offset }
|
func (o *overflow) Offset() geom.PointF32 { return o.offset }
|
||||||
|
|
||||||
|
func (o *overflow) Parent() Control { return o.parent }
|
||||||
|
|
||||||
func (o *overflow) Render(ctx Context) {
|
func (o *overflow) Render(ctx Context) {
|
||||||
var renderer = ctx.Renderer()
|
var renderer = ctx.Renderer()
|
||||||
|
|
||||||
|
10
ui/proxy.go
10
ui/proxy.go
@ -8,8 +8,8 @@ type Proxy struct {
|
|||||||
Content Control
|
Content Control
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proxy) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32) {
|
func (p *Proxy) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||||
p.Content.Arrange(ctx, bounds, offset)
|
p.Content.Arrange(ctx, bounds, offset, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proxy) DesiredSize(ctx Context) geom.PointF32 {
|
func (p *Proxy) DesiredSize(ctx Context) geom.PointF32 {
|
||||||
@ -28,6 +28,10 @@ func (p *Proxy) Bounds() geom.RectangleF32 {
|
|||||||
return p.Content.Bounds()
|
return p.Content.Bounds()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) IsInBounds(pt geom.PointF32) bool { return p.Content.IsInBounds(pt) }
|
||||||
|
|
||||||
|
func (p *Proxy) IsOver() bool { return p.Content.IsOver() }
|
||||||
|
|
||||||
func (p *Proxy) Offset() geom.PointF32 { return p.Content.Offset() }
|
func (p *Proxy) Offset() geom.PointF32 { return p.Content.Offset() }
|
||||||
|
|
||||||
func (p *Proxy) OnClick(fn ClickFn) {
|
func (p *Proxy) OnClick(fn ClickFn) {
|
||||||
@ -45,3 +49,5 @@ func (p *Proxy) OnDragMove(fn DragMoveFn) {
|
|||||||
func (p *Proxy) OnDragEnd(fn DragEndFn) {
|
func (p *Proxy) OnDragEnd(fn DragEndFn) {
|
||||||
p.Content.OnDragEnd(fn)
|
p.Content.OnDragEnd(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) Parent() Control { return p.Content.Parent() }
|
||||||
|
@ -9,24 +9,24 @@ type Scrollbar struct {
|
|||||||
|
|
||||||
Orientation Orientation
|
Orientation Orientation
|
||||||
|
|
||||||
Content float32
|
ContentLength float32
|
||||||
Offset float32
|
ContentOffset float32
|
||||||
|
|
||||||
handle ScrollbarHandle
|
handle ScrollbarHandle
|
||||||
startDragOffset float32
|
startDragOffset float32
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildScrollbar(o Orientation, fn func(s *Scrollbar)) *Scrollbar {
|
func BuildScrollbar(o Orientation, fn func(s *Scrollbar)) *Scrollbar {
|
||||||
var s = &Scrollbar{Orientation: o, Content: 0, Offset: 0}
|
var s = &Scrollbar{Orientation: o, ContentLength: 0, ContentOffset: 0}
|
||||||
s.handle.OnDragStart(func(_ Context, _ Control, _ geom.PointF32) {
|
s.handle.OnDragStart(func(_ Context, _ Control, _ geom.PointF32) {
|
||||||
s.startDragOffset = s.Offset
|
s.startDragOffset = s.ContentOffset
|
||||||
})
|
})
|
||||||
s.handle.OnDragMove(func(_ Context, _ Control, start, move geom.PointF32) {
|
s.handle.OnDragMove(func(_ Context, _ Control, start, move geom.PointF32) {
|
||||||
var length = s.Orientation.SizeParallel(s.bounds)
|
var length = s.Orientation.SizeParallel(s.bounds)
|
||||||
var handleMaxOffset = length - s.Orientation.SizeParallel(s.handle.bounds)
|
var handleMaxOffset = length - s.Orientation.SizeParallel(s.handle.bounds)
|
||||||
var hidden = s.Content - length
|
var hidden = s.ContentLength - length
|
||||||
var offset = (s.Orientation.LengthParallel(move) - s.Orientation.LengthParallel(start)) / handleMaxOffset
|
var offset = (s.Orientation.LengthParallel(move) - s.Orientation.LengthParallel(start)) / handleMaxOffset
|
||||||
s.Offset = geom.Max32(0, geom.Min32(s.startDragOffset+offset*hidden, hidden))
|
s.ContentOffset = geom.Max32(0, geom.Min32(s.startDragOffset+offset*hidden, hidden))
|
||||||
})
|
})
|
||||||
if fn != nil {
|
if fn != nil {
|
||||||
fn(s)
|
fn(s)
|
||||||
@ -34,8 +34,8 @@ func BuildScrollbar(o Orientation, fn func(s *Scrollbar)) *Scrollbar {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scrollbar) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32) {
|
func (s *Scrollbar) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||||
s.ControlBase.Arrange(ctx, bounds, offset)
|
s.ControlBase.Arrange(ctx, bounds, offset, parent)
|
||||||
s.updateBar(ctx)
|
s.updateBar(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ func (s *Scrollbar) Handle(ctx Context, e Event) {
|
|||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case *MouseMoveEvent:
|
case *MouseMoveEvent:
|
||||||
if e.MouseWheel != 0 && e.Pos().Sub(s.offset).In(s.bounds) {
|
if e.MouseWheel != 0 && e.Pos().Sub(s.offset).In(s.bounds) {
|
||||||
s.Offset = geom.Max32(0, geom.Min32(s.Content-s.Orientation.SizeParallel(s.bounds), s.Offset-36*e.MouseWheel))
|
s.ContentOffset = geom.Max32(0, geom.Min32(s.ContentLength-s.Orientation.SizeParallel(s.bounds), s.ContentOffset-36*e.MouseWheel))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.ControlBase.Handle(ctx, e)
|
s.ControlBase.Handle(ctx, e)
|
||||||
@ -63,17 +63,17 @@ func (s *Scrollbar) updateBar(ctx Context) {
|
|||||||
var width = ctx.Style().Dimensions.ScrollbarWidth
|
var width = ctx.Style().Dimensions.ScrollbarWidth
|
||||||
var handleLength = length
|
var handleLength = length
|
||||||
var handleOffset = s.Orientation.LengthParallel(s.bounds.Min)
|
var handleOffset = s.Orientation.LengthParallel(s.bounds.Min)
|
||||||
if s.Content > length {
|
if s.ContentLength > length {
|
||||||
handleLength = geom.Max32(2*width, length*length/s.Content)
|
handleLength = geom.Max32(2*width, length*length/s.ContentLength)
|
||||||
var hidden = s.Content - length
|
var hidden = s.ContentLength - length
|
||||||
if s.Offset > hidden {
|
if s.ContentOffset > hidden {
|
||||||
s.Offset = hidden
|
s.ContentOffset = hidden
|
||||||
}
|
}
|
||||||
var offset = geom.Min32(1, s.Offset/hidden)
|
var offset = geom.Min32(1, s.ContentOffset/hidden)
|
||||||
handleOffset = handleOffset + offset*(length-handleLength)
|
handleOffset = handleOffset + offset*(length-handleLength)
|
||||||
}
|
}
|
||||||
var min = s.Orientation.Pt(handleOffset, s.Orientation.LengthPerpendicular(s.bounds.Max)-width)
|
var min = s.Orientation.Pt(handleOffset, s.Orientation.LengthPerpendicular(s.bounds.Max)-width)
|
||||||
s.handle.Arrange(ctx, s.Orientation.Rect(min, handleLength, width), s.offset)
|
s.handle.Arrange(ctx, s.Orientation.Rect(min, handleLength, width), s.offset, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ScrollbarHandle struct {
|
type ScrollbarHandle struct {
|
||||||
|
@ -7,6 +7,8 @@ import (
|
|||||||
type Spacing struct {
|
type Spacing struct {
|
||||||
Proxy
|
Proxy
|
||||||
|
|
||||||
|
bounds geom.RectangleF32
|
||||||
|
|
||||||
Margin SideLengths
|
Margin SideLengths
|
||||||
Width *Length
|
Width *Length
|
||||||
Height *Length
|
Height *Length
|
||||||
@ -21,58 +23,55 @@ func BuildSpacing(content Control, fn func(*Spacing)) *Spacing {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func sumLengths(lengths ...float32) (float32, int) {
|
func insetMargins(bounds geom.RectangleF32, margin SideLengths, width, height float32) geom.RectangleF32 {
|
||||||
var fixed float32
|
var zero = func(f float32) float32 {
|
||||||
var infinite int
|
if geom.IsNaN32(f) {
|
||||||
for _, l := range lengths {
|
return 0
|
||||||
if geom.IsNaN32(l) {
|
|
||||||
infinite++
|
|
||||||
} else {
|
|
||||||
fixed += l
|
|
||||||
}
|
}
|
||||||
|
return f
|
||||||
}
|
}
|
||||||
return fixed, infinite
|
var nan = func(f float32) int {
|
||||||
|
if geom.IsNaN32(f) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var partial = func(f, p float32) float32 {
|
||||||
|
if geom.IsNaN32(f) {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
var inset = func(min, max, marginMin, marginMax, length float32) (float32, float32) {
|
||||||
|
var available = max - min
|
||||||
|
var margins = zero(marginMin) + zero(marginMax)
|
||||||
|
if margins > available {
|
||||||
|
return min + zero(marginMin)*available/margins, max - zero(marginMax)*available/margins
|
||||||
|
}
|
||||||
|
var fixed = margins + zero(length)
|
||||||
|
if fixed > available {
|
||||||
|
return min + zero(marginMin), max - zero(marginMax)
|
||||||
|
}
|
||||||
|
var nans = nan(marginMin) + nan(length) + nan(marginMax)
|
||||||
|
var part float32 = 0
|
||||||
|
if nans > 0 {
|
||||||
|
part = (available - fixed) / float32(nans)
|
||||||
|
}
|
||||||
|
return min + partial(marginMin, part), max - partial(marginMax, part)
|
||||||
|
}
|
||||||
|
bounds.Min.X, bounds.Max.X = inset(bounds.Min.X, bounds.Max.X, margin.Left.Value(), margin.Right.Value(), width)
|
||||||
|
bounds.Min.Y, bounds.Max.Y = inset(bounds.Min.Y, bounds.Max.Y, margin.Top.Value(), margin.Bottom.Value(), height)
|
||||||
|
return bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
func addMargin(bounds geom.PointF32, size geom.PointF32, margin SideLengths) geom.RectangleF32 {
|
func (s *Spacing) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||||
var left = margin.Left.Value()
|
size := s.DesiredSize(ctx)
|
||||||
var top = margin.Top.Value()
|
content := insetMargins(bounds, s.Margin, s.Width.Zero(size.X), s.Height.Zero(size.Y))
|
||||||
var w, hor = sumLengths(left, size.X, margin.Right.Value())
|
s.bounds = bounds
|
||||||
var h, ver = sumLengths(top, size.Y, margin.Bottom.Value())
|
s.Proxy.Arrange(ctx, content, offset, parent)
|
||||||
var rem = geom.PtF32(geom.Max32(0, bounds.X-w), geom.Max32(0, bounds.Y-h))
|
|
||||||
if geom.IsNaN32(left) {
|
|
||||||
left = rem.X / float32(hor)
|
|
||||||
} else if rem.X == 0 {
|
|
||||||
left = (left * bounds.X) / w
|
|
||||||
}
|
|
||||||
if geom.IsNaN32(size.X) {
|
|
||||||
size.X = rem.X / float32(hor)
|
|
||||||
} else if rem.X == 0 {
|
|
||||||
size.X = (size.X * bounds.X) / w
|
|
||||||
} else if hor == 0 { // nothing stretches
|
|
||||||
size.X += rem.X
|
|
||||||
}
|
|
||||||
if geom.IsNaN32(top) {
|
|
||||||
top = rem.Y / float32(ver)
|
|
||||||
} else if rem.Y == 0 {
|
|
||||||
top = (top * bounds.Y) / h
|
|
||||||
}
|
|
||||||
if geom.IsNaN32(size.Y) {
|
|
||||||
size.Y = rem.Y / float32(ver)
|
|
||||||
} else if rem.Y == 0 {
|
|
||||||
size.Y = (size.Y * bounds.Y) / h
|
|
||||||
} else if ver == 0 { // nothing stretches
|
|
||||||
size.Y += rem.Y
|
|
||||||
}
|
|
||||||
return geom.RectF32(left, top, left+size.X, top+size.Y)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Spacing) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32) {
|
func (s *Spacing) Bounds() geom.RectangleF32 { return s.bounds }
|
||||||
var size = s.Proxy.DesiredSize(ctx)
|
|
||||||
var w, h = s.Width.Zero(size.X), s.Height.Zero(size.Y)
|
|
||||||
var content = addMargin(geom.PtF32(bounds.Dx(), bounds.Dy()), geom.PtF32(w, h), s.Margin).Add(bounds.Min)
|
|
||||||
s.Proxy.Arrange(ctx, content, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Spacing) Center() {
|
func (s *Spacing) Center() {
|
||||||
s.Margin.Left = Infinite()
|
s.Margin.Left = Infinite()
|
||||||
|
@ -27,7 +27,7 @@ func TestNoStretchFillsAvailableSpace(t *testing.T) {
|
|||||||
s := BuildSpacing(m, func(s *Spacing) {
|
s := BuildSpacing(m, func(s *Spacing) {
|
||||||
s.SetSize(0, 0)
|
s.SetSize(0, 0)
|
||||||
})
|
})
|
||||||
s.Arrange(nil, geom.RectF32(31, 37, 73, 79), geom.ZeroPtF32)
|
s.Arrange(nil, geom.RectF32(31, 37, 73, 79), geom.ZeroPtF32, nil)
|
||||||
assert.Equal(t, geom.RectF32(31, 37, 73, 79), m.bounds)
|
assert.Equal(t, geom.RectF32(31, 37, 73, 79), m.bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ func TestStretch(t *testing.T) {
|
|||||||
s := BuildSpacing(m, func(s *Spacing) {
|
s := BuildSpacing(m, func(s *Spacing) {
|
||||||
s.SetSize(geom.NaN32(), geom.NaN32())
|
s.SetSize(geom.NaN32(), geom.NaN32())
|
||||||
})
|
})
|
||||||
s.Arrange(nil, geom.RectF32(31, 37, 73, 79), geom.ZeroPtF32)
|
s.Arrange(nil, geom.RectF32(31, 37, 73, 79), geom.ZeroPtF32, nil)
|
||||||
assert.Equal(t, geom.RectF32(31, 37, 73, 79), m.bounds)
|
assert.Equal(t, geom.RectF32(31, 37, 73, 79), m.bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ func TestCenter(t *testing.T) {
|
|||||||
s := BuildSpacing(m, func(s *Spacing) {
|
s := BuildSpacing(m, func(s *Spacing) {
|
||||||
s.Center()
|
s.Center()
|
||||||
})
|
})
|
||||||
s.Arrange(nil, geom.RectF32(31, 37, 73, 79), geom.ZeroPtF32)
|
s.Arrange(nil, geom.RectF32(31, 37, 73, 79), geom.ZeroPtF32, nil)
|
||||||
assert.Equal(t, geom.RectF32(51, 56.5, 53, 59.5), m.bounds)
|
assert.Equal(t, geom.RectF32(51, 56.5, 53, 59.5), m.bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +57,23 @@ func TestFixedMargin(t *testing.T) {
|
|||||||
s.Margin.Right = Fixed(3)
|
s.Margin.Right = Fixed(3)
|
||||||
s.Margin.Bottom = Fixed(5)
|
s.Margin.Bottom = Fixed(5)
|
||||||
})
|
})
|
||||||
s.Arrange(nil, geom.RectF32(31, 37, 73, 79), geom.ZeroPtF32)
|
s.Arrange(nil, geom.RectF32(31, 37, 73, 79), geom.ZeroPtF32, nil)
|
||||||
assert.Equal(t, geom.RectF32(38, 39, 70, 74), m.bounds)
|
assert.Equal(t, geom.RectF32(38, 39, 70, 74), m.bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRightAlign(t *testing.T) {
|
||||||
|
m := &Mock{Size: &geom.PointF32{X: 2, Y: 3}}
|
||||||
|
s := Margins(m, geom.NaN32(), 0, 0, 0)
|
||||||
|
s.Arrange(nil, geom.RectF32(31, 37, 73, 79), geom.ZeroPtF32, nil)
|
||||||
|
assert.Equal(t, geom.RectF32(71, 37, 73, 79), m.bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRightAlignFixedWidth(t *testing.T) {
|
||||||
|
m := &Mock{Size: &geom.PointF32{X: 2, Y: 3}}
|
||||||
|
s := BuildSpacing(m, func(s *Spacing) {
|
||||||
|
s.Margin.Left = Infinite()
|
||||||
|
s.Width = Fixed(5)
|
||||||
|
})
|
||||||
|
s.Arrange(nil, geom.RectF32(31, 37, 73, 79), geom.ZeroPtF32, nil)
|
||||||
|
assert.Equal(t, geom.RectF32(68, 37, 73, 79), m.bounds)
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import "opslag.de/schobers/geom"
|
import (
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
type StackPanel struct {
|
type StackPanel struct {
|
||||||
ContainerBase
|
ContainerBase
|
||||||
@ -15,7 +17,7 @@ func BuildStackPanel(o Orientation, fn func(*StackPanel)) *StackPanel {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *StackPanel) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32) {
|
func (p *StackPanel) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||||
bounds = p.Orientation.FlipRect(bounds)
|
bounds = p.Orientation.FlipRect(bounds)
|
||||||
var length float32
|
var length float32
|
||||||
var stretch int
|
var stretch int
|
||||||
@ -41,10 +43,10 @@ func (p *StackPanel) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var child = geom.RectF32(bounds.Min.X, bounds.Min.Y+childOffset, bounds.Max.X, bounds.Min.Y+childOffset+height)
|
var child = geom.RectF32(bounds.Min.X, bounds.Min.Y+childOffset, bounds.Max.X, bounds.Min.Y+childOffset+height)
|
||||||
p.Children[i].Arrange(ctx, p.Orientation.FlipRect(child), offset)
|
p.Children[i].Arrange(ctx, p.Orientation.FlipRect(child), offset, p)
|
||||||
childOffset += height
|
childOffset += height
|
||||||
}
|
}
|
||||||
p.ControlBase.Arrange(ctx, p.Orientation.FlipRect(bounds), offset)
|
p.ControlBase.Arrange(ctx, p.Orientation.FlipRect(bounds), offset, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *StackPanel) DesiredSize(ctx Context) geom.PointF32 {
|
func (p *StackPanel) DesiredSize(ctx Context) geom.PointF32 {
|
||||||
|
@ -52,9 +52,9 @@ func (b *TextBox) pad(ctx Context) float32 {
|
|||||||
return ctx.Style().Dimensions.TextPadding
|
return ctx.Style().Dimensions.TextPadding
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *TextBox) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32) {
|
func (b *TextBox) Arrange(ctx Context, bounds geom.RectangleF32, offset geom.PointF32, parent Control) {
|
||||||
b.ControlBase.Arrange(ctx, bounds, offset)
|
b.ControlBase.Arrange(ctx, bounds, offset, parent)
|
||||||
b.box.Arrange(ctx, bounds.Inset(b.pad(ctx)), offset)
|
b.box.Arrange(ctx, bounds.Inset(b.pad(ctx)), offset, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *TextBox) DesiredSize(ctx Context) geom.PointF32 {
|
func (b *TextBox) DesiredSize(ctx Context) geom.PointF32 {
|
||||||
@ -67,7 +67,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.mousePos(e)
|
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.Renderer().Font(b.FontName(ctx))
|
||||||
var carets = [3]int{0, 0, len(b.Text)}
|
var carets = [3]int{0, 0, len(b.Text)}
|
||||||
|
2
ui/ui.go
2
ui/ui.go
@ -31,7 +31,7 @@ func RunWait(r Renderer, s *Style, view Control, wait bool) error {
|
|||||||
for !ctx.quit {
|
for !ctx.quit {
|
||||||
var size = r.Size()
|
var size = r.Size()
|
||||||
var bounds = geom.RectF32(0, 0, size.X, size.Y)
|
var bounds = geom.RectF32(0, 0, size.X, size.Y)
|
||||||
view.Arrange(ctx, bounds, geom.ZeroPtF32)
|
view.Arrange(ctx, bounds, geom.ZeroPtF32, nil)
|
||||||
view.Render(ctx)
|
view.Render(ctx)
|
||||||
if ctx.quit {
|
if ctx.quit {
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
Reference in New Issue
Block a user