290 lines
6.9 KiB
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
|
|
}
|