@@ 1,8 1,11 @@
#!/home/sietse/anaconda3/bin/python3.6
import curses
+import re
+from enum import Enum, unique
from pprint import pformat
+
TASKS = [
'Workrave',
'Bril',
@@ 18,6 21,13 @@ TASKS = [
'Status of HPP project',
]
+@unique
+class Debug(Enum):
+ Show = 1
+ PleaseHide = 2
+ Hidden = 3
+
+
def color_pair(id, fg, bg):
"""Combine defining a colour pair and returning its reference"""
curses.init_pair(id, fg, bg)
@@ 60,11 70,43 @@ def update(state, key):
elif key == ' ':
checked, desc = OS['tasks'][OS['current_task_id']]
NS['tasks'][OS['current_task_id']] = not checked, desc
+ elif key == 'd':
+ if OS['debug'] is Debug.Show:
+ NS['debug'] = Debug.PleaseHide
+ elif OS['debug'] in (Debug.PleaseHide, Debug.Hidden):
+ NS['debug'] = Debug.Show
elif key == 'q':
NS['running'] = False
return NS
+def autoupdate_state_no_rerender(state):
+ """Perform state updates that only depend on the previous state, and
+ that do not require a rerender.
+
+ These are the 'automatic' state updates that happen after rendering,
+ regardless of keypresses.
+
+ Rerendering will only happen after the next keypress!
+
+ If this auto_update function ever needs to trigger immediate
+ rerenders, let's not hack about with `rerender` flags; instead,
+ let's switch to a model where both terminal keypresses and messages
+ triggered by auto_update are part of the same message stream / event
+ stream, so neither update loop can block the other.
+ """
+ OS, NS = state, state.copy() # Old State and New State
+
+ if OS['debug'] is Debug.PleaseHide:
+ # The view() call just before us has processed the PleaseHide
+ # state and wiped the debug info; now we change PleaseHide to
+ # Hidden so that the next cycle does not needlessly re-wipe the
+ # debug info. The change from PleaseHide to Hidden does not
+ # require a rerender.
+ NS['debug'] = Debug.Hidden
+ return NS
+
+
def checkbox(checked):
return '[x]' if checked else '[ ]'
@@ 78,8 120,12 @@ def view(state, t, styles) -> None:
style = styles['plain']
t.addstr(i, 0, f'- {checkbox(checked)} {task}', style)
- if state['debug']:
+ if state['debug'] is Debug.Show:
t.addstr(i + 2, 0, pformat(state), styles['plain'])
+ elif state['debug'] is Debug.PleaseHide:
+ t.addstr(i + 2, 0, re.sub('.', ' ', pformat(state)), styles['plain'])
+ elif state['debug'] is Debug.Hidden:
+ pass
def run(t, styles):
@@ 87,16 133,18 @@ def run(t, styles):
'tasks': [(False, t) for t in TASKS],
'current_task_id': 0,
'running': True,
- 'debug': False,
+ 'debug': Debug.Hidden,
}
view(state, t, styles)
while True:
- key = t.getkey()
- state = update(state, key)
- view(state, t, styles)
+ key = t.getkey() # Wait for the next keypress...
+ state = update(state, key) # ...update state based on the key...
+ view(state, t, styles) # ...render the new state...
+ state = autoupdate_state_no_rerender(state) # ...and do housekeeping.
if not state['running']:
break
+
def main():
with TerminalAndStyles() as (t, styles):
run(t, styles)