listpick 0.1.14.7__py3-none-any.whl → 0.1.14.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of listpick might be problematic. Click here for more details.

listpick/listpick_app.py CHANGED
@@ -40,6 +40,7 @@ from listpick.utils.dump import dump_state, load_state, dump_data
40
40
  from listpick.ui.build_help import build_help_rows
41
41
  from listpick.ui.footer import StandardFooter, CompactFooter, NoFooter
42
42
  from listpick.utils.picker_log import setup_logger
43
+ from listpick.utils.user_input import get_char, open_tty
43
44
 
44
45
 
45
46
  try:
@@ -187,6 +188,8 @@ class Picker:
187
188
  sheet_index = 0,
188
189
  sheet_states = [{}],
189
190
 
191
+ redraw_screen_accessory: Callable = lambda : None,
192
+
190
193
  ):
191
194
  self.stdscr = stdscr
192
195
  self.items = items
@@ -335,6 +338,7 @@ class Picker:
335
338
  self.sheet_states = sheet_states
336
339
  self.sheets = sheets
337
340
 
341
+ self.redraw_screen_accessory = redraw_screen_accessory
338
342
  self.initialise_picker_state(reset_colours=self.reset_colours)
339
343
 
340
344
  # Note: We have to set the footer after initialising the picker state so that the footer can use the get_function_data method
@@ -747,6 +751,8 @@ class Picker:
747
751
 
748
752
  def draw_screen_(self, indexed_items: list[Tuple[int, list[str]]], highlights: list[dict] = [{}], clear: bool = True) -> None:
749
753
  """ Draw Picker screen. """
754
+
755
+ self.redraw_screen_accessory()
750
756
  self.logger.debug("Draw screen.")
751
757
 
752
758
  if clear:
@@ -876,10 +882,10 @@ class Picker:
876
882
  else:
877
883
  cell_value = self.indexed_items[row][1][col] + self.separator
878
884
  # cell_value = cell_value[:min(cell_width, cell_max_width)-len(self.separator)]
879
- cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width)-len(self.separator), self.unicode_char_width)
885
+ cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width)-len(self.separator), self.centre_in_cols, self.unicode_char_width)
880
886
  cell_value = cell_value + self.separator
881
887
  # cell_value = cell_value
882
- cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width), self.unicode_char_width)
888
+ cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width), self.centre_in_cols, self.unicode_char_width)
883
889
  # row_str = truncate_to_display_width(row_str_left_adj, min(w-self.startx, visible_columns_total_width))
884
890
  self.stdscr.addstr(y, cell_pos, cell_value, curses.color_pair(self.colours_start+colour_pair_number) | curses.A_BOLD)
885
891
  # Part of the cell is on screen
@@ -1121,6 +1127,7 @@ class Picker:
1121
1127
  "hidden_columns": [],
1122
1128
  "title": title,
1123
1129
  "reset_colours": False,
1130
+ "cell_cursor": False,
1124
1131
  }
1125
1132
 
1126
1133
  OptionPicker = Picker(submenu_win, **infobox_data)
@@ -1235,6 +1242,7 @@ class Picker:
1235
1242
  "sheets": self.sheets,
1236
1243
  "sheet_name": self.sheet_name,
1237
1244
  "sheet_states": self.sheet_states,
1245
+ "redraw_screen_accessory": self.redraw_screen_accessory,
1238
1246
  }
1239
1247
  return function_data
1240
1248
 
@@ -1269,6 +1277,7 @@ class Picker:
1269
1277
  "centre_in_terminal_vertical",
1270
1278
  "centre_in_cols",
1271
1279
  "centre_in_terminal",
1280
+ "redraw_screen_accessory",
1272
1281
  ]
1273
1282
 
1274
1283
  for var in variables:
@@ -1361,6 +1370,7 @@ class Picker:
1361
1370
  "cancel_is_back": True,
1362
1371
  "number_columns": False,
1363
1372
  "reset_colours": False,
1373
+ "cell_cursor": False,
1364
1374
  }
1365
1375
  while True:
1366
1376
  h, w = stdscr.getmaxyx()
@@ -1407,6 +1417,11 @@ class Picker:
1407
1417
 
1408
1418
  submenu_win = curses.newwin(notification_height, notification_width, 3, w - (notification_width+4))
1409
1419
  # submenu_win = self.stdscr.subwin(notification_height, notification_width, 3, w - (notification_width+4))
1420
+ def update_after_resize():
1421
+ h, w = self.stdscr.getmaxyx()
1422
+ submenu_win.mvwin(3, w - (notification_width+4))
1423
+ self.draw_screen(self.indexed_items, self.highlights)
1424
+
1410
1425
  notification_data = {
1411
1426
  "items": submenu_items,
1412
1427
  "title": title,
@@ -1427,9 +1442,15 @@ class Picker:
1427
1442
  "loaded_file_states": [],
1428
1443
  "loaded_file": "",
1429
1444
  "loaded_file_index": 0,
1445
+ "cell_cursor": False,
1446
+ # "redraw_screen_accessory": lambda : self.draw_screen(self.indexed_items, self.highlights),
1447
+ "redraw_screen_accessory": update_after_resize,
1448
+ "get_new_data": False,
1449
+ # "key_remappings": notification_remap_keys,
1430
1450
  }
1431
1451
  OptionPicker = Picker(submenu_win, **notification_data)
1432
1452
  s, o, f = OptionPicker.run()
1453
+ os.system(f"notify-send resizing")
1433
1454
 
1434
1455
  if o != "refresh": break
1435
1456
  submenu_win.clear()
@@ -1438,6 +1459,7 @@ class Picker:
1438
1459
  stdscr.clear()
1439
1460
  stdscr.refresh()
1440
1461
  self.draw_screen(self.indexed_items, self.highlights)
1462
+
1441
1463
  # set_colours(colours=get_colours(0))
1442
1464
 
1443
1465
  def toggle_column_visibility(self, col_index:int) -> None:
@@ -2306,6 +2328,7 @@ class Picker:
2306
2328
  "centre_in_terminal_vertical": True,
2307
2329
  "hidden_columns": [],
2308
2330
  "reset_colours": False,
2331
+ "cell_cursor": False,
2309
2332
 
2310
2333
  }
2311
2334
  OptionPicker = Picker(self.stdscr, **help_data)
@@ -2416,6 +2439,7 @@ class Picker:
2416
2439
  "centre_in_terminal_vertical": True,
2417
2440
  "hidden_columns": [],
2418
2441
  "reset_colours": False,
2442
+ "cell_cursor": False,
2419
2443
 
2420
2444
  }
2421
2445
  OptionPicker = Picker(self.stdscr, **info_data)
@@ -3575,50 +3599,6 @@ def unrestrict_curses(stdscr: curses.window) -> None:
3575
3599
  curses.curs_set(False)
3576
3600
 
3577
3601
 
3578
- def open_tty():
3579
- """ Return a file descriptor for the tty that we are opening"""
3580
- tty_fd = os.open('/dev/tty', os.O_RDONLY)
3581
- tty.setraw(tty_fd)
3582
- return tty_fd
3583
-
3584
- def get_char(tty_fd, timeout: float = 0.2, secondary: bool = False) -> int:
3585
- """ Get character from a tty_fd with a timeout. """
3586
- rlist, _, _ = select.select([tty_fd], [], [], timeout)
3587
- if rlist:
3588
- # key = ord(tty_fd.read(1))
3589
- key = ord(os.read(tty_fd, 1))
3590
- if not secondary:
3591
- if key == 27:
3592
- key2 = get_char(tty_fd, timeout=0.01, secondary=True)
3593
- key3 = get_char(tty_fd, timeout=0.01, secondary=True)
3594
- key4 = get_char(tty_fd, timeout=0.01, secondary=True)
3595
- key5 = get_char(tty_fd, timeout=0.01, secondary=True)
3596
- if key2 == ord('O') and key3 == ord('B'):
3597
- key = curses.KEY_DOWN
3598
- elif key2 == ord('O') and key3 == ord('A'):
3599
- key = curses.KEY_UP
3600
- elif key2 == ord('O') and key3 == ord('D'):
3601
- key = curses.KEY_LEFT
3602
- elif key2 == ord('O') and key3 == ord('C'):
3603
- key = curses.KEY_RIGHT
3604
- elif key2 == ord('[') and key3 == ord('Z'):
3605
- key = 353
3606
- elif key2 == ord('O') and key3 == ord('F'):
3607
- key = curses.KEY_END
3608
- elif key2 == ord('O') and key3 == ord('H'):
3609
- key = curses.KEY_HOME
3610
- elif key2 == ord('[') and key3 == ord('3') and key4 == ord('~'):
3611
- key = curses.KEY_DC
3612
- elif key2 == ord('[') and key3 == ord('3') and key4 == ord('~'):
3613
- key = curses.KEY_DC
3614
- elif key2 == ord('O') and key3 == ord('P'):
3615
- key = curses.KEY_F1
3616
- elif key2 == ord('[') and key3 == ord('1') and key4 == ord('5') and key5 == ord('~'):
3617
- key = curses.KEY_F5
3618
-
3619
- else:
3620
- key = -1
3621
- return key
3622
3602
 
3623
3603
  def main() -> None:
3624
3604
  """ Main function when listpick is executed. Deals with command line arguments and starts a Picker. """
@@ -3693,7 +3673,7 @@ def main() -> None:
3693
3673
  app.load_input_history("~/.config/listpick/cmdhist.json")
3694
3674
  app.run()
3695
3675
 
3696
- # app.save_input_history("~/.config/listpick/cmdhist.json")
3676
+ app.save_input_history("~/.config/listpick/cmdhist.json")
3697
3677
  except Exception as e:
3698
3678
  print(e)
3699
3679
 
@@ -18,22 +18,24 @@ import logging
18
18
  logger = logging.getLogger('picker_log')
19
19
  import select
20
20
  import tty
21
-
22
- def open_tty():
23
- """ Return a file descriptor for the tty that we are opening"""
24
- tty_fd = os.open('/dev/tty', os.O_RDONLY)
25
- tty.setraw(tty_fd)
26
- return tty_fd
27
-
28
- def get_char(tty_fd, timeout: float = 0.2) -> int:
29
- """ Get character from a tty_fd with a timeout. """
30
- rlist, _, _ = select.select([tty_fd], [], [], timeout)
31
- if rlist:
32
- # key = ord(tty_fd.read(1))
33
- key = ord(os.read(tty_fd, 1))
34
- else:
35
- key = -1
36
- return key
21
+ from listpick.utils.user_input import get_char, open_tty
22
+ from listpick.utils import keycodes
23
+
24
+ # def open_tty():
25
+ # """ Return a file descriptor for the tty that we are opening"""
26
+ # tty_fd = os.open('/dev/tty', os.O_RDONLY)
27
+ # tty.setraw(tty_fd)
28
+ # return tty_fd
29
+ #
30
+ # def get_char(tty_fd, timeout: float = 0.2) -> int:
31
+ # """ Get character from a tty_fd with a timeout. """
32
+ # rlist, _, _ = select.select([tty_fd], [], [], timeout)
33
+ # if rlist:
34
+ # # key = ord(tty_fd.read(1))
35
+ # key = ord(os.read(tty_fd, 1))
36
+ # else:
37
+ # key = -1
38
+ # return key
37
39
 
38
40
  def input_field(
39
41
  stdscr: curses.window,
@@ -230,100 +232,94 @@ def input_field(
230
232
  key = get_char(tty_fd, timeout=0.5)
231
233
  # key = stdscr.getch()
232
234
 
233
- if key in [27, 7]: # ESC/ALT key or Ctrl+g
234
- # For Alt-key combinations: set nodelay and get the second key
235
- # stdscr.nodelay(True)
236
- # key2 = stdscr.getch()
237
- key2 = get_char(tty_fd, timeout=0.05)
238
-
239
- if key2 == -1: # ESCAPE key (no key-combination)
240
- stdscr.nodelay(False)
241
- return "", False
242
- elif key2 == curses.KEY_BACKSPACE:
243
- # Delete to backslash or space (word_separator_chars)
244
- search_txt = usrtxt[:-cursor] if cursor > 0 else usrtxt
245
- index = -1
246
- for word_separator_char in word_separator_chars:
247
- tmp_index = search_txt[::-1].find(word_separator_char)
248
- if tmp_index > -1:
249
- if index == -1:
250
- index = tmp_index
251
- else:
252
- index = min(index, tmp_index)
253
-
254
- if index == -1:
255
- if cursor == 0:
256
- kill_ring.append(usrtxt)
257
- usrtxt = ""
258
- else:
259
- kill_ring.append(usrtxt[:-(cursor+1)])
260
- usrtxt = usrtxt[-(cursor+1):]
261
- cursor = len(usrtxt)
262
- else:
263
- if index == 0:
264
- kill_ring.append(search_txt[-1:])
265
- usrtxt = search_txt[:-1] + usrtxt[len(search_txt):]
235
+
236
+ if key in [27, 7]: # ESC/ALT key or Ctrl+g
237
+ return "", False
238
+
239
+ elif key == keycodes.META_BS:
240
+ # Delete to backslash or space (word_separator_chars)
241
+ search_txt = usrtxt[:-cursor] if cursor > 0 else usrtxt
242
+ index = -1
243
+ for word_separator_char in word_separator_chars:
244
+ tmp_index = search_txt[::-1].find(word_separator_char)
245
+ if tmp_index > -1:
246
+ if index == -1:
247
+ index = tmp_index
266
248
  else:
267
- kill_ring.append(search_txt[-index:])
268
- usrtxt = search_txt[:-index] + usrtxt[len(search_txt):]
249
+ index = min(index, tmp_index)
269
250
 
270
- potential_path = usrtxt
271
- kill_ring_active = False
272
-
273
- elif key2 == ord('f'):
274
- # Forward word
275
- search_txt = usrtxt[-cursor:]
276
- index = -1
277
- for word_separator_char in word_separator_chars:
278
- tmp_index = search_txt.find(word_separator_char)
279
-
280
- if tmp_index > -1:
281
- if index == -1:
282
- index = tmp_index
283
- else:
284
- index = min(index, tmp_index)
285
-
286
- if index == -1:
287
- cursor = 0
251
+ if index == -1:
252
+ if cursor == 0:
253
+ kill_ring.append(usrtxt)
254
+ usrtxt = ""
288
255
  else:
289
- cursor -= index + 1
290
- cursor = max(cursor, 0)
291
- kill_ring_active = False
292
-
293
- elif key2 == ord('b'):
294
- # Backwards word
295
- search_txt = usrtxt[:-cursor] if cursor > 0 else usrtxt
296
- index = -1
297
- for word_separator_char in word_separator_chars:
298
- tmp_index = search_txt[::-1].find(word_separator_char)
299
-
300
- if tmp_index == 0:
301
- tmp_index = search_txt[:-1][::-1].find(word_separator_char)
302
-
303
- if tmp_index > -1:
304
- if index == -1:
305
- index = tmp_index
306
- else:
307
- index = min(index, tmp_index)
308
-
309
- if index == -1:
310
- cursor = len(usrtxt)
256
+ kill_ring.append(usrtxt[:-(cursor+1)])
257
+ usrtxt = usrtxt[-(cursor+1):]
258
+ cursor = len(usrtxt)
259
+ else:
260
+ if index == 0:
261
+ kill_ring.append(search_txt[-1:])
262
+ usrtxt = search_txt[:-1] + usrtxt[len(search_txt):]
311
263
  else:
312
- cursor += index + 1
313
- kill_ring_active = False
314
-
315
- elif key2 == ord('y'):
316
- prev_kill_ring_index = kill_ring_index
317
- kill_ring_index = (kill_ring_index + 1)%len(kill_ring)
318
- if kill_ring_active and len(kill_ring):
319
- if cursor == 0:
320
- usrtxt = usrtxt[:-len(kill_ring[prev_kill_ring_index])]
321
- usrtxt += kill_ring[kill_ring_index]
264
+ kill_ring.append(search_txt[-index:])
265
+ usrtxt = search_txt[:-index] + usrtxt[len(search_txt):]
266
+
267
+ potential_path = usrtxt
268
+ kill_ring_active = False
269
+
270
+ elif key == keycodes.META_f:
271
+ # Forward word
272
+ search_txt = usrtxt[-cursor:]
273
+ index = -1
274
+ for word_separator_char in word_separator_chars:
275
+ tmp_index = search_txt.find(word_separator_char)
276
+
277
+ if tmp_index > -1:
278
+ if index == -1:
279
+ index = tmp_index
280
+ else:
281
+ index = min(index, tmp_index)
282
+
283
+ if index == -1:
284
+ cursor = 0
285
+ else:
286
+ cursor -= index + 1
287
+ cursor = max(cursor, 0)
288
+ kill_ring_active = False
289
+
290
+ elif key == keycodes.META_b:
291
+ # Backwards word
292
+ search_txt = usrtxt[:-cursor] if cursor > 0 else usrtxt
293
+ index = -1
294
+ for word_separator_char in word_separator_chars:
295
+ tmp_index = search_txt[::-1].find(word_separator_char)
296
+
297
+ if tmp_index == 0:
298
+ tmp_index = search_txt[:-1][::-1].find(word_separator_char)
299
+
300
+ if tmp_index > -1:
301
+ if index == -1:
302
+ index = tmp_index
322
303
  else:
323
- usrtxt = usrtxt[-cursor:-(cursor+len(kill_ring[prev_kill_ring_index]))]
324
- usrtxt = usrtxt[:-cursor] + kill_ring[kill_ring_index] + usrtxt[-cursor:]
325
- # Return to delayed getch
326
- stdscr.nodelay(False)
304
+ index = min(index, tmp_index)
305
+
306
+ if index == -1:
307
+ cursor = len(usrtxt)
308
+ else:
309
+ cursor += index + 1
310
+ kill_ring_active = False
311
+
312
+ elif key == keycodes.META_y:
313
+ # Kill ring
314
+ prev_kill_ring_index = kill_ring_index
315
+ kill_ring_index = (kill_ring_index + 1)%len(kill_ring)
316
+ if kill_ring_active and len(kill_ring):
317
+ if cursor == 0:
318
+ usrtxt = usrtxt[:-len(kill_ring[prev_kill_ring_index])]
319
+ usrtxt += kill_ring[kill_ring_index]
320
+ else:
321
+ usrtxt = usrtxt[-cursor:-(cursor+len(kill_ring[prev_kill_ring_index]))]
322
+ usrtxt = usrtxt[:-cursor] + kill_ring[kill_ring_index] + usrtxt[-cursor:]
327
323
 
328
324
  elif key == 3: # ctrl+c
329
325
  # Immediate exit
listpick/ui/keys.py CHANGED
@@ -9,6 +9,7 @@ License: MIT
9
9
  """
10
10
 
11
11
  import curses
12
+ from listpick.utils import keycodes
12
13
 
13
14
  picker_keys = {
14
15
  "refresh": [curses.KEY_F5],
@@ -25,8 +26,8 @@ picker_keys = {
25
26
  "page_down": [curses.KEY_NPAGE, 6], # Ctrl+f
26
27
  "cursor_bottom": [ord('G'), curses.KEY_END],
27
28
  "cursor_top": [ord('g'), curses.KEY_HOME],
28
- "five_up": [ord('K')],
29
- "five_down": [ord('J')],
29
+ "five_up": [ord('K'), keycodes.META_k],
30
+ "five_down": [ord('J'), keycodes.META_j],
30
31
  "toggle_select": [ord(' ')],
31
32
  "select_all": [ord('m'), 1], # Ctrl-a
32
33
  "select_none": [ord('M'), 18], # Ctrl-r
@@ -135,6 +136,7 @@ notification_keys = {
135
136
  "five_up": [ord('K')],
136
137
  "five_down": [ord('J')],
137
138
  "redraw_screen": [12], # Ctrl-l
139
+ "refresh": [curses.KEY_F5, curses.KEY_RESIZE],
138
140
  }
139
141
 
140
142
 
@@ -0,0 +1,88 @@
1
+ # Set a base value to ensure that we have no collisions with any unicode characters
2
+ BASE_VALUE = 2**32
3
+
4
+ # Define constants for alt+a to alt+z
5
+ META_A = BASE_VALUE + 1000
6
+ META_B = BASE_VALUE + 1001
7
+ META_C = BASE_VALUE + 1002
8
+ META_D = BASE_VALUE + 1003
9
+ META_E = BASE_VALUE + 1004
10
+ META_F = BASE_VALUE + 1005
11
+ META_G = BASE_VALUE + 1006
12
+ META_H = BASE_VALUE + 1007
13
+ META_I = BASE_VALUE + 1008
14
+ META_J = BASE_VALUE + 1009
15
+ META_K = BASE_VALUE + 1010
16
+ META_L = BASE_VALUE + 1011
17
+ META_M = BASE_VALUE + 1012
18
+ META_N = BASE_VALUE + 1013
19
+ META_O = BASE_VALUE + 1014
20
+ META_P = BASE_VALUE + 1015
21
+ META_Q = BASE_VALUE + 1016
22
+ META_R = BASE_VALUE + 1017
23
+ META_S = BASE_VALUE + 1018
24
+ META_T = BASE_VALUE + 1019
25
+ META_U = BASE_VALUE + 1020
26
+ META_V = BASE_VALUE + 1021
27
+ META_W = BASE_VALUE + 1022
28
+ META_X = BASE_VALUE + 1023
29
+ META_Y = BASE_VALUE + 1024
30
+ META_Z = BASE_VALUE + 1025
31
+
32
+ # Define constants for alt+A to alt+Z (using uppercase)
33
+ META_a = BASE_VALUE + 1050
34
+ META_b = BASE_VALUE + 1051
35
+ META_c = BASE_VALUE + 1052
36
+ META_d = BASE_VALUE + 1053
37
+ META_e = BASE_VALUE + 1054
38
+ META_f = BASE_VALUE + 1055
39
+ META_g = BASE_VALUE + 1056
40
+ META_h = BASE_VALUE + 1057
41
+ META_i = BASE_VALUE + 1058
42
+ META_j = BASE_VALUE + 1059
43
+ META_k = BASE_VALUE + 1060
44
+ META_l = BASE_VALUE + 1061
45
+ META_m = BASE_VALUE + 1062
46
+ META_n = BASE_VALUE + 1063
47
+ META_o = BASE_VALUE + 1064
48
+ META_p = BASE_VALUE + 1065
49
+ META_q = BASE_VALUE + 1066
50
+ META_r = BASE_VALUE + 1067
51
+ META_s = BASE_VALUE + 1068
52
+ META_t = BASE_VALUE + 1069
53
+ META_u = BASE_VALUE + 1070
54
+ META_v = BASE_VALUE + 1071
55
+ META_w = BASE_VALUE + 1072
56
+ META_x = BASE_VALUE + 1073
57
+ META_y = BASE_VALUE + 1074
58
+ META_z = BASE_VALUE + 1075
59
+
60
+
61
+ META_0 = BASE_VALUE + 1090
62
+ META_1 = BASE_VALUE + 1091
63
+ META_2 = BASE_VALUE + 1092
64
+ META_3 = BASE_VALUE + 1093
65
+ META_4 = BASE_VALUE + 1094
66
+ META_5 = BASE_VALUE + 1095
67
+ META_6 = BASE_VALUE + 1096
68
+ META_7 = BASE_VALUE + 1097
69
+ META_8 = BASE_VALUE + 1098
70
+ META_9 = BASE_VALUE + 1099
71
+
72
+
73
+ META_BS = BASE_VALUE + 1100
74
+
75
+ # Dictionary to map character to its constant value
76
+ META_KEY_MAP = {
77
+ 'a': META_a, 'b': META_b, 'c': META_c, 'd': META_d, 'e': META_e,
78
+ 'f': META_f, 'g': META_g, 'h': META_h, 'i': META_i, 'j': META_j,
79
+ 'k': META_k, 'l': META_l, 'm': META_m, 'n': META_n, 'o': META_o,
80
+ 'p': META_p, 'q': META_q, 'r': META_r, 's': META_s, 't': META_t,
81
+ 'u': META_u, 'v': META_v, 'w': META_w, 'x': META_x, 'y': META_y,
82
+ 'z': META_z, 'A': META_A, 'B': META_B, 'C': META_C, 'D': META_D,
83
+ 'E': META_E, 'F': META_F, 'G': META_G, 'H': META_H, 'I': META_I,
84
+ 'J': META_J, 'K': META_K, 'L': META_L, 'M': META_M, 'N': META_N,
85
+ 'O': META_O, 'P': META_P, 'Q': META_Q, 'R': META_R, 'S': META_S,
86
+ 'T': META_T, 'U': META_U, 'V': META_V, 'W': META_W, 'X': META_X,
87
+ 'Y': META_Y, 'Z': META_Z
88
+ }
@@ -0,0 +1,104 @@
1
+ from listpick.utils import keycodes
2
+ import os, tty, select, curses
3
+
4
+ def open_tty():
5
+ """ Return a file descriptor for the tty that we are opening"""
6
+ tty_fd = os.open('/dev/tty', os.O_RDONLY)
7
+ tty.setraw(tty_fd)
8
+ return tty_fd
9
+
10
+ def get_char(tty_fd, timeout: float = 0.2, secondary: bool = False) -> int:
11
+ """ Get character from a tty_fd with a timeout. """
12
+ rlist, _, _ = select.select([tty_fd], [], [], timeout)
13
+ if rlist:
14
+ # key = ord(tty_fd.read(1))
15
+ key = ord(os.read(tty_fd, 1))
16
+ if not secondary:
17
+ if key == 27:
18
+ key2 = get_char(tty_fd, timeout=0.01, secondary=True)
19
+ key3 = get_char(tty_fd, timeout=0.01, secondary=True)
20
+ key4 = get_char(tty_fd, timeout=0.01, secondary=True)
21
+ key5 = get_char(tty_fd, timeout=0.01, secondary=True)
22
+ key6 = get_char(tty_fd, timeout=0.01, secondary=True)
23
+
24
+ keys = [key2, key3, key4, key5, key6]
25
+
26
+ key_str = "".join([chr(k) for k in keys if k != -1])
27
+
28
+ ## Arrow Keys
29
+ if key2 == ord('O') and key3 == ord('B'):
30
+ key = curses.KEY_DOWN
31
+ elif key2 == ord('O') and key3 == ord('A'):
32
+ key = curses.KEY_UP
33
+ elif key2 == ord('O') and key3 == ord('D'):
34
+ key = curses.KEY_LEFT
35
+ elif key2 == ord('O') and key3 == ord('C'):
36
+ key = curses.KEY_RIGHT
37
+
38
+ ## Shift+ Tab
39
+ elif key2 == ord('[') and key3 == ord('Z'):
40
+ key = 353
41
+
42
+ ## Home, End, Pgup, Pgdn
43
+ elif key2 == ord('O') and key3 == ord('F'):
44
+ key = curses.KEY_END
45
+ elif key2 == ord('O') and key3 == ord('H'):
46
+ key = curses.KEY_HOME
47
+ elif key2 == ord('[') and key3 == ord('6') and key4 == ord("~"):
48
+ key = curses.KEY_NPAGE
49
+ elif key2 == ord('[') and key3 == ord('5') and key4 == ord("~"):
50
+ key = curses.KEY_PPAGE
51
+
52
+
53
+ # Delete key
54
+ elif key_str == "[3~": ## Delete
55
+ key = curses.KEY_DC
56
+ elif key_str == "[3;2~": ## Shift+Delete
57
+ key = 383
58
+
59
+ ## Function Keys
60
+ elif key2 == ord('O') and key3 == ord('P'):
61
+ key = curses.KEY_F1
62
+ elif key_str == "OQ":
63
+ key = curses.KEY_F2
64
+ elif key_str == "OR":
65
+ key = curses.KEY_F3
66
+ elif key_str == "OS":
67
+ key = curses.KEY_F4
68
+ elif key_str == "[15~":
69
+ key = curses.KEY_F5
70
+ elif key_str == "[17~":
71
+ key = curses.KEY_F6
72
+ elif key_str == "[17~":
73
+ key = curses.KEY_F7
74
+ elif key_str == "[19~":
75
+ key = curses.KEY_F8
76
+ elif key_str == "[20~":
77
+ key = curses.KEY_F9
78
+ elif key_str == "[21~":
79
+ key = curses.KEY_F10
80
+ elif key_str == "[23~":
81
+ key = curses.KEY_F11
82
+ elif key_str == "[24~":
83
+ key = curses.KEY_F12
84
+
85
+ ## Alt+KEY
86
+ elif key2 >= ord('a') and key2 <= ord('z') and key3 == -1: ## Alt+[a-zA-Z]
87
+ key = keycodes.META_a + (key2 - ord('a'))
88
+ elif key2 >= ord('A') and key2 <= ord('Z') and key3 == -1: ## Alt+[a-zA-Z]
89
+ key = keycodes.META_A + (key2 - ord('A'))
90
+ elif key2 == ord('0') and key3 == -1: ## Alt+0
91
+ key = keycodes.META_0
92
+ elif key2 >= ord('1') and key2 <= ord('9') and key3 == -1: ## Alt+1-9
93
+ key = keycodes.META_1 + (key2 - ord('1'))
94
+ elif key2 == 127: ## Alt+BS
95
+ key = keycodes.META_BS
96
+
97
+ # If it is an unknown key with an escape sequence then return -1.
98
+ elif key2 != -1:
99
+ key = -1
100
+
101
+
102
+ else:
103
+ key = -1
104
+ return key
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: listpick
3
- Version: 0.1.14.7
3
+ Version: 0.1.14.9
4
4
  Summary: Listpick is a powerful TUI data tool for creating TUI apps or viewing/comparing tabulated data.
5
5
  Home-page: https://github.com/grimandgreedy/listpick
6
6
  Author: Grim
@@ -1,13 +1,12 @@
1
1
  listpick/__init__.py,sha256=ExXc97-bibodH--wlwpQivl0zCNR5D1hvpvrf7OBofU,154
2
2
  listpick/__main__.py,sha256=wkCjDdqw093W27yWwnlC3nG_sMRKaIad7hHHWy0RBgY,193
3
- listpick/listpick_app.py,sha256=ZSfPfFbztHdjRiZVTiUi0XEKgCLWsupLrGmCPi2T9-I,188702
3
+ listpick/listpick_app.py,sha256=9urXL8NUghj8tdbUCLxCa48nrgzeW4B41lv0_b_LRAw,187823
4
4
  listpick/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  listpick/ui/build_help.py,sha256=8QtsRosIE2IMagRc_remzmwSWpCurFgLenLL7w1ly94,8949
6
6
  listpick/ui/footer.py,sha256=ZM5OWCxOZqy5RG6tFTe1ipvu9PRu6Gh3JCA8KXBR_Wc,15004
7
7
  listpick/ui/help_screen.py,sha256=zbfGIgb-IXtATpl4_Sx7nPbsnRXZ7eiMYlCKGS9EFmw,5608
8
- listpick/ui/input_field.py,sha256=PrYZICoT9M4e85W755d909TItscOae2LYvYfQsWtiXE,31068
9
- listpick/ui/keycodes.py,sha256=1UilWAwtjhs801BrP-SndP-qIRS3AQJHl6KYKrWPgus,1614
10
- listpick/ui/keys.py,sha256=C9wG_VPhaXq_c2bREBGKhd4Tb-AKqqOgub7y4W8xQmI,13182
8
+ listpick/ui/input_field.py,sha256=ylf3fiLXdAD4pueHWfzIrlwaRb9f5zm8f1UGkEPBxgM,30539
9
+ listpick/ui/keys.py,sha256=7ZhJfsSatpk-jwfXj_FvzgQsQdUoF7JkD5Mniu9XZ0o,13328
11
10
  listpick/ui/pane_stuff.py,sha256=7GXa4UnV_7YmBv-baRi5moN51wYcuS4p0odl5C3m0Tc,169
12
11
  listpick/ui/picker_colours.py,sha256=FLOzvkq83orrN2bL0Mw-6RugWOZyuwUjQCrUFMUnKGY,11563
13
12
  listpick/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -16,6 +15,7 @@ listpick/utils/config.py,sha256=MEnAZg2Rhfl38XofEIN0uoVAOY7I0ftc79Evk3fOiVw,1654
16
15
  listpick/utils/dump.py,sha256=60YVIMNtBoYvWhmzfTJOsNGcetOvcCB3_T7yv-bYTPQ,3838
17
16
  listpick/utils/filtering.py,sha256=uS9sW0inmFvq0cIrwRC1BfuP8kjAD5IWWtls4jGB-70,1199
18
17
  listpick/utils/generate_data.py,sha256=7sv6JRhk0-Gcj4kOlkzx4qPNBJZ-GFWg9vM77GktzpI,3073
18
+ listpick/utils/keycodes.py,sha256=ZGkw1-4szxPnP81wj80r92L6_neIOlBBjQltEieCwnk,2696
19
19
  listpick/utils/options_selectors.py,sha256=Vbv4jRkUsSPs7g-EQAv9Q0nhYy6Z4sFsJqMjUIe1oeQ,2814
20
20
  listpick/utils/paste_operations.py,sha256=7wDXLPlxUgA3CA99gwsm47juWGO2YQ9EJghW06yo9vI,1242
21
21
  listpick/utils/picker_log.py,sha256=SW6GmjxpI7YrSf72fSr4O8Ux0fY_OzaSXUgTFdz6Xo4,805
@@ -23,10 +23,11 @@ listpick/utils/search_and_filter_utils.py,sha256=XxGfkyDVXO9OAKcftPat8IReMTFIuTH
23
23
  listpick/utils/searching.py,sha256=Xk5UIqamNHL2L90z3ACB_Giqdpi9iRKoAJ6pKaqaD7Q,3093
24
24
  listpick/utils/sorting.py,sha256=WZZiVlVA3Zkcpwji3U5SNFlQ14zVEk3cZJtQirBkecQ,5329
25
25
  listpick/utils/table_to_list_of_lists.py,sha256=XBj7eGBDF15BRME-swnoXyOfZWxXCxrXp0pzsBfcJ5g,12224
26
+ listpick/utils/user_input.py,sha256=oyJZPAwA7UGAclPhdPL44tKnPIVNHWhX-tZEnCdBKC0,4318
26
27
  listpick/utils/utils.py,sha256=McOl9uT3jh7l4TIWeSd8ZGjK_e7r0YZF0Gl20yI6fl0,13873
27
- listpick-0.1.14.7.dist-info/licenses/LICENSE.txt,sha256=2mP-MRHJptADDNE9VInMNg1tE-C6Qv93Z4CCQKrpg9w,1061
28
- listpick-0.1.14.7.dist-info/METADATA,sha256=L6KW4_BETNSyFC4HvwMLyzoU-K1uZy2KSF5RhV4z-II,8090
29
- listpick-0.1.14.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
- listpick-0.1.14.7.dist-info/entry_points.txt,sha256=-QCf_BKIkUz35Y9nkYpjZWs2Qg0KfRna2PAs5DnF6BE,43
31
- listpick-0.1.14.7.dist-info/top_level.txt,sha256=5mtsGEz86rz3qQDe0D463gGjAfSp6A3EWg4J4AGYr-Q,9
32
- listpick-0.1.14.7.dist-info/RECORD,,
28
+ listpick-0.1.14.9.dist-info/licenses/LICENSE.txt,sha256=2mP-MRHJptADDNE9VInMNg1tE-C6Qv93Z4CCQKrpg9w,1061
29
+ listpick-0.1.14.9.dist-info/METADATA,sha256=kgR_E4Y4IFLqmrx8nDzgLOvHqW41BPWYuAv_BX8j0bU,8090
30
+ listpick-0.1.14.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
+ listpick-0.1.14.9.dist-info/entry_points.txt,sha256=-QCf_BKIkUz35Y9nkYpjZWs2Qg0KfRna2PAs5DnF6BE,43
32
+ listpick-0.1.14.9.dist-info/top_level.txt,sha256=5mtsGEz86rz3qQDe0D463gGjAfSp6A3EWg4J4AGYr-Q,9
33
+ listpick-0.1.14.9.dist-info/RECORD,,
listpick/ui/keycodes.py DELETED
@@ -1,70 +0,0 @@
1
- # Define constants for alt+a to alt+z
2
- META_A = 1000
3
- META_B = 1001
4
- META_C = 1002
5
- META_D = 1003
6
- META_E = 1004
7
- META_F = 1005
8
- META_G = 1006
9
- META_H = 1007
10
- META_I = 1008
11
- META_J = 1009
12
- META_K = 1010
13
- META_L = 1011
14
- META_M = 1012
15
- META_N = 1013
16
- META_O = 1014
17
- META_P = 1015
18
- META_Q = 1016
19
- META_R = 1017
20
- META_S = 1018
21
- META_T = 1019
22
- META_U = 1020
23
- META_V = 1021
24
- META_W = 1022
25
- META_X = 1023
26
- META_Y = 1024
27
- META_Z = 1025
28
-
29
- # Define constants for alt+A to alt+Z (using uppercase)
30
- META_a = 1050
31
- META_b = 1051
32
- META_c = 1052
33
- META_d = 1053
34
- META_e = 1054
35
- META_f = 1055
36
- META_g = 1056
37
- META_h = 1057
38
- META_i = 1058
39
- META_j = 1059
40
- META_k = 1060
41
- META_l = 1061
42
- META_m = 1062
43
- META_n = 1063
44
- META_o = 1064
45
- META_p = 1065
46
- META_q = 1066
47
- META_r = 1067
48
- META_s = 1068
49
- META_t = 1069
50
- META_u = 1070
51
- META_v = 1071
52
- META_w = 1072
53
- META_x = 1073
54
- META_y = 1074
55
- META_z = 1075
56
-
57
- # Dictionary to map character to its constant value
58
- META_KEY_MAP = {
59
- 'a': META_a, 'b': META_b, 'c': META_c, 'd': META_d, 'e': META_e,
60
- 'f': META_f, 'g': META_g, 'h': META_h, 'i': META_i, 'j': META_j,
61
- 'k': META_k, 'l': META_l, 'm': META_m, 'n': META_n, 'o': META_o,
62
- 'p': META_p, 'q': META_q, 'r': META_r, 's': META_s, 't': META_t,
63
- 'u': META_u, 'v': META_v, 'w': META_w, 'x': META_x, 'y': META_y,
64
- 'z': META_z, 'A': META_A, 'B': META_B, 'C': META_C, 'D': META_D,
65
- 'E': META_E, 'F': META_F, 'G': META_G, 'H': META_H, 'I': META_I,
66
- 'J': META_J, 'K': META_K, 'L': META_L, 'M': META_M, 'N': META_N,
67
- 'O': META_O, 'P': META_P, 'Q': META_Q, 'R': META_R, 'S': META_S,
68
- 'T': META_T, 'U': META_U, 'V': META_V, 'W': META_W, 'X': META_X,
69
- 'Y': META_Y, 'Z': META_Z
70
- }