(stdscr, title, options, start_index=0)
| 292 | |
| 293 | |
| 294 | def curses_select_from_list(stdscr, title, options, start_index=0): |
| 295 | curses.curs_set(0) |
| 296 | if not options: |
| 297 | return None |
| 298 | idx = start_index |
| 299 | while True: |
| 300 | stdscr.erase() |
| 301 | h, w = stdscr.getmaxyx() |
| 302 | centered_addstr(stdscr, 1, title) |
| 303 | per_page = max(6, h - 8) |
| 304 | page = idx // per_page |
| 305 | start = page * per_page |
| 306 | end = min(len(options), start + per_page) |
| 307 | for i in range(start, end): |
| 308 | attr = curses.A_REVERSE if i == idx else 0 |
| 309 | centered_addstr(stdscr, 3 + i - start, f" {options[i]} ", attr) |
| 310 | centered_addstr(stdscr, h-2, 'Up/Down move, Left/Right page, Enter select, / search, ESC cancel') |
| 311 | stdscr.refresh() |
| 312 | ch = stdscr.getch() |
| 313 | if ch in (curses.KEY_UP,): |
| 314 | idx = (idx - 1) % len(options) |
| 315 | elif ch in (curses.KEY_DOWN,): |
| 316 | idx = (idx + 1) % len(options) |
| 317 | elif ch == curses.KEY_LEFT: |
| 318 | idx = max(0, idx - per_page) |
| 319 | elif ch == curses.KEY_RIGHT: |
| 320 | idx = min(len(options)-1, idx + per_page) |
| 321 | elif ch in (10,13): |
| 322 | return options[idx] |
| 323 | elif ch == 27: |
| 324 | return None |
| 325 | elif ch == ord('/'): |
| 326 | q = curses_input(stdscr, 'Search:') |
| 327 | if q: |
| 328 | ql = q.lower() |
| 329 | for i,opt in enumerate(options): |
| 330 | if ql in opt.lower(): |
| 331 | idx = i |
| 332 | break |
| 333 | |
| 334 | # Search |
| 335 | def curses_search(stdscr, notes): |
no test coverage detected