Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maintain and control GUI states and substates

I'm looking to create a graph reader program that will take any scanned graph and convert it's values to a CSV with little effort. The following graph describes the basic control flow

enter image description here

I've got a prototype up and running that fulfills my basic requirements, but before proceeding with developement I want to know the best way to maintain GUI states and substates. Right now I'm using an enum:

public enum UIState {
    MAKING_SELECTION, SELECTING_AXIS_POINTS, SETTING_VALUES, SELECTING_GRAPH_POINTS
}

The controller has one UIState that can be set by buttons and mouse listeners by calling

public void setUiState(UIState uiState) {
    switch (this.uiState = uiState) {
        case MAKING_SELECTION:
            frame.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            break;
        case SELECTING_AXIS_POINTS:
            clearSelection();
            frame.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
            break;
        case SETTING_VALUES:
            clearSelection();
            break;
        case SELECTING_GRAPH_POINTS:
            frame.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
            clearSelection();
            break;
    }

    updateView(); //Repaints frame
    setChanged();
    notifyObservers(uiState);
}

My OptionsPanel, the panel that contains all buttons and input fields, observes the controller and calls a huge switch if it observes change:

@Override
public void update(Observable o, Object arg) {
    if (arg instanceof UIState) {
        switch ((UIState)arg) {
            case MAKING_SELECTION:
                //Set component statuses
                break;
            case SELECTING_AXIS_POINTS:
                //Set component statuses
                break;
            case SETTING_VALUES:
                //Set component statuses
                break;
            case SELECTING_GRAPH_POINTS:
                //Set component statuses
                break;
        }
    }
}

The setting of the components is done just by calling setEnabled() on every single one of my buttons and text fields which becomes unclear already with a small amount of components. My mouse listeners also have similar switches in addition to the isLeftMouseButton() and isRightMouseButton() queries which make the whole thing really confusing.

My implementation doesn't even include substates and even though I've kinda got it working right now, there already is inconsistency with the setting of the cursor.

So my question is: Is there a better solution to maintaining those GUI states and substates and set the UI components status based on user input, especially one that allows for more consistency easier?

like image 391
Marv Avatar asked Dec 17 '14 00:12

Marv


1 Answers

The best way to handle this is to use the State design pattern:

http://en.wikipedia.org/wiki/State_pattern

You have general purpose input listeners which delegate task execution to your concrete States. Then whichever is the current concrete state handles the input in its own unique way. There are plenty of variations on how to apply the concept. The key idea though is that when the application state changes, a different State object becomes the "current State" and is responsible for handling the input.

For example: in a mouseMove() event handler call currentState.mouseMove(evt)and let the current state object handle the mouseMove with no need for switch statements at all.

like image 189
Tim B Avatar answered Oct 19 '22 08:10

Tim B