diff --git a/internal/theme/default/assets/icons-large.png b/internal/theme/default/assets/icons-large.png new file mode 100644 index 0000000..02c8772 Binary files /dev/null and b/internal/theme/default/assets/icons-large.png differ diff --git a/internal/theme/default/assets/icons-large.xcf b/internal/theme/default/assets/icons-large.xcf new file mode 100644 index 0000000..1495c6c Binary files /dev/null and b/internal/theme/default/assets/icons-large.xcf differ diff --git a/internal/theme/default/assets/icons-small.png b/internal/theme/default/assets/icons-small.png new file mode 100644 index 0000000..c3b2fdb Binary files /dev/null and b/internal/theme/default/assets/icons-small.png differ diff --git a/internal/theme/default/assets/icons-small.xcf b/internal/theme/default/assets/icons-small.xcf new file mode 100644 index 0000000..1ea13b9 Binary files /dev/null and b/internal/theme/default/assets/icons-small.xcf differ diff --git a/internal/theme/default/default.go b/internal/theme/default/default.go index 624958e..4482c08 100644 --- a/internal/theme/default/default.go +++ b/internal/theme/default/default.go @@ -1,220 +1,9 @@ package defaultTheme import "image/color" -import "git.tebibyte.media/tomo/tomo" -import "golang.org/x/image/font/basicfont" import "git.tebibyte.media/tomo/tomo/theme" import dataTheme "git.tebibyte.media/tomo/nasin/internal/theme" -var colorFocus = color.RGBA { R: 61, G: 128, B: 143, A: 255 } -var colorInput = color.RGBA { R: 208, G: 203, B: 150, A: 255 } -var colorCarved = color.RGBA { R: 151, G: 160, B: 150, A: 255 } -var colorShadow = color.RGBA { R: 57, G: 59, B: 57, A: 255 } -var colorInputShadow = color.RGBA { R: 143, G: 146, B: 91, A: 255 } -var colorHighlight = color.RGBA { R: 207, G: 215, B: 210, A: 255 } -var colorBackground = color.RGBA { R: 169, G: 171, B: 168, A: 255 } -var colorCarvedPressed = color.RGBA { R: 129, G: 142, B: 137, A: 255 } -var colorForeground = color.Black -var colorOutline = color.Black - -var outline = tomo.Border { - Width: tomo.I(1), - Color: [4]color.Color { - colorOutline, - colorOutline, - colorOutline, - colorOutline, - }, -} - -var borderColorEngraved = [4]color.Color { colorShadow, colorHighlight, colorHighlight, colorShadow } -var borderColorLifted = [4]color.Color { colorHighlight, colorShadow, colorShadow, colorHighlight } -var borderColorInput = [4]color.Color { colorInputShadow, colorInput, colorInput, colorInputShadow } -var borderColorFocused = [4]color.Color { colorFocus, colorFocus, colorFocus, colorFocus } - -var rules = []dataTheme.Rule { - // *.*[*] - dataTheme.Rule { - Default: dataTheme.AS ( - dataTheme.AttrFace { Face: basicfont.Face7x13 }, - dataTheme.AttrTextColor { Color: theme.ColorForeground }, - dataTheme.AttrDotColor { Color: theme.ColorAccent }, - dataTheme.AttrGap { X: 8, Y: 8 }, - ), - }, - - // *.Button[*] - dataTheme.Rule { - Role: theme.R("", "Button", ""), - Default: dataTheme.AS ( - dataTheme.AttrBorder { - outline, - tomo.Border { - Width: tomo.I(1), - Color: borderColorLifted, - }, - }, - dataTheme.AttrPadding(tomo.I(4, 8)), - dataTheme.AttrColor { Color: theme.ColorRaised }, - ), - Pressed: dataTheme.AS ( - dataTheme.AttrBorder { - outline, - tomo.Border { - Width: tomo.I(1, 0, 0, 1), - Color: borderColorEngraved, - }, - }, - dataTheme.AttrPadding(tomo.I(5, 8, 4, 9)), - dataTheme.AttrColor { Color: colorCarvedPressed }, - ), - Focused: dataTheme.AS ( - dataTheme.AttrBorder { - outline, - tomo.Border { - Width: tomo.I(1), - Color: borderColorFocused, - }, - }, - dataTheme.AttrPadding(tomo.I(4, 8)), - ), - }, - - // *.TextInput[*] - dataTheme.Rule { - Role: theme.R("", "TextInput", ""), - Default: dataTheme.AS ( - dataTheme.AttrBorder { - outline, - tomo.Border { - Width: tomo.I(1), - Color: borderColorInput, - }, - }, - dataTheme.AttrColor { Color: colorInput }, - dataTheme.AttrPadding(tomo.I(5, 4, 4, 5)), - ), - Focused: dataTheme.AS ( - dataTheme.AttrBorder { - outline, - tomo.Border { - Width: tomo.I(1), - Color: borderColorFocused, - }, - }, - ), - }, - - // *.NumberInput[*] - dataTheme.Rule { - Role: theme.R("", "NumberInput", ""), - Default: dataTheme.AS ( - dataTheme.AttrBorder { - outline, - tomo.Border { - Width: tomo.I(1), - Color: borderColorInput, - }, - }, - dataTheme.AttrColor { Color: colorInput }, - dataTheme.AttrPadding(tomo.I(5, 4, 4, 5)), - ), - Focused: dataTheme.AS ( - dataTheme.AttrBorder { - outline, - tomo.Border { - Width: tomo.I(1), - Color: borderColorFocused, - }, - }, - ), - }, - - // *.Container[outer] - dataTheme.Rule { - Role: theme.R("", "Container", "outer"), - Default: dataTheme.AS ( - dataTheme.AttrColor { Color: theme.ColorBackground }, - dataTheme.AttrPadding(tomo.I(8)), - ), - }, - - // *.Heading[*] - dataTheme.Rule { - Role: theme.R("", "Heading", ""), - Default: dataTheme.AS ( - dataTheme.AttrAlign { X: tomo.AlignMiddle, Y: tomo.AlignMiddle }, - ), - }, - - // *.Separator[*] - dataTheme.Rule { - Role: theme.R("", "Separator", ""), - Default: dataTheme.AS ( - dataTheme.AttrBorder { - tomo.Border { - Width: tomo.I(1), - Color: borderColorEngraved, - }, - }, - ), - }, - - // *.Slider[*] - dataTheme.Rule { - Role: theme.R("", "Slider", ""), - Default: dataTheme.AS ( - dataTheme.AttrBorder { - outline, - tomo.Border { - Width: tomo.I(1, 0, 0, 1), - Color: borderColorEngraved, - }, - }, - dataTheme.AttrColor { Color: theme.ColorSunken }, - dataTheme.AttrPadding(tomo.I(0, 1, 1, 0)), - ), - Focused: dataTheme.AS ( - dataTheme.AttrBorder { - outline, - tomo.Border { - Width: tomo.I(1), - Color: borderColorFocused, - }, - }, - dataTheme.AttrPadding(tomo.I(0)), - ), - }, - - // *.Slider[horizontal] - dataTheme.Rule { - Role: theme.R("", "Slider", "horizontal"), - Default: dataTheme.AS(dataTheme.AttrMinimumSize { X: 48 }), - }, - - // *.Slider[vertical] - dataTheme.Rule { - Role: theme.R("", "Slider", "vertical"), - Default: dataTheme.AS(dataTheme.AttrMinimumSize { Y: 48 }), - }, - - // *.SliderHandle[*] - dataTheme.Rule { - Role: theme.R("", "SliderHandle", ""), - Default: dataTheme.AS ( - dataTheme.AttrBorder { - outline, - tomo.Border { - Width: tomo.I(1), - Color: borderColorLifted, - }, - }, - dataTheme.AttrColor { Color: theme.ColorRaised }, - dataTheme.AttrMinimumSize { X: 12, Y: 12, }, - ), - }, -} - // Theme returns Wintergreen, the default Tomo theme. It is neutral-gray with // green and turquoise accents. func Theme () theme.Theme { @@ -227,5 +16,6 @@ func Theme () theme.Theme { theme.ColorAccent: colorFocus, }, Rules: rules, + IconTheme: &iconTheme { }, } } diff --git a/internal/theme/default/icon.go b/internal/theme/default/icon.go new file mode 100644 index 0000000..5f68417 --- /dev/null +++ b/internal/theme/default/icon.go @@ -0,0 +1,228 @@ +package defaultTheme + +import "bytes" +import "image" +import _ "embed" +import _ "image/png" +import "git.tebibyte.media/tomo/tomo" +import "git.tebibyte.media/tomo/tomo/data" +import "git.tebibyte.media/tomo/tomo/theme" +import "git.tebibyte.media/tomo/tomo/canvas" + +//go:embed assets/icons-small.png +var atlasSmallBytes []byte +//go:embed assets/icons-large.png +var atlasLargeBytes []byte + +func generateSource (data []byte, width int) map[theme.Icon] canvas.Texture { + atlasImage, _, err := image.Decode(bytes.NewReader(data)) + if err != nil { panic(err) } + atlasTexture := tomo.NewTexture(atlasImage) + + source := make(map[theme.Icon] canvas.Texture) + x := 0 + y := 0 + + row := func () { + x = 0 + y ++ + } + col := func (id theme.Icon) { + source[id] = atlasTexture.Clip(image.Rect ( + x * width, + y * width, + (x + 1) * width, + (y + 1) * width)) + x++ + } + + // objects + col(theme.IconUnknown) + col(theme.IconFile) + col(theme.IconDirectory) + col(theme.IconStorage) + col(theme.IconApplication) + col(theme.IconNetwork) + col(theme.IconDevice) + col(theme.IconPeripheral) + col(theme.IconPort) + + // actions: files + row() + col(theme.IconActionOpen) + col(theme.IconActionOpenIn) + col(theme.IconActionSave) + col(theme.IconActionSaveAs) + col(theme.IconActionPrint) + col(theme.IconActionNew) + col(theme.IconActionNewDirectory) + col(theme.IconActionDelete) + col(theme.IconActionRename) + col(theme.IconActionGetInformation) + col(theme.IconActionChangePermissions) + col(theme.IconActionRevert) + + // actions: list management + row() + col(theme.IconActionAdd) + col(theme.IconActionRemove) + col(theme.IconActionAddBookmark) + col(theme.IconActionRemoveBookmark) + col(theme.IconActionAddFavorite) + col(theme.IconActionRemoveFavorite) + + // actions: media + row() + col(theme.IconActionPlay) + col(theme.IconActionPause) + col(theme.IconActionStop) + col(theme.IconActionFastForward) + col(theme.IconActionRewind) + col(theme.IconActionToBeginning) + col(theme.IconActionToEnd) + col(theme.IconActionRecord) + col(theme.IconActionVolumeUp) + col(theme.IconActionVolumeDown) + col(theme.IconActionMute) + + // actions: editing + row() + col(theme.IconActionUndo) + col(theme.IconActionRedo) + col(theme.IconActionCut) + col(theme.IconActionCopy) + col(theme.IconActionPaste) + col(theme.IconActionFind) + col(theme.IconActionReplace) + col(theme.IconActionSelectAll) + col(theme.IconActionSelectNone) + col(theme.IconActionIncrement) + col(theme.IconActionDecrement) + + // actions: window management + row() + col(theme.IconActionClose) + col(theme.IconActionQuit) + col(theme.IconActionIconify) + col(theme.IconActionShade) + col(theme.IconActionMaximize) + col(theme.IconActionFullScreen) + col(theme.IconActionRestore) + + // actions: view + row() + col(theme.IconActionExpand) + col(theme.IconActionContract) + col(theme.IconActionBack) + col(theme.IconActionForward) + col(theme.IconActionUp) + col(theme.IconActionDown) + col(theme.IconActionReload) + col(theme.IconActionZoomIn) + col(theme.IconActionZoomOut) + col(theme.IconActionZoomReset) + col(theme.IconActionMove) + col(theme.IconActionResize) + col(theme.IconActionGoTo) + + // actions: tools + row() + col(theme.IconActionTransform) + col(theme.IconActionTranslate) + col(theme.IconActionRotate) + col(theme.IconActionScale) + col(theme.IconActionWarp) + col(theme.IconActionCornerPin) + col(theme.IconActionSelectRectangle) + col(theme.IconActionSelectEllipse) + col(theme.IconActionSelectLasso) + col(theme.IconActionSelectGeometric) + col(theme.IconActionSelectAuto) + col(theme.IconActionCrop) + col(theme.IconActionFill) + row() + col(theme.IconActionGradient) + col(theme.IconActionPencil) + col(theme.IconActionBrush) + col(theme.IconActionEraser) + col(theme.IconActionText) + col(theme.IconActionEyedropper) + + // status: dialog + row() + col(theme.IconStatusInformation) + col(theme.IconStatusQuestion) + col(theme.IconStatusWarning) + col(theme.IconStatusError) + col(theme.IconStatusCancel) + col(theme.IconStatusOkay) + + // status: network + row() + col(theme.IconStatusCellSignal0) + col(theme.IconStatusCellSignal1) + col(theme.IconStatusCellSignal2) + col(theme.IconStatusCellSignal3) + col(theme.IconStatusWirelessSignal0) + col(theme.IconStatusWirelessSignal1) + col(theme.IconStatusWirelessSignal2) + col(theme.IconStatusWirelessSignal3) + + // status: power + row() + col(theme.IconStatusBattery0) + col(theme.IconStatusBattery1) + col(theme.IconStatusBattery2) + col(theme.IconStatusBattery3) + col(theme.IconStatusBrightness0) + col(theme.IconStatusBrightness1) + col(theme.IconStatusBrightness2) + col(theme.IconStatusBrightness3) + + // status: media + row() + col(theme.IconStatusVolume0) + col(theme.IconStatusVolume1) + col(theme.IconStatusVolume2) + col(theme.IconStatusVolume3) + + return source +} + +type iconTheme struct { + texturesSmall map[theme.Icon] canvas.Texture + texturesLarge map[theme.Icon] canvas.Texture +} + +func (this *iconTheme) ensure () { + if this.texturesSmall != nil { return } + this.texturesSmall = generateSource(atlasSmallBytes, 16) + this.texturesLarge = generateSource(atlasLargeBytes, 32) +} + +func (this *iconTheme) selectSource (size theme.IconSize) map[theme.Icon] canvas.Texture { + if size == theme.IconSizeSmall { + return this.texturesSmall + } else { + return this.texturesLarge + } +} + +func (this *iconTheme) Icon (icon theme.Icon, size theme.IconSize) canvas.Texture { + this.ensure() + source := this.selectSource(size) + if texture, ok := source[icon]; ok { + return texture + } + return source[theme.IconUnknown] +} + +func (this *iconTheme) MimeIcon (mime data.Mime, size theme.IconSize) canvas.Texture { + this.ensure() + source := this.selectSource(size) + if mime == data.M("inode", "directory") { + return source[theme.IconDirectory] + } else { + return source[theme.IconFile] + } +} diff --git a/internal/theme/default/style.go b/internal/theme/default/style.go new file mode 100644 index 0000000..12336c4 --- /dev/null +++ b/internal/theme/default/style.go @@ -0,0 +1,216 @@ +package defaultTheme + +import "image/color" +import "git.tebibyte.media/tomo/tomo" +import "golang.org/x/image/font/basicfont" +import "git.tebibyte.media/tomo/tomo/theme" +import dataTheme "git.tebibyte.media/tomo/nasin/internal/theme" + +var colorFocus = color.RGBA { R: 61, G: 128, B: 143, A: 255 } +var colorInput = color.RGBA { R: 208, G: 203, B: 150, A: 255 } +var colorCarved = color.RGBA { R: 151, G: 160, B: 150, A: 255 } +var colorShadow = color.RGBA { R: 57, G: 59, B: 57, A: 255 } +var colorInputShadow = color.RGBA { R: 143, G: 146, B: 91, A: 255 } +var colorHighlight = color.RGBA { R: 207, G: 215, B: 210, A: 255 } +var colorBackground = color.RGBA { R: 169, G: 171, B: 168, A: 255 } +var colorCarvedPressed = color.RGBA { R: 129, G: 142, B: 137, A: 255 } +var colorForeground = color.Black +var colorOutline = color.Black + +var outline = tomo.Border { + Width: tomo.I(1), + Color: [4]color.Color { + colorOutline, + colorOutline, + colorOutline, + colorOutline, + }, +} + +var borderColorEngraved = [4]color.Color { colorShadow, colorHighlight, colorHighlight, colorShadow } +var borderColorLifted = [4]color.Color { colorHighlight, colorShadow, colorShadow, colorHighlight } +var borderColorInput = [4]color.Color { colorInputShadow, colorInput, colorInput, colorInputShadow } +var borderColorFocused = [4]color.Color { colorFocus, colorFocus, colorFocus, colorFocus } + +var rules = []dataTheme.Rule { + // *.*[*] + dataTheme.Rule { + Default: dataTheme.AS ( + dataTheme.AttrFace { Face: basicfont.Face7x13 }, + dataTheme.AttrTextColor { Color: theme.ColorForeground }, + dataTheme.AttrDotColor { Color: theme.ColorAccent }, + dataTheme.AttrGap { X: 8, Y: 8 }, + ), + }, + + // *.Button[*] + dataTheme.Rule { + Role: theme.R("", "Button", ""), + Default: dataTheme.AS ( + dataTheme.AttrBorder { + outline, + tomo.Border { + Width: tomo.I(1), + Color: borderColorLifted, + }, + }, + dataTheme.AttrPadding(tomo.I(4, 8)), + dataTheme.AttrColor { Color: theme.ColorRaised }, + ), + Pressed: dataTheme.AS ( + dataTheme.AttrBorder { + outline, + tomo.Border { + Width: tomo.I(1, 0, 0, 1), + Color: borderColorEngraved, + }, + }, + dataTheme.AttrPadding(tomo.I(5, 8, 4, 9)), + dataTheme.AttrColor { Color: colorCarvedPressed }, + ), + Focused: dataTheme.AS ( + dataTheme.AttrBorder { + outline, + tomo.Border { + Width: tomo.I(1), + Color: borderColorFocused, + }, + }, + dataTheme.AttrPadding(tomo.I(4, 8)), + ), + }, + + // *.TextInput[*] + dataTheme.Rule { + Role: theme.R("", "TextInput", ""), + Default: dataTheme.AS ( + dataTheme.AttrBorder { + outline, + tomo.Border { + Width: tomo.I(1), + Color: borderColorInput, + }, + }, + dataTheme.AttrColor { Color: colorInput }, + dataTheme.AttrPadding(tomo.I(5, 4, 4, 5)), + ), + Focused: dataTheme.AS ( + dataTheme.AttrBorder { + outline, + tomo.Border { + Width: tomo.I(1), + Color: borderColorFocused, + }, + }, + ), + }, + + // *.NumberInput[*] + dataTheme.Rule { + Role: theme.R("", "NumberInput", ""), + Default: dataTheme.AS ( + dataTheme.AttrBorder { + outline, + tomo.Border { + Width: tomo.I(1), + Color: borderColorInput, + }, + }, + dataTheme.AttrColor { Color: colorInput }, + dataTheme.AttrPadding(tomo.I(5, 4, 4, 5)), + ), + Focused: dataTheme.AS ( + dataTheme.AttrBorder { + outline, + tomo.Border { + Width: tomo.I(1), + Color: borderColorFocused, + }, + }, + ), + }, + + // *.Container[outer] + dataTheme.Rule { + Role: theme.R("", "Container", "outer"), + Default: dataTheme.AS ( + dataTheme.AttrColor { Color: theme.ColorBackground }, + dataTheme.AttrPadding(tomo.I(8)), + ), + }, + + // *.Heading[*] + dataTheme.Rule { + Role: theme.R("", "Heading", ""), + Default: dataTheme.AS ( + dataTheme.AttrAlign { X: tomo.AlignMiddle, Y: tomo.AlignMiddle }, + ), + }, + + // *.Separator[*] + dataTheme.Rule { + Role: theme.R("", "Separator", ""), + Default: dataTheme.AS ( + dataTheme.AttrBorder { + tomo.Border { + Width: tomo.I(1), + Color: borderColorEngraved, + }, + }, + ), + }, + + // *.Slider[*] + dataTheme.Rule { + Role: theme.R("", "Slider", ""), + Default: dataTheme.AS ( + dataTheme.AttrBorder { + outline, + tomo.Border { + Width: tomo.I(1, 0, 0, 1), + Color: borderColorEngraved, + }, + }, + dataTheme.AttrColor { Color: theme.ColorSunken }, + dataTheme.AttrPadding(tomo.I(0, 1, 1, 0)), + ), + Focused: dataTheme.AS ( + dataTheme.AttrBorder { + outline, + tomo.Border { + Width: tomo.I(1), + Color: borderColorFocused, + }, + }, + dataTheme.AttrPadding(tomo.I(0)), + ), + }, + + // *.Slider[horizontal] + dataTheme.Rule { + Role: theme.R("", "Slider", "horizontal"), + Default: dataTheme.AS(dataTheme.AttrMinimumSize { X: 48 }), + }, + + // *.Slider[vertical] + dataTheme.Rule { + Role: theme.R("", "Slider", "vertical"), + Default: dataTheme.AS(dataTheme.AttrMinimumSize { Y: 48 }), + }, + + // *.SliderHandle[*] + dataTheme.Rule { + Role: theme.R("", "SliderHandle", ""), + Default: dataTheme.AS ( + dataTheme.AttrBorder { + outline, + tomo.Border { + Width: tomo.I(1), + Color: borderColorLifted, + }, + }, + dataTheme.AttrColor { Color: theme.ColorRaised }, + dataTheme.AttrMinimumSize { X: 12, Y: 12, }, + ), + }, +}