# HG changeset patch # User Sietse Brouwer # Date 1548422463 -3600 # Fri Jan 25 14:21:03 2019 +0100 # Node ID 18e867f962e73ce2ad190529f96bb04f74007f07 # Parent 00606074a4e9081aaf72b5d0de92d6496209e5a6 Support toggling `debug` on and off. This introduces an `autoupdate` function that runs after the render, forwards temporary states as needed, and does not trigger any rerenders itself. diff --git a/check.py b/check.py --- a/check.py +++ b/check.py @@ -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 @@ '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 @@ 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 @@ 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 @@ '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)