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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
xs "github.com/huandu/xstrings"
|
||||
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
|
||||
func NewFrameBuffer(w, h int) *FrameBuffer {
|
||||
if w < 3 || h < 3 {
|
||||
panic(fmt.Sprintf("Invalid size: %vx%v.", w, h))
|
||||
if w < 3 {
|
||||
w = 3
|
||||
}
|
||||
if h < 3 {
|
||||
h = 3
|
||||
}
|
||||
|
||||
c := new(FrameBuffer)
|
||||
@ -39,12 +41,15 @@ FrameBuffer is recreated and cleared with default colors. Both FrameBuffer width
|
||||
height must be greater than 2
|
||||
*/
|
||||
func (fb *FrameBuffer) SetSize(w, h int) {
|
||||
if w == fb.w && h == fb.h {
|
||||
return
|
||||
if w < 3 {
|
||||
w = 3
|
||||
}
|
||||
if h < 3 {
|
||||
h = 3
|
||||
}
|
||||
|
||||
if w < 3 || h < 3 {
|
||||
panic(fmt.Sprintf("Invalid size: %vx%v.", w, h))
|
||||
if w == fb.w && h == fb.h {
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if frameChars == "" {
|
||||
if xs.Len(frameChars) < 6 {
|
||||
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]
|
||||
|
||||
if h == 1 {
|
||||
|
16
composer.go
16
composer.go
@ -36,11 +36,12 @@ func (c *Composer) initBuffer() {
|
||||
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 {
|
||||
err := term.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
c := new(Composer)
|
||||
@ -128,13 +129,13 @@ func (c *Composer) checkWindowUnderMouse(screenX, screenY int) (View, HitResult)
|
||||
return nil, HitOutside
|
||||
}
|
||||
|
||||
func (c *Composer) activateView(view View) {
|
||||
func (c *Composer) activateView(view View) bool {
|
||||
if c.topView() == view {
|
||||
for _, v := range c.views {
|
||||
v.SetActive(false)
|
||||
}
|
||||
view.SetActive(true)
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
var wList []View
|
||||
@ -150,11 +151,12 @@ func (c *Composer) activateView(view View) {
|
||||
}
|
||||
|
||||
if !found {
|
||||
panic("Invalid view to activate")
|
||||
return false
|
||||
}
|
||||
|
||||
view.SetActive(true)
|
||||
c.views = append(wList, view)
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Composer) moveActiveWindowToBottom() bool {
|
||||
@ -176,7 +178,9 @@ func (c *Composer) moveActiveWindowToBottom() bool {
|
||||
}
|
||||
|
||||
c.views[0] = last
|
||||
c.activateView(c.topView())
|
||||
if !c.activateView(c.topView()) {
|
||||
return false
|
||||
}
|
||||
|
||||
event = Event{Type: EventActivate, X: 1} // send 'activated'
|
||||
c.sendEventToActiveView(event)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package clui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
term "github.com/nsf/termbox-go"
|
||||
"log"
|
||||
)
|
||||
@ -46,13 +45,22 @@ func (c *ControlBase) Size() (int, int) {
|
||||
// SetSize changes control size. Constant DoNotChange can be
|
||||
// used as placeholder to indicate that the control attrubute
|
||||
// should be unchanged.
|
||||
// Method panics if new size is less than minimal size
|
||||
func (c *ControlBase) SetSize(width, height int) {
|
||||
if width != DoNotChange && (width > 1000 || width < c.minW) {
|
||||
panic(fmt.Sprintf("Invalid width: %v", width))
|
||||
if width != DoNotChange {
|
||||
if width > 1000 {
|
||||
width = 1000
|
||||
}
|
||||
if width < c.minW {
|
||||
width = c.minW
|
||||
}
|
||||
}
|
||||
if height != DoNotChange && (height > 200 || height < c.minH) {
|
||||
panic(fmt.Sprintf("Invalid height: %v", height))
|
||||
if height != DoNotChange {
|
||||
if height > 200 {
|
||||
height = 200
|
||||
}
|
||||
if height < c.minH {
|
||||
height = c.minH
|
||||
}
|
||||
}
|
||||
|
||||
if width != DoNotChange {
|
||||
|
@ -151,11 +151,13 @@ func (d *ConfirmationDialog) Result() int {
|
||||
// selectedItem is the index of the item that is selected after
|
||||
// the dialog is created
|
||||
// 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 {
|
||||
dlg := new(SelectDialog)
|
||||
|
||||
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()
|
||||
|
8
frame.go
8
frame.go
@ -119,7 +119,8 @@ func (f *Frame) RecalculateConstraints() {
|
||||
// its minimal size
|
||||
func (f *Frame) AddChild(c Control, scale int) {
|
||||
if f.view.ChildExists(c) {
|
||||
panic("Frame: Cannot add the same control twice")
|
||||
// Frame: Cannot add the same control twice
|
||||
return
|
||||
}
|
||||
|
||||
c.SetScale(scale)
|
||||
@ -135,10 +136,11 @@ func (f *Frame) Children() []Control {
|
||||
|
||||
// SetPack changes the direction of children packing.
|
||||
// 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) {
|
||||
if len(f.children) > 0 {
|
||||
panic("Control already has children")
|
||||
// Control already has children
|
||||
return
|
||||
}
|
||||
|
||||
f.pack = pk
|
||||
|
@ -104,7 +104,6 @@ type View interface {
|
||||
// SetSize changes control size. Constant DoNotChange can be
|
||||
// used as placeholder to indicate that the control attrubute
|
||||
// should be unchanged.
|
||||
// Method panics if new size is less than minimal size
|
||||
SetSize(int, int)
|
||||
// Pos returns the current control position: X and Y.
|
||||
// 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)
|
||||
// AddChild add control to a list of view children. Minimal size
|
||||
// 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)
|
||||
// SetPack changes the direction of children packing
|
||||
SetPack(PackType)
|
||||
@ -251,7 +250,6 @@ type Control interface {
|
||||
// SetSize changes control size. Constant DoNotChange can be
|
||||
// used as placeholder to indicate that the control attrubute
|
||||
// should be unchanged.
|
||||
// Method panics if new size is less than minimal size
|
||||
SetSize(int, int)
|
||||
// Scale return scale coefficient that is used to calculate
|
||||
// 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
|
||||
Colors are the keys that end with 'Back' or 'Text' - background
|
||||
and text color, respectively. If theme manager cannot
|
||||
value to color it panics. See Color*Back * Color*Text constants,
|
||||
just drop 'Color' at the beginning of key name.
|
||||
value to color it uses black color. See Color*Back * Color*Text
|
||||
constants, just drop 'Color' at the beginning of key name.
|
||||
Rules of converting text to color:
|
||||
1. If the value does not end neither with 'Back' nor with 'Text'
|
||||
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:
|
||||
Viewback=parent.ViewText
|
||||
ViewText=parent.ViewBack
|
||||
Converting text to real color panics if a) the string does not look
|
||||
like real color(e.g, typo as in 'grean bold'), b) parent theme
|
||||
has not loaded yet, c) parent theme does not have the color
|
||||
Converting text to real color fails and retuns black color if
|
||||
a) the string does not look like real color(e.g, typo as in
|
||||
'grean bold'), b) parent theme has not loaded yet, c) parent
|
||||
theme does not have the color
|
||||
with the same name
|
||||
Other keys are considered as objects - see Obj* constants, just drop
|
||||
'Obj' at the beginning of the key name
|
||||
@ -180,7 +181,9 @@ func (s *ThemeManager) Reset() {
|
||||
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 {
|
||||
sch, ok := s.themes[s.current]
|
||||
if !ok {
|
||||
@ -216,7 +219,9 @@ func (s *ThemeManager) SysColor(color string) term.Attribute {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
sch, ok := s.themes[s.current]
|
||||
if !ok {
|
||||
|
37
window.go
37
window.go
@ -1,7 +1,6 @@
|
||||
package clui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
term "github.com/nsf/termbox-go"
|
||||
"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
|
||||
// used as placeholder to indicate that the control attrubute
|
||||
// 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
|
||||
func (w *Window) SetSize(width, height int) {
|
||||
if width == w.width && height == w.height {
|
||||
return
|
||||
}
|
||||
|
||||
if width != DoNotChange && (width > 1000 || width < w.minW) {
|
||||
panic(fmt.Sprintf("Invalid width: %v", width))
|
||||
if width != DoNotChange {
|
||||
if width > 1000 {
|
||||
width = 1000
|
||||
}
|
||||
if width < w.minW {
|
||||
width = w.minW
|
||||
}
|
||||
}
|
||||
if height != DoNotChange && (height > 200 || height < w.minH) {
|
||||
panic(fmt.Sprintf("Invalid height: %v", height))
|
||||
if height != DoNotChange {
|
||||
if height > 200 {
|
||||
height = 200
|
||||
}
|
||||
if height < w.minH {
|
||||
height = w.minH
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// repaint all view children
|
||||
// repaint all view children.
|
||||
// Method does nothing if coordinates are outside canvas
|
||||
func (w *Window) Draw(canvas Canvas) {
|
||||
for y := 0; y < w.height; y++ {
|
||||
for x := 0; x < w.width; x++ {
|
||||
s, ok := w.canvas.Symbol(x, y)
|
||||
if ok {
|
||||
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
|
||||
}
|
||||
|
||||
// SetPack changes the direction of children packing. Call the method only before any child is added to view. Otherwise, the method
|
||||
// panics if a view already contains children
|
||||
// SetPack changes the direction of children packing. Call the method
|
||||
// only before any child is added to view. Otherwise, the method
|
||||
// does nothing
|
||||
func (w *Window) SetPack(pk PackType) {
|
||||
if len(w.children) > 0 {
|
||||
panic("Control already has children")
|
||||
return
|
||||
}
|
||||
|
||||
w.pack = pk
|
||||
@ -289,10 +296,10 @@ func (w *Window) RegisterControl(c Control) {
|
||||
|
||||
// AddChild add control to a list of view children. Minimal size
|
||||
// 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) {
|
||||
if w.ChildExists(c) {
|
||||
panic("Cannot add the same control twice")
|
||||
return
|
||||
}
|
||||
|
||||
c.SetScale(scale)
|
||||
|
Loading…
x
Reference in New Issue
Block a user