bedrocktool/ui/gui/pages/packs/packs.go

241 lines
5.1 KiB
Go

package packs
import (
"fmt"
"image"
"image/color"
"sort"
"sync"
"gioui.org/f32"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget"
"gioui.org/widget/material"
"gioui.org/x/component"
"github.com/bedrock-tool/bedrocktool/ui/gui/pages"
"github.com/bedrock-tool/bedrocktool/ui/messages"
"github.com/bedrock-tool/bedrocktool/utils"
)
type (
C = layout.Context
D = layout.Dimensions
)
type Page struct {
*pages.Router
State messages.UIState
packsList widget.List
l sync.Mutex
Packs map[string]*packEntry
}
type packEntry struct {
IsFinished bool
UUID string
HasIcon bool
Icon paint.ImageOp
Size uint64
Loaded uint64
Name string
Path string
Err error
}
func New(router *pages.Router) *Page {
return &Page{
Router: router,
packsList: widget.List{
List: layout.List{
Axis: layout.Vertical,
},
},
Packs: make(map[string]*packEntry),
}
}
var _ pages.Page = &Page{}
func (p *Page) Actions() []component.AppBarAction {
return []component.AppBarAction{}
}
func (p *Page) Overflow() []component.OverflowAction {
return []component.OverflowAction{}
}
func (p *Page) NavItem() component.NavItem {
return component.NavItem{
Name: "Pack Download",
//Icon: icon.OtherIcon,
}
}
func drawPackIcon(ops *op.Ops, imageOp paint.ImageOp, bounds image.Point) {
imageOp.Add(ops)
s := imageOp.Size()
p := f32.Pt(float32(s.X), float32(s.Y))
p.X = 1 / (p.X / float32(bounds.X))
p.Y = 1 / (p.Y / float32(bounds.Y))
defer op.Affine(f32.Affine2D{}.Scale(f32.Pt(0, 0), p)).Push(ops).Pop()
paint.PaintOp{}.Add(ops)
}
func MulAlpha(c color.NRGBA, alpha uint8) color.NRGBA {
c.A = uint8(uint32(c.A) * uint32(alpha) / 0xFF)
return c
}
func drawPackEntry(gtx C, th *material.Theme, entry *packEntry) D {
var size = ""
var colorSize = th.Palette.Fg
if entry.IsFinished {
size = utils.SizeofFmt(float32(entry.Size))
} else {
size = fmt.Sprintf("%s / %s %.02f%%",
utils.SizeofFmt(float32(entry.Size)),
utils.SizeofFmt(float32(entry.Size)),
float32(entry.Loaded)/float32(entry.Size)*100,
)
colorSize = color.NRGBA{0x00, 0xc9, 0xc9, 0xff}
}
return layout.UniformInset(5).Layout(gtx, func(gtx C) D {
return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
layout.Rigid(func(gtx C) D {
s := image.Pt(50, 50)
if entry.HasIcon {
drawPackIcon(gtx.Ops, entry.Icon, s)
}
return D{Size: s.Add(image.Pt(10, 10))}
}),
layout.Flexed(1, func(gtx C) D {
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(material.Label(th, th.TextSize, entry.Name).Layout),
layout.Rigid(material.LabelStyle{
Text: size,
Color: colorSize,
SelectionColor: MulAlpha(th.Palette.ContrastBg, 0x60),
TextSize: th.TextSize,
Shaper: th.Shaper,
}.Layout),
layout.Rigid(func(gtx C) D {
if entry.Err != nil {
return material.LabelStyle{
Color: color.NRGBA{0xbb, 0x00, 0x00, 0xff},
Text: entry.Err.Error(),
}.Layout(gtx)
}
return D{}
}),
)
}),
)
})
}
func (p *Page) layoutFinished(gtx C, th *material.Theme) D {
return layout.Center.Layout(gtx, func(gtx C) D {
return layout.Flex{
Axis: layout.Vertical,
}.Layout(gtx,
layout.Rigid(material.Label(th, 20, "Downloaded Packs").Layout),
layout.Flexed(1, func(gtx C) D {
p.l.Lock()
defer p.l.Unlock()
keys := make([]string, 0, len(p.Packs))
for k := range p.Packs {
keys = append(keys, k)
}
sort.Strings(keys)
return material.List(th, &p.packsList).Layout(gtx, len(keys), func(gtx C, index int) D {
entry := p.Packs[keys[index]]
return drawPackEntry(gtx, th, entry)
})
}),
)
})
}
func (p *Page) Layout(gtx C, th *material.Theme) D {
margin := layout.Inset{
Top: unit.Dp(25),
Bottom: unit.Dp(25),
Right: unit.Dp(35),
Left: unit.Dp(35),
}
return margin.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
switch p.State {
case messages.UIStateMain:
return p.layoutFinished(gtx, th)
}
return layout.Dimensions{}
})
}
func (p *Page) Handler(data interface{}) messages.MessageResponse {
r := messages.MessageResponse{
Ok: false,
Data: nil,
}
switch m := data.(type) {
case messages.SetUIState:
p.State = m
p.Router.Invalidate()
r.Ok = true
case messages.InitialPacksInfo:
p.State = messages.UIStateMain
p.l.Lock()
for _, dp := range m.Packs {
e := &packEntry{
IsFinished: false,
UUID: dp.UUID,
Name: dp.SubPackName + "v" + dp.Version,
Size: dp.Size,
}
p.Packs[e.UUID] = e
}
p.l.Unlock()
p.Router.Invalidate()
case messages.PackDownloadProgress:
p.l.Lock()
e := p.Packs[m.UUID]
e.Loaded += m.LoadedAdd
if e.Loaded == e.Size {
e.IsFinished = true
}
p.l.Unlock()
p.Router.Invalidate()
case messages.FinishedDownloadingPacks:
p.l.Lock()
for _, dp := range m.Packs {
e := p.Packs[dp.UUID]
if dp.Icon != nil {
e.Icon = paint.NewImageOpFilter(dp.Icon, paint.FilterNearest)
e.HasIcon = true
}
e.Err = dp.Err
e.IsFinished = true
}
p.l.Unlock()
p.Router.Invalidate()
r.Ok = true
}
return r
}