64 lines
2.1 KiB
Python
64 lines
2.1 KiB
Python
|
"""A stack-based naviation system.
|
||
|
|
||
|
Maintains a stack of states through which a user can navigate.
|
||
|
Think Android's back button model.
|
||
|
|
||
|
Every screen is modeled as a function that takes one argument "state".
|
||
|
It can modify state and should either return
|
||
|
* None (which means go back one level)
|
||
|
* A new function (which adds to the stack)
|
||
|
* A keyword like NEXT_EXIT (exit nav) or NEXT_TOP (go to top of stack)
|
||
|
|
||
|
Children get a a shallow clone of the current state, going back will
|
||
|
lead to a screen with the original state.
|
||
|
"""
|
||
|
|
||
|
___dependencies___ = ["dialogs", "database"]
|
||
|
___license___ = "MIT"
|
||
|
|
||
|
import dialogs, database
|
||
|
|
||
|
NEXT_EXIT = "exit" # Leave navigation
|
||
|
NEXT_TOP = "init" # Go to top of stack
|
||
|
def nav(init_fn, init_state={}):
|
||
|
stack = [(init_fn, init_state)]
|
||
|
while stack:
|
||
|
(fn, state) = stack[-1] # peek
|
||
|
next_state = state.copy()
|
||
|
result = fn(next_state)
|
||
|
if callable(result):
|
||
|
stack.append((result, next_state))
|
||
|
elif result == NEXT_TOP:
|
||
|
stack = [(init_fn, init_state)]
|
||
|
elif result == NEXT_EXIT:
|
||
|
break
|
||
|
else:
|
||
|
stack.pop()
|
||
|
|
||
|
# A simple menu. Format of items is [{"foo":fn}, ...]
|
||
|
def selection(items, title="Settings", none_text="Back"):
|
||
|
items = [{"title": t, "function": fn} for (t, fn) in items.items()]
|
||
|
selection = dialogs.prompt_option(items, none_text=none_text, title=title)
|
||
|
if selection:
|
||
|
return selection["function"]
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
# A wrapper for a simple string menu
|
||
|
def change_string(title, getter_setter_function):
|
||
|
def inner(state):
|
||
|
value = getter_setter_function()
|
||
|
value = dialogs.prompt_text(title, init_text=value, true_text="Change", false_text="Back")
|
||
|
if value:
|
||
|
getter_setter_function(value)
|
||
|
return inner
|
||
|
|
||
|
# A wrapper for a database key
|
||
|
def change_database_string(title, key, default=""):
|
||
|
def inner(state):
|
||
|
value = database.get(key, default)
|
||
|
value = dialogs.prompt_text(title, init_text=value, true_text="Change", false_text="Back")
|
||
|
if value:
|
||
|
database.set(key, value)
|
||
|
return inner
|