Macro called from ButtonWidget doesn't work as expected

I think that the answer of this question should be obvious to me, but unfortunately it is not. Hopefully it’s only because it’s late and I’m a bit tired already.

It’s a macro for rounding a time value (hh:mm) to the next or last full quarter hour, depending on a modifier. The time value is being read form a field and the rounded value should be written back into the same field.

Here’s the protoype (with lots of debugging information for me):

\define RoundQHtest(Which Modifier)
<$set name="TimeStr" filter="[[$Which$]match[B]then{!!begin-time}else{!!end-time}]">
<$let Hours={{{ [<TimeStr>split[:]nth[1]] }}} Mins={{{ [<TimeStr>split[:]nth[2]] }}} MinsC={{{ [<Mins>subtract[1]] }}}>

Which: [$Which$]<br>
Modifier: [$Modifier$]<br>
<br>
~TimeStr: <<TimeStr>><br>
Hours: <<Hours>><br>
Minutes: <<Mins>><br>
Minutes-Calc: <<MinsC>><br>

<$let rest={{{ [<MinsC>remainder[15]] }}}>
Remainder: <<rest>><br> 

<$let term1={{{ [<MinsC>subtract<rest>] }}} term1={{{ [<term1>compare:number:lt[10]addprefix[0]else<term1>] }}}>
term1: <<term1>><br>

<$let resultplus={{{ [<term1>add[15]] }}}>
result[+]: <<resultplus>><br>
result[-]: <<term1>><br>

<$let result={{{ [[$Modifier$]match[-]then<term1>else<resultplus>] }}}>
result: <<Hours>>:<<result>><br>

\end

This works basically. But after stripping out the debugging information, warpping it into this construct …

<$button><$action-setfield $field="begin-time" $value=<<RoundQHtemp Which:"E" Modifier:"-">> /> Begin-Time ~ &#x00BC;</$button>

… it fills the field with macro source code?

Here’s the stripped down version:

\define RoundQHtest(Which Modifier)
<$set name="TimeStr" filter="[[$Which$]match[B]then{!!begin-time}else{!!end-time}]">
<$let Hours={{{ [<TimeStr>split[:]nth[1]] }}} Mins={{{ [<TimeStr>split[:]nth[2]] }}} MinsC={{{ [<Mins>subtract[1]] }}}>
<$let rest={{{ [<MinsC>remainder[15]] }}}>
<$let term1={{{ [<MinsC>subtract<rest>] }}} term1={{{ [<term1>compare:number:lt[10]addprefix[0]else<term1>] }}}>
<$let resultplus={{{ [<term1>add[15]] }}}>
<$let result={{{ [[$Modifier$]match[-]then<term1>else<resultplus>] }}}>
<<Hours>>:<<result>>
\end

And the input-field:

 Start:<$edit-text field="begin-time" size="2" tag="input" />

Maybe some of you could spot the mistake easily and please tell me what I’m doing wrong here? Thank you in advance.

Macro are NOT functions

They do not “evaluate and return a result”. Rather, they are “expanded and inserted” in the place where they are invoked.

If invoked in a wikitext context, the inserted macro code is then rendered to display the results of the macro.

However, when invoked as the value of a widget parameter, they are NOT rendered, and thus the parameter value is the actual macro contents, not the result of any calculation it performs.

One way to work around this is to use the $wikify widget to force the macro output to be rendered and the result captured as plain text in a variable that can then be referenced, like this:

<$button>
   <$wikify name="result" text=<<RoundQHtemp Which:"E" Modifier:"-">>>
   <$action-setfield begin-time=<<result>>/>
   </$wikify>
</$button>

Note also some suggested macro code cleanup:

  • <$set name="TimeStr" filter="[[$Which$]match[B]then{!!begin-time}else{!!end-time}]">
    can be written as:
    <$let TimeStr={{{ [[$Which$]match[B]then{!!begin-time}else{!!end-time}] }}}>

  • <$let term1={{{ [<MinsC>subtract<rest>] }}} term1={{{ [<term1>compare:number:lt[10]addprefix[0]else<term1>] }}}>
    can be simplified to
    <$let term1={{{ [<MinsC>subtract<rest>pad[2]] }}}>

Let me know how it goes…

enjoy,
-e

2 Likes

Actions should always be called like that:

\define myActions()
<!-- something gets done here -->
\end

<$button actions=<<myActions>> >button-text</$button>

If done differently, there may be problems with the action values. The values of variables in the botton-body are evaluated, when the button is drawn. So there may be a different values when the button is clicked.

I know it’s wrong in the tw-docs – we need to change that.

2 Likes

Eric, thank you for pointing out that “macro are not functions” :slight_smile:
I thought that I I understand this behaviour already - I don’t expect the macro to return a value, but more or less “text”, which I can use for further logic. It’s like some preprocessor as being available in C. But what I missed is when a macro is used inside a widget, it doesn’t get rendered (which I learned now is the proper description what a macro really does).
Therefore your solution is working perfectly and helped me out once more, thank you very much.
Your suggested macro code cleanup was really useful, code has been cleaned up as suggested. The “pad” operator is very useful, too.

@Mario:
Thanks for your hint, too - I modified my button code according your suggestion!

Both posts of you helped me saving much time :slight_smile:

2 Likes