From dbc2b923120622c54198e723b71993e7ae839cca Mon Sep 17 00:00:00 2001 From: Vladimir Markelov Date: Tue, 4 Jul 2017 18:04:58 -0700 Subject: [PATCH] add a way to hide a window instead of destroying it --- changelog | 16 ++++++++++++++- composer.go | 56 ++++++++++++++++++++++++++++++++++++---------------- dialog.go | 7 +++++-- tableview.go | 2 +- window.go | 47 ++++++++++++++++++++++++++++++++----------- 5 files changed, 96 insertions(+), 32 deletions(-) diff --git a/changelog b/changelog index 7ba7a6b..109b4fd 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,18 @@ -2017-06-30 - Version 0.6.0 +2017-07-04 - Version 0.6.1 +[*] Fix selection Window with mouse: clicking non-active Window makes the + Window active (unless the top Window is modal one) +[*] TableView does not fire OnSelectionChange event if a user clicked outside + the table. Before the fix a callback got selected index greater than + the total number of rows and might crash a callback +[+] Add a new feature to Windows: to keep all changes the Windows can be + hidden instead of destroying when a user clicks window close button. It + can be done with overriding OnClose and returning 'true' if Windows must + be kept. To remove the Window from screen use SetVisible(false) +[*] Window OnClose callback event now must return boolean. See change above +[*] Fix typo: TableView did not send TableDelete event if the selected Row + was Row number 1 + +2017-06-30 - Version 0.6.0 [+] Added new control TextReader - a virtual text view control with built-in support of some hots keys to list the text [+] Added a new global event to close active view diff --git a/composer.go b/composer.go index 46187e7..1ba2f73 100644 --- a/composer.go +++ b/composer.go @@ -65,7 +65,10 @@ func RefreshScreen() { term.Clear(ColorWhite, ColorBlack) for _, wnd := range comp.windows { - wnd.Draw() + v := comp.topWindow().(*Window) + if v.Visible() { + wnd.Draw() + } } term.Flush() @@ -144,18 +147,36 @@ func (c *Composer) moveActiveWindowToBottom() bool { return false } + anyVisible := false + for _, w := range c.windows { + v := w.(*Window) + if v.Visible() { + anyVisible = true + break + } + } + if !anyVisible { + return false + } + event := Event{Type: EventActivate, X: 0} // send deactivated c.sendEventToActiveWindow(event) - last := c.topWindow() + for { + last := c.topWindow() + for i := len(c.windows) - 1; i > 0; i-- { + c.windows[i] = c.windows[i-1] + } + c.windows[0] = last - for i := len(c.windows) - 1; i > 0; i-- { - c.windows[i] = c.windows[i-1] - } + v := c.topWindow().(*Window) + if v.Visible() { + if !c.activateWindow(c.topWindow()) { + return false + } - c.windows[0] = last - if !c.activateWindow(c.topWindow()) { - return false + break + } } event = Event{Type: EventActivate, X: 1} // send 'activated' @@ -246,13 +267,14 @@ func (c *Composer) closeTopWindow() { if len(c.windows) > 1 { view := c.topWindow() event := Event{Type: EventClose, X: 1} - c.sendEventToActiveWindow(event) - c.DestroyWindow(view) - activate := c.topWindow() - c.activateWindow(activate) - event = Event{Type: EventActivate, X: 1} // send 'activated' - c.sendEventToActiveWindow(event) + if c.sendEventToActiveWindow(event) { + c.DestroyWindow(view) + activate := c.topWindow() + c.activateWindow(activate) + event = Event{Type: EventActivate, X: 1} // send 'activated' + c.sendEventToActiveWindow(event) + } RefreshScreen() } else { @@ -451,9 +473,9 @@ func (c *Composer) processMouse(ev Event) { return } } else if !c.topWindow().Modal() { - c.activateWindow(view) - return - } + c.activateWindow(view) + return + } if ev.Key == term.MouseLeft { c.lastX = ev.X diff --git a/dialog.go b/dialog.go index 36e17cc..2586b89 100644 --- a/dialog.go +++ b/dialog.go @@ -111,7 +111,7 @@ func CreateConfirmationDialog(title, question string, buttons []string, defaultB CreateFrame(frm1, 1, 1, BorderNone, 1) - dlg.view.OnClose(func(ev Event) { + dlg.view.OnClose(func(ev Event) bool { if dlg.result == DialogAlive { dlg.result = DialogClosed if ev.X != 1 { @@ -121,6 +121,7 @@ func CreateConfirmationDialog(title, question string, buttons []string, defaultB go dlg.onClose() } } + return true }) return dlg @@ -220,7 +221,7 @@ func CreateSelectDialog(title string, items []string, selectedItem int, typ Sele ActivateControl(dlg.view, btn2) CreateFrame(frm1, 1, 1, BorderNone, 1) - dlg.view.OnClose(func(ev Event) { + dlg.view.OnClose(func(ev Event) bool { if dlg.result == DialogAlive { dlg.result = DialogClosed if ev.X != 1 { @@ -230,6 +231,8 @@ func CreateSelectDialog(title string, items []string, selectedItem int, typ Sele go dlg.onClose() } } + + return true }) return dlg diff --git a/tableview.go b/tableview.go index 546cc7c..83d8ad3 100644 --- a/tableview.go +++ b/tableview.go @@ -793,7 +793,7 @@ func (l *TableView) ProcessEvent(event Event) bool { go l.onAction(ev) } case term.KeyDelete: - if l.selectedRow != 1 && l.onAction != nil { + if l.selectedRow != -1 && l.onAction != nil { ev := TableEvent{Action: TableActionDelete, Col: l.selectedCol, Row: l.selectedRow} go l.onAction(ev) } diff --git a/window.go b/window.go index 3860a13..5b7c208 100644 --- a/window.go +++ b/window.go @@ -16,9 +16,10 @@ type Window struct { origHeight int origX int origY int + hidden bool - onClose func(Event) - onKeyDown func(Event) bool + onClose func(Event) bool + onKeyDown func(Event) bool } func CreateWindow(x, y, w, h int, title string) *Window { @@ -211,8 +212,11 @@ func (c *Window) ProcessEvent(ev Event) bool { c.PlaceChildren() case EventClose: if c.onClose != nil { - c.onClose(ev) + if !c.onClose(ev) { + return false + } } + return true case EventKey: if ev.Key == term.KeyTab { aC := ActiveControl(c) @@ -229,13 +233,13 @@ func (c *Window) ProcessEvent(ev Event) bool { } return true } else { - if SendEventToChild(c, ev) { - return true - } - if c.onKeyDown != nil { - return c.onKeyDown(ev) - } - return false + if SendEventToChild(c, ev) { + return true + } + if c.onKeyDown != nil { + return c.onKeyDown(ev) + } + return false } default: if ev.Type == EventMouse && ev.Key == term.MouseLeft { @@ -247,7 +251,7 @@ func (c *Window) ProcessEvent(ev Event) bool { return false } -func (w *Window) OnClose(fn func(Event)) { +func (w *Window) OnClose(fn func(Event) bool) { w.onClose = fn } @@ -282,3 +286,24 @@ func (w *Window) SetMaximized(maximize bool) { func (w *Window) Maximized() bool { return w.maximized } + +// Visible returns if the window must be drawn on the screen +func (w *Window) Visible() bool { + return !w.hidden +} + +// SetVisible allows to temporarily remove the window from screen +// and show it later without reconstruction +func (w *Window) SetVisible(visible bool) { + if w.hidden == visible { + w.hidden = !visible + if w.hidden { + w.SetModal(false) + if composer().topWindow() == w { + composer().moveActiveWindowToBottom() + } + } else { + composer().activateWindow(w) + } + } +}