doodle/pkg/windows/filesystem_window.go
Noah Petherbridge c5e3fc297c Manage Embedded Files In Levels
In the Level Editor, the "Level->Attached files" menu opens the
FileSystem Window, which shows a paginated list of attached files and a
"Delete" button to remove them.

- Custom doodads which also exist locally can be deleted from the
  level's filesystem at any time.
- If a custom doodad does NOT exist locally, and one of them is still
  placed somewhere within the level, you can not delete it.
- You can't delete the custom wallpaper image IF the level is still
  using it. Change to a default wallpaper and then you can delete the
  custom wallpaper image.
2021-06-13 16:03:32 -07:00

261 lines
5.1 KiB
Go

package windows
import (
"fmt"
"math"
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/level"
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/go/render"
"git.kirsle.net/go/ui"
)
// FileSystem window shows the file attachments to the current level.
type FileSystem struct {
// Settings passed in by doodle
Supervisor *ui.Supervisor
Engine render.Engine
Level *level.Level
OnDelete func(filename string) bool
OnCancel func()
// Private vars.
includeBuiltins bool // show built-in doodads in checkbox-list.
}
// NewFileSystemWindow initializes the window.
func NewFileSystemWindow(cfg FileSystem) *ui.Window {
var (
windowColor = render.RGBA(255, 255, 200, 255)
windowTitleColor = render.RGBA(255, 153, 0, 255)
windowWidth = 380
windowHeight = 360
page = 1
perPage = 6
pages = 1
maxPageButtons = 8
// columns and sizes to draw the doodad list
btnHeight = 14
)
window := ui.NewWindow("Attached Files")
window.SetButtons(ui.CloseButton)
window.ActiveTitleBackground = windowTitleColor // TODO: not working?
window.InactiveTitleBackground = windowTitleColor.Darken(60)
window.InactiveTitleForeground = render.Grey
window.Configure(ui.Config{
Width: windowWidth,
Height: windowHeight,
Background: windowColor,
})
/////////////
// Intro text
introFrame := ui.NewFrame("Intro Frame")
window.Pack(introFrame, ui.Pack{
Side: ui.N,
FillX: true,
})
lines := []struct {
Text string
Font render.Text
}{
{
Text: "About",
Font: balance.LabelFont,
},
{
Text: "These are the files embedded inside your level data. When\n" +
"a level is Published, it can attach all of its custom doodads,\n" +
"wallpapers or other custom asset so that it easily plays on\n" +
"a different computer.",
Font: balance.UIFont,
},
{
Text: "File Attachments",
Font: balance.LabelFont,
},
}
for n, row := range lines {
frame := ui.NewFrame(fmt.Sprintf("Intro Line %d", n))
introFrame.Pack(frame, ui.Pack{
Side: ui.N,
FillX: true,
})
label := ui.NewLabel(ui.Label{
Text: row.Text,
Font: row.Font,
})
frame.Pack(label, ui.Pack{
Side: ui.W,
})
}
/////////////
// Attached files table.
fsFrame := ui.NewFrame("Doodads Frame")
fsFrame.Resize(render.Rect{
W: windowWidth,
H: btnHeight*perPage + 140,
})
window.Pack(fsFrame, ui.Pack{
Side: ui.N,
FillX: true,
})
var fileRows = []*ui.Frame{}
// Get the file attachments.
files := cfg.Level.ListFiles()
for _, file := range files {
file := file
row := ui.NewFrame("Row: " + file)
label := ui.NewLabel(ui.Label{
Text: file,
Font: balance.UIFont,
})
row.Pack(label, ui.Pack{
Side: ui.W,
Padding: 1,
})
delBtn := ui.NewButton("Delete: "+file, ui.NewLabel(ui.Label{
Text: "Delete",
Font: balance.SmallFont,
}))
delBtn.SetStyle(&balance.ButtonDanger)
delBtn.Handle(ui.Click, func(ed ui.EventData) error {
if cfg.OnDelete != nil {
if cfg.OnDelete(file) {
row.Hide()
}
}
return nil
})
cfg.Supervisor.Add(delBtn)
row.Place(delBtn, ui.Place{
Right: 4,
})
fileRows = append(fileRows, row)
}
for i, row := range fileRows {
fsFrame.Pack(row, ui.Pack{
Side: ui.N,
FillX: true,
PadY: 2,
})
// Hide if too long for 1st page.
if i >= perPage {
row.Hide()
}
}
/////////////
// Buttons at bottom of window
bottomFrame := ui.NewFrame("Button Frame")
window.Pack(bottomFrame, ui.Pack{
Side: ui.S,
FillX: true,
})
// Pager for the doodads.
pages = int(
math.Ceil(
float64(len(fileRows)) / float64(perPage),
),
)
pagerOnChange := func(newPage, perPage int) {
page = newPage
log.Info("Page: %d, %d", page, perPage)
// Re-evaluate which rows are shown/hidden for the page we're on.
var (
minRow = (page - 1) * perPage
visible = 0
)
for i, row := range fileRows {
if visible >= perPage {
row.Hide()
continue
}
if i < minRow {
row.Hide()
} else {
row.Show()
visible++
}
}
}
pager := ui.NewPager(ui.Pager{
Name: "Files List Pager",
Page: page,
Pages: pages,
PerPage: perPage,
MaxPageButtons: maxPageButtons,
Font: balance.MenuFont,
OnChange: pagerOnChange,
})
pager.Compute(cfg.Engine)
pager.Supervise(cfg.Supervisor)
bottomFrame.Place(pager, ui.Place{
Top: 20,
Left: 20,
})
frame := ui.NewFrame("Button frame")
buttons := []struct {
label string
primary bool
f func()
}{
{"Close", false, func() {
if cfg.OnCancel != nil {
cfg.OnCancel()
}
}},
}
for _, button := range buttons {
button := button
btn := ui.NewButton(button.label, ui.NewLabel(ui.Label{
Text: button.label,
Font: balance.MenuFont,
}))
if button.primary {
btn.SetStyle(&balance.ButtonPrimary)
}
btn.Handle(ui.Click, func(ed ui.EventData) error {
button.f()
return nil
})
btn.Compute(cfg.Engine)
cfg.Supervisor.Add(btn)
frame.Pack(btn, ui.Pack{
Side: ui.W,
PadX: 4,
Expand: true,
Fill: true,
})
}
bottomFrame.Pack(frame, ui.Pack{
Side: ui.E,
Padding: 8,
})
return window
}