python-curses-scroll-example icon indicating copy to clipboard operation
python-curses-scroll-example copied to clipboard

Not sure if super relevent but I made a version with a few more features

Open GanerCodes opened this issue 3 years ago • 1 comments

import curses
import curses.textpad

class Screen(object):
    UP, DOWN = -1, 1

    def __init__(self, items):
        self.init_curses()

        self.items = items
        self.top = 0
        self.current = 0
    
    def update_size(self):
        self.max_lines, self.max_width = self.window.getmaxyx()
    
    def init_curses(self):
        self.window = curses.initscr()
        self.window.keypad(True)
        self.update_size()

        curses.noecho()
        curses.cbreak()

        curses.start_color()
        curses.init_pair(1, curses.COLOR_CYAN, curses.COLOR_BLACK)
        curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_CYAN)

    def run(self):
        try:
            self.input_stream()
        except KeyboardInterrupt:
            pass
        finally:
            curses.endwin()

    def input_stream(self):
        while True:
            self.display()

            match self.window.getch():
                case curses.KEY_UP:
                    self.scroll(self.UP)
                case curses.KEY_DOWN:
                    self.scroll(self.DOWN)
                
                case curses.KEY_PPAGE | curses.KEY_LEFT:
                    self.scroll(self.UP * self.max_lines)
                case curses.KEY_NPAGE | curses.KEY_RIGHT:
                    self.scroll(self.DOWN * self.max_lines)
                
                case curses.KEY_HOME:
                    self.scroll(self.UP * 10 ** 10)
                case curses.KEY_END:
                    self.scroll(self.DOWN * 10 ** 10)
                
                case curses.KEY_RESIZE:
                    self.update_size()
                    self.window.refresh()
                    self.scroll()
                
                case curses.ascii.ESC:
                    break

    def scroll(self, direction=0):
        l = len(self.items)
        self.current = min(max(0, self.current + direction), l - 1)
        
        if l < self.max_lines:
            self.top = 0
            return
        
        if self.current >= self.top + self.max_lines:
            self.top = self.current - self.max_lines + 1
        elif self.current < self.top:
            self.top = self.current
        elif self.top > (p := l - self.max_lines):
            self.top = p
        
    def display(self):
        self.window.erase()
        for idx, item in enumerate(self.items[self.top:self.top + self.max_lines]):
            if self.top + idx == self.current:
                self.window.addstr(idx, 0, item, curses.color_pair(2))
            else:
                self.window.addstr(idx, 0, item, curses.color_pair(1))
        self.window.refresh()

items = [f'{num + 1}. Item' for num in range(55)]
screen = Screen(items)
screen.run()```

GanerCodes avatar Sep 04 '22 17:09 GanerCodes

Super useful!! Damn starting to understand why curses is called curses

mvsoom avatar Jan 24 '24 12:01 mvsoom