X-Git-Url: https://git.armaanb.net/?a=blobdiff_plain;f=lightcards%2Fdisplay.py;h=cb52c8d93565952252ccda0e762002d6eaedbf32;hb=79afb3261a260272d0c3975eac67872a1c0b25df;hp=47399828e7361253ba75ea00b28151b178d45e07;hpb=dc6c791fb09b9e5aab0eec970fdca707cd70dc47;p=lightcards.git diff --git a/lightcards/display.py b/lightcards/display.py index 4739982..cb52c8d 100644 --- a/lightcards/display.py +++ b/lightcards/display.py @@ -3,12 +3,13 @@ import curses import curses.panel +import os from random import shuffle import sys import textwrap import time -from . import runner, progress +from . import runner, progress, parse def panel_create(x, y): @@ -19,6 +20,46 @@ def panel_create(x, y): return (win, panel) +class CursesError(BaseException): + def __init__(self, message="lightcards: Curses error!"): + self.message = message + print(self.message) + sys.exit(3) + + +class Quit: + def __init__(self, outer, mlines=5, mcols=20): + self.outer = outer + (self.win, self.panel) = panel_create(mlines, mcols) + self.panel.top() + self.panel.hide() + + self.win.addstr( + 1, + 2, + "QUIT LIGHTCARDS?", + curses.color_pair(1) + curses.A_BOLD, + ) + self.win.hline(2, 1, curses.ACS_HLINE, mcols) + self.win.addstr(3, 1, "Quit? [y/n]") + + self.win.box() + + def disp(self): + """Display quit confirmation screen""" + (mlines, mcols) = self.outer.win.getmaxyx() + self.win.mvwin(int(mlines / 2) - 3, int(mcols / 2) - 10) + self.panel.show() + + while True: + key = self.win.getkey() + if key == "y": + break + elif key == "n": + self.panel.hide() + self.outer.get_key() + + class Help: def __init__(self, outer, mlines=21, mcols=52): """Initialize help screen""" @@ -28,8 +69,8 @@ class Help: self.panel.hide() text = [ - "Welcome to runner. Here are some keybindings", - "to get you started:", + "Welcome to Lightcards. Here are the default", + "keybindings to get you started:", "", "h, left previous card", "l, right next card", @@ -38,9 +79,9 @@ class Help: "0, ^, home go to the start of the deck", "$, end go to the end of the deck", "H, ? open this screen", - "e open the input file in $EDITOR", "m open the control menu", "1, 2, 3 switch views", + "q quit", "", "More information can be found in the man page, or", "by running `lightcards --help`.", @@ -64,7 +105,7 @@ class Help: def disp(self): """Display help screen""" (mlines, mcols) = self.outer.win.getmaxyx() - self.win.mvwin(int(mlines / 2) - 10, int(mcols / 2) - 25) + self.win.mvwin(int(mlines / 2) - 11, int(mcols / 2) - 25) self.panel.show() while True: @@ -77,7 +118,7 @@ class Help: class Menu: - def __init__(self, outer, mlines=16, mcols=44): + def __init__(self, outer, mlines=17, mcols=44): """Initialize the menu with content""" self.outer = outer (self.win, self.panel) = panel_create(mlines, mcols) @@ -99,6 +140,7 @@ class Menu: "[u]: unstar all", "[d]: star all", "[s]: update stack to include starred only", + "[e]: open the input file in $EDITOR", "", "[r]: restart", "[m]: close menu", @@ -116,7 +158,7 @@ class Menu: else: color = curses.color_pair(1) - self.win.addstr(14, 1, string, color) + self.win.addstr(15, 1, string, color) self.menu_grab() def menu_grab(self): @@ -146,6 +188,13 @@ class Menu: elif key == "z": shuffle(self.outer.stack) self.menu_print("Stack shuffled!") + elif key == "e": + curses.endwin() + os.system(f"$EDITOR {self.outer.input_file}"), + (self.outer.headers, self.outer.stack) = parse.parse_html( + parse.md2html(self.outer.input_file) + ) + self.outer.get_key() elif key == "s": # Check if there are any starred cards before proceeding, and # if not, don't allow to proceed and show an error message @@ -182,11 +231,13 @@ class Menu: class Display: - def __init__(self, stack, headers, obj, view): + def __init__(self, stack, headers, obj, view, args, conf): self.stack = stack self.headers = headers self.obj = obj self.view = view + self.input_file = args.inp[0] + self.config = conf def run(self, stdscr): """Set important options that require stdscr before starting""" @@ -200,6 +251,7 @@ class Display: self.main_panel = curses.panel.new_panel(self.win) self.menu_obj = Menu(self) self.help_obj = Help(self) + self.quit_obj = Quit(self) self.get_key() @@ -222,7 +274,8 @@ class Display: self.disp_card() def leave(self): - """Pickle stack before quitting""" + """Pickle stack and confirm before quitting""" + self.quit_obj.disp() if self.obj.index + 1 == len(self.stack): self.obj.index = 0 @@ -239,7 +292,7 @@ class Display: status, and card side. """ (mlines, mcols) = self.win.getmaxyx() - self.win.hline(mlines - 2, 0, 0, mcols) + self.win.hline(mlines - 3, 0, 0, mcols) # Calculate percent done if len(self.stack) <= 1: @@ -258,29 +311,52 @@ class Display: # Compose bar text bar_start = "[" bar_middle = self.current_card().printStar() - bar_end = ( - f"] [{len(self.nstarred())}/{str(len(self.stack))} starred] " - f"[{percent}% (" - f"{str(self.obj.index).zfill(len(str(len(self.stack))))}" - f"/{str(len(self.stack))})]" - ) - if self.view == 3: + bar_end = f"] [{len(self.nstarred())}/{str(len(self.stack))} starred] " + if self.view != 3: bar_end += ( - f" [{self.headers[self.current_card().side]} (" + f" [{self.get_side()} (" f"{str(int(self.current_card().side) + 1)})]" ) bar_end += f" [View {str(self.view)}]" # Put it all togethor - self.win.addstr(mlines - 1, 0, bar_start, curses.color_pair(1)) - self.win.addstr(mlines - 1, len(bar_start), bar_middle, star_color) + height = mlines - 2 + self.win.addstr(height, 0, bar_start, curses.color_pair(1)) + self.win.addstr(height, len(bar_start), bar_middle, star_color) self.win.addstr( - mlines - 1, + height, len(bar_start + bar_middle), textwrap.shorten(bar_end, width=mcols - 20, placeholder="…"), curses.color_pair(1), ) + progress = ( + f"[{percent}% (" + f"{str(self.obj.index + 1).zfill(len(str(len(self.stack))))}" + f"/{str(len(self.stack))})] " + ) + + self.win.addstr( + height + 1, + 0, + progress, + curses.color_pair(1), + ) + + for i in range( + int( + self.obj.index + / (len(self.stack) - 1) + * (mcols - len(progress)) + - 1 + ) + ): + # TODO: Use the variying width unicode block characters to make a + # super accurate bar + self.win.addch( + height + 1, i + len(progress), "»", curses.color_pair(1) + ) + def wrap_width(self): """Calculate the width at which the body text should wrap""" (_, mcols) = self.win.getmaxyx() @@ -289,13 +365,19 @@ class Display: wrap_width = 80 return wrap_width + def get_side(self): + if self.obj.side == 0: + return self.headers[self.current_card().side] + else: + return self.headers[self.current_card().get_reverse()] + def disp_card(self): (_, mcols) = self.win.getmaxyx() self.main_panel.bottom() self.win.clear() num_done = str(self.obj.index + 1).zfill(len(str(len(self.stack)))) - if self.view in [1, 2]: + if self.view in [1, 2, 4]: """ Display the contents of the card. Shows a header, a horizontal line, and the contents of the current @@ -309,12 +391,12 @@ class Display: self.obj.side = 1 if self.current_card().side == 0: - top = num_done + " | " + self.headers[self.obj.side] + top = num_done + " | " + self.get_side() else: top = ( num_done + " | " - + self.headers[self.obj.side] + + self.get_side() + ' | "' + str(self.current_card().get()[self.obj.get_reverse()]) + '"' @@ -389,40 +471,41 @@ class Display: key = self.win.getkey() if key == "q": self.leave() - elif key in ["h", "KEY_LEFT"]: + elif key in self.config["card_prev"]: self.obj.back() self.current_card().side = 0 self.disp_card() - elif key in ["l", "KEY_RIGHT"]: + elif key in self.config["card_next"]: if self.obj.index + 1 == len(self.stack): self.menu_obj.disp() else: self.obj.forward(self.stack) self.current_card().side = 0 self.disp_card() - elif key in ["j", "k", "KEY_UP", "KEY_DOWN"] and self.view != 3: + elif key in self.config["card_flip"] and self.view != 3: self.current_card().flip() self.disp_card() - elif key in ["i", "/"]: + elif key in self.config["card_star"]: self.current_card().toggleStar() self.disp_card() - elif key in ["0", "^", "KEY_HOME"]: + elif key in self.config["card_first"]: self.obj.index = 0 self.current_card().side = 0 self.disp_card() - elif key in ["$", "KEY_END"]: + elif key in self.config["card_last"]: self.obj.index = len(self.stack) - 1 self.current_card().side = 0 self.disp_card() - elif key in ["H", "?"]: + elif key in self.config["help_disp"]: self.help_obj.disp() - elif key == "m": + elif key in self.config["menu_disp"]: self.menu_obj.disp() - elif key == "e": - (self.headers, self.stack) = runner.reparse() - self.get_key() - elif key in ["1", "2", "3"]: - self.view = int(key) + elif key in self.config["view_one"]: + self.view = 1 + elif key in self.config["view_two"]: + self.view = 2 + elif key in self.config["view_three"]: + self.view = 3 def disp_sidebar(self): """Display a sidebar with the starred terms""" @@ -435,7 +518,7 @@ class Display: "STARRED CARDS", curses.color_pair(3) + curses.A_BOLD, ) - self.win.vline(0, mcols - 20, 0, mlines - 2) + self.win.vline(0, mcols - 20, 0, mlines - 3) nstarred = self.nstarred() for i, card in enumerate(nstarred): @@ -443,12 +526,12 @@ class Display: if len(term) > 18: term = term[:18] + "…" - if i > mlines - 5: + if i > mlines - 6: for i in range(19): self.win.addch(mlines - 3, left + i, " ") self.win.addstr( - mlines - 3, + mlines - 4, left, f"({len(nstarred) - i - 2} more)", )