]> git.armaanb.net Git - lightcards.git/blob - lightcards/display.py
Redo menu at end of stack
[lightcards.git] / lightcards / display.py
1 # Display card output and retreive input
2 # Armaan Bhojwani 2021
3
4 import curses
5 from random import shuffle
6 import sys
7 import textwrap
8
9 from . import lightcards
10
11
12 def disp_bar(stdscr, stack, headers, obj):
13     """
14     Display the statusbar at the bottom of the screen with progress, star
15     status, and card side.
16     """
17     (mlines, mcols) = stdscr.getmaxyx()
18
19     # Calculate percent done
20     if len(stack) <= 1:
21         percent = "100"
22     else:
23         percent = str(round(obj.getIdx() / (len(stack) - 1) * 100)).zfill(3)
24
25     # Print yellow if starred
26     stdscr.addstr(mlines - 1, 0, "[", curses.color_pair(1))
27     if stack[obj.getIdx()].getStar():
28         stdscr.addstr(stack[obj.getIdx()].printStar(), curses.color_pair(3))
29     else:
30         stdscr.addstr(stack[obj.getIdx()].printStar(), curses.color_pair(1))
31
32     # Put all the info together
33     stdscr.addstr("] [" +
34                   percent +
35                   "% (" +
36                   str(obj.getIdx() + 1).zfill(len(str(len(stack)))) +
37                   "/" +
38                   str(len(stack)) +
39                   ")]" +
40                   " [" +
41                   headers[obj.getSide()] +
42                   " (" +
43                   str(obj.getSide() + 1) +
44                   ")]", curses.color_pair(1))
45
46
47 def disp_menu(stdscr, stack, headers, idx):
48     """
49     Display a menu once the end of the deck has been reached, offering
50     multiple options on how to continue.
51     """
52     def menu_print(stdscr, stack, headers, idx, string, err=False):
53         stdscr.clear()
54         if err:
55             stdscr.addstr(string + "\n\n", curses.color_pair(2))
56         else:
57             stdscr.addstr(string + "\n\n", curses.color_pair(1))
58         disp_menu(stdscr, stack, headers, idx)
59
60     stdscr.addstr("Choose one of the following options:\n\n" +
61                   "[y]: reset stack to original state\n" +
62                   "[z]: shuffle stack\n" +
63                   "[f]: flip all cards in stack\n" +
64                   "[t]: reverse stack order\n" +
65                   "[u]: unstar all\n" +
66                   "[s]: update stack to include starred only\n\n" +
67                   "[r]: restart\n" +
68                   "[q]: quit")
69     idx.setIdx(0)
70     while True:
71         key = stdscr.getkey()
72         if key == "q":
73             sys.exit(0)
74         elif key == "y":
75             stack = lightcards.get_orig()[1]
76             menu_print(stdscr, stack, headers, idx,
77                        "Stack reset!")
78         elif key == "u":
79             [x.unStar() for x in stack]
80             menu_print(stdscr, stack, headers, idx,
81                        "All unstarred!")
82         elif key == "t":
83             stack.reverse()
84             menu_print(stdscr, stack, headers, idx,
85                        "Stack reversed!")
86         elif key == "z":
87             shuffle(stack)
88             menu_print(stdscr, stack, headers, idx,
89                        "Stack shuffled!")
90         elif key == "f":
91             for x in stack:
92                 x[0], x[1] = x[1], x[0]
93             headers[0], headers[1] = headers[1], headers[0]
94             menu_print(stdscr, stack, headers, idx,
95                        "Cards flipped!")
96         elif key == "s":
97             # Check if there are any starred cards before proceeding, and if
98             # not, don't allow to proceed and show an error message
99             cont = False
100             for x in stack:
101                 if x.getStar():
102                     cont = True
103                     break
104
105             if cont:
106                 stack = [x for x in stack if x.getStar()]
107                 menu_print(stdscr, stack, headers, idx,
108                            "Stars only!")
109             else:
110                 menu_print(stdscr, stack, headers, idx,
111                            "ERR: None are starred!",
112                            err=True)
113         elif key == "r":
114             get_key(stdscr, stack, headers, idx)
115
116
117 def disp_card(stdscr, stack, headers, obj):
118     """
119     Display the contents of the card
120     Shows a header, a horizontal line, and the contents of the current side.
121     """
122     stdscr.clear()
123     (mlines, mcols) = stdscr.getmaxyx()
124     if obj.getIdx() == len(stack):
125         disp_menu(stdscr, stack, headers, obj)
126     else:
127         # If on the back of the card, show the content of the front side in the
128         # header
129         num_done = str(obj.getIdx() + 1).zfill(len(str(len(stack))))
130         if obj.getSide() == 0:
131             top = num_done + " | " + headers[obj.getSide()]
132         else:
133             top = num_done + " | " + headers[obj.getSide()] + " | \"" + \
134                 str(stack[obj.getIdx()][0]) + "\""
135         header_width = mcols
136         if mcols > 80:
137             header_width = 80
138
139         stdscr.addstr(textwrap.shorten(top, width=header_width,
140                                        placeholder="…"), curses.A_BOLD)
141
142         # Add horizontal line
143         lin_width = header_width
144         if len(top) < header_width:
145             lin_width = len(top)
146         stdscr.hline(1, 0, curses.ACS_HLINE, lin_width)
147
148         # Show current side
149         wrap_width = mcols
150         if mcols > 80:
151             wrap_width = 80
152         stdscr.addstr(2, 0, textwrap.fill(stack[obj.getIdx()][obj.getSide()],
153                                           width=wrap_width))
154     disp_bar(stdscr, stack, headers, obj)
155
156
157 def disp_help(stdscr, stack, headers, idx):
158     """Display help screen"""
159     stdscr.clear()
160     stdscr.addstr("LIGHTCARDS HELP", curses.color_pair(1) + curses.A_BOLD)
161     stdscr.hline(1, 0, curses.ACS_HLINE, 15)
162     stdscr.addstr(2, 0,
163                   "Welcome to lightcards. Here are some keybindings to get\n" +
164                   "you started.\n\n" +
165                   "h, left            previous card\n" +
166                   "l, right           next card\n" +
167                   "j, k, up, down     flip card\n" +
168                   "i, /               star card\n" +
169                   "0, ^, home         go to the start of the deck\n" +
170                   "$, end             go to the end of the deck\n" +
171                   "H, ?               open this screen\n" +
172                   "e                  open the input file in $EDITOR\n\n" +
173                   "More information can be found in the man page, or by\n" +
174                   "running `lightcards --help`.\n\n" +
175                   "Press [q], [H], or [?] to go back.")
176     while True:
177         key = stdscr.getkey()
178         if key in ["q", "H", "?"]:
179             get_key(stdscr, stack, headers, idx)
180
181
182 def init_disp(stdscr, stack, headers, idx):
183     """Initialize curses options. Entrypoint into the display functions."""
184     curses.curs_set(0)  # Hide cursor
185     curses.init_pair(1, curses.COLOR_CYAN, 0)
186     curses.init_pair(2, curses.COLOR_RED, 0)
187     curses.init_pair(3, curses.COLOR_YELLOW, 0)
188     get_key(stdscr, stack, headers, idx)
189
190
191 def get_key(stdscr, stack, headers, idx):
192     """
193     Display a card and wait for the input.
194     Used as a general way of getting back into the card flow from a menu
195     """
196
197     disp_card(stdscr, stack, headers, idx)
198     while True:
199         key = stdscr.getkey()
200         if key == "q":
201             sys.exit(0)
202         elif key in ["l", "KEY_LEFT"]:
203             idx.forward(stack)
204             idx.setSide(0)
205             disp_card(stdscr, stack, headers, idx)
206         elif key in ["h", "KEY_RIGHT"]:
207             idx.back()
208             idx.setSide(0)
209             disp_card(stdscr, stack, headers,  idx)
210         elif key in ["j", "k", "KEY_UP", "KEY_DOWN"]:
211             idx.flip()
212             disp_card(stdscr, stack, headers, idx)
213         elif key in ["i", "/"]:
214             stack[idx.getIdx()].toggleStar()
215             disp_card(stdscr, stack, headers, idx)
216         elif key in ["0", "^", "KEY_HOME"]:
217             idx.setIdx(0)
218             idx.setSide(0)
219             disp_card(stdscr, stack, headers, idx)
220         elif key in ["$", "KEY_END"]:
221             idx.setIdx(len(stack) - 1)
222             idx.setSide(0)
223             disp_card(stdscr, stack, headers, idx)
224         elif key in ["H", "?"]:
225             disp_help(stdscr, stack, headers, idx)
226         elif key == "e":
227             (headers, stack) = lightcards.reparse()
228             get_key(stdscr, stack, headers, idx)