subclient

Subsonic ncurses client
git clone https://git.e1e0.net/subclient.git
Log | Files | Refs | README

commit 7020a60bfedc3fb426314840abd33f6e65e2542e
parent 28417c7dcea0c5613c25e2d1075055e038403779
Author: Paco Esteban <paco@e1e0.net>
Date:   Sun, 27 Jun 2021 17:37:33 +0200

betterer and clearerer scroll method

Diffstat:
Msubclient/subclient.py | 149++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
1 file changed, 94 insertions(+), 55 deletions(-)

diff --git a/subclient/subclient.py b/subclient/subclient.py @@ -20,80 +20,119 @@ def get_config_file(): class SubClient: + UP = -1 + DOWN = 1 + def __init__(self): self.config = configparser.ConfigParser() self.config.read(get_config_file()) + self.subsonic = subsonic.Subsonic(self.config['subclient']) + + self.window = None + self.hight = 0 + self.width = 0 + self.init_curses() + self.nav_list = self.subsonic.get_artists() - self.nav_y = 0 self.nav_selected = 0 - self.nav_list_selected = 0 + self.nav_top = 0 + self.nav_bottom = len(self.nav_list) + self.nav_max_lines = curses.LINES + self.nav_page = self.nav_bottom // self.nav_max_lines - def load_artists(self): - for i, artist in enumerate(self.nav_list): - if i == self.nav_list_selected: - self.nav_win.addstr(i, 0, artist.name, curses.A_REVERSE) - else: - self.nav_win.addstr(i, 0, artist.name) - - def nav_down(self): - self.nav_selected += 1 - self.nav_list_selected += 1 - if self.nav_list_selected > len(self.nav_list): - self.nav_list_selected = len(self.nav_list) - if self.nav_selected >= (curses.LINES - 5): - self.nav_y += 1 - self.nav_selected = curses.LINES - 5 - self.nav_win.clear() - self.load_artists() - self.nav_win.refresh(self.nav_y, 0, 1, 0, - curses.LINES - 2, curses.COLS - 1) - - def nav_up(self): - self.nav_selected -= 1 - self.nav_list_selected -= 1 - if self.nav_list_selected < 0: - self.nav_list_selected = 0 - if self.nav_selected <= 3: - self.nav_y -= 1 - if self.nav_selected < 0: - self.nav_selected = 0 - if self.nav_y < 0: - self.nav_y = 0 - self.nav_win.clear() - self.load_artists() - self.nav_win.refresh(self.nav_y, 0, 1, 0, - curses.LINES - 2, curses.COLS - 1) + def init_curses(self): + self.window = curses.initscr() + self.window.keypad(True) - def refresh(self): - self.stdscr.noutrefresh() - self.nav_win.noutrefresh(self.nav_y, 0, 1, 0, - curses.LINES - 2, curses.COLS - 1) - curses.doupdate() + curses.curs_set(0) # disable cursor + curses.noecho() + curses.cbreak() + + self.height, self.width = self.window.getmaxyx() - def main_loop(self): + def run(self): + # read chars until we exit, then clean + try: + self.input_loop() + except KeyboardInterrupt: + pass + finally: + curses.endwin() + + def input_loop(self): while True: - c = self.stdscr.getkey() + self.window.erase() + self.load_list() + self.refresh() + + c = self.window.getkey() if c == 'q': # quit break elif c == 'j': - self.nav_down() + self.nav_scroll(self.DOWN) elif c == 'k': - self.nav_up() + self.nav_scroll(self.UP) + elif c in [curses.KEY_ENTER, '\r', '\n']: + with open('foo.txt', 'w') as f: + print(f"top: {self.nav_top}", file=f) + print(f"bottom: {self.nav_bottom}", file=f) + print(f"selected: {self.nav_selected}", file=f) + break - def start(self, stdscr): - self.stdscr = stdscr - self.nav_win = curses.newpad(len(self.nav_list) + 1, curses.COLS - 1) - self.stdscr.clear() - curses.curs_set(0) # disable cursor - self.load_artists() - self.refresh() - self.main_loop() + def load_list(self): + for i, t in enumerate( + self.nav_list[self.nav_top:self.nav_top + self.nav_max_lines]): + if i == self.nav_selected: + self.window.addstr(i, 0, t.name, curses.A_REVERSE) + else: + self.window.addstr(i, 0, t.name) + + def nav_scroll(self, direction): + # next cursor position + next_line = self.nav_selected + direction + + # scroll up direction + if direction == self.UP: + # with overflow + # current cursor position is 0 so we are at the first line, + # but top position is greater than 0, so we can scroll up. + # so we substract 1 from nav_top + if self.nav_top > 0 and self.nav_selected == 0: + self.nav_top += direction + return + # without overflow + # current cursor position is greater than 0 + # so we just adjust the selected line. + if self.nav_selected > 0: + self.nav_selected = next_line + return + + # Scroll in down direction + if direction == self.DOWN: + # With overflow + # next cursor position reaches max lines, so we increment the top + # but we take care to not increase if we reach end of list (top + # + one page < than len(list) aka bottom) + if (next_line == self.nav_max_lines + and self.nav_top + self.nav_max_lines < self.nav_bottom): + self.nav_top += direction + return + # Without overflow + # next cursor position is above max lines, so we increment the + # selected line + if next_line < self.nav_max_lines: + self.nav_selected = next_line + return + + def refresh(self): + self.window.noutrefresh() + curses.doupdate() def main(): subclient = SubClient() - curses.wrapper(subclient.start) + subclient.run() if __name__ == "__main__":