closes #38 - only vital panic calls are remained

This commit is contained in:
Vladimir Markelov 2015-12-08 16:25:31 -08:00
parent 2b10c92a2f
commit a225b5047b
8 changed files with 80 additions and 53 deletions

View File

@ -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 {

View File

@ -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)

View File

@ -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 {
if height > 200 {
height = 200
}
if height < c.minH {
height = c.minH
} }
if height != DoNotChange && (height > 200 || height < c.minH) {
panic(fmt.Sprintf("Invalid height: %v", height))
} }
if width != DoNotChange { if width != DoNotChange {

View File

@ -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()

View File

@ -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

View File

@ -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.

View File

@ -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 {

View File

@ -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 {
if height > 200 {
height = 200
}
if height < w.minH {
height = w.minH
} }
if height != DoNotChange && (height > 200 || height < w.minH) {
panic(fmt.Sprintf("Invalid height: %v", height))
} }
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)