mirror of
https://github.com/VladimirMarkelov/clui.git
synced 2025-04-26 13:49:01 +08:00
closes #38 - only vital panic calls are remained
This commit is contained in:
parent
2b10c92a2f
commit
a225b5047b
25
canvas.go
25
canvas.go
@ -1,7 +1,6 @@
|
|||||||
package clui
|
package clui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
xs "github.com/huandu/xstrings"
|
xs "github.com/huandu/xstrings"
|
||||||
term "github.com/nsf/termbox-go"
|
term "github.com/nsf/termbox-go"
|
||||||
)
|
)
|
||||||
@ -18,8 +17,11 @@ type FrameBuffer struct {
|
|||||||
|
|
||||||
// NewFrameBuffer creates new buffer. Width and height of a new buffer cannot be less than 3
|
// NewFrameBuffer creates new buffer. Width and height of a new buffer cannot be less than 3
|
||||||
func NewFrameBuffer(w, h int) *FrameBuffer {
|
func NewFrameBuffer(w, h int) *FrameBuffer {
|
||||||
if w < 3 || h < 3 {
|
if w < 3 {
|
||||||
panic(fmt.Sprintf("Invalid size: %vx%v.", w, h))
|
w = 3
|
||||||
|
}
|
||||||
|
if h < 3 {
|
||||||
|
h = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
c := new(FrameBuffer)
|
c := new(FrameBuffer)
|
||||||
@ -39,12 +41,15 @@ FrameBuffer is recreated and cleared with default colors. Both FrameBuffer width
|
|||||||
height must be greater than 2
|
height must be greater than 2
|
||||||
*/
|
*/
|
||||||
func (fb *FrameBuffer) SetSize(w, h int) {
|
func (fb *FrameBuffer) SetSize(w, h int) {
|
||||||
if w == fb.w && h == fb.h {
|
if w < 3 {
|
||||||
return
|
w = 3
|
||||||
|
}
|
||||||
|
if h < 3 {
|
||||||
|
h = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
if w < 3 || h < 3 {
|
if w == fb.w && h == fb.h {
|
||||||
panic(fmt.Sprintf("Invalid size: %vx%v.", w, h))
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fb.w, fb.h = w, h
|
fb.w, fb.h = w, h
|
||||||
@ -223,15 +228,11 @@ func (fb *FrameBuffer) DrawFrame(x, y, w, h int, fg, bg term.Attribute, frameCha
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if frameChars == "" {
|
if xs.Len(frameChars) < 6 {
|
||||||
frameChars = "─│┌┐└┘"
|
frameChars = "─│┌┐└┘"
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := []rune(frameChars)
|
parts := []rune(frameChars)
|
||||||
if len(parts) < 6 {
|
|
||||||
panic("Invalid theme: single border")
|
|
||||||
}
|
|
||||||
|
|
||||||
H, V, UL, UR, DL, DR := parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]
|
H, V, UL, UR, DL, DR := parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]
|
||||||
|
|
||||||
if h == 1 {
|
if h == 1 {
|
||||||
|
16
composer.go
16
composer.go
@ -36,11 +36,12 @@ func (c *Composer) initBuffer() {
|
|||||||
c.canvas.Clear(ColorBlack)
|
c.canvas.Clear(ColorBlack)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitLibrary initializes library and starts console management
|
// InitLibrary initializes library and starts console management.
|
||||||
|
// Retuns nil in case of error
|
||||||
func InitLibrary() *Composer {
|
func InitLibrary() *Composer {
|
||||||
err := term.Init()
|
err := term.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c := new(Composer)
|
c := new(Composer)
|
||||||
@ -128,13 +129,13 @@ func (c *Composer) checkWindowUnderMouse(screenX, screenY int) (View, HitResult)
|
|||||||
return nil, HitOutside
|
return nil, HitOutside
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Composer) activateView(view View) {
|
func (c *Composer) activateView(view View) bool {
|
||||||
if c.topView() == view {
|
if c.topView() == view {
|
||||||
for _, v := range c.views {
|
for _, v := range c.views {
|
||||||
v.SetActive(false)
|
v.SetActive(false)
|
||||||
}
|
}
|
||||||
view.SetActive(true)
|
view.SetActive(true)
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
var wList []View
|
var wList []View
|
||||||
@ -150,11 +151,12 @@ func (c *Composer) activateView(view View) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
panic("Invalid view to activate")
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
view.SetActive(true)
|
view.SetActive(true)
|
||||||
c.views = append(wList, view)
|
c.views = append(wList, view)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Composer) moveActiveWindowToBottom() bool {
|
func (c *Composer) moveActiveWindowToBottom() bool {
|
||||||
@ -176,7 +178,9 @@ func (c *Composer) moveActiveWindowToBottom() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.views[0] = last
|
c.views[0] = last
|
||||||
c.activateView(c.topView())
|
if !c.activateView(c.topView()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
event = Event{Type: EventActivate, X: 1} // send 'activated'
|
event = Event{Type: EventActivate, X: 1} // send 'activated'
|
||||||
c.sendEventToActiveView(event)
|
c.sendEventToActiveView(event)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package clui
|
package clui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
term "github.com/nsf/termbox-go"
|
term "github.com/nsf/termbox-go"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
@ -46,13 +45,22 @@ func (c *ControlBase) Size() (int, int) {
|
|||||||
// SetSize changes control size. Constant DoNotChange can be
|
// SetSize changes control size. Constant DoNotChange can be
|
||||||
// used as placeholder to indicate that the control attrubute
|
// used as placeholder to indicate that the control attrubute
|
||||||
// should be unchanged.
|
// should be unchanged.
|
||||||
// Method panics if new size is less than minimal size
|
|
||||||
func (c *ControlBase) SetSize(width, height int) {
|
func (c *ControlBase) SetSize(width, height int) {
|
||||||
if width != DoNotChange && (width > 1000 || width < c.minW) {
|
if width != DoNotChange {
|
||||||
panic(fmt.Sprintf("Invalid width: %v", width))
|
if width > 1000 {
|
||||||
|
width = 1000
|
||||||
|
}
|
||||||
|
if width < c.minW {
|
||||||
|
width = c.minW
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if height != DoNotChange && (height > 200 || height < c.minH) {
|
if height != DoNotChange {
|
||||||
panic(fmt.Sprintf("Invalid height: %v", height))
|
if height > 200 {
|
||||||
|
height = 200
|
||||||
|
}
|
||||||
|
if height < c.minH {
|
||||||
|
height = c.minH
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if width != DoNotChange {
|
if width != DoNotChange {
|
||||||
|
@ -151,11 +151,13 @@ func (d *ConfirmationDialog) Result() int {
|
|||||||
// selectedItem is the index of the item that is selected after
|
// selectedItem is the index of the item that is selected after
|
||||||
// the dialog is created
|
// the dialog is created
|
||||||
// typ is a selection type: ListBox or RadioGroup
|
// typ is a selection type: ListBox or RadioGroup
|
||||||
|
// Returns nil in case of creation process fails, e.g, if item list is empty
|
||||||
func NewSelectDialog(c *Composer, title string, items []string, selectedItem int, typ SelectDialogType) *SelectDialog {
|
func NewSelectDialog(c *Composer, title string, items []string, selectedItem int, typ SelectDialogType) *SelectDialog {
|
||||||
dlg := new(SelectDialog)
|
dlg := new(SelectDialog)
|
||||||
|
|
||||||
if len(items) == 0 {
|
if len(items) == 0 {
|
||||||
panic("Item list must contain at least 1 item")
|
// Item list must contain at least 1 item
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cw, ch := term.Size()
|
cw, ch := term.Size()
|
||||||
|
8
frame.go
8
frame.go
@ -119,7 +119,8 @@ func (f *Frame) RecalculateConstraints() {
|
|||||||
// its minimal size
|
// its minimal size
|
||||||
func (f *Frame) AddChild(c Control, scale int) {
|
func (f *Frame) AddChild(c Control, scale int) {
|
||||||
if f.view.ChildExists(c) {
|
if f.view.ChildExists(c) {
|
||||||
panic("Frame: Cannot add the same control twice")
|
// Frame: Cannot add the same control twice
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.SetScale(scale)
|
c.SetScale(scale)
|
||||||
@ -135,10 +136,11 @@ func (f *Frame) Children() []Control {
|
|||||||
|
|
||||||
// SetPack changes the direction of children packing.
|
// SetPack changes the direction of children packing.
|
||||||
// Changing pack type on the fly is not always possible:
|
// Changing pack type on the fly is not always possible:
|
||||||
// it panics if a frame already contains children
|
// it does nothing if a frame already contains children
|
||||||
func (f *Frame) SetPack(pk PackType) {
|
func (f *Frame) SetPack(pk PackType) {
|
||||||
if len(f.children) > 0 {
|
if len(f.children) > 0 {
|
||||||
panic("Control already has children")
|
// Control already has children
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
f.pack = pk
|
f.pack = pk
|
||||||
|
@ -104,7 +104,6 @@ type View interface {
|
|||||||
// SetSize changes control size. Constant DoNotChange can be
|
// SetSize changes control size. Constant DoNotChange can be
|
||||||
// used as placeholder to indicate that the control attrubute
|
// used as placeholder to indicate that the control attrubute
|
||||||
// should be unchanged.
|
// should be unchanged.
|
||||||
// Method panics if new size is less than minimal size
|
|
||||||
SetSize(int, int)
|
SetSize(int, int)
|
||||||
// Pos returns the current control position: X and Y.
|
// Pos returns the current control position: X and Y.
|
||||||
// For View the position's origin is top left corner of console window,
|
// For View the position's origin is top left corner of console window,
|
||||||
@ -173,7 +172,7 @@ type View interface {
|
|||||||
SetPaddings(int, int, int, int)
|
SetPaddings(int, int, int, int)
|
||||||
// AddChild add control to a list of view children. Minimal size
|
// AddChild add control to a list of view children. Minimal size
|
||||||
// of the view calculated as a sum of sizes of its children.
|
// of the view calculated as a sum of sizes of its children.
|
||||||
// Method panics if the same control is added twice
|
// Method does nothing if the control is already added
|
||||||
AddChild(Control, int)
|
AddChild(Control, int)
|
||||||
// SetPack changes the direction of children packing
|
// SetPack changes the direction of children packing
|
||||||
SetPack(PackType)
|
SetPack(PackType)
|
||||||
@ -251,7 +250,6 @@ type Control interface {
|
|||||||
// SetSize changes control size. Constant DoNotChange can be
|
// SetSize changes control size. Constant DoNotChange can be
|
||||||
// used as placeholder to indicate that the control attrubute
|
// used as placeholder to indicate that the control attrubute
|
||||||
// should be unchanged.
|
// should be unchanged.
|
||||||
// Method panics if new size is less than minimal size
|
|
||||||
SetSize(int, int)
|
SetSize(int, int)
|
||||||
// Scale return scale coefficient that is used to calculate
|
// Scale return scale coefficient that is used to calculate
|
||||||
// new control size after its parent resizes.
|
// new control size after its parent resizes.
|
||||||
|
19
theme.go
19
theme.go
@ -40,8 +40,8 @@ Theme file is a simple text file that has similar to INI file format:
|
|||||||
6. Non-system keys are divided into two groups: Colors and Objects
|
6. Non-system keys are divided into two groups: Colors and Objects
|
||||||
Colors are the keys that end with 'Back' or 'Text' - background
|
Colors are the keys that end with 'Back' or 'Text' - background
|
||||||
and text color, respectively. If theme manager cannot
|
and text color, respectively. If theme manager cannot
|
||||||
value to color it panics. See Color*Back * Color*Text constants,
|
value to color it uses black color. See Color*Back * Color*Text
|
||||||
just drop 'Color' at the beginning of key name.
|
constants, just drop 'Color' at the beginning of key name.
|
||||||
Rules of converting text to color:
|
Rules of converting text to color:
|
||||||
1. If the value does not end neither with 'Back' nor with 'Text'
|
1. If the value does not end neither with 'Back' nor with 'Text'
|
||||||
it is considered as raw attribute value(e.g, 'green bold')
|
it is considered as raw attribute value(e.g, 'green bold')
|
||||||
@ -60,9 +60,10 @@ Theme file is a simple text file that has similar to INI file format:
|
|||||||
Better way is:
|
Better way is:
|
||||||
Viewback=parent.ViewText
|
Viewback=parent.ViewText
|
||||||
ViewText=parent.ViewBack
|
ViewText=parent.ViewBack
|
||||||
Converting text to real color panics if a) the string does not look
|
Converting text to real color fails and retuns black color if
|
||||||
like real color(e.g, typo as in 'grean bold'), b) parent theme
|
a) the string does not look like real color(e.g, typo as in
|
||||||
has not loaded yet, c) parent theme does not have the color
|
'grean bold'), b) parent theme has not loaded yet, c) parent
|
||||||
|
theme does not have the color
|
||||||
with the same name
|
with the same name
|
||||||
Other keys are considered as objects - see Obj* constants, just drop
|
Other keys are considered as objects - see Obj* constants, just drop
|
||||||
'Obj' at the beginning of the key name
|
'Obj' at the beginning of the key name
|
||||||
@ -180,7 +181,9 @@ func (s *ThemeManager) Reset() {
|
|||||||
s.themes[defaultTheme] = defTheme
|
s.themes[defaultTheme] = defTheme
|
||||||
}
|
}
|
||||||
|
|
||||||
// SysColor returns attribute by its id for the current theme
|
// SysColor returns attribute by its id for the current theme.
|
||||||
|
// The method panics if theme loop is detected - check if
|
||||||
|
// parent attribute is correct
|
||||||
func (s *ThemeManager) SysColor(color string) term.Attribute {
|
func (s *ThemeManager) SysColor(color string) term.Attribute {
|
||||||
sch, ok := s.themes[s.current]
|
sch, ok := s.themes[s.current]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -216,7 +219,9 @@ func (s *ThemeManager) SysColor(color string) term.Attribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SysObject returns object look by its id for the current
|
// SysObject returns object look by its id for the current
|
||||||
// theme. E.g, border lines for frame or arrows for scrollbar
|
// theme. E.g, border lines for frame or arrows for scrollbar.
|
||||||
|
// The method panics if theme loop is detected - check if
|
||||||
|
// parent attribute is correct
|
||||||
func (s *ThemeManager) SysObject(object string) string {
|
func (s *ThemeManager) SysObject(object string) string {
|
||||||
sch, ok := s.themes[s.current]
|
sch, ok := s.themes[s.current]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
37
window.go
37
window.go
@ -1,7 +1,6 @@
|
|||||||
package clui
|
package clui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
term "github.com/nsf/termbox-go"
|
term "github.com/nsf/termbox-go"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
@ -62,18 +61,27 @@ func NewWindow(parent Screen, x, y, w, h int, title string) *Window {
|
|||||||
// SetSize changes control size. Constant DoNotChange can be
|
// SetSize changes control size. Constant DoNotChange can be
|
||||||
// used as placeholder to indicate that the control attrubute
|
// used as placeholder to indicate that the control attrubute
|
||||||
// should be unchanged.
|
// should be unchanged.
|
||||||
// Method panics if new size is less than minimal size.
|
|
||||||
// View automatically recalculates position and size of its children after changing its size
|
// View automatically recalculates position and size of its children after changing its size
|
||||||
func (w *Window) SetSize(width, height int) {
|
func (w *Window) SetSize(width, height int) {
|
||||||
if width == w.width && height == w.height {
|
if width == w.width && height == w.height {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if width != DoNotChange && (width > 1000 || width < w.minW) {
|
if width != DoNotChange {
|
||||||
panic(fmt.Sprintf("Invalid width: %v", width))
|
if width > 1000 {
|
||||||
|
width = 1000
|
||||||
|
}
|
||||||
|
if width < w.minW {
|
||||||
|
width = w.minW
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if height != DoNotChange && (height > 200 || height < w.minH) {
|
if height != DoNotChange {
|
||||||
panic(fmt.Sprintf("Invalid height: %v", height))
|
if height > 200 {
|
||||||
|
height = 200
|
||||||
|
}
|
||||||
|
if height < w.minH {
|
||||||
|
height = w.minH
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if width != DoNotChange {
|
if width != DoNotChange {
|
||||||
@ -120,16 +128,14 @@ func (w *Window) SetConstraints(width, height int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw paints the view screen buffer to a canvas. It does not
|
// Draw paints the view screen buffer to a canvas. It does not
|
||||||
// repaint all view children
|
// repaint all view children.
|
||||||
|
// Method does nothing if coordinates are outside canvas
|
||||||
func (w *Window) Draw(canvas Canvas) {
|
func (w *Window) Draw(canvas Canvas) {
|
||||||
for y := 0; y < w.height; y++ {
|
for y := 0; y < w.height; y++ {
|
||||||
for x := 0; x < w.width; x++ {
|
for x := 0; x < w.width; x++ {
|
||||||
s, ok := w.canvas.Symbol(x, y)
|
s, ok := w.canvas.Symbol(x, y)
|
||||||
if ok {
|
if ok {
|
||||||
canvas.PutSymbol(x+w.x, y+w.y, s)
|
canvas.PutSymbol(x+w.x, y+w.y, s)
|
||||||
} else {
|
|
||||||
wx, wy := w.Size()
|
|
||||||
panic(fmt.Sprintf("Invalid x, y: %vx%v of %vx%v", x, y, wx, wy))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -242,11 +248,12 @@ func (w *Window) Buttons() ViewButton {
|
|||||||
return w.buttons
|
return w.buttons
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPack changes the direction of children packing. Call the method only before any child is added to view. Otherwise, the method
|
// SetPack changes the direction of children packing. Call the method
|
||||||
// panics if a view already contains children
|
// only before any child is added to view. Otherwise, the method
|
||||||
|
// does nothing
|
||||||
func (w *Window) SetPack(pk PackType) {
|
func (w *Window) SetPack(pk PackType) {
|
||||||
if len(w.children) > 0 {
|
if len(w.children) > 0 {
|
||||||
panic("Control already has children")
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.pack = pk
|
w.pack = pk
|
||||||
@ -289,10 +296,10 @@ func (w *Window) RegisterControl(c Control) {
|
|||||||
|
|
||||||
// AddChild add control to a list of view children. Minimal size
|
// AddChild add control to a list of view children. Minimal size
|
||||||
// of the view calculated as a sum of sizes of its children.
|
// of the view calculated as a sum of sizes of its children.
|
||||||
// Method panics if the same control is added twice
|
// Method does nothing if the control is already added
|
||||||
func (w *Window) AddChild(c Control, scale int) {
|
func (w *Window) AddChild(c Control, scale int) {
|
||||||
if w.ChildExists(c) {
|
if w.ChildExists(c) {
|
||||||
panic("Cannot add the same control twice")
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.SetScale(scale)
|
c.SetScale(scale)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user