1
$\begingroup$

The top function setstyles is the only one we need to focus on. However, the four functions below it are needed for this to work. These validate up to 4 properties of user-input, always returning a list of two styles for the on and off states of a 4-state toggle button.

When working correctly, the function setstyles determines which style properties to return in a list of 4 styles. The argument sta is the state (1,2,3, or 4) which is key in determining which properties to pick. Once properties are determined, they are set to the dynamic variables as shown {bg, col, app, lab} = {style1, ..., style4}.

The odd thing is that most of the time it works fine -- absolutely no complaints. But if I change something somewhere else (symbols elsewhere in program) it will start listing an error for each occurrence of First, Last, and Take even though the input is structurally and syntactically the same before edits were made and all values are valid styles.

If you think you can diagnose the errors, your help is most appreciated. If you do help, please pay attention to the comments in the code. Thank you.

(* This is where errors are generated *)

ClearAll[setstyles, colQ]
colQ = (ColorQ@# || # == Automatic || # == None &);
Options[setstyles] = {
   "state" -> sta,
   "Backgrounds" -> Automatic, "Colors" -> Automatic, 
   "Appearances" -> Automatic, "Labels" -> Automatic};
setstyles[opts : OptionsPattern[]] := Module[{
    s = OptionValue["state"],
    vbgs = valbgs[OptionValue["Backgrounds"]],
    vcols = valcols[OptionValue["Colors"]],
    vapps = valapps[OptionValue["Appearances"]],
    vlabs = vallabs[OptionValue["Labels"]],
    off = First[#] & /@ {vbgs, vcols, vapps, vlabs},
    on = Last[#] & /@ {vbgs, vcols, vapps, vlabs}
    },
   With[{
     styles = Which[s == 1, off, s == 2, on, s == 3, off, s == 3, on]
     },
    Set[{bg, col, app, lab}, styles]
    ]];

(* the 4 validators below work properly *)

ClearAll[vallabs];
stage::invlabs = "Option Labels \[Rule] `1`, expected a single Button \
expression (label) or a List of 1 or 2 labels."; vallabs[l_] :=
 If[MatchQ[l, Automatic], {"AutoSave", "Saving"}, {l, l}];
vallabs[{l_}] := 
  If[MatchQ[l, Automatic], {"AutoSave", "Saving"}, {l, "Saving"}];
vallabs[{m_, n_}] := With[{
    a = If[MatchQ[m, Automatic], "AutoSave", m],
    b = If[MatchQ[n, Automatic], "Saving", n]
    }, {a, b}];
vallabs[e_List] := Message[stage::invlabs, e];

ClearAll[valbgs];
stage::invbgs = "Option Backgounds \[Rule] `1`, expected a valid \
Background (value) or a List of 1 or 2 values."; 
valbgs[c_?colQ] := {c, c};
valbgs[{c_?colQ}] := {c, Black};
valbgs[{a_?colQ, b_?colQ}] := {a, b};
valbgs[e_] := Message[stage::invbgs, e];

ClearAll[valcols];
stage::invcols = "Option Colors \[Rule] `1`, expected a Color \
Directive, None, or Automatic (value) or a List of 1 or 2 values."; 
valcols[c_?colQ] := 
 If[MatchQ[c, None | Automatic], {Black, Black}, {c, c}];
valcols[{c_?colQ}] := 
  If[MatchQ[c, None | Automatic], {Black, Black}, {c, Black}];
valcols[{c_?colQ, d_?colQ}] := With[{
    a = If[MatchQ[c, None | Automatic], Black, c],
    b = If[MatchQ[d, None | Automatic], Black, d]
    }, {a, b}];
valcols[e_] := Message[stage::invcols, e];

ClearAll[valapps];
appear = {Automatic, None, "DialogBox", "Frameless", "Palette", 
   "FramedPalette", "AbuttingLeftRight", "AbuttingRight" , "Pressed"};
stage::invapps = 
  "Option Appearances \[Rule] `1`, must be a Button Appearance \
(value) or a list of up to 2 values.";
valapps[a_] := 
  If[MemberQ[appear, a], {a, {a, "Pressed"}}, 
   Message[stage::invapps, a]];
valapps[{a_}] := 
  If[MemberQ[appear, a], {a, {a, "Pressed"}}, 
   Message[stage::invapps, a]];
valapps[{a_, b_}] := 
  If[MemberQ[appear, a] && MemberQ[appear, b], {a, {b, "Pressed"}}, 
   Message[stage::invapps, {a, b}]];

(* this code emulates state *)
sta = 1;
Echo[{bg, col, app, 
   lab}, Row[{Style["Dynamic Vars: {bg, col, app, lab}", White], "  \[Rule]"}]];


(* two cases: simple and not so sinple *)
(* all user inpurs are correct even if they appear inconsistent *)
(* validators allow for this and still return 2 values for each style *)

setstyles[]

setstyles[Backgrounds -> {Darker@Blue, Darker@Green}, Colors -> White,
   Labels -> {"off", "on"}, Appearances -> "FramedPalette"];
$\endgroup$
3
  • 2
    $\begingroup$ You cannot use Module variables in the values for other variables of the same Module. You'll need to just declare on and off with no values, and then set them in the body of the Module $\endgroup$
    – Lukas Lang
    Sep 21, 2022 at 8:21
  • $\begingroup$ @LukasLang you have helped on several occasions so its always appreciated. i can almost see a shadow of what youre intending to express. if you have a few minutes to spare can you show me a simplified example in an answer of what im doing and how to correct it. i really want to learn from this. i promise to give it an upvote and possibly checkmark. $\endgroup$ Sep 21, 2022 at 8:32
  • $\begingroup$ see the answer of MarcoB, that's almost exactly what I would have suggested $\endgroup$
    – Lukas Lang
    Sep 21, 2022 at 11:40

2 Answers 2

3
$\begingroup$

It is often safer to declare module variables first, and then assign them values separately within the Module's code. For instance, this is a small modification of the setstyles code that first declares the variables as local to the Module, and then makes assignments:

setstyles[opts : OptionsPattern[]] := 
 Module[
  {s, vbgs, vcols, vapps, vlabs, off, on},
  s = OptionValue["state"]; 
  vbgs = valbgs[OptionValue["Backgrounds"]];
  vcols = valcols[OptionValue["Colors"]]; 
  vapps = valapps[OptionValue["Appearances"]]; 
  vlabs = vallabs[OptionValue["Labels"]]; 
  off = First /@ {vbgs, vcols, vapps, vlabs}; 
  on = Last /@ {vbgs, vcols, vapps, vlabs}; 
  With[
   {styles = Which[s == 1, off, s == 2, on, s == 3, off, s == 3, on]}, 
   Set[{bg, col, app, lab}, styles]
  ]
]

Note that I also changed e.g. First[#] & /@ {vbgs, vcols, vapps, vlabs} to First /@ {vbgs, vcols, vapps, vlabs} which should be completely equivalent.

$\endgroup$
3
  • $\begingroup$ it worked thank you. but i only needed to move the off and on below the curlies. $\endgroup$ Sep 21, 2022 at 11:39
  • $\begingroup$ @JulesManson Yes, moving on and off was strictly necessary. I would still recommend the rest of the changes as well, stylistically if not functionally. The resulting code seems clearer to me. However, that may be a stylistic choice. $\endgroup$
    – MarcoB
    Sep 21, 2022 at 13:48
  • $\begingroup$ after more extensive testing i found that i needed to move everything down as you kindly suggested. my bad lol $\endgroup$ Sep 21, 2022 at 19:49
0
$\begingroup$

Thank you to Lukas Lang who suggested a few good tips but especially to MarcoB for providing an answer. both were correct and both helped. Below is how i modified setstyles and it seems to work as intended.

ClearAll[setstyles];
Options[setstyles] = {
   "state" -> $SaveState[],
   "Backgrounds" -> Automatic, "Colors" -> Automatic, 
   "Appearances" -> Automatic, "Labels" -> Automatic};
setstyles[opts : OptionsPattern[]] := Module[{
    sta = OptionValue["state"],
    vbgs, vcols, vapps, vlabs, fn
    },
   vbgs = valbgs[OptionValue["Backgrounds"]];
   vcols = valcols[OptionValue["Colors"]];
   vapps = valapps[OptionValue["Appearances"]];
   vlabs = vallabs[OptionValue["Labels"]];
   fn = If[sta == 1 || sta == 3, First, Last];
   {bg, col, app, lab} = fn /@ {vbgs, vcols, vapps, vlabs}
   ];
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.