added user presets

This commit is contained in:
grimsace
2026-03-11 14:24:18 -05:00
parent b65bf2539f
commit 6858634807
2 changed files with 256 additions and 17 deletions
+247 -14
View File
@@ -72,6 +72,12 @@ type presetEntry struct {
Path string
}
type presetInfo struct {
Name string
Path string
Source string
}
func listEmbeddedPresets() ([]presetEntry, error) {
entries, err := fs.ReadDir(embeddedPresets, "Presets")
if err != nil {
@@ -106,6 +112,102 @@ func loadEmbeddedPreset(path string) (*Settings, error) {
return decodeSettingsJSON(raw)
}
func listUserPresets(dir string) ([]presetEntry, error) {
entries, err := os.ReadDir(dir)
if err != nil {
if os.IsNotExist(err) {
return []presetEntry{}, nil
}
return nil, err
}
var presets []presetEntry
for _, entry := range entries {
if entry.IsDir() {
continue
}
name := entry.Name()
if !strings.HasSuffix(strings.ToLower(name), ".json") {
continue
}
displayName := strings.TrimSuffix(name, filepath.Ext(name))
presets = append(presets, presetEntry{
Name: displayName,
Path: filepath.Join(dir, name),
})
}
sort.Slice(presets, func(i, j int) bool {
return presets[i].Name < presets[j].Name
})
return presets, nil
}
func buildPresetOptions(settings *Settings, userPresetDir string) ([]string, map[string]presetInfo) {
hidden := map[string]bool{}
for _, name := range settings.HiddenPresets {
hidden[name] = true
}
info := map[string]presetInfo{}
var names []string
embedded, err := listEmbeddedPresets()
if err != nil {
log.Println("Error loading embedded presets:", err)
} else {
for _, preset := range embedded {
if hidden[preset.Name] {
continue
}
info[preset.Name] = presetInfo{
Name: preset.Name,
Path: preset.Path,
Source: "embedded",
}
names = append(names, preset.Name)
}
}
userPresets, err := listUserPresets(userPresetDir)
if err != nil {
log.Println("Error loading user presets:", err)
} else {
for _, preset := range userPresets {
if _, exists := info[preset.Name]; exists {
continue
}
info[preset.Name] = presetInfo{
Name: preset.Name,
Path: preset.Path,
Source: "user",
}
names = append(names, preset.Name)
}
}
sort.Strings(names)
return append([]string{"No Preset"}, names...), info
}
func savePresetToFile(path string, settings *Settings) error {
raw, err := json.Marshal(settings)
if err != nil {
return err
}
var data map[string]any
if err := json.Unmarshal(raw, &data); err != nil {
return err
}
delete(data, "seed")
delete(data, "last_export_path")
delete(data, "image_view_state")
pretty, err := json.MarshalIndent(data, "", " ")
if err != nil {
return err
}
return os.WriteFile(path, pretty, 0644)
}
func main() {
// Initialize the Fyne application and window
a := app.NewWithID("com.example.rpgcitymaker")
@@ -1651,26 +1753,38 @@ func main() {
updateGenerateBtnState()
}
presetEntries, err := listEmbeddedPresets()
if err != nil {
log.Println("Error loading embedded presets:", err)
}
presetNames := []string{"No Preset"}
presetPathByName := map[string]string{}
for _, preset := range presetEntries {
presetNames = append(presetNames, preset.Name)
presetPathByName[preset.Name] = preset.Path
}
presetSelect := widget.NewSelect(presetNames, func(name string) {
if name == "" || name == "No Preset" {
presetDir := filepath.Join(appConfigDir, "presets")
var presetInfoByName map[string]presetInfo
var suppressPresetApply bool
var removePresetBtn *widget.Button
presetSelect := widget.NewSelect([]string{"No Preset"}, func(name string) {
if suppressPresetApply {
return
}
path, ok := presetPathByName[name]
if name == "" || name == "No Preset" {
if removePresetBtn != nil {
removePresetBtn.Disable()
}
return
}
info, ok := presetInfoByName[name]
if !ok {
return
}
applyingPreset = true
imported, err := loadEmbeddedPreset(path)
var imported *Settings
var err error
if info.Source == "embedded" {
imported, err = loadEmbeddedPreset(info.Path)
} else {
raw, readErr := os.ReadFile(info.Path)
if readErr != nil {
err = readErr
} else {
imported, err = decodeSettingsJSON(raw)
}
}
if err != nil {
applyingPreset = false
dialog.ShowError(err, w)
@@ -1683,6 +1797,9 @@ func main() {
applyingPreset = false
})
})
if removePresetBtn != nil {
removePresetBtn.Enable()
}
})
presetSelect.SetSelected("No Preset")
clearPresetSelection = func() {
@@ -1691,6 +1808,121 @@ func main() {
}
}
refreshPresetOptions := func(selectName string) {
names, info := buildPresetOptions(settings, presetDir)
presetInfoByName = info
suppressPresetApply = true
presetSelect.Options = names
presetSelect.Refresh()
if selectName == "" {
selectName = presetSelect.Selected
}
if _, ok := presetInfoByName[selectName]; !ok {
selectName = "No Preset"
}
presetSelect.SetSelected(selectName)
if removePresetBtn != nil {
if selectName == "" || selectName == "No Preset" {
removePresetBtn.Disable()
} else {
removePresetBtn.Enable()
}
}
suppressPresetApply = false
}
addPresetBtn := widget.NewButton("Add Preset", func() {
nameEntry := widget.NewEntry()
nameEntry.SetPlaceHolder("Preset Name")
content := container.NewVBox(
widget.NewLabel("Preset Name:"),
nameEntry,
)
var addDialog *dialog.CustomDialog
saveBtn := widget.NewButton("Save", func() {
name := strings.TrimSpace(nameEntry.Text)
if name == "" || name == "No Preset" {
return
}
if strings.ContainsAny(name, "/\\") {
dialog.ShowError(fmt.Errorf("preset name cannot contain path separators"), w)
return
}
if _, exists := presetInfoByName[name]; exists {
dialog.ShowError(fmt.Errorf("preset name already exists"), w)
return
}
if err := os.MkdirAll(presetDir, 0755); err != nil {
dialog.ShowError(err, w)
return
}
filePath := filepath.Join(presetDir, name+".json")
if err := savePresetToFile(filePath, settings); err != nil {
dialog.ShowError(err, w)
return
}
refreshPresetOptions(name)
if addDialog != nil {
addDialog.Hide()
}
})
saveBtn.Disable()
nameEntry.OnChanged = func(text string) {
if strings.TrimSpace(text) == "" {
saveBtn.Disable()
} else {
saveBtn.Enable()
}
}
addDialog = dialog.NewCustom("Add Preset", "Cancel", content, w)
addDialog.SetButtons([]fyne.CanvasObject{
widget.NewButton("Cancel", func() {
addDialog.Hide()
}),
saveBtn,
})
addDialog.Show()
})
removePresetBtn = widget.NewButton("Remove Preset", func() {
name := presetSelect.Selected
if name == "" || name == "No Preset" {
return
}
info, ok := presetInfoByName[name]
if !ok {
return
}
if info.Source == "embedded" {
found := false
for _, existing := range settings.HiddenPresets {
if existing == name {
found = true
break
}
}
if !found {
settings.HiddenPresets = append(settings.HiddenPresets, name)
if err := settings.Save(); err != nil {
log.Println("Error saving hidden preset list:", err)
}
}
} else {
if err := os.Remove(info.Path); err != nil {
dialog.ShowError(err, w)
return
}
}
refreshPresetOptions("No Preset")
})
removePresetBtn.Disable()
refreshPresetOptions("No Preset")
buildingsTab := container.NewTabItem("Buildings", container.NewVBox(
numBuildingsSlider,
minBuildingSizeSlider,
@@ -1705,6 +1937,7 @@ func main() {
imageTab := container.NewTabItem("Image", container.NewVBox(
widget.NewLabel("Preset:"),
presetSelect,
container.NewGridWithColumns(2, addPresetBtn, removePresetBtn),
widget.NewSeparator(),
widget.NewLabel("Width:"),
widthEntry, widget.NewLabel("Height:"),