diff --git a/event/event.go b/event/event.go index ef1e6ae..0af1ea3 100644 --- a/event/event.go +++ b/event/event.go @@ -45,6 +45,10 @@ 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 { id int broadcaster *Broadcaster[L] @@ -65,3 +69,17 @@ 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() + } +} diff --git a/go.mod b/go.mod index f9ffc90..f9b1ee3 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module git.tebibyte.media/tomo/tomo go 1.20 -require golang.org/x/image v0.8.0 +require golang.org/x/image v0.11.0 diff --git a/go.sum b/go.sum index bb724e4..ed117e2 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk 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= @@ -26,6 +28,7 @@ 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= diff --git a/object.go b/object.go index 5e1c585..3ca47d8 100644 --- a/object.go +++ b/object.go @@ -104,7 +104,7 @@ type Align int; const ( // Object is any obscreen object. Each object must be linked to a box, even if // it is that box. type Object interface { - Box () Box + GetBox () Box } // Box is a basic styled box. @@ -238,7 +238,7 @@ type TextBox interface { // SetSelectable sets whether or not the text content can be // highlighted/selected. SetSelectable (bool) - // SetDotColor sets the highlight color of the text selection. + // SetDotColor sets the highlight color of selected text. SetDotColor (color.Color) // Select sets the text cursor or selection. Select (text.Dot) diff --git a/theme/icon.go b/theme/icon.go new file mode 100644 index 0000000..c7cbc4e --- /dev/null +++ b/theme/icon.go @@ -0,0 +1,288 @@ +package theme + +import "image" +import "git.tebibyte.media/tomo/tomo/data" + +// IconSize represents the size of an icon. +type IconSize int; const ( + IconSizeSmall IconSize = iota; + IconSizeMedium + IconSizeLarge +) + +// Icon represents an icon ID. +type Icon int; const ( + // --- Objects --- // + + // files + IconFile Icon = iota + IconDirectory + IconDirectoryFull + + // places + IconDownloads + IconPhotos + IconBooks + IconDocuments + IconRepositories + IconMusic + IconArchives + IconFonts + IconBinaries + IconVideos + Icon3DObjects + IconHistory + IconPreferences + + // storage + IconStorage // generic + IconMagneticTape + IconFloppyDisk + IconHardDisk + IconSolidStateDrive + IconFlashDrive + IconMemoryCard + IconROMDisk + IconRAMDisk + IconCD + IconDVD + + // network + IconNetwork // generic + IconLocalNetwork + IconInternet + IconEthernet + IconWireless + IconCell + IconBluetooth + IconRadio + + // devices + IconDevice // generic + IconRouter + IconServer + IconDesktop + IconLaptop + IconTablet + IconPhone + IconWatch + IconCamera + + // peripherals + IconPeripheral // generic + IconKeyboard + IconMouse + IconMonitor + IconWebcam + IconMicrophone + IconSpeaker + IconPenTablet + IconTrackpad + IconController + + // i/o + IconPort // generic + IconEthernetPort + IconUSBPort + IconParallelPort + IconSerialPort + IconPS2Port + IconDisplayConnector + IconCGAPort + IconVGAPort + IconHDMIPort + IconDisplayPort + IconInfrared + + // --- Actions --- // + + // files + IconOpen + IconOpenIn + IconSave + IconSaveAs + IconPrints + IconNew + IconNewDirectory + IconDelete + IconRename + IconGetInformation + IconChangePermissions + IconRevert + + // list management + IconAdd + IconRemove + IconAddBookmark + IconRemoveBookmark + IconAddFavorite + IconRemoveFavorite + + // media + IconPlay + IconPause + IconStop + IconFastForward + IconRewind + IconToBeginning + IconToEnd + IconRecord + IconVolumeUp + IconVolumeDown + IconMute + + // editing + IconUndo + IconRedo + IconCut + IconCopy + IconPaste + IconFind + IconReplace + IconSelectAll + IconSelectNone + IconIncrement + IconDecrement + + // window management + IconClose + IconQuit + IconIconify + IconShade + IconMaximize + IconFullScreen + IconRestore + + // view controls + IconExpand + IconContract + IconBack + IconForward + IconUp + IconDown + IconReload + IconZoomIn + IconZoomOut + IconZoomReset + IconMove + IconResize + IconGoTo + + // tools + IconTransform + IconTranslate + IconRotate + IconScale + IconWarp + IconCornerPin + IconSelectRectangle + IconSelectEllipse + IconSelectLasso + IconSelectGeometric + IconSelectAuto + IconCrop + IconFill + IconGradient + IconPencil + IconBrush + IconEraser + IconText + IconEyedropper + + // --- Status --- // + + // dialogs + IconInformation + IconQuestion + IconWarning + IconError + IconCancel + IconOkay + + // network + IconCellSignal0 + IconCellSignal1 + IconCellSignal2 + IconCellSignal3 + IconWirelessSignal0 + IconWirelessSignal1 + IconWirelessSignal2 + IconWirelessSignal3 + + // power + IconBattery0 + IconBattery1 + IconBattery2 + IconBattery3 + IconBrightness0 + IconBrightness1 + IconBrightness2 + IconBrightness3 + + // media + IconVolume0 + IconVolume1 + IconVolume2 + IconVolume3 +) + +// Icon returns an image of the corresponding icon ID. +func (id Icon) Image (size IconSize) image.Image { + if current == nil { return image.Rect(0, 0, 16, 16) } + return current.Icon(id, size) +} + +// MimeIcon returns an icon corresponding to a MIME type. This must +// always return a non-nil value. +func MimeIcon (mime data.Mime, size IconSize) image.Image { + if current == nil { return image.Rect(0, 0, 16, 16) } + return current.MimeIcon(mime, size) +} + +// ApplicationIcon describes the icon of the application. +type ApplicationIcon struct { + // The name or ID of the application. If applicable, this should + // correspond to the file name (without the path or extension) of the + // icon on the system. This field is optional. + Name string + + // Role describes what the application does. If a specific icon file + // cannot be found, a generic one is picked using this field. + Role ApplicationRole +} + +// ApplicationRole describes what an application does. +type ApplicationRole int; const ( + RoleUnknown ApplicationRole = iota + + RoleWebBrowser + RoleMesssanger + RolePhone + RoleMail + + RoleTerminalEmulator + RoleFileBrowser + RoleTextEditor + + RoleDocumentViewer + RoleWordProcessor + RoleSpreadsheet + RoleSlideshow + RoleCalculator + + RolePreferences + RoleProcessManager + RoleSystemInformation + RoleManual + + RoleCamera + RoleImageViewer + RoleMediaPlayer + RoleImageEditor + RoleAudioEditor + RoleVideoEditor + + RoleClock + RoleCalendar + RoleChecklist +) diff --git a/theme/theme.go b/theme/theme.go new file mode 100644 index 0000000..d80d8a5 --- /dev/null +++ b/theme/theme.go @@ -0,0 +1,85 @@ +package theme + +import "image" +import "git.tebibyte.media/tomo/tomo" +import "git.tebibyte.media/tomo/tomo/data" +import "git.tebibyte.media/tomo/tomo/event" + +// Role describes the role of an object. +type Role struct { + // Package is an optional namespace field. If specified, it should be + // the package name or module name the object is from. + Package string + + // Object specifies what type of object it is. For example: + // - TextInput + // - Table + // - Label + // - Dial + // This should correspond directly to the type name of the object. + Object string + + // Variant is an optional field to be used when an object has one or + // more soft variants under one type. For example, an object "Slider" + // may have variations "horizontal" and "vertical". + Variant string +} + +// 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 +) + +// RGBA satisfies the color.Color interface. +func (id Color) RGBA () (r, g, b, a uint32) { + if current == nil { return } + return current.RGBA(id) +} + +// Theme is an object that can apply a visual style to different objects. +type Theme interface { + // 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 will 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 an image of the corresponding icon ID. This must always + // return a non-nil value. + Icon (Icon, IconSize) image.Image + + // MimeIcon returns an icon corresponding to a MIME type. This must + // always return a non-nil value. + MimeIcon (data.Mime, IconSize) image.Image + + // ApplicationIcon returns an icon corresponding to an application. This + // must always return a non-nil value. + ApplicationIcon (ApplicationIcon, IconSize) image.Image +} + +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) +} +