Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 484ead0f76 | |||
| 018f93cbf9 | |||
| 8657730c25 | |||
| 9d63e27ab6 | |||
| 8a3f41c7db | |||
| 987bb613dd | |||
| 7bf62c25fe | |||
| 763e5db3fc | |||
| dcdb411c0e | |||
| efbdaef390 | |||
| 8546f11471 | |||
| 46f4c50381 | |||
| b88e32fa49 | |||
| 8f43fa310c | |||
| 20c7e0fdb1 | |||
| d8a8ad7e0a | |||
| 3feba5f811 | |||
| 75c654d4ae | |||
| da38e5411f | |||
| d47b525e42 | |||
| 862e08edf1 | |||
| 47b2231acd | |||
| b05e1f5d50 | |||
| 608a898be3 | |||
| 91a8ae2fa5 | |||
| cacfd20a8a | |||
| d08fe845fc | |||
| e23a688103 | |||
| f4cc47eb16 | |||
| 43fb3b8feb | |||
| a022fa3ad4 | |||
| 7e3a9759ee | |||
| 559490e5e8 | |||
| bc38ea14e1 | |||
| b264e11ea6 | |||
| a01e5f8716 | |||
| 3de570373f | |||
| a1eb53c4db | |||
| 750882eef1 | |||
| a98d09d320 | |||
| e6a4b6c70e | |||
| 03fab6fcc0 | |||
| 6fd236f96c | |||
| cf092b4447 | |||
| 8403d621a8 | |||
| 92660ef7de |
248
attribute.go
248
attribute.go
@@ -2,143 +2,128 @@ package tomo
|
||||
|
||||
import "image"
|
||||
import "image/color"
|
||||
import "golang.org/x/image/font"
|
||||
import "git.tebibyte.media/tomo/tomo/canvas"
|
||||
|
||||
// AttrSet is a set of attributes wherein only one/zero of each attribute type
|
||||
// can exist. It is keyed by the AttrNumber of each attribute and must not be
|
||||
// modified directly.
|
||||
type AttrSet map[int] Attr
|
||||
|
||||
// AS builds an AttrSet out of a vararg list of Attr values.
|
||||
func AS (attrs ...Attr) AttrSet {
|
||||
set := AttrSet { }
|
||||
set.Add(attrs...)
|
||||
return set
|
||||
}
|
||||
|
||||
// Add adds attributes to the set.
|
||||
func (this AttrSet) Add (attrs ...Attr) {
|
||||
for _, attr := range attrs {
|
||||
this[attr.attr()] = attr
|
||||
}
|
||||
}
|
||||
|
||||
// MergeUnder takes attributes from another set and adds them if they don't
|
||||
// already exist in this one.
|
||||
func (this AttrSet) MergeUnder (other AttrSet) {
|
||||
if other == nil { return }
|
||||
for _, attr := range other {
|
||||
if _, exists := this[attr.attr()]; !exists {
|
||||
this.Add(attr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MergeOver takes attributes from another set and adds them, overriding this
|
||||
// one.
|
||||
func (this AttrSet) MergeOver (other AttrSet) {
|
||||
if other == nil { return }
|
||||
for _, attr := range other {
|
||||
this.Add(attr)
|
||||
}
|
||||
}
|
||||
|
||||
// Attr modifies one thing about a box's style.
|
||||
// Attr modifies one thing about a Box's style.
|
||||
type Attr interface {
|
||||
// Equals returns true if both attributes can reasonably be declared
|
||||
// equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
Equals (Attr) bool
|
||||
attr () int
|
||||
Kind () AttrKind
|
||||
attr ()
|
||||
}
|
||||
|
||||
// AttrColor sets the background color of a box.
|
||||
type AttrColor struct { color.Color }
|
||||
// AttrTexture sets the texture of a box to a named texture.
|
||||
type AttrTexture struct { canvas.Texture }
|
||||
// AttrTextureMode sets the rendering mode of a box's texture.
|
||||
type AttrTextureMode TextureMode
|
||||
// AttrBorder sets the border of a box.
|
||||
type AttrBorder []Border
|
||||
// AttrMinimumSize sets the minimum size of a box.
|
||||
type AttrMinimumSize image.Point
|
||||
// AttrPadding sets the inner padding of a box.
|
||||
type AttrPadding Inset
|
||||
// AttrGap sets the gap between child boxes, if the box is a ContainerBox.
|
||||
type AttrGap image.Point
|
||||
// AttrTextColor sets the text color, if the box is a TextBox.
|
||||
type AttrTextColor struct { color.Color }
|
||||
// AttrDotColor sets the text selection color, if the box is a TextBox.
|
||||
type AttrDotColor struct { color.Color }
|
||||
// AttrFace sets the font face, if the box is a TextBox.
|
||||
type AttrFace struct { font.Face }
|
||||
// AttrWrap sets if the text wraps, if the box is a TextBox.
|
||||
type AttrWrap bool
|
||||
// AttrAlign sets the alignment, if the box is a ContentBox.
|
||||
type AttrAlign struct { X, Y Align }
|
||||
// AttrOverflow sets the overflow, if the box is a ContentBox.
|
||||
type AttrOverflow struct { X, Y bool }
|
||||
// AttrLayout sets the layout, if the box is a ContentBox.
|
||||
type AttrLayout struct { Layout }
|
||||
// AttrKind enumerates all attribute kinds. Each Attr- type has one of these.
|
||||
type AttrKind int; const (
|
||||
AttrKindColor AttrKind = iota
|
||||
AttrKindTexture
|
||||
AttrKindTextureMode
|
||||
AttrKindBorder
|
||||
AttrKindMinimumSize
|
||||
AttrKindPadding
|
||||
AttrKindGap
|
||||
AttrKindTextColor
|
||||
AttrKindDotColor
|
||||
AttrKindFace
|
||||
AttrKindWrap
|
||||
AttrKindAlign
|
||||
AttrKindOverflow
|
||||
AttrKindLayout
|
||||
AttrKindCursor
|
||||
)
|
||||
|
||||
// AColor is a convenience constructor for the color attribute.
|
||||
// AttrColor sets the background color of a Box.
|
||||
type AttrColor struct { color.Color }
|
||||
// AttrTexture sets the texture of a Box.
|
||||
type AttrTexture struct { canvas.Texture }
|
||||
// AttrTextureMode sets the rendering mode of a Box's texture.
|
||||
type AttrTextureMode TextureMode
|
||||
// AttrBorder sets the Border of a Box.
|
||||
type AttrBorder []Border
|
||||
// AttrMinimumSize sets the minimum size of a Box.
|
||||
type AttrMinimumSize image.Point
|
||||
// AttrPadding sets the inner padding of a Box.
|
||||
type AttrPadding Inset
|
||||
// AttrGap sets the gap between child Boxes, if the Box is a ContainerBox.
|
||||
type AttrGap image.Point
|
||||
// AttrTextColor sets the text color, if the Box is a TextBox.
|
||||
type AttrTextColor struct { color.Color }
|
||||
// AttrDotColor sets the text selection color, if the Box is a TextBox.
|
||||
type AttrDotColor struct { color.Color }
|
||||
// AttrFace sets the type face, if the Box is a TextBox.
|
||||
type AttrFace Face
|
||||
// AttrWrap sets whether or not the text wraps, if the Box is a TextBox.
|
||||
type AttrWrap bool
|
||||
// AttrAlign sets the layout alignment, if the Box is a ContentBox.
|
||||
type AttrAlign struct { X, Y Align }
|
||||
// AttrOverflow sets the overflow, if the Box is a ContentBox.
|
||||
type AttrOverflow struct { X, Y bool }
|
||||
// AttrLayout sets the Layout, if the Box is a ContentBox.
|
||||
type AttrLayout struct { Layout }
|
||||
// AttrCursor sets the mouse cursor shape.
|
||||
type AttrCursor Cursor
|
||||
|
||||
// AColor is a convenience constructor for AttrColor.
|
||||
func AColor (col color.Color) AttrColor {
|
||||
return AttrColor { Color: col }
|
||||
}
|
||||
// ATexture is a convenience constructor for the texture attribute.
|
||||
// ATexture is a convenience constructor for AttrTexture.
|
||||
func ATexture (texture canvas.Texture) AttrTexture {
|
||||
return AttrTexture { Texture: texture }
|
||||
}
|
||||
// ATextureMode is a convenience constructor for the texture mode attribute.
|
||||
// ATextureMode is a convenience constructor for AttrTextureMode.
|
||||
func ATextureMode (mode TextureMode) AttrTextureMode {
|
||||
return AttrTextureMode(mode)
|
||||
}
|
||||
// ABorder is a convenience constructor for the border attribute.
|
||||
// ABorder is a convenience constructor for AttrBorder.
|
||||
func ABorder (borders ...Border) AttrBorder {
|
||||
return AttrBorder(borders)
|
||||
}
|
||||
// AMinimumSize is a convenience constructor for the minimum size attribute.
|
||||
// AMinimumSize is a convenience constructor for AttrMinimumSize.
|
||||
func AMinimumSize (x, y int) AttrMinimumSize {
|
||||
return AttrMinimumSize(image.Pt(x, y))
|
||||
}
|
||||
// APadding is a convenience constructor for the padding attribute.
|
||||
// APadding is a convenience constructor for AttrPadding.
|
||||
func APadding (sides ...int) AttrPadding {
|
||||
return AttrPadding(I(sides...))
|
||||
}
|
||||
// AGap is a convenience constructor for the gap attribute.
|
||||
// AGap is a convenience constructor for AttrGap.
|
||||
func AGap (x, y int) AttrGap {
|
||||
return AttrGap(image.Pt(x, y))
|
||||
}
|
||||
// ATextColor is a convenience constructor for the text color attribute.
|
||||
// ATextColor is a convenience constructor for AttrTextColor.
|
||||
func ATextColor (col color.Color) AttrTextColor {
|
||||
return AttrTextColor { Color: col }
|
||||
}
|
||||
// ADotColor is a convenience constructor for the dot color attribute.
|
||||
// ADotColor is a convenience constructor for AttrDotColor.
|
||||
func ADotColor (col color.Color) AttrDotColor {
|
||||
return AttrDotColor { Color: col }
|
||||
}
|
||||
// AFace is a convenience constructor for the face attribute.
|
||||
func AFace (face font.Face) AttrFace {
|
||||
return AttrFace { Face: face }
|
||||
// AFace is a convenience constructor for AttrFace.
|
||||
func AFace (face Face) AttrFace {
|
||||
return AttrFace(face)
|
||||
}
|
||||
// AWrap is a convenience constructor for the wrap attribute.
|
||||
// AWrap is a convenience constructor for AttrWrap.
|
||||
func AWrap (wrap bool) AttrWrap {
|
||||
return AttrWrap(wrap)
|
||||
}
|
||||
// AAlign is a convenience constructor for the align attribute.
|
||||
// AAlign is a convenience constructor for AttrAlign.
|
||||
func AAlign (x, y Align) AttrAlign {
|
||||
return AttrAlign { X: x, Y: y }
|
||||
}
|
||||
// AOverflow is a convenience constructor for the overflow attribute.
|
||||
// AOverflow is a convenience constructor for AttrOverflow.
|
||||
func AOverflow (x, y bool) AttrOverflow {
|
||||
return AttrOverflow { X: x, Y: y }
|
||||
}
|
||||
// ALayout is a convenience constructor for the overflow attribute.
|
||||
// ALayout is a convenience constructor for AttrLayout.
|
||||
func ALayout (layout Layout) AttrLayout {
|
||||
return AttrLayout { Layout: layout }
|
||||
}
|
||||
// ACursor is a convenience constructor for AttrCursor.
|
||||
func ACursor (cursor Cursor) AttrCursor {
|
||||
return AttrCursor(cursor)
|
||||
}
|
||||
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrColor) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrColor); ok {
|
||||
return this == other
|
||||
@@ -146,7 +131,7 @@ func (this AttrColor) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrTexture) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrTexture); ok {
|
||||
return this == other
|
||||
@@ -154,7 +139,7 @@ func (this AttrTexture) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrTextureMode) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrTextureMode); ok {
|
||||
return this == other
|
||||
@@ -162,7 +147,7 @@ func (this AttrTextureMode) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrBorder) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrBorder); ok {
|
||||
if len(this) != len(other) { return false }
|
||||
@@ -176,7 +161,7 @@ func (this AttrBorder) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrMinimumSize) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrMinimumSize); ok {
|
||||
return this == other
|
||||
@@ -184,7 +169,7 @@ func (this AttrMinimumSize) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrPadding) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrPadding); ok {
|
||||
return this == other
|
||||
@@ -192,7 +177,7 @@ func (this AttrPadding) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrGap) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrGap); ok {
|
||||
return this == other
|
||||
@@ -200,7 +185,7 @@ func (this AttrGap) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrTextColor) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrTextColor); ok {
|
||||
return this == other
|
||||
@@ -208,7 +193,7 @@ func (this AttrTextColor) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrDotColor) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrDotColor); ok {
|
||||
return this == other
|
||||
@@ -216,7 +201,7 @@ func (this AttrDotColor) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrFace) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrFace); ok {
|
||||
return this == other
|
||||
@@ -224,7 +209,7 @@ func (this AttrFace) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrWrap) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrWrap); ok {
|
||||
return this == other
|
||||
@@ -232,7 +217,7 @@ func (this AttrWrap) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrAlign) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrAlign); ok {
|
||||
return this == other
|
||||
@@ -240,7 +225,7 @@ func (this AttrAlign) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrOverflow) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrOverflow); ok {
|
||||
return this == other
|
||||
@@ -248,33 +233,50 @@ func (this AttrOverflow) Equals (other Attr) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrLayout) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrLayout); ok {
|
||||
// for some goofy reason if we try to compare two AttrLayouts we get a
|
||||
// fucking runtime error????? its probably for the best anyways because
|
||||
// two layouts cannot "reasonably" be declared equal
|
||||
return false
|
||||
}
|
||||
// Equals returns true if both Attrs can reasonably be declared equal.
|
||||
func (this AttrCursor) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrCursor); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// AttrNumber returns the number of an attribute. Each attribute type has a
|
||||
// unique number. The exact values of these numbers are not part of the API and
|
||||
// may change.
|
||||
func AttrNumber (attr Attr) int {
|
||||
return attr.attr()
|
||||
}
|
||||
func (AttrColor) Kind () AttrKind { return AttrKindColor }
|
||||
func (AttrTexture) Kind () AttrKind { return AttrKindTexture }
|
||||
func (AttrTextureMode) Kind () AttrKind { return AttrKindTextureMode }
|
||||
func (AttrBorder) Kind () AttrKind { return AttrKindBorder }
|
||||
func (AttrMinimumSize) Kind () AttrKind { return AttrKindMinimumSize }
|
||||
func (AttrPadding) Kind () AttrKind { return AttrKindPadding }
|
||||
func (AttrGap) Kind () AttrKind { return AttrKindGap }
|
||||
func (AttrTextColor) Kind () AttrKind { return AttrKindTextColor }
|
||||
func (AttrDotColor) Kind () AttrKind { return AttrKindDotColor }
|
||||
func (AttrFace) Kind () AttrKind { return AttrKindFace }
|
||||
func (AttrWrap) Kind () AttrKind { return AttrKindWrap }
|
||||
func (AttrAlign) Kind () AttrKind { return AttrKindAlign }
|
||||
func (AttrOverflow) Kind () AttrKind { return AttrKindOverflow }
|
||||
func (AttrLayout) Kind () AttrKind { return AttrKindLayout }
|
||||
func (AttrCursor) Kind () AttrKind { return AttrKindCursor }
|
||||
|
||||
func (AttrColor) attr () int { return 0 }
|
||||
func (AttrTexture) attr () int { return 1 }
|
||||
func (AttrTextureMode) attr () int { return 2 }
|
||||
func (AttrBorder) attr () int { return 3 }
|
||||
func (AttrMinimumSize) attr () int { return 4 }
|
||||
func (AttrPadding) attr () int { return 5 }
|
||||
func (AttrGap) attr () int { return 6 }
|
||||
func (AttrTextColor) attr () int { return 7 }
|
||||
func (AttrDotColor) attr () int { return 8 }
|
||||
func (AttrFace) attr () int { return 9 }
|
||||
func (AttrWrap) attr () int { return 10 }
|
||||
func (AttrAlign) attr () int { return 11 }
|
||||
func (AttrOverflow) attr () int { return 12 }
|
||||
func (AttrLayout) attr () int { return 13 }
|
||||
func (AttrColor) attr () { }
|
||||
func (AttrTexture) attr () { }
|
||||
func (AttrTextureMode) attr () { }
|
||||
func (AttrBorder) attr () { }
|
||||
func (AttrMinimumSize) attr () { }
|
||||
func (AttrPadding) attr () { }
|
||||
func (AttrGap) attr () { }
|
||||
func (AttrTextColor) attr () { }
|
||||
func (AttrDotColor) attr () { }
|
||||
func (AttrFace) attr () { }
|
||||
func (AttrWrap) attr () { }
|
||||
func (AttrAlign) attr () { }
|
||||
func (AttrOverflow) attr () { }
|
||||
func (AttrLayout) attr () { }
|
||||
func (AttrCursor) attr () { }
|
||||
|
||||
108
backend.go
108
backend.go
@@ -1,15 +1,15 @@
|
||||
package tomo
|
||||
|
||||
import "sort"
|
||||
import "sync"
|
||||
import "image"
|
||||
import "errors"
|
||||
import "git.tebibyte.media/tomo/tomo/data"
|
||||
import "git.tebibyte.media/tomo/tomo/canvas"
|
||||
|
||||
// Backend is any Tomo implementation. Backends handle window creation, layout,
|
||||
// rendering, and events so that there can be as many platform-specific
|
||||
// optimizations as possible.
|
||||
type Backend interface {
|
||||
// These methods create new objects. The backend must reject any object
|
||||
// These methods create new Objects. The backend must reject any Object
|
||||
// that was not made by it.
|
||||
NewBox () Box
|
||||
NewTextBox () TextBox
|
||||
@@ -18,37 +18,40 @@ type Backend interface {
|
||||
NewContainerBox () ContainerBox
|
||||
|
||||
// NewWindow creates a normal Window and returns it.
|
||||
NewWindow (image.Rectangle) (Window, error)
|
||||
NewWindow (WindowKind, image.Rectangle) (Window, error)
|
||||
|
||||
// NewPlainWindow creates an undecorated window that does not appear in
|
||||
// window lists and returns it. This is intended for making things like
|
||||
// panels, docks, etc.
|
||||
NewPlainWindow (image.Rectangle) (Window, error)
|
||||
|
||||
// NewTexture creates a new texture from an image. The backend must
|
||||
// reject any texture that was not made by it.
|
||||
// NewTexture creates a new canvs.Texture from an image. The backend
|
||||
// must reject any texture that was not made by it.
|
||||
NewTexture (image.Image) canvas.TextureCloser
|
||||
|
||||
// NewCanvas creates a new canvas with the specified bounds. The backend
|
||||
// must reject any canvas that was not made by it.
|
||||
// NewCanvas creates a new canvas.Canvas with the specified bounds. The
|
||||
// backend must reject any canvas that was not made by it.
|
||||
NewCanvas (image.Rectangle) canvas.CanvasCloser
|
||||
|
||||
// SetStyle sets the style that will be used on objects. The backend is
|
||||
// in charge of applying the style to objects. When this method is
|
||||
// called, it must propagate a StyleChange event to all boxes it is
|
||||
// keeping track of.
|
||||
SetStyle (*Style)
|
||||
// ColorRGBA returns the RGBA of a color according to the current style,
|
||||
// as specified in color.Color.RGBA. It may be rendered invalid if the
|
||||
// visual style changes, but the Backend must send a StyleChange event
|
||||
// to all managed boxes when this happens.
|
||||
ColorRGBA (id Color) (r, g, b, a uint32)
|
||||
|
||||
// SetIconSet sets the icon set that icons will be pulled from. When
|
||||
// this method is called, it must propagate an IconChange event to all
|
||||
// boxes it is keeping track of.
|
||||
SetIconSet (IconSet)
|
||||
// IconTexture returns the canvas.Texture of an Icon. It may be closed
|
||||
// and therefore rendered invalid if the icon set changes, but the
|
||||
// Backend must send an IconSetChange event to all managed boxes when
|
||||
// this happens.
|
||||
IconTexture (Icon, IconSize) canvas.Texture
|
||||
|
||||
// Run runs the event loop until Stop() is called, or the backend
|
||||
// MimeIconTexture returns the canvas.Texture of an icon corresponding
|
||||
// to the specified MIME type. It may be closed and therefore rendered
|
||||
// invalid if the icon set changes, but the Backend must send an
|
||||
// IconSetChange event to all managed boxes when this happens.
|
||||
MimeIconTexture (data.Mime, IconSize) canvas.Texture
|
||||
|
||||
// Run runs the event loop until Stop is called, or the Backend
|
||||
// experiences a fatal error.
|
||||
Run () error
|
||||
|
||||
// Stop must unblock run.
|
||||
// Stop must unblock Run. This behavior may only be called from within
|
||||
// tomo.Stop.
|
||||
Stop ()
|
||||
|
||||
// Do performs a callback function in the event loop thread as soon as
|
||||
@@ -56,54 +59,21 @@ type Backend interface {
|
||||
Do (func ())
|
||||
}
|
||||
|
||||
// Factory is a function that attempts to instatiate a backend. If the
|
||||
// instantiation process fails at any point, it must clean up all resources and
|
||||
// return nil and an error explaining what happened.
|
||||
type Factory func () (Backend, error)
|
||||
var backendLock sync.Mutex
|
||||
var backend Backend
|
||||
|
||||
var registered []factoryEntry
|
||||
type factoryEntry struct {
|
||||
Factory
|
||||
priority int
|
||||
func assertBackend () {
|
||||
if backend == nil { panic("nil backend") }
|
||||
}
|
||||
|
||||
// Register registers a backend factory with the given priority number.
|
||||
func Register (priority int, factory Factory) {
|
||||
registered = append(registered, factoryEntry {
|
||||
priority: priority,
|
||||
Factory: factory,
|
||||
})
|
||||
}
|
||||
// SetBackend sets the Backend that functions in this package will call upon.
|
||||
// This function will panic if there is already a Backend running.
|
||||
func SetBackend (back Backend) {
|
||||
backendLock.Lock()
|
||||
defer backendLock.Unlock()
|
||||
|
||||
// initialize instantiates a backend. The first backend (sorted by priority)
|
||||
// that does not throw an error when initialized is used. If no backend could be
|
||||
// instantiated, this function returns an error. This function should be called
|
||||
// only once.
|
||||
func initialize () (Backend, error) {
|
||||
backend, err := instantiate()
|
||||
if err != nil { return nil, err }
|
||||
return backend, err
|
||||
}
|
||||
|
||||
func instantiate () (Backend, error) {
|
||||
if len(registered) < 0 {
|
||||
return nil, errors.New("no available backends")
|
||||
if backend != nil {
|
||||
panic("SetBackend called while another backend was running")
|
||||
}
|
||||
|
||||
// sort backends by priority
|
||||
sort.Slice(registered, func (left, right int) bool {
|
||||
return registered[left].priority < registered[right].priority
|
||||
})
|
||||
|
||||
// attempt to instantiate
|
||||
errorLog := ""
|
||||
for _, factory := range registered {
|
||||
backend, err := factory.Factory()
|
||||
if err == nil {
|
||||
return backend, nil
|
||||
} else {
|
||||
errorLog += " " + err.Error() + "\n"
|
||||
}
|
||||
}
|
||||
return nil, errors.New("all backends failed:\n" + errorLog)
|
||||
backend = back
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ type StrokeAlign int; const (
|
||||
// have multiple pens associated with it, each maintaining their own drawing
|
||||
// state.
|
||||
type Pen interface {
|
||||
// Rectangle draws a rectangle.
|
||||
// Rectangle draws an image.Rectangle.
|
||||
Rectangle (image.Rectangle)
|
||||
|
||||
// Path draws a path, which is a series of connected points.
|
||||
@@ -69,7 +69,7 @@ type Pen interface {
|
||||
type Canvas interface {
|
||||
draw.Image
|
||||
|
||||
// Pen returns a new pen for this canvas.
|
||||
// Pen returns a new pen for this Canvas.
|
||||
Pen () Pen
|
||||
|
||||
// SubCanvas returns a returns a Canvas representing the portion of this
|
||||
@@ -87,11 +87,11 @@ type CanvasCloser interface {
|
||||
|
||||
// Drawer can draw to a canvas.
|
||||
type Drawer interface {
|
||||
// Draw draws to the given canvas.
|
||||
// Draw draws to the given Canvas.
|
||||
Draw (Canvas)
|
||||
}
|
||||
|
||||
// PushCanvas is a canvas that can push a region of itself to the screen (or
|
||||
// PushCanvas is a Canvas that can push a region of itself to the screen (or
|
||||
// some other destination).
|
||||
type PushCanvas interface {
|
||||
Canvas
|
||||
|
||||
@@ -2,7 +2,10 @@ package canvas
|
||||
|
||||
import "image"
|
||||
|
||||
// Shatter takes in a bounding rectangle, and several rectangles to be
|
||||
// TODO look into other options besides returning a slice, as that causes memory
|
||||
// allocations.
|
||||
|
||||
// Shatter takes in a bounding rectangle, and several image.Rectangles to be
|
||||
// subtracted from it. It returns a slice of rectangles that tile together to
|
||||
// make up the difference between them. This is intended to be used for figuring
|
||||
// out which areas of a container box's background are covered by other boxes so
|
||||
|
||||
@@ -3,7 +3,7 @@ package canvas
|
||||
import "io"
|
||||
import "image"
|
||||
|
||||
// Texture is a handle that points to a 2D raster image managed by the backend.
|
||||
// Texture is a handle that points to a 2D raster image.
|
||||
type Texture interface {
|
||||
image.Image
|
||||
|
||||
@@ -13,7 +13,7 @@ type Texture interface {
|
||||
SubTexture (image.Rectangle) Texture
|
||||
}
|
||||
|
||||
// TextureCloser is a texture that can be closed. Anything that receives a
|
||||
// TextureCloser is a Texture that can be closed. Anything that receives a
|
||||
// TextureCloser must close it after use.
|
||||
type TextureCloser interface {
|
||||
Texture
|
||||
|
||||
44
config/config.go
Normal file
44
config/config.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Package config stores common configuration parameters. They are unmanaged,
|
||||
// and do not broadcast events when changed. Thus, they must be queried each
|
||||
// time they are used, whether that be by backends, applications, or objects.
|
||||
// They are intended to be set by things like application frameworks. Values set
|
||||
// by other things are subject to being overridden.
|
||||
package config
|
||||
|
||||
import "time"
|
||||
import "git.tebibyte.media/tomo/tomo/input"
|
||||
|
||||
// DoubleClickDelay is the maximum amount of time that can pass between two
|
||||
// consecutive clicks for them to be considered a double-click.
|
||||
var DoubleClickDelay time.Duration = time.Second
|
||||
|
||||
// ScrollSpeed is how many units (pixels at a scale of 1.0) the content of a
|
||||
// ContentBox should be moved in response to a scroll delta of 1.0.
|
||||
var ScrollSpeed int = 16
|
||||
|
||||
// KeyChordConfirm is used to activate focused objects, and commonly to signal
|
||||
// to the application that the user wishes to submit a value they have entered
|
||||
// using the keyboard into an input field.
|
||||
var KeyChordConfirm = input.KC(input.KeyEnter, input.ModNone)
|
||||
|
||||
// KeyChordClose is used to break out of some mode or state, such as a modal
|
||||
// dialog.
|
||||
var KeyChordClose = input.KC(input.KeyEscape, input.ModNone)
|
||||
|
||||
// KeyChordFocusNext is used to advance the input focus to the next focusable
|
||||
// object.
|
||||
var KeyChordFocusNext = input.KC(input.KeyTab, input.ModNone)
|
||||
|
||||
// KeyChordFocusPrevious is used to advance the input focus to the previous
|
||||
// focusable object.
|
||||
var KeyChordFocusPrevious = input.KC(input.KeyTab, input.ModShift)
|
||||
|
||||
// ButtonChordInteract is used to select, activate, and drag objects.
|
||||
var ButtonChordInteract = input.BC(input.ButtonLeft, input.ModNone)
|
||||
|
||||
// ButtonChordContextMenu is used to open a context menu on an object.
|
||||
var ButtonChordContextMenu = input.BC(input.ButtonRight, input.ModNone)
|
||||
|
||||
// ButtonChordPan is used to move the content of a content object relative to
|
||||
// its inner bounds.
|
||||
var ButtonChordPan = input.BC(input.ButtonMiddle, input.ModNone)
|
||||
35
cursor.go
Normal file
35
cursor.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package tomo
|
||||
|
||||
// Cursor represents a mouse cursor shape.
|
||||
type Cursor string
|
||||
|
||||
// A list of standard cursor shapes. This is based off of the XDG Cursor
|
||||
// Conventions Specification
|
||||
// (https://www.freedesktop.org/wiki/Specifications/cursor-spec/).
|
||||
const (
|
||||
CursorUnknown Cursor = ""
|
||||
CursorDefault Cursor = "Default"
|
||||
CursorText Cursor = "Text"
|
||||
CursorPointer Cursor = "Pointer"
|
||||
CursorHelp Cursor = "Help"
|
||||
CursorProgress Cursor = "Progress"
|
||||
CursorWait Cursor = "Wait"
|
||||
CursorCopy Cursor = "Copy"
|
||||
CursorAlias Cursor = "Alias"
|
||||
CursorNoDrop Cursor = "NoDrop"
|
||||
CursorNotAllowed Cursor = "NotAllowed"
|
||||
CursorAllScroll Cursor = "AllScroll"
|
||||
CursorRowResize Cursor = "RowResize"
|
||||
CursorColResize Cursor = "ColResize"
|
||||
CursorEResize Cursor = "EResize"
|
||||
CursorNEResize Cursor = "NEResize"
|
||||
CursorNWResize Cursor = "NWResize"
|
||||
CursorNResize Cursor = "NResize"
|
||||
CursorSEResize Cursor = "SEResize"
|
||||
CursorSWResize Cursor = "SWResize"
|
||||
CursorSResize Cursor = "SResize"
|
||||
CursorWResize Cursor = "WResize"
|
||||
CursorVerticalText Cursor = "VerticalText"
|
||||
CursorCrosshair Cursor = "Crosshair"
|
||||
CursorCell Cursor = "Cell"
|
||||
)
|
||||
@@ -2,11 +2,36 @@
|
||||
// handlers.
|
||||
package event
|
||||
|
||||
// A cookie is returned when you add an event handler so you can remove it
|
||||
// later if you so choose.
|
||||
type Cookie interface {
|
||||
// Close removes the event handler this cookie is for.
|
||||
Close ()
|
||||
import "io"
|
||||
import "errors"
|
||||
|
||||
// Cookie is returned when you add an event handler so you can remove it later
|
||||
// if you so choose. When the Close behavior is called, the handler must be
|
||||
// removed, even if an error is returned.
|
||||
type Cookie io.Closer
|
||||
|
||||
// FuncCookie is a cookie that calls a function (itself) when closed.
|
||||
type FuncCookie func () error
|
||||
func (cookie FuncCookie) Close () error { return cookie () }
|
||||
|
||||
// NoCookie is a cookie that does nothing when closed.
|
||||
type NoCookie struct { }
|
||||
func (NoCookie) Close () error { return nil }
|
||||
|
||||
type multiCookie []Cookie
|
||||
|
||||
// MultiCookie creates a single cookie that, when closed, closes a list of other
|
||||
// cookies.
|
||||
func MultiCookie (cookies ...Cookie) Cookie {
|
||||
return multiCookie(cookies)
|
||||
}
|
||||
|
||||
func (cookies multiCookie) Close () error {
|
||||
errs := make([]error, len(cookies))
|
||||
for index, cookie := range cookies {
|
||||
errs[index] = cookie.Close()
|
||||
}
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// Broadcaster manages event listeners.
|
||||
@@ -16,7 +41,7 @@ type Broadcaster[L any] struct {
|
||||
}
|
||||
|
||||
// Connect adds a new listener to the broadcaster and returns a corresponding
|
||||
// cookie.
|
||||
// Cookie.
|
||||
func (broadcaster *Broadcaster[L]) Connect (listener L) Cookie {
|
||||
broadcaster.ensure()
|
||||
|
||||
@@ -31,9 +56,9 @@ func (broadcaster *Broadcaster[L]) Listeners () map[int] L {
|
||||
return broadcaster.listeners
|
||||
}
|
||||
|
||||
func (broadcaster *Broadcaster[L]) newCookie () cookie[L] {
|
||||
func (broadcaster *Broadcaster[L]) newCookie () broadcasterCookie[L] {
|
||||
broadcaster.lastID ++
|
||||
return cookie[L] {
|
||||
return broadcasterCookie[L] {
|
||||
id: broadcaster.lastID,
|
||||
broadcaster: broadcaster,
|
||||
}
|
||||
@@ -45,20 +70,17 @@ func (broadcaster *Broadcaster[L]) ensure () {
|
||||
}
|
||||
}
|
||||
|
||||
// NoCookie is a cookie that does nothing when closed.
|
||||
type NoCookie struct { }
|
||||
func (NoCookie) Close () { }
|
||||
|
||||
type cookie[L any] struct {
|
||||
type broadcasterCookie[L any] struct {
|
||||
id int
|
||||
broadcaster *Broadcaster[L]
|
||||
}
|
||||
|
||||
func (cookie cookie[L]) Close () {
|
||||
func (cookie broadcasterCookie[L]) Close () error {
|
||||
delete(cookie.broadcaster.listeners, cookie.id)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FuncBroadcaster is a broadcaster that manages functions with no arguments.
|
||||
// FuncBroadcaster is a Broadcaster that manages functions with no arguments.
|
||||
type FuncBroadcaster struct {
|
||||
Broadcaster[func ()]
|
||||
}
|
||||
@@ -69,17 +91,3 @@ func (broadcaster *FuncBroadcaster) Broadcast () {
|
||||
listener()
|
||||
}
|
||||
}
|
||||
|
||||
type multiCookie []Cookie
|
||||
|
||||
// MultiCookie creates a single cookie that, when closed, closes a list of other
|
||||
// cookies.
|
||||
func MultiCookie (cookies ...Cookie) Cookie {
|
||||
return multiCookie(cookies)
|
||||
}
|
||||
|
||||
func (cookies multiCookie) Close () {
|
||||
for _, cookie := range cookies {
|
||||
cookie.Close()
|
||||
}
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@@ -1,5 +1,3 @@
|
||||
module git.tebibyte.media/tomo/tomo
|
||||
|
||||
go 1.20
|
||||
|
||||
require golang.org/x/image v0.11.0
|
||||
|
||||
33
go.sum
33
go.sum
@@ -1,33 +0,0 @@
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
|
||||
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
73
icon.go
73
icon.go
@@ -1,21 +1,32 @@
|
||||
package tomo
|
||||
|
||||
import "fmt"
|
||||
import "git.tebibyte.media/tomo/tomo/data"
|
||||
import "git.tebibyte.media/tomo/tomo/canvas"
|
||||
|
||||
// IconSize represents the size of an icon.
|
||||
// IconSize represents the size of an Icon.
|
||||
type IconSize int; const (
|
||||
IconSizeSmall IconSize = iota;
|
||||
IconSizeMedium
|
||||
IconSizeLarge
|
||||
)
|
||||
|
||||
func (size IconSize) String () string {
|
||||
switch size {
|
||||
case IconSizeSmall: return "IconSizeSmall"
|
||||
case IconSizeMedium: return "IconSizeMedium"
|
||||
case IconSizeLarge: return "IconSizeLarge"
|
||||
default: return fmt.Sprintf("IconSize(%d)", size)
|
||||
}
|
||||
}
|
||||
|
||||
// Icon represents an icon ID.
|
||||
type Icon string
|
||||
|
||||
// A list of standard icon IDs. This is roughly based off of the XDG Icon Naming
|
||||
// Specification (https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html).
|
||||
const (
|
||||
// IconUnknown should be a blank space the size of a regular Icon.
|
||||
IconUnknown Icon = ""
|
||||
|
||||
// actions
|
||||
@@ -90,6 +101,9 @@ const (
|
||||
// actions: list
|
||||
IconListAdd Icon = "ListAdd"
|
||||
IconListRemove Icon = "ListRemove"
|
||||
IconListChoose Icon = "ListChoose"
|
||||
IconListExpand Icon = "ListExpand"
|
||||
IconListContract Icon = "ListContract"
|
||||
// actions: mail
|
||||
IconMailForward Icon = "MailForward"
|
||||
IconMailMarkImportant Icon = "MailMarkImportant"
|
||||
@@ -275,9 +289,6 @@ const (
|
||||
IconPlaceHistory Icon = "PlaceHistory"
|
||||
IconPlacePreferences Icon = "PlacePreferences"
|
||||
|
||||
// status: checkbox
|
||||
IconCheckboxChecked Icon = "CheckboxChecked"
|
||||
IconCheckboxUnchecked Icon = "CheckboxUnchecked"
|
||||
// status: appointments
|
||||
IconAppointmentMissed Icon = "AppointmentMissed"
|
||||
IconAppointmentSoon Icon = "AppointmentSoon"
|
||||
@@ -370,46 +381,20 @@ const (
|
||||
IconWeatherStorm Icon = "WeatherStorm"
|
||||
)
|
||||
|
||||
// Texture returns a texture of the corresponding icon ID.
|
||||
// Texture returns a canvas.Texture of the corresponding icon ID. It may be
|
||||
// closed and therefore rendered invalid if the icon set changes, so it is
|
||||
// necessary to subscribe to the IconSetChange event in order to get a new icon
|
||||
// texture when this happens.
|
||||
func (id Icon) Texture (size IconSize) canvas.Texture {
|
||||
if iconSet == nil { return nil }
|
||||
return iconSet.Icon(id, size)
|
||||
}
|
||||
|
||||
// MimeIcon returns an icon corresponding to a MIME type.
|
||||
func MimeIcon (mime data.Mime, size IconSize) canvas.Texture {
|
||||
if iconSet == nil { return nil }
|
||||
return iconSet.MimeIcon(mime, size)
|
||||
}
|
||||
|
||||
// IconSet holds a set of icon textures.
|
||||
type IconSet interface {
|
||||
// A word on textures:
|
||||
//
|
||||
// Because textures can be linked to some resource that is outside of
|
||||
// the control of Go's garbage collector, methods of IconSet must not
|
||||
// allocate new copies of a texture each time they are called. It is
|
||||
// fine to lazily load textures and save them for later use, but the
|
||||
// same texture must never be allocated multiple times as this could
|
||||
// cause a memory leak.
|
||||
//
|
||||
// As such, textures returned by these methods must be protected.
|
||||
|
||||
// Icon returns a texture of the corresponding icon ID. If there is no
|
||||
// suitable option, it should return nil.
|
||||
Icon (Icon, IconSize) canvas.Texture
|
||||
|
||||
// MimeIcon returns a texture of an icon corresponding to a MIME type.
|
||||
// If there is no suitable specific option, it should return a more
|
||||
// generic icon or a plain file icon.
|
||||
MimeIcon (data.Mime, IconSize) canvas.Texture
|
||||
}
|
||||
|
||||
var iconSet IconSet
|
||||
|
||||
// SetIconSet sets the icon set.
|
||||
func SetIconSet (set IconSet) {
|
||||
assertBackend()
|
||||
iconSet = set
|
||||
backend.SetIconSet(set)
|
||||
return backend.IconTexture(id, size)
|
||||
}
|
||||
|
||||
// MimeIconTexture returns a canvas.Texture of the icon corresponding to a MIME
|
||||
// type. It may be closed and therefore rendered invalid if the icon set
|
||||
// changes, so it is necessary to subscribe to the IconSetChange event in order
|
||||
// to get a new icon texture when this happens.
|
||||
func MimeIconTexture (mime data.Mime, size IconSize) canvas.Texture {
|
||||
assertBackend()
|
||||
return backend.MimeIconTexture(mime, size)
|
||||
}
|
||||
|
||||
@@ -107,11 +107,80 @@ func (key Key) Printable () (printable bool) {
|
||||
// Modifiers lists what modifier keys are being pressed. 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
|
||||
type Modifiers uint; const (
|
||||
ModNone Modifiers = 0
|
||||
ModShift Modifiers = 1 << iota
|
||||
ModControl
|
||||
ModAlt
|
||||
ModMeta
|
||||
ModSuper
|
||||
ModHyper
|
||||
)
|
||||
|
||||
// Shift returns whether the list of modifiers includes the shift key.
|
||||
func (modifiers Modifiers) Shift () bool {
|
||||
return modifiers & ModShift != ModNone
|
||||
}
|
||||
|
||||
// Control returns whether the list of modifiers includes the control key.
|
||||
func (modifiers Modifiers) Control () bool {
|
||||
return modifiers & ModControl != ModNone
|
||||
}
|
||||
|
||||
// Alt returns whether the list of modifiers includes the alt key.
|
||||
func (modifiers Modifiers) Alt () bool {
|
||||
return modifiers & ModAlt != ModNone
|
||||
}
|
||||
|
||||
// Meta returns whether the list of modifiers includes the meta key.
|
||||
func (modifiers Modifiers) Meta () bool {
|
||||
return modifiers & ModAlt != ModNone
|
||||
}
|
||||
|
||||
// Super returns whether the list of modifiers includes the super key.
|
||||
func (modifiers Modifiers) Super () bool {
|
||||
return modifiers & ModSuper != ModNone
|
||||
}
|
||||
|
||||
// Hyper returns whether the list of modifiers includes the hyper key.
|
||||
func (modifiers Modifiers) Hyper () bool {
|
||||
return modifiers & ModHyper != ModNone
|
||||
}
|
||||
|
||||
// KeyChord combines a keyboard key with Modifiers.
|
||||
type KeyChord struct {
|
||||
Key Key
|
||||
Modifiers Modifiers
|
||||
}
|
||||
|
||||
// KC is a convenience constructor for a KeyChord.
|
||||
func KC (key Key, modifiers Modifiers) KeyChord {
|
||||
return KeyChord {
|
||||
Key: key,
|
||||
Modifiers: modifiers,
|
||||
}
|
||||
}
|
||||
|
||||
// Pressed returns true if the given Key Modifiers match the KeyChord.
|
||||
func (chord KeyChord) Pressed (key Key, modifiers Modifiers) bool {
|
||||
return chord == KC(key, modifiers)
|
||||
}
|
||||
|
||||
// ButtonChord combines a mouse button with a number of modifiers.
|
||||
type ButtonChord struct {
|
||||
Button Button
|
||||
Modifiers Modifiers
|
||||
}
|
||||
|
||||
// BC is a convenience constructor for a ButtonChord.
|
||||
func BC (button Button, modifiers Modifiers) ButtonChord {
|
||||
return ButtonChord {
|
||||
Button: button,
|
||||
Modifiers: modifiers,
|
||||
}
|
||||
}
|
||||
|
||||
// Pressed returns true if the given Button and Modifiers match the ButtonChord.
|
||||
func (chord ButtonChord) Pressed (button Button, modifiers Modifiers) bool {
|
||||
return chord == BC(button, modifiers)
|
||||
}
|
||||
|
||||
201
object.go
201
object.go
@@ -14,7 +14,7 @@ type Object interface {
|
||||
GetBox () Box
|
||||
}
|
||||
|
||||
// ContentObject is an object that has some kind of content.
|
||||
// ContentObject is an Object that contains some kind of content.
|
||||
type ContentObject interface {
|
||||
Object
|
||||
|
||||
@@ -29,44 +29,49 @@ type ContentObject interface {
|
||||
OnContentBoundsChange (func ()) event.Cookie
|
||||
}
|
||||
|
||||
// Box is a basic styled box. Implementations of Box, as well as any interface
|
||||
// that embed Box, may only originate from a Backend.
|
||||
// Box is a basic box with no content. Implementations of Box, as well as any
|
||||
// interface that embed Box, may only originate from a Backend.
|
||||
type Box interface {
|
||||
Object
|
||||
|
||||
// Window returns the Window this Box is a part of.
|
||||
Window () Window
|
||||
// Bounds returns the outer bounding rectangle of the Box relative to
|
||||
// the Window.
|
||||
// Bounds returns the outer bounding image.Rectangle of the Box relative
|
||||
// to the Window.
|
||||
Bounds () image.Rectangle
|
||||
// InnerBounds returns the inner bounding rectangle of the box. It is
|
||||
// the value of Bounds inset by the Box's border and padding.
|
||||
// InnerBounds returns the inner bounding image.Rectangle of the box. It
|
||||
// is the value of Bounds inset by the Box's border and padding.
|
||||
InnerBounds () image.Rectangle
|
||||
// Role returns this Box's role as set by SetRole.
|
||||
Role () Role
|
||||
// SetRole sets what role this Box takes on. It is used to apply styling
|
||||
// from a theme.
|
||||
// SetRole sets what role this Box takes on. It is used by the Backend
|
||||
// for applying styling.
|
||||
SetRole (Role)
|
||||
// Tag returns whether or not a named tag exists. These are used for
|
||||
// applying styling, among other things. There are some special tags
|
||||
// that are only and always extant during certain user input states:
|
||||
// - hovered: The mouse pointer is within the box
|
||||
// - focused: The box has keyboard focus
|
||||
// - pressed: The box is being pressed by the left mouse button
|
||||
// Tag returns whether or not a named tag exists. These are used by the
|
||||
// Backend for applying styling, among other things. There are some
|
||||
// special tags that are only and always extant during certain user
|
||||
// input states:
|
||||
// - hovered: The mouse pointer is within the Box
|
||||
// - focused: The Box has keyboard focus
|
||||
// - pressed: The Box is being pressed by config.ButtonChordInteract
|
||||
Tag (string) bool
|
||||
// SetTag adds or removes a named tag.
|
||||
SetTag (string, bool)
|
||||
|
||||
// SetAttr overrides a style attribute.
|
||||
SetAttr(Attr)
|
||||
// SetAttr sets a style attribute, overriding the currently applied
|
||||
// style.
|
||||
SetAttr (Attr)
|
||||
// UnsetAttr reverts a style attribute to whatever is specified by the
|
||||
// currently applied style.
|
||||
UnsetAttr (AttrKind)
|
||||
|
||||
// SetDNDData sets the data that will be picked up if this Box is
|
||||
// dragged. If this is nil (which is the default), this Box will not be
|
||||
// picked up.
|
||||
SetDNDData (data.Data)
|
||||
// SetDNDAccept sets the type of data that can be dropped onto this Box.
|
||||
// If this is nil (which is the default), this Box will reject all
|
||||
// drops.
|
||||
// SetDNDAccept sets the types of data which can be dropped onto this
|
||||
// Box. If none are specified (which is the default), this Box will
|
||||
// reject all drops.
|
||||
SetDNDAccept (...data.Mime)
|
||||
// SetFocused sets whether or not this Box has keyboard focus. If set to
|
||||
// true, this method will steal focus away from whichever Object
|
||||
@@ -79,7 +84,7 @@ type Box interface {
|
||||
// These are event subscription behaviors that allow callbacks to be
|
||||
// connected to particular events. Multiple callbacks may be connected
|
||||
// to the same event at once. Callbacks can be removed by closing the
|
||||
// returned cookie.
|
||||
// returned event.Cookie.
|
||||
OnFocusEnter (func () ) event.Cookie
|
||||
OnFocusLeave (func () ) event.Cookie
|
||||
OnStyleChange (func () ) event.Cookie
|
||||
@@ -94,8 +99,13 @@ type Box interface {
|
||||
// to the Box which is most directly affected by them, and then to all
|
||||
// of its parents from the bottom-up. Returning true from the callback
|
||||
// will cause the propagation to stop immediately, thereby "catching"
|
||||
// the event. Generally, if the event was successfully handled, the
|
||||
// callbacks ought to return true.
|
||||
// the event.
|
||||
//
|
||||
// Generally, if the event was successfully handled, the callbacks ought
|
||||
// to return true. Additionally, when subscribing to an event that is
|
||||
// often paired with a second one (for example, KeyDown and KeyUp), it
|
||||
// is good practice to subscribe to both and return true/false under the
|
||||
// same circumstances.
|
||||
OnMouseMove (func () bool) event.Cookie
|
||||
OnButtonDown (func (button input.Button) bool) event.Cookie
|
||||
OnButtonUp (func (button input.Button) bool) event.Cookie
|
||||
@@ -104,46 +114,50 @@ type Box interface {
|
||||
OnKeyUp (func (key input.Key, numberPad bool) bool) event.Cookie
|
||||
}
|
||||
|
||||
// CanvasBox is a box that can be drawn to.
|
||||
// CanvasBox is a Box that can be drawn to.
|
||||
type CanvasBox interface {
|
||||
Box
|
||||
|
||||
// SetDrawer sets the Drawer that will be called upon to draw the Box's
|
||||
// content when it is invalidated. The Canvas passed to the drawer will
|
||||
// have these properties:
|
||||
// - It will have the same origin (0, 0) as the window which contains
|
||||
// the CanvasBox
|
||||
// - The Canvas bounds will describe the portion of the CanvasBox
|
||||
// visible on screen
|
||||
// SetDrawer sets the canvas.Drawer that will be called upon to draw the
|
||||
// Box's content when it is invalidated. The Canvas passed to the Drawer
|
||||
// will have these properties:
|
||||
// - It will have the same origin (0, 0) as the Window which contains
|
||||
// the CanvasBox.
|
||||
// - Its Bounds will describe the portion of the CanvasBox visible on
|
||||
// screen. Therefore, it should not be used to determine the
|
||||
// position of anything drawn within it. The Bounds of the CanvasBox
|
||||
// should be used for this purpose.
|
||||
SetDrawer (canvas.Drawer)
|
||||
|
||||
// Invalidate causes the Box's area to be redrawn at the end of the
|
||||
// event cycle, even if it wouldn't be otherwise.
|
||||
// Invalidate causes the CanvasBox's area to be redrawn at the end of
|
||||
// the event cycle, even if it wouldn't otherwise be. This will call the
|
||||
// canvas.Drawer specified by SetDrawer.
|
||||
Invalidate ()
|
||||
}
|
||||
|
||||
// SurfaceBox is a box that can be drawn to via a hardware accelerated (or
|
||||
// SurfaceBox is a Box that can be drawn to via a hardware accelerated (or
|
||||
// platform-specific) graphics context.
|
||||
type SurfaceBox interface {
|
||||
Box
|
||||
|
||||
// Surface returns the underlying graphics context. The result must be
|
||||
// asserted to the expected type or passed through a type switch before
|
||||
// use. The exact type returned here depends on the backend being used,
|
||||
// use. The exact type returned here depends on the Backend being used,
|
||||
// and it is up to the application author to ensure that the application
|
||||
// and backend agree on it. Applications should fail gracefully if the
|
||||
// expected type was not found.
|
||||
// and Backend agree on it. Applications should fail gracefully if the
|
||||
// expected type was not found. If the surface has been destroyed by the
|
||||
// Backend, it will return nil.
|
||||
Surface () any
|
||||
|
||||
// Invalidate causes the data within the surface to be pushed to the
|
||||
// screen at the end of the event cycle, even if it wouldn't be
|
||||
// otherwise.
|
||||
// screen at the end of the event cycle, even if it wouldn't otherwise
|
||||
// be.
|
||||
Invalidate ()
|
||||
|
||||
// OnSizeChange specifies a function to be called when the size of the
|
||||
// Box is changed, or the surface is re-allocated. The application must
|
||||
// call Surface() each time this event fires in order to not draw to a
|
||||
// closed surface.
|
||||
// Box is changed, the surface is re-allocated, or the surface is
|
||||
// destroyed. The application must call Surface() each time this event
|
||||
// fires in order to not draw onto a closed surface.
|
||||
OnSizeChange (func ())
|
||||
}
|
||||
|
||||
@@ -155,7 +169,7 @@ type ContentBox interface {
|
||||
// relative to the Box's InnerBounds.
|
||||
ContentBounds () image.Rectangle
|
||||
// ScrollTo shifts the origin of the Box's content to the origin of the
|
||||
// Box's InnerBounds, offset by the given point.
|
||||
// Box's InnerBounds, offset by the given image.Point.
|
||||
ScrollTo (image.Point)
|
||||
|
||||
// OnContentBoundsChange specifies a function to be called when the
|
||||
@@ -187,8 +201,8 @@ type TextBox interface {
|
||||
type ContainerBox interface {
|
||||
ContentBox
|
||||
|
||||
// Add appends a child Object. If the object is already a child of
|
||||
// another object, it will be removed from that object first.
|
||||
// Add appends a child Object. If the Object is already a child of
|
||||
// another Object, it will be removed from that Object first.
|
||||
Add (Object)
|
||||
// Remove removes a child Object, if it is a child of this Box.
|
||||
Remove (Object)
|
||||
@@ -215,7 +229,7 @@ type ContainerBox interface {
|
||||
// types.
|
||||
}
|
||||
|
||||
// LayoutHints are passed to a layout to tell it how to arrange child boxes.
|
||||
// LayoutHints are passed to a layout to tell it how to arrange child Boxes.
|
||||
type LayoutHints struct {
|
||||
// Bounds is the bounding rectangle that children should be placed
|
||||
// within. Any padding values are already applied.
|
||||
@@ -238,7 +252,7 @@ type Layout interface {
|
||||
// MinimumSize returns the minimum width and height of
|
||||
// LayoutHints.Bounds needed to properly lay out all child Boxes.
|
||||
MinimumSize (LayoutHints, BoxQuerier) image.Point
|
||||
// Arrange arranges child boxes according to the given LayoutHints.
|
||||
// Arrange arranges child Boxes according to the given LayoutHints.
|
||||
Arrange (LayoutHints, BoxArranger)
|
||||
// RecommendedHeight returns the recommended height for a given width,
|
||||
// if supported. Otherwise, it should just return the minimum height.
|
||||
@@ -254,18 +268,18 @@ type Layout interface {
|
||||
|
||||
// BoxQuerier allows the attributes of a ContainerBox's children to be queried.
|
||||
type BoxQuerier interface {
|
||||
// Len returns the amount of boxes.
|
||||
// Len returns the amount of Boxes.
|
||||
Len () int
|
||||
// MinimumSize returns the minimum size of a box.
|
||||
// MinimumSize returns the minimum size of a Box.
|
||||
MinimumSize (index int) image.Point
|
||||
// RecommendedWidth returns the recommended width for a given height for
|
||||
// a box, if supported. Otherwise, it should just return the minimum
|
||||
// width of that box. The result of this behavior may or may not be
|
||||
// a Box, if supported. Otherwise, it should just return the minimum
|
||||
// width of that Box. The result of this behavior may or may not be
|
||||
// respected, depending on the situation.
|
||||
RecommendedWidth (index int, height int) int
|
||||
// RecommendedHeight returns the recommended height for a given width
|
||||
// for a box, if supported. Otherwise, it should just return the minimum
|
||||
// width of that box. The result of this behavireor may or may not be
|
||||
// for a Box, if supported. Otherwise, it should just return the minimum
|
||||
// width of that Box. The result of this behavior may or may not be
|
||||
// respected, depending on the situation.
|
||||
RecommendedHeight (index int, width int) int
|
||||
}
|
||||
@@ -275,39 +289,65 @@ type BoxQuerier interface {
|
||||
type BoxArranger interface {
|
||||
BoxQuerier
|
||||
|
||||
// SetBounds sets the bounds of a box.
|
||||
// SetBounds sets the bounds of a Box.
|
||||
SetBounds (index int, bounds image.Rectangle)
|
||||
}
|
||||
|
||||
// Window is an operating system window. It can contain one object. Windows
|
||||
// themselves are completely transparent, and become opaque once an opaque
|
||||
// object is added as their root.
|
||||
// WindowKind specifies a Window's kind, which determines how it is displayed
|
||||
// and managed by the operating system.
|
||||
type WindowKind string; const (
|
||||
// Normal is a normal Window.
|
||||
WindowKindNormal WindowKind = "Normal"
|
||||
// Plain is an undecorated Window that does not appear in window lists.
|
||||
// It is intended for things like docks, panels, etc.
|
||||
WindowKindPlain WindowKind = "Plain"
|
||||
// Utility is a small Window for toolboxes, command palletes, etc. It is
|
||||
// usually given special styling and management by the OS.
|
||||
WindowKindUtility WindowKind = "Utility"
|
||||
// Toolbar is a small Window for menus and panes which have been "torn
|
||||
// off" from their main Window or position. It is usually given special
|
||||
// styling and management by the OS.
|
||||
WindowKindToolbar WindowKind = "Toolbar"
|
||||
// Menu is an undecorated Window for drop down menus, context menus,
|
||||
// etc. It closes once the user interacts outside of it.
|
||||
WindowKindMenu WindowKind = "Menu"
|
||||
// Modal, while open, blocks all user input from reaching its parent
|
||||
// window, forcing the user's attention. It is usually given special
|
||||
// styling and management by the OS. Note that in some environments it
|
||||
// will not be given window controls, so it should contain some "Close"
|
||||
// or "Cancel" button.
|
||||
WindowKindModal WindowKind = "Modal"
|
||||
)
|
||||
|
||||
// Window is an operating system Window. It can directly contain one object,
|
||||
// which is usually a container. Windows themselves are completely transparent,
|
||||
// and become opaque once an opaque object is added as their root.
|
||||
type Window interface {
|
||||
// SetRoot sets the root child of the window. There can only be one at
|
||||
// Bounds returns the bounds of the Window including its frame, if
|
||||
// possible. This means that the top-left point of the bounds will be
|
||||
// either zero or negative.
|
||||
Bounds () image.Rectangle
|
||||
// InnerBounds returns the inner bounds of the Window, not including its
|
||||
// frame. This means that the top-left point of the bounds will be zero.
|
||||
InnerBounds () image.Rectangle
|
||||
// SetRoot sets the root child of the Window. There can only be one at
|
||||
// a time, and setting it will remove the current child if there is one.
|
||||
SetRoot (Object)
|
||||
// SetTitle sets the title of the window.
|
||||
// SetTitle sets the title of the Window.
|
||||
SetTitle (string)
|
||||
// SetIcon sets the icon of the window.
|
||||
// SetIcon sets the icon of the Window.
|
||||
SetIcon (Icon)
|
||||
// SetResizable sets whether the window can be resized by the user in
|
||||
// SetResizable sets whether the Window can be resized by the user in
|
||||
// the X and Y directions. If one or both axes are false, the ones that
|
||||
// are will be shrunk to the window's minimum size.
|
||||
// are will be shrunk to the Window's minimum size.
|
||||
SetResizable (x, y bool)
|
||||
// SetBounds sets this window's bounds. This may or may not have any
|
||||
// effect on the window's position on screen depending on the platform.
|
||||
// SetBounds sets this Window's bounds. This may or may not have any
|
||||
// effect on the Window's position on screen depending on the platform.
|
||||
SetBounds (image.Rectangle)
|
||||
// NewChild creates a new window that is semantically a child of this
|
||||
// window. It does not actually reside within this window, but it may be
|
||||
// linked to it via some other means. This is intended for things like
|
||||
// toolboxes and tear-off menus.
|
||||
NewChild (image.Rectangle) (Window, error)
|
||||
// NewMenu creates a new menu window. This window is undecorated and
|
||||
// will close once the user clicks outside of it.
|
||||
NewMenu (image.Rectangle) (Window, error)
|
||||
// NewModal creates a new modal window that blocks all input to this
|
||||
// window until it is closed.
|
||||
NewModal (image.Rectangle) (Window, error)
|
||||
// NewChild creates a new Window that is semantically a child of this
|
||||
// Window. It does not actually reside within this Window, but it may be
|
||||
// linked to it via some other means.
|
||||
NewChild (WindowKind, image.Rectangle) (Window, error)
|
||||
// Modifiers returns which modifier keys on the keyboard are currently
|
||||
// being held down.
|
||||
Modifiers () input.Modifiers
|
||||
@@ -324,8 +364,13 @@ type Window interface {
|
||||
SetVisible (bool)
|
||||
// Visible returns whether or not this window is visible.
|
||||
Visible () bool
|
||||
// Close closes the window.
|
||||
Close ()
|
||||
// OnClose specifies a function to be called when the window is closed.
|
||||
// Close closes the window. This does not trigger the TryClose event.
|
||||
Close () error
|
||||
// OnTryClose specifies a function to be called when the user attempts
|
||||
// to close the Window. If any registered handlers returns false, the
|
||||
// Window will not be closed. This can be used to display some sort of
|
||||
// "Unsaved changes" warning to the user.
|
||||
OnTryClose (func () bool) event.Cookie
|
||||
// OnClose specifies a function to be called when the Window is closed.
|
||||
OnClose (func ()) event.Cookie
|
||||
}
|
||||
|
||||
80
style.go
80
style.go
@@ -1,80 +0,0 @@
|
||||
package tomo
|
||||
|
||||
import "fmt"
|
||||
import "image/color"
|
||||
|
||||
// Role describes the role of an object.
|
||||
type Role struct {
|
||||
// Package is an optional namespace field. If specified, it should be
|
||||
// the package name or module name the object is from.
|
||||
Package string
|
||||
|
||||
// Object specifies what type of object it is. For example:
|
||||
// - TextInput
|
||||
// - Table
|
||||
// - Label
|
||||
// - Dial
|
||||
// This should correspond directly to the type name of the object.
|
||||
Object string
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface. It follows the format of:
|
||||
// Package.Object
|
||||
func (r Role) String () string {
|
||||
return fmt.Sprintf("%s.%s", r.Package, r.Object)
|
||||
}
|
||||
|
||||
// R is shorthand for creating a role structure.
|
||||
func R (pack, object string) Role {
|
||||
return Role { Package: pack, Object: object }
|
||||
}
|
||||
|
||||
// Color represents a color ID.
|
||||
type Color int; const (
|
||||
ColorBackground Color = iota
|
||||
ColorForeground
|
||||
ColorRaised
|
||||
ColorSunken
|
||||
ColorAccent
|
||||
)
|
||||
|
||||
// RGBA satisfies the color.Color interface.
|
||||
func (id Color) RGBA () (r, g, b, a uint32) {
|
||||
if style == nil { return }
|
||||
return style.Colors[id].RGBA()
|
||||
}
|
||||
|
||||
var style *Style
|
||||
|
||||
// SetStyle sets the style.
|
||||
func SetStyle (sty *Style) {
|
||||
assertBackend()
|
||||
style = sty
|
||||
backend.SetStyle(sty)
|
||||
}
|
||||
|
||||
// Style can apply a visual style to different objects.
|
||||
type Style struct {
|
||||
// Rules determines which styles get applied to which Objects.
|
||||
Rules []Rule
|
||||
|
||||
// Colors maps tomo.Color values to color.RGBA values.
|
||||
Colors map[Color] color.Color
|
||||
}
|
||||
|
||||
// Rule describes under what circumstances should certain style attributes be
|
||||
// active.
|
||||
type Rule struct {
|
||||
Role Role
|
||||
Tags []string
|
||||
Set AttrSet
|
||||
}
|
||||
|
||||
// Ru is shorthand for creating a rule structure
|
||||
func Ru (set AttrSet, role Role, tags ...string) Rule {
|
||||
return Rule {
|
||||
Role: role,
|
||||
Tags: tags,
|
||||
Set: set,
|
||||
}
|
||||
}
|
||||
74
tomo.go
74
tomo.go
@@ -2,67 +2,49 @@ package tomo
|
||||
|
||||
import "sync"
|
||||
import "image"
|
||||
import "errors"
|
||||
import "git.tebibyte.media/tomo/tomo/canvas"
|
||||
|
||||
var backendLock sync.Mutex
|
||||
var backend Backend
|
||||
// TODO this really sucks. It might be a good idea to have Do be the entry point
|
||||
// for every off-thread call, and Stop should just call backend.Stop within
|
||||
// backend.Do. This is because Do is a queue and is not vulnerable to recursive
|
||||
// locking.
|
||||
|
||||
// Run initializes a backend, runs the specified callback function, and runs the
|
||||
// event loop in that order. This function blocks until Stop is called, or the
|
||||
// backend experiences a fatal error.
|
||||
func Run (callback func ()) error {
|
||||
if backend != nil {
|
||||
return errors.New("there is already a backend running")
|
||||
}
|
||||
|
||||
back, err := initialize()
|
||||
if err != nil { return err }
|
||||
|
||||
backendLock.Lock()
|
||||
backend = back
|
||||
backendLock.Unlock()
|
||||
|
||||
callback()
|
||||
// callback may have called tomo.Stop
|
||||
if backend == nil { return nil }
|
||||
return backend.Run()
|
||||
var stopping bool
|
||||
var stoppingLock sync.Mutex
|
||||
func isStopping () bool {
|
||||
stoppingLock.Lock()
|
||||
defer stoppingLock.Unlock()
|
||||
return stopping
|
||||
}
|
||||
func setStopping (is bool) {
|
||||
stoppingLock.Lock()
|
||||
defer stoppingLock.Unlock()
|
||||
stopping = is
|
||||
}
|
||||
|
||||
func assertBackend () {
|
||||
if backend == nil { panic("nil backend") }
|
||||
}
|
||||
|
||||
// Stop stops the backend, unblocking run. Run may be called again after calling
|
||||
// Stop.
|
||||
// Stop stops the currently running Backend.
|
||||
func Stop () {
|
||||
assertBackend()
|
||||
backend.Stop()
|
||||
|
||||
if isStopping() { return }
|
||||
setStopping(true)
|
||||
backendLock.Lock()
|
||||
defer backendLock.Unlock()
|
||||
if backend == nil { return }
|
||||
backend.Stop()
|
||||
backend = nil
|
||||
backendLock.Unlock()
|
||||
setStopping(false)
|
||||
}
|
||||
|
||||
// Do performs a callback function in the event loop thread as soon as possible.
|
||||
func Do (callback func ()) {
|
||||
backendLock.Lock()
|
||||
defer backendLock.Unlock()
|
||||
if backend != nil { backend.Do(callback) }
|
||||
backendLock.Unlock()
|
||||
}
|
||||
|
||||
// NewWindow creates and returns a window within the specified bounds on screen.
|
||||
func NewWindow (bounds image.Rectangle) (Window, error) {
|
||||
// NewWindow creates and returns a Window within the specified bounds on screen.
|
||||
func NewWindow (kind WindowKind, bounds image.Rectangle) (Window, error) {
|
||||
assertBackend()
|
||||
return backend.NewWindow(bounds)
|
||||
}
|
||||
|
||||
// NewPlainWindow is like NewWindow, but it creates an undecorated window that
|
||||
// does not appear in window lists. It is intended for creating things like
|
||||
// docks, panels, etc.
|
||||
func NewPlainWindow (bounds image.Rectangle) (Window, error) {
|
||||
assertBackend()
|
||||
return backend.NewPlainWindow(bounds)
|
||||
return backend.NewWindow(kind, bounds)
|
||||
}
|
||||
|
||||
// NewBox creates and returns a basic Box.
|
||||
@@ -89,8 +71,8 @@ func NewContainerBox () ContainerBox {
|
||||
return backend.NewContainerBox()
|
||||
}
|
||||
|
||||
// NewTexture creates a new texture from an image. When no longer in use, it
|
||||
// must be freed using Close().
|
||||
// NewTexture creates a new canvas.Texture from an image. When no longer in use,
|
||||
// it must be freed using Close().
|
||||
func NewTexture (source image.Image) canvas.TextureCloser {
|
||||
assertBackend()
|
||||
return backend.NewTexture(source)
|
||||
|
||||
130
unit.go
130
unit.go
@@ -1,5 +1,6 @@
|
||||
package tomo
|
||||
|
||||
import "fmt"
|
||||
import "image"
|
||||
import "image/color"
|
||||
|
||||
@@ -11,6 +12,16 @@ type Side int; const (
|
||||
SideLeft
|
||||
)
|
||||
|
||||
func (side Side) String () string {
|
||||
switch side {
|
||||
case SideTop: return "SideTop"
|
||||
case SideRight: return "SideRight"
|
||||
case SideBottom: return "SideBottom"
|
||||
case SideLeft: return "SideLeft"
|
||||
default: return fmt.Sprintf("Side(%d)", side)
|
||||
}
|
||||
}
|
||||
|
||||
// Inset represents a rectangle inset that can have a different value for each
|
||||
// side.
|
||||
type Inset [4]int
|
||||
@@ -37,9 +48,13 @@ func I (sides ...int) Inset {
|
||||
}
|
||||
}
|
||||
|
||||
// Apply returns the given rectangle, shrunk on all four sides by the given
|
||||
// inset. If a measurment of the inset is negative, that side will instead be
|
||||
// expanded outward. If the rectangle's dimensions cannot be reduced any
|
||||
func (inset Inset) String () string {
|
||||
return fmt.Sprintf("%d %d %d %d", inset[0], inset[1], inset[2], inset[3])
|
||||
}
|
||||
|
||||
// Apply returns the given image.Rectangle, shrunk on all four sides by the
|
||||
// given Inset. If a measurment of the Inset is negative, that side will instead
|
||||
// be expanded outward. If the rectangle's dimensions cannot be reduced any
|
||||
// further, an empty rectangle near its center will be returned.
|
||||
func (inset Inset) Apply (bigger image.Rectangle) (smaller image.Rectangle) {
|
||||
smaller = bigger
|
||||
@@ -61,7 +76,7 @@ func (inset Inset) Apply (bigger image.Rectangle) (smaller image.Rectangle) {
|
||||
return
|
||||
}
|
||||
|
||||
// Inverse returns a negated version of the inset.
|
||||
// Inverse returns a negated version of the Inset.
|
||||
func (inset Inset) Inverse () (prime Inset) {
|
||||
return Inset {
|
||||
inset[0] * -1,
|
||||
@@ -81,12 +96,18 @@ func (inset Inset) Vertical () int {
|
||||
return inset[SideTop] + inset[SideBottom]
|
||||
}
|
||||
|
||||
// Border represents a single border of a box.
|
||||
// Border represents a single border of a Box.
|
||||
type Border struct {
|
||||
Width Inset
|
||||
Color [4]color.Color
|
||||
}
|
||||
|
||||
func (border Border) String () string {
|
||||
return fmt.Sprintf("%v %v %v %v / %v",
|
||||
border.Color[0], border.Color[1], border.Color[2], border.Color[3],
|
||||
border.Width)
|
||||
}
|
||||
|
||||
// Align lists basic alignment types.
|
||||
type Align int; const (
|
||||
AlignStart Align = iota // similar to left-aligned text
|
||||
@@ -95,6 +116,16 @@ type Align int; const (
|
||||
AlignEven // similar to justified text
|
||||
)
|
||||
|
||||
func (align Align) String () string {
|
||||
switch align {
|
||||
case AlignStart: return "AlignStart"
|
||||
case AlignMiddle: return "AlignMiddle"
|
||||
case AlignEnd: return "AlignEnd"
|
||||
case AlignEven: return "AlignEven"
|
||||
default: return fmt.Sprintf("Align(%d)", align)
|
||||
}
|
||||
}
|
||||
|
||||
// TextureMode lists texture rendering modes.
|
||||
type TextureMode int; const (
|
||||
TextureModeTile TextureMode = iota
|
||||
@@ -103,3 +134,92 @@ type TextureMode int; const (
|
||||
// TODO more modes: fit, fill, and stretch
|
||||
// Of course canvas will need to have a concept of this.
|
||||
)
|
||||
|
||||
func (mode TextureMode) String () string {
|
||||
switch mode {
|
||||
case TextureModeTile: return "TextureModeTile"
|
||||
case TextureModeCenter: return "TextureModeCenter"
|
||||
default: return fmt.Sprintf("TextureMode(%d)", mode)
|
||||
}
|
||||
}
|
||||
|
||||
// Face represents a typeface.
|
||||
type Face struct {
|
||||
// Font specifies the font name. This should be searched for in a list
|
||||
// of installed fonts.
|
||||
Font string
|
||||
// Size is the point size of the face.
|
||||
Size float64
|
||||
// Weight is the weight of the face. If zero, it should be interpreted
|
||||
// as normal (equivalent to 400).
|
||||
Weight int
|
||||
// Italic is how italicized the face is. It ranges from 0 to 1. It is
|
||||
// different from Slant in that it may alter the design of the glyphs
|
||||
// instead of simply skewing them.
|
||||
Italic float64
|
||||
// Slant is how slanted the face is. It ranges from 0 to 1. It is
|
||||
// different from Italic in that it simply skews the glyphs without
|
||||
// altering their design.
|
||||
Slant float64
|
||||
}
|
||||
|
||||
func (face Face) String () string {
|
||||
return fmt.Sprintf (
|
||||
"%s %fpt W%d I%f S%f",
|
||||
face.Font, face.Size, face.Weight, face.Italic, face.Slant)
|
||||
}
|
||||
|
||||
// Role describes the role of an Object.
|
||||
type Role struct {
|
||||
// Package is an optional namespace field. If specified, it should be
|
||||
// the package name or module name the object is from.
|
||||
Package string
|
||||
|
||||
// Object specifies what type of Object it is. For example:
|
||||
// - TextInput
|
||||
// - Table
|
||||
// - Label
|
||||
// - Dial
|
||||
// This should correspond directly to the type name of the Object.
|
||||
Object string
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface. It follows the format of:
|
||||
// Package.Object
|
||||
func (r Role) String () string {
|
||||
return fmt.Sprintf("%s.%s", r.Package, r.Object)
|
||||
}
|
||||
|
||||
// R is a convenience constructor for Role.
|
||||
func R (pack, object string) Role {
|
||||
return Role { Package: pack, Object: object }
|
||||
}
|
||||
|
||||
// Color represents a color ID.
|
||||
type Color int; const (
|
||||
ColorBackground Color = iota
|
||||
ColorForeground
|
||||
ColorRaised
|
||||
ColorSunken
|
||||
ColorAccent
|
||||
)
|
||||
|
||||
func (id Color) String () string {
|
||||
switch id {
|
||||
case ColorBackground: return "ColorBackground"
|
||||
case ColorForeground: return "ColorForeground"
|
||||
case ColorRaised: return "ColorRaised"
|
||||
case ColorSunken: return "ColorSunken"
|
||||
case ColorAccent: return "ColorAccent"
|
||||
default: return fmt.Sprintf("Color(%d)", id)
|
||||
}
|
||||
}
|
||||
|
||||
// RGBA satisfies the color.Color interface. The result of this method may be
|
||||
// rendered invalid if the visual style changes, so it is often necessary to
|
||||
// subscribe to the StyleChange event in order to get new RGBA values when this
|
||||
// happens.
|
||||
func (id Color) RGBA () (r, g, b, a uint32) {
|
||||
assertBackend()
|
||||
return backend.ColorRGBA(id)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user