diff --git a/Gopkg.lock b/Gopkg.lock index 795953b..5148388 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,11 +3,11 @@ [[projects]] branch = "master" - digest = "1:06cb27109f9e5ae9e844ccd7527ce1e698caca6b427d088ea43ff1fc91c8ab1c" + digest = "1:cf90665ea4bb37bee01cbecff0a5b2e3b49355a62bfb7dd4aa5433eda11682b5" name = "github.com/VladimirMarkelov/clui" packages = ["."] pruneopts = "UT" - revision = "7749b2936744140b703023c6c9d76491e3abd22c" + revision = "89a7c86623ec35323906bc09de4f1d98bc75abdb" [[projects]] digest = "1:dd1417c60900a2be6d1653e90306614657761fc9c3066eccc558ae432b31aa60" @@ -18,12 +18,12 @@ version = "v0.1.0" [[projects]] - digest = "1:4e0aa2a8f7c5d6e125af6ed331f5389003429e4fb0747f6d4be9715f246800f0" + digest = "1:f9a5e090336881be43cfc1cf468330c1bdd60abdc9dd194e0b1ab69f4b94dd7c" name = "github.com/huandu/xstrings" packages = ["."] pruneopts = "UT" - revision = "55ae428c2ac4f74d7430952ef528631e656ac92c" - version = "v1.1.0" + revision = "f02667b379e2fb5916c3cda2cf31e0eb885d79f8" + version = "v1.2.0" [[projects]] digest = "1:cdb899c199f907ac9fb50495ec71212c95cb5b0e0a8ee0800da0238036091033" diff --git a/vendor/github.com/VladimirMarkelov/clui/README.md b/vendor/github.com/VladimirMarkelov/clui/README.md index f10429b..877b98b 100644 --- a/vendor/github.com/VladimirMarkelov/clui/README.md +++ b/vendor/github.com/VladimirMarkelov/clui/README.md @@ -7,7 +7,7 @@ Command Line User Interface (Console UI inspired by TurboVision) with built-in t ## Current version -The current version is 0.9.0 RC2. Please see details in [changelog](./changelog). +The current version is 0.9.0 RC3. Please see details in [changelog](./changelog). ## Applications that uses the library * Terminal FB2 reader(termfb2): https://github.com/VladimirMarkelov/termfb2 @@ -40,6 +40,7 @@ The current version is 0.9.0 RC2. Please see details in [changelog](./changelog) * GridView (Table to show structured data - only virtual and readonly mode with scroll support) * [FilePicker](/docs/fselect.md) * LoginDialog - a simple authorization dialog with two fields: Username and Password +* Scrollable frame ## Screenshots The main demo (theme changing and radio group control) diff --git a/vendor/github.com/VladimirMarkelov/clui/VERSION b/vendor/github.com/VladimirMarkelov/clui/VERSION index 6f4eebd..94f6550 100644 --- a/vendor/github.com/VladimirMarkelov/clui/VERSION +++ b/vendor/github.com/VladimirMarkelov/clui/VERSION @@ -1 +1 @@ -0.8.1 +0.9.0-rc3 diff --git a/vendor/github.com/VladimirMarkelov/clui/barchart.go b/vendor/github.com/VladimirMarkelov/clui/barchart.go index d6933ac..94f7b61 100644 --- a/vendor/github.com/VladimirMarkelov/clui/barchart.go +++ b/vendor/github.com/VladimirMarkelov/clui/barchart.go @@ -117,7 +117,7 @@ func (b *BarChart) Draw() { PushAttributes() defer PopAttributes() - fg, bg := RealColor(b.fg, ColorBarChartText), RealColor(b.bg, ColorBarChartBack) + fg, bg := RealColor(b.fg, b.Style(), ColorBarChartText), RealColor(b.bg, b.Style(), ColorBarChartBack) SetTextColor(fg) SetBackColor(bg) @@ -235,7 +235,7 @@ func (b *BarChart) drawLegend() { PushAttributes() defer PopAttributes() - fg, bg := RealColor(b.fg, ColorBarChartText), RealColor(b.bg, ColorBarChartBack) + fg, bg := RealColor(b.fg, b.Style(), ColorBarChartText), RealColor(b.bg, b.Style(), ColorBarChartBack) parts := []rune(SysObject(ObjBarChart)) defRune := parts[0] diff --git a/vendor/github.com/VladimirMarkelov/clui/base_control.go b/vendor/github.com/VladimirMarkelov/clui/base_control.go index fd89b97..d216883 100644 --- a/vendor/github.com/VladimirMarkelov/clui/base_control.go +++ b/vendor/github.com/VladimirMarkelov/clui/base_control.go @@ -32,6 +32,9 @@ type BaseControl struct { children []Control mtx sync.RWMutex onActive func(active bool) + style string + clipped bool + clipper *rect } var ( @@ -46,6 +49,22 @@ func NewBaseControl() BaseControl { return BaseControl{refID: nextRefId()} } +func (c *BaseControl) SetClipped(clipped bool) { + c.clipped = clipped +} + +func (c *BaseControl) Clipped() bool { + return c.clipped +} + +func (c *BaseControl) SetStyle(style string) { + c.style = style +} + +func (c *BaseControl) Style() string { + return c.style +} + func (c *BaseControl) RefID() int64 { return c.refID } @@ -81,8 +100,22 @@ func (c *BaseControl) Pos() (x int, y int) { } func (c *BaseControl) SetPos(x, y int) { - c.x = x - c.y = y + if c.clipped && c.clipper != nil { + cx, cy, _, _ := c.Clipper() + px, py := c.Paddings() + + distX := cx - c.x + distY := cy - c.y + + c.clipper.x = x + px + c.clipper.y = y + py + + c.x = (x - distX) + px + c.y = (y - distY) + py + } else { + c.x = x + c.y = y + } } func (c *BaseControl) applyConstraints() { @@ -390,6 +423,10 @@ func (c *BaseControl) AddChild(control Control) { mainCtrl.ResizeChildren() mainCtrl.PlaceChildren() } + + if c.clipped && c.clipper == nil { + c.setClipper() + } } func (c *BaseControl) Children() []Control { @@ -443,6 +480,10 @@ func (c *BaseControl) MinimalSize() (w int, h int) { } for _, ctrl := range c.children { + if ctrl.Clipped() { + continue + } + if !ctrl.Visible() { continue } @@ -482,13 +523,37 @@ func (c *BaseControl) DrawChildren() { PushClip() defer PopClip() - SetClipRect(c.x+c.padX, c.y+c.padY, c.width-2*c.padX, c.height-2*c.padY) + cp := ClippedParent(c) + var cTarget Control + + cTarget = c + if cp != nil { + cTarget = cp + } + + x, y, w, h := cTarget.Clipper() + SetClipRect(x, y, w, h) for _, child := range c.children { child.Draw() } } +func (c *BaseControl) Clipper() (int, int, int, int) { + clipped := ClippedParent(c) + + if clipped == nil || (c.clipped && c.clipper != nil) { + return c.clipper.x, c.clipper.y, c.clipper.w, c.clipper.h + } + + return CalcClipper(clipped) +} + +func (c *BaseControl) setClipper() { + x, y, w, h := CalcClipper(c) + c.clipper = &rect{x: x, y: y, w: w, h: h} +} + func (c *BaseControl) HitTest(x, y int) HitResult { if x > c.x && x < c.x+c.width-1 && y > c.y && y < c.y+c.height-1 { @@ -572,4 +637,5 @@ func (c *BaseControl) removeChild(control Control) { // Destroy removes an object from its parental chain func (c *BaseControl) Destroy() { c.parent.removeChild(c) + c.parent.SetConstraints(0, 0) } diff --git a/vendor/github.com/VladimirMarkelov/clui/button.go b/vendor/github.com/VladimirMarkelov/clui/button.go index 48e7c25..47b1cee 100644 --- a/vendor/github.com/VladimirMarkelov/clui/button.go +++ b/vendor/github.com/VladimirMarkelov/clui/button.go @@ -77,13 +77,13 @@ func (b *Button) Draw() { w, h := b.Size() fg, bg := b.fg, b.bg - shadow := RealColor(b.shadowColor, ColorButtonShadow) + shadow := RealColor(b.shadowColor, b.Style(), ColorButtonShadow) if b.disabled { - fg, bg = RealColor(fg, ColorButtonDisabledText), RealColor(bg, ColorButtonDisabledBack) + fg, bg = RealColor(fg, b.Style(), ColorButtonDisabledText), RealColor(bg, b.Style(), ColorButtonDisabledBack) } else if b.Active() { - fg, bg = RealColor(b.fgActive, ColorButtonActiveText), RealColor(b.bgActive, ColorButtonActiveBack) + fg, bg = RealColor(b.fgActive, b.Style(), ColorButtonActiveText), RealColor(b.bgActive, b.Style(), ColorButtonActiveBack) } else { - fg, bg = RealColor(fg, ColorButtonText), RealColor(bg, ColorButtonBack) + fg, bg = RealColor(fg, b.Style(), ColorButtonText), RealColor(bg, b.Style(), ColorButtonBack) } dy := int((h - 1) / 2) diff --git a/vendor/github.com/VladimirMarkelov/clui/canvas.go b/vendor/github.com/VladimirMarkelov/clui/canvas.go index e84cb6a..c40a2ee 100644 --- a/vendor/github.com/VladimirMarkelov/clui/canvas.go +++ b/vendor/github.com/VladimirMarkelov/clui/canvas.go @@ -276,12 +276,12 @@ func DrawText(x, y int, text string) { SetBackColor(elem.Bg) drawn := PutChar(x, y, elem.Ch) - if unicode.Is(unicode.Scripts["Han"], elem.Ch){ + if unicode.Is(unicode.Scripts["Han"], elem.Ch) { x += 2 } else { x += 1 } - + if firstdrawn && !drawn { break } @@ -432,9 +432,9 @@ func DrawScrollBar(x, y, w, h, pos int) { PushAttributes() defer PopAttributes() - fg, bg := RealColor(ColorDefault, ColorScrollText), RealColor(ColorDefault, ColorScrollBack) + fg, bg := RealColor(ColorDefault, "", ColorScrollText), RealColor(ColorDefault, "", ColorScrollBack) // TODO: add thumb styling - // fgThumb, bgThumb := RealColor(ColorDefault, ColorThumbText), RealColor(ColorDefault, ColorThumbBack) + // fgThumb, bgThumb := RealColor(ColorDefault, "", ColorThumbText), RealColor(ColorDefault, "", ColorThumbBack) SetTextColor(fg) SetBackColor(bg) diff --git a/vendor/github.com/VladimirMarkelov/clui/changelog b/vendor/github.com/VladimirMarkelov/clui/changelog index a557697..1613ba5 100644 --- a/vendor/github.com/VladimirMarkelov/clui/changelog +++ b/vendor/github.com/VladimirMarkelov/clui/changelog @@ -1,3 +1,8 @@ +2018-09-06 - version 0.9.0 RC3 (Thanks to Leandro Dorileo) +[+] New big feature: scrollable Frame +[+] Custom theme support: overriding theme colors with custom ones +[*] Fix bug in getLinearControlList + 2018-08-13 - version 0.9.0 RC2 [+] New control: LoginDialog (a dialog with username and password fields) diff --git a/vendor/github.com/VladimirMarkelov/clui/checkbox.go b/vendor/github.com/VladimirMarkelov/clui/checkbox.go index 828e4e3..48a3ef4 100644 --- a/vendor/github.com/VladimirMarkelov/clui/checkbox.go +++ b/vendor/github.com/VladimirMarkelov/clui/checkbox.go @@ -66,11 +66,11 @@ func (c *CheckBox) Draw() { x, y := c.Pos() w, h := c.Size() - fg, bg := RealColor(c.fg, ColorControlText), RealColor(c.bg, ColorControlBack) + fg, bg := RealColor(c.fg, c.Style(), ColorControlText), RealColor(c.bg, c.Style(), ColorControlBack) if !c.Enabled() { - fg, bg = RealColor(c.fg, ColorControlDisabledText), RealColor(c.bg, ColorControlDisabledBack) + fg, bg = RealColor(c.fg, c.Style(), ColorControlDisabledText), RealColor(c.bg, c.Style(), ColorControlDisabledBack) } else if c.Active() { - fg, bg = RealColor(c.fg, ColorControlActiveText), RealColor(c.bg, ColorControlActiveBack) + fg, bg = RealColor(c.fg, c.Style(), ColorControlActiveText), RealColor(c.bg, c.Style(), ColorControlActiveBack) } parts := []rune(SysObject(ObjCheckBox)) diff --git a/vendor/github.com/VladimirMarkelov/clui/consts.go b/vendor/github.com/VladimirMarkelov/clui/consts.go index 5ed4dde..21a6091 100644 --- a/vendor/github.com/VladimirMarkelov/clui/consts.go +++ b/vendor/github.com/VladimirMarkelov/clui/consts.go @@ -291,6 +291,9 @@ const ( EventCloseWindow // Make a control (Target field of Event structure) to recalculate and reposition all its children EventLayout + // A scroll-able control's child has been activated, then notify its parent to handle + // the scrolling + EventActivateChild ) // ConfirmationDialog and SelectDialog exit codes diff --git a/vendor/github.com/VladimirMarkelov/clui/control_intf.go b/vendor/github.com/VladimirMarkelov/clui/control_intf.go index 89104fb..248c675 100644 --- a/vendor/github.com/VladimirMarkelov/clui/control_intf.go +++ b/vendor/github.com/VladimirMarkelov/clui/control_intf.go @@ -157,4 +157,20 @@ type Control interface { // it implies this control will stop receiving events and will not be drawn nor // will impact on other objects position and size calculation Destroy() + // SetStyle sets a control's custom style grouper/modifier, with a style set + // the control will prefix the control theme with style, i.e if a button is modified + // and set style to "MyCustom" then the theme will engine will first attempt to apply + // MyCustomButtonBack and MyCustomButtonText if not present then apply the default + // and standard ButtonBack and ButtonText + SetStyle(style string) + // Style returns the custom style grouper/modifier + Style() string + // SetClipped marks a control as clip-able, meaning the children components will not + // affect the control's size - i.e will not make it expand + SetClipped(clipped bool) + // Clipped returns the current control's clipped flag + Clipped() bool + // Clipper if the component is clipped then return the clipper geometry, however + // the size and pos is returned + Clipper() (int, int, int, int) } diff --git a/vendor/github.com/VladimirMarkelov/clui/ctrlutil.go b/vendor/github.com/VladimirMarkelov/clui/ctrlutil.go index 2794845..273a74e 100644 --- a/vendor/github.com/VladimirMarkelov/clui/ctrlutil.go +++ b/vendor/github.com/VladimirMarkelov/clui/ctrlutil.go @@ -192,7 +192,7 @@ func getLinearControlList(parent Control, fn func(Control) bool) []Control { result := []Control{} for _, curr := range parent.Children() { - if fn(curr) { + if fn != nil && fn(curr) { result = append(result, curr) } @@ -266,8 +266,58 @@ func SendEventToChild(parent Control, ev Event) bool { } if child != nil && child != parent { - return child.ProcessEvent(ev) + ev.Target = child + res := child.ProcessEvent(ev) + + if cparent := ClippedParent(child); cparent != nil && cparent != child { + cparent.ProcessEvent(ev) + } + + return res } return false } + +// CalcClipper calculates the clipper size based on the control's size, position +// and paddings +func CalcClipper(c Control) (int, int, int, int) { + w, h := c.Size() + x, y := c.Pos() + px, py := c.Paddings() + + x = x + px + y = y + py + w = w - 2*px + h = h - 2*py + + return x, y, w, h +} + +// ClippedParent finds the first c parent with clipped flag +func ClippedParent(c Control) Control { + var clipped Control + + ctrl := c.Parent() + clipped = c + + for ctrl != nil { + if ctrl.Clipped() { + clipped = ctrl + break + } + + ctrl = ctrl.Parent() + } + + return clipped +} + +// ControlInRect returns true if c is within a given rect +func ControlInRect(c Control, x int, y int, w int, h int) bool { + xx, yy := c.Pos() + ww, hh := c.Size() + + return xx >= x && ww <= x+w && yy <= y+h && + yy+hh <= y+h && yy >= y && yy+h >= y +} diff --git a/vendor/github.com/VladimirMarkelov/clui/edit.go b/vendor/github.com/VladimirMarkelov/clui/edit.go index 1a02b3a..d604795 100644 --- a/vendor/github.com/VladimirMarkelov/clui/edit.go +++ b/vendor/github.com/VladimirMarkelov/clui/edit.go @@ -99,11 +99,11 @@ func (e *EditField) Draw() { } } - fg, bg := RealColor(e.fg, ColorEditText), RealColor(e.bg, ColorEditBack) + fg, bg := RealColor(e.fg, e.Style(), ColorEditText), RealColor(e.bg, e.Style(), ColorEditBack) if !e.Enabled() { - fg, bg = RealColor(e.fg, ColorDisabledText), RealColor(e.fg, ColorDisabledBack) + fg, bg = RealColor(e.fg, e.Style(), ColorDisabledText), RealColor(e.fg, e.Style(), ColorDisabledBack) } else if e.Active() { - fg, bg = RealColor(e.fg, ColorEditActiveText), RealColor(e.bg, ColorEditActiveBack) + fg, bg = RealColor(e.fg, e.Style(), ColorEditActiveText), RealColor(e.bg, e.Style(), ColorEditActiveBack) } SetTextColor(fg) diff --git a/vendor/github.com/VladimirMarkelov/clui/frame.go b/vendor/github.com/VladimirMarkelov/clui/frame.go index 76d7b38..9fcefa0 100644 --- a/vendor/github.com/VladimirMarkelov/clui/frame.go +++ b/vendor/github.com/VladimirMarkelov/clui/frame.go @@ -2,6 +2,7 @@ package clui import ( xs "github.com/huandu/xstrings" + "math" ) /* @@ -12,9 +13,11 @@ is required */ type Frame struct { BaseControl - border BorderStyle - children []Control - pack PackType + border BorderStyle + children []Control + pack PackType + scrollable bool + lastScrollProp int } /* @@ -58,6 +61,30 @@ func CreateFrame(parent Control, width, height int, bs BorderStyle, scale int) * return f } +func (f *Frame) SetScrollable(scrollable bool) { + f.scrollable = scrollable + + if scrollable { + px, py := f.Paddings() + + if f.Pack() == Vertical { + px += 1 + } + + if f.Pack() == Horizontal { + py += 1 + } + + f.SetPaddings(px, py) + } + + f.SetClipped(scrollable) +} + +func (f *Frame) Scrollable() bool { + return f.scrollable +} + // Repaint draws the control on its View surface func (f *Frame) Draw() { if f.hidden { @@ -67,16 +94,52 @@ func (f *Frame) Draw() { PushAttributes() defer PopAttributes() + x, y, w, h := f.Clipper() + + if f.scrollable { + + _, fy := f.Pos() + _, fh := f.Size() + _, fpy := f.Paddings() + + var dist float64 + prop := 0 + ctrl := ActiveControl(f) + + if ctrl != nil { + var frameProp float64 + + _, ty := ctrl.Pos() + + dist = (float64(fy) + float64(fpy)) - float64(ty) + dist = math.Sqrt(dist * dist) + + if dist > 0 { + frameProp = (dist * 100) / float64(fh) + } + + if frameProp > 0 { + prop = int(math.Round((float64(h-2) / (100 / frameProp)))) + } + + f.lastScrollProp = prop + } + + DrawScrollBar(x+w, y, 1, h, f.lastScrollProp) + } + + fg, bg := RealColor(f.fg, f.Style(), ColorViewText), RealColor(f.bg, f.Style(), ColorViewBack) + if f.border == BorderNone { + if bg != ColorDefault { + SetBackColor(bg) + FillRect(x, y, w, h, ' ') + } + f.DrawChildren() return } - x, y := f.Pos() - w, h := f.Size() - - fg, bg := RealColor(f.fg, ColorViewText), RealColor(f.bg, ColorViewBack) - SetTextColor(fg) SetBackColor(bg) DrawFrame(x, y, w, h, f.border) @@ -92,3 +155,48 @@ func (f *Frame) Draw() { f.DrawChildren() } + +func (f *Frame) ProcessEvent(ev Event) bool { + if ev.Type != EventActivateChild || (!f.scrollable || ev.Target == nil) { + return false + } + + x, y := f.Pos() + px, py := f.Paddings() + + cx, cy, cw, ch := f.Clipper() + + tw, th := ev.Target.Size() + tx, ty := ev.Target.Pos() + + if ControlInRect(ev.Target, cx, cy, cw, ch) { + return false + } + + xx := x + yy := y + + if (ty+th)-(py/2) > cy+ch { + delta := (ty + th) - (cy + ch) + yy = y - delta + } else if ty < cy { + delta := cy - ty + yy = y + delta + } + + if (tx+tw)-(px/2) > cx+cw { + delta := (tx + tw) - (cx + cw) + xx = (x - delta) + } else if tx < cx { + delta := cx - tx + xx = x + delta + } + + f.x = xx + f.y = yy + + f.ResizeChildren() + f.PlaceChildren() + + return false +} diff --git a/vendor/github.com/VladimirMarkelov/clui/label.go b/vendor/github.com/VladimirMarkelov/clui/label.go index 3bc88ec..ff2fbc4 100644 --- a/vendor/github.com/VladimirMarkelov/clui/label.go +++ b/vendor/github.com/VladimirMarkelov/clui/label.go @@ -73,9 +73,9 @@ func (l *Label) Draw() { PushAttributes() defer PopAttributes() - fg, bg := RealColor(l.fg, ColorText), RealColor(l.bg, ColorBack) + fg, bg := RealColor(l.fg, l.Style(), ColorText), RealColor(l.bg, l.Style(), ColorBack) if !l.Enabled() { - fg = RealColor(l.fg, ColorDisabledText) + fg = RealColor(l.fg, l.Style(), ColorDisabledText) } SetTextColor(fg) diff --git a/vendor/github.com/VladimirMarkelov/clui/listbox.go b/vendor/github.com/VladimirMarkelov/clui/listbox.go index f0b5048..e84fb35 100644 --- a/vendor/github.com/VladimirMarkelov/clui/listbox.go +++ b/vendor/github.com/VladimirMarkelov/clui/listbox.go @@ -87,11 +87,11 @@ func (l *ListBox) drawItems() { maxDy := l.height - 1 maxWidth := l.width - 1 - fg, bg := RealColor(l.fg, ColorEditText), RealColor(l.bg, ColorEditBack) + fg, bg := RealColor(l.fg, l.Style(), ColorEditText), RealColor(l.bg, l.Style(), ColorEditBack) if l.Active() { - fg, bg = RealColor(l.fg, ColorEditActiveText), RealColor(l.bg, ColorEditActiveBack) + fg, bg = RealColor(l.fg, l.Style(), ColorEditActiveText), RealColor(l.bg, l.Style(), ColorEditActiveBack) } - fgSel, bgSel := RealColor(l.fgActive, ColorSelectionText), RealColor(l.bgActive, ColorSelectionBack) + fgSel, bgSel := RealColor(l.fgActive, l.Style(), ColorSelectionText), RealColor(l.bgActive, l.Style(), ColorSelectionBack) for curr <= maxCurr && dy <= maxDy { f, b := fg, bg @@ -122,9 +122,9 @@ func (l *ListBox) Draw() { x, y := l.Pos() w, h := l.Size() - fg, bg := RealColor(l.fg, ColorEditText), RealColor(l.bg, ColorEditBack) + fg, bg := RealColor(l.fg, l.Style(), ColorEditText), RealColor(l.bg, l.Style(), ColorEditBack) if l.Active() { - fg, bg = RealColor(l.fg, ColorEditActiveText), RealColor(l.bg, ColorEditActiveBack) + fg, bg = RealColor(l.fg, l.Style(), ColorEditActiveText), RealColor(l.bg, l.Style(), ColorEditActiveBack) } SetTextColor(fg) SetBackColor(bg) diff --git a/vendor/github.com/VladimirMarkelov/clui/progressbar.go b/vendor/github.com/VladimirMarkelov/clui/progressbar.go index 823ab3d..dc7c19b 100644 --- a/vendor/github.com/VladimirMarkelov/clui/progressbar.go +++ b/vendor/github.com/VladimirMarkelov/clui/progressbar.go @@ -85,8 +85,8 @@ func (b *ProgressBar) Draw() { PushAttributes() defer PopAttributes() - fgOff, fgOn := RealColor(b.fg, ColorProgressText), RealColor(b.fgActive, ColorProgressActiveText) - bgOff, bgOn := RealColor(b.bg, ColorProgressBack), RealColor(b.bgActive, ColorProgressActiveBack) + fgOff, fgOn := RealColor(b.fg, b.Style(), ColorProgressText), RealColor(b.fgActive, b.Style(), ColorProgressActiveText) + bgOff, bgOn := RealColor(b.bg, b.Style(), ColorProgressBack), RealColor(b.bgActive, b.Style(), ColorProgressActiveBack) parts := []rune(SysObject(ObjProgressBar)) cFilled, cEmpty := parts[0], parts[1] @@ -126,7 +126,7 @@ func (b *ProgressBar) Draw() { if title != "" { shift, str := AlignText(title, w, b.align) - titleClr := RealColor(b.titleFg, ColorProgressTitleText) + titleClr := RealColor(b.titleFg, b.Style(), ColorProgressTitleText) var sOn, sOff string if filled == 0 || shift >= filled { sOff = str diff --git a/vendor/github.com/VladimirMarkelov/clui/radio.go b/vendor/github.com/VladimirMarkelov/clui/radio.go index 32fed29..a177001 100644 --- a/vendor/github.com/VladimirMarkelov/clui/radio.go +++ b/vendor/github.com/VladimirMarkelov/clui/radio.go @@ -59,11 +59,11 @@ func (c *Radio) Draw() { x, y := c.Pos() w, h := c.Size() - fg, bg := RealColor(c.fg, ColorControlText), RealColor(c.bg, ColorControlBack) + fg, bg := RealColor(c.fg, c.Style(), ColorControlText), RealColor(c.bg, c.Style(), ColorControlBack) if !c.Enabled() { - fg, bg = RealColor(c.fg, ColorControlDisabledText), RealColor(c.bg, ColorControlDisabledBack) + fg, bg = RealColor(c.fg, c.Style(), ColorControlDisabledText), RealColor(c.bg, c.Style(), ColorControlDisabledBack) } else if c.Active() { - fg, bg = RealColor(c.fg, ColorControlActiveText), RealColor(c.bg, ColorControlActiveBack) + fg, bg = RealColor(c.fg, c.Style(), ColorControlActiveText), RealColor(c.bg, c.Style(), ColorControlActiveBack) } parts := []rune(SysObject(ObjRadio)) diff --git a/vendor/github.com/VladimirMarkelov/clui/sparkchart.go b/vendor/github.com/VladimirMarkelov/clui/sparkchart.go index 4887039..94a9285 100644 --- a/vendor/github.com/VladimirMarkelov/clui/sparkchart.go +++ b/vendor/github.com/VladimirMarkelov/clui/sparkchart.go @@ -83,7 +83,7 @@ func (b *SparkChart) Draw() { PushAttributes() defer PopAttributes() - fg, bg := RealColor(b.fg, ColorSparkChartText), RealColor(b.bg, ColorSparkChartBack) + fg, bg := RealColor(b.fg, b.Style(), ColorSparkChartText), RealColor(b.bg, b.Style(), ColorSparkChartBack) SetTextColor(fg) SetBackColor(bg) FillRect(b.x, b.y, b.width, b.height, ' ') @@ -117,8 +117,8 @@ func (b *SparkChart) drawBars() { h := b.height pos := b.x + start - mxFg, mxBg := RealColor(b.maxFg, ColorSparkChartMaxText), RealColor(b.maxBg, ColorSparkChartMaxBack) - brFg, brBg := RealColor(b.fg, ColorSparkChartBarText), RealColor(b.bg, ColorSparkChartBarBack) + mxFg, mxBg := RealColor(b.maxFg, b.Style(), ColorSparkChartMaxText), RealColor(b.maxBg, b.Style(), ColorSparkChartMaxBack) + brFg, brBg := RealColor(b.fg, b.Style(), ColorSparkChartBarText), RealColor(b.bg, b.Style(), ColorSparkChartBarBack) parts := []rune(SysObject(ObjSparkChart)) var dt []float64 diff --git a/vendor/github.com/VladimirMarkelov/clui/tableview.go b/vendor/github.com/VladimirMarkelov/clui/tableview.go index f75e603..019caf5 100644 --- a/vendor/github.com/VladimirMarkelov/clui/tableview.go +++ b/vendor/github.com/VladimirMarkelov/clui/tableview.go @@ -164,8 +164,8 @@ func (l *TableView) drawHeader() { PushAttributes() defer PopAttributes() - fg, bg := RealColor(l.fg, ColorTableHeaderText), RealColor(l.bg, ColorTableHeaderBack) - fgLine := RealColor(l.fg, ColorTableLineText) + fg, bg := RealColor(l.fg, l.Style(), ColorTableHeaderText), RealColor(l.bg, l.Style(), ColorTableHeaderBack) + fgLine := RealColor(l.fg, l.Style(), ColorTableLineText) x, y := l.Pos() w, _ := l.Size() SetTextColor(fg) @@ -269,10 +269,10 @@ func (l *TableView) drawCells() { dy := 2 maxDy := l.height - 2 - fg, bg := RealColor(l.fg, ColorTableText), RealColor(l.bg, ColorTableBack) - fgRow, bgRow := RealColor(l.fg, ColorTableSelectedText), RealColor(l.bg, ColorTableSelectedBack) - fgCell, bgCell := RealColor(l.fg, ColorTableActiveCellText), RealColor(l.bg, ColorTableActiveCellBack) - fgLine := RealColor(l.fg, ColorTableLineText) + fg, bg := RealColor(l.fg, l.Style(), ColorTableText), RealColor(l.bg, l.Style(), ColorTableBack) + fgRow, bgRow := RealColor(l.fg, l.Style(), ColorTableSelectedText), RealColor(l.bg, l.Style(), ColorTableSelectedBack) + fgCell, bgCell := RealColor(l.fg, l.Style(), ColorTableActiveCellText), RealColor(l.bg, l.Style(), ColorTableActiveCellBack) + fgLine := RealColor(l.fg, l.Style(), ColorTableLineText) parts := []rune(SysObject(ObjTableView)) start := 0 @@ -361,7 +361,7 @@ func (l *TableView) Draw() { x, y := l.Pos() w, h := l.Size() - bg := RealColor(l.bg, ColorTableBack) + bg := RealColor(l.bg, l.Style(), ColorTableBack) SetBackColor(bg) FillRect(x, y+2, w, h-2, ' ') l.drawHeader() diff --git a/vendor/github.com/VladimirMarkelov/clui/textreader.go b/vendor/github.com/VladimirMarkelov/clui/textreader.go index e01ad32..a56822b 100644 --- a/vendor/github.com/VladimirMarkelov/clui/textreader.go +++ b/vendor/github.com/VladimirMarkelov/clui/textreader.go @@ -50,9 +50,9 @@ func (l *TextReader) drawText() { PushAttributes() defer PopAttributes() - bg, fg := RealColor(l.bg, ColorEditBack), RealColor(l.fg, ColorEditText) + bg, fg := RealColor(l.bg, l.Style(), ColorEditBack), RealColor(l.fg, l.Style(), ColorEditText) if l.Active() { - bg, fg = RealColor(l.bg, ColorEditActiveBack), RealColor(l.fg, ColorEditActiveText) + bg, fg = RealColor(l.bg, l.Style(), ColorEditActiveBack), RealColor(l.fg, l.Style(), ColorEditActiveText) } SetTextColor(fg) SetBackColor(bg) @@ -91,9 +91,9 @@ func (l *TextReader) Draw() { x, y := l.Pos() w, h := l.Size() - bg, fg := RealColor(l.bg, ColorEditBack), RealColor(l.fg, ColorEditText) + bg, fg := RealColor(l.bg, l.Style(), ColorEditBack), RealColor(l.fg, l.Style(), ColorEditText) if l.Active() { - bg, fg = RealColor(l.bg, ColorEditActiveBack), RealColor(l.fg, ColorEditActiveText) + bg, fg = RealColor(l.bg, l.Style(), ColorEditActiveBack), RealColor(l.fg, l.Style(), ColorEditActiveText) } SetTextColor(fg) diff --git a/vendor/github.com/VladimirMarkelov/clui/textview.go b/vendor/github.com/VladimirMarkelov/clui/textview.go index f4bd52c..5b8dd06 100644 --- a/vendor/github.com/VladimirMarkelov/clui/textview.go +++ b/vendor/github.com/VladimirMarkelov/clui/textview.go @@ -177,9 +177,9 @@ func (l *TextView) Draw() { x, y := l.Pos() w, h := l.Size() - bg, fg := RealColor(l.bg, ColorEditBack), RealColor(l.fg, ColorEditText) + bg, fg := RealColor(l.bg, l.Style(), ColorEditBack), RealColor(l.fg, l.Style(), ColorEditText) if l.Active() { - bg, fg = RealColor(l.bg, ColorEditActiveBack), RealColor(l.fg, ColorEditActiveText) + bg, fg = RealColor(l.bg, l.Style(), ColorEditActiveBack), RealColor(l.fg, l.Style(), ColorEditActiveText) } SetTextColor(fg) diff --git a/vendor/github.com/VladimirMarkelov/clui/theme.go b/vendor/github.com/VladimirMarkelov/clui/theme.go index 995937d..651a46a 100644 --- a/vendor/github.com/VladimirMarkelov/clui/theme.go +++ b/vendor/github.com/VladimirMarkelov/clui/theme.go @@ -508,15 +508,36 @@ func ThemeInfo(name string) ThemeDesc { // Attribute selection work this way: if color is not ColorDefault, // it is returned as is, otherwise the function tries to load // color from the theme. +// +// With the style argument themes may be grouped by control, i.e +// an application may have multiple list controls where they all share +// the same theme attributes however the same application may have +// one specific list control with some different theme attributes, +// in that case the user may call control.SetStyle("custom") and define +// a set of custom.* attributes, i.e: +// +// custom.EditBox = white +// custom.EditText = black bold +// ... +// // clr - current object color +// style - the theme prefix style set // id - color ID in theme -func RealColor(clr term.Attribute, id string) term.Attribute { +func RealColor(clr term.Attribute, style string, id string) term.Attribute { + var prefix string + + if style != "" { + prefix = fmt.Sprintf("%s.", style) + } + + ccolor := fmt.Sprintf("%s%s", prefix, id) + if clr == ColorDefault { - clr = SysColor(id) + clr = SysColor(ccolor) } if clr == ColorDefault { - panic("Failed to load color value for " + id) + panic("Failed to load color value for " + ccolor) } return clr diff --git a/vendor/github.com/VladimirMarkelov/clui/window.go b/vendor/github.com/VladimirMarkelov/clui/window.go index d8e108a..87bc28e 100644 --- a/vendor/github.com/VladimirMarkelov/clui/window.go +++ b/vendor/github.com/VladimirMarkelov/clui/window.go @@ -21,8 +21,14 @@ type Window struct { fixedSize bool onClose func(Event) bool - onKeyDown func(Event) bool onScreenResize func(Event) + + onKeyDown *keyDownCb +} + +type keyDownCb struct { + data interface{} + fn func(evt Event, data interface{}) bool } func CreateWindow(x, y, w, h int, title string) *Window { @@ -133,7 +139,7 @@ func (wnd *Window) Draw() { PushAttributes() defer PopAttributes() - fg, bg := RealColor(wnd.fg, ColorViewText), RealColor(wnd.bg, ColorViewBack) + fg, bg := RealColor(wnd.fg, wnd.Style(), ColorViewText), RealColor(wnd.bg, wnd.Style(), ColorViewBack) SetBackColor(bg) FillRect(wnd.x, wnd.y, wnd.width, wnd.height, ' ') @@ -233,6 +239,24 @@ func (c *Window) ProcessEvent(ev Event) bool { aC := ActiveControl(c) nC := NextControl(c, aC, ev.Key != term.KeyArrowUp) + + var clipped Control + + if aC != nil && aC.Clipped() { + clipped = aC + } else { + clipped = ClippedParent(nC) + } + + if clipped != nil { + dir := 1 + if ev.Key != term.KeyArrowUp { + dir = -1 + } + + clipped.ProcessEvent(Event{Type: EventActivateChild, Target: nC, X: dir}) + } + if nC != aC { if aC != nil { aC.SetActive(false) @@ -249,7 +273,7 @@ func (c *Window) ProcessEvent(ev Event) bool { return true } if c.onKeyDown != nil { - return c.onKeyDown(ev) + return c.onKeyDown.fn(ev, c.onKeyDown.data) } return false } @@ -270,8 +294,12 @@ func (w *Window) OnClose(fn func(Event) bool) { // OnKeyDown sets the callback that is called when a user presses a key // while the Window is active -func (w *Window) OnKeyDown(fn func(Event) bool) { - w.onKeyDown = fn +func (w *Window) OnKeyDown(fn func(Event, interface{}) bool, data interface{}) { + if fn == nil { + w.onKeyDown = nil + } else { + w.onKeyDown = &keyDownCb{data: data, fn: fn} + } } // OnScreenResize sets the callback that is called when size of terminal changes diff --git a/vendor/github.com/huandu/xstrings/README.md b/vendor/github.com/huandu/xstrings/README.md index c824a5c..a4a36f4 100644 --- a/vendor/github.com/huandu/xstrings/README.md +++ b/vendor/github.com/huandu/xstrings/README.md @@ -55,6 +55,7 @@ Here is a list of functions in [strings](http://golang.org/pkg/strings) and [xst | [Successor](https://godoc.org/github.com/huandu/xstrings#Successor) | `String#succ` or `String#next` in Ruby | [#22](https://github.com/huandu/xstrings/issues/22) | | [SwapCase](https://godoc.org/github.com/huandu/xstrings#SwapCase) | `str.swapcase` in Python; `String#swapcase` in Ruby | [#12](https://github.com/huandu/xstrings/issues/12) | | [ToCamelCase](https://godoc.org/github.com/huandu/xstrings#ToCamelCase) | `String#camelize` in RoR | [#1](https://github.com/huandu/xstrings/issues/1) | +| [ToKebab](https://godoc.org/github.com/huandu/xstrings#ToKebabCase) | - | [#41](https://github.com/huandu/xstrings/issues/41) | | [ToSnakeCase](https://godoc.org/github.com/huandu/xstrings#ToSnakeCase) | `String#underscore` in RoR | [#1](https://github.com/huandu/xstrings/issues/1) | | [Translate](https://godoc.org/github.com/huandu/xstrings#Translate) | `str.translate` in Python; `String#tr` in Ruby; `strtr` in PHP; `tr///` in Perl | [#21](https://github.com/huandu/xstrings/issues/21) | | [Width](https://godoc.org/github.com/huandu/xstrings#Width) | `mb_strwidth` in PHP | [#26](https://github.com/huandu/xstrings/issues/26) | diff --git a/vendor/github.com/huandu/xstrings/convert.go b/vendor/github.com/huandu/xstrings/convert.go index 3399a05..8253fa9 100644 --- a/vendor/github.com/huandu/xstrings/convert.go +++ b/vendor/github.com/huandu/xstrings/convert.go @@ -72,7 +72,7 @@ func ToCamelCase(str string) string { } // ToSnakeCase can convert all upper case characters in a string to -// underscore format. +// snake case format. // // Some samples. // "FirstName" => "first_name" @@ -85,6 +85,27 @@ func ToCamelCase(str string) string { // "http2xx" => "http_2xx" // "HTTP20xOK" => "http_20x_ok" func ToSnakeCase(str string) string { + return camelCaseToLowerCase(str, '_') +} + +// ToKebabCase can convert all upper case characters in a string to +// kebab case format. +// +// Some samples. +// "FirstName" => "first-name" +// "HTTPServer" => "http-server" +// "NoHTTPS" => "no-https" +// "GO_PATH" => "go-path" +// "GO PATH" => "go-path" // space is converted to '-'. +// "GO-PATH" => "go-path" // hyphen is converted to '-'. +// "HTTP2XX" => "http-2xx" // insert a '-' before a number and after an alphabet. +// "http2xx" => "http-2xx" +// "HTTP20xOK" => "http-20x-ok" +func ToKebabCase(str string) string { + return camelCaseToLowerCase(str, '-') +} + +func camelCaseToLowerCase(str string, connector rune) string { if len(str) == 0 { return "" } @@ -93,7 +114,7 @@ func ToSnakeCase(str string) string { var prev, r0, r1 rune var size int - r0 = '_' + r0 = connector for len(str) > 0 { prev = r0 @@ -102,11 +123,11 @@ func ToSnakeCase(str string) string { switch { case r0 == utf8.RuneError: - buf.WriteByte(byte(str[0])) + buf.WriteRune(r0) case unicode.IsUpper(r0): - if prev != '_' && !unicode.IsNumber(prev) { - buf.WriteRune('_') + if prev != connector && !unicode.IsNumber(prev) { + buf.WriteRune(connector) } buf.WriteRune(unicode.ToLower(r0)) @@ -123,7 +144,7 @@ func ToSnakeCase(str string) string { break } - // find next non-upper-case character and insert `_` properly. + // find next non-upper-case character and insert connector properly. // it's designed to convert `HTTPServer` to `http_server`. // if there are more than 2 adjacent upper case characters in a word, // treat them as an abbreviation plus a normal word. @@ -134,23 +155,23 @@ func ToSnakeCase(str string) string { if r0 == utf8.RuneError { buf.WriteRune(unicode.ToLower(r1)) - buf.WriteByte(byte(str[0])) + buf.WriteRune(r0) break } if !unicode.IsUpper(r0) { if r0 == '_' || r0 == ' ' || r0 == '-' { - r0 = '_' + r0 = connector buf.WriteRune(unicode.ToLower(r1)) } else if unicode.IsNumber(r0) { // treat a number as an upper case rune // so that both `http2xx` and `HTTP2XX` can be converted to `http_2xx`. buf.WriteRune(unicode.ToLower(r1)) - buf.WriteRune('_') + buf.WriteRune(connector) buf.WriteRune(r0) } else { - buf.WriteRune('_') + buf.WriteRune(connector) buf.WriteRune(unicode.ToLower(r1)) buf.WriteRune(r0) } @@ -161,20 +182,20 @@ func ToSnakeCase(str string) string { buf.WriteRune(unicode.ToLower(r1)) } - if len(str) == 0 || r0 == '_' { + if len(str) == 0 || r0 == connector { buf.WriteRune(unicode.ToLower(r0)) } case unicode.IsNumber(r0): - if prev != '_' && !unicode.IsNumber(prev) { - buf.WriteRune('_') + if prev != connector && !unicode.IsNumber(prev) { + buf.WriteRune(connector) } buf.WriteRune(r0) default: - if r0 == ' ' || r0 == '-' { - r0 = '_' + if r0 == ' ' || r0 == '-' || r0 == '_' { + r0 = connector } buf.WriteRune(r0)