a useful ui pattern

I was writing a GUI diagram drawing program to showcase my language called "kc"Code examples will also be written in this language. You should treat it as pseudocode. Expect Python syntax with Haskell's type system, naming scheme and keywords. . I let the code structure evolve naturally over time before the final refactor. After doing that, I realized I've already written such a thing. It was for my hand gesture screen control program made for my Bachelor's.

This pattern is useful, because it provides clear structure for writing GUI programs. The downside is that the theoretical version only allows one action at a time (although it's easy to hack it in depending on whatever requirements there are.)

The typical code structure looks like this:

input-action = None
while ...
	input-action <= handle-input(input-action, mouse-location, ...whatever)

	...
	draw-input-action-stuff(input-action)
	...

Each action should start, update and end inside handle-input() .

Variable input-actionMaybe InputAction stores the current (or the absence of) input action (mouse over to see its type). "Input action" is whatever action is currently happening on the screen: eg. moving a diagram blog, resizing it, creating a new one, etc.

handling input

Let's first check inside handle-input() :

handle-input (input Maybe InputAction, ...)
	case input
		None
			...

		Just(MkRect(begin-rect))
			...

		Just(MkArrow(begin-arrow))
			...
		...

You can probably imagine, that None means that is currently being performed. Here, we can "start" input actions. We can also match on specific actions like MkRect and MkArrow among others. The begin-rect and begin-arrow are what's stored inside this InputAction ADThttps://en.wikipedia.org/wiki/Algebraic_data_type constructor.

In None we handle "action starts". Both singular actions (Ctrl+Z-ing) and longer actions (dragging, etc.) are represented here.

# These actions do not modify input-action and are executed once.
elif Raycuck.is-key-pressed(KeyZ)
	undo(full)

elif Raycuck.is-key-pressed(KeyR)
	redo(full)

...

# These actions modify the input-action.
elif Raycuck.is-mouse-button-pressed(MouseLeftButton) and \
  state try-last-rect() maybe(...)
	return Just(MoveRect(state last-rect()&, mp))

elif Raycuck.is-mouse-button-pressed(MouseLeftButton)
	return Just(MkRect(mp))

...

In the Just(...) cases, we either continue or end the actions. Notice in MkRect action, that if we release the mouse button, the action stops and we add this new block to the diagram. Otherwise, it keeps going.

Just(MkRect(begin-rect))
	if Raycuck.is-mouse-button-up(MouseLeftButton)
		r = mk-rect(begin-rect, mp)
		(&state&.rects) BoundedList.add(r)
		full add-action(...)  # add undo-redo for this action.
		return None

	return Just(MkRect(begin-rect))

display

input-actionMaybe InputAction also has the necessary information to display things to the GUI. Remember the blue selection rectangle when you left click and drag the mouse in Michaelsoft Bindows? This would be represented by input-actionMaybe InputAction .

Here's an example:

draw-input-stuff (input Maybe InputAction, mp Vec2) -> Unit
	case input
		Just(MkRect(v1))
			Raycuck.draw-rectangle-rec(mk-rect(v1, mp) as-rectangle(), BLUE)

that's it.

whole InputAction for reference

InputAction
	MkRect Vec2
	MoveRect Rect Vec2
	ResizeRect Rect GrabbedCorner Vec2
	SelectActionListElem  # currently unused!
	MkArrow Vec2

miau miau