diff --git a/area.go b/area.go index 2dc9328..d1c7420 100644 --- a/area.go +++ b/area.go @@ -3,6 +3,7 @@ package ui import ( + "fmt" "sync" "image" ) @@ -10,6 +11,7 @@ import ( // Area represents a blank canvas upon which programs may draw anything and receive arbitrary events from the user. // An Area has an explicit size, represented in pixels, that may be different from the size shown in its Window; Areas have horizontal and vertical scrollbars that are hidden when not needed. // The coordinate system of an Area always has an origin of (0,0) which maps to the top-left corner; all image.Points and image.Rectangles sent across Area's channels conform to this. +// The size of an Area must be at least 1x1 (that is, neither its width nor its height may be zero or negative). // // To handle events to the Area, an Area must be paired with an AreaHandler. // See AreaHandler for details. @@ -260,9 +262,16 @@ const ( // TODO add Super ) +func checkAreaSize(width int, height int, which string) { + if width <= 0 || height <= 0 { + panic(fmt.Errorf("invalid size %dx%d in %s", width, height, which)) + } +} + // NewArea creates a new Area with the given size and handler. -// It panics if handler is nil. +// It panics if handler is nil or if width or height is zero or negative. func NewArea(width int, height int, handler AreaHandler) *Area { + checkAreaSize(width, height, "NewArea()") if handler == nil { panic("handler passed to NewArea() must not be nil") } @@ -276,10 +285,12 @@ func NewArea(width int, height int, handler AreaHandler) *Area { // SetSize sets the Area's internal drawing size. // It has no effect on the actual control size. +// It panics if width or height is zero or negative. func (a *Area) SetSize(width int, height int) { a.lock.Lock() defer a.lock.Unlock() + checkAreaSize(width, height, "Area.SetSize()") if a.created { a.sysData.setAreaSize(width, height) return diff --git a/test/main.go b/test/main.go index f19c655..7397cc6 100644 --- a/test/main.go +++ b/test/main.go @@ -78,6 +78,7 @@ func invalidTest(c *Combobox, l *Listbox, s *Stack, g *Grid) { func() { defer x("Listbox.Delete > len"); l.Delete(c.Len() + 5); panic(nil) }() + // TODO get rid of arg != nil by creating dummy arg if s != nil { func() { defer x("Stack.SetStretchy < 0"); s.SetStretchy(-5); panic(nil) @@ -86,6 +87,7 @@ func invalidTest(c *Combobox, l *Listbox, s *Stack, g *Grid) { defer x("Stack.SetStretchy > len"); s.SetStretchy(5555); panic(nil) }() } + // TODO grid creation checks if g != nil { func() { defer x("Grid.SetFilling x < 0"); g.SetFilling(-5, 0); panic(nil) @@ -112,6 +114,33 @@ func invalidTest(c *Combobox, l *Listbox, s *Stack, g *Grid) { defer x("Grid.SetStretchy y > len"); g.SetStretchy(0, 5555); panic(nil) }() } + ah := &areaHandler{nil} + type at struct { + msg string + x, y int + } + var ats = []at{ + at{"width < 0", -5, 5}, + at{"width == 0", 0, 5}, + at{"height < 0", 5, -5}, + at{"height == 0", 5, 0}, + at{"width/heght < 0", -5, -5}, + at{"width < 0/height == 0", -5, 0}, + at{"width == 0/height < 0", 0, -5}, + at{"width/height == 0", 0, 0}, + } + for _, q := range ats { + func() { + defer x("NewArea() " + q.msg); NewArea(q.x, q.y, ah); panic(nil) + }() + } + // no need to pass this in as an argument since it's handled by the portable code, not by sysData + a := NewArea(50, 50, ah) + for _, q := range ats { + func() { + defer x("Area.SetSize() " + q.msg); a.SetSize(q.x, q.y); panic(nil) + }() + } MsgBox("test", "all working as intended") } diff --git a/todo.md b/todo.md index d04417c..71425a0 100644 --- a/todo.md +++ b/todo.md @@ -1,5 +1,4 @@ so I don't forget: -- add bounds checking to Area's sizing methods - describe thread-safety of Area.SetSize() - should all instances of -1 as error returns from Windows functions be changed to ^0 or does the uintptr() conversion handle sign extension?