aluminum/theme.go

290 lines
6.9 KiB
Go

package aluminum
import "bytes"
import "image"
import _ "embed"
import _ "image/png"
import "image/color"
import "git.tebibyte.media/tomo/tomo"
import "golang.org/x/image/font/basicfont"
import "git.tebibyte.media/tomo/tomo/data"
import "git.tebibyte.media/tomo/tomo/theme"
import "git.tebibyte.media/tomo/tomo/input"
import "git.tebibyte.media/tomo/tomo/event"
import "git.tebibyte.media/tomo/tomo/canvas"
//go:embed assets/mime-generic-medium.png
var genericMimeIconsMedium []byte
//go:embed assets/mime-medium.png
var mimeIconsMedium []byte
func hex (color uint32) (c color.RGBA) {
c.A = uint8(color)
c.B = uint8(color >> 8)
c.G = uint8(color >> 16)
c.R = uint8(color >> 24)
return
}
func border (top, right, bottom, left uint32, width ...int) tomo.Border {
return tomo.Border {
Width: tomo.I(width...),
Color: [4]color.Color {
hex(top), hex(right),
hex(bottom), hex(left),
},
}
}
var engravedBorder = border(0xc3c3c5FF, 0xe3e3e3FF, 0xe9e9e9ff, 0xc2c2c2ff, 1)
var gapBorder = border(0x697c7cFF, 0x566767FF, 0x566767ff, 0x697c7cff, 1)
var focusedBorder = border(0x5f8bc4FF, 0x5f8bc4FF, 0x5f8bc4ff, 0x5f8bc4ff, 1)
var buttonBorder = border(0xf9fafcFF, 0xc2c8d3FF, 0xa4afc0ff, 0xf5f6f8ff, 1)
var sunkenShadow = border(0xa4afc0FF, 0xa4afc0FF, 0xa4afc0ff, 0xa4afc0ff, 1, 0, 0, 1)
var outerShadow = border(0xa4afc0FF, 0xa4afc0FF, 0xa4afc0ff, 0xa4afc0ff, 0, 1, 1, 0)
var buttonColor = hex(0xe9eaeaFF)
var buttonColorPressed = hex(0xe3e4e4FF)
var buttonColorFocused = hex(0xe4e6e8FF)
var buttonColorHovered = hex(0xf1f3f5FF)
var dotColor = hex(0x7391c080)
var inputColor = hex(0xe3e4e4FF)
var textColor = hex(0x000000FF)
var backgroundColor = hex(0xd6d6d6FF)
var gutterColor = hex(0xbfc6d1FF)
var gutterColorHovered = hex(0xc5cbd6FF)
type Theme struct {
unpackedIcons bool
icons iconSet[theme.Icon]
mimeIcons iconSet[string]
genericMimeIcons iconSet[string]
appIcons iconSet[string]
genericAppIcons iconSet[tomo.ApplicationRole]
}
func (this *Theme) RGBA (id theme.Color) (r, g, b, a uint32) {
switch id {
case theme.ColorBackground: return backgroundColor .RGBA()
case theme.ColorForeground: return textColor .RGBA()
case theme.ColorRaised: return buttonColor .RGBA()
case theme.ColorSunken: return inputColor .RGBA()
case theme.ColorAccent: return hex(0x5f8bc4FF) .RGBA()
default: return color.Transparent.RGBA()
}
}
func (this *Theme) Apply (object tomo.Object, role theme.Role) event.Cookie {
box := object.GetBox()
if textBox, ok := box.(tomo.TextBox); ok {
textBox.SetDotColor(dotColor)
textBox.SetTextColor(textColor)
textBox.SetFace(basicfont.Face7x13)
}
if containerBox, ok := box.(tomo.ContainerBox); ok {
containerBox.SetGap(image.Pt(8, 8))
}
switch role.Object {
case "Button":
pressed := false
hovered := false
updateStyle := func () {
border := []tomo.Border {
engravedBorder,
gapBorder,
buttonBorder,
}
padding := tomo.I(4, 8)
box.SetColor(buttonColor)
if box.Focused() {
box.SetColor(buttonColorFocused)
border[1] = focusedBorder
}
if pressed {
box.SetColor(buttonColorPressed)
padding = tomo.I(5, 8, 4, 9)
border[2] = sunkenShadow
} else if hovered {
box.SetColor(buttonColorHovered)
}
box.SetBorder(border...)
box.SetPadding(padding)
}
updateStyle()
return event.MultiCookie (
box.OnMouseDown(func (button input.Button) {
if button != input.ButtonLeft { return }
pressed = true
updateStyle()
}),
box.OnMouseUp(func (button input.Button) {
if button != input.ButtonLeft { return }
pressed = false
updateStyle()
}),
box.OnFocusEnter(updateStyle),
box.OnFocusLeave(updateStyle),
box.OnMouseEnter(func () {
hovered = true
updateStyle()
}),
box.OnMouseLeave(func () {
hovered = false
updateStyle()
}))
case "TextInput", "NumberInput":
box.SetPadding(tomo.I(5, 4, 4, 5))
updateStyle := func () {
border := []tomo.Border {
engravedBorder,
gapBorder,
sunkenShadow,
}
box.SetColor(inputColor)
if box.Focused() {
border[1] = focusedBorder
}
box.SetBorder(border...)
}
updateStyle()
return event.MultiCookie (
box.OnFocusEnter(updateStyle),
box.OnFocusLeave(updateStyle))
case "Container":
if role.Variant == "outer" {
box.SetColor(backgroundColor)
box.SetPadding(tomo.I(8))
}
case "Heading":
if textBox, ok := box.(tomo.TextBox); ok {
textBox.SetAlign(tomo.AlignMiddle, tomo.AlignMiddle)
}
case "Separator":
box.SetBorder(engravedBorder)
case "Slider":
pressed := false
hovered := false
if role.Variant == "vertical" {
box.SetMinimumSize(image.Pt(0, 48))
} else {
box.SetMinimumSize(image.Pt(48, 0))
}
updateStyle := func () {
border := []tomo.Border {
engravedBorder,
gapBorder,
sunkenShadow,
}
box.SetColor(gutterColor)
if box.Focused() {
border[1] = focusedBorder
}
if hovered && !pressed {
box.SetColor(gutterColorHovered)
}
box.SetBorder(border...)
}
updateStyle()
return event.MultiCookie (
box.OnFocusEnter(updateStyle),
box.OnFocusLeave(updateStyle),
box.OnMouseDown(func (button input.Button) {
pressed = true
updateStyle()
}),
box.OnMouseUp(func (button input.Button) {
pressed = false
updateStyle()
}),
box.OnMouseEnter(func () {
hovered = true
updateStyle()
}),
box.OnMouseLeave(func () {
hovered = false
updateStyle()
}))
case "SliderHandle":
box.SetMinimumSize(image.Pt(12, 12))
border := []tomo.Border {
outerShadow,
gapBorder,
buttonBorder,
}
box.SetBorder(border...)
box.SetColor(buttonColor)
}
return event.MultiCookie()
}
func (this *Theme) Icon (id theme.Icon, size theme.IconSize) canvas.Texture {
this.ensureIcons()
return this.icons.Get(id, size)
}
func (this *Theme) MimeIcon (mime data.Mime, size theme.IconSize) canvas.Texture {
this.ensureIcons()
texture := this.mimeIcons.Get(mime.String(), size)
if texture != nil {
return texture
} else {
return this.genericMimeIcons.Get(mime.Type, size)
}
}
func (this *Theme) ApplicationIcon (id theme.ApplicationIcon, size theme.IconSize) canvas.Texture {
this.ensureIcons()
texture := this.appIcons.Get(id.Name, size)
if texture != nil {
return texture
} else {
return this.genericAppIcons.Get(id.Role, size)
}
}
func (this *Theme) ensureIcons () {
if this.unpackedIcons { return }
this.unpackedIcons = true
// this.icons.Init () // TODO
this.mimeIcons.Init (
nil, // TODO
bytes.NewReader(mimeIconsMedium),
nil, // TODO
32, 12,
// TODO
)
this.genericMimeIcons.Init (
nil, // TODO
bytes.NewReader(genericMimeIconsMedium),
nil, // TODO
1, 5,
i(0, 0, "text"),
i(1, 0, "image"),
i(2, 0, "audio"),
i(3, 0, "video"),
i(4, 0, "application"),
)
// this.appIcons.Init () // TODO
// this.genericAppIcons.Init () // TODO
}