#84 - add new Control property Visible

This commit is contained in:
Vladimir Markelov 2018-03-13 22:14:40 -07:00
parent d1b209b51f
commit 77ee1b7531
20 changed files with 227 additions and 25 deletions

View File

@ -106,6 +106,10 @@ func CreateBarChart(parent Control, w, h int, scale int) *BarChart {
// Repaint draws the control on its View surface // Repaint draws the control on its View surface
func (b *BarChart) Draw() { func (b *BarChart) Draw() {
if b.hidden {
return
}
b.mtx.RLock() b.mtx.RLock()
defer b.mtx.RUnlock() defer b.mtx.RUnlock()

View File

@ -19,6 +19,7 @@ type BaseControl struct {
bgActive term.Attribute bgActive term.Attribute
tabSkip bool tabSkip bool
disabled bool disabled bool
hidden bool
align Align align Align
parent Control parent Control
inactive bool inactive bool
@ -118,6 +119,34 @@ func (c *BaseControl) SetEnabled(enabled bool) {
c.disabled = !enabled c.disabled = !enabled
} }
func (c *BaseControl) Visible() bool {
c.mtx.RLock()
defer c.mtx.RUnlock()
return !c.hidden
}
func (c *BaseControl) SetVisible(visible bool) {
c.mtx.Lock()
defer c.mtx.Unlock()
if visible == !c.hidden {
return
}
c.hidden = !visible
if c.parent == nil {
return
}
p := c.Parent()
for p.Parent() != nil {
p = p.Parent()
}
go PutEvent(Event{Type: EventLayout, Target: p})
}
func (c *BaseControl) Parent() Control { func (c *BaseControl) Parent() Control {
return c.parent return c.parent
} }
@ -204,23 +233,39 @@ func (c *BaseControl) SetBackColor(clr term.Attribute) {
c.bg = clr c.bg = clr
} }
func (c *BaseControl) childCount() int {
cnt := 0
for _, child := range c.children {
if child.Visible() {
cnt++
}
}
return cnt
}
func (c *BaseControl) ResizeChildren() { func (c *BaseControl) ResizeChildren() {
if len(c.children) == 0 { children := c.childCount()
if children == 0 {
return return
} }
fullWidth := c.width - 2*c.padX fullWidth := c.width - 2*c.padX
fullHeight := c.height - 2*c.padY fullHeight := c.height - 2*c.padY
if c.pack == Horizontal { if c.pack == Horizontal {
fullWidth -= (len(c.children) - 1) * c.gapX fullWidth -= (children - 1) * c.gapX
} else { } else {
fullHeight -= (len(c.children) - 1) * c.gapY fullHeight -= (children - 1) * c.gapY
} }
totalSc := c.ChildrenScale() totalSc := c.ChildrenScale()
minWidth := 0 minWidth := 0
minHeight := 0 minHeight := 0
for _, child := range c.children { for _, child := range c.children {
if !child.Visible() {
continue
}
cw, ch := child.MinimalSize() cw, ch := child.MinimalSize()
if c.pack == Horizontal { if c.pack == Horizontal {
minWidth += cw minWidth += cw
@ -239,6 +284,10 @@ func (c *BaseControl) ResizeChildren() {
} }
for _, ctrl := range c.children { for _, ctrl := range c.children {
if !ctrl.Visible() {
continue
}
tw, th := ctrl.MinimalSize() tw, th := ctrl.MinimalSize()
sc := ctrl.Scale() sc := ctrl.Scale()
d := int(ctrl.Scale() * aStep) d := int(ctrl.Scale() * aStep)
@ -332,20 +381,23 @@ func (c *BaseControl) ChildExists(control Control) bool {
} }
func (c *BaseControl) ChildrenScale() int { func (c *BaseControl) ChildrenScale() int {
if len(c.children) == 0 { if c.childCount() == 0 {
return c.scale return c.scale
} }
total := 0 total := 0
for _, ctrl := range c.children { for _, ctrl := range c.children {
total += ctrl.Scale() if ctrl.Visible() {
total += ctrl.Scale()
}
} }
return total return total
} }
func (c *BaseControl) MinimalSize() (w int, h int) { func (c *BaseControl) MinimalSize() (w int, h int) {
if len(c.children) == 0 { children := c.childCount()
if children == 0 {
return c.minW, c.minH return c.minW, c.minH
} }
@ -353,12 +405,15 @@ func (c *BaseControl) MinimalSize() (w int, h int) {
totalY := 2 * c.padY totalY := 2 * c.padY
if c.pack == Vertical { if c.pack == Vertical {
totalY += (len(c.children) - 1) * c.gapY totalY += (children - 1) * c.gapY
} else { } else {
totalX += (len(c.children) - 1) * c.gapX totalX += (children - 1) * c.gapX
} }
for _, ctrl := range c.children { for _, ctrl := range c.children {
if !ctrl.Visible() {
continue
}
ww, hh := ctrl.MinimalSize() ww, hh := ctrl.MinimalSize()
if c.pack == Vertical { if c.pack == Vertical {
totalY += hh totalY += hh
@ -388,6 +443,10 @@ func (c *BaseControl) Draw() {
} }
func (c *BaseControl) DrawChildren() { func (c *BaseControl) DrawChildren() {
if c.hidden {
return
}
PushClip() PushClip()
defer PopClip() defer PopClip()
@ -422,14 +481,18 @@ func (c *BaseControl) ProcessEvent(ev Event) bool {
} }
func (c *BaseControl) PlaceChildren() { func (c *BaseControl) PlaceChildren() {
if c.children == nil || len(c.children) == 0 { children := c.childCount()
if c.children == nil || children == 0 {
return return
} }
xx, yy := c.x+c.padX, c.y+c.padY xx, yy := c.x+c.padX, c.y+c.padY
for _, ctrl := range c.children { for _, ctrl := range c.children {
ctrl.SetPos(xx, yy) if !ctrl.Visible() {
continue
}
ctrl.SetPos(xx, yy)
ww, hh := ctrl.Size() ww, hh := ctrl.Size()
if c.pack == Vertical { if c.pack == Vertical {
yy += c.gapY + hh yy += c.gapY + hh

View File

@ -3,8 +3,8 @@ package clui
import ( import (
xs "github.com/huandu/xstrings" xs "github.com/huandu/xstrings"
term "github.com/nsf/termbox-go" term "github.com/nsf/termbox-go"
"sync/atomic"
"time" "time"
"sync/atomic"
) )
/* /*
@ -63,6 +63,10 @@ func CreateButton(parent Control, width, height int, title string, scale int) *B
// Repaint draws the control on its View surface // Repaint draws the control on its View surface
func (b *Button) Draw() { func (b *Button) Draw() {
if b.hidden {
return
}
b.mtx.RLock() b.mtx.RLock()
defer b.mtx.RUnlock() defer b.mtx.RUnlock()
PushAttributes() PushAttributes()
@ -122,7 +126,7 @@ func (b *Button) ProcessEvent(event Event) bool {
ev := Event{Type: EventRedraw} ev := Event{Type: EventRedraw}
go func() { go func() {
PutEvent(ev) PutEvent(ev)
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
b.setPressed(0) b.setPressed(0)
PutEvent(ev) PutEvent(ev)

View File

@ -1,4 +1,15 @@
2018-01-26 - version 0.7.0 2018-03-13 - version 0.8.0-RC1
[+] Added new property for all controls: Visible. It makes possible to show
and hide any control with its children(if there are any).
New Control interface methods: Visible and SetVisible
[+] A new event to support hiding/displaying controls: EventLayout with one
argument - Control that should handle the event. On receiving the event
the Control must recalculate and reposition all its children.
At this moment only Windows handle this event. Other kinds of Control
never receieves the event
[+] Add a new simple demo to play with Control visiblity: demos/visible
2018-01-26 - version 0.7.0
[+] Added new event handler for Window: set a callback OnScreenResize if you [+] Added new event handler for Window: set a callback OnScreenResize if you
want to handle terminal resize event want to handle terminal resize event

View File

@ -52,6 +52,10 @@ func CreateCheckBox(parent Control, width int, title string, scale int) *CheckBo
// Repaint draws the control on its View surface // Repaint draws the control on its View surface
func (c *CheckBox) Draw() { func (c *CheckBox) Draw() {
if c.hidden {
return
}
c.mtx.RLock() c.mtx.RLock()
defer c.mtx.RUnlock() defer c.mtx.RUnlock()

View File

@ -696,5 +696,13 @@ func ProcessEvent(ev Event) {
comp.processKey(ev) comp.processKey(ev)
case EventMouse: case EventMouse:
comp.processMouse(ev) comp.processMouse(ev)
case EventLayout:
for _, c := range comp.windows {
if c == ev.Target {
c.ResizeChildren()
c.PlaceChildren()
break
}
}
} }
} }

View File

@ -85,6 +85,7 @@ type Event struct {
// For resize event - new terminal size // For resize event - new terminal size
Width int Width int
Height int Height int
Target Control
} }
// BorderStyle constants // BorderStyle constants
@ -286,8 +287,10 @@ const (
EventDialogClose EventDialogClose
// Close application // Close application
EventQuit EventQuit
// Close top window - or application is there is only one window // Close top window - or application is there is only one window
EventCloseWindow EventCloseWindow
// Make a control (Target field of Event structure) to recalculate and reposition all its children
EventLayout
) )
// ConfirmationDialog and SelectDialog exit codes // ConfirmationDialog and SelectDialog exit codes

View File

@ -38,6 +38,9 @@ type Control interface {
// Enable return if a control can process keyboard and mouse events // Enable return if a control can process keyboard and mouse events
Enabled() bool Enabled() bool
SetEnabled(enabled bool) SetEnabled(enabled bool)
// Visible return if a control is visible
Visible() bool
SetVisible(enabled bool)
// Parent return control's container or nil if there is no parent container // Parent return control's container or nil if there is no parent container
// that is true for Windows // that is true for Windows
Parent() Control Parent() Control

View File

@ -211,7 +211,7 @@ func _nextControl(parent Control, curr, prev Control, foundPrev, next bool) (boo
} }
} }
if ctrl.Enabled() && ctrl.TabStop() { if ctrl.Enabled() && ctrl.TabStop() && ctrl.Visible() {
if found { if found {
return found, ctrl return found, ctrl
} else if !next { } else if !next {
@ -233,7 +233,7 @@ func _nextControl(parent Control, curr, prev Control, foundPrev, next bool) (boo
// that has tab-stop feature on. Used by library when processing TAB key // that has tab-stop feature on. Used by library when processing TAB key
func NextControl(parent Control, curr Control, next bool) Control { func NextControl(parent Control, curr Control, next bool) Control {
fnTab := func(c Control) bool { fnTab := func(c Control) bool {
return c.TabStop() return c.TabStop() && c.Visible()
} }
var defControl Control var defControl Control

62
demos/visible/visible.go Normal file
View File

@ -0,0 +1,62 @@
package main
import (
ui "github.com/VladimirMarkelov/clui"
)
func main() {
ui.InitLibrary()
defer ui.DeinitLibrary()
view := ui.AddWindow(0, 0, 10, 7, "Hello World!")
view.SetPack(ui.Vertical)
frmResize := ui.CreateFrame(view, 8, 6, ui.BorderNone, ui.Fixed)
frmResize.SetTitle("FrameTop")
frmResize.SetPack(ui.Horizontal)
btn1 := ui.CreateButton(frmResize, 8, 5, "Button 1", 1)
btn2 := ui.CreateButton(frmResize, 8, 5, "Button 2", 1)
btn3 := ui.CreateButton(frmResize, 8, 5, "Button 3", 1)
frmBtns := ui.CreateFrame(view, 8, 5, ui.BorderNone, ui.Fixed)
frmBtns.SetPack(ui.Horizontal)
frmBtns.SetTitle("FrameBottom")
btnHide1 := ui.CreateButton(frmBtns, 8, 4, "Hide 1", 1)
btnHide1.OnClick(func(ev ui.Event) {
if btn1.Visible() {
btnHide1.SetTitle("Show 1")
btn1.SetVisible(false)
} else {
btnHide1.SetTitle("Hide 1")
btn1.SetVisible(true)
}
})
btnHide2 := ui.CreateButton(frmBtns, 8, 4, "Hide 2", 1)
btnHide2.OnClick(func(ev ui.Event) {
if btn2.Visible() {
btnHide2.SetTitle("Show 2")
btn2.SetVisible(false)
} else {
btnHide2.SetTitle("Hide 2")
btn2.SetVisible(true)
}
})
btnHide3 := ui.CreateButton(frmBtns, 8, 4, "Hide 3", 1)
btnHide3.OnClick(func(ev ui.Event) {
if btn3.Visible() {
btnHide3.SetTitle("Show 3")
btn3.SetVisible(false)
} else {
btnHide3.SetTitle("Hide 3")
btn3.SetVisible(true)
}
})
btnQuit := ui.CreateButton(frmBtns, 8, 4, "Quit", 1)
btnQuit.OnClick(func(ev ui.Event) {
go ui.Stop()
})
ui.MainLoop()
}

View File

@ -32,6 +32,10 @@ func (e *EditField) SetTitle(title string) {
// Repaint draws the control on its View surface // Repaint draws the control on its View surface
func (e *EditField) Draw() { func (e *EditField) Draw() {
if e.hidden {
return
}
PushAttributes() PushAttributes()
defer PopAttributes() defer PopAttributes()

View File

@ -59,6 +59,10 @@ func CreateFrame(parent Control, width, height int, bs BorderStyle, scale int) *
// Repaint draws the control on its View surface // Repaint draws the control on its View surface
func (f *Frame) Draw() { func (f *Frame) Draw() {
if f.hidden {
return
}
PushAttributes() PushAttributes()
defer PopAttributes() defer PopAttributes()

View File

@ -63,6 +63,10 @@ func (l *Label) SetDirection(dir Direction) {
} }
func (l *Label) Draw() { func (l *Label) Draw() {
if l.hidden {
return
}
PushAttributes() PushAttributes()
defer PopAttributes() defer PopAttributes()

View File

@ -111,6 +111,10 @@ func (l *ListBox) drawItems() {
// Repaint draws the control on its View surface // Repaint draws the control on its View surface
func (l *ListBox) Draw() { func (l *ListBox) Draw() {
if l.hidden {
return
}
PushAttributes() PushAttributes()
defer PopAttributes() defer PopAttributes()

View File

@ -71,8 +71,12 @@ func CreateProgressBar(parent Control, width, height int, scale int) *ProgressBa
// pb.SetTitle("{{value}} of {{max}}") // pb.SetTitle("{{value}} of {{max}}")
// pb.SetTitle("{{percent}}%") // pb.SetTitle("{{percent}}%")
func (b *ProgressBar) Draw() { func (b *ProgressBar) Draw() {
b.mtx.RLock() if b.hidden {
defer b.mtx.RUnlock() return
}
b.mtx.RLock()
defer b.mtx.RUnlock()
if b.max <= b.min { if b.max <= b.min {
return return
} }
@ -164,8 +168,8 @@ func (b *ProgressBar) Draw() {
// SetValue sets new progress value. If value exceeds ProgressBar // SetValue sets new progress value. If value exceeds ProgressBar
// limits then the limit value is used // limits then the limit value is used
func (b *ProgressBar) SetValue(pos int) { func (b *ProgressBar) SetValue(pos int) {
b.mtx.Lock() b.mtx.Lock()
defer b.mtx.Unlock() defer b.mtx.Unlock()
if pos < b.min { if pos < b.min {
b.value = b.min b.value = b.min
} else if pos > b.max { } else if pos > b.max {
@ -177,8 +181,8 @@ func (b *ProgressBar) SetValue(pos int) {
// Value returns the current ProgressBar value // Value returns the current ProgressBar value
func (b *ProgressBar) Value() int { func (b *ProgressBar) Value() int {
b.mtx.RLock() b.mtx.RLock()
defer b.mtx.RUnlock() defer b.mtx.RUnlock()
return b.value return b.value
} }
@ -204,8 +208,8 @@ func (b *ProgressBar) SetLimits(min, max int) {
// Step increases ProgressBar value by 1 if the value is less // Step increases ProgressBar value by 1 if the value is less
// than ProgressBar high limit // than ProgressBar high limit
func (b *ProgressBar) Step() int { func (b *ProgressBar) Step() int {
b.mtx.Lock() b.mtx.Lock()
defer b.mtx.Unlock() defer b.mtx.Unlock()
b.value++ b.value++
if b.value > b.max { if b.value > b.max {

View File

@ -48,6 +48,10 @@ func CreateRadio(parent Control, width int, title string, scale int) *Radio {
// Repaint draws the control on its View surface // Repaint draws the control on its View surface
func (c *Radio) Draw() { func (c *Radio) Draw() {
if c.hidden {
return
}
PushAttributes() PushAttributes()
defer PopAttributes() defer PopAttributes()

View File

@ -72,6 +72,10 @@ func CreateSparkChart(parent Control, w, h int, scale int) *SparkChart {
// Repaint draws the control on its View surface // Repaint draws the control on its View surface
func (b *SparkChart) Draw() { func (b *SparkChart) Draw() {
if b.hidden {
return
}
b.mtx.RLock() b.mtx.RLock()
defer b.mtx.RUnlock() defer b.mtx.RUnlock()

View File

@ -348,6 +348,10 @@ func (l *TableView) drawCells() {
// Repaint draws the control on its View surface // Repaint draws the control on its View surface
func (l *TableView) Draw() { func (l *TableView) Draw() {
if l.hidden {
return
}
l.mtx.RLock() l.mtx.RLock()
defer l.mtx.RUnlock() defer l.mtx.RUnlock()
PushAttributes() PushAttributes()

View File

@ -80,6 +80,10 @@ func (l *TextReader) drawText() {
// Repaint draws the control on its View surface // Repaint draws the control on its View surface
func (l *TextReader) Draw() { func (l *TextReader) Draw() {
if l.hidden {
return
}
PushAttributes() PushAttributes()
defer PopAttributes() defer PopAttributes()

View File

@ -166,6 +166,10 @@ func (l *TextView) drawText() {
// Repaint draws the control on its View surface // Repaint draws the control on its View surface
func (l *TextView) Draw() { func (l *TextView) Draw() {
if l.hidden {
return
}
PushAttributes() PushAttributes()
defer PopAttributes() defer PopAttributes()