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