Skip to content

Commit

Permalink
Allow editing of substitutions
Browse files Browse the repository at this point in the history
  • Loading branch information
richardwilkes committed Jul 19, 2024
1 parent dee40a1 commit f2731cb
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 61 deletions.
5 changes: 5 additions & 0 deletions model/gurps/conditional_modifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ func (c *ConditionalModifier) DataOwner() DataOwner {
func (c *ConditionalModifier) SetDataOwner(_ DataOwner) {
}

// NameableReplacements returns the replacements to be used with Nameables.
func (c *ConditionalModifier) NameableReplacements() map[string]string {
return nil
}

// FillWithNameableKeys adds any nameable keys found to the provided map.
func (c *ConditionalModifier) FillWithNameableKeys(_, _ map[string]string) {
}
Expand Down
1 change: 1 addition & 0 deletions model/gurps/nameables.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type NameableFiller interface {

// Nameables defines methods types that want to participate the nameable adjustments should implement.
type Nameables interface {
NameableAccesser
NameableFiller
// ApplyNameableKeys applies the nameable keys to this object.
ApplyNameableKeys(m map[string]string)
Expand Down
2 changes: 2 additions & 0 deletions model/gurps/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type DataOwnerProvider interface {
// NodeTypes is a constraint that defines the types that may be nodes.
type NodeTypes interface {
*ConditionalModifier | *Equipment | *EquipmentModifier | *Note | *Skill | *Spell | *Trait | *TraitModifier | *Weapon
Nameables
fmt.Stringer
}

// Node defines the methods required of nodes in our tables.
Expand Down
4 changes: 4 additions & 0 deletions svg/naming.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions svg/svg.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ var (
menuData string
Menu = unison.MustSVGFromContentString(menuData)

//go:embed naming.svg
namingData string
Naming = unison.MustSVGFromContentString(namingData)

//go:embed new_folder.svg
newFolderData string
NewFolder = unison.MustSVGFromContentString(newFolderData)
Expand Down
4 changes: 4 additions & 0 deletions ux/container_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
package ux

import (
"fmt"

"github.com/richardwilkes/gcs/v5/model/gurps"
"github.com/richardwilkes/unison"
)

// ConvertableNodeTypes defines the types that the container conversion can work on.
type ConvertableNodeTypes interface {
*gurps.Equipment | *gurps.Note
fmt.Stringer
gurps.Nameables
Container() bool
CanConvertToFromContainer() bool
ConvertToContainer()
Expand Down
26 changes: 26 additions & 0 deletions ux/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type editor[N gurps.NodeTypes, D gurps.EditorData[N]] struct {
scroll *unison.ScrollPanel
applyButton *unison.Button
cancelButton *unison.Button
nameablesButton *unison.Button
beforeData D
editorData D
modificationCallback func()
Expand Down Expand Up @@ -193,6 +194,20 @@ func (e *editor[N, D]) createToolbar(helpMD string, initToolbar func(*editor[N,
}
toolbar.AddChild(e.cancelButton)

if _, ok := any(e.target).(*gurps.Weapon); !ok {
e.nameablesButton = unison.NewSVGButton(svg.Naming)
e.nameablesButton.Tooltip = newWrappedTooltip(i18n.Text("Set Substitutions"))
e.nameablesButton.ClickCallback = func() {
if tmp, m := e.prepareForSubstitutions(); len(m) > 0 {
ShowNameablesDialog([]string{tmp.String()}, []map[string]string{m})
tmp.ApplyNameableKeys(m)
e.editorData.CopyFrom(tmp)
e.Rebuild(false)
}
}
toolbar.AddChild(e.nameablesButton)
}

if initToolbar != nil {
initToolbar(e, toolbar)
}
Expand All @@ -208,6 +223,15 @@ func (e *editor[N, D]) createToolbar(helpMD string, initToolbar func(*editor[N,
return toolbar
}

func (e *editor[N, D]) prepareForSubstitutions() (N, map[string]string) {
node := gurps.AsNode(e.target)
tmp := node.Clone(node.GetSource().LibraryFile, node.DataOwner(), nil, true)
e.editorData.ApplyTo(tmp)
m := make(map[string]string)
tmp.FillWithNameableKeys(m, nil)
return tmp, m
}

func (e *editor[N, D]) TitleIcon(suggestedSize unison.Size) unison.Drawable {
return &unison.DrawableSVG{
SVG: e.svg,
Expand Down Expand Up @@ -251,6 +275,8 @@ func (e *editor[N, D]) Modified() bool {
modified := e.isModified()
e.applyButton.SetEnabled(modified)
e.cancelButton.SetEnabled(modified)
_, m := e.prepareForSubstitutions()
e.nameablesButton.SetEnabled(len(m) > 0)
return modified
}

Expand Down
10 changes: 9 additions & 1 deletion ux/modifier_processing.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
package ux

import (
"fmt"

"github.com/richardwilkes/gcs/v5/model/gurps"
"github.com/richardwilkes/toolbox/i18n"
"github.com/richardwilkes/toolbox/txt"
Expand All @@ -19,6 +21,12 @@ import (
"github.com/richardwilkes/unison/enums/check"
)

type modifiersOnly interface {
*gurps.TraitModifier | *gurps.EquipmentModifier
fmt.Stringer
gurps.Nameables
}

// ProcessModifiersForSelection processes the selected rows for modifiers that can be toggled on or off.
func ProcessModifiersForSelection[T gurps.NodeTypes](table *unison.Table[*Node[T]]) {
rows := table.SelectedRows(true)
Expand Down Expand Up @@ -48,7 +56,7 @@ func ProcessModifiers[T gurps.NodeTypes](owner unison.Paneler, rows []T) {
}
}

func processModifiers[T *gurps.TraitModifier | *gurps.EquipmentModifier](title string, modifiers []T) bool {
func processModifiers[T modifiersOnly](title string, modifiers []T) bool {
if len(modifiers) == 0 {
return false
}
Expand Down
129 changes: 69 additions & 60 deletions ux/nameables_processing.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,92 +31,101 @@ func ProcessNameablesForSelection[T gurps.NodeTypes](table *unison.Table[*Node[T
// ProcessNameables processes the rows and their children for any nameables.
func ProcessNameables[T gurps.NodeTypes](owner unison.Paneler, rows []T) {
var data []T
var titles []string
var nameables []map[string]string
for _, row := range rows {
gurps.Traverse(func(row T) bool {
m := make(map[string]string)
gurps.AsNode(row).FillWithNameableKeys(m, nil)
if len(m) > 0 {
data = append(data, row)
titles = append(titles, gurps.AsNode(row).String())
nameables = append(nameables, m)
}
return false
}, false, false, row)
}
if len(data) > 0 {
list := unison.NewPanel()
list.SetBorder(unison.NewEmptyBorder(unison.NewUniformInsets(unison.StdHSpacing)))
list.SetLayout(&unison.FlexLayout{
Columns: 2,
HSpacing: unison.StdHSpacing,
VSpacing: unison.StdVSpacing,
})
for i, one := range data {
keys := make([]string, 0, len(nameables[i]))
for k := range nameables[i] {
keys = append(keys, k)
if ShowNameablesDialog(titles, nameables) {
for i, row := range data {
gurps.AsNode(row).ApplyNameableKeys(nameables[i])
}
txt.SortStringsNaturalAscending(keys)
if i != 0 {
sep := unison.NewSeparator()
sep.SetLayoutData(&unison.FlexLayoutData{
HSpan: 2,
HAlign: align.Fill,
VAlign: align.Middle,
HGrab: true,
})
list.AddChild(sep)
if rebuildable := unison.Ancestor[Rebuildable](owner); rebuildable != nil {
rebuildable.Rebuild(true)
}
header := unison.NewLabel()
header.Font = unison.SystemFont
header.SetTitle(txt.Truncate(gurps.AsNode(one).String(), 40, true))
header.SetLayoutData(&unison.FlexLayoutData{
}
}
}

// ShowNameablesDialog shows a dialog for editing nameables.
func ShowNameablesDialog(titles []string, nameables []map[string]string) bool {
list := unison.NewPanel()
list.SetBorder(unison.NewEmptyBorder(unison.NewUniformInsets(unison.StdHSpacing)))
list.SetLayout(&unison.FlexLayout{
Columns: 2,
HSpacing: unison.StdHSpacing,
VSpacing: unison.StdVSpacing,
})
for i, one := range titles {
keys := make([]string, 0, len(nameables[i]))
for k := range nameables[i] {
keys = append(keys, k)
}
txt.SortStringsNaturalAscending(keys)
if i != 0 {
sep := unison.NewSeparator()
sep.SetLayoutData(&unison.FlexLayoutData{
HSpan: 2,
HAlign: align.Fill,
VAlign: align.Middle,
HGrab: true,
})
list.AddChild(header)
for _, k := range keys {
label := unison.NewLabel()
label.SetTitle(k)
label.SetLayoutData(&unison.FlexLayoutData{
HAlign: align.End,
VAlign: align.Middle,
})
list.AddChild(label)
list.AddChild(createNameableField(k, nameables[i]))
}
list.AddChild(sep)
}
scroll := unison.NewScrollPanel()
scroll.SetBorder(unison.NewLineBorder(unison.ThemeSurfaceEdge, 0, unison.NewUniformInsets(1), false))
scroll.SetContent(list, behavior.Fill, behavior.Fill)
scroll.BackgroundInk = unison.ThemeSurface
scroll.SetLayoutData(&unison.FlexLayoutData{
header := unison.NewLabel()
header.Font = unison.SystemFont
header.SetTitle(txt.Truncate(one, 40, true))
header.SetLayoutData(&unison.FlexLayoutData{
HSpan: 2,
HAlign: align.Fill,
VAlign: align.Fill,
VAlign: align.Middle,
HGrab: true,
VGrab: true,
})
panel := unison.NewPanel()
panel.SetLayout(&unison.FlexLayout{
Columns: 1,
HSpacing: unison.StdHSpacing,
VSpacing: unison.StdVSpacing,
HAlign: align.Fill,
VAlign: align.Fill,
})
label := unison.NewLabel()
label.SetTitle(i18n.Text("Provide substitutions:"))
panel.AddChild(label)
panel.AddChild(scroll)
if unison.QuestionDialogWithPanel(panel) == unison.ModalResponseOK {
for i, row := range data {
gurps.AsNode(row).ApplyNameableKeys(nameables[i])
}
unison.Ancestor[Rebuildable](owner).Rebuild(true)
list.AddChild(header)
for _, k := range keys {
label := unison.NewLabel()
label.SetTitle(k)
label.SetLayoutData(&unison.FlexLayoutData{
HAlign: align.End,
VAlign: align.Middle,
})
list.AddChild(label)
list.AddChild(createNameableField(k, nameables[i]))
}
}
scroll := unison.NewScrollPanel()
scroll.SetBorder(unison.NewLineBorder(unison.ThemeSurfaceEdge, 0, unison.NewUniformInsets(1), false))
scroll.SetContent(list, behavior.Fill, behavior.Fill)
scroll.BackgroundInk = unison.ThemeSurface
scroll.SetLayoutData(&unison.FlexLayoutData{
HAlign: align.Fill,
VAlign: align.Fill,
HGrab: true,
VGrab: true,
})
panel := unison.NewPanel()
panel.SetLayout(&unison.FlexLayout{
Columns: 1,
HSpacing: unison.StdHSpacing,
VSpacing: unison.StdVSpacing,
HAlign: align.Fill,
VAlign: align.Fill,
})
label := unison.NewLabel()
label.SetTitle(i18n.Text("Provide substitutions:"))
panel.AddChild(label)
panel.AddChild(scroll)
return unison.QuestionDialogWithPanel(panel) == unison.ModalResponseOK
}

func createNameableField(key string, m map[string]string) *unison.Field {
Expand Down

0 comments on commit f2731cb

Please sign in to comment.