atomize-modules #7
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
| ) ( | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
| @ -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), | ||||
|  | ||||
| @ -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() | ||||
| 		} | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| package tomo | ||||
| package canvas | ||||
| 
 | ||||
| import "image" | ||||
| import "image/draw" | ||||
							
								
								
									
										22
									
								
								config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								config/config.go
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| package tomo | ||||
| package data | ||||
| 
 | ||||
| import "io" | ||||
| 
 | ||||
| @ -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() | ||||
|  | ||||
| @ -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() { | ||||
|  | ||||
| @ -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()) | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| package basic | ||||
| package basicElements | ||||
| 
 | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| package basic | ||||
| package basicElements | ||||
| 
 | ||||
| import "image" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
|  | ||||
| @ -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 () { | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| package basic | ||||
| package basicElements | ||||
| 
 | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
|  | ||||
| @ -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() { | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
| @ -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)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
| @ -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 ( | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| package tomo | ||||
| package elements | ||||
| 
 | ||||
| import "image" | ||||
| 
 | ||||
| @ -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. | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -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() | ||||
| 		 | ||||
|  | ||||
| @ -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 () {	 | ||||
|  | ||||
| @ -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()) | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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() | ||||
| } | ||||
|  | ||||
| @ -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) | ||||
| 	 | ||||
|  | ||||
| @ -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) | ||||
| 		 | ||||
|  | ||||
| @ -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 () { | ||||
|  | ||||
| @ -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) | ||||
| 	 | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -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 | ||||
| 	} | ||||
| } | ||||
| @ -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 | ||||
| @ -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 | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| @ -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 | ||||
| @ -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, | ||||
| 	) | ||||
| } | ||||
| @ -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() | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
							
								
								
									
										8
									
								
								tomo.go
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								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) | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user