Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bc38ea14e1 | |||
| b264e11ea6 | |||
| a01e5f8716 | |||
| 3de570373f | |||
| a1eb53c4db | |||
| 750882eef1 | |||
| a98d09d320 | |||
| e6a4b6c70e | |||
| 03fab6fcc0 | |||
| 6fd236f96c | |||
| cf092b4447 | |||
| 8403d621a8 | |||
| 92660ef7de | |||
| 140de5917f | |||
| 3bd9de9110 | |||
| 89f49fee71 | |||
| f561b71c56 | |||
| 75fd31bb24 | |||
| 5eeb03b113 | |||
| 3b3ea6d837 | |||
| ab4728144f | |||
| 1bc85775b8 | |||
| 2b2dca77c8 | |||
| d6229459c5 | |||
| 9914cdeb9c | |||
| ba31748b2e | |||
| 4b56306bd2 | |||
| 6859a2b0f7 | |||
| 469b27bdfb | |||
| 70aaa79b7d | |||
| e2017f04ff | |||
| a8c72f7391 | |||
| 1d4bc03a7d | |||
| 1030bede90 | |||
| 2570fada95 | |||
| 8894b98c8b | |||
| 1c20acd993 | |||
| 87494c014a | |||
| f6556aa57f | |||
| 35b52c6a3f | |||
| 109283f520 | |||
| dc50e7290d |
@@ -3,6 +3,6 @@
|
||||
[](https://pkg.go.dev/git.tebibyte.media/tomo/tomo)
|
||||
|
||||
Tomo is a lightweight GUI toolkit written in pure Go. This repository defines
|
||||
the API that other components of the toolkit agree on. In order to use Tomo in
|
||||
the API that other components of the toolkit agree upon. In order to use Tomo in
|
||||
an application, use [Nasin](https://git.tebibyte.media/tomo/nasin), which builds
|
||||
an application framework on top of Tomo.
|
||||
|
||||
323
attribute.go
Normal file
323
attribute.go
Normal file
@@ -0,0 +1,323 @@
|
||||
package tomo
|
||||
|
||||
import "image"
|
||||
import "image/color"
|
||||
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 AttrKind of each attribute and must not be
|
||||
// modified directly.
|
||||
type AttrSet map[AttrKind] Attr
|
||||
|
||||
// AS builds an AttrSet out of a vararg list of Attr values. If multiple Attrs
|
||||
// of the same kind are specified, the last one will override the others.
|
||||
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.Kind()] = 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.Kind()]; !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
|
||||
Kind () AttrKind
|
||||
attr ()
|
||||
}
|
||||
|
||||
type AttrKind int; const (
|
||||
AttrKindColor AttrKind = iota
|
||||
AttrKindIcon
|
||||
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 }
|
||||
// AttrIcon sets the icon of a box to a named icon. It has the same end result
|
||||
// as setting the texture of a box to a centered icon texture.
|
||||
type AttrIcon Icon
|
||||
// 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
|
||||
// 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 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 }
|
||||
|
||||
// AColor is a convenience constructor for the color attribute.
|
||||
func AColor (col color.Color) AttrColor {
|
||||
return AttrColor { Color: col }
|
||||
}
|
||||
// AIcon is a convenience constructor for the icon attribute.
|
||||
func AIcon (icon Icon) AttrIcon {
|
||||
return AttrIcon(icon)
|
||||
}
|
||||
// ATexture is a convenience constructor for the texture attribute.
|
||||
func ATexture (texture canvas.Texture) AttrTexture {
|
||||
return AttrTexture { Texture: texture }
|
||||
}
|
||||
// ATextureMode is a convenience constructor for the texture mode attribute.
|
||||
func ATextureMode (mode TextureMode) AttrTextureMode {
|
||||
return AttrTextureMode(mode)
|
||||
}
|
||||
// ABorder is a convenience constructor for the border attribute.
|
||||
func ABorder (borders ...Border) AttrBorder {
|
||||
return AttrBorder(borders)
|
||||
}
|
||||
// AMinimumSize is a convenience constructor for the minimum size attribute.
|
||||
func AMinimumSize (x, y int) AttrMinimumSize {
|
||||
return AttrMinimumSize(image.Pt(x, y))
|
||||
}
|
||||
// APadding is a convenience constructor for the padding attribute.
|
||||
func APadding (sides ...int) AttrPadding {
|
||||
return AttrPadding(I(sides...))
|
||||
}
|
||||
// AGap is a convenience constructor for the gap attribute.
|
||||
func AGap (x, y int) AttrGap {
|
||||
return AttrGap(image.Pt(x, y))
|
||||
}
|
||||
// ATextColor is a convenience constructor for the text color attribute.
|
||||
func ATextColor (col color.Color) AttrTextColor {
|
||||
return AttrTextColor { Color: col }
|
||||
}
|
||||
// ADotColor is a convenience constructor for the dot color attribute.
|
||||
func ADotColor (col color.Color) AttrDotColor {
|
||||
return AttrDotColor { Color: col }
|
||||
}
|
||||
// AFace is a convenience constructor for the face attribute.
|
||||
func AFace (face Face) AttrFace {
|
||||
return AttrFace(face)
|
||||
}
|
||||
// AWrap is a convenience constructor for the wrap attribute.
|
||||
func AWrap (wrap bool) AttrWrap {
|
||||
return AttrWrap(wrap)
|
||||
}
|
||||
// AAlign is a convenience constructor for the align attribute.
|
||||
func AAlign (x, y Align) AttrAlign {
|
||||
return AttrAlign { X: x, Y: y }
|
||||
}
|
||||
// AOverflow is a convenience constructor for the overflow attribute.
|
||||
func AOverflow (x, y bool) AttrOverflow {
|
||||
return AttrOverflow { X: x, Y: y }
|
||||
}
|
||||
// ALayout is a convenience constructor for the overflow attribute.
|
||||
func ALayout (layout Layout) AttrLayout {
|
||||
return AttrLayout { Layout: layout }
|
||||
}
|
||||
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrColor) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrColor); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrIcon) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrIcon); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrTexture) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrTexture); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrTextureMode) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrTextureMode); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes 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 }
|
||||
for index := range this {
|
||||
thisBorder := this[index]
|
||||
otherBorder := other[index]
|
||||
if thisBorder != otherBorder { return false }
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrMinimumSize) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrMinimumSize); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrPadding) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrPadding); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrGap) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrGap); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrTextColor) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrTextColor); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrDotColor) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrDotColor); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrFace) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrFace); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrWrap) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrWrap); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrAlign) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrAlign); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrOverflow) Equals (other Attr) bool {
|
||||
if other, ok := other.(AttrOverflow); ok {
|
||||
return this == other
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Equals returns true if both attributes can reasonably be declared equal.
|
||||
func (this AttrLayout) Equals (other Attr) bool {
|
||||
// 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
|
||||
}
|
||||
|
||||
func (AttrColor) Kind () AttrKind { return AttrKindColor }
|
||||
func (AttrIcon) Kind () AttrKind { return AttrKindIcon }
|
||||
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 () { }
|
||||
func (AttrIcon) 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 () { }
|
||||
26
backend.go
26
backend.go
@@ -3,6 +3,7 @@ package tomo
|
||||
import "sort"
|
||||
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,16 +34,23 @@ 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)
|
||||
|
||||
// SetIcons 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.
|
||||
SetIcons (Icons)
|
||||
// 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
|
||||
|
||||
// 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.
|
||||
|
||||
116
data/data.go
116
data/data.go
@@ -3,57 +3,127 @@ package data
|
||||
|
||||
import "io"
|
||||
import "bytes"
|
||||
import "strings"
|
||||
|
||||
// Data represents arbitrary polymorphic data that can be used for data transfer
|
||||
// between applications.
|
||||
type Data map[Mime] io.ReadSeekCloser
|
||||
type Data interface {
|
||||
// Convert converts the data to the specified MIME type and returns it.
|
||||
// If the type is not supported, this behavior will return nil, and
|
||||
// false for ok. Note that Convert may be called multiple times for the
|
||||
// same MIME type, so it must not return the same reader.
|
||||
Convert (Mime) (reader io.ReadSeekCloser, ok bool)
|
||||
|
||||
// Supported returns a slice of MIME types that Convert can accept.
|
||||
Supported () []Mime
|
||||
}
|
||||
|
||||
// Mime represents a MIME type.
|
||||
type Mime struct {
|
||||
// Type is the first half of the MIME type, and Subtype is the second
|
||||
// half. The separating slash is not included in either. For example,
|
||||
// text/html becomes:
|
||||
// Mime { Type: "text", Subtype: "html" }
|
||||
// Mime { Type: "text", Subtype: "html" }
|
||||
// The subtype is stored here including the tree and the suffix.
|
||||
Type, Subtype string
|
||||
|
||||
// Charset is an optional field applicable to text types that specifies
|
||||
// the character encoding of the data. If empty, UTF-8 is conventionally
|
||||
// assumed because it's the only text encoding worth using.
|
||||
Charset string
|
||||
}
|
||||
|
||||
// M is shorthand for creating a MIME type.
|
||||
func M (ty, subtype string) Mime {
|
||||
return Mime { ty, subtype }
|
||||
return Mime {
|
||||
Type: strings.ToLower(ty),
|
||||
Subtype: strings.ToLower(subtype),
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string representation of the MIME type.
|
||||
func (mime Mime) String () string {
|
||||
return mime.Type + "/" + mime.Subtype
|
||||
// ParseMime parses a MIME type from text.
|
||||
func ParseMime (text string) Mime {
|
||||
ty, subty, _ := strings.Cut(text, "/")
|
||||
subty, parameters, _ := strings.Cut(subty, ";")
|
||||
mime := M(ty, subty)
|
||||
for _, parameter := range strings.Split(parameters, " ") {
|
||||
if parameter == "" { continue }
|
||||
key, val, _ := strings.Cut(parameter, "=")
|
||||
// TODO handle things like quoted values
|
||||
val = strings.TrimSpace(val)
|
||||
switch strings.TrimSpace(strings.ToLower(key)) {
|
||||
case "charset":
|
||||
mime.Charset = val
|
||||
}
|
||||
}
|
||||
return mime
|
||||
}
|
||||
|
||||
// MimePlain returns the MIME type of plain text.
|
||||
func MimePlain () Mime { return Mime { "text", "plain" } }
|
||||
func MimePlain () Mime { return Mime { Type: "text", Subtype: "plain" } }
|
||||
|
||||
// MimeFile returns the MIME type of a file path/URI.
|
||||
func MimeFile () Mime { return Mime { "text", "uri-list" } }
|
||||
func MimeFile () Mime { return Mime { Type: "text", Subtype: "uri-list" } }
|
||||
|
||||
// String returns the string representation of the MIME type.
|
||||
func (mime Mime) String () string {
|
||||
out := mime.Type + "/" + mime.Subtype
|
||||
if mime.Charset != "" {
|
||||
out += "; charset=" + mime.Charset
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// FromText returns plain text Data given a string.
|
||||
func FromText (text string) Data {
|
||||
return FromBytes(MimePlain(), []byte(text))
|
||||
}
|
||||
|
||||
type byteReadCloser struct { *bytes.Reader }
|
||||
func (byteReadCloser) Close () error { return nil }
|
||||
|
||||
// Text returns plain text Data given a string.
|
||||
func Text (text string) Data {
|
||||
return Bytes(MimePlain(), []byte(text))
|
||||
type bytesData struct {
|
||||
mime Mime
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
// Bytes constructs a Data given a buffer and a mime type.
|
||||
func Bytes (mime Mime, buffer []byte) Data {
|
||||
return Data {
|
||||
mime: byteReadCloser { bytes.NewReader(buffer) },
|
||||
// FromBytes constructs a Data given a buffer and a mime type.
|
||||
func FromBytes (mime Mime, buffer []byte) Data {
|
||||
return bytesData {
|
||||
mime: mime,
|
||||
buffer: buffer,
|
||||
}
|
||||
}
|
||||
|
||||
// Merge combines several Datas together. If multiple Datas provide a reader for
|
||||
// the same mime type, the ones further on in the list will take precedence.
|
||||
func Merge (individual ...Data) (combined Data) {
|
||||
for _, data := range individual {
|
||||
for mime, reader := range data {
|
||||
combined[mime] = reader
|
||||
}}
|
||||
return
|
||||
func (bytesDat bytesData) Convert (mime Mime) (io.ReadSeekCloser, bool) {
|
||||
if mime != bytesDat.mime { return nil, false }
|
||||
return byteReadCloser { bytes.NewReader(bytesDat.buffer) }, true
|
||||
}
|
||||
|
||||
func (bytesDat bytesData) Supported () []Mime {
|
||||
return []Mime { bytesDat.mime }
|
||||
}
|
||||
|
||||
type mergedData []Data
|
||||
|
||||
func (merged mergedData) Convert (mime Mime) (io.ReadSeekCloser, bool) {
|
||||
for _, individual := range merged {
|
||||
if reader, ok := individual.Convert(mime); ok {
|
||||
return reader, ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (merged mergedData) Supported () (supported []Mime) {
|
||||
for _, individual := range merged {
|
||||
supported = append(individual.Supported(), supported...)
|
||||
}
|
||||
return supported
|
||||
}
|
||||
|
||||
// Merge combines several Datas together. If multiple Datas provide a reader for
|
||||
// the same mime type, the ones first in the list will take precedence.
|
||||
func Merge (individual ...Data) (combined Data) {
|
||||
return mergedData(individual)
|
||||
}
|
||||
|
||||
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=
|
||||
|
||||
64
icon.go
64
icon.go
@@ -10,16 +10,6 @@ type IconSize int; const (
|
||||
IconSizeLarge
|
||||
)
|
||||
|
||||
// String satisfies the fmt.Stringer interface.
|
||||
func (size IconSize) String () string {
|
||||
switch size {
|
||||
case IconSizeSmall: return "small"
|
||||
case IconSizeMedium: return "medium"
|
||||
case IconSizeLarge: return "large"
|
||||
default: return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Icon represents an icon ID.
|
||||
type Icon string
|
||||
|
||||
@@ -380,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 icons == nil { return nil }
|
||||
return icons.Icon(id, size)
|
||||
}
|
||||
|
||||
// MimeIcon returns an icon corresponding to a MIME type.
|
||||
func MimeIcon (mime data.Mime, size IconSize) canvas.Texture {
|
||||
if icons == nil { return nil }
|
||||
return icons.MimeIcon(mime, size)
|
||||
}
|
||||
|
||||
// Icons holds a set of icon textures.
|
||||
type Icons 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 Icons 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 icons Icons
|
||||
|
||||
// SetIcons sets the icon set.
|
||||
func SetIcons (icns Icons) {
|
||||
assertBackend()
|
||||
icons = icns
|
||||
backend.SetIcons(icns)
|
||||
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)
|
||||
}
|
||||
|
||||
204
object.go
204
object.go
@@ -1,8 +1,6 @@
|
||||
package tomo
|
||||
|
||||
import "image"
|
||||
import "image/color"
|
||||
import "golang.org/x/image/font"
|
||||
import "git.tebibyte.media/tomo/tomo/text"
|
||||
import "git.tebibyte.media/tomo/tomo/data"
|
||||
import "git.tebibyte.media/tomo/tomo/event"
|
||||
@@ -44,43 +42,27 @@ type Box interface {
|
||||
// InnerBounds returns the inner bounding rectangle of the box. It is
|
||||
// the value of Bounds inset by the Box's border and padding.
|
||||
InnerBounds () image.Rectangle
|
||||
// MinimumSize returns the minimum width and height this Box's bounds
|
||||
// can be set to. This will return the value of whichever of these is
|
||||
// greater:
|
||||
// - The size as set by SetMinimumSize
|
||||
// - The size taken up by the Box's border and padding. If there is
|
||||
// internal content that does not overflow, the size of that is also
|
||||
// taken into account here.
|
||||
MinimumSize () image.Point
|
||||
// Role returns this Box's role as set by SetRole.
|
||||
Role () Role
|
||||
// SetBounds sets the bounding rectangle of this Box relative to the
|
||||
// Window.
|
||||
SetBounds (image.Rectangle)
|
||||
// SetColor sets the background color of this Box.
|
||||
SetColor (color.Color)
|
||||
// SetTextureTile sets a repeating background texture. If the texture is
|
||||
// transparent, it will be overlayed atop the color specified by
|
||||
// SetColor(). This will remove any previous texture set by this method
|
||||
// or another method.
|
||||
SetTextureTile (canvas.Texture)
|
||||
// SetTextureCenter sets a centered background texture. If the texture
|
||||
// is transparent, it will be overlayed atop the color specified by
|
||||
// SetColor(). This will remove any previous texture set by this method
|
||||
// or another method.
|
||||
SetTextureCenter (canvas.Texture)
|
||||
// SetBorder sets the Border(s) of the box. The first Border will be the
|
||||
// most outset, and the last Border will be the most inset.
|
||||
SetBorder (...Border)
|
||||
// SetMinimumSize sets the minimum width and height of the box, as
|
||||
// described in MinimumSize.
|
||||
SetMinimumSize (image.Point)
|
||||
// SetPadding sets the padding between the Box's innermost Border and
|
||||
// its content.
|
||||
SetPadding (Inset)
|
||||
// SetRole sets what role this Box takes on. It is used to apply styling
|
||||
// from a theme.
|
||||
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 (string) bool
|
||||
// SetTag adds or removes a named tag.
|
||||
SetTag (string, bool)
|
||||
|
||||
// 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
|
||||
@@ -98,34 +80,32 @@ type Box interface {
|
||||
// If set to false and the Box is already focused. the focus is removed.
|
||||
SetFocusable (bool)
|
||||
|
||||
// Focused returns whether or not this Box has keyboard focus.
|
||||
Focused () bool
|
||||
// Modifiers returns which modifier keys on the keyboard are currently
|
||||
// being held down.
|
||||
Modifiers () input.Modifiers
|
||||
// MousePosition returns the position of the mouse pointer relative to
|
||||
// the Window.
|
||||
MousePosition () image.Point
|
||||
|
||||
// These are event subscription functions that allow callbacks to be
|
||||
// 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.
|
||||
OnFocusEnter (func ()) event.Cookie
|
||||
OnFocusLeave (func ()) event.Cookie
|
||||
OnDNDEnter (func ()) event.Cookie
|
||||
OnDNDLeave (func ()) event.Cookie
|
||||
OnDNDDrop (func (data.Data)) event.Cookie
|
||||
OnMouseEnter (func ()) event.Cookie
|
||||
OnMouseLeave (func ()) event.Cookie
|
||||
OnMouseMove (func ()) event.Cookie
|
||||
OnMouseDown (func (input.Button)) event.Cookie
|
||||
OnMouseUp (func (input.Button)) event.Cookie
|
||||
OnScroll (func (deltaX, deltaY float64)) event.Cookie
|
||||
OnKeyDown (func (key input.Key, numberPad bool)) event.Cookie
|
||||
OnKeyUp (func (key input.Key, numberPad bool)) event.Cookie
|
||||
OnStyleChange (func ()) event.Cookie
|
||||
OnIconsChange (func ()) event.Cookie
|
||||
OnFocusEnter (func () ) event.Cookie
|
||||
OnFocusLeave (func () ) event.Cookie
|
||||
OnStyleChange (func () ) event.Cookie
|
||||
OnIconSetChange (func () ) event.Cookie
|
||||
OnDNDEnter (func () ) event.Cookie
|
||||
OnDNDLeave (func () ) event.Cookie
|
||||
OnDNDDrop (func (data.Data)) event.Cookie
|
||||
OnMouseEnter (func () ) event.Cookie
|
||||
OnMouseLeave (func () ) event.Cookie
|
||||
// These event subscription behaviors require their callbacks to return
|
||||
// a bool value. Under normal circumstances, these events are propagated
|
||||
// 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.
|
||||
OnMouseMove (func () bool) event.Cookie
|
||||
OnButtonDown (func (button input.Button) bool) event.Cookie
|
||||
OnButtonUp (func (button input.Button) bool) event.Cookie
|
||||
OnScroll (func (deltaX, deltaY float64) bool) event.Cookie
|
||||
OnKeyDown (func (key input.Key, numberPad bool) bool) event.Cookie
|
||||
OnKeyUp (func (key input.Key, numberPad bool) bool) event.Cookie
|
||||
}
|
||||
|
||||
// CanvasBox is a box that can be drawn to.
|
||||
@@ -174,28 +154,14 @@ type SurfaceBox interface {
|
||||
// ContentBox is a box that has some kind of content.
|
||||
type ContentBox interface {
|
||||
Box
|
||||
|
||||
// SetOverflow sets whether or not the Box's content overflows
|
||||
// horizontally and vertically. Overflowing content is clipped to the
|
||||
// bounds of the Box inset by all Borders (but not padding).
|
||||
SetOverflow (horizontal, vertical bool)
|
||||
// SetAlign sets how the Box's content is distributed horizontally and
|
||||
// vertically.
|
||||
SetAlign (x, y Align)
|
||||
|
||||
// ContentBounds returns the bounds of the inner content of the Box
|
||||
// relative to the Box's InnerBounds.
|
||||
ContentBounds () image.Rectangle
|
||||
// RecommendedHeight returns the recommended height for a given width,
|
||||
// if supported by the current content or layout. Otherwise, it should
|
||||
// just return the minimum height.
|
||||
RecommendedHeight (width int) int
|
||||
// RecommendedWidth returns the recommended width for a given height, if
|
||||
// supported by the current content or layout. Otherwise, it should just
|
||||
// return the minimum width.
|
||||
RecommendedWidth (height int) int
|
||||
// ScrollTo shifts the origin of the Box's content to the origin of the
|
||||
// Box's InnerBounds, offset by the given point.
|
||||
ScrollTo (image.Point)
|
||||
|
||||
// OnContentBoundsChange specifies a function to be called when the
|
||||
// Box's ContentBounds or InnerBounds changes.
|
||||
OnContentBoundsChange (func ()) event.Cookie
|
||||
@@ -207,18 +173,10 @@ type TextBox interface {
|
||||
|
||||
// SetText sets the text content of the Box.
|
||||
SetText (string)
|
||||
// SetTextColor sets the text color.
|
||||
SetTextColor (color.Color)
|
||||
// SetFace sets the font face text is rendered in.
|
||||
SetFace (font.Face)
|
||||
// SetWrap sets whether or not the text wraps.
|
||||
SetWrap (bool)
|
||||
|
||||
|
||||
// SetSelectable sets whether or not the text content can be
|
||||
// highlighted/selected.
|
||||
SetSelectable (bool)
|
||||
// SetDotColor sets the highlight color of selected text.
|
||||
SetDotColor (color.Color)
|
||||
// Select sets the text cursor or selection.
|
||||
Select (text.Dot)
|
||||
// Dot returns the text cursor or selection.
|
||||
@@ -233,8 +191,6 @@ type TextBox interface {
|
||||
type ContainerBox interface {
|
||||
ContentBox
|
||||
|
||||
// SetGap sets the gap between child Objects.
|
||||
SetGap (image.Point)
|
||||
// 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)
|
||||
@@ -246,22 +202,21 @@ type ContainerBox interface {
|
||||
Insert (child Object, before Object)
|
||||
// Clear removes all child Objects.
|
||||
Clear ()
|
||||
// Length returns the amount of child Objects.
|
||||
Length () int
|
||||
// Len returns the amount of child Objects.
|
||||
Len () int
|
||||
// At returns the child Object at the specified index.
|
||||
At (int) Object
|
||||
// SetLayout sets the layout of this Box. Child Objects will be
|
||||
// positioned according to it.
|
||||
SetLayout (Layout)
|
||||
// SetInputMask sets whether or not user input events will be sent to
|
||||
// this Box's children. If false, which is the default, input events
|
||||
// will be sent to this Box as well as all of its children. If true,
|
||||
// any input events that would otherwise go to this Box's children are
|
||||
// sent to it instead. This prevents children from performing event
|
||||
// catching as described in the documentation for Box.
|
||||
SetInputMask (bool)
|
||||
|
||||
// These methods control whether certain user input events get
|
||||
// propagated to child Objects. If set to true, the relevant events will
|
||||
// be sent to this container. If set to false (which is the default),
|
||||
// the events will be sent to the appropriate child Object.
|
||||
CaptureDND (bool)
|
||||
CaptureMouse (bool)
|
||||
CaptureScroll (bool)
|
||||
CaptureKeyboard (bool)
|
||||
// TODO: if it turns out selecting specific kinds of events to mask off
|
||||
// is a good idea, have SetInputMask take in a vararg list of event
|
||||
// types.
|
||||
}
|
||||
|
||||
// LayoutHints are passed to a layout to tell it how to arrange child boxes.
|
||||
@@ -286,19 +241,46 @@ type LayoutHints struct {
|
||||
type Layout interface {
|
||||
// MinimumSize returns the minimum width and height of
|
||||
// LayoutHints.Bounds needed to properly lay out all child Boxes.
|
||||
MinimumSize (LayoutHints, []Box) image.Point
|
||||
MinimumSize (LayoutHints, BoxQuerier) image.Point
|
||||
// Arrange arranges child boxes according to the given LayoutHints.
|
||||
Arrange (LayoutHints, []Box)
|
||||
Arrange (LayoutHints, BoxArranger)
|
||||
// RecommendedHeight returns the recommended height for a given width,
|
||||
// if supported. Otherwise, it should just return the minimum height.
|
||||
// The result of this behavior may or may not be respected, depending on
|
||||
// the situation.
|
||||
RecommendedHeight (LayoutHints, []Box, int) int
|
||||
RecommendedHeight (LayoutHints, BoxQuerier, int) int
|
||||
// RecommendedWidth returns the recommended width for a given height, if
|
||||
// supported. Otherwise, it should just return the minimum width. The
|
||||
// result of this behavior may or may not be respected, depending on the
|
||||
// situation.
|
||||
RecommendedWidth (LayoutHints, []Box, int) int
|
||||
RecommendedWidth (LayoutHints, BoxQuerier, int) int
|
||||
}
|
||||
|
||||
// BoxQuerier allows the attributes of a ContainerBox's children to be queried.
|
||||
type BoxQuerier interface {
|
||||
// Len returns the amount of boxes.
|
||||
Len () int
|
||||
// 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
|
||||
// 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
|
||||
// respected, depending on the situation.
|
||||
RecommendedHeight (index int, width int) int
|
||||
}
|
||||
|
||||
// BoxArranger is a BoxQuerier that allows arranging child boxes according to a
|
||||
// layout.
|
||||
type BoxArranger interface {
|
||||
BoxQuerier
|
||||
|
||||
// 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
|
||||
@@ -310,9 +292,8 @@ type Window interface {
|
||||
SetRoot (Object)
|
||||
// SetTitle sets the title of the window.
|
||||
SetTitle (string)
|
||||
// SetIcon sets the icon of the window. When multiple icon sizes are
|
||||
// provided, the best fitting one is chosen for display.
|
||||
SetIcon (... canvas.Texture)
|
||||
// SetIcon sets the icon of the window.
|
||||
SetIcon (Icon)
|
||||
// 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.
|
||||
@@ -320,11 +301,6 @@ type Window interface {
|
||||
// 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)
|
||||
// Widget returns a window representing a smaller iconified form of this
|
||||
// window. How exactly this window is used depends on the platform.
|
||||
// Subsequent calls to this method on the same window will return the
|
||||
// same window object.
|
||||
Widget () (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. This is intended for things like
|
||||
@@ -336,6 +312,12 @@ type Window interface {
|
||||
// NewModal creates a new modal window that blocks all input to this
|
||||
// window until it is closed.
|
||||
NewModal (image.Rectangle) (Window, error)
|
||||
// Modifiers returns which modifier keys on the keyboard are currently
|
||||
// being held down.
|
||||
Modifiers () input.Modifiers
|
||||
// MousePosition returns the position of the mouse pointer relative to
|
||||
// the Window.
|
||||
MousePosition () image.Point
|
||||
// Copy copies data to the clipboard.
|
||||
Copy (data.Data)
|
||||
// Paste reads data from the clipboard. When the data is available or an
|
||||
|
||||
83
style.go
83
style.go
@@ -1,83 +0,0 @@
|
||||
package tomo
|
||||
|
||||
import "fmt"
|
||||
import "git.tebibyte.media/tomo/tomo/event"
|
||||
|
||||
// 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
|
||||
|
||||
// Variant is an optional field to be used when an object has one or
|
||||
// more soft variants under one type. For example, an object "Slider"
|
||||
// may have variations "horizontal" and "vertical".
|
||||
Variant string
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface.
|
||||
// It follows the format of:
|
||||
// Package.Object[Variant]
|
||||
func (r Role) String () string {
|
||||
return fmt.Sprintf("%s.%s[%s]", r.Package, r.Object, r.Variant)
|
||||
}
|
||||
|
||||
// R is shorthand for creating a Role structure.
|
||||
func R (pack, object, variant string) Role {
|
||||
return Role { Package: pack, Object: object, Variant: variant }
|
||||
}
|
||||
|
||||
// Color represents a color ID.
|
||||
type Color int; const (
|
||||
ColorBackground Color = iota
|
||||
ColorForeground
|
||||
ColorRaised
|
||||
ColorSunken
|
||||
ColorAccent
|
||||
)
|
||||
|
||||
// String satisfies the fmt.Stringer interface.
|
||||
func (c Color) String () string {
|
||||
switch c {
|
||||
case ColorBackground: return "background"
|
||||
case ColorForeground: return "foreground"
|
||||
case ColorRaised: return "raised"
|
||||
case ColorSunken: return "sunken"
|
||||
case ColorAccent: return "accent"
|
||||
default: return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// RGBA satisfies the color.Color interface.
|
||||
func (id Color) RGBA () (r, g, b, a uint32) {
|
||||
if style == nil { return }
|
||||
return style.RGBA(id)
|
||||
}
|
||||
|
||||
// Style can apply a visual style to different objects.
|
||||
type Style interface {
|
||||
// Apply applies the theme to the given object, according to its role.
|
||||
// This may register event listeners with the given object; closing the
|
||||
// returned cookie must remove them.
|
||||
Apply (Object) event.Cookie
|
||||
|
||||
// RGBA returns the RGBA values of the corresponding color ID.
|
||||
RGBA (Color) (r, g, b, a uint32)
|
||||
}
|
||||
|
||||
var style Style
|
||||
|
||||
// SetStyle sets the style.
|
||||
func SetStyle (sty Style) {
|
||||
assertBackend()
|
||||
style = sty
|
||||
backend.SetStyle(sty)
|
||||
}
|
||||
2
tomo.go
2
tomo.go
@@ -24,6 +24,8 @@ func Run (callback func ()) error {
|
||||
backendLock.Unlock()
|
||||
|
||||
callback()
|
||||
// callback may have called tomo.Stop
|
||||
if backend == nil { return nil }
|
||||
return backend.Run()
|
||||
}
|
||||
|
||||
|
||||
129
unit.go
129
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
|
||||
@@ -94,3 +115,111 @@ type Align int; const (
|
||||
AlignEnd // similar to right-aligned text
|
||||
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
|
||||
TextureModeCenter
|
||||
|
||||
// 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