diff --git a/artist/ellipse.go b/artist/ellipse.go index 837bd30..7dc9d4c 100644 --- a/artist/ellipse.go +++ b/artist/ellipse.go @@ -3,11 +3,11 @@ package artist import "math" import "image" import "image/color" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/canvas" // FillEllipse draws a filled ellipse with the specified pattern. func FillEllipse ( - destination tomo.Canvas, + destination canvas.Canvas, source Pattern, bounds image.Rectangle, ) ( @@ -36,7 +36,7 @@ func FillEllipse ( // StrokeEllipse draws the outline of an ellipse with the specified line weight // and pattern. func StrokeEllipse ( - destination tomo.Canvas, + destination canvas.Canvas, source Pattern, weight int, bounds image.Rectangle, diff --git a/artist/line.go b/artist/line.go index 265698f..733c626 100644 --- a/artist/line.go +++ b/artist/line.go @@ -2,14 +2,14 @@ package artist import "image" import "image/color" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/canvas" // TODO: draw thick lines more efficiently // Line draws a line from one point to another with the specified weight and // pattern. func Line ( - destination tomo.Canvas, + destination canvas.Canvas, source Pattern, weight int, min image.Point, @@ -46,7 +46,7 @@ func Line ( } func lineLow ( - destination tomo.Canvas, + destination canvas.Canvas, source Pattern, weight int, min image.Point, @@ -82,7 +82,7 @@ func lineLow ( } func lineHigh ( - destination tomo.Canvas, + destination canvas.Canvas, source Pattern, weight int, min image.Point, diff --git a/artist/rectangle.go b/artist/rectangle.go index f4e11c3..14f2108 100644 --- a/artist/rectangle.go +++ b/artist/rectangle.go @@ -1,12 +1,12 @@ package artist import "image" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/canvas" // Paste transfers one canvas onto another, offset by the specified point. func Paste ( - destination tomo.Canvas, - source tomo.Canvas, + destination canvas.Canvas, + source canvas.Canvas, offset image.Point, ) ( updatedRegion image.Rectangle, @@ -31,7 +31,7 @@ func Paste ( // FillRectangle draws a filled rectangle with the specified pattern. func FillRectangle ( - destination tomo.Canvas, + destination canvas.Canvas, source Pattern, bounds image.Rectangle, ) ( @@ -61,7 +61,7 @@ func FillRectangle ( // StrokeRectangle draws the outline of a rectangle with the specified line // weight and pattern. func StrokeRectangle ( - destination tomo.Canvas, + destination canvas.Canvas, source Pattern, weight int, bounds image.Rectangle, diff --git a/artist/text.go b/artist/text.go index d7c4d8f..88616f1 100644 --- a/artist/text.go +++ b/artist/text.go @@ -6,7 +6,7 @@ import "unicode" import "image/draw" import "golang.org/x/image/font" import "golang.org/x/image/math/fixed" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/canvas" type characterLayout struct { x int @@ -95,7 +95,7 @@ func (drawer *TextDrawer) SetAlignment (align Align) { // Draw draws the drawer's text onto the specified canvas at the given offset. func (drawer *TextDrawer) Draw ( - destination tomo.Canvas, + destination canvas.Canvas, source Pattern, offset image.Point, ) ( diff --git a/backend.go b/backend.go index 6ad3f70..3b1ff31 100644 --- a/backend.go +++ b/backend.go @@ -1,6 +1,8 @@ package tomo import "errors" +import "git.tebibyte.media/sashakoshka/tomo/data" +import "git.tebibyte.media/sashakoshka/tomo/elements" // Backend represents a connection to a display server, or something similar. // It is capable of managing an event loop, and creating windows. @@ -19,13 +21,13 @@ type Backend interface { // NewWindow creates a new window with the specified width and height, // and returns a struct representing it that fulfills the Window // interface. - NewWindow (width, height int) (window Window, err error) + NewWindow (width, height int) (window elements.Window, err error) // Copy puts data into the clipboard. - Copy (Data) + Copy (data.Data) // Paste returns the data currently in the clipboard. - Paste (accept []Mime) (Data) + Paste (accept []data.Mime) (data.Data) } // BackendFactory represents a function capable of constructing a backend diff --git a/backends/x/encoding.go b/backends/x/encoding.go index 5a5c8db..cf5a404 100644 --- a/backends/x/encoding.go +++ b/backends/x/encoding.go @@ -3,64 +3,64 @@ package x import "unicode" import "github.com/jezek/xgb/xproto" import "github.com/jezek/xgbutil/keybind" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" // when making changes to this file, look at keysymdef.h and // https://tronche.com/gui/x/xlib/input/keyboard-encoding.html -var buttonCodeTable = map[xproto.Keysym] tomo.Key { - 0xFFFFFF: tomo.KeyNone, +var buttonCodeTable = map[xproto.Keysym] input.Key { + 0xFFFFFF: input.KeyNone, - 0xFF63: tomo.KeyInsert, - 0xFF67: tomo.KeyMenu, - 0xFF61: tomo.KeyPrintScreen, - 0xFF6B: tomo.KeyPause, - 0xFFE5: tomo.KeyCapsLock, - 0xFF14: tomo.KeyScrollLock, - 0xFF7F: tomo.KeyNumLock, - 0xFF08: tomo.KeyBackspace, - 0xFF09: tomo.KeyTab, - 0xFE20: tomo.KeyTab, - 0xFF0D: tomo.KeyEnter, - 0xFF1B: tomo.KeyEscape, + 0xFF63: input.KeyInsert, + 0xFF67: input.KeyMenu, + 0xFF61: input.KeyPrintScreen, + 0xFF6B: input.KeyPause, + 0xFFE5: input.KeyCapsLock, + 0xFF14: input.KeyScrollLock, + 0xFF7F: input.KeyNumLock, + 0xFF08: input.KeyBackspace, + 0xFF09: input.KeyTab, + 0xFE20: input.KeyTab, + 0xFF0D: input.KeyEnter, + 0xFF1B: input.KeyEscape, - 0xFF52: tomo.KeyUp, - 0xFF54: tomo.KeyDown, - 0xFF51: tomo.KeyLeft, - 0xFF53: tomo.KeyRight, - 0xFF55: tomo.KeyPageUp, - 0xFF56: tomo.KeyPageDown, - 0xFF50: tomo.KeyHome, - 0xFF57: tomo.KeyEnd, + 0xFF52: input.KeyUp, + 0xFF54: input.KeyDown, + 0xFF51: input.KeyLeft, + 0xFF53: input.KeyRight, + 0xFF55: input.KeyPageUp, + 0xFF56: input.KeyPageDown, + 0xFF50: input.KeyHome, + 0xFF57: input.KeyEnd, - 0xFFE1: tomo.KeyLeftShift, - 0xFFE2: tomo.KeyRightShift, - 0xFFE3: tomo.KeyLeftControl, - 0xFFE4: tomo.KeyRightControl, + 0xFFE1: input.KeyLeftShift, + 0xFFE2: input.KeyRightShift, + 0xFFE3: input.KeyLeftControl, + 0xFFE4: input.KeyRightControl, - 0xFFE7: tomo.KeyLeftMeta, - 0xFFE8: tomo.KeyRightMeta, - 0xFFE9: tomo.KeyLeftAlt, - 0xFFEA: tomo.KeyRightAlt, - 0xFFEB: tomo.KeyLeftSuper, - 0xFFEC: tomo.KeyRightSuper, - 0xFFED: tomo.KeyLeftHyper, - 0xFFEE: tomo.KeyRightHyper, + 0xFFE7: input.KeyLeftMeta, + 0xFFE8: input.KeyRightMeta, + 0xFFE9: input.KeyLeftAlt, + 0xFFEA: input.KeyRightAlt, + 0xFFEB: input.KeyLeftSuper, + 0xFFEC: input.KeyRightSuper, + 0xFFED: input.KeyLeftHyper, + 0xFFEE: input.KeyRightHyper, - 0xFFFF: tomo.KeyDelete, + 0xFFFF: input.KeyDelete, - 0xFFBE: tomo.KeyF1, - 0xFFBF: tomo.KeyF2, - 0xFFC0: tomo.KeyF3, - 0xFFC1: tomo.KeyF4, - 0xFFC2: tomo.KeyF5, - 0xFFC3: tomo.KeyF6, - 0xFFC4: tomo.KeyF7, - 0xFFC5: tomo.KeyF8, - 0xFFC6: tomo.KeyF9, - 0xFFC7: tomo.KeyF10, - 0xFFC8: tomo.KeyF11, - 0xFFC9: tomo.KeyF12, + 0xFFBE: input.KeyF1, + 0xFFBF: input.KeyF2, + 0xFFC0: input.KeyF3, + 0xFFC1: input.KeyF4, + 0xFFC2: input.KeyF5, + 0xFFC3: input.KeyF6, + 0xFFC4: input.KeyF7, + 0xFFC5: input.KeyF8, + 0xFFC6: input.KeyF9, + 0xFFC7: input.KeyF10, + 0xFFC8: input.KeyF11, + 0xFFC9: input.KeyF12, // TODO: send this whenever a compose key, dead key, etc is pressed, // and then send the resulting character while witholding the key @@ -68,46 +68,46 @@ var buttonCodeTable = map[xproto.Keysym] tomo.Key { // concerned, a magical key with the final character was pressed and the // KeyDead key is just so that the program might provide some visual // feedback to the user while input is being waited for. - 0xFF20: tomo.KeyDead, + 0xFF20: input.KeyDead, } -var keypadCodeTable = map[xproto.Keysym] tomo.Key { - 0xff80: tomo.Key(' '), - 0xff89: tomo.KeyTab, - 0xff8d: tomo.KeyEnter, - 0xff91: tomo.KeyF1, - 0xff92: tomo.KeyF2, - 0xff93: tomo.KeyF3, - 0xff94: tomo.KeyF4, - 0xff95: tomo.KeyHome, - 0xff96: tomo.KeyLeft, - 0xff97: tomo.KeyUp, - 0xff98: tomo.KeyRight, - 0xff99: tomo.KeyDown, - 0xff9a: tomo.KeyPageUp, - 0xff9b: tomo.KeyPageDown, - 0xff9c: tomo.KeyEnd, - 0xff9d: tomo.KeyHome, - 0xff9e: tomo.KeyInsert, - 0xff9f: tomo.KeyDelete, - 0xffbd: tomo.Key('='), - 0xffaa: tomo.Key('*'), - 0xffab: tomo.Key('+'), - 0xffac: tomo.Key(','), - 0xffad: tomo.Key('-'), - 0xffae: tomo.Key('.'), - 0xffaf: tomo.Key('/'), +var keypadCodeTable = map[xproto.Keysym] input.Key { + 0xff80: input.Key(' '), + 0xff89: input.KeyTab, + 0xff8d: input.KeyEnter, + 0xff91: input.KeyF1, + 0xff92: input.KeyF2, + 0xff93: input.KeyF3, + 0xff94: input.KeyF4, + 0xff95: input.KeyHome, + 0xff96: input.KeyLeft, + 0xff97: input.KeyUp, + 0xff98: input.KeyRight, + 0xff99: input.KeyDown, + 0xff9a: input.KeyPageUp, + 0xff9b: input.KeyPageDown, + 0xff9c: input.KeyEnd, + 0xff9d: input.KeyHome, + 0xff9e: input.KeyInsert, + 0xff9f: input.KeyDelete, + 0xffbd: input.Key('='), + 0xffaa: input.Key('*'), + 0xffab: input.Key('+'), + 0xffac: input.Key(','), + 0xffad: input.Key('-'), + 0xffae: input.Key('.'), + 0xffaf: input.Key('/'), - 0xffb0: tomo.Key('0'), - 0xffb1: tomo.Key('1'), - 0xffb2: tomo.Key('2'), - 0xffb3: tomo.Key('3'), - 0xffb4: tomo.Key('4'), - 0xffb5: tomo.Key('5'), - 0xffb6: tomo.Key('6'), - 0xffb7: tomo.Key('7'), - 0xffb8: tomo.Key('8'), - 0xffb9: tomo.Key('9'), + 0xffb0: input.Key('0'), + 0xffb1: input.Key('1'), + 0xffb2: input.Key('2'), + 0xffb3: input.Key('3'), + 0xffb4: input.Key('4'), + 0xffb5: input.Key('5'), + 0xffb6: input.Key('6'), + 0xffb7: input.Key('7'), + 0xffb8: input.Key('8'), + 0xffb9: input.Key('9'), } // initializeKeymapInformation grabs keyboard mapping information from the X @@ -168,7 +168,7 @@ func (backend *Backend) keycodeToKey ( keycode xproto.Keycode, state uint16, ) ( - button tomo.Key, + button input.Key, numberPad bool, ) { // PARAGRAPH 3 @@ -359,7 +359,7 @@ func (backend *Backend) keycodeToKey ( if numberPad { return } // otherwise, use the rune - button = tomo.Key(selectedRune) + button = input.Key(selectedRune) return } diff --git a/backends/x/event.go b/backends/x/event.go index 7c62293..4a940ae 100644 --- a/backends/x/event.go +++ b/backends/x/event.go @@ -1,6 +1,7 @@ package x -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" +import "git.tebibyte.media/sashakoshka/tomo/elements" import "github.com/jezek/xgbutil" import "github.com/jezek/xgb/xproto" @@ -98,9 +99,9 @@ func (window *Window) exposeEventFollows (event xproto.ConfigureNotifyEvent) (fo func (window *Window) modifiersFromState ( state uint16, ) ( - modifiers tomo.Modifiers, + modifiers input.Modifiers, ) { - return tomo.Modifiers { + return input.Modifiers { Shift: (state & xproto.ModMaskShift) > 0 || (state & window.backend.modifierMasks.shiftLock) > 0, @@ -123,18 +124,18 @@ func (window *Window) handleKeyPress ( modifiers := window.modifiersFromState(keyEvent.State) modifiers.NumberPad = numberPad - if key == tomo.KeyTab && modifiers.Alt { - if child, ok := window.child.(tomo.Focusable); ok { - direction := tomo.KeynavDirectionForward + if key == input.KeyTab && modifiers.Alt { + if child, ok := window.child.(elements.Focusable); ok { + direction := input.KeynavDirectionForward if modifiers.Shift { - direction = tomo.KeynavDirectionBackward + direction = input.KeynavDirectionBackward } if !child.HandleFocus(direction) { child.HandleUnfocus() } } - } else if child, ok := window.child.(tomo.KeyboardTarget); ok { + } else if child, ok := window.child.(elements.KeyboardTarget); ok { child.HandleKeyDown(key, modifiers) } } @@ -168,7 +169,7 @@ func (window *Window) handleKeyRelease ( modifiers := window.modifiersFromState(keyEvent.State) modifiers.NumberPad = numberPad - if child, ok := window.child.(tomo.KeyboardTarget); ok { + if child, ok := window.child.(elements.KeyboardTarget); ok { child.HandleKeyUp(key, modifiers) } } @@ -179,7 +180,7 @@ func (window *Window) handleButtonPress ( ) { if window.child == nil { return } - if child, ok := window.child.(tomo.MouseTarget); ok { + if child, ok := window.child.(elements.MouseTarget); ok { buttonEvent := *event.ButtonPressEvent if buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7 { sum := scrollSum { } @@ -193,7 +194,7 @@ func (window *Window) handleButtonPress ( child.HandleMouseDown ( int(buttonEvent.EventX), int(buttonEvent.EventY), - tomo.Button(buttonEvent.Detail)) + input.Button(buttonEvent.Detail)) } } @@ -205,13 +206,13 @@ func (window *Window) handleButtonRelease ( ) { if window.child == nil { return } - if child, ok := window.child.(tomo.MouseTarget); ok { + if child, ok := window.child.(elements.MouseTarget); ok { buttonEvent := *event.ButtonReleaseEvent if buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7 { return } child.HandleMouseUp ( int(buttonEvent.EventX), int(buttonEvent.EventY), - tomo.Button(buttonEvent.Detail)) + input.Button(buttonEvent.Detail)) } } @@ -221,7 +222,7 @@ func (window *Window) handleMotionNotify ( ) { if window.child == nil { return } - if child, ok := window.child.(tomo.MouseTarget); ok { + if child, ok := window.child.(elements.MouseTarget); ok { motionEvent := window.compressMotionNotify(*event.MotionNotifyEvent) child.HandleMouseMove ( int(motionEvent.EventX), diff --git a/backends/x/window.go b/backends/x/window.go index 8956ba5..d51d948 100644 --- a/backends/x/window.go +++ b/backends/x/window.go @@ -7,14 +7,16 @@ import "github.com/jezek/xgbutil/icccm" import "github.com/jezek/xgbutil/xevent" import "github.com/jezek/xgbutil/xwindow" import "github.com/jezek/xgbutil/xgraphics" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" +import "git.tebibyte.media/sashakoshka/tomo/canvas" +import "git.tebibyte.media/sashakoshka/tomo/elements" type Window struct { backend *Backend xWindow *xwindow.Window xCanvas *xgraphics.Image - canvas tomo.BasicCanvas - child tomo.Element + canvas canvas.BasicCanvas + child elements.Element onClose func () skipChildDrawCallback bool @@ -27,7 +29,7 @@ type Window struct { func (backend *Backend) NewWindow ( width, height int, ) ( - output tomo.Window, + output elements.Window, err error, ) { if backend == nil { panic("nil backend") } @@ -79,16 +81,16 @@ func (backend *Backend) NewWindow ( return } -func (window *Window) Adopt (child tomo.Element) { +func (window *Window) Adopt (child elements.Element) { // disown previous child if window.child != nil { window.child.OnDamage(nil) window.child.OnMinimumSizeChange(nil) } - if previousChild, ok := window.child.(tomo.Flexible); ok { + if previousChild, ok := window.child.(elements.Flexible); ok { previousChild.OnFlexibleHeightChange(nil) } - if previousChild, ok := window.child.(tomo.Focusable); ok { + if previousChild, ok := window.child.(elements.Focusable); ok { previousChild.OnFocusRequest(nil) previousChild.OnFocusMotionRequest(nil) if previousChild.Focused() { @@ -98,10 +100,10 @@ func (window *Window) Adopt (child tomo.Element) { // adopt new child window.child = child - if newChild, ok := child.(tomo.Flexible); ok { + if newChild, ok := child.(elements.Flexible); ok { newChild.OnFlexibleHeightChange(window.resizeChildToFit) } - if newChild, ok := child.(tomo.Focusable); ok { + if newChild, ok := child.(elements.Focusable); ok { newChild.OnFocusRequest(window.childSelectionRequestCallback) } if child != nil { @@ -116,7 +118,7 @@ func (window *Window) Adopt (child tomo.Element) { } } -func (window *Window) Child () (child tomo.Element) { +func (window *Window) Child () (child elements.Element) { child = window.child return } @@ -229,7 +231,7 @@ func (window *Window) redrawChildEntirely () { func (window *Window) resizeChildToFit () { window.skipChildDrawCallback = true - if child, ok := window.child.(tomo.Flexible); ok { + if child, ok := window.child.(elements.Flexible); ok { minimumHeight := child.FlexibleHeightFor(window.metrics.width) minimumWidth, _ := child.MinimumSize() @@ -252,12 +254,12 @@ func (window *Window) resizeChildToFit () { window.skipChildDrawCallback = false } -func (window *Window) childDrawCallback (region tomo.Canvas) { +func (window *Window) childDrawCallback (region canvas.Canvas) { if window.skipChildDrawCallback { return } window.pushRegion(window.paste(region)) } -func (window *Window) paste (canvas tomo.Canvas) (updatedRegion image.Rectangle) { +func (window *Window) paste (canvas canvas.Canvas) (updatedRegion image.Rectangle) { data, stride := canvas.Buffer() bounds := canvas.Bounds().Intersect(window.xCanvas.Bounds()) for x := bounds.Min.X; x < bounds.Max.X; x ++ { @@ -293,18 +295,18 @@ func (window *Window) childMinimumSizeChangeCallback (width, height int) { } func (window *Window) childSelectionRequestCallback () (granted bool) { - if child, ok := window.child.(tomo.Focusable); ok { - child.HandleFocus(tomo.KeynavDirectionNeutral) + if child, ok := window.child.(elements.Focusable); ok { + child.HandleFocus(input.KeynavDirectionNeutral) } return true } func (window *Window) childSelectionMotionRequestCallback ( - direction tomo.KeynavDirection, + direction input.KeynavDirection, ) ( granted bool, ) { - if child, ok := window.child.(tomo.Focusable); ok { + if child, ok := window.child.(elements.Focusable); ok { if !child.HandleFocus(direction) { child.HandleUnfocus() } diff --git a/backends/x/x.go b/backends/x/x.go index 4fafc18..c7a514b 100644 --- a/backends/x/x.go +++ b/backends/x/x.go @@ -1,6 +1,7 @@ package x import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/data" import "github.com/jezek/xgbutil" import "github.com/jezek/xgb/xproto" @@ -81,14 +82,14 @@ func (backend *Backend) Do (callback func ()) { // Copy puts data into the clipboard. This method is not yet implemented and // will do nothing! -func (backend *Backend) Copy (data tomo.Data) { +func (backend *Backend) Copy (data data.Data) { backend.assert() // TODO } // Paste returns the data currently in the clipboard. This method may // return nil. This method is not yet implemented and will do nothing! -func (backend *Backend) Paste (accept []tomo.Mime) (data tomo.Data) { +func (backend *Backend) Paste (accept []data.Mime) (data data.Data) { backend.assert() // TODO return diff --git a/canvas.go b/canvas/canvas.go similarity index 99% rename from canvas.go rename to canvas/canvas.go index 608d388..cb2da2a 100644 --- a/canvas.go +++ b/canvas/canvas.go @@ -1,4 +1,4 @@ -package tomo +package canvas import "image" import "image/draw" diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..8828639 --- /dev/null +++ b/config/config.go @@ -0,0 +1,22 @@ +package config + +// Padding returns the amount of internal padding elements should have. An +// element's inner content (such as text) should be inset by this amount, +// in addition to the inset returned by the pattern of its background. When +// using the aforementioned inset values to calculate the element's minimum size +// or the position and alignment of its content, all parameters in the +// PatternState should be unset except for Case. +func Padding () int { + return 7 +} + +// Margin returns how much space should be put in between elements. +func Margin () int { + return 8 +} + +// HandleWidth returns how large grab handles should typically be. This is +// important for accessibility reasons. +func HandleWidth () int { + return 16 +} diff --git a/data.go b/data/data.go similarity index 97% rename from data.go rename to data/data.go index d5da9a3..8055711 100644 --- a/data.go +++ b/data/data.go @@ -1,4 +1,4 @@ -package tomo +package data import "io" diff --git a/elements/basic/button.go b/elements/basic/button.go index b9055b5..c768d23 100644 --- a/elements/basic/button.go +++ b/elements/basic/button.go @@ -1,7 +1,7 @@ -package basic +package basicElements import "image" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/theme" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/elements/core" @@ -38,10 +38,10 @@ func NewButton (text string) (element *Button) { return } -func (element *Button) HandleMouseDown (x, y int, button tomo.Button) { +func (element *Button) HandleMouseDown (x, y int, button input.Button) { if !element.Enabled() { return } if !element.Focused() { element.Focus() } - if button != tomo.ButtonLeft { return } + if button != input.ButtonLeft { return } element.pressed = true if element.core.HasImage() { element.draw() @@ -49,8 +49,8 @@ func (element *Button) HandleMouseDown (x, y int, button tomo.Button) { } } -func (element *Button) HandleMouseUp (x, y int, button tomo.Button) { - if button != tomo.ButtonLeft { return } +func (element *Button) HandleMouseUp (x, y int, button input.Button) { + if button != input.ButtonLeft { return } element.pressed = false if element.core.HasImage() { element.draw() @@ -69,9 +69,9 @@ func (element *Button) HandleMouseUp (x, y int, button tomo.Button) { func (element *Button) HandleMouseMove (x, y int) { } func (element *Button) HandleMouseScroll (x, y int, deltaX, deltaY float64) { } -func (element *Button) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { +func (element *Button) HandleKeyDown (key input.Key, modifiers input.Modifiers) { if !element.Enabled() { return } - if key == tomo.KeyEnter { + if key == input.KeyEnter { element.pressed = true if element.core.HasImage() { element.draw() @@ -80,8 +80,8 @@ func (element *Button) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { } } -func (element *Button) HandleKeyUp(key tomo.Key, modifiers tomo.Modifiers) { - if key == tomo.KeyEnter && element.pressed { +func (element *Button) HandleKeyUp(key input.Key, modifiers input.Modifiers) { + if key == input.KeyEnter && element.pressed { element.pressed = false if element.core.HasImage() { element.draw() diff --git a/elements/basic/checkbox.go b/elements/basic/checkbox.go index c4a0f78..ba0a869 100644 --- a/elements/basic/checkbox.go +++ b/elements/basic/checkbox.go @@ -1,7 +1,7 @@ -package basic +package basicElements import "image" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/theme" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/elements/core" @@ -39,7 +39,7 @@ func NewCheckbox (text string, checked bool) (element *Checkbox) { return } -func (element *Checkbox) HandleMouseDown (x, y int, button tomo.Button) { +func (element *Checkbox) HandleMouseDown (x, y int, button input.Button) { if !element.Enabled() { return } element.Focus() element.pressed = true @@ -49,8 +49,8 @@ func (element *Checkbox) HandleMouseDown (x, y int, button tomo.Button) { } } -func (element *Checkbox) HandleMouseUp (x, y int, button tomo.Button) { - if button != tomo.ButtonLeft || !element.pressed { return } +func (element *Checkbox) HandleMouseUp (x, y int, button input.Button) { + if button != input.ButtonLeft || !element.pressed { return } element.pressed = false within := image.Point { x, y }. @@ -71,8 +71,8 @@ func (element *Checkbox) HandleMouseUp (x, y int, button tomo.Button) { func (element *Checkbox) HandleMouseMove (x, y int) { } func (element *Checkbox) HandleMouseScroll (x, y int, deltaX, deltaY float64) { } -func (element *Checkbox) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { - if key == tomo.KeyEnter { +func (element *Checkbox) HandleKeyDown (key input.Key, modifiers input.Modifiers) { + if key == input.KeyEnter { element.pressed = true if element.core.HasImage() { element.draw() @@ -81,8 +81,8 @@ func (element *Checkbox) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) } } -func (element *Checkbox) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) { - if key == tomo.KeyEnter && element.pressed { +func (element *Checkbox) HandleKeyUp (key input.Key, modifiers input.Modifiers) { + if key == input.KeyEnter && element.pressed { element.pressed = false element.checked = !element.checked if element.core.HasImage() { diff --git a/elements/basic/container.go b/elements/basic/container.go index a23cdc4..328e18a 100644 --- a/elements/basic/container.go +++ b/elements/basic/container.go @@ -1,9 +1,12 @@ -package basic +package basicElements import "image" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/theme" +import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/artist" +import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/elements" import "git.tebibyte.media/sashakoshka/tomo/elements/core" var containerCase = theme.C("basic", "container") @@ -14,21 +17,21 @@ type Container struct { *core.Core core core.CoreControl - layout tomo.Layout - children []tomo.LayoutEntry - drags [10]tomo.MouseTarget + layout layouts.Layout + children []layouts.LayoutEntry + drags [10]elements.MouseTarget warping bool focused bool focusable bool flexible bool onFocusRequest func () (granted bool) - onFocusMotionRequest func (tomo.KeynavDirection) (granted bool) + onFocusMotionRequest func (input.KeynavDirection) (granted bool) onFlexibleHeightChange func () } // NewContainer creates a new container. -func NewContainer (layout tomo.Layout) (element *Container) { +func NewContainer (layout layouts.Layout) (element *Container) { element = &Container { } element.Core, element.core = core.NewCore(element.redoAll) element.SetLayout(layout) @@ -36,7 +39,7 @@ func NewContainer (layout tomo.Layout) (element *Container) { } // SetLayout sets the layout of this container. -func (element *Container) SetLayout (layout tomo.Layout) { +func (element *Container) SetLayout (layout layouts.Layout) { element.layout = layout if element.core.HasImage() { element.redoAll() @@ -47,28 +50,28 @@ func (element *Container) SetLayout (layout tomo.Layout) { // Adopt adds a new child element to the container. If expand is set to true, // the element will expand (instead of contract to its minimum size), in // whatever way is defined by the current layout. -func (element *Container) Adopt (child tomo.Element, expand bool) { +func (element *Container) Adopt (child elements.Element, expand bool) { // set event handlers - child.OnDamage (func (region tomo.Canvas) { + child.OnDamage (func (region canvas.Canvas) { element.core.DamageRegion(region.Bounds()) }) child.OnMinimumSizeChange(element.updateMinimumSize) - if child0, ok := child.(tomo.Flexible); ok { + if child0, ok := child.(elements.Flexible); ok { child0.OnFlexibleHeightChange(element.updateMinimumSize) } - if child0, ok := child.(tomo.Focusable); ok { + if child0, ok := child.(elements.Focusable); ok { child0.OnFocusRequest (func () (granted bool) { return element.childFocusRequestCallback(child0) }) child0.OnFocusMotionRequest ( - func (direction tomo.KeynavDirection) (granted bool) { + func (direction input.KeynavDirection) (granted bool) { if element.onFocusMotionRequest == nil { return } return element.onFocusMotionRequest(direction) }) } // add child - element.children = append (element.children, tomo.LayoutEntry { + element.children = append (element.children, layouts.LayoutEntry { Element: child, Expand: expand, }) @@ -106,7 +109,7 @@ func (element *Container) Warp (callback func ()) { // Disown removes the given child from the container if it is contained within // it. -func (element *Container) Disown (child tomo.Element) { +func (element *Container) Disown (child elements.Element) { for index, entry := range element.children { if entry.Element == child { element.clearChildEventHandlers(entry.Element) @@ -125,18 +128,18 @@ func (element *Container) Disown (child tomo.Element) { } } -func (element *Container) clearChildEventHandlers (child tomo.Element) { +func (element *Container) clearChildEventHandlers (child elements.Element) { child.DrawTo(nil) child.OnDamage(nil) child.OnMinimumSizeChange(nil) - if child0, ok := child.(tomo.Focusable); ok { + if child0, ok := child.(elements.Focusable); ok { child0.OnFocusRequest(nil) child0.OnFocusMotionRequest(nil) if child0.Focused() { child0.HandleUnfocus() } } - if child0, ok := child.(tomo.Flexible); ok { + if child0, ok := child.(elements.Flexible); ok { child0.OnFlexibleHeightChange(nil) } } @@ -154,8 +157,8 @@ func (element *Container) DisownAll () { } // Children returns a slice containing this element's children. -func (element *Container) Children () (children []tomo.Element) { - children = make([]tomo.Element, len(element.children)) +func (element *Container) Children () (children []elements.Element) { + children = make([]elements.Element, len(element.children)) for index, entry := range element.children { children[index] = entry.Element } @@ -169,14 +172,14 @@ func (element *Container) CountChildren () (count int) { // Child returns the child at the specified index. If the index is out of // bounds, this method will return nil. -func (element *Container) Child (index int) (child tomo.Element) { +func (element *Container) Child (index int) (child elements.Element) { if index < 0 || index > len(element.children) { return } return element.children[index].Element } // ChildAt returns the child that contains the specified x and y coordinates. If // there are no children at the coordinates, this method will return nil. -func (element *Container) ChildAt (point image.Point) (child tomo.Element) { +func (element *Container) ChildAt (point image.Point) (child elements.Element) { for _, entry := range element.children { if point.In(entry.Bounds) { child = entry.Element @@ -185,7 +188,7 @@ func (element *Container) ChildAt (point image.Point) (child tomo.Element) { return } -func (element *Container) childPosition (child tomo.Element) (position image.Point) { +func (element *Container) childPosition (child elements.Element) (position image.Point) { for _, entry := range element.children { if entry.Element == child { position = entry.Bounds.Min @@ -209,18 +212,18 @@ func (element *Container) redoAll () { // cut our canvas up and give peices to child elements for _, entry := range element.children { - entry.DrawTo(tomo.Cut(element, entry.Bounds)) + entry.DrawTo(canvas.Cut(element, entry.Bounds)) } } -func (element *Container) HandleMouseDown (x, y int, button tomo.Button) { - child, handlesMouse := element.ChildAt(image.Pt(x, y)).(tomo.MouseTarget) +func (element *Container) HandleMouseDown (x, y int, button input.Button) { + child, handlesMouse := element.ChildAt(image.Pt(x, y)).(elements.MouseTarget) if !handlesMouse { return } element.drags[button] = child child.HandleMouseDown(x, y, button) } -func (element *Container) HandleMouseUp (x, y int, button tomo.Button) { +func (element *Container) HandleMouseUp (x, y int, button input.Button) { child := element.drags[button] if child == nil { return } element.drags[button] = nil @@ -235,14 +238,14 @@ func (element *Container) HandleMouseMove (x, y int) { } func (element *Container) HandleMouseScroll (x, y int, deltaX, deltaY float64) { - child, handlesMouse := element.ChildAt(image.Pt(x, y)).(tomo.MouseTarget) + child, handlesMouse := element.ChildAt(image.Pt(x, y)).(elements.MouseTarget) if !handlesMouse { return } child.HandleMouseScroll(x, y, deltaX, deltaY) } -func (element *Container) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { - element.forFocused (func (child tomo.Focusable) bool { - child0, handlesKeyboard := child.(tomo.KeyboardTarget) +func (element *Container) HandleKeyDown (key input.Key, modifiers input.Modifiers) { + element.forFocused (func (child elements.Focusable) bool { + child0, handlesKeyboard := child.(elements.KeyboardTarget) if handlesKeyboard { child0.HandleKeyDown(key, modifiers) } @@ -250,9 +253,9 @@ func (element *Container) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) }) } -func (element *Container) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) { - element.forFocused (func (child tomo.Focusable) bool { - child0, handlesKeyboard := child.(tomo.KeyboardTarget) +func (element *Container) HandleKeyUp (key input.Key, modifiers input.Modifiers) { + element.forFocused (func (child elements.Focusable) bool { + child0, handlesKeyboard := child.(elements.KeyboardTarget) if handlesKeyboard { child0.HandleKeyUp(key, modifiers) } @@ -261,7 +264,9 @@ func (element *Container) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) { } func (element *Container) FlexibleHeightFor (width int) (height int) { - return element.layout.FlexibleHeightFor(element.children, width) + return element.layout.FlexibleHeightFor ( + element.children, + theme.Margin(), width) } func (element *Container) OnFlexibleHeightChange (callback func ()) { @@ -278,7 +283,7 @@ func (element *Container) Focus () { } } -func (element *Container) HandleFocus (direction tomo.KeynavDirection) (ok bool) { +func (element *Container) HandleFocus (direction input.KeynavDirection) (ok bool) { if !element.focusable { return false } direction = direction.Canon() @@ -288,12 +293,12 @@ func (element *Container) HandleFocus (direction tomo.KeynavDirection) (ok bool) // the first or last focusable element depending on the // direction. switch direction { - case tomo.KeynavDirectionNeutral, tomo.KeynavDirectionForward: + case input.KeynavDirectionNeutral, input.KeynavDirectionForward: // if we recieve a neutral or forward direction, focus // the first focusable element. return element.focusFirstFocusableElement(direction) - case tomo.KeynavDirectionBackward: + case input.KeynavDirectionBackward: // if we recieve a backward direction, focus the last // focusable element. return element.focusLastFocusableElement(direction) @@ -302,7 +307,7 @@ func (element *Container) HandleFocus (direction tomo.KeynavDirection) (ok bool) // an element is currently focused, so we need to move the // focus in the specified direction firstFocusedChild := - element.children[firstFocused].Element.(tomo.Focusable) + element.children[firstFocused].Element.(elements.Focusable) // before we move the focus, the currently focused child // may also be able to move its focus. if the child is able @@ -319,7 +324,7 @@ func (element *Container) HandleFocus (direction tomo.KeynavDirection) (ok bool) child, focusable := element.children[index]. - Element.(tomo.Focusable) + Element.(elements.Focusable) if focusable && child.HandleFocus(direction) { // we have found one, so we now actually move // the focus. @@ -334,11 +339,11 @@ func (element *Container) HandleFocus (direction tomo.KeynavDirection) (ok bool) } func (element *Container) focusFirstFocusableElement ( - direction tomo.KeynavDirection, + direction input.KeynavDirection, ) ( ok bool, ) { - element.forFocusable (func (child tomo.Focusable) bool { + element.forFocusable (func (child elements.Focusable) bool { if child.HandleFocus(direction) { element.focused = true ok = true @@ -350,11 +355,11 @@ func (element *Container) focusFirstFocusableElement ( } func (element *Container) focusLastFocusableElement ( - direction tomo.KeynavDirection, + direction input.KeynavDirection, ) ( ok bool, ) { - element.forFocusableBackward (func (child tomo.Focusable) bool { + element.forFocusableBackward (func (child elements.Focusable) bool { if child.HandleFocus(direction) { element.focused = true ok = true @@ -367,7 +372,7 @@ func (element *Container) focusLastFocusableElement ( func (element *Container) HandleUnfocus () { element.focused = false - element.forFocused (func (child tomo.Focusable) bool { + element.forFocused (func (child elements.Focusable) bool { child.HandleUnfocus() return true }) @@ -378,41 +383,41 @@ func (element *Container) OnFocusRequest (callback func () (granted bool)) { } func (element *Container) OnFocusMotionRequest ( - callback func (direction tomo.KeynavDirection) (granted bool), + callback func (direction input.KeynavDirection) (granted bool), ) { element.onFocusMotionRequest = callback } -func (element *Container) forFocused (callback func (child tomo.Focusable) bool) { +func (element *Container) forFocused (callback func (child elements.Focusable) bool) { for _, entry := range element.children { - child, focusable := entry.Element.(tomo.Focusable) + child, focusable := entry.Element.(elements.Focusable) if focusable && child.Focused() { if !callback(child) { break } } } } -func (element *Container) forFocusable (callback func (child tomo.Focusable) bool) { +func (element *Container) forFocusable (callback func (child elements.Focusable) bool) { for _, entry := range element.children { - child, focusable := entry.Element.(tomo.Focusable) + child, focusable := entry.Element.(elements.Focusable) if focusable { if !callback(child) { break } } } } -func (element *Container) forFlexible (callback func (child tomo.Flexible) bool) { +func (element *Container) forFlexible (callback func (child elements.Flexible) bool) { for _, entry := range element.children { - child, flexible := entry.Element.(tomo.Flexible) + child, flexible := entry.Element.(elements.Flexible) if flexible { if !callback(child) { break } } } } -func (element *Container) forFocusableBackward (callback func (child tomo.Focusable) bool) { +func (element *Container) forFocusableBackward (callback func (child elements.Focusable) bool) { for index := len(element.children) - 1; index >= 0; index -- { - child, focusable := element.children[index].Element.(tomo.Focusable) + child, focusable := element.children[index].Element.(elements.Focusable) if focusable { if !callback(child) { break } } @@ -421,7 +426,7 @@ func (element *Container) forFocusableBackward (callback func (child tomo.Focusa func (element *Container) firstFocused () (index int) { for currentIndex, entry := range element.children { - child, focusable := entry.Element.(tomo.Focusable) + child, focusable := entry.Element.(elements.Focusable) if focusable && child.Focused() { return currentIndex } @@ -431,12 +436,12 @@ func (element *Container) firstFocused () (index int) { func (element *Container) reflectChildProperties () { element.focusable = false - element.forFocusable (func (tomo.Focusable) bool { + element.forFocusable (func (elements.Focusable) bool { element.focusable = true return false }) element.flexible = false - element.forFlexible (func (tomo.Flexible) bool { + element.forFlexible (func (elements.Flexible) bool { element.flexible = true return false }) @@ -446,16 +451,16 @@ func (element *Container) reflectChildProperties () { } func (element *Container) childFocusRequestCallback ( - child tomo.Focusable, + child elements.Focusable, ) ( granted bool, ) { if element.onFocusRequest != nil && element.onFocusRequest() { - element.forFocused (func (child tomo.Focusable) bool { + element.forFocused (func (child elements.Focusable) bool { child.HandleUnfocus() return true }) - child.HandleFocus(tomo.KeynavDirectionNeutral) + child.HandleFocus(input.KeynavDirectionNeutral) return true } else { return false @@ -463,13 +468,16 @@ func (element *Container) childFocusRequestCallback ( } func (element *Container) updateMinimumSize () { - width, height := element.layout.MinimumSize(element.children) + width, height := element.layout.MinimumSize ( + element.children, theme.Margin()) if element.flexible { - height = element.layout.FlexibleHeightFor(element.children, width) + height = element.layout.FlexibleHeightFor ( + element.children, theme.Margin(), width) } element.core.SetMinimumSize(width, height) } func (element *Container) recalculate () { - element.layout.Arrange(element.children, element.Bounds()) + element.layout.Arrange ( + element.children, theme.Margin(), element.Bounds()) } diff --git a/elements/basic/label.go b/elements/basic/label.go index 5189422..b105580 100644 --- a/elements/basic/label.go +++ b/elements/basic/label.go @@ -1,4 +1,4 @@ -package basic +package basicElements import "git.tebibyte.media/sashakoshka/tomo/theme" import "git.tebibyte.media/sashakoshka/tomo/artist" diff --git a/elements/basic/list.go b/elements/basic/list.go index 854aa93..e8a60b9 100644 --- a/elements/basic/list.go +++ b/elements/basic/list.go @@ -1,9 +1,10 @@ -package basic +package basicElements import "fmt" import "image" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/theme" +import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/elements/core" @@ -73,10 +74,10 @@ func (element *List) Collapse (width, height int) { element.updateMinimumSize() } -func (element *List) HandleMouseDown (x, y int, button tomo.Button) { +func (element *List) HandleMouseDown (x, y int, button input.Button) { if !element.Enabled() { return } if !element.Focused() { element.Focus() } - if button != tomo.ButtonLeft { return } + if button != input.ButtonLeft { return } element.pressed = true if element.selectUnderMouse(x, y) && element.core.HasImage() { element.draw() @@ -84,8 +85,8 @@ func (element *List) HandleMouseDown (x, y int, button tomo.Button) { } } -func (element *List) HandleMouseUp (x, y int, button tomo.Button) { - if button != tomo.ButtonLeft { return } +func (element *List) HandleMouseUp (x, y int, button input.Button) { + if button != input.ButtonLeft { return } element.pressed = false } @@ -100,18 +101,18 @@ func (element *List) HandleMouseMove (x, y int) { func (element *List) HandleMouseScroll (x, y int, deltaX, deltaY float64) { } -func (element *List) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { +func (element *List) HandleKeyDown (key input.Key, modifiers input.Modifiers) { if !element.Enabled() { return } altered := false switch key { - case tomo.KeyLeft, tomo.KeyUp: + case input.KeyLeft, input.KeyUp: altered = element.changeSelectionBy(-1) - case tomo.KeyRight, tomo.KeyDown: + case input.KeyRight, input.KeyDown: altered = element.changeSelectionBy(1) - case tomo.KeyEscape: + case input.KeyEscape: altered = element.selectEntry(-1) } @@ -121,7 +122,7 @@ func (element *List) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { } } -func (element *List) HandleKeyUp(key tomo.Key, modifiers tomo.Modifiers) { } +func (element *List) HandleKeyUp(key input.Key, modifiers input.Modifiers) { } // ScrollContentBounds returns the full content size of the element. func (element *List) ScrollContentBounds () (bounds image.Rectangle) { @@ -383,7 +384,7 @@ func (element *List) draw () { bounds.Min.X, bounds.Min.Y - element.scroll, } - innerCanvas := tomo.Cut(element, bounds) + innerCanvas := canvas.Cut(element, bounds) for index, entry := range element.entries { entryPosition := dot dot.Y += entry.Bounds().Dy() diff --git a/elements/basic/listentry.go b/elements/basic/listentry.go index 45a14a0..fb45ba2 100644 --- a/elements/basic/listentry.go +++ b/elements/basic/listentry.go @@ -1,8 +1,8 @@ -package basic +package basicElements import "image" -import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/theme" +import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/artist" var listEntryCase = theme.C("basic", "listEntry") @@ -53,7 +53,7 @@ func (entry *ListEntry) updateBounds () { } func (entry *ListEntry) Draw ( - destination tomo.Canvas, + destination canvas.Canvas, offset image.Point, focused bool, on bool, diff --git a/elements/basic/progressbar.go b/elements/basic/progressbar.go index 4344f2c..e97665c 100644 --- a/elements/basic/progressbar.go +++ b/elements/basic/progressbar.go @@ -1,4 +1,4 @@ -package basic +package basicElements import "image" import "git.tebibyte.media/sashakoshka/tomo/theme" diff --git a/elements/basic/scrollcontainer.go b/elements/basic/scrollcontainer.go index c9491e1..a3ccbbb 100644 --- a/elements/basic/scrollcontainer.go +++ b/elements/basic/scrollcontainer.go @@ -1,9 +1,11 @@ -package basic +package basicElements import "image" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/theme" +import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/artist" +import "git.tebibyte.media/sashakoshka/tomo/elements" import "git.tebibyte.media/sashakoshka/tomo/elements/core" var scrollContainerCase = theme.C("basic", "scrollContainer") @@ -17,7 +19,7 @@ type ScrollContainer struct { core core.CoreControl focused bool - child tomo.Scrollable + child elements.Scrollable childWidth, childHeight int horizontal struct { @@ -41,7 +43,7 @@ type ScrollContainer struct { } onFocusRequest func () (granted bool) - onFocusMotionRequest func (tomo.KeynavDirection) (granted bool) + onFocusMotionRequest func (input.KeynavDirection) (granted bool) } // NewScrollContainer creates a new scroll container with the specified scroll @@ -64,7 +66,7 @@ func (element *ScrollContainer) handleResize () { // Adopt adds a scrollable element to the scroll container. The container can // only contain one scrollable element at a time, and when a new one is adopted // it replaces the last one. -func (element *ScrollContainer) Adopt (child tomo.Scrollable) { +func (element *ScrollContainer) Adopt (child elements.Scrollable) { // disown previous child if it exists if element.child != nil { element.clearChildEventHandlers(child) @@ -76,7 +78,7 @@ func (element *ScrollContainer) Adopt (child tomo.Scrollable) { child.OnDamage(element.childDamageCallback) child.OnMinimumSizeChange(element.updateMinimumSize) child.OnScrollBoundsChange(element.childScrollBoundsChangeCallback) - if newChild, ok := child.(tomo.Focusable); ok { + if newChild, ok := child.(elements.Focusable); ok { newChild.OnFocusRequest ( element.childFocusRequestCallback) newChild.OnFocusMotionRequest ( @@ -96,19 +98,19 @@ func (element *ScrollContainer) Adopt (child tomo.Scrollable) { } } -func (element *ScrollContainer) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { - if child, ok := element.child.(tomo.KeyboardTarget); ok { +func (element *ScrollContainer) HandleKeyDown (key input.Key, modifiers input.Modifiers) { + if child, ok := element.child.(elements.KeyboardTarget); ok { child.HandleKeyDown(key, modifiers) } } -func (element *ScrollContainer) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) { - if child, ok := element.child.(tomo.KeyboardTarget); ok { +func (element *ScrollContainer) HandleKeyUp (key input.Key, modifiers input.Modifiers) { + if child, ok := element.child.(elements.KeyboardTarget); ok { child.HandleKeyUp(key, modifiers) } } -func (element *ScrollContainer) HandleMouseDown (x, y int, button tomo.Button) { +func (element *ScrollContainer) HandleMouseDown (x, y int, button input.Button) { point := image.Pt(x, y) if point.In(element.horizontal.bar) { element.horizontal.dragging = true @@ -140,12 +142,12 @@ func (element *ScrollContainer) HandleMouseDown (x, y int, button tomo.Button) { element.scrollChildBy(0, -16) } - } else if child, ok := element.child.(tomo.MouseTarget); ok { + } else if child, ok := element.child.(elements.MouseTarget); ok { child.HandleMouseDown(x, y, button) } } -func (element *ScrollContainer) HandleMouseUp (x, y int, button tomo.Button) { +func (element *ScrollContainer) HandleMouseUp (x, y int, button input.Button) { if element.horizontal.dragging { element.horizontal.dragging = false element.drawHorizontalBar() @@ -156,7 +158,7 @@ func (element *ScrollContainer) HandleMouseUp (x, y int, button tomo.Button) { element.drawVerticalBar() element.core.DamageRegion(element.vertical.bar) - } else if child, ok := element.child.(tomo.MouseTarget); ok { + } else if child, ok := element.child.(elements.MouseTarget); ok { child.HandleMouseUp(x, y, button) } } @@ -168,7 +170,7 @@ func (element *ScrollContainer) HandleMouseMove (x, y int) { } else if element.vertical.dragging { element.dragVerticalBar(image.Pt(x, y)) - } else if child, ok := element.child.(tomo.MouseTarget); ok { + } else if child, ok := element.child.(elements.MouseTarget); ok { child.HandleMouseMove(x, y) } } @@ -199,11 +201,11 @@ func (element *ScrollContainer) Focus () { } func (element *ScrollContainer) HandleFocus ( - direction tomo.KeynavDirection, + direction input.KeynavDirection, ) ( accepted bool, ) { - if child, ok := element.child.(tomo.Focusable); ok { + if child, ok := element.child.(elements.Focusable); ok { element.focused = true return child.HandleFocus(direction) } else { @@ -213,7 +215,7 @@ func (element *ScrollContainer) HandleFocus ( } func (element *ScrollContainer) HandleUnfocus () { - if child, ok := element.child.(tomo.Focusable); ok { + if child, ok := element.child.(elements.Focusable); ok { child.HandleUnfocus() } element.focused = false @@ -224,20 +226,20 @@ func (element *ScrollContainer) OnFocusRequest (callback func () (granted bool)) } func (element *ScrollContainer) OnFocusMotionRequest ( - callback func (direction tomo.KeynavDirection) (granted bool), + callback func (direction input.KeynavDirection) (granted bool), ) { element.onFocusMotionRequest = callback } -func (element *ScrollContainer) childDamageCallback (region tomo.Canvas) { +func (element *ScrollContainer) childDamageCallback (region canvas.Canvas) { element.core.DamageRegion(artist.Paste(element, region, image.Point { })) } func (element *ScrollContainer) childFocusRequestCallback () (granted bool) { - child, ok := element.child.(tomo.Focusable) + child, ok := element.child.(elements.Focusable) if !ok { return false } if element.onFocusRequest != nil && element.onFocusRequest() { - child.HandleFocus(tomo.KeynavDirectionNeutral) + child.HandleFocus(input.KeynavDirectionNeutral) return true } else { return false @@ -245,7 +247,7 @@ func (element *ScrollContainer) childFocusRequestCallback () (granted bool) { } func (element *ScrollContainer) childFocusMotionRequestCallback ( - direction tomo.KeynavDirection, + direction input.KeynavDirection, ) ( granted bool, ) { @@ -253,19 +255,19 @@ func (element *ScrollContainer) childFocusMotionRequestCallback ( return element.onFocusMotionRequest(direction) } -func (element *ScrollContainer) clearChildEventHandlers (child tomo.Scrollable) { +func (element *ScrollContainer) clearChildEventHandlers (child elements.Scrollable) { child.DrawTo(nil) child.OnDamage(nil) child.OnMinimumSizeChange(nil) child.OnScrollBoundsChange(nil) - if child0, ok := child.(tomo.Focusable); ok { + if child0, ok := child.(elements.Focusable); ok { child0.OnFocusRequest(nil) child0.OnFocusMotionRequest(nil) if child0.Focused() { child0.HandleUnfocus() } } - if child0, ok := child.(tomo.Flexible); ok { + if child0, ok := child.(elements.Flexible); ok { child0.OnFlexibleHeightChange(nil) } } @@ -275,7 +277,7 @@ func (element *ScrollContainer) resizeChildToFit () { 0, 0, element.childWidth, element.childHeight).Add(element.Bounds().Min) - element.child.DrawTo(tomo.Cut(element, childBounds)) + element.child.DrawTo(canvas.Cut(element, childBounds)) } func (element *ScrollContainer) recalculate () { diff --git a/elements/basic/spacer.go b/elements/basic/spacer.go index 940c462..931087e 100644 --- a/elements/basic/spacer.go +++ b/elements/basic/spacer.go @@ -1,4 +1,4 @@ -package basic +package basicElements import "git.tebibyte.media/sashakoshka/tomo/theme" import "git.tebibyte.media/sashakoshka/tomo/artist" diff --git a/elements/basic/switch.go b/elements/basic/switch.go index a30523a..8a92e4e 100644 --- a/elements/basic/switch.go +++ b/elements/basic/switch.go @@ -1,7 +1,7 @@ -package basic +package basicElements import "image" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/theme" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/elements/core" @@ -41,7 +41,7 @@ func NewSwitch (text string, on bool) (element *Switch) { return } -func (element *Switch) HandleMouseDown (x, y int, button tomo.Button) { +func (element *Switch) HandleMouseDown (x, y int, button input.Button) { if !element.Enabled() { return } element.Focus() element.pressed = true @@ -51,8 +51,8 @@ func (element *Switch) HandleMouseDown (x, y int, button tomo.Button) { } } -func (element *Switch) HandleMouseUp (x, y int, button tomo.Button) { - if button != tomo.ButtonLeft || !element.pressed { return } +func (element *Switch) HandleMouseUp (x, y int, button input.Button) { + if button != input.ButtonLeft || !element.pressed { return } element.pressed = false within := image.Point { x, y }. @@ -73,8 +73,8 @@ func (element *Switch) HandleMouseUp (x, y int, button tomo.Button) { func (element *Switch) HandleMouseMove (x, y int) { } func (element *Switch) HandleMouseScroll (x, y int, deltaX, deltaY float64) { } -func (element *Switch) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { - if key == tomo.KeyEnter { +func (element *Switch) HandleKeyDown (key input.Key, modifiers input.Modifiers) { + if key == input.KeyEnter { element.pressed = true if element.core.HasImage() { element.draw() @@ -83,8 +83,8 @@ func (element *Switch) HandleKeyDown (key tomo.Key, modifiers tomo.Modifiers) { } } -func (element *Switch) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) { - if key == tomo.KeyEnter && element.pressed { +func (element *Switch) HandleKeyUp (key input.Key, modifiers input.Modifiers) { + if key == input.KeyEnter && element.pressed { element.pressed = false element.checked = !element.checked if element.core.HasImage() { diff --git a/elements/basic/textbox.go b/elements/basic/textbox.go index b1028b1..397d84f 100644 --- a/elements/basic/textbox.go +++ b/elements/basic/textbox.go @@ -1,7 +1,7 @@ -package basic +package basicElements import "image" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/theme" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/textmanip" @@ -24,7 +24,7 @@ type TextBox struct { placeholderDrawer artist.TextDrawer valueDrawer artist.TextDrawer - onKeyDown func (key tomo.Key, modifiers tomo.Modifiers) (handled bool) + onKeyDown func (key input.Key, modifiers input.Modifiers) (handled bool) onChange func () onScrollBoundsChange func () } @@ -59,16 +59,16 @@ func (element *TextBox) handleResize () { } } -func (element *TextBox) HandleMouseDown (x, y int, button tomo.Button) { +func (element *TextBox) HandleMouseDown (x, y int, button input.Button) { if !element.Enabled() { return } if !element.Focused() { element.Focus() } } -func (element *TextBox) HandleMouseUp (x, y int, button tomo.Button) { } +func (element *TextBox) HandleMouseUp (x, y int, button input.Button) { } func (element *TextBox) HandleMouseMove (x, y int) { } func (element *TextBox) HandleMouseScroll (x, y int, deltaX, deltaY float64) { } -func (element *TextBox) HandleKeyDown(key tomo.Key, modifiers tomo.Modifiers) { +func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers) { if element.onKeyDown != nil && element.onKeyDown(key, modifiers) { return } @@ -77,7 +77,7 @@ func (element *TextBox) HandleKeyDown(key tomo.Key, modifiers tomo.Modifiers) { altered := true textChanged := false switch { - case key == tomo.KeyBackspace: + case key == input.KeyBackspace: if len(element.text) < 1 { break } element.text, element.cursor = textmanip.Backspace ( element.text, @@ -85,7 +85,7 @@ func (element *TextBox) HandleKeyDown(key tomo.Key, modifiers tomo.Modifiers) { modifiers.Control) textChanged = true - case key == tomo.KeyDelete: + case key == input.KeyDelete: if len(element.text) < 1 { break } element.text, element.cursor = textmanip.Delete ( element.text, @@ -93,13 +93,13 @@ func (element *TextBox) HandleKeyDown(key tomo.Key, modifiers tomo.Modifiers) { modifiers.Control) textChanged = true - case key == tomo.KeyLeft: + case key == input.KeyLeft: element.cursor = textmanip.MoveLeft ( element.text, element.cursor, modifiers.Control) - case key == tomo.KeyRight: + case key == input.KeyRight: element.cursor = textmanip.MoveRight ( element.text, element.cursor, @@ -136,7 +136,7 @@ func (element *TextBox) HandleKeyDown(key tomo.Key, modifiers tomo.Modifiers) { } } -func (element *TextBox) HandleKeyUp(key tomo.Key, modifiers tomo.Modifiers) { } +func (element *TextBox) HandleKeyUp(key input.Key, modifiers input.Modifiers) { } func (element *TextBox) SetPlaceholder (placeholder string) { if element.placeholder == placeholder { return } @@ -177,7 +177,7 @@ func (element *TextBox) Filled () (filled bool) { } func (element *TextBox) OnKeyDown ( - callback func (key tomo.Key, modifiers tomo.Modifiers) (handled bool), + callback func (key input.Key, modifiers input.Modifiers) (handled bool), ) { element.onKeyDown = callback } diff --git a/elements/core/core.go b/elements/core/core.go index 6a42623..bb9357e 100644 --- a/elements/core/core.go +++ b/elements/core/core.go @@ -2,12 +2,12 @@ package core import "image" import "image/color" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/canvas" // Core is a struct that implements some core functionality common to most // widgets. It is meant to be embedded directly into a struct. type Core struct { - canvas tomo.Canvas + canvas canvas.Canvas metrics struct { minimumWidth int @@ -16,7 +16,7 @@ type Core struct { drawSizeChange func () onMinimumSizeChange func () - onDamage func (region tomo.Canvas) + onDamage func (region canvas.Canvas) } // NewCore creates a new element core and its corresponding control. @@ -49,7 +49,7 @@ func (core *Core) Set (x, y int, c color.Color) () { core.canvas.Set(x, y, c) } -// Buffer fulfills the tomo.Canvas interface. +// Buffer fulfills the canvas.Canvas interface. func (core *Core) Buffer () (data []color.RGBA, stride int) { if core.canvas == nil { return } return core.canvas.Buffer() @@ -63,7 +63,7 @@ func (core *Core) MinimumSize () (width, height int) { // DrawTo fulfills the tomo.Element interface. This should not need to be // overridden. -func (core *Core) DrawTo (canvas tomo.Canvas) { +func (core *Core) DrawTo (canvas canvas.Canvas) { core.canvas = canvas if core.drawSizeChange != nil { core.drawSizeChange() @@ -72,7 +72,7 @@ func (core *Core) DrawTo (canvas tomo.Canvas) { // OnDamage fulfils the tomo.Element interface. This should not need to be // overridden. -func (core *Core) OnDamage (callback func (region tomo.Canvas)) { +func (core *Core) OnDamage (callback func (region canvas.Canvas)) { core.onDamage = callback } @@ -100,7 +100,7 @@ func (control CoreControl) HasImage () (has bool) { // does not need to be called when responding to a resize event. func (control CoreControl) DamageRegion (bounds image.Rectangle) { if control.core.onDamage != nil { - control.core.onDamage(tomo.Cut(control.core, bounds)) + control.core.onDamage(canvas.Cut(control.core, bounds)) } } diff --git a/elements/core/selectable.go b/elements/core/selectable.go index 1c28649..b7ad329 100644 --- a/elements/core/selectable.go +++ b/elements/core/selectable.go @@ -1,6 +1,6 @@ package core -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" // FocusableCore is a struct that can be embedded into objects to make them // focusable, giving them the default keynav behavior. @@ -9,7 +9,7 @@ type FocusableCore struct { enabled bool drawFocusChange func () onFocusRequest func () (granted bool) - onFocusMotionRequest func(tomo.KeynavDirection) (granted bool) + onFocusMotionRequest func(input.KeynavDirection) (granted bool) } // NewFocusableCore creates a new focusability core and its corresponding @@ -46,13 +46,13 @@ func (core *FocusableCore) Focus () { // HandleFocus causes this element to mark itself as focused, if it can // currently be. Otherwise, it will return false and do nothing. func (core *FocusableCore) HandleFocus ( - direction tomo.KeynavDirection, + direction input.KeynavDirection, ) ( accepted bool, ) { direction = direction.Canon() if !core.enabled { return false } - if core.focused && direction != tomo.KeynavDirectionNeutral { + if core.focused && direction != input.KeynavDirectionNeutral { return false } @@ -80,7 +80,7 @@ func (core *FocusableCore) OnFocusRequest (callback func () (granted bool)) { // should return true if the request was granted, and false if it was // not. func (core *FocusableCore) OnFocusMotionRequest ( - callback func (direction tomo.KeynavDirection) (granted bool), + callback func (direction input.KeynavDirection) (granted bool), ) { core.onFocusMotionRequest = callback } diff --git a/element.go b/elements/element.go similarity index 86% rename from element.go rename to elements/element.go index 4a650d7..f97a48c 100644 --- a/element.go +++ b/elements/element.go @@ -1,13 +1,15 @@ -package tomo +package elements import "image" +import "git.tebibyte.media/sashakoshka/tomo/input" +import "git.tebibyte.media/sashakoshka/tomo/canvas" // Element represents a basic on-screen object. type Element interface { // Element must implement the Canvas interface. Elements should start // out with a completely blank buffer, and only allocate memory and draw // on it for the first time when sent an EventResize event. - Canvas + canvas.Canvas // MinimumSize specifies the minimum amount of pixels this element's // width and height may be set to. If the element is given a resize @@ -18,37 +20,17 @@ type Element interface { // DrawTo sets this element's canvas. This should only be called by the // parent element. This is typically a region of the parent element's // canvas. - DrawTo (canvas Canvas) + DrawTo (canvas canvas.Canvas) // OnDamage sets a function to be called when an area of the element is // drawn on and should be pushed to the screen. - OnDamage (callback func (region Canvas)) + OnDamage (callback func (region canvas.Canvas)) // OnMinimumSizeChange sets a function to be called when the element's // minimum size is changed. OnMinimumSizeChange (callback func ()) } -// KeynavDirection represents a keyboard navigation direction. -type KeynavDirection int - -const ( - KeynavDirectionNeutral KeynavDirection = 0 - KeynavDirectionBackward KeynavDirection = -1 - KeynavDirectionForward KeynavDirection = 1 -) - -// Canon returns a well-formed direction. -func (direction KeynavDirection) Canon () (canon KeynavDirection) { - if direction > 0 { - return KeynavDirectionForward - } else if direction == 0 { - return KeynavDirectionNeutral - } else { - return KeynavDirectionBackward - } -} - // Focusable represents an element that has keyboard navigation support. This // includes inputs, buttons, sliders, etc. as well as any elements that have // children (so keyboard navigation events can be propagated downward). @@ -67,7 +49,7 @@ type Focusable interface { // selectable children in the given direction, it should return false // and do nothing. Otherwise, it should select itself and any children // (if applicable) and return true. - HandleFocus (direction KeynavDirection) (accepted bool) + HandleFocus (direction input.KeynavDirection) (accepted bool) // HandleDeselection causes this element to mark itself and all of its // children as unfocused. @@ -83,7 +65,7 @@ type Focusable interface { // front of it, depending on the specified direction. Parent elements // should return true if the request was granted, and false if it was // not. - OnFocusMotionRequest (func (direction KeynavDirection) (granted bool)) + OnFocusMotionRequest (func (direction input.KeynavDirection) (granted bool)) } // KeyboardTarget represents an element that can receive keyboard input. @@ -95,11 +77,11 @@ type KeyboardTarget interface { // every key down event is guaranteed to be paired with exactly one key // up event. This is the reason a list of modifier keys held down at the // time of the key press is given. - HandleKeyDown (key Key, modifiers Modifiers) + HandleKeyDown (key input.Key, modifiers input.Modifiers) // HandleKeyUp is called when a key is released while this element has // keyboard focus. - HandleKeyUp (key Key, modifiers Modifiers) + HandleKeyUp (key input.Key, modifiers input.Modifiers) } // MouseTarget represents an element that can receive mouse events. @@ -111,11 +93,11 @@ type MouseTarget interface { // HandleMouseDown is called when a mouse button is pressed down on this // element. - HandleMouseDown (x, y int, button Button) + HandleMouseDown (x, y int, button input.Button) // HandleMouseUp is called when a mouse button is released that was // originally pressed down on this element. - HandleMouseUp (x, y int, button Button) + HandleMouseUp (x, y int, button input.Button) // HandleMouseMove is called when the mouse is moved over this element, // or the mouse is moving while being held down and originally pressed diff --git a/elements/testing/mouse.go b/elements/testing/mouse.go index 421029c..baf6044 100644 --- a/elements/testing/mouse.go +++ b/elements/testing/mouse.go @@ -2,7 +2,7 @@ package testing import "image" import "image/color" -import "git.tebibyte.media/sashakoshka/tomo" +import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/theme" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/elements/core" @@ -44,12 +44,12 @@ func (element *Mouse) draw () { bounds.Min.Add(image.Pt(bounds.Dx() - 2, 1))) } -func (element *Mouse) HandleMouseDown (x, y int, button tomo.Button) { +func (element *Mouse) HandleMouseDown (x, y int, button input.Button) { element.drawing = true element.lastMousePos = image.Pt(x, y) } -func (element *Mouse) HandleMouseUp (x, y int, button tomo.Button) { +func (element *Mouse) HandleMouseUp (x, y int, button input.Button) { element.drawing = false mousePos := image.Pt(x, y) element.core.DamageRegion (artist.Line ( diff --git a/window.go b/elements/window.go similarity index 98% rename from window.go rename to elements/window.go index 94980f8..618d795 100644 --- a/window.go +++ b/elements/window.go @@ -1,4 +1,4 @@ -package tomo +package elements import "image" diff --git a/examples/button/main.go b/examples/button/main.go index 2b860b3..7e79a74 100644 --- a/examples/button/main.go +++ b/examples/button/main.go @@ -11,7 +11,7 @@ func main () { func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("example button") - button := basic.NewButton("hello tomo!") + button := basicElements.NewButton("hello tomo!") button.OnClick (func () { // when we set the button's text to something longer, the window // will automatically resize to accomodate it. diff --git a/examples/checkbox/main.go b/examples/checkbox/main.go index fe3364f..b8ec7ad 100644 --- a/examples/checkbox/main.go +++ b/examples/checkbox/main.go @@ -2,7 +2,7 @@ package main import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/popups" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -14,22 +14,22 @@ func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("Checkboxes") - container := basic.NewContainer(layouts.Vertical { true, true }) + container := basicElements.NewContainer(basicLayouts.Vertical { true, true }) window.Adopt(container) - container.Adopt (basic.NewLabel ( + container.Adopt (basicElements.NewLabel ( "We advise you to not read thPlease listen to me. I am " + "trapped inside the example code. This is the only way for " + "me to communicate.", true), true) - container.Adopt(basic.NewSpacer(true), false) - container.Adopt(basic.NewCheckbox("Oh god", false), false) - container.Adopt(basic.NewCheckbox("Can you hear them", true), false) - container.Adopt(basic.NewCheckbox("They are in the walls", false), false) - container.Adopt(basic.NewCheckbox("They are coming for us", false), false) - disabledCheckbox := basic.NewCheckbox("We are but their helpless prey", false) + container.Adopt(basicElements.NewSpacer(true), false) + container.Adopt(basicElements.NewCheckbox("Oh god", false), false) + container.Adopt(basicElements.NewCheckbox("Can you hear them", true), false) + container.Adopt(basicElements.NewCheckbox("They are in the walls", false), false) + container.Adopt(basicElements.NewCheckbox("They are coming for us", false), false) + disabledCheckbox := basicElements.NewCheckbox("We are but their helpless prey", false) disabledCheckbox.SetEnabled(false) container.Adopt(disabledCheckbox, false) - vsync := basic.NewCheckbox("Enable vsync", false) + vsync := basicElements.NewCheckbox("Enable vsync", false) vsync.OnToggle (func () { if vsync.Value() { popups.NewDialog ( @@ -39,7 +39,7 @@ func run () { } }) container.Adopt(vsync, false) - button := basic.NewButton("What") + button := basicElements.NewButton("What") button.OnClick(tomo.Stop) container.Adopt(button, false) button.Focus() diff --git a/examples/dialogLayout/main.go b/examples/dialogLayout/main.go index 89edb33..63fe452 100644 --- a/examples/dialogLayout/main.go +++ b/examples/dialogLayout/main.go @@ -1,7 +1,7 @@ package main import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -13,14 +13,14 @@ func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("dialog") - container := basic.NewContainer(layouts.Dialog { true, true }) + container := basicElements.NewContainer(basicLayouts.Dialog { true, true }) window.Adopt(container) - container.Adopt(basic.NewLabel("you will explode", true), true) - cancel := basic.NewButton("Cancel") + container.Adopt(basicElements.NewLabel("you will explode", true), true) + cancel := basicElements.NewButton("Cancel") cancel.SetEnabled(false) container.Adopt(cancel, false) - okButton := basic.NewButton("OK") + okButton := basicElements.NewButton("OK") container.Adopt(okButton, false) okButton.Focus() diff --git a/examples/flow/main.go b/examples/flow/main.go index 8b69c3b..5b479fb 100644 --- a/examples/flow/main.go +++ b/examples/flow/main.go @@ -2,7 +2,7 @@ package main import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/flow" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -13,21 +13,21 @@ func main () { func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("adventure") - container := basic.NewContainer(layouts.Vertical { true, true }) + container := basicElements.NewContainer(basicLayouts.Vertical { true, true }) window.Adopt(container) var world flow.Flow world.Transition = container.DisownAll world.Stages = map [string] func () { "start": func () { - label := basic.NewLabel ( + label := basicElements.NewLabel ( "you are standing next to a river.", true) - button0 := basic.NewButton("go in the river") + button0 := basicElements.NewButton("go in the river") button0.OnClick(world.SwitchFunc("wet")) - button1 := basic.NewButton("walk along the river") + button1 := basicElements.NewButton("walk along the river") button1.OnClick(world.SwitchFunc("house")) - button2 := basic.NewButton("turn around") + button2 := basicElements.NewButton("turn around") button2.OnClick(world.SwitchFunc("bear")) container.Warp ( func () { @@ -39,13 +39,13 @@ func run () { }) }, "wet": func () { - label := basic.NewLabel ( + label := basicElements.NewLabel ( "you get completely soaked.\n" + "you die of hypothermia.", true) - button0 := basic.NewButton("try again") + button0 := basicElements.NewButton("try again") button0.OnClick(world.SwitchFunc("start")) - button1 := basic.NewButton("exit") + button1 := basicElements.NewButton("exit") button1.OnClick(tomo.Stop) container.Warp (func () { @@ -56,13 +56,13 @@ func run () { }) }, "house": func () { - label := basic.NewLabel ( + label := basicElements.NewLabel ( "you are standing in front of a delapidated " + "house.", true) - button1 := basic.NewButton("go inside") + button1 := basicElements.NewButton("go inside") button1.OnClick(world.SwitchFunc("inside")) - button0 := basic.NewButton("turn back") + button0 := basicElements.NewButton("turn back") button0.OnClick(world.SwitchFunc("start")) container.Warp (func () { @@ -73,14 +73,14 @@ func run () { }) }, "inside": func () { - label := basic.NewLabel ( + label := basicElements.NewLabel ( "you are standing inside of the house.\n" + "it is dark, but rays of light stream " + "through the window.\n" + "there is nothing particularly interesting " + "here.", true) - button0 := basic.NewButton("go back outside") + button0 := basicElements.NewButton("go back outside") button0.OnClick(world.SwitchFunc("house")) container.Warp (func () { @@ -90,13 +90,13 @@ func run () { }) }, "bear": func () { - label := basic.NewLabel ( + label := basicElements.NewLabel ( "you come face to face with a bear.\n" + "it eats you (it was hungry).", true) - button0 := basic.NewButton("try again") + button0 := basicElements.NewButton("try again") button0.OnClick(world.SwitchFunc("start")) - button1 := basic.NewButton("exit") + button1 := basicElements.NewButton("exit") button1.OnClick(tomo.Stop) container.Warp (func () { diff --git a/examples/goroutines/main.go b/examples/goroutines/main.go index f0253f7..86840be 100644 --- a/examples/goroutines/main.go +++ b/examples/goroutines/main.go @@ -3,7 +3,7 @@ package main import "os" import "time" import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/fun" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -16,12 +16,12 @@ func main () { func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("clock") - container := basic.NewContainer(layouts.Vertical { true, true }) + container := basicElements.NewContainer(basicLayouts.Vertical { true, true }) window.Adopt(container) clock := fun.NewAnalogClock(time.Now()) container.Adopt(clock, true) - label := basic.NewLabel(formatTime(), false) + label := basicElements.NewLabel(formatTime(), false) container.Adopt(label, false) window.OnClose(tomo.Stop) @@ -33,7 +33,7 @@ func formatTime () (timeString string) { return time.Now().Format("2006-01-02 15:04:05") } -func tick (label *basic.Label, clock *fun.AnalogClock) { +func tick (label *basicElements.Label, clock *fun.AnalogClock) { for { tomo.Do (func () { label.SetText(formatTime()) diff --git a/examples/horizontalLayout/main.go b/examples/horizontalLayout/main.go index 39ecc0c..6bc45da 100644 --- a/examples/horizontalLayout/main.go +++ b/examples/horizontalLayout/main.go @@ -1,7 +1,7 @@ package main import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -13,12 +13,12 @@ func run () { window, _ := tomo.NewWindow(360, 2) window.SetTitle("horizontal stack") - container := basic.NewContainer(layouts.Horizontal { true, true }) + container := basicElements.NewContainer(basicLayouts.Horizontal { true, true }) window.Adopt(container) - container.Adopt(basic.NewLabel("this is sample text", true), true) - container.Adopt(basic.NewLabel("this is sample text", true), true) - container.Adopt(basic.NewLabel("this is sample text", true), true) + container.Adopt(basicElements.NewLabel("this is sample text", true), true) + container.Adopt(basicElements.NewLabel("this is sample text", true), true) + container.Adopt(basicElements.NewLabel("this is sample text", true), true) window.OnClose(tomo.Stop) window.Show() diff --git a/examples/input/main.go b/examples/input/main.go index f16bdbd..ea08ca3 100644 --- a/examples/input/main.go +++ b/examples/input/main.go @@ -2,7 +2,7 @@ package main import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/popups" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -13,14 +13,14 @@ func main () { func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("Enter Details") - container := basic.NewContainer(layouts.Vertical { true, true }) + container := basicElements.NewContainer(basicLayouts.Vertical { true, true }) window.Adopt(container) // create inputs - firstName := basic.NewTextBox("First name", "") - lastName := basic.NewTextBox("Last name", "") - fingerLength := basic.NewTextBox("Length of fingers", "") - button := basic.NewButton("Ok") + firstName := basicElements.NewTextBox("First name", "") + lastName := basicElements.NewTextBox("Last name", "") + fingerLength := basicElements.NewTextBox("Length of fingers", "") + button := basicElements.NewButton("Ok") button.SetEnabled(false) button.OnClick (func () { @@ -45,11 +45,11 @@ func run () { fingerLength.OnChange(check) // add elements to container - container.Adopt(basic.NewLabel("Choose your words carefully.", false), true) + container.Adopt(basicElements.NewLabel("Choose your words carefully.", false), true) container.Adopt(firstName, false) container.Adopt(lastName, false) container.Adopt(fingerLength, false) - container.Adopt(basic.NewSpacer(true), false) + container.Adopt(basicElements.NewSpacer(true), false) container.Adopt(button, false) window.OnClose(tomo.Stop) diff --git a/examples/label/main.go b/examples/label/main.go index 9a0f59b..45633bb 100644 --- a/examples/label/main.go +++ b/examples/label/main.go @@ -11,7 +11,7 @@ func main () { func run () { window, _ := tomo.NewWindow(480, 2) window.SetTitle("example label") - window.Adopt(basic.NewLabel(text, true)) + window.Adopt(basicElements.NewLabel(text, true)) window.OnClose(tomo.Stop) window.Show() } diff --git a/examples/list/main.go b/examples/list/main.go index 731c635..151db82 100644 --- a/examples/list/main.go +++ b/examples/list/main.go @@ -2,7 +2,8 @@ package main import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/popups" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/elements" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/testing" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -15,11 +16,11 @@ func run () { window, _ := tomo.NewWindow(300, 2) window.SetTitle("List Sidebar") - container := basic.NewContainer(layouts.Horizontal { true, true }) + container := basicElements.NewContainer(basicLayouts.Horizontal { true, true }) window.Adopt(container) - var currentPage tomo.Element - turnPage := func (newPage tomo.Element) { + var currentPage elements.Element + turnPage := func (newPage elements.Element) { container.Warp (func () { if currentPage != nil { container.Disown(currentPage) @@ -29,27 +30,27 @@ func run () { }) } - intro := basic.NewLabel ( + intro := basicElements.NewLabel ( "The List element can be easily used as a sidebar. " + "Click on entries to flip pages!", true) - button := basic.NewButton("I do nothing!") + button := basicElements.NewButton("I do nothing!") button.OnClick (func () { popups.NewDialog(popups.DialogKindInfo, "", "Sike!") }) mouse := testing.NewMouse() - input := basic.NewTextBox("Write some text", "") - form := basic.NewContainer(layouts.Vertical { true, false}) - form.Adopt(basic.NewLabel("I have:", false), false) - form.Adopt(basic.NewSpacer(true), false) - form.Adopt(basic.NewCheckbox("Skin", true), false) - form.Adopt(basic.NewCheckbox("Blood", false), false) - form.Adopt(basic.NewCheckbox("Bone", false), false) + input := basicElements.NewTextBox("Write some text", "") + form := basicElements.NewContainer(basicLayouts.Vertical { true, false}) + form.Adopt(basicElements.NewLabel("I have:", false), false) + form.Adopt(basicElements.NewSpacer(true), false) + form.Adopt(basicElements.NewCheckbox("Skin", true), false) + form.Adopt(basicElements.NewCheckbox("Blood", false), false) + form.Adopt(basicElements.NewCheckbox("Bone", false), false) - list := basic.NewList ( - basic.NewListEntry("button", func () { turnPage(button) }), - basic.NewListEntry("mouse", func () { turnPage(mouse) }), - basic.NewListEntry("input", func () { turnPage(input) }), - basic.NewListEntry("form", func () { turnPage(form) })) + list := basicElements.NewList ( + basicElements.NewListEntry("button", func () { turnPage(button) }), + basicElements.NewListEntry("mouse", func () { turnPage(mouse) }), + basicElements.NewListEntry("input", func () { turnPage(input) }), + basicElements.NewListEntry("form", func () { turnPage(form) })) list.OnNoEntrySelected(func () { turnPage (intro) }) list.Collapse(96, 0) diff --git a/examples/popups/main.go b/examples/popups/main.go index 5f338d4..819ae7c 100644 --- a/examples/popups/main.go +++ b/examples/popups/main.go @@ -2,7 +2,7 @@ package main import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/popups" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -14,12 +14,12 @@ func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("Dialog Boxes") - container := basic.NewContainer(layouts.Vertical { true, true }) + container := basicElements.NewContainer(basicLayouts.Vertical { true, true }) window.Adopt(container) - container.Adopt(basic.NewLabel("Try out different dialogs:", false), true) + container.Adopt(basicElements.NewLabel("Try out different dialogs:", false), true) - infoButton := basic.NewButton("popups.DialogKindInfo") + infoButton := basicElements.NewButton("popups.DialogKindInfo") infoButton.OnClick (func () { popups.NewDialog ( popups.DialogKindInfo, @@ -29,7 +29,7 @@ func run () { container.Adopt(infoButton, false) infoButton.Focus() - questionButton := basic.NewButton("popups.DialogKindQuestion") + questionButton := basicElements.NewButton("popups.DialogKindQuestion") questionButton.OnClick (func () { popups.NewDialog ( popups.DialogKindQuestion, @@ -41,7 +41,7 @@ func run () { }) container.Adopt(questionButton, false) - warningButton := basic.NewButton("popups.DialogKindWarning") + warningButton := basicElements.NewButton("popups.DialogKindWarning") warningButton.OnClick (func () { popups.NewDialog ( popups.DialogKindQuestion, @@ -50,7 +50,7 @@ func run () { }) container.Adopt(warningButton, false) - errorButton := basic.NewButton("popups.DialogKindError") + errorButton := basicElements.NewButton("popups.DialogKindError") errorButton.OnClick (func () { popups.NewDialog ( popups.DialogKindQuestion, @@ -59,7 +59,7 @@ func run () { }) container.Adopt(errorButton, false) - cancelButton := basic.NewButton("No thank you.") + cancelButton := basicElements.NewButton("No thank you.") cancelButton.OnClick(tomo.Stop) container.Adopt(cancelButton, false) diff --git a/examples/progress/main.go b/examples/progress/main.go index 0af985c..0cc291c 100644 --- a/examples/progress/main.go +++ b/examples/progress/main.go @@ -3,7 +3,7 @@ package main import "time" import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/popups" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -14,14 +14,14 @@ func main () { func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("Approaching") - container := basic.NewContainer(layouts.Vertical { true, true }) + container := basicElements.NewContainer(basicLayouts.Vertical { true, true }) window.Adopt(container) - container.Adopt (basic.NewLabel ( + container.Adopt (basicElements.NewLabel ( "Rapidly approaching your location...", false), false) - bar := basic.NewProgressBar(0) + bar := basicElements.NewProgressBar(0) container.Adopt(bar, false) - button := basic.NewButton("Stop") + button := basicElements.NewButton("Stop") button.SetEnabled(false) container.Adopt(button, false) @@ -30,7 +30,7 @@ func run () { go fill(bar) } -func fill (bar *basic.ProgressBar) { +func fill (bar *basicElements.ProgressBar) { for progress := 0.0; progress < 1.0; progress += 0.01 { time.Sleep(time.Second / 24) tomo.Do (func () { diff --git a/examples/scroll/main.go b/examples/scroll/main.go index 6389c13..749ed39 100644 --- a/examples/scroll/main.go +++ b/examples/scroll/main.go @@ -1,7 +1,7 @@ package main import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -12,13 +12,13 @@ func main () { func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("Scroll") - container := basic.NewContainer(layouts.Vertical { true, true }) + container := basicElements.NewContainer(basicLayouts.Vertical { true, true }) window.Adopt(container) - container.Adopt(basic.NewLabel("look at this non sense", false), false) + container.Adopt(basicElements.NewLabel("look at this non sense", false), false) - textBox := basic.NewTextBox("", "sample text sample text") - scrollContainer := basic.NewScrollContainer(true, false) + textBox := basicElements.NewTextBox("", "sample text sample text") + scrollContainer := basicElements.NewScrollContainer(true, false) scrollContainer.Adopt(textBox) container.Adopt(scrollContainer, true) diff --git a/examples/spacer/main.go b/examples/spacer/main.go index a365523..f9bbe59 100644 --- a/examples/spacer/main.go +++ b/examples/spacer/main.go @@ -1,7 +1,7 @@ package main import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -13,14 +13,14 @@ func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("Spaced Out") - container := basic.NewContainer(layouts.Vertical { true, true }) + container := basicElements.NewContainer(basicLayouts.Vertical { true, true }) window.Adopt(container) - container.Adopt (basic.NewLabel("This is at the top", false), false) - container.Adopt (basic.NewSpacer(true), false) - container.Adopt (basic.NewLabel("This is in the middle", false), false) - container.Adopt (basic.NewSpacer(false), true) - container.Adopt (basic.NewLabel("This is at the bottom", false), false) + container.Adopt (basicElements.NewLabel("This is at the top", false), false) + container.Adopt (basicElements.NewSpacer(true), false) + container.Adopt (basicElements.NewLabel("This is in the middle", false), false) + container.Adopt (basicElements.NewSpacer(false), true) + container.Adopt (basicElements.NewLabel("This is at the bottom", false), false) window.OnClose(tomo.Stop) window.Show() diff --git a/examples/switch/main.go b/examples/switch/main.go index acc684a..d677288 100644 --- a/examples/switch/main.go +++ b/examples/switch/main.go @@ -1,7 +1,7 @@ package main import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -13,12 +13,12 @@ func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("Switches") - container := basic.NewContainer(layouts.Vertical { true, true }) + container := basicElements.NewContainer(basicLayouts.Vertical { true, true }) window.Adopt(container) - container.Adopt(basic.NewSwitch("hahahah", false), false) - container.Adopt(basic.NewSwitch("hehehehheheh", false), false) - container.Adopt(basic.NewSwitch("you can flick da swicth", false), false) + container.Adopt(basicElements.NewSwitch("hahahah", false), false) + container.Adopt(basicElements.NewSwitch("hehehehheheh", false), false) + container.Adopt(basicElements.NewSwitch("you can flick da swicth", false), false) window.OnClose(tomo.Stop) window.Show() diff --git a/examples/verticalLayout/main.go b/examples/verticalLayout/main.go index 05356aa..d4d4930 100644 --- a/examples/verticalLayout/main.go +++ b/examples/verticalLayout/main.go @@ -1,7 +1,7 @@ package main import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/testing" import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" @@ -14,15 +14,15 @@ func run () { window, _ := tomo.NewWindow(2, 2) window.SetTitle("vertical stack") - container := basic.NewContainer(layouts.Vertical { true, true }) + container := basicElements.NewContainer(basicLayouts.Vertical { true, true }) window.Adopt(container) - label := basic.NewLabel("it is a label hehe", true) - button := basic.NewButton("drawing pad") - okButton := basic.NewButton("OK") + label := basicElements.NewLabel("it is a label hehe", true) + button := basicElements.NewButton("drawing pad") + okButton := basicElements.NewButton("OK") button.OnClick (func () { container.DisownAll() - container.Adopt(basic.NewLabel("Draw here:", false), false) + container.Adopt(basicElements.NewLabel("Draw here:", false), false) container.Adopt(testing.NewMouse(), true) container.Adopt(okButton, false) okButton.Focus() diff --git a/input.go b/input/input.go similarity index 82% rename from input.go rename to input/input.go index 6337058..b523829 100644 --- a/input.go +++ b/input/input.go @@ -1,4 +1,4 @@ -package tomo +package input import "unicode" @@ -110,3 +110,22 @@ type Modifiers struct { NumberPad bool } +// KeynavDirection represents a keyboard navigation direction. +type KeynavDirection int + +const ( + KeynavDirectionNeutral KeynavDirection = 0 + KeynavDirectionBackward KeynavDirection = -1 + KeynavDirectionForward KeynavDirection = 1 +) + +// Canon returns a well-formed direction. +func (direction KeynavDirection) Canon () (canon KeynavDirection) { + if direction > 0 { + return KeynavDirectionForward + } else if direction == 0 { + return KeynavDirectionNeutral + } else { + return KeynavDirectionBackward + } +} diff --git a/layouts/dialog.go b/layouts/basic/dialog.go similarity index 78% rename from layouts/dialog.go rename to layouts/basic/dialog.go index 836d4b3..ba75df7 100644 --- a/layouts/dialog.go +++ b/layouts/basic/dialog.go @@ -1,8 +1,8 @@ -package layouts +package basicLayouts import "image" -import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/theme" +import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/elements" // Dialog arranges elements in the form of a dialog box. The first element is // positioned above as the main focus of the dialog, and is set to expand @@ -19,13 +19,18 @@ type Dialog struct { } // Arrange arranges a list of entries into a dialog. -func (layout Dialog) Arrange (entries []tomo.LayoutEntry, bounds image.Rectangle) { - if layout.Pad { bounds = bounds.Inset(theme.Margin()) } +func (layout Dialog) Arrange ( + entries []layouts.LayoutEntry, + margin int, + bounds image.Rectangle, +) { + if layout.Pad { bounds = bounds.Inset(margin) } controlRowWidth, controlRowHeight := 0, 0 if len(entries) > 1 { controlRowWidth, - controlRowHeight = layout.minimumSizeOfControlRow(entries[1:]) + controlRowHeight = layout.minimumSizeOfControlRow ( + entries[1:], margin) } if len(entries) > 0 { @@ -33,7 +38,7 @@ func (layout Dialog) Arrange (entries []tomo.LayoutEntry, bounds image.Rectangle main.Bounds.Min = bounds.Min mainHeight := bounds.Dy() - controlRowHeight if layout.Gap { - mainHeight -= theme.Margin() + mainHeight -= margin } main.Bounds.Max = main.Bounds.Min.Add(image.Pt(bounds.Dx(), mainHeight)) entries[0] = main @@ -53,7 +58,7 @@ func (layout Dialog) Arrange (entries []tomo.LayoutEntry, bounds image.Rectangle freeSpace -= entryMinWidth } if index > 0 && layout.Gap { - freeSpace -= theme.Margin() + freeSpace -= margin } } expandingElementWidth := 0 @@ -69,7 +74,7 @@ func (layout Dialog) Arrange (entries []tomo.LayoutEntry, bounds image.Rectangle // set the size and position of each element in the control row for index, entry := range entries[1:] { - if index > 0 && layout.Gap { dot.X += theme.Margin() } + if index > 0 && layout.Gap { dot.X += margin } entry.Bounds.Min = dot entryWidth := 0 @@ -95,7 +100,8 @@ func (layout Dialog) Arrange (entries []tomo.LayoutEntry, bounds image.Rectangle // MinimumSize returns the minimum width and height that will be needed to // arrange the given list of entries. func (layout Dialog) MinimumSize ( - entries []tomo.LayoutEntry, + entries []layouts.LayoutEntry, + margin int, ) ( width, height int, ) { @@ -106,9 +112,10 @@ func (layout Dialog) MinimumSize ( } if len(entries) > 1 { - if layout.Gap { height += theme.Margin() } + if layout.Gap { height += margin } additionalWidth, - additionalHeight := layout.minimumSizeOfControlRow(entries[1:]) + additionalHeight := layout.minimumSizeOfControlRow ( + entries[1:], margin) height += additionalHeight if additionalWidth > width { width = additionalWidth @@ -116,8 +123,8 @@ func (layout Dialog) MinimumSize ( } if layout.Pad { - width += theme.Margin() * 2 - height += theme.Margin() * 2 + width += margin * 2 + height += margin * 2 } return } @@ -125,18 +132,19 @@ func (layout Dialog) MinimumSize ( // FlexibleHeightFor Returns the minimum height the layout needs to lay out the // specified elements at the given width, taking into account flexible elements. func (layout Dialog) FlexibleHeightFor ( - entries []tomo.LayoutEntry, + entries []layouts.LayoutEntry, + margin int, width int, ) ( height int, ) { if layout.Pad { - width -= theme.Margin() * 2 + width -= margin * 2 } if len(entries) > 0 { mainChildHeight := 0 - if child, flexible := entries[0].Element.(tomo.Flexible); flexible { + if child, flexible := entries[0].Element.(elements.Flexible); flexible { mainChildHeight = child.FlexibleHeightFor(width) } else { _, mainChildHeight = entries[0].MinimumSize() @@ -145,13 +153,14 @@ func (layout Dialog) FlexibleHeightFor ( } if len(entries) > 1 { - if layout.Gap { height += theme.Margin() } - _, additionalHeight := layout.minimumSizeOfControlRow(entries[1:]) + if layout.Gap { height += margin } + _, additionalHeight := layout.minimumSizeOfControlRow ( + entries[1:], margin) height += additionalHeight } if layout.Pad { - height += theme.Margin() * 2 + height += margin * 2 } return } @@ -159,7 +168,8 @@ func (layout Dialog) FlexibleHeightFor ( // TODO: possibly flatten this method to account for flexible elements within // the control row. func (layout Dialog) minimumSizeOfControlRow ( - entries []tomo.LayoutEntry, + entries []layouts.LayoutEntry, + margin int, ) ( width, height int, ) { @@ -170,7 +180,7 @@ func (layout Dialog) minimumSizeOfControlRow ( } width += entryWidth if layout.Gap && index > 0 { - width += theme.Margin() + width += margin } } return diff --git a/layouts/horizontal.go b/layouts/basic/horizontal.go similarity index 72% rename from layouts/horizontal.go rename to layouts/basic/horizontal.go index 95c414b..220dcb8 100644 --- a/layouts/horizontal.go +++ b/layouts/basic/horizontal.go @@ -1,8 +1,8 @@ -package layouts +package basicLayouts import "image" -import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/theme" +import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/elements" // Horizontal arranges elements horizontally. Elements at the start of the entry // list will be positioned on the left, and elements at the end of the entry @@ -17,16 +17,21 @@ type Horizontal struct { } // Arrange arranges a list of entries horizontally. -func (layout Horizontal) Arrange (entries []tomo.LayoutEntry, bounds image.Rectangle) { - if layout.Pad { bounds = bounds.Inset(theme.Margin()) } +func (layout Horizontal) Arrange ( + entries []layouts.LayoutEntry, + margin int, + bounds image.Rectangle, +) { + if layout.Pad { bounds = bounds.Inset(margin) } // get width of expanding elements - expandingElementWidth := layout.expandingElementWidth(entries, bounds.Dx()) + expandingElementWidth := layout.expandingElementWidth ( + entries, margin, bounds.Dx()) // set the size and position of each element dot := bounds.Min for index, entry := range entries { - if index > 0 && layout.Gap { dot.X += theme.Margin() } + if index > 0 && layout.Gap { dot.X += margin } entry.Bounds.Min = dot entryWidth := 0 @@ -45,7 +50,8 @@ func (layout Horizontal) Arrange (entries []tomo.LayoutEntry, bounds image.Recta // MinimumSize returns the minimum width and height that will be needed to // arrange the given list of entries. func (layout Horizontal) MinimumSize ( - entries []tomo.LayoutEntry, + entries []layouts.LayoutEntry, + margin int, ) ( width, height int, ) { @@ -56,13 +62,13 @@ func (layout Horizontal) MinimumSize ( } width += entryWidth if layout.Gap && index > 0 { - width += theme.Margin() + width += margin } } if layout.Pad { - width += theme.Margin() * 2 - height += theme.Margin() * 2 + width += margin * 2 + height += margin * 2 } return } @@ -70,21 +76,22 @@ func (layout Horizontal) MinimumSize ( // FlexibleHeightFor Returns the minimum height the layout needs to lay out the // specified elements at the given width, taking into account flexible elements. func (layout Horizontal) FlexibleHeightFor ( - entries []tomo.LayoutEntry, + entries []layouts.LayoutEntry, + margin int, width int, ) ( height int, ) { - if layout.Pad { - width -= theme.Margin() * 2 - } + if layout.Pad { width -= margin * 2 } + // get width of expanding elements - expandingElementWidth := layout.expandingElementWidth(entries, width) + expandingElementWidth := layout.expandingElementWidth ( + entries, margin, width) x, y := 0, 0 if layout.Pad { - x += theme.Margin() - y += theme.Margin() + x += margin + y += margin } // set the size and position of each element @@ -93,23 +100,24 @@ func (layout Horizontal) FlexibleHeightFor ( if entry.Expand { entryWidth = expandingElementWidth } - if child, flexible := entry.Element.(tomo.Flexible); flexible { + if child, flexible := entry.Element.(elements.Flexible); flexible { entryHeight = child.FlexibleHeightFor(entryWidth) } if entryHeight > height { height = entryHeight } x += entryWidth - if index > 0 && layout.Gap { x += theme.Margin() } + if index > 0 && layout.Gap { x += margin } } if layout.Pad { - height += theme.Margin() * 2 + height += margin * 2 } return } func (layout Horizontal) expandingElementWidth ( - entries []tomo.LayoutEntry, + entries []layouts.LayoutEntry, + margin int, freeSpace int, ) ( width int, @@ -126,7 +134,7 @@ func (layout Horizontal) expandingElementWidth ( freeSpace -= entryMinWidth } if index > 0 && layout.Gap { - freeSpace -= theme.Margin() + freeSpace -= margin } } diff --git a/layouts/vertical.go b/layouts/basic/vertical.go similarity index 78% rename from layouts/vertical.go rename to layouts/basic/vertical.go index 9455bc0..2002db4 100644 --- a/layouts/vertical.go +++ b/layouts/basic/vertical.go @@ -1,8 +1,8 @@ -package layouts +package basicLayouts import "image" -import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/theme" +import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/elements" // Vertical arranges elements vertically. Elements at the start of the entry // list will be positioned at the top, and elements at the end of the entry list @@ -17,8 +17,12 @@ type Vertical struct { } // Arrange arranges a list of entries vertically. -func (layout Vertical) Arrange (entries []tomo.LayoutEntry, bounds image.Rectangle) { - if layout.Pad { bounds = bounds.Inset(theme.Margin()) } +func (layout Vertical) Arrange ( + entries []layouts.LayoutEntry, + margin int, + bounds image.Rectangle, +) { + if layout.Pad { bounds = bounds.Inset(margin) } // count the number of expanding elements and the amount of free space // for them to collectively occupy, while gathering minimum heights. @@ -28,7 +32,7 @@ func (layout Vertical) Arrange (entries []tomo.LayoutEntry, bounds image.Rectang for index, entry := range entries { var entryMinHeight int - if child, flexible := entry.Element.(tomo.Flexible); flexible { + if child, flexible := entry.Element.(elements.Flexible); flexible { entryMinHeight = child.FlexibleHeightFor(bounds.Dx()) } else { _, entryMinHeight = entry.MinimumSize() @@ -41,7 +45,7 @@ func (layout Vertical) Arrange (entries []tomo.LayoutEntry, bounds image.Rectang freeSpace -= entryMinHeight } if index > 0 && layout.Gap { - freeSpace -= theme.Margin() + freeSpace -= margin } } @@ -53,7 +57,7 @@ func (layout Vertical) Arrange (entries []tomo.LayoutEntry, bounds image.Rectang // set the size and position of each element dot := bounds.Min for index, entry := range entries { - if index > 0 && layout.Gap { dot.Y += theme.Margin() } + if index > 0 && layout.Gap { dot.Y += margin } entry.Bounds.Min = dot entryHeight := 0 @@ -72,7 +76,8 @@ func (layout Vertical) Arrange (entries []tomo.LayoutEntry, bounds image.Rectang // MinimumSize returns the minimum width and height that will be needed to // arrange the given list of entries. func (layout Vertical) MinimumSize ( - entries []tomo.LayoutEntry, + entries []layouts.LayoutEntry, + margin int, ) ( width, height int, ) { @@ -83,13 +88,13 @@ func (layout Vertical) MinimumSize ( } height += entryHeight if layout.Gap && index > 0 { - height += theme.Margin() + height += margin } } if layout.Pad { - width += theme.Margin() * 2 - height += theme.Margin() * 2 + width += margin * 2 + height += margin * 2 } return } @@ -97,18 +102,19 @@ func (layout Vertical) MinimumSize ( // FlexibleHeightFor Returns the minimum height the layout needs to lay out the // specified elements at the given width, taking into account flexible elements. func (layout Vertical) FlexibleHeightFor ( - entries []tomo.LayoutEntry, + entries []layouts.LayoutEntry, + margin int, width int, ) ( height int, ) { if layout.Pad { - width -= theme.Margin() * 2 - height += theme.Margin() * 2 + width -= margin * 2 + height += margin * 2 } for index, entry := range entries { - child, flexible := entry.Element.(tomo.Flexible) + child, flexible := entry.Element.(elements.Flexible) if flexible { height += child.FlexibleHeightFor(width) } else { @@ -117,7 +123,7 @@ func (layout Vertical) FlexibleHeightFor ( } if layout.Gap && index > 0 { - height += theme.Margin() + height += margin } } return diff --git a/layout.go b/layouts/layout.go similarity index 74% rename from layout.go rename to layouts/layout.go index b6dc034..1e44f9b 100644 --- a/layout.go +++ b/layouts/layout.go @@ -1,11 +1,12 @@ -package tomo +package layouts import "image" +import "git.tebibyte.media/sashakoshka/tomo/elements" // LayoutEntry associates an element with layout and positioning information so // it can be arranged by a Layout. type LayoutEntry struct { - Element + elements.Element Bounds image.Rectangle Expand bool } @@ -17,14 +18,20 @@ type Layout interface { // and changes the position of the entiries in the slice so that they // are properly laid out. The given width and height should not be less // than what is returned by MinimumSize. - Arrange (entries []LayoutEntry, bounds image.Rectangle) + Arrange (entries []LayoutEntry, margin int, bounds image.Rectangle) // MinimumSize returns the minimum width and height that the layout // needs to properly arrange the given slice of layout entries. - MinimumSize (entries []LayoutEntry) (width, height int) + MinimumSize (entries []LayoutEntry, margin int) (width, height int) // FlexibleHeightFor Returns the minimum height the layout needs to lay // out the specified elements at the given width, taking into account // flexible elements. - FlexibleHeightFor (entries []LayoutEntry, squeeze int) (height int) + FlexibleHeightFor ( + entries []LayoutEntry, + margin int, + squeeze int, + ) ( + height int, + ) } diff --git a/popups/dialog.go b/popups/dialog.go index 14f598c..c8634d8 100644 --- a/popups/dialog.go +++ b/popups/dialog.go @@ -1,7 +1,8 @@ package popups import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/layouts" +import "git.tebibyte.media/sashakoshka/tomo/elements" +import "git.tebibyte.media/sashakoshka/tomo/layouts/basic" import "git.tebibyte.media/sashakoshka/tomo/elements/basic" // DialogKind defines the semantic role of a dialog window. @@ -30,24 +31,24 @@ func NewDialog ( title, message string, buttons ...Button, ) ( - window tomo.Window, + window elements.Window, ) { window, _ = tomo.NewWindow(2, 2) window.SetTitle(title) - container := basic.NewContainer(layouts.Dialog { true, true }) + container := basicElements.NewContainer(basicLayouts.Dialog { true, true }) window.Adopt(container) - container.Adopt(basic.NewLabel(message, false), true) + container.Adopt(basicElements.NewLabel(message, false), true) if len(buttons) == 0 { - button := basic.NewButton("OK") + button := basicElements.NewButton("OK") button.OnClick(window.Close) container.Adopt(button, false) button.Focus() } else { - var button *basic.Button + var button *basicElements.Button for _, buttonDescriptor := range buttons { - button = basic.NewButton(buttonDescriptor.Name) + button = basicElements.NewButton(buttonDescriptor.Name) button.SetEnabled(buttonDescriptor.OnPress != nil) button.OnClick (func () { buttonDescriptor.OnPress() diff --git a/theme/theme.go b/theme/theme.go index 168b25d..3f59fe6 100644 --- a/theme/theme.go +++ b/theme/theme.go @@ -111,24 +111,3 @@ func FontFaceItalic () font.Face { func FontFaceBoldItalic () font.Face { return defaultfont.FaceBoldItalic } - -// Padding returns the amount of internal padding elements should have. An -// element's inner content (such as text) should be inset by this amount, -// in addition to the inset returned by the pattern of its background. When -// using the aforementioned inset values to calculate the element's minimum size -// or the position and alignment of its content, all parameters in the -// PatternState should be unset except for Case. -func Padding () int { - return 7 -} - -// Margin returns how much space should be put in between elements. -func Margin () int { - return 8 -} - -// HandleWidth returns how large grab handles should typically be. This is -// important for accessibility reasons. -func HandleWidth () int { - return 16 -} diff --git a/tomo.go b/tomo.go index 5dc4dde..b1416b0 100644 --- a/tomo.go +++ b/tomo.go @@ -1,6 +1,8 @@ package tomo import "errors" +import "git.tebibyte.media/sashakoshka/tomo/data" +import "git.tebibyte.media/sashakoshka/tomo/elements" var backend Backend @@ -32,7 +34,7 @@ func Do (callback func ()) { // Window. If the window could not be created, an error is returned explaining // why. If this function is called without a running backend, an error is // returned as well. -func NewWindow (width, height int) (window Window, err error) { +func NewWindow (width, height int) (window elements.Window, err error) { if backend == nil { err = errors.New("no backend is running.") return @@ -41,14 +43,14 @@ func NewWindow (width, height int) (window Window, err error) { } // Copy puts data into the clipboard. -func Copy (data Data) { +func Copy (data data.Data) { if backend == nil { panic("no backend is running") } backend.Copy(data) } // Paste returns the data currently in the clipboard. This method may // return nil. -func Paste (accept []Mime) (Data) { +func Paste (accept []data.Mime) (data.Data) { if backend == nil { panic("no backend is running") } return backend.Paste(accept) }