241 lines
5.1 KiB
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
|
|
}
|