2
$\begingroup$

The Play function defines a toggle button to start and stop:

enter image description here

I'm trying to emulate that, but with a single button. I feel like this is a common enough task that this question should be answered somewhere (but I can't find it).

Question: I'd like a single button that toggles between (at least two) states: where each state has a different image and each performs a different action when clicked. The api could be this:

StateButton[<|ImageA -> ActionA, ImageB -> ActionB, ...|>] 

Here was a first attempt using just row which uses nasty hacks to center the "play symbol":

startStop[startAction_,stopAction_] := 
            Row[{Button["\!\(\*
        StyleBox[AdjustmentBox[\"\[RightPointer]\",\nBoxBaselineShift->0.12313612313612314`,\nBoxMargins->{{0., 0.}, {-0.12313612313612314`, 0.12313612313612314`}}],\nFontSize->24]\)", 
startAction,Appearance->"Frameless",ImageSize->{30,30},Alignment->Center],
Button["\!\(\*AdjustmentBox[\(\[FilledSquare]\),\nBoxBaselineShift->-0.1704961704961705,\nBoxMargins->{{0., 0.}, 
{0.1704961704961705, -0.1704961704961705}}]\)",stopAction,Appearance->"Frameless",ImageSize->{30,30},Alignment->Center]}," ",BaselinePosition->Center]

I have tried wrapping Button and Toggler together in various obvious ways e.g. Toggler[Dynamic[x], {Button[1, Print[1]], Button[2, Print[2]]}] which don't work - I cannot find a solution.

$\endgroup$
1
$\begingroup$

Turned out it was easy to replicate the buttons from Play:

$playView=ToExpression@GraphicsBox[
        TagBox[{
          {GrayLevel[0.9], RectangleBox[{5, -158}, {29, -135}]}, 
          {GrayLevel[0.3], 
           PolygonBox[NCache[{{13, -153}, {13, -140}, {21, Rational[-293, 2]}, {13, -153}}, {{13, -153}, {13, -140}, {21, -146.5}, {13, -153}}]]}, 
          {GrayLevel[0.5], 
           StyleBox[LineBox[{{5, -158}, {5, -135}, {29, -135}, {29, -158}, {5, -158}}],
            Antialiasing->False]}},
         Null],
        ImageSize->{29.4921875, Automatic}];

    $stopView=ToExpression@GraphicsBox[
         TagBox[{
           {GrayLevel[0.9], RectangleBox[{34, -158}, {58, -135}]}, 
           {GrayLevel[0.3], RectangleBox[{42, -150}, {50, -143}]}, 
           {GrayLevel[0.5], 
            StyleBox[LineBox[{{34, -158}, {34, -135}, {58, -135}, {58, -158}, {34, -158}}],
             Antialiasing->False]}},
          Null]];


Clear[cycleState]
SetAttributes[cycleState, HoldFirst]
cycleState[s_ /; s > 0, len_: 2] := (s = If[s >= len, 1, s + 1])

DynamicModule[{views = {$playView, $stopView}, state = 1},
     Button[Dynamic[views[[state]]], 
      Switch[state, 1, Print@"start", 2, Print@"stop"]; 
      cycleState[state], Appearance -> "Palette"]
     ]
$\endgroup$
  • $\begingroup$ Might even be easier to use a Trigger control which is designed to handle this kind of behavior. $\endgroup$ – m_goldberg Apr 20 '15 at 23:07
1
$\begingroup$

My solution uses SetterBar with the items framed.

{SetterBar[Dynamic[x], 
 {"Play" -> Framed[Rotate["\[FilledUpTriangle]", -\[Pi]/2]], 
  "Stop" -> Framed@"\[FilledSquare]"}, 
 BaseStyle -> {FontSize -> 20}], 
Dynamic@Switch[x, "Play", Style["Play", Green], "Stop", Style["Stop", Red]]}

It is pretty close. I'm certain you can tweak the font size to get it identical. It is probably what is used behind the scenes.

Hope this helps.

$\endgroup$
  • $\begingroup$ There are multiple buttons, the question asks for a single button. $\endgroup$ – M.R. Apr 23 '15 at 17:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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