Compare commits

...

109 Commits

Author SHA1 Message Date
484ead0f76 More doc comment improvements 2024-09-12 00:21:44 -04:00
018f93cbf9 Add String method to IconSize 2024-09-12 00:02:08 -04:00
8657730c25 Forgot one 2024-09-11 23:56:12 -04:00
9d63e27ab6 Many doc comment fixes 2024-09-11 23:52:36 -04:00
8a3f41c7db The "pressed" tag now references config.ButtonChordInteract 2024-09-11 23:15:10 -04:00
987bb613dd Change the buttons in config to button chords 2024-09-11 22:56:46 -04:00
7bf62c25fe Fix misspelling in config doc comment 2024-09-11 22:42:35 -04:00
763e5db3fc Config now stores common keybinds 2024-09-11 22:41:30 -04:00
dcdb411c0e Modifier keys are now stored using bit flags
Or-ing together constants is cleaner than a struct literal with
booleans
2024-09-11 22:40:19 -04:00
efbdaef390 Add utilities for key/mouse chords to input 2024-09-11 18:44:26 -04:00
8546f11471 Add config package
Closes #6
2024-09-11 01:49:25 -04:00
46f4c50381 Document the window kinds 2024-09-11 01:06:54 -04:00
b88e32fa49 Introduce the concept of window kinds 2024-09-11 00:54:45 -04:00
8f43fa310c Add new child window constructors
Closes #29
2024-09-11 00:21:57 -04:00
20c7e0fdb1 Add FuncCookie
Closes #27
2024-08-20 22:55:31 -04:00
d8a8ad7e0a Re-organize event.go 2024-08-20 22:55:20 -04:00
3feba5f811 event.Cookie is now just io.Closer
Closes #26
2024-08-20 22:50:51 -04:00
75c654d4ae Add error to Window.Close 2024-08-20 22:42:31 -04:00
da38e5411f Add TryClose event to Window
Closes #24
2024-08-20 22:41:41 -04:00
d47b525e42 Add documentation for AttrKind 2024-08-20 21:29:12 -04:00
862e08edf1 Add AttrCursor 2024-08-20 21:25:54 -04:00
47b2231acd Add list of cursor shapes
Progress on #25
2024-08-20 21:22:19 -04:00
b05e1f5d50 Fixed IconListContract being identical to IconListExpand 2024-08-18 16:16:40 -04:00
608a898be3 Fix deadlock in Stop 2024-08-16 17:40:39 -04:00
91a8ae2fa5 Add some new icons
Closes #23
2024-08-16 17:08:28 -04:00
cacfd20a8a Describe IconUnknown 2024-08-16 17:05:11 -04:00
d08fe845fc Add Bounds, InnerBounds to Window
Closes #22
2024-08-16 17:03:24 -04:00
e23a688103 Remove checkbox icons 2024-08-13 12:30:47 -04:00
f4cc47eb16 Fix meaningless panics 2024-08-13 12:17:54 -04:00
43fb3b8feb Remove AttrIcon
- Has no compelling use case (all use cases need the texture size
  which this attribute cannot provide)
- Duplicates functionality
- The rationale for adding it initially was never that strong
2024-08-11 22:23:42 -04:00
a022fa3ad4 Remove the registry mechanism
Nasin has its own registry system that is way more flexible than
what was in this module, that ought to be used instead.
2024-08-11 01:45:51 -04:00
7e3a9759ee AttrIcon has size information 2024-08-10 21:08:09 -04:00
559490e5e8 Remove AttrSet 2024-08-09 23:24:02 -04:00
bc38ea14e1 Add String methods for all types in unit.go 2024-08-07 19:13:17 -04:00
b264e11ea6 Merge style.go and unit.go 2024-08-03 22:09:16 -04:00
a01e5f8716 Change the name of MimeIcon to MimeIconTexture 2024-08-03 22:04:59 -04:00
3de570373f Remove Style as per #21 2024-08-03 22:04:06 -04:00
a1eb53c4db Added functions to get icon textures from the backend 2024-08-03 21:57:10 -04:00
750882eef1 Address icon attribute changes in #21 2024-08-03 21:52:36 -04:00
a98d09d320 golang.org/x/image is no longer a dependency 2024-08-03 21:26:05 -04:00
e6a4b6c70e Change AttrFont to AttrFace as per #21 2024-08-02 19:12:25 -04:00
03fab6fcc0 Remove the font interface and add a Face struct as per #21 2024-08-02 19:08:48 -04:00
6fd236f96c AttrFace is now AttrFont
Closes #19
2024-07-31 00:35:29 -04:00
cf092b4447 Add UnsetAttr
Fully address #20
2024-07-31 00:19:39 -04:00
8403d621a8 AttrKind values are now part of the API 2024-07-30 18:23:37 -04:00
92660ef7de AttrLayout no longer attempts to compare itself 2024-07-25 18:09:10 -04:00
140de5917f Make text wrapping an attribute 2024-07-25 04:01:46 -04:00
3bd9de9110 AttrSet is now just a map 2024-07-25 03:57:55 -04:00
89f49fee71 Removed Tag from BoxQuerier 2024-07-25 02:59:32 -04:00
f561b71c56 Layouts are now attributes 2024-07-24 20:06:20 -04:00
75fd31bb24 Way better event API for objects 2024-07-24 18:37:45 -04:00
5eeb03b113 Replace the catch methods on ContainerBox with SetInputMask 2024-07-24 14:21:39 -04:00
3b3ea6d837 Various attribute improvements 2024-07-24 14:17:31 -04:00
ab4728144f Tags are now used for checking hover, focus, pressed status 2024-07-24 14:15:35 -04:00
1bc85775b8 Add equality checking for attributes 2024-07-21 22:06:10 -04:00
2b2dca77c8 Fix grammar in readme 2024-07-21 22:06:01 -04:00
d6229459c5 Fix ATextureMode 2024-07-21 10:59:35 -04:00
9914cdeb9c Add TextureMode attribute 2024-07-21 02:55:29 -04:00
ba31748b2e Add convenience constructors for attributes 2024-07-21 00:19:16 -04:00
4b56306bd2 Fix spelling of recommended 2024-07-21 00:10:16 -04:00
6859a2b0f7 Fix BoxQuerier interface 2024-07-21 00:00:00 -04:00
469b27bdfb Rename Icons to IconSize 2024-07-20 23:09:32 -04:00
70aaa79b7d Changed Rule contstructor to Ru 2024-07-20 17:12:54 -04:00
e2017f04ff Create constructor for rules 2024-07-20 17:12:31 -04:00
a8c72f7391 Remove the map of named textures from Style 2024-07-20 14:37:06 -04:00
1d4bc03a7d Remove unnecessary String methods 2024-07-20 14:32:59 -04:00
1030bede90 Clean up Box methods a bit 2024-07-20 14:21:41 -04:00
2570fada95 Redid style system with a focus on attributes 2024-07-20 14:18:49 -04:00
8894b98c8b Add BoxQuerier and BoxArranger for use in Layout 2024-07-20 01:23:46 -04:00
1c20acd993 Revise event API for boxes 2024-07-20 01:14:31 -04:00
87494c014a Change Text, Bytes to FromText, FromBytes 2024-07-19 15:26:03 -04:00
f6556aa57f Fix mergedData.Supported 2024-07-19 15:24:44 -04:00
35b52c6a3f Completely redo the data API 2024-07-19 15:24:16 -04:00
109283f520 Dont't crash if tomo.Run callback calls tomo.Stop 2024-07-19 13:40:56 -04:00
dc50e7290d Remove Window.Widget 2024-06-11 23:41:06 -04:00
e102c032cb Layout recommended size takes in layout hints and boxes 2024-06-07 23:07:15 -04:00
d35b0532a1 Speaks english for 21 years and still has no idea how to spell 2024-06-07 19:22:13 -04:00
dffe766328 Rename theme.go to style.go 2024-06-07 19:19:35 -04:00
c1d77028b4 Add a note that reccomended sizes arent always respected 2024-06-07 19:15:33 -04:00
83b2c3c665 Add system for reccomending widths/heights
Remedy #6
2024-06-07 19:13:22 -04:00
89307e0dd6 Slight wording fix 2024-06-07 19:06:18 -04:00
5d9c584743 Document that windows are supposed to be transparent
Remedy #16
2024-06-07 19:05:38 -04:00
fd7d2b7ead Add Window.SetBounds
Remedy #17
2024-06-07 18:59:43 -04:00
ca4d3e62c4 Remedy #10 2024-06-07 18:57:16 -04:00
d587e39506 Separate icon functionality out of Style and into Icons 2024-06-07 18:27:26 -04:00
f0c092be8a Break some scalars, vectors, and enums into a separate file 2024-06-07 18:21:15 -04:00
06f45c80e2 Change Theme to Style 2024-06-07 18:19:43 -04:00
71171d762c Add a "SetResizable" behavior to Window 2024-06-07 14:11:36 -04:00
341eea5b5d Remove references to MainWindow 2024-06-07 00:56:29 -04:00
453953bbcd Unify Window and MainWindow 2024-06-07 00:54:14 -04:00
31ce286bc7 Window icons are set using textures 2024-06-06 20:15:00 -04:00
26cf1f4a88 Roles are stored in Boxes now 2024-06-03 19:57:20 -04:00
6ef1f7ba78 Add OnSizeChange to SurfaceBox
Remedy #15
2024-06-03 19:37:34 -04:00
d7093be696 Update go.sum 2024-06-03 19:33:07 -04:00
64bf8f3b2d NewCanvas actually returns a Canvas 2024-06-03 19:32:47 -04:00
50cbac17a9 CanvasCloser is now actually a valid type 2024-06-03 19:32:06 -04:00
d096d40c57 Add a way for applications to request canvases
Makes fix to #14 useful
2024-06-03 19:29:41 -04:00
34358da6eb Add canvas.CanvasCloser
Remedy #14
2024-06-03 19:28:03 -04:00
be1b9d77e8 Instruct themes to return nil icons if the icon doesn't exist 2024-05-28 22:01:56 -04:00
d71ac02748 Add unchecked checkbox icon 2024-05-27 15:10:11 -04:00
2bbf85ebd8 Add more missing icons 2024-05-27 04:02:22 -04:00
320535e7f3 Remove Box.Visible, box.SetVisible
It was a bad idea. Remedy #12.
2024-05-26 17:42:11 -04:00
775fb84e9b Add back some missing icons 2024-05-26 17:42:04 -04:00
6bf3450799 Add link to XDG icon specification in icon.go 2024-05-25 00:57:26 -04:00
874171c9de Un-export backend Initialize() function 2024-05-25 00:52:25 -04:00
6cfb16f9fe Change theme variable from current -> theme 2024-05-25 00:51:34 -04:00
73e9b28eeb Redo standard icon set around the XDG icon naming spec
https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
Remedy #11
2024-05-25 00:50:15 -04:00
17039059ce Merge theme package into central package 2024-05-24 21:45:16 -04:00
a6b115b591 Replace SetTexture with SetTextureTile and SetTextureCenter
Remedy #8
2024-05-24 14:47:43 -04:00
19 changed files with 1504 additions and 843 deletions

View File

@@ -3,6 +3,6 @@
[![Go Reference](https://pkg.go.dev/badge/git.tebibyte.media/tomo/tomo.svg)](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.

282
attribute.go Normal file
View File

@@ -0,0 +1,282 @@
package tomo
import "image"
import "image/color"
import "git.tebibyte.media/tomo/tomo/canvas"
// Attr modifies one thing about a Box's style.
type Attr interface {
// Equals returns true if both Attrs can reasonably be declared equal.
Equals (Attr) bool
Kind () AttrKind
attr ()
}
// 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
)
// 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 AttrTexture.
func ATexture (texture canvas.Texture) AttrTexture {
return AttrTexture { Texture: texture }
}
// ATextureMode is a convenience constructor for AttrTextureMode.
func ATextureMode (mode TextureMode) AttrTextureMode {
return AttrTextureMode(mode)
}
// ABorder is a convenience constructor for AttrBorder.
func ABorder (borders ...Border) AttrBorder {
return AttrBorder(borders)
}
// 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 AttrPadding.
func APadding (sides ...int) AttrPadding {
return AttrPadding(I(sides...))
}
// 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 AttrTextColor.
func ATextColor (col color.Color) AttrTextColor {
return AttrTextColor { Color: col }
}
// ADotColor is a convenience constructor for AttrDotColor.
func ADotColor (col color.Color) AttrDotColor {
return AttrDotColor { Color: col }
}
// AFace is a convenience constructor for AttrFace.
func AFace (face Face) AttrFace {
return AttrFace(face)
}
// AWrap is a convenience constructor for AttrWrap.
func AWrap (wrap bool) AttrWrap {
return AttrWrap(wrap)
}
// 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 AttrOverflow.
func AOverflow (x, y bool) AttrOverflow {
return AttrOverflow { X: x, Y: y }
}
// 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 Attrs 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 Attrs 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 Attrs 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 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 }
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 Attrs 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 Attrs 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 Attrs 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 Attrs 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 Attrs 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 Attrs 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 Attrs 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 Attrs 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 Attrs 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 Attrs 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
}
// 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
}
}
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 () { }
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 () { }

View File

@@ -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
@@ -17,23 +17,41 @@ type Backend interface {
NewSurfaceBox () (SurfaceBox, error)
NewContainerBox () ContainerBox
// NewWindow creates a normal MainWindow and returns it.
NewWindow (image.Rectangle) (MainWindow, error)
// NewWindow creates a normal Window and returns it.
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) (MainWindow, 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
// Run runs the event loop until Stop() is called, or the backend
// 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
// 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)
// 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
// 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
@@ -41,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
}

View File

@@ -2,6 +2,7 @@
// primitives.
package canvas
import "io"
import "image"
import "image/draw"
import "image/color"
@@ -32,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.
@@ -68,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
@@ -77,13 +78,20 @@ type Canvas interface {
SubCanvas (image.Rectangle) Canvas
}
// CanvasCloser is a canvas that can be closed. Anything that receives a
// CanvasCloser must close it after use.
type CanvasCloser interface {
Canvas
io.Closer
}
// 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

View File

@@ -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

View File

@@ -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
View 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
View 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"
)

View File

@@ -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)
}

View File

@@ -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
View File

@@ -1,5 +1,3 @@
module git.tebibyte.media/tomo/tomo
go 1.20
require golang.org/x/image v0.11.0

36
go.sum
View File

@@ -1,36 +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.8.0 h1:agUcRXV/+w6L9ryntYYsF2x9fQTMd4T8fiiYXAVW6Jg=
golang.org/x/image v0.8.0/go.mod h1:PwLxp3opCYg4WR2WO9P0L6ESnsD6bLTWcw8zanLMVFM=
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.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
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=

400
icon.go Normal file
View File

@@ -0,0 +1,400 @@
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.
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
IconAddressBookNew Icon = "AddressBookNew"
IconApplicationExit Icon = "ApplicationExit"
IconAppointmentNew Icon = "AppointmentNew"
IconCallStart Icon = "CallStart"
IconCallStop Icon = "CallStop"
IconContactNew Icon = "ContactNew"
// actions: dialog
IconDialogOkay Icon = "DialogOkay"
IconDialogCancel Icon = "DialogCancel"
// actions: edit
IconEditClear Icon = "EditClear"
IconEditCopy Icon = "EditCopy"
IconEditCut Icon = "EditCut"
IconEditDelete Icon = "EditDelete"
IconEditFind Icon = "EditFind"
IconEditFindReplace Icon = "EditFindReplace"
IconEditPaste Icon = "EditPaste"
IconEditRedo Icon = "EditRedo"
IconEditSelectAll Icon = "EditSelectAll"
IconEditUndo Icon = "EditUndo"
// actions: file
IconFileNew Icon = "FileNew"
IconDirectoryNew Icon = "DirectoryNew"
IconFileOpen Icon = "FileOpen"
IconFileOpenRecent Icon = "FileOpenRecent"
IconFilePageSetup Icon = "FilePageSetup"
IconFilePrint Icon = "FilePrint"
IconFilePrintPreview Icon = "FilePrintPreview"
IconFilePermissions Icon = "FilePermissions"
IconFileProperties Icon = "FileProperties"
IconFileRename Icon = "FileRename"
IconFileRevert Icon = "FileRevert"
IconFileSave Icon = "FileSave"
IconFileSaveAs Icon = "FileSaveAs"
IconFileSend Icon = "FileSend"
// actions: format
IconFormatIndentLess Icon = "FormatIndentLess"
IconFormatIndentMore Icon = "FormatIndentMore"
IconFormatAlignCenter Icon = "FormatAlignCenter"
IconFormatAlignEven Icon = "FormatAlignEven"
IconFormatAlignLeft Icon = "FormatAlignLeft"
IconFormatAlignRight Icon = "FormatAlignRight"
IconFormatTextDirectionLtr Icon = "FormatTextDirectionLtr"
IconFormatTextDirectionRtl Icon = "FormatTextDirectionRtl"
IconFormatTextBold Icon = "FormatTextBold"
IconFormatTextItalic Icon = "FormatTextItalic"
IconFormatTextUnderline Icon = "FormatTextUnderline"
IconFormatTextStrikethrough Icon = "FormatTextStrikethrough"
// actions: go
IconGoBottom Icon = "GoBottom"
IconGoDown Icon = "GoDown"
IconGoFirst Icon = "GoFirst"
IconGoHome Icon = "GoHome"
IconGoJump Icon = "GoJump"
IconGoLast Icon = "GoLast"
IconGoNext Icon = "GoNext"
IconGoPrevious Icon = "GoPrevious"
IconGoTop Icon = "GoTop"
IconGoUp Icon = "GoUp"
// actions: help
IconHelpAbout Icon = "HelpAbout"
IconHelpContents Icon = "HelpContents"
IconHelpFaq Icon = "HelpFaq"
// actions: insert
IconInsertImage Icon = "InsertImage"
IconInsertLink Icon = "InsertLink"
IconInsertObject Icon = "InsertObject"
IconInsertText Icon = "InsertText"
// 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"
IconMailMarkJunk Icon = "MailMarkJunk"
IconMailMarkNotJunk Icon = "MailMarkNotJunk"
IconMailMarkRead Icon = "MailMarkRead"
IconMailMarkUnread Icon = "MailMarkUnread"
IconMailMessageNew Icon = "MailMessageNew"
IconMailReplyAll Icon = "MailReplyAll"
IconMailReplySender Icon = "MailReplySender"
IconMailSend Icon = "MailSend"
IconMailReceive Icon = "MailReceive"
// actions: media
IconMediaEject Icon = "MediaEject"
IconMediaPlaybackPause Icon = "MediaPlaybackPause"
IconMediaPlaybackStart Icon = "MediaPlaybackStart"
IconMediaPlaybackStop Icon = "MediaPlaybackStop"
IconMediaRecord Icon = "MediaRecord"
IconMediaSeekBackward Icon = "MediaSeekBackward"
IconMediaSeekForward Icon = "MediaSeekForward"
IconMediaSkipBackward Icon = "MediaSkipBackward"
IconMediaSkipForward Icon = "MediaSkipForward"
// actions: object
IconObjectFlipHorizontal Icon = "ObjectFlipHorizontal"
IconObjectFlipVertical Icon = "ObjectFlipVertical"
IconObjectRotateLeft Icon = "ObjectRotateLeft"
IconObjectRotateRight Icon = "ObjectRotateRight"
// actions: process
IconProcessStop Icon = "ProcessStop"
// actions: system
IconSystemLockScreen Icon = "SystemLockScreen"
IconSystemLogOut Icon = "SystemLogOut"
IconSystemRun Icon = "SystemRun"
IconSystemSearch Icon = "SystemSearch"
IconSystemReboot Icon = "SystemReboot"
IconSystemShutdown Icon = "SystemShutdown"
// actions: tools
IconToolsCheckSpelling Icon = "ToolsCheckSpelling"
// actions: value
IconValueIncrement Icon = "ValueIncrement"
IconValueDecrement Icon = "ValueDecrement"
IconValueReset Icon = "ValueReset"
// actions: view
IconViewFullscreen Icon = "ViewFullscreen"
IconViewRefresh Icon = "ViewRefresh"
IconViewRestore Icon = "ViewRestore"
IconViewSortAscending Icon = "ViewSortAscending"
IconViewSortDescending Icon = "ViewSortDescending"
// actions: window
IconWindowClose Icon = "WindowClose"
IconWindowNew Icon = "WindowNew"
// actions: zoom
IconZoomFitBest Icon = "ZoomFitBest"
IconZoomIn Icon = "ZoomIn"
IconZoomOriginal Icon = "ZoomOriginal"
IconZoomOut Icon = "ZoomOut"
// applications
// Keep these in sync with nasin.ApplicationRole!
IconApplication Icon = "Application" // generic
IconApplicationWebBrowser Icon = "ApplicationWebBrowser"
IconApplicationMesssanger Icon = "ApplicationMesssanger"
IconApplicationPhone Icon = "ApplicationPhone"
IconApplicationMail Icon = "ApplicationMail"
IconApplicationTerminalEmulator Icon = "ApplicationTerminalEmulator"
IconApplicationFileBrowser Icon = "ApplicationFileBrowser"
IconApplicationTextEditor Icon = "ApplicationTextEditor"
IconApplicationDocumentViewer Icon = "ApplicationDocumentViewer"
IconApplicationWordProcessor Icon = "ApplicationWordProcessor"
IconApplicationSpreadsheet Icon = "ApplicationSpreadsheet"
IconApplicationSlideshow Icon = "ApplicationSlideshow"
IconApplicationCalculator Icon = "ApplicationCalculator"
IconApplicationPreferences Icon = "ApplicationPreferences"
IconApplicationProcessManager Icon = "ApplicationProcessManager"
IconApplicationSystemInformation Icon = "ApplicationSystemInformation"
IconApplicationManual Icon = "ApplicationManual"
IconApplicationCamera Icon = "ApplicationCamera"
IconApplicationImageViewer Icon = "ApplicationImageViewer"
IconApplicationMediaPlayer Icon = "ApplicationMediaPlayer"
IconApplicationImageEditor Icon = "ApplicationImageEditor"
IconApplicationAudioEditor Icon = "ApplicationAudioEditor"
IconApplicationVideoEditor Icon = "ApplicationVideoEditor"
IconApplicationClock Icon = "ApplicationClock"
IconApplicationCalendar Icon = "ApplicationCalendar"
IconApplicationChecklist Icon = "ApplicationChecklist"
// categories: applications
IconApplications Icon = "Applications"
IconApplicationsAccessories Icon = "ApplicationsAccessories"
IconApplicationsDevelopment Icon = "ApplicationsDevelopment"
IconApplicationsEngineering Icon = "ApplicationsEngineering"
IconApplicationsGames Icon = "ApplicationsGames"
IconApplicationsGraphics Icon = "ApplicationsGraphics"
IconApplicationsInternet Icon = "ApplicationsInternet"
IconApplicationsMultimedia Icon = "ApplicationsMultimedia"
IconApplicationsOffice Icon = "ApplicationsOffice"
IconApplicationsScience Icon = "ApplicationsScience"
IconApplicationsSystem Icon = "ApplicationsSystem"
IconApplicationsUtilities Icon = "ApplicationsUtilities"
// categories: preferences
IconPreferences Icon = "Preferences"
IconPreferencesDesktop Icon = "PreferencesDesktop"
IconPreferencesPeripherals Icon = "PreferencesPeripherals"
IconPreferencesPersonal Icon = "PreferencesPersonal"
IconPreferencesSystem Icon = "PreferencesSystem"
IconPreferencesNetwork Icon = "PreferencesNetwork"
// devices
IconDevice Icon = "Device"
IconDeviceCamera Icon = "DeviceCamera"
IconDeviceWebCamera Icon = "DeviceWebCamera"
IconDeviceComputer Icon = "DeviceComputer"
IconDevicePda Icon = "DevicePda"
IconDevicePhone Icon = "DevicePhone"
IconDevicePrinter Icon = "DevicePrinter"
IconDeviceScanner Icon = "DeviceScanner"
IconDeviceMultimediaPlayer Icon = "DeviceMultimediaPlayer"
IconDeviceVideoDisplay Icon = "DeviceVideoDisplay"
IconDeviceAudioInput Icon = "DeviceAudioInput"
IconDeviceAudioOutput Icon = "DeviceAudioOutput"
// devices: hardware
IconHardware Icon = "Hardware"
IconHardwareCPU Icon = "HardwareCPU"
IconHardwareGPU Icon = "HardwareGPU"
IconHardwareRAM Icon = "HardwareRAM"
IconHardwareSoundCard Icon = "HardwareSoundCard"
IconHardwareNetworkAdapter Icon = "HardwareNetworkAdapter"
// devices: power
IconPowerBattery Icon = "PowerBattery"
// devices: storage
IconStorageHardDisk Icon = "StorageHardDisk"
IconStorageFloppyDisk Icon = "StorageFloppyDisk"
IconStorageSolidState Icon = "StorageSolidState"
IconStorageOptical Icon = "StorageOptical"
IconStorageFlashStick Icon = "StorageFlashStick"
IconStorageFlashCard Icon = "StorageFlashCard"
IconStorageMagneticTape Icon = "StorageMagneticTape"
// devices: input
IconInputGaming Icon = "InputGaming"
IconInputKeyboard Icon = "InputKeyboard"
IconInputMouse Icon = "InputMouse"
IconInputTablet Icon = "InputTablet"
// devices: network
IconNetworkWired Icon = "NetworkWired"
IconNetworkWireless Icon = "NetworkWireless"
IconNetworkCellular Icon = "NetworkCellular"
IconNetworkLocal Icon = "NetworkLocal"
IconNetworkInternet Icon = "NetworkInternet"
IconNetworkVPN Icon = "NetworkVPN"
IconNetworkServer Icon = "NetworkServer"
IconNetworkWorkgroup Icon = "NetworkWorkgroup"
// emblems
IconEmblemDefault Icon = "EmblemDefault"
IconEmblemEncrypted Icon = "EmblemEncrypted"
IconEmblemFavorite Icon = "EmblemFavorite"
IconEmblemImportant Icon = "EmblemImportant"
IconEmblemReadOnly Icon = "EmblemReadOnly"
IconEmblemShared Icon = "EmblemShared"
IconEmblemSymbolicLink Icon = "EmblemSymbolicLink"
IconEmblemSynchronized Icon = "EmblemSynchronized"
IconEmblemSystem Icon = "EmblemSystem"
IconEmblemUnreadable Icon = "EmblemUnreadable"
// places
IconPlaceDirectory Icon = "PlaceDirectory"
IconPlaceRemote Icon = "PlaceRemote"
IconPlaceHome Icon = "PlaceHome"
IconPlaceDownloads Icon = "PlaceDownloads"
IconPlaceDesktop Icon = "PlaceDesktop"
IconPlacePhotos Icon = "PlacePhotos"
IconPlaceBooks Icon = "PlaceBooks"
IconPlaceBookmarks Icon = "PlaceBookmarks"
IconPlaceTrash Icon = "PlaceTrash"
IconPlaceDocuments Icon = "PlaceDocuments"
IconPlaceRepositories Icon = "PlaceRepositories"
IconPlaceMusic Icon = "PlaceMusic"
IconPlaceArchives Icon = "PlaceArchives"
IconPlaceFonts Icon = "PlaceFonts"
IconPlaceBinaries Icon = "PlaceBinaries"
IconPlaceVideos Icon = "PlaceVideos"
IconPlace3DObjects Icon = "Place3DObjects"
IconPlaceHistory Icon = "PlaceHistory"
IconPlacePreferences Icon = "PlacePreferences"
// status: appointments
IconAppointmentMissed Icon = "AppointmentMissed"
IconAppointmentSoon Icon = "AppointmentSoon"
// status: dialogs
IconDialogError Icon = "DialogError"
IconDialogInformation Icon = "DialogInformation"
IconDialogPassword Icon = "DialogPassword"
IconDialogQuestion Icon = "DialogQuestion"
IconDialogWarning Icon = "DialogWarning"
// status: directories
IconDirectoryDragAccept Icon = "DirectoryDragAccept"
IconDirectoryFull Icon = "DirectoryFull"
IconDirectoryOpen Icon = "DirectoryOpen"
IconDirectoryVisiting Icon = "DirectoryVisiting"
// status: trash
IconTrashFull Icon = "TrashFull"
// status: resource
IconResourceLoading Icon = "ResourceLoading"
IconResourceMissing Icon = "ResourceMissing"
// status: mail
IconMailAttachment Icon = "MailAttachment"
IconMailUnread Icon = "MailUnread"
IconMailReplied Icon = "MailReplied"
IconMailSigned Icon = "MailSigned"
IconMailSignedVerified Icon = "MailSignedVerified"
// status: network
IconCellularSignal0 Icon = "CellularSignal0"
IconCellularSignal1 Icon = "CellularSignal1"
IconCellularSignal2 Icon = "CellularSignal2"
IconCellularSignal3 Icon = "CellularSignal3"
IconWirelessSignal0 Icon = "WirelessSignal0"
IconWirelessSignal1 Icon = "WirelessSignal1"
IconWirelessSignal2 Icon = "WirelessSignal2"
IconWirelessSignal3 Icon = "WirelessSignal3"
IconNetworkError Icon = "NetworkError"
IconNetworkIdle Icon = "NetworkIdle"
IconNetworkOffline Icon = "NetworkOffline"
IconNetworkReceive Icon = "NetworkReceive"
IconNetworkTransmit Icon = "NetworkTransmit"
IconNetworkTransmitReceive Icon = "NetworkTransmitReceive"
// status: print
IconPrintError Icon = "PrintError"
IconPrintPrinting Icon = "PrintPrinting"
// status: security
IconSecurityHigh Icon = "SecurityHigh"
IconSecurityMedium Icon = "SecurityMedium"
IconSecurityLow Icon = "SecurityLow"
// status: software
IconSoftwareUpdateAvailable Icon = "SoftwareUpdateAvailable"
IconSoftwareUpdateUrgent Icon = "SoftwareUpdateUrgent"
IconSoftwareInstalling Icon = "SoftwareInstalling"
// status: sync
IconSyncError Icon = "SyncError"
IconSyncSynchronizing Icon = "SyncSynchronizing"
// status: tasks
IconTaskDue Icon = "TaskDue"
IconTaskPastDue Icon = "TaskPastDue"
// status: users
IconUserAvailable Icon = "UserAvailable"
IconUserAway Icon = "UserAway"
IconUserIdle Icon = "UserIdle"
IconUserOffline Icon = "UserOffline"
// status: power
IconBattery0 Icon = "Battery0"
IconBattery1 Icon = "Battery1"
IconBattery2 Icon = "Battery2"
IconBattery3 Icon = "Battery3"
IconBrightness0 Icon = "Brightness0"
IconBrightness1 Icon = "Brightness1"
IconBrightness2 Icon = "Brightness2"
IconBrightness3 Icon = "Brightness3"
// status: media
IconVolume0 Icon = "Volume0"
IconVolume1 Icon = "Volume1"
IconVolume2 Icon = "Volume2"
IconVolume3 Icon = "Volume3"
IconPlaylistRepeat Icon = "PlaylistRepeat"
IconPlaylistShuffle Icon = "PlaylistShuffle"
// status: weather
IconWeatherClear Icon = "WeatherClear"
IconWeatherClearNight Icon = "WeatherClearNight"
IconWeatherFewClouds Icon = "WeatherFewClouds"
IconWeatherFewCloudsNight Icon = "WeatherFewCloudsNight"
IconWeatherFog Icon = "WeatherFog"
IconWeatherOvercast Icon = "WeatherOvercast"
IconWeatherSevereAlert Icon = "WeatherSevereAlert"
IconWeatherShowers Icon = "WeatherShowers"
IconWeatherShowersScattered Icon = "WeatherShowersScattered"
IconWeatherSnow Icon = "WeatherSnow"
IconWeatherStorm Icon = "WeatherStorm"
)
// 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 {
assertBackend()
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)
}

View File

@@ -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)
}

462
object.go
View File

@@ -1,114 +1,20 @@
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"
import "git.tebibyte.media/tomo/tomo/input"
import "git.tebibyte.media/tomo/tomo/canvas"
// Side represents one side of a rectangle.
type Side int; const (
SideTop Side = iota
SideRight
SideBottom
SideLeft
)
// Inset represents a rectangle inset that can have a different value for each
// side.
type Inset [4]int
// I allows you to create an inset in a CSS-ish way:
//
// - One argument: all sides are set to this value
// - Two arguments: the top and bottom sides are set to the first value, and
// the left and right sides are set to the second value.
// - Three arguments: the top side is set by the first value, the left and
// right sides are set by the second vaue, and the bottom side is set by the
// third value.
// - Four arguments: each value corresponds to a side.
//
// This function will panic if an argument count that isn't one of these is
// given.
func I (sides ...int) Inset {
switch len(sides) {
case 1: return Inset { sides[0], sides[0], sides[0], sides[0] }
case 2: return Inset { sides[0], sides[1], sides[0], sides[1] }
case 3: return Inset { sides[0], sides[1], sides[2], sides[1] }
case 4: return Inset { sides[0], sides[1], sides[2], sides[3] }
default: panic("I: illegal argument count.")
}
}
// 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
// further, an empty rectangle near its center will be returned.
func (inset Inset) Apply (bigger image.Rectangle) (smaller image.Rectangle) {
smaller = bigger
if smaller.Dx() < inset[3] + inset[1] {
smaller.Min.X = (smaller.Min.X + smaller.Max.X) / 2
smaller.Max.X = smaller.Min.X
} else {
smaller.Min.X += inset[3]
smaller.Max.X -= inset[1]
}
if smaller.Dy() < inset[0] + inset[2] {
smaller.Min.Y = (smaller.Min.Y + smaller.Max.Y) / 2
smaller.Max.Y = smaller.Min.Y
} else {
smaller.Min.Y += inset[0]
smaller.Max.Y -= inset[2]
}
return
}
// Inverse returns a negated version of the inset.
func (inset Inset) Inverse () (prime Inset) {
return Inset {
inset[0] * -1,
inset[1] * -1,
inset[2] * -1,
inset[3] * -1,
}
}
// Horizontal returns the sum of SideRight and SideLeft.
func (inset Inset) Horizontal () int {
return inset[SideRight] + inset[SideLeft]
}
// Vertical returns the sum of SideTop and SideBottom.
func (inset Inset) Vertical () int {
return inset[SideTop] + inset[SideBottom]
}
// Border represents a single border of a box.
type Border struct {
Width Inset
Color [4]color.Color
}
// Align lists basic alignment types.
type Align int; const (
AlignStart Align = iota // similar to left-aligned text
AlignMiddle // similar to center-aligned text
AlignEnd // similar to right-aligned text
AlignEven // similar to justified text
)
// Object is any onscreen object that is linked to a box (or is that box).
// Unlike the Box interface and associated interfaces, Object interfaces may
// originate from anywhere.
// Unlike the Box interface and associated interfaces, Object implementations
// may originate from anywhere.
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
@@ -123,59 +29,49 @@ type ContentObject interface {
OnContentBoundsChange (func ()) event.Cookie
}
// Box is a basic styled box. Box, as well as any interface that embeds 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
// 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
// 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)
// SetTexture sets a repeating background texture. If the texture is
// transparent, it will be overlayed atop the color specified by
// SetColor().
SetTexture (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)
// SetVisible sets whether or not this box is visible. If invisible,
// it will not be drawn and no space will be created for it in the
// layout. Boxes are visible by default.
SetVisible (bool)
// Visible returns whether or not this box is visible.
Visible () bool
// Role returns this Box's role as set by SetRole.
Role () Role
// 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 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 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
@@ -185,87 +81,97 @@ 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
// returned 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. 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
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.
// 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 Box's area to be redrawn at the end of the
// event cycle, even if it wouldn't be otherwise.
// 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 otherwise
// be.
Invalidate ()
// OnSizeChange specifies a function to be called when the size of the
// 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 ())
}
// 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
// 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
// Box's ContentBounds or InnerBounds changes.
OnContentBoundsChange (func ()) event.Cookie
@@ -277,18 +183,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.
@@ -303,10 +201,8 @@ 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 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)
@@ -316,25 +212,24 @@ 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.
// 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.
@@ -356,32 +251,109 @@ 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
// Arrange arranges child boxes according to the given LayoutHints.
Arrange (LayoutHints, []Box)
MinimumSize (LayoutHints, BoxQuerier) image.Point
// 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.
// The result of this behavior may or may not be respected, depending on
// the situation.
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, BoxQuerier, int) int
}
// Window is an operating system window. It can contain one object.
// 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 behavior 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)
}
// 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. When multiple icon sizes are
// provided, the best fitting one is chosen for display.
SetIcon (... image.Image)
// 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)
// 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)
// 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.
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 (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.
NewChild (WindowKind, 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
@@ -392,19 +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
}
// MainWindow is a top-level operating system window.
type MainWindow interface {
Window
// 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)
}

View File

@@ -1,278 +0,0 @@
package theme
import "git.tebibyte.media/tomo/tomo/data"
import "git.tebibyte.media/tomo/tomo/canvas"
// IconSize represents the size of an icon.
type IconSize int; const (
IconSizeSmall IconSize = iota;
IconSizeMedium
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
// A list of standard icon IDs.
const (
IconUnknown Icon = ""
// objects: files
IconFile Icon = "File" // generic
IconDirectory Icon = "Directory"
IconDirectoryFull Icon = "DirectoryFull"
// objects: places
IconPlaceHome Icon = "PlaceHome"
IconPlaceDownloads Icon = "PlaceDownloads"
IconPlacePhotos Icon = "PlacePhotos"
IconPlaceBooks Icon = "PlaceBooks"
IconPlaceDocuments Icon = "PlaceDocuments"
IconPlaceRepositories Icon = "PlaceRepositories"
IconPlaceMusic Icon = "PlaceMusic"
IconPlaceArchives Icon = "PlaceArchives"
IconPlaceFonts Icon = "PlaceFonts"
IconPlaceBinaries Icon = "PlaceBinaries"
IconPlaceVideos Icon = "PlaceVideos"
IconPlace3DObjects Icon = "Place3DObjects"
IconPlaceHistory Icon = "PlaceHistory"
IconPlacePreferences Icon = "PlacePreferences"
// objects: storage
IconStorage Icon = "Storage" // generic
IconStorageMagneticTape Icon = "StorageMagneticTape"
IconStorageFloppyDisk Icon = "StorageFloppyDisk"
IconStorageHardDisk Icon = "StorageHardDisk"
IconStorageSolidStateDisk Icon = "StorageSolidState"
IconStorageFlashStick Icon = "StorageFlashStick"
IconStorageFlashCard Icon = "StorageFlashCard"
IconStorageROM Icon = "StorageROM"
IconStorageRAM Icon = "StorageRAM"
IconStorageCD Icon = "StorageCD"
IconStorageDVD Icon = "StorageDVD"
// objects: applications
// Keep these in sync with nasin.ApplicationRole!
IconApplication Icon = "Application" // generic
IconApplicationWebBrowser Icon = "ApplicationWebBrowser"
IconApplicationMesssanger Icon = "ApplicationMesssanger"
IconApplicationPhone Icon = "ApplicationPhone"
IconApplicationMail Icon = "ApplicationMail"
IconApplicationTerminalEmulator Icon = "ApplicationTerminalEmulator"
IconApplicationFileBrowser Icon = "ApplicationFileBrowser"
IconApplicationTextEditor Icon = "ApplicationTextEditor"
IconApplicationDocumentViewer Icon = "ApplicationDocumentViewer"
IconApplicationWordProcessor Icon = "ApplicationWordProcessor"
IconApplicationSpreadsheet Icon = "ApplicationSpreadsheet"
IconApplicationSlideshow Icon = "ApplicationSlideshow"
IconApplicationCalculator Icon = "ApplicationCalculator"
IconApplicationPreferences Icon = "ApplicationPreferences"
IconApplicationProcessManager Icon = "ApplicationProcessManager"
IconApplicationSystemInformation Icon = "ApplicationSystemInformation"
IconApplicationManual Icon = "ApplicationManual"
IconApplicationCamera Icon = "ApplicationCamera"
IconApplicationImageViewer Icon = "ApplicationImageViewer"
IconApplicationMediaPlayer Icon = "ApplicationMediaPlayer"
IconApplicationImageEditor Icon = "ApplicationImageEditor"
IconApplicationAudioEditor Icon = "ApplicationAudioEditor"
IconApplicationVideoEditor Icon = "ApplicationVideoEditor"
IconApplicationClock Icon = "ApplicationClock"
IconApplicationCalendar Icon = "ApplicationCalendar"
IconApplicationChecklist Icon = "ApplicationChecklist"
// objects: networks
IconNetwork Icon = "Network" // generic
IconNetworkLocal Icon = "NetworkLocal"
IconNetworkInternet Icon = "NetworkInternet"
IconNetworkEthernet Icon = "NetworkEthernet"
IconNetworkWireless Icon = "NetworkWireless"
IconNetworkCell Icon = "NetworkCell"
IconNetworkBluetooth Icon = "NetworkBluetooth"
IconNetworkRadio Icon = "NetworkRadio"
// objects: devices
IconDevice Icon = "Device" // generic
IconDeviceRouter Icon = "DeviceRouter"
IconDeviceServer Icon = "DeviceServer"
IconDeviceDesktop Icon = "DeviceDesktop"
IconDeviceLaptop Icon = "DeviceLaptop"
IconDeviceTablet Icon = "DeviceTablet"
IconDevicePhone Icon = "DevicePhone"
IconDeviceWatch Icon = "DeviceWatch"
IconDeviceCamera Icon = "DeviceCamera"
// objects: peripherals
IconPeripheral Icon = "Peripheral" // generic
IconPeripheralKeyboard Icon = "PeripheralKeyboard"
IconPeripheralMouse Icon = "PeripheralMouse"
IconPeripheralMonitor Icon = "PeripheralMonitor"
IconPeripheralWebcam Icon = "PeripheralWebcam"
IconPeripheralMicrophone Icon = "PeripheralMicrophone"
IconPeripheralSpeaker Icon = "PeripheralSpeaker"
IconPeripheralPenTablet Icon = "PeripheralPenTablet"
IconPeripheralTrackpad Icon = "PeripheralTrackpad"
IconPeripheralController Icon = "PeripheralController"
// objects: i/o
IconPort Icon = "Port" // generic
IconPortEthernet Icon = "PortEthernet"
IconPortUSB Icon = "PortUSB"
IconPortParallel Icon = "PortParallel"
IconPortSerial Icon = "PortSerial"
IconPortPS2 Icon = "PortPS2"
IconPortDisplay Icon = "PortDisplay"
IconPortCGA Icon = "PortCGA"
IconPortVGA Icon = "PortVGA"
IconPortHDMI Icon = "PortHDMI"
IconPortDisplayPort Icon = "PortDisplayPort"
IconPortInfrared Icon = "PortInfrared"
// actions: files
IconActionOpen Icon = "ActionOpen"
IconActionOpenIn Icon = "ActionOpenIn"
IconActionSave Icon = "ActionSave"
IconActionSaveAs Icon = "ActionSaveAs"
IconActionPrint Icon = "ActionPrint"
IconActionNew Icon = "ActionNew"
IconActionNewDirectory Icon = "ActionNewDirectory"
IconActionDelete Icon = "ActionDelete"
IconActionRename Icon = "ActionRename"
IconActionGetInformation Icon = "ActionGetInformation"
IconActionChangePermissions Icon = "ActionChangePermissions"
IconActionRevert Icon = "ActionRevert"
// actions: list management
IconActionAdd Icon = "ActionAdd"
IconActionRemove Icon = "ActionRemove"
IconActionAddBookmark Icon = "ActionAddBookmark"
IconActionRemoveBookmark Icon = "ActionRemoveBookmark"
IconActionAddFavorite Icon = "ActionAddFavorite"
IconActionRemoveFavorite Icon = "ActionRemoveFavorite"
// actions: media
IconActionPlay Icon = "ActionPlay"
IconActionPause Icon = "ActionPause"
IconActionStop Icon = "ActionStop"
IconActionFastForward Icon = "ActionFastForward"
IconActionRewind Icon = "ActionRewind"
IconActionToBeginning Icon = "ActionToBeginning"
IconActionToEnd Icon = "ActionToEnd"
IconActionRecord Icon = "ActionRecord"
IconActionVolumeUp Icon = "ActionVolumeUp"
IconActionVolumeDown Icon = "ActionVolumeDown"
IconActionMute Icon = "ActionMute"
// actions: editing
IconActionUndo Icon = "ActionUndo"
IconActionRedo Icon = "ActionRedo"
IconActionCut Icon = "ActionCut"
IconActionCopy Icon = "ActionCopy"
IconActionPaste Icon = "ActionPaste"
IconActionFind Icon = "ActionFind"
IconActionReplace Icon = "ActionReplace"
IconActionSelectAll Icon = "ActionSelectAll"
IconActionSelectNone Icon = "ActionSelectNone"
IconActionIncrement Icon = "ActionIncrement"
IconActionDecrement Icon = "ActionDecrement"
// actions: window management
IconActionClose Icon = "ActionClose"
IconActionQuit Icon = "ActionQuit"
IconActionIconify Icon = "ActionIconify"
IconActionShade Icon = "ActionShade"
IconActionMaximize Icon = "ActionMaximize"
IconActionFullScreen Icon = "ActionFullScreen"
IconActionRestore Icon = "ActionRestore"
// actions: view
IconActionExpand Icon = "ActionExpand"
IconActionContract Icon = "ActionContract"
IconActionBack Icon = "ActionBack"
IconActionForward Icon = "ActionForward"
IconActionUp Icon = "ActionUp"
IconActionDown Icon = "ActionDown"
IconActionReload Icon = "ActionReload"
IconActionZoomIn Icon = "ActionZoomIn"
IconActionZoomOut Icon = "ActionZoomOut"
IconActionZoomReset Icon = "ActionZoomReset"
IconActionMove Icon = "ActionMove"
IconActionResize Icon = "ActionResize"
IconActionGoTo Icon = "ActionGoTo"
// actions: tools
IconActionTransform Icon = "ActionTransform"
IconActionTranslate Icon = "ActionTranslate"
IconActionRotate Icon = "ActionRotate"
IconActionScale Icon = "ActionScale"
IconActionWarp Icon = "ActionWarp"
IconActionCornerPin Icon = "ActionCornerPin"
IconActionSelectRectangle Icon = "ActionSelectRectangle"
IconActionSelectEllipse Icon = "ActionSelectEllipse"
IconActionSelectLasso Icon = "ActionSelectLasso"
IconActionSelectGeometric Icon = "ActionSelectGeometric"
IconActionSelectAuto Icon = "ActionSelectAuto"
IconActionCrop Icon = "ActionCrop"
IconActionFill Icon = "ActionFill"
IconActionGradient Icon = "ActionGradient"
IconActionPencil Icon = "ActionPencil"
IconActionBrush Icon = "ActionBrush"
IconActionEraser Icon = "ActionEraser"
IconActionText Icon = "ActionText"
IconActionEyedropper Icon = "ActionEyedropper"
// status: dialog
IconStatusInformation Icon = "StatusInformation"
IconStatusQuestion Icon = "StatusQuestion"
IconStatusWarning Icon = "StatusWarning"
IconStatusError Icon = "StatusError"
IconStatusCancel Icon = "StatusCancel"
IconStatusOkay Icon = "StatusOkay"
// status: network
IconStatusCellSignal0 Icon = "StatusCellSignal0"
IconStatusCellSignal1 Icon = "StatusCellSignal1"
IconStatusCellSignal2 Icon = "StatusCellSignal2"
IconStatusCellSignal3 Icon = "StatusCellSignal3"
IconStatusWirelessSignal0 Icon = "StatusWirelessSignal0"
IconStatusWirelessSignal1 Icon = "StatusWirelessSignal1"
IconStatusWirelessSignal2 Icon = "StatusWirelessSignal2"
IconStatusWirelessSignal3 Icon = "StatusWirelessSignal3"
// status: power
IconStatusBattery0 Icon = "StatusBattery0"
IconStatusBattery1 Icon = "StatusBattery1"
IconStatusBattery2 Icon = "StatusBattery2"
IconStatusBattery3 Icon = "StatusBattery3"
IconStatusBrightness0 Icon = "StatusBrightness0"
IconStatusBrightness1 Icon = "StatusBrightness1"
IconStatusBrightness2 Icon = "StatusBrightness2"
IconStatusBrightness3 Icon = "StatusBrightness3"
// status: media
IconStatusVolume0 Icon = "StatusVolume0"
IconStatusVolume1 Icon = "StatusVolume1"
IconStatusVolume2 Icon = "StatusVolume2"
IconStatusVolume3 Icon = "StatusVolume3"
)
// Texture returns a texture of the corresponding icon ID.
func (id Icon) Texture (size IconSize) canvas.Texture {
if current == nil { return nil }
return current.Icon(id, size)
}
// MimeIcon returns an icon corresponding to a MIME type.
func MimeIcon (mime data.Mime, size IconSize) canvas.Texture {
if current == nil { return nil }
return current.MimeIcon(mime, size)
}

View File

@@ -1,109 +0,0 @@
package theme
import "fmt"
import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/tomo/data"
import "git.tebibyte.media/tomo/tomo/event"
import "git.tebibyte.media/tomo/tomo/canvas"
// 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 current == nil { return }
return current.RGBA(id)
}
// Theme can apply a visual style to different objects.
type Theme 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 Theme 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.
// Apply applies the theme to the given object, according to the given
// role. This may register event listeners with the given object;
// closing the returned cookie must remove them.
Apply (tomo.Object, Role) event.Cookie
// RGBA returns the RGBA values of the corresponding color ID.
RGBA (Color) (r, g, b, a uint32)
// Icon returns a texture of the corresponding icon ID.
Icon (Icon, IconSize) canvas.Texture
// MimeIcon returns an icon corresponding to a MIME type.
MimeIcon (data.Mime, IconSize) canvas.Texture
}
var current Theme
// SetTheme sets the theme.
func SetTheme (theme Theme) {
current = theme
}
// Apply applies the current theme to the given object, according to the given
// role. This may register event listeners with the given object; closing the
// returned cookie will remove them.
func Apply (object tomo.Object, role Role) event.Cookie {
if current == nil { return event.NoCookie { } }
return current.Apply(object, role)
}

79
tomo.go
View File

@@ -2,65 +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()
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) (MainWindow, 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) (MainWindow, error) {
assertBackend()
return backend.NewPlainWindow(bounds)
return backend.NewWindow(kind, bounds)
}
// NewBox creates and returns a basic Box.
@@ -87,9 +71,16 @@ 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)
}
// NewCanvas creates a new canvas with the specified bounds. When no longer in
// use, it must be freed using Close().
func NewCanvas (bounds image.Rectangle) canvas.CanvasCloser {
assertBackend()
return backend.NewCanvas(bounds)
}

225
unit.go Normal file
View File

@@ -0,0 +1,225 @@
package tomo
import "fmt"
import "image"
import "image/color"
// Side represents one side of a rectangle.
type Side int; const (
SideTop Side = iota
SideRight
SideBottom
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
// I allows you to create an inset in a CSS-ish way:
//
// - One argument: all sides are set to this value
// - Two arguments: the top and bottom sides are set to the first value, and
// the left and right sides are set to the second value.
// - Three arguments: the top side is set by the first value, the left and
// right sides are set by the second vaue, and the bottom side is set by the
// third value.
// - Four arguments: each value corresponds to a side.
//
// This function will panic if an argument count that isn't one of these is
// given.
func I (sides ...int) Inset {
switch len(sides) {
case 1: return Inset { sides[0], sides[0], sides[0], sides[0] }
case 2: return Inset { sides[0], sides[1], sides[0], sides[1] }
case 3: return Inset { sides[0], sides[1], sides[2], sides[1] }
case 4: return Inset { sides[0], sides[1], sides[2], sides[3] }
default: panic("I: illegal argument count.")
}
}
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
if smaller.Dx() < inset[3] + inset[1] {
smaller.Min.X = (smaller.Min.X + smaller.Max.X) / 2
smaller.Max.X = smaller.Min.X
} else {
smaller.Min.X += inset[3]
smaller.Max.X -= inset[1]
}
if smaller.Dy() < inset[0] + inset[2] {
smaller.Min.Y = (smaller.Min.Y + smaller.Max.Y) / 2
smaller.Max.Y = smaller.Min.Y
} else {
smaller.Min.Y += inset[0]
smaller.Max.Y -= inset[2]
}
return
}
// Inverse returns a negated version of the Inset.
func (inset Inset) Inverse () (prime Inset) {
return Inset {
inset[0] * -1,
inset[1] * -1,
inset[2] * -1,
inset[3] * -1,
}
}
// Horizontal returns the sum of SideRight and SideLeft.
func (inset Inset) Horizontal () int {
return inset[SideRight] + inset[SideLeft]
}
// Vertical returns the sum of SideTop and SideBottom.
func (inset Inset) Vertical () int {
return inset[SideTop] + inset[SideBottom]
}
// 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
AlignMiddle // similar to center-aligned text
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 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)
}