diff --git a/assets/mime-generic-medium.png b/assets/mime-generic-medium.png new file mode 100644 index 0000000..54a1194 Binary files /dev/null and b/assets/mime-generic-medium.png differ diff --git a/assets/mime-generic-medium.xcf b/assets/mime-generic-medium.xcf new file mode 100644 index 0000000..3c02bd8 Binary files /dev/null and b/assets/mime-generic-medium.xcf differ diff --git a/assets/mime-medium.png b/assets/mime-medium.png new file mode 100644 index 0000000..c4b56e1 Binary files /dev/null and b/assets/mime-medium.png differ diff --git a/go.mod b/go.mod index 12edc19..3a1e761 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module git.tebibyte.media/tomo/aluminum go 1.20 require ( - git.tebibyte.media/tomo/tomo v0.26.1 + git.tebibyte.media/tomo/tomo v0.27.0 golang.org/x/image v0.11.0 ) diff --git a/go.sum b/go.sum index 3d1664a..12b71c7 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.tebibyte.media/tomo/tomo v0.26.1 h1:V5ciRuixMYb79aAawgquFEfJ1icyEmMKBKFPWwi94NE= -git.tebibyte.media/tomo/tomo v0.26.1/go.mod h1:C9EzepS9wjkTJjnZaPBh22YvVPyA4hbBAJVU20Rdmps= +git.tebibyte.media/tomo/tomo v0.27.0 h1:gCwxQe0qm1hZLfHkMI3OccNMC/lB1cfs4BbaMz/bXug= +git.tebibyte.media/tomo/tomo v0.27.0/go.mod h1:C9EzepS9wjkTJjnZaPBh22YvVPyA4hbBAJVU20Rdmps= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= diff --git a/icons.go b/icons.go index f7aaf08..00ab1ed 100644 --- a/icons.go +++ b/icons.go @@ -1,56 +1,66 @@ package aluminum -import "fmt" -import "io/fs" +import "io" import "image" -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" -type iconSet struct { - fs fs.FS - icons map[theme.IconSize] map[string] canvas.Texture +type iconIndex interface { + ~int | ~string } -func (this *iconSet) get (size theme.IconSize, name string) canvas.Texture { - texture := this.icons[size][name] - if texture != nil { - return texture - } else { - texture = nil - defer func () { this.icons[size][name] = texture } () +type iconEntry[T iconIndex] struct { + position image.Point + index T +} - file, err := this.fs.Open(fmt.Sprint(size, "/", name, ".png")) - if err != nil { return texture } - defer file.Close() - image, _, err := image.Decode(file) - if err != nil { return texture } - - texture = tomo.NewTexture(image) - return texture +func i[T iconIndex] (x, y int, index T) iconEntry[T] { + return iconEntry[T] { + position: image.Pt(x, y), + index: index, } } -func (this *iconSet) Icon (id theme.Icon, size theme.IconSize) canvas.Texture { - return this.get(size, fmt.Sprint("actions/", id)) +type iconSet[T iconIndex] struct { + icons map[theme.IconSize] map[T] canvas.Texture } -func (this *iconSet) MimeIcon (mime data.Mime, size theme.IconSize) canvas.Texture { - texture := this.get(size, fmt.Sprint("mime/", mime)) - if texture != nil { - return texture - } else { - return this.get(size, fmt.Sprint("mime-generic/", mime.Type)) - } +func (this *iconSet[T]) Get (index T,size theme.IconSize) canvas.Texture { + return this.icons[size][index] } -func (this *iconSet) ApplicationIcon (id theme.ApplicationIcon, size theme.IconSize) canvas.Texture { - texture := this.get(size, fmt.Sprint("app/", id.Name)) - if texture != nil { - return texture - } else { - return this.get(size, fmt.Sprint("app-generic/", id.Role)) +func (this *iconSet[T]) Init ( + small, medium, large io.Reader, + rows, columns int, + icons ...iconEntry[T], +) { + this.icons = make(map[theme.IconSize] map[T] canvas.Texture) + this.initFor(theme.IconSizeSmall, small, rows, columns, icons...) + this.initFor(theme.IconSizeMedium, medium, rows, columns, icons...) + this.initFor(theme.IconSizeLarge, large, rows, columns, icons...) +} + +func (this *iconSet[T]) initFor ( + size theme.IconSize, + file io.Reader, + rows, columns int, + icons ...iconEntry[T], +) { + if file == nil { return } + + atlasImage, _, err := image.Decode(file) + if err != nil { panic(err) } + atlas := tomo.NewTexture(atlasImage) + cellW := atlasImage.Bounds().Dx() / columns + cellH := atlasImage.Bounds().Dy() / rows + cellSize := image.Rect(0, 0, cellW, cellH) + + this.icons[size] = make(map[T] canvas.Texture) + for _, icon := range icons { + offset := image.Pt ( + icon.position.X * cellW, + icon.position.Y * cellH) + this.icons[size][icon.index] = atlas.Clip(cellSize.Add(offset)) } } diff --git a/theme.go b/theme.go index a287fbb..c50862d 100644 --- a/theme.go +++ b/theme.go @@ -1,12 +1,22 @@ 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) @@ -45,7 +55,12 @@ var gutterColor = hex(0xbfc6d1FF) var gutterColorHovered = hex(0xc5cbd6FF) type Theme struct { - iconSet + 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) { @@ -215,7 +230,67 @@ func (this *Theme) Apply (object tomo.Object, role theme.Role) event.Cookie { } box.SetBorder(border...) box.SetColor(buttonColor) + + case "Icon": + switch role.Variant { + case "small": box.SetMinimumSize(image.Pt(16, 16)) + case "medium": box.SetMinimumSize(image.Pt(32, 32)) + case "large": box.SetMinimumSize(image.Pt(48, 48)) + } } 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 +}