Files

312 lines
7.2 KiB
Go

package assets
import (
"path"
"slices"
"strings"
"unicode"
"aego/asset"
"aego/asset/pipeline"
"aego/core/mathx"
"aego/editor/core/code/highlight"
"aego/ui/core"
"aego/ui/draw"
"aego/ui/widgets"
)
var tileSizes = [...]float32{48, 64, 96}
var tileLabels = [...]string{"S", "M", "L"}
func (p *Panel) tileSize() float32 {
if p.tile <= 0 {
return tileSizes[1]
}
return p.tile
}
func (p *Panel) SetTileSize(px float32) {
p.tile = px
}
func (p *Panel) tileW() float32 {
return p.tileSize() + 32
}
func (p *Panel) sizeButtons(r mathx.Rect) {
ui := p.f.UI
w := r.W / float32(len(tileSizes))
for i, s := range tileSizes {
on := p.tileSize() == s
if widgets.ToggleButton(ui, core.IDSeq(ui.ID("tilesize"), i), mathx.Rect{X: r.X + float32(i)*w, Y: r.Y, W: w - 2, H: r.H}, tileLabels[i], &on) {
p.tile = s
p.gridScroll = 0
}
}
}
func kindIcon(k asset.ArtifactKind) string {
switch k {
case asset.ArtScene:
return "file.scene"
case asset.ArtPrefab:
return "file.prefab"
case asset.ArtScript:
return "file.script"
case asset.ArtTexture, asset.ArtSpriteRegion, asset.ArtAtlasPage:
return "file.texture"
case asset.ArtData:
return "file.data"
case asset.ArtTable:
return "file.table"
case asset.ArtMesh:
return "file.mesh"
}
return "file.unknown"
}
func (p *Panel) kindColor(k asset.ArtifactKind) mathx.Color {
th := &p.f.UI.Theme
c := th.TextDim
switch k {
case asset.ArtScene:
c = th.Accent
case asset.ArtPrefab:
c = th.AxisY
case asset.ArtScript:
c = th.AxisZ
case asset.ArtData, asset.ArtTable:
c = th.Warn
case asset.ArtMesh:
c = th.AxisX
}
return c
}
func (p *Panel) checker(r mathx.Rect) {
ui := p.f.UI
th := &ui.Theme
ui.Rect(r, th.PanelAlt)
const cell = float32(8)
dark := th.Widget
ui.PushClip(r)
for y, row := r.Y, 0; y < r.Y+r.H; y, row = y+cell, row+1 {
for x, col := r.X, 0; x < r.X+r.W; x, col = x+cell, col+1 {
if (row+col)%2 == 0 {
ui.Rect(mathx.Rect{X: x, Y: y, W: cell, H: cell}, dark)
}
}
}
ui.PopClip()
}
func (p *Panel) schematic(a *pipeline.AssetInfo, img mathx.Rect) {
ui := p.f.UI
th := &ui.Theme
tint := p.kindColor(a.Kind)
rad := th.Radius.Button
ui.RoundRect(img, rad, th.Border)
card := mathx.Rect{X: img.X + 1, Y: img.Y + 1, W: img.W - 2, H: img.H - 2}
ui.RoundRect(card, rad, th.CardBg)
ui.List().RoundRect(mathx.Rect{X: card.X, Y: card.Y, W: card.W, H: 3}, rad, tint, draw.CornersTop)
if a.Kind == asset.ArtScript {
if lines := p.d.Thumbs.Lines(*a); len(lines) > 0 {
p.code(lines, mathx.Rect{X: card.X + 4, Y: card.Y + 6, W: card.W - 8, H: card.H - 9})
return
}
}
sub := ""
if a.Kind == asset.ArtData || a.Kind == asset.ArtTable {
if p.d.DataType != nil {
if t, ok := p.d.DataType(a.ID); ok {
sub = t[max(0, lastDot(t)+1):]
}
}
}
showSub := sub != "" && card.H >= 44
icon := min(card.W*0.38, 32)
cy := card.Y + 3 + (card.H-3-icon)/2
var small float32
if showSub {
ui.PushFont(ui.FontAt(11))
small = ui.LineHeight()
ui.PopFont()
cy -= small / 2
}
widgets.IconAt(ui, mathx.Rect{X: card.X + (card.W-icon)/2, Y: cy, W: icon, H: icon}, kindIcon(a.Kind), tint)
if showSub {
ui.PushFont(ui.FontAt(11))
sr := mathx.Rect{X: card.X + 3, Y: cy + icon + 2, W: card.W - 6, H: small}
ui.TextIn(sr, widgets.Elide(ui, sub, sr.W), th.TextDim, core.AlignCenter)
ui.PopFont()
return
}
p.badge(a, card, tint)
}
func lastDot(s string) int {
for i := len(s) - 1; i >= 0; i-- {
if s[i] == '.' {
return i
}
}
return -1
}
func (p *Panel) stack(dir string) []int {
if p.stackRev != p.rev || p.stacks == nil {
p.stacks = map[string][]int{}
p.stackRev = p.rev
}
if s, ok := p.stacks[dir]; ok {
return s
}
var out []int
for i := range p.items {
it := &p.items[i]
if path.Dir(it.Path) != dir || it.Kind == asset.ArtScript || it.Kind == asset.ArtData || it.Kind == asset.ArtTable {
continue
}
out = append(out, i)
if len(out) == 4 {
break
}
}
p.stacks[dir] = slices.Clip(out)
return p.stacks[dir]
}
func (p *Panel) folderStack(dir string, img mathx.Rect) {
ui := p.f.UI
th := &ui.Theme
list := p.stack(dir)
white := mathx.Color{R: 1, G: 1, B: 1, A: 1}
shown := 0
card := img.W * 0.62
for k := len(list) - 1; k >= 0; k-- {
it := &p.items[list[k]]
reg, ok := p.d.Thumbs.Get(*it)
if !ok {
continue
}
off := float32(k) * img.W * 0.1
cr := mathx.Rect{X: img.X + img.W - card - off, Y: img.Y + off, W: card, H: card}
ui.Rect(cr, th.PanelAlt)
ui.List().Texture(reg, fitRect(cr, reg.W, reg.H), white)
ui.Outline(cr, th.Border)
shown++
}
icon := img.W * 0.45
at := mathx.Rect{X: img.X, Y: img.Y + img.H - icon, W: icon, H: icon}
if shown == 0 {
icon = img.W * 0.7
at = mathx.Rect{X: img.X + (img.W-icon)/2, Y: img.Y + (img.H-icon)/2, W: icon, H: icon}
}
widgets.IconAt(ui, at, "file.folder", th.Accent)
}
func (p *Panel) code(lines []string, r mathx.Rect) {
ui := p.f.UI
th := &ui.Theme
pal := highlight.Palette(&th.Code)
ui.PushClip(r)
defer ui.PopClip()
y := r.Y
var st highlight.State
if name, kind, ok := declName(lines); ok && r.W >= 80 {
ui.PushFont(ui.MonoFontAt(11))
ui.Text(widgets.Elide(ui, name, r.W), mathx.Vec2{X: r.X, Y: y}, pal[kind])
y += ui.LineHeight() + 3
ui.PopFont()
p.spans, st = highlight.Line([]byte(lines[0]), st, p.spans)
lines = lines[1:]
}
lh := max(float32(2.5), r.W/24)
cw := lh * 0.5
bar := max(float32(1.5), lh*0.55)
for _, l := range lines {
if y+bar > r.Y+r.H {
break
}
p.spans, st = highlight.Line([]byte(l), st, p.spans)
for _, sp := range p.spans {
seg := l[sp.Start:sp.End]
col := pal[sp.Kind]
col.A = 0.8
start := -1
for i := 0; i <= len(seg); i++ {
if i < len(seg) && seg[i] != ' ' {
if start < 0 {
start = i
}
continue
}
if start >= 0 {
x := r.X + float32(sp.Start+start)*cw
if x < r.X+r.W {
ui.Rect(mathx.Rect{X: x, Y: y, W: min(float32(i-start)*cw, r.X+r.W-x), H: bar}, col)
}
start = -1
}
}
}
y += lh
}
}
func declName(lines []string) (string, highlight.Kind, bool) {
if len(lines) == 0 {
return "", 0, false
}
kw, rest, _ := strings.Cut(strings.TrimSpace(lines[0]), " ")
kind := highlight.Kind(0)
switch kw {
case "func":
kind = highlight.Function
if strings.HasPrefix(rest, "(") {
if _, after, ok := strings.Cut(rest, ")"); ok {
rest, kind = strings.TrimSpace(after), highlight.Method
}
}
case "const":
kind = highlight.Constant
case "var":
kind = highlight.Variable
case "type":
kind = highlight.Type
default:
return "", 0, false
}
end := strings.IndexFunc(rest, func(r rune) bool {
return r != '_' && !unicode.IsLetter(r) && !unicode.IsDigit(r)
})
if end >= 0 {
rest = rest[:end]
}
return rest, kind, rest != ""
}
func (p *Panel) badge(a *pipeline.AssetInfo, card mathx.Rect, tint mathx.Color) {
if card.W < 60 {
return
}
ui := p.f.UI
ext := strings.TrimPrefix(path.Ext(a.Path), ".")
if len(ext) > 4 && strings.HasPrefix(ext, "ae") {
ext = ext[2:]
}
ext = strings.ToUpper(ext)
if ext == "" {
return
}
ui.PushFont(ui.MonoFontAt(10))
sz := ui.TextSize(ext)
br := mathx.Rect{X: card.X + card.W - sz.X - 9, Y: card.Y + card.H - sz.Y - 5, W: sz.X + 6, H: sz.Y + 2}
bg := tint
bg.A = 0.85
ui.RoundRect(br, 3, bg)
ui.Text(ext, mathx.Vec2{X: br.X + 3, Y: br.Y + 1}, ui.Theme.CardBg)
ui.PopFont()
}