Compare commits
16 Commits
92660ef7de
...
v0.45.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 43fb3b8feb | |||
| a022fa3ad4 | |||
| 7e3a9759ee | |||
| 559490e5e8 | |||
| bc38ea14e1 | |||
| b264e11ea6 | |||
| a01e5f8716 | |||
| 3de570373f | |||
| a1eb53c4db | |||
| 750882eef1 | |||
| a98d09d320 | |||
| e6a4b6c70e | |||
| 03fab6fcc0 | |||
| 6fd236f96c | |||
| cf092b4447 | |||
| 8403d621a8 |
118
attribute.go
118
attribute.go
@@ -2,59 +2,37 @@ 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.
|
||||
type Attr interface {
|
||||
// Equals returns true if both attributes can reasonably be declared
|
||||
// equal.
|
||||
Equals (Attr) bool
|
||||
attr () int
|
||||
Kind () AttrKind
|
||||
attr ()
|
||||
}
|
||||
|
||||
type AttrKind int; const (
|
||||
AttrKindColor AttrKind = iota
|
||||
AttrKindTexture
|
||||
AttrKindTextureMode
|
||||
AttrKindBorder
|
||||
AttrKindMinimumSize
|
||||
AttrKindPadding
|
||||
AttrKindGap
|
||||
AttrKindTextColor
|
||||
AttrKindDotColor
|
||||
AttrKindFace
|
||||
AttrKindWrap
|
||||
AttrKindAlign
|
||||
AttrKindOverflow
|
||||
AttrKindLayout
|
||||
)
|
||||
|
||||
// AttrColor sets the background color of a box.
|
||||
type AttrColor struct { color.Color }
|
||||
// AttrTexture sets the texture of a box to a named texture.
|
||||
// AttrTexture sets the texture of a box to a texture.
|
||||
type AttrTexture struct { canvas.Texture }
|
||||
// AttrTextureMode sets the rendering mode of a box's texture.
|
||||
type AttrTextureMode TextureMode
|
||||
@@ -70,8 +48,8 @@ type AttrGap image.Point
|
||||
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 }
|
||||
// AttrFace sets the type face, if the box is a TextBox.
|
||||
type AttrFace 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.
|
||||
@@ -118,8 +96,8 @@ 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 }
|
||||
func AFace (face Face) AttrFace {
|
||||
return AttrFace(face)
|
||||
}
|
||||
// AWrap is a convenience constructor for the wrap attribute.
|
||||
func AWrap (wrap bool) AttrWrap {
|
||||
@@ -256,24 +234,32 @@ func (this AttrLayout) Equals (other Attr) bool {
|
||||
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 (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 () { }
|
||||
|
||||
91
backend.go
91
backend.go
@@ -1,8 +1,8 @@
|
||||
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,
|
||||
@@ -33,22 +33,30 @@ type Backend interface {
|
||||
// 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 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 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 +64,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
|
||||
}
|
||||
|
||||
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=
|
||||
|
||||
54
icon.go
54
icon.go
@@ -370,46 +370,20 @@ const (
|
||||
IconWeatherStorm Icon = "WeatherStorm"
|
||||
)
|
||||
|
||||
// Texture returns a texture of the corresponding icon ID.
|
||||
// Texture returns a 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 an 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)
|
||||
}
|
||||
|
||||
@@ -57,8 +57,12 @@ type Box interface {
|
||||
// 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
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
30
tomo.go
30
tomo.go
@@ -1,38 +1,8 @@
|
||||
package tomo
|
||||
|
||||
import "sync"
|
||||
import "image"
|
||||
import "errors"
|
||||
import "git.tebibyte.media/tomo/tomo/canvas"
|
||||
|
||||
var backendLock sync.Mutex
|
||||
var backend Backend
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
func assertBackend () {
|
||||
if backend == nil { panic("nil backend") }
|
||||
}
|
||||
|
||||
// Stop stops the backend, unblocking run. Run may be called again after calling
|
||||
// Stop.
|
||||
func Stop () {
|
||||
|
||||
120
unit.go
120
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,6 +48,10 @@ func I (sides ...int) Inset {
|
||||
}
|
||||
}
|
||||
|
||||
func (inset Inset) String () string {
|
||||
return fmt.Sprintf("%d %d %d %d", inset[0], inset[1], inset[2], inset[3])
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -87,6 +102,12 @@ type Border struct {
|
||||
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 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
|
||||
)
|
||||
|
||||
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