From 1f44a01230215e6e3fb31fe73195f1f090b72403 Mon Sep 17 00:00:00 2001 From: Leandro Dorileo Date: Fri, 23 Mar 2018 14:09:48 -0700 Subject: [PATCH 1/2] BaseControl: add refID In order to guarantee BaseControl and Control are fully comparable in the BaseControl level this patch introduces a refID interface, so the objects can safely be comparable based on this interface. --- barchart.go | 1 + base_control.go | 18 ++++++++++++++++++ button.go | 1 + checkbox.go | 1 + control_intf.go | 2 ++ edit_osx.go | 1 + edit_other.go | 1 + frame.go | 1 + label.go | 1 + listbox.go | 1 + progressbar.go | 1 + radio.go | 1 + sparkchart.go | 1 + tableview.go | 1 + textreader.go | 11 ++++++----- textview.go | 1 + window.go | 2 ++ 17 files changed, 41 insertions(+), 5 deletions(-) diff --git a/barchart.go b/barchart.go index b04e3fa..d6933ac 100644 --- a/barchart.go +++ b/barchart.go @@ -79,6 +79,7 @@ control should keep its original size. */ func CreateBarChart(parent Control, w, h int, scale int) *BarChart { c := new(BarChart) + c.BaseControl = NewBaseControl() if w == AutoSize { w = 10 diff --git a/base_control.go b/base_control.go index 719e817..6b5b930 100644 --- a/base_control.go +++ b/base_control.go @@ -3,12 +3,14 @@ package clui import ( term "github.com/nsf/termbox-go" "sync" + "sync/atomic" ) // BaseControl is a base for all visible controls. // Every new control must inherit it or implement // the same set of methods type BaseControl struct { + refID int64 title string x, y int width, height int @@ -31,6 +33,22 @@ type BaseControl struct { mtx sync.RWMutex } +var ( + globalRefId int64 +) + +func nextRefId() int64 { + return atomic.AddInt64(&globalRefId, 1) +} + +func NewBaseControl() BaseControl { + return BaseControl{refID: nextRefId()} +} + +func (c *BaseControl) RefID() int64 { + return c.refID +} + func (c *BaseControl) Title() string { return c.title } diff --git a/button.go b/button.go index dbae036..48e7c25 100644 --- a/button.go +++ b/button.go @@ -31,6 +31,7 @@ control should keep its original size. */ func CreateButton(parent Control, width, height int, title string, scale int) *Button { b := new(Button) + b.BaseControl = NewBaseControl() b.parent = parent b.align = AlignCenter diff --git a/checkbox.go b/checkbox.go index 4f4c7e2..828e4e3 100644 --- a/checkbox.go +++ b/checkbox.go @@ -28,6 +28,7 @@ CheckBox state can be changed using mouse or pressing space on keyboard while th */ func CreateCheckBox(parent Control, width int, title string, scale int) *CheckBox { c := new(CheckBox) + c.BaseControl = NewBaseControl() c.parent = parent if width == AutoSize { diff --git a/control_intf.go b/control_intf.go index 96125d3..f041689 100644 --- a/control_intf.go +++ b/control_intf.go @@ -147,4 +147,6 @@ type Control interface { // that the control do not want or cannot process the event and the caller sends // the event to the control parent ProcessEvent(ev Event) bool + // RefID returns the controls internal reference id + RefID() int64 } diff --git a/edit_osx.go b/edit_osx.go index cedeb4a..4ae0e32 100644 --- a/edit_osx.go +++ b/edit_osx.go @@ -44,6 +44,7 @@ type EditField struct { // control should keep its original size. func CreateEditField(parent Control, width int, text string, scale int) *EditField { e := new(EditField) + e.BaseControl = NewBaseControl() e.onChange = nil e.SetTitle(text) e.SetEnabled(true) diff --git a/edit_other.go b/edit_other.go index bd0b4ed..6c2276c 100644 --- a/edit_other.go +++ b/edit_other.go @@ -40,6 +40,7 @@ type EditField struct { // control should keep its original size. func CreateEditField(parent Control, width int, text string, scale int) *EditField { e := new(EditField) + e.BaseControl = NewBaseControl() e.onChange = nil e.SetTitle(text) e.SetEnabled(true) diff --git a/frame.go b/frame.go index 6f13964..76d7b38 100644 --- a/frame.go +++ b/frame.go @@ -28,6 +28,7 @@ control should keep its original size. */ func CreateFrame(parent Control, width, height int, bs BorderStyle, scale int) *Frame { f := new(Frame) + f.BaseControl = NewBaseControl() if width == AutoSize { width = 5 diff --git a/label.go b/label.go index ee05496..d5794a2 100644 --- a/label.go +++ b/label.go @@ -29,6 +29,7 @@ control should keep its original size. */ func CreateLabel(parent Control, w, h int, title string, scale int) *Label { c := new(Label) + c.BaseControl = NewBaseControl() if w == AutoSize { w = xs.Len(title) diff --git a/listbox.go b/listbox.go index 57eaaa6..06c9a58 100644 --- a/listbox.go +++ b/listbox.go @@ -38,6 +38,7 @@ control should keep its original size. */ func CreateListBox(parent Control, width, height int, scale int) *ListBox { l := new(ListBox) + l.BaseControl = NewBaseControl() if height == AutoSize { height = 3 diff --git a/progressbar.go b/progressbar.go index b1e7632..823ab3d 100644 --- a/progressbar.go +++ b/progressbar.go @@ -32,6 +32,7 @@ control should keep its original size. */ func CreateProgressBar(parent Control, width, height int, scale int) *ProgressBar { b := new(ProgressBar) + b.BaseControl = NewBaseControl() if height == AutoSize { height = 1 diff --git a/radio.go b/radio.go index 1333d33..32fed29 100644 --- a/radio.go +++ b/radio.go @@ -26,6 +26,7 @@ control should keep its original size. */ func CreateRadio(parent Control, width int, title string, scale int) *Radio { c := new(Radio) + c.BaseControl = NewBaseControl() if width == AutoSize { width = xs.Len(title) + 4 diff --git a/sparkchart.go b/sparkchart.go index 0ee051e..4887039 100644 --- a/sparkchart.go +++ b/sparkchart.go @@ -45,6 +45,7 @@ control should keep its original size. */ func CreateSparkChart(parent Control, w, h int, scale int) *SparkChart { c := new(SparkChart) + c.BaseControl = NewBaseControl() if w == AutoSize { w = 10 diff --git a/tableview.go b/tableview.go index 2e68e49..f75e603 100644 --- a/tableview.go +++ b/tableview.go @@ -127,6 +127,7 @@ control should keep its original size. */ func CreateTableView(parent Control, width, height int, scale int) *TableView { l := new(TableView) + l.BaseControl = NewBaseControl() if height == AutoSize { height = 3 diff --git a/textreader.go b/textreader.go index 33b01f0..e01ad32 100644 --- a/textreader.go +++ b/textreader.go @@ -17,6 +17,7 @@ type TextReader struct { func CreateTextReader(parent Control, width, height int, scale int) *TextReader { l := new(TextReader) + l.BaseControl = NewBaseControl() if height == AutoSize { height = 10 @@ -260,10 +261,10 @@ func (l *TextReader) TopLine() int { func (l *TextReader) SetTopLine(top int) { if top < l.lineCount { - l.topLine = top + l.topLine = top - if l.onPositionChanged != nil { - l.onPositionChanged(l.topLine, l.lineCount) - } - } + if l.onPositionChanged != nil { + l.onPositionChanged(l.topLine, l.lineCount) + } + } } diff --git a/textview.go b/textview.go index 78be754..f4bd52c 100644 --- a/textview.go +++ b/textview.go @@ -46,6 +46,7 @@ control should keep its original size. */ func CreateTextView(parent Control, width, height int, scale int) *TextView { l := new(TextView) + l.BaseControl = NewBaseControl() if height == AutoSize { height = 3 diff --git a/window.go b/window.go index 74660f2..9a2f750 100644 --- a/window.go +++ b/window.go @@ -27,6 +27,8 @@ type Window struct { func CreateWindow(x, y, w, h int, title string) *Window { wnd := new(Window) + wnd.BaseControl = NewBaseControl() + if w == AutoSize || w < 1 || w > 1000 { w = 10 } From be1a2b7283dc3a60a36fd831150cb9ba4d926482 Mon Sep 17 00:00:00 2001 From: Leandro Dorileo Date: Fri, 23 Mar 2018 14:18:18 -0700 Subject: [PATCH 2/2] BaseControl: add a Destroy() interface This interface should be used to remove an object from its parental chain, this destroyed object will not receive events nor will be drawn neither will impact on other objects position and sizing. These objects if properly handled could be eligible for collection as well. --- base_control.go | 22 ++++++++++++++++++++++ control_intf.go | 8 ++++++++ 2 files changed, 30 insertions(+) diff --git a/base_control.go b/base_control.go index 6b5b930..c96cc64 100644 --- a/base_control.go +++ b/base_control.go @@ -542,3 +542,25 @@ func (c *BaseControl) SetActiveTextColor(clr term.Attribute) { func (c *BaseControl) SetActiveBackColor(clr term.Attribute) { c.bgActive = clr } + +func (c *BaseControl) removeChild(control Control) { + children := []Control{} + + for _, child := range c.children { + if child.RefID() == control.RefID() { + continue + } + + children = append(children, child) + } + c.children = nil + + for _, child := range children { + c.AddChild(child) + } +} + +// Destroy removes an object from its parental chain +func (c *BaseControl) Destroy() { + c.parent.removeChild(c) +} diff --git a/control_intf.go b/control_intf.go index f041689..89104fb 100644 --- a/control_intf.go +++ b/control_intf.go @@ -149,4 +149,12 @@ type Control interface { ProcessEvent(ev Event) bool // RefID returns the controls internal reference id RefID() int64 + // removeChild removes a child from a container + // It's used to "destroy" controls whenever a control is no longer used + // by the user + removeChild(control Control) + // Destroy is the public interface to remove an object from its parental chain + // it implies this control will stop receiving events and will not be drawn nor + // will impact on other objects position and size calculation + Destroy() }