I am going insane
This commit is contained in:
50
default/config/config.go
Normal file
50
default/config/config.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package config
|
||||
|
||||
import "git.tebibyte.media/sashakoshka/tomo"
|
||||
|
||||
// Default specifies default configuration values.
|
||||
type Default struct { }
|
||||
|
||||
|
||||
// HandleWidth returns the default handle width value.
|
||||
func (Default) HandleWidth () int {
|
||||
return 15
|
||||
}
|
||||
|
||||
// ScrollVelocity returns the default scroll velocity value.
|
||||
func (Default) ScrollVelocity () int {
|
||||
return 16
|
||||
}
|
||||
|
||||
// ThemePath returns the default theme path.
|
||||
func (Default) ThemePath () (string) {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Wrapped wraps a configuration and uses Default if it is nil.
|
||||
type Wrapped struct {
|
||||
tomo.Config
|
||||
}
|
||||
|
||||
// HandleWidth returns how large grab handles should typically be. This
|
||||
// is important for accessibility reasons.
|
||||
func (wrapped Wrapped) HandleWidth () int {
|
||||
return wrapped.ensure().HandleWidth()
|
||||
}
|
||||
|
||||
// ScrollVelocity returns how many pixels should be scrolled every time
|
||||
// a scroll button is pressed.
|
||||
func (wrapped Wrapped) ScrollVelocity () int {
|
||||
return wrapped.ensure().ScrollVelocity()
|
||||
}
|
||||
|
||||
// ThemePath returns the directory path to the theme.
|
||||
func (wrapped Wrapped) ThemePath () string {
|
||||
return wrapped.ensure().ThemePath()
|
||||
}
|
||||
|
||||
func (wrapped Wrapped) ensure () (real tomo.Config) {
|
||||
real = wrapped.Config
|
||||
if real == nil { real = Default { } }
|
||||
return
|
||||
}
|
||||
2
default/config/doc.go
Normal file
2
default/config/doc.go
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package config implements a default configuration.
|
||||
package config
|
||||
9
default/config/parse.go
Normal file
9
default/config/parse.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
// import "io"
|
||||
|
||||
// Parse parses one or more configuration files and returns them as a Config.
|
||||
// func Parse (sources ...io.Reader) (config tomo.Config) {
|
||||
// // TODO
|
||||
// return Default { }
|
||||
// }
|
||||
BIN
default/theme/assets/wintergreen-icons-large.png
Normal file
BIN
default/theme/assets/wintergreen-icons-large.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
BIN
default/theme/assets/wintergreen-icons-small.png
Normal file
BIN
default/theme/assets/wintergreen-icons-small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
default/theme/assets/wintergreen.png
Normal file
BIN
default/theme/assets/wintergreen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
298
default/theme/default.go
Normal file
298
default/theme/default.go
Normal file
@@ -0,0 +1,298 @@
|
||||
package theme
|
||||
|
||||
import "image"
|
||||
import "bytes"
|
||||
import _ "embed"
|
||||
import _ "image/png"
|
||||
import "image/color"
|
||||
import "golang.org/x/image/font"
|
||||
import "git.tebibyte.media/sashakoshka/tomo"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/data"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/defaultfont"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/artist/patterns"
|
||||
|
||||
//go:embed assets/wintergreen.png
|
||||
var defaultAtlasBytes []byte
|
||||
var defaultAtlas canvas.Canvas
|
||||
var defaultTextures [14][9]artist.Pattern
|
||||
//go:embed assets/wintergreen-icons-small.png
|
||||
var defaultIconsSmallAtlasBytes []byte
|
||||
var defaultIconsSmall [640]binaryIcon
|
||||
//go:embed assets/wintergreen-icons-large.png
|
||||
var defaultIconsLargeAtlasBytes []byte
|
||||
var defaultIconsLarge [640]binaryIcon
|
||||
|
||||
func atlasCell (col, row int, border artist.Inset) {
|
||||
bounds := image.Rect(0, 0, 16, 16).Add(image.Pt(col, row).Mul(16))
|
||||
defaultTextures[col][row] = patterns.Border {
|
||||
Canvas: canvas.Cut(defaultAtlas, bounds),
|
||||
Inset: border,
|
||||
}
|
||||
}
|
||||
|
||||
func atlasCol (col int, border artist.Inset) {
|
||||
for index, _ := range defaultTextures[col] {
|
||||
atlasCell(col, index, border)
|
||||
}
|
||||
}
|
||||
|
||||
type binaryIcon struct {
|
||||
data []bool
|
||||
stride int
|
||||
}
|
||||
|
||||
func (icon binaryIcon) Draw (destination canvas.Canvas, color color.RGBA, at image.Point) {
|
||||
bounds := icon.Bounds().Add(at).Intersect(destination.Bounds())
|
||||
point := image.Point { }
|
||||
data, stride := destination.Buffer()
|
||||
|
||||
for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ {
|
||||
for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ {
|
||||
srcPoint := point.Sub(at)
|
||||
srcIndex := srcPoint.X + srcPoint.Y * icon.stride
|
||||
dstIndex := point.X + point.Y * stride
|
||||
if icon.data[srcIndex] {
|
||||
data[dstIndex] = color
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
func (icon binaryIcon) Bounds () image.Rectangle {
|
||||
return image.Rect(0, 0, icon.stride, len(icon.data) / icon.stride)
|
||||
}
|
||||
|
||||
func binaryIconFrom (source image.Image, clip image.Rectangle) (icon binaryIcon) {
|
||||
bounds := source.Bounds().Intersect(clip)
|
||||
if bounds.Empty() { return }
|
||||
|
||||
icon.stride = bounds.Dx()
|
||||
icon.data = make([]bool, bounds.Dx() * bounds.Dy())
|
||||
|
||||
point := image.Point { }
|
||||
dstIndex := 0
|
||||
for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ {
|
||||
for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ {
|
||||
r, g, b, a := source.At(point.X, point.Y).RGBA()
|
||||
if a > 0x8000 && (r + g + b) / 3 < 0x8000 {
|
||||
icon.data[dstIndex] = true
|
||||
}
|
||||
dstIndex ++
|
||||
}}
|
||||
return
|
||||
}
|
||||
|
||||
func init () {
|
||||
defaultAtlasImage, _, _ := image.Decode(bytes.NewReader(defaultAtlasBytes))
|
||||
defaultAtlas = canvas.FromImage(defaultAtlasImage)
|
||||
|
||||
// PatternDead
|
||||
atlasCol(0, artist.Inset { })
|
||||
// PatternRaised
|
||||
atlasCol(1, artist.Inset { 6, 6, 6, 6 })
|
||||
// PatternSunken
|
||||
atlasCol(2, artist.Inset { 4, 4, 4, 4 })
|
||||
// PatternPinboard
|
||||
atlasCol(3, artist.Inset { 2, 2, 2, 2 })
|
||||
// PatternButton
|
||||
atlasCol(4, artist.Inset { 6, 6, 6, 6 })
|
||||
// PatternInput
|
||||
atlasCol(5, artist.Inset { 4, 4, 4, 4 })
|
||||
// PatternGutter
|
||||
atlasCol(6, artist.Inset { 7, 7, 7, 7 })
|
||||
// PatternHandle
|
||||
atlasCol(7, artist.Inset { 3, 3, 3, 3 })
|
||||
// PatternLine
|
||||
atlasCol(8, artist.Inset { 1, 1, 1, 1 })
|
||||
// PatternMercury
|
||||
atlasCol(13, artist.Inset { 2, 2, 2, 2 })
|
||||
|
||||
// PatternButton: basic.checkbox
|
||||
atlasCol(9, artist.Inset { 3, 3, 3, 3 })
|
||||
// PatternRaised: basic.listEntry
|
||||
atlasCol(10, artist.Inset { 3, 3, 3, 3 })
|
||||
// PatternRaised: fun.flatKey
|
||||
atlasCol(11, artist.Inset { 3, 3, 5, 3 })
|
||||
// PatternRaised: fun.sharpKey
|
||||
atlasCol(12, artist.Inset { 3, 3, 4, 3 })
|
||||
|
||||
// set up small icons
|
||||
defaultIconsSmallAtlasImage, _, _ := image.Decode (
|
||||
bytes.NewReader(defaultIconsSmallAtlasBytes))
|
||||
point := image.Point { }
|
||||
iconIndex := 0
|
||||
for point.Y = 0; point.Y < 20; point.Y ++ {
|
||||
for point.X = 0; point.X < 32; point.X ++ {
|
||||
defaultIconsSmall[iconIndex] = binaryIconFrom (
|
||||
defaultIconsSmallAtlasImage,
|
||||
image.Rect(0, 0, 16, 16).Add(point.Mul(16)))
|
||||
iconIndex ++
|
||||
}}
|
||||
|
||||
// set up large icons
|
||||
defaultIconsLargeAtlasImage, _, _ := image.Decode (
|
||||
bytes.NewReader(defaultIconsLargeAtlasBytes))
|
||||
point = image.Point { }
|
||||
iconIndex = 0
|
||||
for point.Y = 0; point.Y < 8; point.Y ++ {
|
||||
for point.X = 0; point.X < 32; point.X ++ {
|
||||
defaultIconsLarge[iconIndex] = binaryIconFrom (
|
||||
defaultIconsLargeAtlasImage,
|
||||
image.Rect(0, 0, 32, 32).Add(point.Mul(32)))
|
||||
iconIndex ++
|
||||
}}
|
||||
iconIndex = 384
|
||||
for point.Y = 8; point.Y < 12; point.Y ++ {
|
||||
for point.X = 0; point.X < 32; point.X ++ {
|
||||
defaultIconsLarge[iconIndex] = binaryIconFrom (
|
||||
defaultIconsLargeAtlasImage,
|
||||
image.Rect(0, 0, 32, 32).Add(point.Mul(32)))
|
||||
iconIndex ++
|
||||
}}
|
||||
}
|
||||
|
||||
// Default is the default theme.
|
||||
type Default struct { }
|
||||
|
||||
// FontFace returns the default font face.
|
||||
func (Default) FontFace (style tomo.FontStyle, size tomo.FontSize, c tomo.Case) font.Face {
|
||||
switch style {
|
||||
case tomo.FontStyleBold:
|
||||
return defaultfont.FaceBold
|
||||
case tomo.FontStyleItalic:
|
||||
return defaultfont.FaceItalic
|
||||
case tomo.FontStyleBoldItalic:
|
||||
return defaultfont.FaceBoldItalic
|
||||
default:
|
||||
return defaultfont.FaceRegular
|
||||
}
|
||||
}
|
||||
|
||||
// Icon returns an icon from the default set corresponding to the given name.
|
||||
func (Default) Icon (id tomo.Icon, size tomo.IconSize, c tomo.Case) artist.Icon {
|
||||
if size == tomo.IconSizeLarge {
|
||||
if id < 0 || int(id) >= len(defaultIconsLarge) {
|
||||
return nil
|
||||
} else {
|
||||
return defaultIconsLarge[id]
|
||||
}
|
||||
} else {
|
||||
if id < 0 || int(id) >= len(defaultIconsSmall) {
|
||||
return nil
|
||||
} else {
|
||||
return defaultIconsSmall[id]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MimeIcon returns an icon from the default set corresponding to the given mime.
|
||||
// type.
|
||||
func (Default) MimeIcon (data.Mime, tomo.IconSize, tomo.Case) artist.Icon {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pattern returns a pattern from the default theme corresponding to the given
|
||||
// pattern ID.
|
||||
func (Default) Pattern (id tomo.Pattern, state tomo.State, c tomo.Case) artist.Pattern {
|
||||
offset := 0; switch {
|
||||
case state.Disabled: offset = 1
|
||||
case state.Pressed && state.On: offset = 4
|
||||
case state.Focused && state.On: offset = 7
|
||||
case state.Invalid && state.On: offset = 8
|
||||
case state.On: offset = 2
|
||||
case state.Pressed: offset = 3
|
||||
case state.Focused: offset = 5
|
||||
case state.Invalid: offset = 6
|
||||
}
|
||||
|
||||
switch id {
|
||||
case tomo.PatternBackground: return patterns.Uhex(0xaaaaaaFF)
|
||||
case tomo.PatternDead: return defaultTextures[0][offset]
|
||||
case tomo.PatternRaised:
|
||||
if c.Match("tomo", "listEntry", "") {
|
||||
return defaultTextures[10][offset]
|
||||
} else {
|
||||
return defaultTextures[1][offset]
|
||||
}
|
||||
case tomo.PatternSunken: return defaultTextures[2][offset]
|
||||
case tomo.PatternPinboard: return defaultTextures[3][offset]
|
||||
case tomo.PatternButton:
|
||||
switch {
|
||||
case c.Match("tomo", "checkbox", ""):
|
||||
return defaultTextures[9][offset]
|
||||
case c.Match("tomo", "piano", "flatKey"):
|
||||
return defaultTextures[11][offset]
|
||||
case c.Match("tomo", "piano", "sharpKey"):
|
||||
return defaultTextures[12][offset]
|
||||
default:
|
||||
return defaultTextures[4][offset]
|
||||
}
|
||||
case tomo.PatternInput: return defaultTextures[5][offset]
|
||||
case tomo.PatternGutter: return defaultTextures[6][offset]
|
||||
case tomo.PatternHandle: return defaultTextures[7][offset]
|
||||
case tomo.PatternLine: return defaultTextures[8][offset]
|
||||
case tomo.PatternMercury: return defaultTextures[13][offset]
|
||||
default: return patterns.Uhex(0xFF00FFFF)
|
||||
}
|
||||
}
|
||||
|
||||
func (Default) Color (id tomo.Color, state tomo.State, c tomo.Case) color.RGBA {
|
||||
if state.Disabled {
|
||||
return artist.Hex(0x444444FF)
|
||||
} else {
|
||||
switch id {
|
||||
case tomo.ColorAccent: return artist.Hex(0x408090FF)
|
||||
case tomo.ColorForeground: return artist.Hex(0x000000FF)
|
||||
default: return artist.Hex(0x888888FF)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Padding returns the default padding value for the given pattern.
|
||||
func (Default) Padding (id tomo.Pattern, c tomo.Case) artist.Inset {
|
||||
switch id {
|
||||
case tomo.PatternRaised:
|
||||
if c.Match("tomo", "listEntry", "") {
|
||||
return artist.I(4, 8)
|
||||
} else {
|
||||
return artist.I(8)
|
||||
}
|
||||
case tomo.PatternSunken:
|
||||
if c.Match("tomo", "list", "") {
|
||||
return artist.I(4, 0, 3)
|
||||
} else if c.Match("basic", "progressBar", "") {
|
||||
return artist.I(2, 1, 1, 2)
|
||||
} else {
|
||||
return artist.I(8)
|
||||
}
|
||||
case tomo.PatternPinboard:
|
||||
if c.Match("tomo", "piano", "") {
|
||||
return artist.I(2)
|
||||
} else {
|
||||
return artist.I(8)
|
||||
}
|
||||
case tomo.PatternGutter: return artist.I(0)
|
||||
case tomo.PatternLine: return artist.I(1)
|
||||
case tomo.PatternMercury: return artist.I(5)
|
||||
default: return artist.I(8)
|
||||
}
|
||||
}
|
||||
|
||||
// Margin returns the default margin value for the given pattern.
|
||||
func (Default) Margin (id tomo.Pattern, c tomo.Case) image.Point {
|
||||
return image.Pt(8, 8)
|
||||
}
|
||||
|
||||
// Hints returns rendering optimization hints for a particular pattern.
|
||||
// These are optional, but following them may result in improved
|
||||
// performance.
|
||||
func (Default) Hints (pattern tomo.Pattern, c tomo.Case) (hints tomo.Hints) {
|
||||
return
|
||||
}
|
||||
|
||||
// Sink returns the default sink vector for the given pattern.
|
||||
func (Default) Sink (pattern tomo.Pattern, c tomo.Case) image.Point {
|
||||
return image.Point { 1, 1 }
|
||||
}
|
||||
2
default/theme/doc.go
Normal file
2
default/theme/doc.go
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package theme implements a default theme.
|
||||
package theme
|
||||
9
default/theme/parse.go
Normal file
9
default/theme/parse.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package theme
|
||||
|
||||
// import "io"
|
||||
|
||||
// Parse parses one or more theme files and returns them as a Theme.
|
||||
// func Parse (sources ...io.Reader) (Theme) {
|
||||
// // TODO
|
||||
// return Default { }
|
||||
// }
|
||||
82
default/theme/wrapped.go
Normal file
82
default/theme/wrapped.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package theme
|
||||
|
||||
import "image"
|
||||
import "image/color"
|
||||
import "golang.org/x/image/font"
|
||||
import "git.tebibyte.media/sashakoshka/tomo"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/data"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
||||
|
||||
// Wrapped wraps any theme and injects a case into it automatically so that it
|
||||
// doesn't need to be specified for each query. Additionally, if the underlying
|
||||
// theme is nil, it just uses the default theme instead.
|
||||
type Wrapped struct {
|
||||
tomo.Theme
|
||||
tomo.Case
|
||||
}
|
||||
|
||||
// FontFace returns the proper font for a given style and size.
|
||||
func (wrapped Wrapped) FontFace (style tomo.FontStyle, size tomo.FontSize) font.Face {
|
||||
real := wrapped.ensure()
|
||||
return real.FontFace(style, size, wrapped.Case)
|
||||
}
|
||||
|
||||
// Icon returns an appropriate icon given an icon name.
|
||||
func (wrapped Wrapped) Icon (id tomo.Icon, size tomo.IconSize) artist.Icon {
|
||||
real := wrapped.ensure()
|
||||
return real.Icon(id, size, wrapped.Case)
|
||||
}
|
||||
|
||||
// MimeIcon returns an appropriate icon given file mime type.
|
||||
func (wrapped Wrapped) MimeIcon (mime data.Mime, size tomo.IconSize) artist.Icon {
|
||||
real := wrapped.ensure()
|
||||
return real.MimeIcon(mime, size, wrapped.Case)
|
||||
}
|
||||
|
||||
// Pattern returns an appropriate pattern given a pattern name and state.
|
||||
func (wrapped Wrapped) Pattern (id tomo.Pattern, state tomo.State) artist.Pattern {
|
||||
real := wrapped.ensure()
|
||||
return real.Pattern(id, state, wrapped.Case)
|
||||
}
|
||||
|
||||
// Color returns an appropriate color given a color name and state.
|
||||
func (wrapped Wrapped) Color (id tomo.Color, state tomo.State) color.RGBA {
|
||||
real := wrapped.ensure()
|
||||
return real.Color(id, state, wrapped.Case)
|
||||
}
|
||||
|
||||
// Padding returns how much space should be between the bounds of a
|
||||
// pattern whatever an element draws inside of it.
|
||||
func (wrapped Wrapped) Padding (id tomo.Pattern) artist.Inset {
|
||||
real := wrapped.ensure()
|
||||
return real.Padding(id, wrapped.Case)
|
||||
}
|
||||
|
||||
// Margin returns the left/right (x) and top/bottom (y) margins that
|
||||
// should be put between any self-contained objects drawn within this
|
||||
// pattern (if applicable).
|
||||
func (wrapped Wrapped) Margin (id tomo.Pattern) image.Point {
|
||||
real := wrapped.ensure()
|
||||
return real.Margin(id, wrapped.Case)
|
||||
}
|
||||
|
||||
// Sink returns a vector that should be added to an element's inner content when
|
||||
// it is pressed down (if applicable) to simulate a 3D sinking effect.
|
||||
func (wrapped Wrapped) Sink (id tomo.Pattern) image.Point {
|
||||
real := wrapped.ensure()
|
||||
return real.Sink(id, wrapped.Case)
|
||||
}
|
||||
|
||||
// Hints returns rendering optimization hints for a particular pattern.
|
||||
// These are optional, but following them may result in improved
|
||||
// performance.
|
||||
func (wrapped Wrapped) Hints (id tomo.Pattern) tomo.Hints {
|
||||
real := wrapped.ensure()
|
||||
return real.Hints(id, wrapped.Case)
|
||||
}
|
||||
|
||||
func (wrapped Wrapped) ensure () (real tomo.Theme) {
|
||||
real = wrapped.Theme
|
||||
if real == nil { real = Default { } }
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user