// Copyright © 2018 Intel Corporation // // SPDX-License-Identifier: GPL-3.0-only package tui import ( "sync/atomic" "time" "github.com/VladimirMarkelov/clui" xs "github.com/huandu/xstrings" term "github.com/nsf/termbox-go" ) // SimpleButton is the implementation of a clui button with simpler // visual elements (i.e no shadows). type SimpleButton struct { clui.BaseControl autoWidth, autoHeight bool pressed int32 onClick func(clui.Event) forceActiveStyle bool } // CreateSimpleButton returns an instance of SimpleButton // parent - is the parent control this button is attached to // width - the minimum width size // height - the minimum height size // title - the button's label // scale - the amount of space to use whenever used in an adjustable layout func CreateSimpleButton(parent clui.Control, width, height int, title string, scale int) *SimpleButton { b := new(SimpleButton) b.BaseControl = clui.NewBaseControl() b.SetParent(parent) b.SetAlign(clui.AlignCenter) if height == clui.AutoSize { b.autoHeight = true height = 1 } if width == clui.AutoSize { b.autoWidth = true width = xs.Len(title) + 2 } b.SetTitle(title) b.SetSize(width, height) b.SetConstraints(width, height) b.SetScale(scale) if parent != nil { parent.AddChild(b) } return b } // ResizeChildren handles parent window resizing func (b *SimpleButton) ResizeChildren() { if !b.Visible() { return } width, height := b.Size() if b.autoHeight { height = 1 } if b.autoWidth { width = xs.Len(b.Title()) + 2 } b.SetSize(width, height) b.SetConstraints(width, height) } // Draw paints the button in the screen and adjust colors depending on the button state func (b *SimpleButton) Draw() { if !b.Visible() { return } clui.PushAttributes() defer clui.PopAttributes() x, y := b.Pos() w, h := b.Size() fg, bg := b.TextColor(), b.BackColor() if !b.Enabled() { fg = clui.RealColor(fg, b.Style(), "ButtonDisabledText") bg = clui.RealColor(bg, b.Style(), "ButtonDisabledBack") } else if b.Active() || b.ForceActiveStyle() { fgActive, bgActive := b.ActiveColors() fg = clui.RealColor(fgActive, b.Style(), "ButtonActiveText") bg = clui.RealColor(bgActive, b.Style(), "ButtonActiveBack") } else { fg = clui.RealColor(fg, b.Style(), "ButtonText") bg = clui.RealColor(bg, b.Style(), "ButtonBack") } clui.SetTextColor(fg) shift, text := clui.AlignColorizedText(b.Title(), w, b.Align()) px, py := b.Paddings() if b.isPressed() == 0 { clui.SetBackColor(bg) clui.FillRect(x, y, w, h, ' ') clui.DrawText(x+shift+px, y+py, text) } else { clui.SetBackColor(bg) clui.FillRect(x, y, w, h, ' ') clui.DrawText(x+shift+px, y+py, b.Title()) } } func (b *SimpleButton) isPressed() int32 { return atomic.LoadInt32(&b.pressed) } func (b *SimpleButton) setPressed(pressed int32) { atomic.StoreInt32(&b.pressed, pressed) } func (b *SimpleButton) processKeyEvent(event clui.Event) bool { ekey := event.Key if (ekey == term.KeySpace || ekey == term.KeyEnter) && b.isPressed() == 0 { b.setPressed(1) ev := clui.Event{Type: clui.EventRedraw} go func() { clui.PutEvent(ev) time.Sleep(100 * time.Millisecond) b.setPressed(0) clui.PutEvent(ev) }() if b.onClick != nil { b.onClick(event) } return true } else if ekey == term.KeyEsc && b.isPressed() != 0 { b.setPressed(0) clui.ReleaseEvents() return true } return false } func (b *SimpleButton) processMouseEvent(event clui.Event) bool { if event.Key == term.MouseLeft { b.setPressed(1) clui.GrabEvents(b) return true } else if event.Key == term.MouseRelease && b.isPressed() != 0 { clui.ReleaseEvents() bX, bY := b.Pos() bw, bh := b.Size() if event.X >= bX && event.Y >= bY && event.X < bX+bw && event.Y < bY+bh { if b.onClick != nil { b.onClick(event) } } b.setPressed(0) return true } return false } // ProcessEvent will process the events triggered by clui mainloop func (b *SimpleButton) ProcessEvent(event clui.Event) bool { if !b.Enabled() { return false } if event.Type == clui.EventKey { return b.processKeyEvent(event) } else if event.Type == clui.EventMouse { return b.processMouseEvent(event) } return false } // OnClick sets the button's onClick callback func (b *SimpleButton) OnClick(fn func(clui.Event)) { b.onClick = fn } // SetForceActiveStyle even if the button is not active the user may want // to force setting the active style func (b *SimpleButton) SetForceActiveStyle(f bool) { b.forceActiveStyle = f } // ForceActiveStyle returns the forceActiveStyle flag func (b *SimpleButton) ForceActiveStyle() bool { return b.forceActiveStyle }