Using edittext widget to edit part of the string of an index in a dictionary tiddler

Hello all,
I’m trying to do something a bit convoluted, because of how I need to architect things in my GURPS TW project (GURPS is a TTRP game for those who don’t know).

Context:

  • Characters have skills. Certain skills can have a specialty, which could be anything. For example, the skill “Animal handling” has to be specialized in a species or type of animal. It could be anything, from Big cats to Unicorns, depending on the setting the game is happening in.
  • The skills themselves (in this example, Animal Handling) are defined in the rules of the game, and are all individual tiddlers in my TW, tagged with GURPS Skill.
  • All the skills a character knows are saved in a dictionary tiddler I called a “skillbook”.
  • A character knows a skill to a certain level of mastery, with a minimum of 1.
  • Skills are saved in the format Skill name (specialty):level

What I’m trying to do:
When the Tiddler of a Character is edited, we obviously have access to all its characteristics, in order to edit them. The skillbook, appears like this in the character sheet tiddler being edited:
image

One can thus change the skillbook of a character via the drop down, or create a new one, or, edit it. When the “Edit skills” is pressed, the list of that character’s known skills appears in a table like this:
image

This table is populated by reading the character’s skillbook tiddler, like this (tried to indent it for easier viewing):

How populating the table is implemented
<table>
<tr><th></th><th>Skill</th><th>Difficulty</th><th>Relative skill</th><th>Effective skill</th><th><div class="mark" title="CP points"><span class="mark label entity clean"><span class="icon-cost-cp">''&nbsp;points''</span></span></div></th></tr>
<$let skillbook={{!!skillbook}}>
	<$list filter="[<skillbook>indexes[]]" variable="skill" emptyMessage="<tr><td colspan=11>No skills in skillbook.</td></tr>">
		<$let pure-skill={{{ [<skill>split[ (]first[]] }}} specialty={{{ [<skill>split[ (]last[]] +[removesuffix[)]] }}}>
		<tr><td>
		<$button class="tc-btn-invisible tc-tiddlylink" tooltip="Remove skill">
		<$action-setfield $tiddler=<<skillbook>> $index=<<skill>> />{{$:/core/images/delete-button}}</$button></td><td><$link to=<<pure-skill>>/> (<<specialty>>) <$macrocall $name=skillspec-edit skill=<<skillitem>> skillbook=<<skillbook>>/>
		</td><td>
			<% if [<pure-skill>!has[difficulty]] %>Skill is missing a difficulty definition.<% elseif [<pure-skill>get[difficulty]match[A]] %>Average<% elseif [<pure-skill>get[difficulty]match[E]] %>Easy<% elseif [<pure-skill>get[difficulty]match[H]] %>Hard<% elseif [<pure-skill>get[difficulty]match[VH]] %>Very Hard<% endif %>
		</td><td>
			<$macrocall $name="rel-lvl" skillbook=<<skillbook>> skill=<<skill>>/>
		</td><td>
			<$wikify name="rskill" text="<$macrocall $name='rel-lvl-value' skillbook=<<skillbook>> skill=<<skillitem>>/>">
				<$let advbook=<<advbook>> IQvalue={{{ [<cp>divide[20]trunc[]add[10]] }}}>
					''<$text text={{{ [<rskill>add<IQvalue>] }}}/>''
				</$let>
			</$wikify>
		</td>
		<td align="center">
			<$edit-text tiddler=<<skillbook>> index=<<skill>> type="number" min="1" default="1" size="2" tag=input/>
		</td></tr>
		</$let>
	</$list>
</$let>
</table>

What I’m working on and can’t yet fully achieve is this: I’d like to have the specialization be editable when the “Edit spec” button is clicked, via a text-edit widget, like in the below image.

This is currently implemented via this (couple) procedure(s):

\procedure skillspec-edit-actions()
<$let skill-new={{{ [[ (]addprefix<pure-skill>addsuffix[<spellbook>get<skill>]] }}}>
<$action-setfield $tiddler=<<skillbook>> $index=<<skill-new>> $value=<<value>>/>
<$action-setfield $tiddler=<<skillbook>> $index=<<skill>>/>
</$let>
\end

\procedure skillspec-edit(skillbook, skill)
<$let pure-skill={{{ [<skill>split[ (]first[]] }}} specialty={{{ [<skill>split[ (]last[]] +[removesuffix[)]] }}} value={{{ [<skillbook>getindex<skill>] }}}>
<$reveal type="nomatch" state="$:/temp/skillspecedit" text="show" animate=yes><$button set="$:/temp/skillspecedit" setTo="show" actions="<$action-setfield $tiddler=<<skillbook>> $field=<<skill>> $value=<<specialty>> />">Edit spec</$button></$reveal>
<$reveal type="match" state="$:/temp/skillspecedit" text="show" animate=yes><$edit-text tiddler=<<skillbook>> field=<<skill>> default=<<specialty>>/><$button set="$:/temp/skillspecedit" setTo="hide" actions="<<skillspec-edit-actions>>">{{$:/core/images/done-button}}&nbsp;Done editing</$button></$reveal>
</$let>
\end

The overarching mechanic being: I’m saving the current specialization in a field on the spellbook tiddler, the field is named with the full name and specialization of the skill so it is unique to each specialization. Then, when the field is edited via the text-edit widget, and the user clicks to validate the edition, I add the new index in the skillbook dictionary tiddler, and remove the old one.

The problem I have, is that I don’t know how to replace the old index (Animal handling (Big cats)) with the new (Animal handling (Small cats)). I’m guessing that I would have to actually delete the old index, and create the new one from scratch, but the way I wrote it above is probably completely wrong syntaxically.

Neither of the <action-setfield> widgets work; when I edit, the old skill index isn’t removed, and the new one isn’t added.

I’m sure <$let skill-new={{{ [[ (]addprefix<pure-skill>addsuffix[<spellbook>get<skill>]] }}}> is full of syntax errors, that for the life of me I can’t fix… but even if it was syntaxically correct, I don’t know why the action-setfield widgets don’t work at all…

You can take a look by following this link. Edit the “Character” tiddler to access the overall editing, then click on the “Edit skills” button.

Edit: It seems the first click on the button <$button set="$:/temp/skillspecedit" setTo="show" actions="<action-setfield $tiddler=<<skillbook>> $field=<<skill>> $value=<<specialty>> />">Edit spec</$button> doesn’t actually create and populate the field of the current skill (specialty). The field is created after editing the text-edit widget and clicking the second button <$button set="$:/temp/skillspecedit" setTo="hide" actions="<<skillspec-edit-actions>>">{{$:/core/images/done-button}}&nbsp;Done editing</$button>, though.

The core of the issue is certainly the <$let skill-new={{{ [[ (]addprefix<pure-skill>addsuffix[<spellbook>get<skill>]] }}}>. I changed the actions procedure to:

\procedure skillspec-edit-actions()
<$let skill-new={{{ [<pure-skill> <skillbook>get[<skill>] +[join[ (]] +[addsuffix[)]] }}}>
<$action-setfield $tiddler=<<skillbook>> $index=<<skill-new>> $value=<<value>>/>
</$let>
\end

… but now, when an edition happens, everything but the value of the first index is removed from the skillbook tiddler.

1 Like

Crossposted from the TW Discord for posterity and anyone else who might have been reading along.


I think I found it! The culprit was the second line in skillspec-edit-actions (in the current demo version of your wiki):

<$let skill-new=`$(pure-skill)$ (${ [<spellbook>get<skill>] }$)`>
<$action-setfield $tiddler=<<skillbook>> $index=<<skill-new>> $value=<<value>>/>

I think you probably got confused because you do have a line in skillspec-edit

<$action-setfield $tiddler=<<skillbook>> $field=<<skill>> $value=<<specialty>> />

Which does create a field on the skillbook tiddler with field name = the base skill and field value = the current specialty (which might be nothing, if you’re editing for the first time.)

However, your $edit-text is editing the field <<skill>> on the temporary tiddler $:/temp/skillspecedit.

  • (Actually tiddler=<<$:/temp/skillspecedit>> in your code… which would have created an additional problem if you tried to retrieve the value, and you may need to go delete the tiddler <<$:/temp/skillspecedit>> and any other with similar names.)
  • This means that whatever changes you make in the textbox don’t get saved back to your dictionary tiddler because $action-setfield isn’t looking for [[$:/temp/skillspecedit]get<skill>], it’s calling [<spellbook>get<skill>].
    • So at best, any changes you made in the temp tiddler get ignored.
    • And at worst, if the value of [<spellbook>get<skill>] is empty, and the name of the index <<skill>> you’re trying to edit = <<pure-skill>>, your action widget will get parsed as <$action-setfield $tiddler=<<skillbook>> $index=<<skill>> $value=(NULL) /> and the index will get deleted.

I suspect the issue arose because you were initially planning to edit a field on the dictionary tiddler (and then save the changes back to the index), but later switched to using a temporary tiddler instead. You changed your code to use the temporary tiddler in most places, but missed the one in a separate \procedure. :wink:

I did some reorganization of your code while I was reading to help myself (and hopefully you!) make sense of it: skill macro edits.json (24.1 KB)

  • This includes a complete revision of your Skill macros tiddler and some changes to the EditTemplate, which seem to fix the issue you described. I hope I didn’t break anything, but just in case, I’d recommend making a backup before importing.
  • I added a number of functions and macros, with an eye to 1) reducing redundancy; 2) improving reuseability of code snippets, and 3) improving legibility, especially in your wikitext tables. (Personally, since your tables are so complex, I’d recommend converting all the wikitext cells to HTML… but I’ll leave that to you.)
  • Since I made a number of opinionated changes, I won’t do a line-by-line breakdown, but do feel free to ask if you have any questions.

EDIT: We did some more debugging and arrived at the following final code for the procedures used for editing:

\procedure skillspec-edit-actions()
<% if [<temp>has:field<skill>] %>
<$let new-index=`$(pure-skill)$${ [<temp>get<skill>addprefix[ (]addsuffix[)]] }$`>
	<% if [<new-index>!match<skill>] %>
		<$tiddler tiddler=<<skillbook>>>
			<$action-setfield $index=<<new-index>> $value=<<value>>/>
			<$action-setfield $index=<<skill>>/>
		</$tiddler>
		<$action-deletefield $tiddler=<<temp>> $field=<<skill>> />
	<% endif %>
</$let>
<% endif %>
\end

\procedure skillspec-edit()
\define temp() $:/temp/skillspecedit
<$let
	value={{{ [<skillbook>getindex<skill>] }}}
>
<% if [<temp>get[text]match[show]] %>
	<$edit-text
		tiddler=<<temp>>
		field=<<skill>>
		default=<<specialty>>
	/>
	<$button
		set=<<temp>> setTo="hide"
		actions="<<skillspec-edit-actions>>"
	>
			{{$:/core/images/done-button}} Done editing
	</$button>
<% else %>
	<$button
		set=<<temp>> setTo="show"
	>
			Edit spec
	</$button>
<% endif %>
\end
1 Like

For readers out there, further fixes to get to the desired behaviour include:

updating the following procedure:

\procedure skillspec-edit-actions()
<% if [<temp>has:field<skill>] %>
<$let new-index=`$(pure-skill)$${ [<temp>get<skill>addprefix[ (]addsuffix[)]] }$`>
    <% if [<new-index>!match<skill>] %>
        <$tiddler tiddler=<<skillbook>>>
            <$action-setfield $index=<<new-index>> $value=<<value>>/>
            <$action-setfield $index=<<skill>>/>
        </$tiddler>
        <$action-deletefield $tiddler=<<temp>> $field=<<skill>> />
    <% endif %>
</$let>
<% endif %>
\end