Compare commits

...

51 Commits

Author SHA1 Message Date
Sasha Koshka a9fc52c55b Change clip behaviors to sub behaviors
Remedies #4
2024-05-15 01:37:21 -04:00
Sasha Koshka e745e80f09 Add more information to README.md 2024-05-14 12:43:40 -04:00
Sasha Koshka 748996c789 Be more descriptive with what a path is 2024-05-14 12:34:42 -04:00
Sasha Koshka d4aac7e26c Set CapRound as the default cap style 2024-05-14 12:32:44 -04:00
Sasha Koshka 3d645b8064 Tweak doc comments for canvas.Pen 2024-05-14 12:32:09 -04:00
Sasha Koshka bde1a2bc34 Remove wierd spacing from box interface definitions
Seemed like a good idea at the time
2024-05-14 12:20:44 -04:00
Sasha Koshka d43ba6b1af Replace Window's Show/Hide methods with Visible/SetVisible 2024-05-14 12:18:21 -04:00
Sasha Koshka 096c1b5e1b Add Visible() and SetVisible() to Box 2024-05-14 12:17:14 -04:00
Sasha Koshka c44ff4a34a Change ContainerBox.Delete() to Remove()
Remedy #3
2024-05-14 12:11:00 -04:00
Sasha Koshka dc02f4dfb4 Remedy #1 2024-05-14 12:09:34 -04:00
Sasha Koshka 7c30b2bac0 Add requirement for ContainerBox.Add to remove a previously
parented object first
2024-05-14 12:03:10 -04:00
Sasha Koshka 24264bbc91 Fix capitalization errors with previous commit 2024-05-14 11:58:48 -04:00
Sasha Koshka f167af3281 Describe the canvas used by CanvasBox in detail 2024-05-14 11:57:48 -04:00
Sasha Koshka 9a5b4ee7e8 Add home icon 2024-05-05 02:53:07 -04:00
Sasha Koshka 3e561d7bf0 Fix wording of theme.Theme doc comment 2024-05-03 13:46:50 -04:00
Sasha Koshka b96e8f744f Namespace icon IDs 2024-05-03 13:21:35 -04:00
Sasha Koshka 9aa6f2900e Added application role icons 2024-05-03 13:10:44 -04:00
Sasha Koshka a3fc0ce66d Remove ApplicationIcon from Theme
Since Icon can now take in arbitrary strings, we can just have it
serve application icons as well.
2024-05-03 13:08:14 -04:00
Sasha Koshka 65bf341514 Icon IDs are now strings 2024-05-03 13:07:27 -04:00
Sasha Koshka 042f2f0131 Completely remove plugin system
Plugins may be moved to Nasin
2024-04-30 13:12:34 -04:00
Sasha Koshka 4fd5e54e42 Add editorconfig 2024-04-29 16:23:46 -04:00
Sasha Koshka 9a9e546b37 Remove application framework stuff 2024-04-29 00:39:17 -04:00
Sasha Koshka a2a5c16e38 Changed the semantics of ContentBounds 2023-09-14 14:47:33 -04:00
Sasha Koshka 28cd889254 Removed tiler for now, needs to be rethought a bit 2023-09-08 20:57:15 -04:00
Sasha Koshka e682fdd9d8 Add icon for switch 2023-09-08 20:57:00 -04:00
Sasha Koshka 9719391e5d NewApplicationWindow returns MainWindow nows 2023-09-08 16:29:03 -04:00
Sasha Koshka 8a531986eb What??? 2023-09-07 18:25:35 -04:00
Sasha Koshka c3c6ff61f5 Add Tiler interface 2023-09-05 18:14:36 -04:00
Sasha Koshka 89f7bf47ce Add ability to create an undecorated window 2023-09-05 13:21:59 -04:00
Sasha Koshka bebd58dac1 Added event capturing to containers 2023-09-05 13:10:35 -04:00
Sasha Koshka f9a85fd949 Added filesystems for application data 2023-09-04 13:48:03 -04:00
Sasha Koshka 6ac653adb6 Made ownership of textures more explicit 2023-09-04 12:21:17 -04:00
Sasha Koshka 7b28419432 Texture must now implement Image 2023-09-04 12:07:29 -04:00
Sasha Koshka 57e6a9ff21 lolll whoops 2023-09-04 02:56:00 -04:00
Sasha Koshka a06f94e41b Added support for defining applications as objects 2023-09-04 02:26:21 -04:00
Sasha Koshka 4d157756eb Added a String method to a bunch of stuff 2023-09-04 01:47:03 -04:00
Sasha Koshka 63a67e40d1 Added a Bounds() method to Texture 2023-09-04 01:28:04 -04:00
Sasha Koshka b629b4eb4e Cleared up wording around theme textures 2023-08-29 15:52:07 -04:00
Sasha Koshka 7510047ef3 Fix package name errors in theme 2023-08-24 16:30:10 -04:00
Sasha Koshka fdea479ee7 Textures have been moved to the canvas module 2023-08-24 01:01:40 -04:00
Sasha Koshka dc31de0ecb Textures are now actually used 2023-08-23 18:04:54 -04:00
Sasha Koshka a8d5a64837 Added SetTexture on Object 2023-08-21 21:52:51 -04:00
Sasha Koshka 50697e4369 All icons now return textures 2023-08-20 18:41:46 -04:00
Sasha Koshka 3614979e35 Add a function to protect textures 2023-08-20 18:33:20 -04:00
Sasha Koshka 5f5b928528 Icons now use textures 2023-08-20 18:28:30 -04:00
Sasha Koshka b62b846745 AhhhahsdjashdMerge branch 'main' of git.tebibyte.media:tomo/tomo 2023-08-20 17:58:45 -04:00
Sasha Koshka 35c6e167be Added a texture interface 2023-08-20 17:54:06 -04:00
Sasha Koshka 6ced7d1372 Fixed syntax errors 2023-08-12 00:56:13 -04:00
Sasha Koshka e259f122c7 Added an icon API 2023-08-12 00:51:16 -04:00
Sasha Koshka 8e25397a05 Theme roles now have a variant field 2023-08-10 17:49:22 -04:00
Sasha Koshka 2884604fdd Renamed Object.Box to GetBox to resolve naming conflicts 2023-08-10 17:48:40 -04:00
11 changed files with 511 additions and 167 deletions

12
.editorconfig Normal file
View File

@ -0,0 +1,12 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = tab
indent_size = 8
charset = utf-8
[*.md]
indent_style = space
indent_size = 2

View File

@ -1,6 +1,8 @@
# tomo # tomo
WIP rewrite of tomo. [![Go Reference](https://pkg.go.dev/badge/git.tebibyte.media/tomo/tomo.svg)](https://pkg.go.dev/git.tebibyte.media/tomo/tomo)
This module will serve as a wafer-thin collection of interfaces and glue code so Tomo is a lightweight GUI toolkit written in pure Go. This repository defines
that plugins will be an actual viable concept. the API that other components of the toolkit agree on. 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.

View File

@ -3,6 +3,7 @@ package tomo
import "sort" import "sort"
import "image" import "image"
import "errors" import "errors"
import "git.tebibyte.media/tomo/tomo/canvas"
// Backend is any Tomo implementation. Backends handle window creation, layout, // Backend is any Tomo implementation. Backends handle window creation, layout,
// rendering, and events so that there can be as many platform-specific // rendering, and events so that there can be as many platform-specific
@ -10,12 +11,23 @@ import "errors"
type Backend interface { 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. // that was not made by it.
NewWindow (image.Rectangle) (MainWindow, error)
NewBox () Box NewBox () Box
NewTextBox () TextBox NewTextBox () TextBox
NewCanvasBox () CanvasBox NewCanvasBox () CanvasBox
NewContainerBox () ContainerBox NewContainerBox () ContainerBox
// NewWindow creates a normal MainWindow and returns it.
NewWindow (image.Rectangle) (MainWindow, 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 (image.Image) canvas.TextureCloser
// Run runs the event loop until Stop() is called, or the backend // Run runs the event loop until Stop() is called, or the backend
// experiences a fatal error. // experiences a fatal error.
Run () error Run () error

View File

@ -8,9 +8,9 @@ import "image/color"
// Cap represents a stroke cap type. // Cap represents a stroke cap type.
type Cap int; const ( type Cap int; const (
CapButt Cap = iota // Square cap that ends at the point CapRound Cap = iota // Round cap that surrounds the point
CapRound // Round cap that surrounds the point CapSquare // Square cap that surrounds the point
CapSquare // square cap that surrounds the point CapButt // Square cap that ends at the point
) )
// Joint represents a stroke joint type. // Joint represents a stroke joint type.
@ -30,22 +30,38 @@ type StrokeAlign int; const (
// Pen represents a drawing context that is linked to a canvas. Each canvas can // Pen represents a drawing context that is linked to a canvas. Each canvas can
// have multiple pens associated with it, each maintaining their own drawing // have multiple pens associated with it, each maintaining their own drawing
// context. // state.
type Pen interface { type Pen interface {
// Rectangle draws a rectangle // Rectangle draws a rectangle.
Rectangle (image.Rectangle) Rectangle (image.Rectangle)
// Path draws a path // Path draws a path, which is a series of connected points.
Path (points ...image.Point) Path (points ...image.Point)
Closed (bool) // if the path is closed // StrokeWeight sets how thick the stroke is. The default value is zero.
Cap (Cap) // line cap stype StrokeWeight (int)
Joint (Joint) // line joint style // SetClosed sets whether the path is a closed shape, or has an open
StrokeWeight (int) // how thick the stroke is // side. This only applies if the stroke weight is greater than zero.
StrokeAlign (StrokeAlign) // where the stroke is drawn Closed (bool)
// Cap sets how the ends of the stroke look. This only applies if the
// stroke weight is greater than zero, and if the path is not closed.
// The default value is CapRound.
Cap (Cap)
// Joint sets how bend points in the stroke look. This only applies if
// the stroke weight is greater than zero. The default value is
// JointRound.
Joint (Joint)
// StrokeAlign sets where the stroke is drawn in relation to the path.
// This only applies if the stroke weight is greater than zero. The
// default value is StrokeAlignCenter.
StrokeAlign (StrokeAlign)
Stroke (color.Color) // Sets the stroke to a solid color // Stroke sets the stroke to a solid color.
Fill (color.Color) // Sets the fill to a solid color Stroke (color.Color)
// Fill sets the fill to a solid color.
Fill (color.Color)
// Texture overlaps a texture onto the fill color.
Texture (Texture)
} }
// Canvas is an image that supports drawing paths. // Canvas is an image that supports drawing paths.
@ -55,8 +71,10 @@ type Canvas interface {
// Pen returns a new pen for this canvas. // Pen returns a new pen for this canvas.
Pen () Pen Pen () Pen
// Clip returns a new canvas that points to a specific area of this one. // SubCanvas returns a returns a Canvas representing the portion of this
Clip (image.Rectangle) Canvas // Canvas visible and drawable through a rectangle. The returned value
// shares pixels with the original Canvas.
SubCanvas (image.Rectangle) Canvas
} }
// Drawer is an object that can draw to a canvas. // Drawer is an object that can draw to a canvas.

21
canvas/texture.go Normal file
View File

@ -0,0 +1,21 @@
package canvas
import "io"
import "image"
// Texture is a handle that points to a 2D raster image managed by the backend.
type Texture interface {
image.Image
// SubTexture returns a returns a Texture representing the portion of
// this Texture visible through a rectangle. The returned value shares
// pixels with the original Texture.
SubTexture (image.Rectangle) Texture
}
// TextureCloser is a texture that can be closed. Anything that receives a
// TextureCloser must close it after use.
type TextureCloser interface {
Texture
io.Closer
}

143
object.go
View File

@ -101,10 +101,10 @@ type Align int; const (
AlignEven // similar to justified text AlignEven // similar to justified text
) )
// Object is any obscreen object. Each object must be linked to a box, even if // Object is any onscreen object. Each object must be linked to a box, even if
// it is that box. // it is that box.
type Object interface { type Object interface {
Box () Box GetBox () Box
} }
// Box is a basic styled box. // Box is a basic styled box.
@ -112,13 +112,13 @@ type Box interface {
Object Object
// Window returns the Window this Box is a part of. // Window returns the Window this Box is a part of.
Window () Window Window () Window
// Bounds returns the outer bounding rectangle of the Box relative to // Bounds returns the outer bounding rectangle of the Box relative to
// the Window. // the Window.
Bounds () image.Rectangle Bounds () image.Rectangle
// InnerBounds returns the inner bounding rectangle of the box. It is // InnerBounds returns the inner bounding rectangle of the box. It is
// the value of Bounds inset by the Box's border and padding. // the value of Bounds inset by the Box's border and padding.
InnerBounds () image.Rectangle InnerBounds () image.Rectangle
// MinimumSize returns the minimum width and height this Box's bounds // 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 // can be set to. This will return the value of whichever of these is
// greater: // greater:
@ -126,26 +126,36 @@ type Box interface {
// - The size taken up by the Box's border and padding. If there is // - 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 // internal content that does not overflow, the size of that is also
// taken into account here. // taken into account here.
MinimumSize () image.Point MinimumSize () image.Point
// SetBounds sets the bounding rectangle of this Box relative to the // SetBounds sets the bounding rectangle of this Box relative to the
// Window. // Window.
SetBounds (image.Rectangle) SetBounds (image.Rectangle)
// SetColor sets the background color of this Box. // SetColor sets the background color of this Box.
SetColor (color.Color) 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 // 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. // most outset, and the last Border will be the most inset.
SetBorder (...Border) SetBorder (...Border)
// SetMinimumSize sets the minimum width and height of the box, as // SetMinimumSize sets the minimum width and height of the box, as
// described in MinimumSize. // described in MinimumSize.
SetMinimumSize (image.Point) SetMinimumSize (image.Point)
// SetPadding sets the padding between the Box's innermost Border and // SetPadding sets the padding between the Box's innermost Border and
// its content. // its content.
SetPadding (Inset) 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
// SetDNDData sets the data that will be picked up if this Box is // 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 // dragged. If this is nil (which is the default), this Box will not be
// picked up. // picked up.
SetDNDData (data.Data) SetDNDData (data.Data)
// SetDNDAccept sets the type of data that can be dropped onto this Box. // 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 // If this is nil (which is the default), this Box will reject all
// drops. // drops.
@ -153,16 +163,16 @@ type Box interface {
// SetFocused sets whether or not this Box has keyboard focus. If set to // SetFocused sets whether or not this Box has keyboard focus. If set to
// true, this method will steal focus away from whichever Object // true, this method will steal focus away from whichever Object
// currently has focus. // currently has focus.
SetFocused (bool) SetFocused (bool)
// SetFocusable sets whether or not this Box can receive keyboard focus. // SetFocusable sets whether or not this Box can receive keyboard focus.
// If set to false and the Box is already focused. the focus is removed. // If set to false and the Box is already focused. the focus is removed.
SetFocusable (bool) SetFocusable (bool)
// Focused returns whether or not this Box has keyboard focus. // Focused returns whether or not this Box has keyboard focus.
Focused () bool Focused () bool
// Modifiers returns which modifier keys on the keyboard are currently // Modifiers returns which modifier keys on the keyboard are currently
// being held down. // being held down.
Modifiers () input.Modifiers Modifiers () input.Modifiers
// MousePosition returns the position of the mouse pointer relative to // MousePosition returns the position of the mouse pointer relative to
// the Window. // the Window.
MousePosition () image.Point MousePosition () image.Point
@ -191,8 +201,13 @@ type CanvasBox interface {
Box Box
// SetDrawer sets the Drawer that will be called upon to draw the Box's // SetDrawer sets the Drawer that will be called upon to draw the Box's
// content when it is invalidated. // content when it is invalidated. The Canvas passed to the drawer will
SetDrawer (canvas.Drawer) // 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 (canvas.Drawer)
// Invalidate causes the Box's area to be redrawn at the end of the // Invalidate causes the Box's area to be redrawn at the end of the
// event cycle, even if it wouldn't be otherwise. // event cycle, even if it wouldn't be otherwise.
@ -207,16 +222,16 @@ type ContentBox interface {
// SetOverflow sets whether or not the Box's content overflows // SetOverflow sets whether or not the Box's content overflows
// horizontally and vertically. Overflowing content is clipped to the // horizontally and vertically. Overflowing content is clipped to the
// bounds of the Box inset by all Borders (but not padding). // bounds of the Box inset by all Borders (but not padding).
SetOverflow (horizontal, vertical bool) SetOverflow (horizontal, vertical bool)
// SetAlign sets how the Box's content is distributed horizontally and // SetAlign sets how the Box's content is distributed horizontally and
// vertically. // vertically.
SetAlign (x, y Align) SetAlign (x, y Align)
// ContentBounds returns the bounds of the inner content of the Box // ContentBounds returns the bounds of the inner content of the Box
// relative to the window. // relative to the Box's InnerBounds.
ContentBounds () image.Rectangle ContentBounds () image.Rectangle
// ScrollTo shifts the origin of the Box's content to the origin of the // 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 point.
ScrollTo (image.Point) ScrollTo (image.Point)
// OnContentBoundsChange specifies a function to be called when the // OnContentBoundsChange specifies a function to be called when the
// Box's ContentBounds or InnerBounds changes. // Box's ContentBounds or InnerBounds changes.
OnContentBoundsChange (func ()) event.Cookie OnContentBoundsChange (func ()) event.Cookie
@ -227,26 +242,26 @@ type TextBox interface {
ContentBox ContentBox
// SetText sets the text content of the Box. // SetText sets the text content of the Box.
SetText (string) SetText (string)
// SetTextColor sets the text color. // SetTextColor sets the text color.
SetTextColor (color.Color) SetTextColor (color.Color)
// SetFace sets the font face text is rendered in. // SetFace sets the font face text is rendered in.
SetFace (font.Face) SetFace (font.Face)
// SetWrap sets whether or not the text wraps. // SetWrap sets whether or not the text wraps.
SetWrap (bool) SetWrap (bool)
// SetSelectable sets whether or not the text content can be // SetSelectable sets whether or not the text content can be
// highlighted/selected. // highlighted/selected.
SetSelectable (bool) SetSelectable (bool)
// SetDotColor sets the highlight color of selected text. // SetDotColor sets the highlight color of selected text.
SetDotColor (color.Color) SetDotColor (color.Color)
// Select sets the text cursor or selection. // Select sets the text cursor or selection.
Select (text.Dot) Select (text.Dot)
// Dot returns the text cursor or selection. // Dot returns the text cursor or selection.
Dot () text.Dot Dot () text.Dot
// OnDotChange specifies a function to be called when the text cursor or // OnDotChange specifies a function to be called when the text cursor or
// selection changes. // selection changes.
OnDotChange (func ()) event.Cookie OnDotChange (func ()) event.Cookie
} }
// ContentBox is a box that can contain child Objects. It arranges them // ContentBox is a box that can contain child Objects. It arranges them
@ -254,30 +269,35 @@ type TextBox interface {
type ContainerBox interface { type ContainerBox interface {
ContentBox ContentBox
// SetPropagateEvents specifies whether or not child Objects will
// receive user input events. It is true by default. If it is false, all
// user input that would otherwise be directed to a child Box is
// directed to this Box.
SetPropagateEvents (bool)
// SetGap sets the gap between child Objects. // SetGap sets the gap between child Objects.
SetGap (image.Point) SetGap (image.Point)
// Add appends a child Object. // Add appends a child Object. If the object is already a child of
Add (Object) // another object, it will be removed from that object first.
// Delete removes a child Object, if it is a child of this Box. Add (Object)
Delete (Object) // Remove removes a child Object, if it is a child of this Box.
Remove (Object)
// Insert inserts a child Object before a specified Object. If the // Insert inserts a child Object before a specified Object. If the
// before Object is nil or is not contained within this Box, the // before Object is nil or is not contained within this Box, the
// inserted Object is appended. // inserted Object is appended.
Insert (child Object, before Object) Insert (child Object, before Object)
// Clear removes all child Objects. // Clear removes all child Objects.
Clear () Clear ()
// Length returns the amount of child objects. // Length returns the amount of child Objects.
Length () int Length () int
// At returns the child Object at the specified index. // At returns the child Object at the specified index.
At (int) Object At (int) Object
// SetLayout sets the layout of this Box. Child Objects will be // SetLayout sets the layout of this Box. Child Objects will be
// positioned according to it. // positioned according to it.
SetLayout (Layout) SetLayout (Layout)
// 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)
} }
// 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.
@ -285,14 +305,14 @@ type LayoutHints struct {
// Bounds is the bounding rectangle that children should be placed // Bounds is the bounding rectangle that children should be placed
// within. Any padding values are already applied. // within. Any padding values are already applied.
Bounds image.Rectangle Bounds image.Rectangle
// OverflowX and OverflowY control wether child Boxes may be positioned // OverflowX and OverflowY control whether child Boxes may be positioned
// outside of Bounds. // outside of Bounds.
OverflowX bool OverflowX bool
OverflowY bool OverflowY bool
// AlignX and AlignY control how child Boxes are aligned horizontally // AlignX and AlignY control how child Boxes are aligned horizontally
// and vertically. The effect of this may vary depending on the Layout. // and vertically. The effect of this may vary depending on the Layout.
AlignX Align AlignX Align
AlignY Align AlignY Align
// Gap controls the amount of horizontal and vertical spacing in-between // Gap controls the amount of horizontal and vertical spacing in-between
// child Boxes. // child Boxes.
Gap image.Point Gap image.Point
@ -304,43 +324,44 @@ type Layout interface {
// LayoutHints.Bounds needed to properly lay out all child Boxes. // LayoutHints.Bounds needed to properly lay out all child Boxes.
MinimumSize (LayoutHints, []Box) image.Point MinimumSize (LayoutHints, []Box) image.Point
// Arrange arranges child boxes according to the given LayoutHints. // Arrange arranges child boxes according to the given LayoutHints.
Arrange (LayoutHints, []Box) Arrange (LayoutHints, []Box)
} }
// Window is an operating system window. It can contain one object. // Window is an operating system window. It can contain one object.
type Window interface { type Window interface {
// SetRoot sets the root child of the window. There can only be one at // 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. // a time, and setting it will remove the current child if there is one.
SetRoot (Object) SetRoot (Object)
// SetTitle sets the title of the window. // SetTitle sets the title of the window.
SetTitle (string) SetTitle (string)
// SetIcon sets the icon of the window. When multiple icon sizes are // SetIcon sets the icon of the window. When multiple icon sizes are
// provided, the best fitting one is chosen for display. // provided, the best fitting one is chosen for display.
SetIcon (... image.Image) SetIcon (... image.Image)
// Widget returns a window representing a smaller iconified form of this // Widget returns a window representing a smaller iconified form of this
// window. How exactly this window is used depends on the platform. // window. How exactly this window is used depends on the platform.
// Subsequent calls to this method on the same window will return the // Subsequent calls to this method on the same window will return the
// same window object. // same window object.
Widget () (Window, error) Widget () (Window, error)
// NewMenu creates a new menu window. This window is undecorated and // NewMenu creates a new menu window. This window is undecorated and
// will close once the user clicks outside of it. // will close once the user clicks outside of it.
NewMenu (image.Rectangle) (Window, error) NewMenu (image.Rectangle) (Window, error)
// NewModal creates a new modal window that blocks all input to this // NewModal creates a new modal window that blocks all input to this
// window until it is closed. // window until it is closed.
NewModal (image.Rectangle) (Window, error) NewModal (image.Rectangle) (Window, error)
// Copy copies data to the clipboard. // Copy copies data to the clipboard.
Copy (data.Data) Copy (data.Data)
// Paste reads data from the clipboard. When the data is available or an // Paste reads data from the clipboard. When the data is available or an
// error has occurred, the provided function will be called. // error has occurred, the provided function will be called.
Paste (callback func (data.Data, error), accept ...data.Mime) Paste (callback func (data.Data, error), accept ...data.Mime)
// Show shows the window. // SetVisible sets whether or not this window is visible. Windows are
Show () // invisible by default.
// Hide hides the window. SetVisible (bool)
Hide () // Visible returns whether or not this window is visible.
Visible () bool
// Close closes the window. // Close closes the window.
Close () Close ()
// OnClose specifies a function to be called when the window is closed. // OnClose specifies a function to be called when the window is closed.
OnClose (func ()) event.Cookie OnClose (func ()) event.Cookie
} }
// MainWindow is a top-level operating system window. // MainWindow is a top-level operating system window.

View File

@ -1,55 +0,0 @@
package tomo
import "os"
import "plugin"
import "path/filepath"
var pluginPaths []string
func loadPlugins () {
for _, dir := range pluginPaths {
if dir != "" {
loadPluginsFrom(dir)
}
}
}
func loadPluginsFrom (dir string) {
entries, err := os.ReadDir(dir)
// its no big deal if one of the dirs doesn't exist
if err != nil { return }
for _, entry := range entries {
if entry.IsDir() { continue }
if filepath.Ext(entry.Name()) != ".so" { continue }
pluginPath := filepath.Join(dir, entry.Name())
loadPlugin(pluginPath)
}
}
func loadPlugin (path string) {
die := func (reason string) {
println("tomo: could not load plugin at", path + ":", reason)
}
plugin, err := plugin.Open(path)
if err != nil {
die(err.Error())
return
}
// check for and obtain basic plugin functions
name, ok := extract[func () string](plugin, "Name")
if !ok { die("does not implement Name() string"); return }
_, ok = extract[func () string](plugin, "Description")
if !ok { die("does not implement Description() string"); return }
println("tomo: loaded plugin", name())
}
func extract[T any] (plugin *plugin.Plugin, name string) (value T, ok bool) {
symbol, err := plugin.Lookup(name)
if err != nil { return }
value, ok = symbol.(T)
return
}

278
theme/icon.go Normal file
View File

@ -0,0 +1,278 @@
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,14 +1,17 @@
package theme package theme
import "fmt"
import "git.tebibyte.media/tomo/tomo" 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/event"
import "git.tebibyte.media/tomo/tomo/canvas"
// Role describes the role of an object. // Role describes the role of an object.
type Role struct { type Role struct {
// Package is an optional namespace field. If specified, it should be // Package is an optional namespace field. If specified, it should be
// the package name or module name the object is from. // the package name or module name the object is from.
Package string Package string
// Object specifies what type of object it is. For example: // Object specifies what type of object it is. For example:
// - TextInput // - TextInput
// - Table // - Table
@ -16,12 +19,24 @@ type Role struct {
// - Dial // - Dial
// This should correspond directly to the type name of the object. // This should correspond directly to the type name of the object.
Object string 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. // R is shorthand for creating a Role structure.
func R (pack, object string) Role { func R (pack, object, variant string) Role {
return Role { Package: pack, Object: object } return Role { Package: pack, Object: object, Variant: variant }
} }
// Color represents a color ID. // Color represents a color ID.
type Color int; const ( type Color int; const (
@ -32,21 +47,50 @@ type Color int; const (
ColorAccent 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. // RGBA satisfies the color.Color interface.
func (id Color) RGBA () (r, g, b, a uint32) { func (id Color) RGBA () (r, g, b, a uint32) {
if current == nil { return } if current == nil { return }
return current.RGBA(id) return current.RGBA(id)
} }
// Theme is an object that can apply a visual style to different objects. // Theme can apply a visual style to different objects.
type Theme interface { 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 // Apply applies the theme to the given object, according to the given
// role. This may register event listeners with the given object; // role. This may register event listeners with the given object;
// closing the returned cookie will remove them. // closing the returned cookie must remove them.
Apply (tomo.Object, Role) event.Cookie Apply (tomo.Object, Role) event.Cookie
// RGBA returns the RGBA values of the corresponding color ID. // RGBA returns the RGBA values of the corresponding color ID.
RGBA (Color) (r, g, b, a uint32) 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 var current Theme
@ -63,4 +107,3 @@ func Apply (object tomo.Object, role Role) event.Cookie {
if current == nil { return event.NoCookie { } } if current == nil { return event.NoCookie { } }
return current.Apply(object, role) return current.Apply(object, role)
} }

18
tomo.go
View File

@ -3,6 +3,7 @@ package tomo
import "sync" import "sync"
import "image" import "image"
import "errors" import "errors"
import "git.tebibyte.media/tomo/tomo/canvas"
var backendLock sync.Mutex var backendLock sync.Mutex
var backend Backend var backend Backend
@ -11,8 +12,6 @@ var backend Backend
// event loop in that order. This function blocks until Stop is called, or the // event loop in that order. This function blocks until Stop is called, or the
// backend experiences a fatal error. // backend experiences a fatal error.
func Run (callback func ()) error { func Run (callback func ()) error {
loadPlugins()
if backend != nil { if backend != nil {
return errors.New("there is already a backend running") return errors.New("there is already a backend running")
} }
@ -56,6 +55,14 @@ func NewWindow (bounds image.Rectangle) (MainWindow, error) {
return backend.NewWindow(bounds) 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)
}
// NewBox creates and returns a basic Box. // NewBox creates and returns a basic Box.
func NewBox () Box { func NewBox () Box {
assertBackend() assertBackend()
@ -79,3 +86,10 @@ func NewContainerBox () ContainerBox {
assertBackend() assertBackend()
return backend.NewContainerBox() return backend.NewContainerBox()
} }
// NewTexture creates a new 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)
}

22
unix.go
View File

@ -1,22 +0,0 @@
//go:build linux || darwin || freebsd
package tomo
import "os"
import "strings"
import "path/filepath"
func init () {
pathVariable := os.Getenv("TOMO_PLUGIN_PATH")
pluginPaths = strings.Split(pathVariable, ":")
pluginPaths = append (
pluginPaths,
"/usr/lib/tomo/plugins",
"/usr/local/lib/tomo/plugins")
homeDir, err := os.UserHomeDir()
if err == nil {
pluginPaths = append (
pluginPaths,
filepath.Join(homeDir, ".local/lib/tomo/plugins"))
}
}