diff --git a/elements/basic/button.go b/elements/basic/button.go index bb254de..06c7edd 100644 --- a/elements/basic/button.go +++ b/elements/basic/button.go @@ -14,10 +14,13 @@ type Button struct { pressed bool enabled bool selected bool - onClick func () text string drawer artist.TextDrawer + + onClick func () + onSelectionRequest func () (granted bool) + onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool) } // NewButton creates a new button with the specified label text. @@ -41,7 +44,7 @@ func (element *Button) HandleMouseDown (x, y int, button tomo.Button) { element.pressed = true if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -50,7 +53,7 @@ func (element *Button) HandleMouseUp (x, y int, button tomo.Button) { element.pressed = false if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } within := image.Point { x, y }. @@ -63,7 +66,7 @@ func (element *Button) HandleMouseUp (x, y int, button tomo.Button) { } func (element *Button) HandleMouseMove (x, y int) { } -func (element *Button) HandleScroll (x, y int, deltaX, deltaY float64) { } +func (element *Button) HandleMouseScroll (x, y int, deltaX, deltaY float64) { } func (element *Button) HandleKeyDown ( key tomo.Key, @@ -75,7 +78,7 @@ func (element *Button) HandleKeyDown ( element.pressed = true if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } } } @@ -85,7 +88,7 @@ func (element *Button) HandleKeyUp(key tomo.Key, modifiers tomo.Modifiers) { element.pressed = false if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } if !element.enabled { return } if element.onClick != nil { @@ -100,7 +103,9 @@ func (element *Button) Selected () (selected bool) { func (element *Button) Select () { if !element.enabled { return } - element.core.RequestSelection() + if element.onSelectionRequest != nil { + element.onSelectionRequest() + } } func (element *Button) HandleSelection ( @@ -117,7 +122,7 @@ func (element *Button) HandleSelection ( element.selected = true if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } return true } @@ -126,10 +131,20 @@ func (element *Button) HandleDeselection () { element.selected = false if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } } +func (element *Button) OnSelectionRequest (callback func () (granted bool)) { + element.onSelectionRequest = callback +} + +func (element *Button) OnSelectionMotionRequest ( + callback func (direction tomo.SelectionDirection) (granted bool), +) { + element.onSelectionMotionRequest = callback +} + // OnClick sets the function to be called when the button is clicked. func (element *Button) OnClick (callback func ()) { element.onClick = callback @@ -141,7 +156,7 @@ func (element *Button) SetEnabled (enabled bool) { element.enabled = enabled if element.core.HasImage () { element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -157,7 +172,7 @@ func (element *Button) SetText (text string) { theme.Padding() * 2 + textBounds.Dy()) if element.core.HasImage () { element.draw() - element.core.PushAll() + element.core.DamageAll() } } diff --git a/elements/basic/checkbox.go b/elements/basic/checkbox.go index 8c9422a..27f85d1 100644 --- a/elements/basic/checkbox.go +++ b/elements/basic/checkbox.go @@ -15,10 +15,13 @@ type Checkbox struct { checked bool enabled bool selected bool - onClick func () text string drawer artist.TextDrawer + + onClick func () + onSelectionRequest func () (granted bool) + onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool) } // NewCheckbox creates a new cbeckbox with the specified label text. @@ -41,7 +44,7 @@ func (element *Checkbox) HandleMouseDown (x, y int, button tomo.Button) { element.pressed = true if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -57,7 +60,7 @@ func (element *Checkbox) HandleMouseUp (x, y int, button tomo.Button) { if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } if within && element.onClick != nil { element.onClick() @@ -76,7 +79,7 @@ func (element *Checkbox) HandleKeyDown ( element.pressed = true if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } } } @@ -87,7 +90,7 @@ func (element *Checkbox) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) { element.checked = !element.checked if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } if element.onClick != nil { element.onClick() @@ -102,7 +105,10 @@ func (element *Checkbox) Selected () (selected bool) { // Select requests that this element be selected. func (element *Checkbox) Select () { - element.core.RequestSelection() + if !element.enabled { return } + if element.onSelectionRequest != nil { + element.onSelectionRequest() + } } func (element *Checkbox) HandleSelection ( @@ -119,7 +125,7 @@ func (element *Checkbox) HandleSelection ( element.selected = true if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } return true } @@ -128,10 +134,20 @@ func (element *Checkbox) HandleDeselection () { element.selected = false if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } } +func (element *Checkbox) OnSelectionRequest (callback func () (granted bool)) { + element.onSelectionRequest = callback +} + +func (element *Checkbox) OnSelectionMotionRequest ( + callback func (direction tomo.SelectionDirection) (granted bool), +) { + element.onSelectionMotionRequest = callback +} + // OnClick sets the function to be called when the checkbox is toggled. func (element *Checkbox) OnClick (callback func ()) { element.onClick = callback @@ -148,7 +164,7 @@ func (element *Checkbox) SetEnabled (enabled bool) { element.enabled = enabled if element.core.HasImage () { element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -164,7 +180,7 @@ func (element *Checkbox) SetText (text string) { textBounds.Dy()) if element.core.HasImage () { element.draw() - element.core.PushAll() + element.core.DamageAll() } } diff --git a/elements/basic/container.go b/elements/basic/container.go index 1e4e785..7770ba8 100644 --- a/elements/basic/container.go +++ b/elements/basic/container.go @@ -19,6 +19,10 @@ type Container struct { selected bool selectable bool flexible bool + + onSelectionRequest func () (granted bool) + onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool) + onFlexibleHeightChange func () } // NewContainer creates a new container. @@ -35,7 +39,7 @@ func (element *Container) SetLayout (layout tomo.Layout) { if element.core.HasImage() { element.recalculate() element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -43,38 +47,40 @@ func (element *Container) SetLayout (layout tomo.Layout) { // the element will expand (instead of contract to its minimum size), in // whatever way is defined by the current layout. func (element *Container) Adopt (child tomo.Element, expand bool) { - child.SetParentHooks (tomo.ParentHooks { - Draw: func (region tomo.Canvas) { - element.drawChildRegion(child, region) - }, - MinimumSizeChange: func (int, int) { - element.updateMinimumSize() - }, - FlexibleHeightChange: element.updateMinimumSize, - SelectionRequest: func () (granted bool) { - child, selectable := child.(tomo.Selectable) - if !selectable { return } - return element.childSelectionRequestCallback(child) - }, - SelectionMotionRequest: func ( - direction tomo.SelectionDirection, - ) ( - granted bool, - ) { - return element.core.RequestSelectionMotion(direction) - }, + // set event handlers + child.OnDamage (func (region tomo.Canvas) { + element.drawChildRegion(child, region) }) + child.OnMinimumSizeChange(element.updateMinimumSize) + if child0, ok := child.(tomo.Flexible); ok { + child0.OnFlexibleHeightChange(element.updateMinimumSize) + } + if child0, ok := child.(tomo.Selectable); ok { + child0.OnSelectionRequest (func () (granted bool) { + return element.childSelectionRequestCallback(child0) + }) + } + if child0, ok := child.(tomo.Selectable); ok { + child0.OnSelectionMotionRequest ( + func (direction tomo.SelectionDirection) (granted bool) { + if element.onSelectionMotionRequest == nil { return } + return element.onSelectionMotionRequest(direction) + }) + } + + // add child element.children = append (element.children, tomo.LayoutEntry { Element: child, Expand: expand, }) + // refresh stale data element.updateMinimumSize() element.reflectChildProperties() if element.core.HasImage() && !element.warping { element.recalculate() element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -97,7 +103,7 @@ func (element *Container) Warp (callback func ()) { if element.core.HasImage() { element.recalculate() element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -106,7 +112,7 @@ func (element *Container) Warp (callback func ()) { func (element *Container) Disown (child tomo.Element) { for index, entry := range element.children { if entry.Element == child { - entry.SetParentHooks(tomo.ParentHooks { }) + element.clearChildEventHandlers(entry.Element) element.children = append ( element.children[:index], element.children[index + 1:]...) @@ -119,7 +125,22 @@ func (element *Container) Disown (child tomo.Element) { if element.core.HasImage() && !element.warping { element.recalculate() element.draw() - element.core.PushAll() + element.core.DamageAll() + } +} + +func (element *Container) clearChildEventHandlers (child tomo.Element) { + child.OnDamage(nil) + child.OnMinimumSizeChange(nil) + if child0, ok := child.(tomo.Selectable); ok { + child0.OnSelectionRequest(nil) + child0.OnSelectionMotionRequest(nil) + if child0.Selected() { + child0.HandleDeselection() + } + } + if child0, ok := child.(tomo.Flexible); ok { + child0.OnFlexibleHeightChange(nil) } } @@ -132,7 +153,7 @@ func (element *Container) DisownAll () { if element.core.HasImage() && !element.warping { element.recalculate() element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -213,7 +234,7 @@ func (element *Container) HandleScroll (x, y int, deltaX, deltaY float64) { child, handlesMouse := element.ChildAt(image.Pt(x, y)).(tomo.MouseTarget) if !handlesMouse { return } childPosition := element.childPosition(child) - child.HandleScroll(x - childPosition.X, y - childPosition.Y, deltaX, deltaY) + child.HandleMouseScroll(x - childPosition.X, y - childPosition.Y, deltaX, deltaY) } func (element *Container) HandleKeyDown ( @@ -245,7 +266,9 @@ func (element *Container) Selected () (selected bool) { } func (element *Container) Select () { - element.core.RequestSelection() + if element.onSelectionRequest != nil { + element.onSelectionRequest() + } } func (element *Container) HandleSelection (direction tomo.SelectionDirection) (ok bool) { @@ -300,10 +323,14 @@ func (element *Container) HandleSelection (direction tomo.SelectionDirection) (o return false } -func (element *Container) MinimumHeightFor (width int) (height int) { +func (element *Container) FlexibleHeightFor (width int) (height int) { return element.layout.MinimumHeightFor(element.children, width) } +func (element *Container) OnFlexibleHeightChange (callback func ()) { + element.onFlexibleHeightChange = callback +} + func (element *Container) HandleDeselection () { element.selected = false element.forSelected (func (child tomo.Selectable) bool { @@ -312,6 +339,16 @@ func (element *Container) HandleDeselection () { }) } +func (element *Container) OnSelectionRequest (callback func () (granted bool)) { + element.onSelectionRequest = callback +} + +func (element *Container) OnSelectionMotionRequest ( + callback func (direction tomo.SelectionDirection) (granted bool), +) { + element.onSelectionMotionRequest = callback +} + func (element *Container) forSelected (callback func (child tomo.Selectable) bool) { for _, entry := range element.children { child, selectable := entry.Element.(tomo.Selectable) @@ -379,7 +416,7 @@ func (element *Container) childSelectionRequestCallback ( ) ( granted bool, ) { - if element.core.RequestSelection() { + if element.onSelectionRequest != nil && element.onSelectionRequest() { element.forSelected (func (child tomo.Selectable) bool { child.HandleDeselection() return true @@ -422,7 +459,7 @@ func (element *Container) drawChildRegion (child tomo.Element, region tomo.Canva for _, entry := range element.children { if entry.Element == child { artist.Paste(element.core, region, entry.Position) - element.core.PushRegion ( + element.core.DamageRegion ( region.Bounds().Add(entry.Position)) break } diff --git a/elements/basic/label.go b/elements/basic/label.go index 65ce585..7c2e2f0 100644 --- a/elements/basic/label.go +++ b/elements/basic/label.go @@ -13,6 +13,8 @@ type Label struct { wrap bool text string drawer artist.TextDrawer + + onFlexibleHeightChange func () } // NewLabel creates a new label. If wrap is set to true, the text inside will be @@ -38,9 +40,9 @@ func (element *Label) Resize (width, height int) { return } -// MinimumHeightFor returns the reccomended height for this element based on the -// given width in order to allow the text to wrap properly. -func (element *Label) MinimumHeightFor (width int) (height int) { +// FlexibleHeightFor returns the reccomended height for this element based on +// the given width in order to allow the text to wrap properly. +func (element *Label) FlexibleHeightFor (width int) (height int) { if element.wrap { return element.drawer.ReccomendedHeightFor(width) } else { @@ -49,6 +51,12 @@ func (element *Label) MinimumHeightFor (width int) (height int) { } } +// OnFlexibleHeightChange sets a function to be called when the parameters +// affecting this element's flexible height are changed. +func (element *Label) OnFlexibleHeightChange (callback func ()) { + element.onFlexibleHeightChange = callback +} + // SetText sets the label's text. func (element *Label) SetText (text string) { if element.text == text { return } @@ -59,7 +67,7 @@ func (element *Label) SetText (text string) { if element.core.HasImage () { element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -78,7 +86,7 @@ func (element *Label) SetWrap (wrap bool) { if element.core.HasImage () { element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -88,7 +96,9 @@ func (element *Label) updateMinimumSize () { if em < 1 { em = theme.Padding() } element.core.SetMinimumSize ( em, element.drawer.LineHeight().Round()) - element.core.NotifyFlexibleHeightChange() + if element.onFlexibleHeightChange != nil { + element.onFlexibleHeightChange() + } } else { bounds := element.drawer.LayoutBounds() element.core.SetMinimumSize(bounds.Dx(), bounds.Dy()) diff --git a/elements/basic/progressbar.go b/elements/basic/progressbar.go index 6ac9778..4bef1b0 100644 --- a/elements/basic/progressbar.go +++ b/elements/basic/progressbar.go @@ -34,7 +34,7 @@ func (element *ProgressBar) SetProgress (progress float64) { element.progress = progress if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } } diff --git a/elements/basic/scrollcontainer.go b/elements/basic/scrollcontainer.go index a48adab..b3c8b81 100644 --- a/elements/basic/scrollcontainer.go +++ b/elements/basic/scrollcontainer.go @@ -23,6 +23,8 @@ type ScrollContainer struct { enabled bool bounds image.Rectangle } + + // TODO event handlers } // NewScrollContainer creates a new scroll container with the specified scroll @@ -52,23 +54,18 @@ func (element *ScrollContainer) Resize (width, height int) { func (element *ScrollContainer) Adopt (child tomo.Scrollable) { // disown previous child if it exists if element.child != nil { - element.child.SetParentHooks (tomo.ParentHooks { }) - if previousChild, ok := element.child.(tomo.Selectable); ok { - if previousChild.Selected() { - previousChild.HandleDeselection() - } - } + element.clearChildEventHandlers(child) } // adopt new child element.child = child if child != nil { - child.SetParentHooks (tomo.ParentHooks { + // child.SetParentHooks (tomo.ParentHooks { // Draw: window.childDrawCallback, // MinimumSizeChange: window.childMinimumSizeChangeCallback, // FlexibleHeightChange: window.resizeChildToFit, // SelectionRequest: window.childSelectionRequestCallback, - }) + // }) // TODO: somehow inform the core that we do not in fact want to // redraw the element. @@ -84,6 +81,21 @@ func (element *ScrollContainer) Adopt (child tomo.Scrollable) { } } +func (element *ScrollContainer) clearChildEventHandlers (child tomo.Element) { + child.OnDamage(nil) + child.OnMinimumSizeChange(nil) + if child0, ok := child.(tomo.Selectable); ok { + child0.OnSelectionRequest(nil) + child0.OnSelectionMotionRequest(nil) + if child0.Selected() { + child0.HandleDeselection() + } + } + if child0, ok := child.(tomo.Flexible); ok { + child0.OnFlexibleHeightChange(nil) + } +} + func (element *ScrollContainer) recalculate () { horizontal := &element.horizontal vertical := &element.vertical diff --git a/elements/basic/spacer.go b/elements/basic/spacer.go index 8456088..7cf7844 100644 --- a/elements/basic/spacer.go +++ b/elements/basic/spacer.go @@ -34,7 +34,7 @@ func (element *Spacer) SetLine (line bool) { element.line = line if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } } diff --git a/elements/basic/textbox.go b/elements/basic/textbox.go index 722dc76..7ba4d0f 100644 --- a/elements/basic/textbox.go +++ b/elements/basic/textbox.go @@ -25,6 +25,9 @@ type TextBox struct { onKeyDown func (tomo.Key, tomo.Modifiers, bool) (bool) onChange func () + onSelectionRequest func () (granted bool) + onSelectionMotionRequest func (tomo.SelectionDirection) (granted bool) + onScrollBoundsChange func () } // NewTextBox creates a new text box with the specified placeholder text, and @@ -119,7 +122,7 @@ func (element *TextBox) HandleKeyDown ( if altered && element.core.HasImage () { element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -130,7 +133,9 @@ func (element *TextBox) Selected () (selected bool) { } func (element *TextBox) Select () { - element.core.RequestSelection() + if element.onSelectionRequest != nil { + element.onSelectionRequest() + } } func (element *TextBox) HandleSelection ( @@ -147,7 +152,7 @@ func (element *TextBox) HandleSelection ( element.selected = true if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } return true } @@ -156,16 +161,26 @@ func (element *TextBox) HandleDeselection () { element.selected = false if element.core.HasImage() { element.draw() - element.core.PushAll() + element.core.DamageAll() } } +func (element *TextBox) OnSelectionRequest (callback func () (granted bool)) { + element.onSelectionRequest = callback +} + +func (element *TextBox) OnSelectionMotionRequest ( + callback func (direction tomo.SelectionDirection) (granted bool), +) { + element.onSelectionMotionRequest = callback +} + func (element *TextBox) SetEnabled (enabled bool) { if element.enabled == enabled { return } element.enabled = enabled if element.core.HasImage () { element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -178,7 +193,7 @@ func (element *TextBox) SetPlaceholder (placeholder string) { element.updateMinimumSize() if element.core.HasImage () { element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -195,7 +210,7 @@ func (element *TextBox) SetValue (text string) { if element.core.HasImage () { element.draw() - element.core.PushAll() + element.core.DamageAll() } } @@ -246,9 +261,11 @@ func (element *TextBox) ScrollTo (position image.Point) { if element.core.HasImage () { element.draw() - element.core.PushAll() + element.core.DamageAll() + } + if element.onScrollBoundsChange != nil { + element.onScrollBoundsChange() } - element.core.NotifyContentBoundsChange() } // ScrollAxes returns the supported axes for scrolling. @@ -286,7 +303,9 @@ func (element *TextBox) scrollToCursor () { element.scroll -= minX - cursorPosition.X if element.scroll < 0 { element.scroll = 0 } } - element.core.NotifyContentBoundsChange() + if element.onScrollBoundsChange != nil { + element.onScrollBoundsChange() + } } func (element *TextBox) draw () {