doodle/pkg/windows/doodad_properties.go

456 lines
9.8 KiB
Go
Raw Normal View History

package windows
import (
"io/ioutil"
"os"
"path/filepath"
"sort"
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/apps/doodle/pkg/modal"
"git.kirsle.net/apps/doodle/pkg/native"
"git.kirsle.net/apps/doodle/pkg/shmem"
"git.kirsle.net/go/render"
"git.kirsle.net/go/ui"
)
// DoodadProperties window.
type DoodadProperties struct {
// Settings passed in by doodle
Supervisor *ui.Supervisor
Engine render.Engine
// Configuration options.
EditDoodad *doodads.Doodad
ActiveTab string // specify the tab to open
OnRefresh func() // caller should rebuild the window
// Widgets.
TabFrame *ui.TabFrame
}
// HACKY GLOBAL VARIABLE
var showTagsOnRefreshDoodadPropertiesWindow bool
// NewSettingsWindow initializes the window.
func NewDoodadPropertiesWindow(cfg *DoodadProperties) *ui.Window {
var (
Width = 400
Height = 300
)
window := ui.NewWindow("Doodad Properties")
window.SetButtons(ui.CloseButton)
window.Configure(ui.Config{
Width: Width,
Height: Height,
Background: render.Grey,
})
///////////
// Tab Bar
tabFrame := ui.NewTabFrame("Tab Frame")
tabFrame.SetBackground(render.DarkGrey)
window.Pack(tabFrame, ui.Pack{
Side: ui.N,
FillX: true,
})
cfg.TabFrame = tabFrame
// Make the tabs.
cfg.makeMetaTab(tabFrame, Width, Height)
cfg.makeTagsTab(tabFrame, Width, Height)
if showTagsOnRefreshDoodadPropertiesWindow {
tabFrame.SetTab("Tags")
showTagsOnRefreshDoodadPropertiesWindow = false
}
tabFrame.Supervise(cfg.Supervisor)
return window
}
// DoodadProperties Window "Metadata" Tab
func (c DoodadProperties) makeMetaTab(tabFrame *ui.TabFrame, Width, Height int) *ui.Frame {
tab := tabFrame.AddTab("Metadata", ui.NewLabel(ui.Label{
Text: "Metadata",
Font: balance.TabFont,
}))
tab.Resize(render.NewRect(Width-4, Height-tab.Size().H-46))
if c.EditDoodad == nil {
return tab
}
//////////////
// Draw the editable metadata form.
for _, data := range []struct {
Label string
Variable *string
Update func(string)
}{
{
Label: "Title:",
Variable: &c.EditDoodad.Title,
Update: func(v string) {
c.EditDoodad.Title = v
},
},
{
Label: "Author:",
Variable: &c.EditDoodad.Author,
Update: func(v string) {
c.EditDoodad.Author = v
},
},
} {
data := data
frame := ui.NewFrame("Metadata " + data.Label + " Frame")
tab.Pack(frame, ui.Pack{
Side: ui.N,
PadY: 4,
FillX: true,
})
// The label
label := ui.NewLabel(ui.Label{
Text: data.Label,
Font: balance.MenuFont,
})
label.Configure(ui.Config{
Width: 75,
})
frame.Pack(label, ui.Pack{
Side: ui.W,
})
// The button.
btn := ui.NewButton(data.Label, ui.NewLabel(ui.Label{
TextVariable: data.Variable,
Font: balance.MenuFont,
}))
btn.Handle(ui.Click, func(ed ui.EventData) error {
shmem.Prompt("Enter a new "+data.Label+" ", func(answer string) {
if answer != "" {
data.Update(answer)
}
})
return nil
})
c.Supervisor.Add(btn)
frame.Pack(btn, ui.Pack{
Side: ui.W,
Expand: true,
PadX: 2,
})
}
//////////////////////////////////
// Draw the JavaScript management
scriptHeader := ui.NewLabel(ui.Label{
Text: "Doodad Script",
Font: balance.LargeLabelFont,
})
tab.Pack(scriptHeader, ui.Pack{
Side: ui.N,
FillX: true,
PadY: 8,
})
// Frame for if a script does exist on the doodad.
var (
ifScript *ui.Frame
elseScript *ui.Frame
)
// "If Script" Frame
{
ifScript = ui.NewFrame("If Script")
tab.Pack(ifScript, ui.Pack{
Side: ui.N,
FillX: true,
})
label := ui.NewLabel(ui.Label{
Text: "This Doodad has a script attached.",
Font: balance.MenuFont,
})
ifScript.Pack(label, ui.Pack{
Side: ui.W,
})
// Delete Button
deleteBtn := ui.NewButton("Save", ui.NewLabel(ui.Label{
Text: "Delete",
Font: balance.MenuFont,
}))
deleteBtn.SetStyle(&balance.ButtonDanger)
deleteBtn.Handle(ui.Click, func(ed ui.EventData) error {
modal.Confirm("Are you sure you want to delete this script?").Then(func() {
c.EditDoodad.Script = ""
ifScript.Hide()
elseScript.Show()
})
return nil
})
c.Supervisor.Add(deleteBtn)
ifScript.Pack(deleteBtn, ui.Pack{
Side: ui.E,
PadX: 2,
})
// Save Button
saveBtn := ui.NewButton("Save", ui.NewLabel(ui.Label{
Text: "Save",
Font: balance.MenuFont,
}))
saveBtn.SetStyle(&balance.ButtonPrimary)
saveBtn.Handle(ui.Click, func(ed ui.EventData) error {
shmem.Prompt("Save script as (*.js): ", func(answer string) {
if answer != "" {
cwd, _ := os.Getwd()
err := ioutil.WriteFile(answer, []byte(c.EditDoodad.Script), 0644)
if err != nil {
shmem.Flash(err.Error())
} else {
shmem.Flash("Written to: %s (%d bytes)", filepath.Join(cwd, answer), len(c.EditDoodad.Script))
}
}
})
return nil
})
c.Supervisor.Add(saveBtn)
ifScript.Pack(saveBtn, ui.Pack{
Side: ui.E,
PadX: 2,
})
}
// "Else Script" Frame
{
elseScript = ui.NewFrame("If Script")
tab.Pack(elseScript, ui.Pack{
Side: ui.N,
FillX: true,
})
label := ui.NewLabel(ui.Label{
Text: "There is no script attached to this doodad.",
Font: balance.MenuFont,
})
elseScript.Pack(label, ui.Pack{
Side: ui.W,
})
}
// Browse Script button.
btnBrowse := ui.NewButton("Browse Script", ui.NewLabel(ui.Label{
Text: "Attach a script...",
Font: balance.MenuFont,
}))
btnBrowse.SetStyle(&balance.ButtonPrimary)
btnBrowse.Handle(ui.Click, func(ed ui.EventData) error {
filename, err := native.OpenFile("Choose a .js file", "*.js")
if err != nil {
shmem.Flash("Couldn't show file dialog: %s", err)
return nil
}
data, err := ioutil.ReadFile(filename)
if err != nil {
shmem.Flash("Couldn't read file: %s", err)
return nil
}
c.EditDoodad.Script = string(data)
shmem.Flash("Attached %d-byte script to this doodad.", len(c.EditDoodad.Script))
// Toggle the if/else frames.
ifScript.Show()
elseScript.Hide()
return nil
})
c.Supervisor.Add(btnBrowse)
tab.Pack(btnBrowse, ui.Pack{
Side: ui.N,
Padding: 4,
})
// Show/hide appropriate frames.
if c.EditDoodad.Script == "" {
ifScript.Hide()
elseScript.Show()
} else {
ifScript.Show()
elseScript.Hide()
}
return tab
}
// DoodadProperties Window "Tags" Tab
func (c DoodadProperties) makeTagsTab(tabFrame *ui.TabFrame, Width, Height int) *ui.Frame {
tab := tabFrame.AddTab("Tags", ui.NewLabel(ui.Label{
Text: "Tags",
Font: balance.TabFont,
}))
tab.Resize(render.NewRect(Width-4, Height-tab.Size().H-46))
if c.EditDoodad == nil {
return tab
}
// Draw a table view of the current tags on this doodad.
var (
headers = []string{"Name", "Value", "Del."}
columns = []int{150, 150, 80} // TODO, Width=400
height = 24
row = ui.NewFrame("HeaderRow")
)
tab.Pack(row, ui.Pack{
Side: ui.N,
FillX: true,
})
for i, value := range headers {
cell := ui.NewLabel(ui.Label{
Text: value,
Font: balance.MenuFontBold,
})
cell.Resize(render.NewRect(columns[i], height))
row.Pack(cell, ui.Pack{
Side: ui.W,
})
}
// No tags?
if len(c.EditDoodad.Tags) == 0 {
label := ui.NewLabel(ui.Label{
Text: "There are no tags on this doodad.",
Font: balance.MenuFont,
})
tab.Pack(label, ui.Pack{
Side: ui.N,
FillX: true,
})
} else {
// Draw the rows for each tag.
var sortedTags []string
for name := range c.EditDoodad.Tags {
sortedTags = append(sortedTags, name)
}
sort.Strings(sortedTags)
for _, tagName := range sortedTags {
var (
name = tagName
value = c.EditDoodad.Tags[name]
)
row = ui.NewFrame("Tag Row")
tab.Pack(row, ui.Pack{
Side: ui.N,
FillX: true,
PadY: 2,
})
lblName := ui.NewLabel(ui.Label{
Text: name,
Font: balance.MenuFont,
})
lblName.Resize(render.NewRect(columns[0], height))
btnValue := ui.NewButton("Tag Button", ui.NewLabel(ui.Label{
Text: value,
Font: balance.MenuFont,
}))
btnValue.Resize(render.NewRect(columns[1], height))
btnValue.Handle(ui.Click, func(ed ui.EventData) error {
shmem.Prompt("Enter new value: ", func(answer string) {
if answer == "" {
return
}
c.EditDoodad.Tags[name] = answer
btnValue.SetText(answer)
})
return nil
})
c.Supervisor.Add(btnValue)
btnDelete := ui.NewButton("Delete Button", ui.NewLabel(ui.Label{
Text: "Delete",
Font: balance.MenuFont,
}))
btnDelete.Resize(render.NewRect(columns[2], height))
btnDelete.SetStyle(&balance.ButtonDanger)
btnDelete.Handle(ui.Click, func(ed ui.EventData) error {
modal.Confirm("Delete tag %s?", name).Then(func() {
log.Info("Delete tag: %s", name)
delete(c.EditDoodad.Tags, name)
// Trigger a refresh.
if c.OnRefresh != nil {
showTagsOnRefreshDoodadPropertiesWindow = true
c.OnRefresh()
}
})
return nil
})
c.Supervisor.Add(btnDelete)
// Pack the widgets.
row.Pack(lblName, ui.Pack{
Side: ui.W,
})
row.Pack(btnValue, ui.Pack{
Side: ui.W,
PadX: 4,
})
row.Pack(btnDelete, ui.Pack{
Side: ui.W,
})
}
}
// Add Tag button.
row = ui.NewFrame("Button Frame")
tab.Pack(row, ui.Pack{
Side: ui.N,
FillX: true,
})
btnAdd := ui.NewButton("New Tag", ui.NewLabel(ui.Label{
Text: "Add Tag",
Font: balance.MenuFont,
}))
btnAdd.SetStyle(&balance.ButtonPrimary)
btnAdd.Handle(ui.Click, func(ed ui.EventData) error {
shmem.Prompt("Enter name of the new tag: ", func(answer string) {
if answer == "" {
return
}
log.Info("Adding doodad tag: %s", answer)
c.EditDoodad.Tags[answer] = ""
if c.OnRefresh != nil {
showTagsOnRefreshDoodadPropertiesWindow = true
c.OnRefresh()
}
})
return nil
})
c.Supervisor.Add(btnAdd)
row.Pack(btnAdd, ui.Pack{
Side: ui.E,
})
return tab
}
func (c DoodadProperties) reloadTagFrame() {
}