Antoine C.
Antoine C.

Reputation: 3962

Button handling (pseudocode/c)

I have a button, and I want it to react according to the actions of the user. Here it is:

enter image description here

You can see that it has 4 different states. A first one is when no perticular action is performed (NONE), a second one is when the mouse pointer hovers the button (HOVER), a third one when the mouse is currently clicking on the button (ONCLICK), and the last one when the button has been clicked (SELECTED).

Even if we don't care that much, the button map is as following:

enter image description here

These states are presenting as an enum:

enum button_states  { NONE, HOVER, ONCLICK, SELECTED, BSTATE_LAST };

Now I tried some code to change the state of the button, code which is executed in an infinite loop. I have a variable prevState that hold the state before the last click, and a variable previouslyClicked that worth 1 if the click was active last loop iteration and worth 0 else.

if (CursorIsOnTheButton())  {
    if (LeftClickIsActive())    {
        if (actualState != ONCLICK)
            prevState = actualState ;
        previouslyClicked = 1;
        actualState = ONCLICK;
    }
    else if (prevState == SELECTED && previouslyClicked) {
        actualState = HOVER;
        prevState = NONE;
        previouslyClicked = 0;
    }
    else if (prevState == HOVER && previouslyClicked)   {
        actualState = SELECTED;
        prevState = NONE;
        previouslyClicked = 0;
    }
    else
        actualState  = HOVER;
}
else if (actualState != SELECTED)
    actualState = NONE;

It works fine except for the SELECTED state: the button never goes into that state.

What did I do wrong?

Upvotes: 0

Views: 5666

Answers (5)

ryyker
ryyker

Reputation: 23226

Your code is written to exclude SELECTED from ever being used in either block:

if (CursorIsOnTheButton())  {
    if (LeftClickIsActive())    {

        //if execution flow ever goes into this section...
        //(i.e. if SELECTED is set here, because you are in this block, it cannot be recognized by the 
        //else if block, because it can only go in there if it did NOT go into this one.)
    }
}
//...it will never go into the else if
else if (actualState != SELECTED)
    actualState = NONE;

Modify your code to include the SELECTED state in the same block as other states.

Would adding this code section to the if block:

else if (actualState == SELECTED)   {

    //do something 
} 

Solve the problem?

Upvotes: 1

user3629249
user3629249

Reputation: 16540

you have 4 event states to handle, plus the external -force- to 'none'
the following should give you a very good guide as to how to handle the state changes

if state is 'none'
    switch event
    case mousein: 
        set state = 'hover'
        break
    case mousedown: 
        set state = 'onclick'
        break
    case mouseup: 
        set state = 'selected'
        break
    case mouseout:
        break
    default:
        break
    end switch

elseif state is 'hover'
    switch event
    case mouseout: 
        set state = 'none'
        break
    case mousedown: 
        set state = 'onclick'
        break
    case mouseup:
        break
    case mousein: 
        break
    default:
        break
    end switch

elseif state is 'onclick'
    switch event
    case mouseout: 
        set state = 'selected'
        set all other button state values to 'none'
        break
    case mouseup:  
        set state = 'selected'
        set all other button state values to 'none'
        break
    case mousedown: 
        break
    case mousein:
        break
    default:
        break
    end switch

elseif state is 'selected'
    switch event
    case mousein: 
        break
    case mousedown: 
        break
    case mouseup:
        break
    case mouseout:
        break
    default:
        break
    end switch
endif

Upvotes: 0

vmp
vmp

Reputation: 2420

Create a function like this... and whenever you mark something to selected you call it passing the recent selected buttong as the 3rd parameter

void ClearOthers("Array of buttons" buttons[], int amountOfButtons, int selected)
{
     for(int i = 0; i < amountOfButtons; i++)
         if(i != selected)
             button[i].State = NONE
}

I think you can simplify your problem...

Use 2 booleans, the actualState starts as false (not selecteded)

A button is either selected or not...

If you click it (and stop the click with mouse over it still) then it changes to selected

Any animation you want to do... you just need to use the 4 possible combinations of those booleans...

if (CursorIsOnTheButton())  
{

   if (LeftClickIsActive())        
       beingClicked = true;      

  // If the user was clicking and released the click with the mouse 
  // still hovering the button, beingclicked will be true and the button will be
  // marked as clicked

   else if (beingClicked)
   {
       beingClicked = false;
       actualState = true;
   }

}
else
    beingClicked = false;

Upvotes: 1

M Oehm
M Oehm

Reputation: 29126

Well, I'll have a go, too. I suggest a reorganisation of state variables. Get rid of the actual and previous states and just use one state for the button appearance, which will be set in our pseudocode snippet. Get rid of previouslyClicked (which is equivalent to state == ONCLICK) and introduce pressed which is true if the button is pressed and false if it isn't:

if (CursorIsOnTheButton())  {
    if (LeftClickIsActive())  {
        if (state != ONCLICK) pressed = !pressed;
        state = ONCLICK;
    } else {
        state = (pressed) ? SELECTED : HOVER;
    }
} else {
    state = (pressed) ? SELECTED : NONE;
}

The ONCLICK state does double duty here; it marks the state "clicking just now" and also ensures that the button isn't flipped on and off during one long mouse-button press. All other states are determined by a simple logic:

pressed?      in button?     clicking?      state
------------  ------------  ------------  ------------
true          dontcare      dontcare       SELECTED
false         true          true           ONCLICK
false         true          false          HOVER
false         false         dontcare       NONE

This approach has the advantage that you can check the button's depression state with pressed, wheras otherwise you'd have to check for the states.

It's still only pseudocode, which means I haven't tested it, of course.

Upvotes: 1

Antoine C.
Antoine C.

Reputation: 3962

Based on vmp answer and all of your great help, I solved that problem.

I used vmp's variables actualSelected and beingClicked, but not as boolean, to store the ID of both selected and clicked buttons.

Let me explain, I think I didn't left enough informations to you to solve this thing. I've not only one button but an array of them, buttons[NBUTTONS] is their states, and they should act exactly like radiobuttons.

for (int i = 0; i < NBUTTONS; i++)  {
    if (IsMouseOnButton(button[i])  {
        if (LeftClickIsActive())    {
            beingClicked = i;
            buttons[i] = ONCLICK;
        }
        else if (beingClicked == i) {
            actualSelected = i;
            beingClicked = -1;
            for (int j = 0; j < NBUTTONS; j++)
                buttons[i] = NONE;
            buttons[i] = SELECTED;
        }
        else if (actualSelected != i)   {
            buttons[i] = HOVER;
        }
    }
    else if (selected != i)
        buttons[i] = NONE;
}

Upvotes: 0

Related Questions