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, | ||||
| ) ( | ||||
|  | ||||
							
								
								
									
										90
									
								
								canvas.go
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								canvas.go
									
									
									
									
									
								
							| @ -1,90 +0,0 @@ | ||||
| package tomo | ||||
| 
 | ||||
| import "image" | ||||
| import "image/draw" | ||||
| import "image/color" | ||||
| 
 | ||||
| // Canvas is like draw.Image but is also able to return a raw pixel buffer for | ||||
| // more efficient drawing. This interface can be easily satisfied using a | ||||
| // BasicCanvas struct. | ||||
| type Canvas interface { | ||||
| 	draw.Image | ||||
| 	Buffer () (data []color.RGBA, stride int) | ||||
| } | ||||
| 
 | ||||
| // BasicCanvas is a general purpose implementation of tomo.Canvas. | ||||
| type BasicCanvas struct { | ||||
| 	pix    []color.RGBA | ||||
| 	stride int | ||||
| 	rect   image.Rectangle | ||||
| } | ||||
| 
 | ||||
| // NewBasicCanvas creates a new basic canvas with the specified width and | ||||
| // height, allocating a buffer for it. | ||||
| func NewBasicCanvas (width, height int) (canvas BasicCanvas) { | ||||
| 	canvas.pix    = make([]color.RGBA, height * width) | ||||
| 	canvas.stride = width | ||||
| 	canvas.rect = image.Rect(0, 0, width, height) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // you know what it do | ||||
| func (canvas BasicCanvas) Bounds () (bounds image.Rectangle) { | ||||
| 	return canvas.rect | ||||
| } | ||||
| 
 | ||||
| // you know what it do | ||||
| func (canvas BasicCanvas) At (x, y int) (color.Color) { | ||||
| 	if !image.Pt(x, y).In(canvas.rect) { return nil } | ||||
| 	return canvas.pix[x + y * canvas.stride] | ||||
| } | ||||
| 
 | ||||
| // you know what it do | ||||
| func (canvas BasicCanvas) ColorModel () (model color.Model) { | ||||
| 	return color.RGBAModel | ||||
| } | ||||
| 
 | ||||
| // you know what it do | ||||
| func (canvas BasicCanvas) Set (x, y int, c color.Color) { | ||||
| 	if !image.Pt(x, y).In(canvas.rect) { return } | ||||
| 	r, g, b, a := c.RGBA() | ||||
| 	canvas.pix[x + y * canvas.stride] = color.RGBA { | ||||
| 		R: uint8(r >> 8), | ||||
| 		G: uint8(g >> 8), | ||||
| 		B: uint8(b >> 8), | ||||
| 		A: uint8(a >> 8), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // you know what it do | ||||
| func (canvas BasicCanvas) Buffer () (data []color.RGBA, stride int) { | ||||
| 	return canvas.pix, canvas.stride | ||||
| } | ||||
| 
 | ||||
| // Reallocate efficiently reallocates the canvas. The data within will be | ||||
| // garbage. This method will do nothing if this is a cut image. | ||||
| func (canvas *BasicCanvas) Reallocate (width, height int) { | ||||
| 	if canvas.rect.Min != (image.Point { }) { return } | ||||
| 
 | ||||
| 	previousLen := len(canvas.pix) | ||||
| 	newLen := width * height | ||||
| 	bigger  := newLen > previousLen | ||||
| 	smaller := newLen < previousLen / 2 | ||||
| 	if bigger || smaller { | ||||
| 		canvas.pix = make ( | ||||
| 			[]color.RGBA, | ||||
| 			((height * width) / 4096) * 4096 + 4096) | ||||
| 	} | ||||
| 	canvas.stride = width | ||||
| 	canvas.rect = image.Rect(0, 0, width, height) | ||||
| } | ||||
| 
 | ||||
| // Cut returns a sub-canvas of a given canvas. | ||||
| func Cut (canvas Canvas, bounds image.Rectangle) (reduced BasicCanvas) { | ||||
| 	// println(canvas.Bounds().String(), bounds.String()) | ||||
| 	bounds = bounds.Intersect(canvas.Bounds()) | ||||
| 	if bounds.Empty() { return } | ||||
| 	reduced.rect = bounds | ||||
| 	reduced.pix, reduced.stride = canvas.Buffer() | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										20
									
								
								data.go
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								data.go
									
									
									
									
									
								
							| @ -1,20 +0,0 @@ | ||||
| package tomo | ||||
| 
 | ||||
| import "io" | ||||
| 
 | ||||
| // Data represents arbitrary polymorphic data that can be used for data transfer | ||||
| // between applications. | ||||
| type Data map[Mime] io.ReadCloser | ||||
| 
 | ||||
| // Mime represents a MIME type. | ||||
| type Mime struct { | ||||
| 	// Type is the first half of the MIME type, and Subtype is the second | ||||
| 	// half. The separating slash is not included in either. For example, | ||||
| 	// text/html becomes: | ||||
| 	// Mime { Type: "text", Subtype: "html" } | ||||
| 	Type, Subtype string | ||||
| } | ||||
| 
 | ||||
| var MimePlain = Mime { "text", "plain" } | ||||
| 
 | ||||
| var MimeFile = Mime { "text", "uri-list" } | ||||
							
								
								
									
										177
									
								
								element.go
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								element.go
									
									
									
									
									
								
							| @ -1,177 +0,0 @@ | ||||
| package tomo | ||||
| 
 | ||||
| import "image" | ||||
| 
 | ||||
| // 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 | ||||
| 
 | ||||
| 	// MinimumSize specifies the minimum amount of pixels this element's | ||||
| 	// width and height may be set to. If the element is given a resize | ||||
| 	// event with dimensions smaller than this, it will use its minimum | ||||
| 	// instead of the offending dimension(s). | ||||
| 	MinimumSize () (width, height int) | ||||
| 
 | ||||
| 	// 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) | ||||
| 
 | ||||
| 	// 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)) | ||||
| 
 | ||||
| 	// 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). | ||||
| type Focusable interface { | ||||
| 	Element | ||||
| 
 | ||||
| 	// Focused returns whether or not this element is currently focused. | ||||
| 	Focused () (selected bool) | ||||
| 
 | ||||
| 	// Focus focuses this element, if its parent element grants the | ||||
| 	// request. | ||||
| 	Focus () | ||||
| 
 | ||||
| 	// HandleFocus causes this element to mark itself as focused. If the | ||||
| 	// element does not have children, it is disabled, or there are no more | ||||
| 	// 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) | ||||
| 
 | ||||
| 	// HandleDeselection causes this element to mark itself and all of its | ||||
| 	// children as unfocused. | ||||
| 	HandleUnfocus () | ||||
| 
 | ||||
| 	// OnFocusRequest sets a function to be called when this element wants | ||||
| 	// its parent element to focus it. Parent elements should return true if | ||||
| 	// the request was granted, and false if it was not. | ||||
| 	OnFocusRequest (func () (granted bool)) | ||||
| 
 | ||||
| 	// OnFocusMotionRequest sets a function to be called when this | ||||
| 	// element wants its parent element to focus the element behind or in | ||||
| 	// 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)) | ||||
| } | ||||
| 
 | ||||
| // KeyboardTarget represents an element that can receive keyboard input. | ||||
| type KeyboardTarget interface { | ||||
| 	Element | ||||
| 
 | ||||
| 	// HandleKeyDown is called when a key is pressed down or repeated while | ||||
| 	// this element has keyboard focus. It is important to note that not | ||||
| 	// 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) | ||||
| 
 | ||||
| 	// HandleKeyUp is called when a key is released while this element has | ||||
| 	// keyboard focus. | ||||
| 	HandleKeyUp (key Key, modifiers Modifiers) | ||||
| } | ||||
| 
 | ||||
| // MouseTarget represents an element that can receive mouse events. | ||||
| type MouseTarget interface { | ||||
| 	Element | ||||
| 
 | ||||
| 	// Each of these handler methods is passed the position of the mouse | ||||
| 	// cursor at the time of the event as x, y. | ||||
| 
 | ||||
| 	// HandleMouseDown is called when a mouse button is pressed down on this | ||||
| 	// element. | ||||
| 	HandleMouseDown (x, y int, button Button) | ||||
| 
 | ||||
| 	// HandleMouseUp is called when a mouse button is released that was | ||||
| 	// originally pressed down on this element. | ||||
| 	HandleMouseUp (x, y int, button Button) | ||||
| 
 | ||||
| 	// HandleMouseMove is called when the mouse is moved over this element, | ||||
| 	// or the mouse is moving while being held down and originally pressed | ||||
| 	// down on this element. | ||||
| 	HandleMouseMove (x, y int) | ||||
| 
 | ||||
| 	// HandleScroll is called when the mouse is scrolled. The X and Y | ||||
| 	// direction of the scroll event are passed as deltaX and deltaY. | ||||
| 	HandleMouseScroll (x, y int, deltaX, deltaY float64) | ||||
| } | ||||
| 
 | ||||
| // Flexible represents an element who's preferred minimum height can change in | ||||
| // response to its width. | ||||
| type Flexible interface { | ||||
| 	Element | ||||
| 
 | ||||
| 	// FlexibleHeightFor returns what the element's minimum height would be | ||||
| 	// if resized to a specified width. This does not actually alter the | ||||
| 	// state of the element in any way, but it may perform significant work, | ||||
| 	// so it should be called sparingly. | ||||
| 	// | ||||
| 	// It is reccomended that parent containers check for this interface and | ||||
| 	// take this method's value into account in order to support things like | ||||
| 	// flow layouts and text wrapping, but it is not absolutely necessary. | ||||
| 	// The element's MinimumSize method will still return the absolute | ||||
| 	// minimum size that the element may be resized to. | ||||
| 	// | ||||
| 	// It is important to note that if a parent container checks for | ||||
| 	// flexible chilren, it itself will likely need to be flexible. | ||||
| 	FlexibleHeightFor (width int) (height int) | ||||
| 
 | ||||
| 	// OnFlexibleHeightChange sets a function to be called when the | ||||
| 	// parameters affecting this element's flexible height are changed. | ||||
| 	OnFlexibleHeightChange (callback func ()) | ||||
| } | ||||
| 
 | ||||
| // Scrollable represents an element that can be scrolled. It acts as a viewport | ||||
| // through which its contents can be observed. | ||||
| type Scrollable interface { | ||||
| 	Element | ||||
| 
 | ||||
| 	// ScrollContentBounds returns the full content size of the element. | ||||
| 	ScrollContentBounds () (bounds image.Rectangle) | ||||
| 
 | ||||
| 	// ScrollViewportBounds returns the size and position of the element's | ||||
| 	// viewport relative to ScrollBounds. | ||||
| 	ScrollViewportBounds () (bounds image.Rectangle) | ||||
| 
 | ||||
| 	// ScrollTo scrolls the viewport to the specified point relative to | ||||
| 	// ScrollBounds. | ||||
| 	ScrollTo (position image.Point) | ||||
| 
 | ||||
| 	// ScrollAxes returns the supported axes for scrolling. | ||||
| 	ScrollAxes () (horizontal, vertical bool) | ||||
| 
 | ||||
| 	// OnScrollBoundsChange sets a function to be called when the element's | ||||
| 	// ScrollContentBounds, ScrollViewportBounds, or ScrollAxes are changed. | ||||
| 	OnScrollBoundsChange (callback func ()) | ||||
| } | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
| @ -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 ( | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
							
								
								
									
										112
									
								
								input.go
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								input.go
									
									
									
									
									
								
							| @ -1,112 +0,0 @@ | ||||
| package tomo | ||||
| 
 | ||||
| import "unicode" | ||||
| 
 | ||||
| // Key represents a keyboard key. | ||||
| type Key int | ||||
| 
 | ||||
| const ( | ||||
| 	KeyNone Key = 0 | ||||
| 	 | ||||
| 	KeyInsert      Key = 1 | ||||
| 	KeyMenu        Key = 2 | ||||
| 	KeyPrintScreen Key = 3 | ||||
| 	KeyPause       Key = 4 | ||||
| 	KeyCapsLock    Key = 5 | ||||
| 	KeyScrollLock  Key = 6 | ||||
| 	KeyNumLock     Key = 7 | ||||
| 	KeyBackspace   Key = 8 | ||||
| 	KeyTab         Key = 9 | ||||
| 	KeyEnter       Key = 10 | ||||
| 	KeyEscape      Key = 11 | ||||
| 	 | ||||
| 	KeyUp       Key = 12 | ||||
| 	KeyDown     Key = 13 | ||||
| 	KeyLeft     Key = 14 | ||||
| 	KeyRight    Key = 15 | ||||
| 	KeyPageUp   Key = 16 | ||||
| 	KeyPageDown Key = 17 | ||||
| 	KeyHome     Key = 18 | ||||
| 	KeyEnd      Key = 19 | ||||
| 	 | ||||
| 	KeyLeftShift    Key = 20 | ||||
| 	KeyRightShift   Key = 21 | ||||
| 	KeyLeftControl  Key = 22 | ||||
| 	KeyRightControl Key = 23 | ||||
| 	KeyLeftAlt      Key = 24 | ||||
| 	KeyRightAlt     Key = 25 | ||||
| 	KeyLeftMeta     Key = 26 | ||||
| 	KeyRightMeta    Key = 27	 | ||||
| 	KeyLeftSuper    Key = 28 | ||||
| 	KeyRightSuper   Key = 29 | ||||
| 	KeyLeftHyper    Key = 30 | ||||
| 	KeyRightHyper   Key = 31 | ||||
| 	 | ||||
| 	KeyDelete Key = 127 | ||||
| 	 | ||||
| 	KeyDead Key = 128 | ||||
| 	 | ||||
| 	KeyF1  Key = 129 | ||||
| 	KeyF2  Key = 130 | ||||
| 	KeyF3  Key = 131 | ||||
| 	KeyF4  Key = 132 | ||||
| 	KeyF5  Key = 133 | ||||
| 	KeyF6  Key = 134 | ||||
| 	KeyF7  Key = 135 | ||||
| 	KeyF8  Key = 136 | ||||
| 	KeyF9  Key = 137 | ||||
| 	KeyF10 Key = 138 | ||||
| 	KeyF11 Key = 139 | ||||
| 	KeyF12 Key = 140 | ||||
| ) | ||||
| 
 | ||||
| // Button represents a mouse button. | ||||
| type Button int | ||||
| 
 | ||||
| const ( | ||||
| 	ButtonNone Button = iota | ||||
| 	 | ||||
| 	Button1 | ||||
| 	Button2 | ||||
| 	Button3 | ||||
| 	Button4 | ||||
| 	Button5 | ||||
| 	Button6 | ||||
| 	Button7 | ||||
| 	Button8 | ||||
| 	Button9 | ||||
| 	 | ||||
| 	ButtonLeft    Button = Button1 | ||||
| 	ButtonMiddle  Button = Button2 | ||||
| 	ButtonRight   Button = Button3 | ||||
| 	ButtonBack    Button = Button8 | ||||
| 	ButtonForward Button = Button9 | ||||
| ) | ||||
| 
 | ||||
| // Printable returns whether or not the key's character could show up on screen. | ||||
| // If this function returns true, the key can be cast to a rune and used as | ||||
| // such. | ||||
| func (key Key) Printable () (printable bool) { | ||||
| 	printable = unicode.IsPrint(rune(key)) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Modifiers lists what modifier keys are being pressed. This is used in | ||||
| // conjunction with a Key code in a Key press event. These should be used | ||||
| // instead of attempting to track the state of the modifier keys, because there | ||||
| // is no guarantee that one press event will be coupled with one release event. | ||||
| type Modifiers struct { | ||||
| 	Shift   bool | ||||
| 	Control bool | ||||
| 	Alt     bool | ||||
| 	Meta    bool | ||||
| 	Super   bool | ||||
| 	Hyper   bool | ||||
| 
 | ||||
| 	// NumberPad does not represent a key, but it behaves like one. If it is | ||||
| 	// set to true, the Key was pressed on the number pad. It is treated | ||||
| 	// as a modifier key because if you don't care whether a key was pressed | ||||
| 	// on the number pad or not, you can just ignore this value. | ||||
| 	NumberPad bool | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										30
									
								
								layout.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								layout.go
									
									
									
									
									
								
							| @ -1,30 +0,0 @@ | ||||
| package tomo | ||||
| 
 | ||||
| import "image" | ||||
| 
 | ||||
| // LayoutEntry associates an element with layout and positioning information so | ||||
| // it can be arranged by a Layout. | ||||
| type LayoutEntry struct { | ||||
| 	Element | ||||
| 	Bounds image.Rectangle | ||||
| 	Expand bool | ||||
| } | ||||
| 
 | ||||
| // Layout is capable of arranging elements within a container. It is also able | ||||
| // to determine the minimum amount of room it needs to do so. | ||||
| type Layout interface { | ||||
| 	// Arrange takes in a slice of entries and a bounding width and height, | ||||
| 	// 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) | ||||
| 
 | ||||
| 	// 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) | ||||
| 
 | ||||
| 	// 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) | ||||
| } | ||||
| @ -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() | ||||
|  | ||||
							
								
								
									
										39
									
								
								window.go
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								window.go
									
									
									
									
									
								
							| @ -1,39 +0,0 @@ | ||||
| package tomo | ||||
| 
 | ||||
| import "image" | ||||
| 
 | ||||
| // Window represents a top-level container generated by the currently running | ||||
| // backend. It can contain a single element. It is hidden by default, and must | ||||
| // be explicitly shown with the Show() method. If it contains no element, it | ||||
| // displays a black (or transprent) background. | ||||
| type Window interface { | ||||
| 	// Adopt sets the root element of the window. There can only be one of | ||||
| 	// these at one time. | ||||
| 	Adopt (child Element) | ||||
| 
 | ||||
| 	// Child returns the root element of the window. | ||||
| 	Child () (child Element) | ||||
| 
 | ||||
| 	// SetTitle sets the title that appears on the window's title bar. This | ||||
| 	// method might have no effect with some backends. | ||||
| 	SetTitle (title string) | ||||
| 
 | ||||
| 	// SetIcon taks in a list different sizes of the same icon and selects | ||||
| 	// the best one to display on the window title bar, dock, or whatever is | ||||
| 	// applicable for the given backend. This method might have no effect | ||||
| 	// for some backends. | ||||
| 	SetIcon (sizes []image.Image) | ||||
| 
 | ||||
| 	// Show shows the window. The window starts off hidden, so this must be | ||||
| 	// called after initial setup to make sure it is visible. | ||||
| 	Show () | ||||
| 
 | ||||
| 	// Hide hides the window. | ||||
| 	Hide () | ||||
| 
 | ||||
| 	// Close closes the window. | ||||
| 	Close () | ||||
| 
 | ||||
| 	// OnClose specifies a function to be called when the window is closed. | ||||
| 	OnClose (func ()) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user