controller.states library Null safety
The states of the controller, reflecting the input mode of the calculator. There are three main states: Resting (the state it's usually in, waiting for something to do), DigitEntry and ProgramEntry. There are a large number of additional states, such as argument input, or running a program. The full hierarchy looks like this:
The state pattern is used to help manage the complexity of the calculator's behavior in the various state. Correctly managing stack lift is an interesting challenge. The three main states use functions attached to Operations to do their work. The API presented to the ProgramEntry state is a reduced one, presented by LimitedState. Note that this introduces a contravariant relationship between states and operations, which isn't completely captured by the Dart type relationships. This does result in a downcast in two places, but it's appropriately guarded. Segmenting the API in this way makes the static type checker ensure that we don't accidentally reference a method that should not be available for the type of operation; this helped simplify development, and removes a potential source of bugs.
- Supertype for the two states that process pressed and calculation functions for NormalOperations. This is where stack lift is exposed, because usually stack lift is enabled after performing a calculation on the model. It is performed when entering digits, or recalling a value from a register or lastX.
- Inputting an argument. Generally, the calculator silently waits for keypresses giving the argument value. For example, when recalling register 1c, the user presses "RCL . c"; the calculator is in this state while waiting for the ". c" to be pressed.
- State when the calculator is off. On desktop and mobile, the calculator screen is dismissed, but that's awkward on the web, especially if the calculator is embedded in a page. So, in the web case, we turn off the LCD display, and wait in this state to see if the user presses ON at some point.
Superclass for all states of a Controller. See the
controller.stateslibrary documentation for an overview, including a diagram.
- The state the calculator's in while entering digits.
- State where we ignore keypresses, because the calculator is off or running self-tests.
- Supertype for the three states that process pressed functions from LimitedOperations (which includes all NormalOperations). This is the contravariant typing relationship mentioned in our library overview. cf. the controller.operations library's class diagram, notably the dashed subtype line from LimitedOperation to NormalOperation.
- State while a message is showing, like "Error 2."
- State for after the ON key is pressed, while we're waiting to see if they pick a special function, or maybe press the ON key again to turn the calculator off.
- State while entering a program.
- The initial state of the calculator, when it's waiting for input telling it to do something.
- State when we resume program execution with the R/S key
- State while we're running a program. While we're in this state, our Controller stays in Running, but we create a RunningController that has its own ControllerState.
- Inputting the argument for GSB. See handleGosubEntryDone.
- State while we're temporarily showing something, while a button is still pressed. For example "f show Hex" lands us in this state.
- State while single-stepping through a program. Like Running, this state creates a RunningController that has its own ControllerState.
- State after the GTO key is pressed, when we don't know if the user will press "." (meaning navigate to a specific line number), or a label. In the spirit of Beckett, we do hope one arrives before too long.
- After "GTO ." is pressed, we're in this state collecting the three-digit line number.