listpick 0.1.13.60__py3-none-any.whl → 0.1.13.62__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
@@ -449,8 +449,6 @@ class Picker:
449
449
  # self.stdscr.refresh()
450
450
  # self.draw_screen(self.indexed_items, self.highlights)
451
451
 
452
-
453
-
454
452
  def initialise_variables(self, get_data: bool = False) -> None:
455
453
  """ Initialise the variables that keep track of the data. """
456
454
 
@@ -473,6 +471,7 @@ class Picker:
473
471
 
474
472
  self.items, self.header = self.refresh_function()
475
473
 
474
+ self.items = pad_lists_to_same_length(self.items)
476
475
 
477
476
  if self.items == []: self.items = [[]]
478
477
  ## Ensure that items is a List[List[Str]] object
@@ -1261,7 +1260,12 @@ class Picker:
1261
1260
  message_width = notification_width-5
1262
1261
 
1263
1262
  if not message: message = "!!"
1264
- submenu_items = [" "+message[i*message_width:(i+1)*message_width] for i in range(len(message)//message_width+1)]
1263
+ if type(message) == type(""):
1264
+ mw = message_width
1265
+ submenu_items = [[message[i*mw:(i+1)*mw]] for i in range(len(message)//mw+1)]
1266
+ elif type(message) != type([]):
1267
+ submenu_items = [[" !!"]]
1268
+
1265
1269
 
1266
1270
  notification_remap_keys = {
1267
1271
  curses.KEY_RESIZE: curses.KEY_F5,
@@ -1959,7 +1963,8 @@ class Picker:
1959
1963
 
1960
1964
  while True:
1961
1965
  key = self.stdscr.getch()
1962
- self.logger.info(f"key={key}")
1966
+ if key:
1967
+ self.logger.info(f"key={key}")
1963
1968
  h, w = self.stdscr.getmaxyx()
1964
1969
  if key in self.disabled_keys: continue
1965
1970
  clear_screen=True
@@ -2760,26 +2765,46 @@ class Picker:
2760
2765
  if not selected_indices:
2761
2766
  selected_indices = [self.indexed_items[self.cursor_pos][0]]
2762
2767
 
2763
- full_values = [format_row_full(self.items[i], self.hidden_columns) for i in selected_indices] # Use format_row_full for full data
2764
- full_values = [self.items[i][self.selected_column] for i in selected_indices]
2768
+ # full_values = [format_row_full(self.items[i], self.hidden_columns) for i in selected_indices] # Use format_row_full for full data
2769
+ if self.cell_cursor:
2770
+
2771
+ full_values = []
2772
+ for row in self.selected_cells_by_row.keys():
2773
+ selected_cell_row_str = ""
2774
+ for cell in self.selected_cells_by_row[row]:
2775
+ if " " in self.items[row][cell]:
2776
+ selected_cell_row_str += repr(self.items[row][cell])
2777
+ else:
2778
+ selected_cell_row_str += self.items[row][cell]
2779
+ selected_cell_row_str += "\t"
2780
+ full_values.append(selected_cell_row_str.strip())
2781
+
2782
+
2783
+ # full_values = ["\t".join([repr(self.items[key][cell]) for cell in self.selected_cells_by_row[key]]) for key in self.selected_cells_by_row.keys()]
2784
+ # full_values = ["\t".join([self.items[key][cell] for cell in self.selected_cells_by_row[key]]) for key in self.selected_cells_by_row.keys()]
2785
+ else:
2786
+ full_values = [self.items[i][self.selected_column] for i in selected_indices]
2765
2787
  if full_values:
2766
- command = usrtxt.split()
2788
+ # command = usrtxt.split()
2789
+ command = usrtxt
2767
2790
  # command = ['xargs', '-d' , '"\n"' '-I', '{}', 'mpv', '{}']
2768
2791
  # command = ['xargs', '-d' , '"\n"' '-I', '{}', 'mpv', '{}']
2769
2792
  # command = "xargs -d '\n' -I{} mpv {}"
2770
2793
 
2771
2794
  try:
2772
- process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
2795
+ process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=True)
2773
2796
 
2774
2797
  if process.stdin != None:
2775
2798
  for value in full_values:
2776
- process.stdin.write((repr(value) + '\n').encode())
2799
+ process.stdin.write((value + '\n').encode())
2800
+ # process.stdin.write((value + '\n').encode())
2777
2801
 
2778
2802
  process.stdin.close()
2779
2803
 
2780
2804
  self.notification(self.stdscr, message=f"{len(full_values)} strings piped to {repr(usrtxt)}")
2781
2805
  except Exception as e:
2782
2806
  self.notification(self.stdscr, message=f"{e}")
2807
+ # self.notification(self.stdscr, message=f"Error: {str(e)}")
2783
2808
 
2784
2809
 
2785
2810
  elif self.check_key("open", key, self.keys_dict):
@@ -3158,9 +3183,9 @@ def main() -> None:
3158
3183
  function_data["centre_in_terminal_vertical"] = True
3159
3184
  function_data["highlight_full_row"] = True
3160
3185
  function_data["pin_cursor"] = True
3161
- function_data["display_infobox"] = True
3162
- function_data["infobox_items"] = [["1"], ["2"], ["3"]]
3163
- function_data["infobox_title"] = "Title"
3186
+ # function_data["display_infobox"] = True
3187
+ # function_data["infobox_items"] = [["1"], ["2"], ["3"]]
3188
+ # function_data["infobox_title"] = "Title"
3164
3189
  # function_data["footer_string"] = "Title"
3165
3190
  function_data["highlights"] = highlights
3166
3191
  function_data["show_footer"] = False
@@ -25,7 +25,10 @@ def generate_columns(funcs: list, files: list) -> list[list[str]]:
25
25
  for file in files:
26
26
  item = []
27
27
  for func in funcs:
28
- item.append(func(file))
28
+ try:
29
+ item.append(func(file))
30
+ except:
31
+ item.append("")
29
32
  items.append(item)
30
33
 
31
34
  return items
listpick/utils/utils.py CHANGED
@@ -344,3 +344,22 @@ def guess_file_type(filename: str) -> str:
344
344
  """ Guess filetype. Currently just uses the extension of the file. """
345
345
  logger.info("function: guess_file_type (utils.py)")
346
346
  return filename.split(".")[-1]
347
+
348
+
349
+ def pad_lists_to_same_length(list_of_lists: list[list[str]]) -> list[list[str]]:
350
+ """ Ensure that all lists in a list of lists are of the same length. Pad any shorter sublists with empty strings. """
351
+ if not list_of_lists or list_of_lists in [[], [[]]]:
352
+ return []
353
+ if type(list_of_lists) == type([]) and len(list_of_lists) and type(list_of_lists[0]) == type(""):
354
+ list_of_lists = [[x] for x in list_of_lists]
355
+
356
+ # Find the maximum length of the sublists
357
+ lengths = [len(sublist) for sublist in list_of_lists]
358
+ max_length = max(lengths)
359
+ min_length = min(lengths)
360
+ if min_length == max_length: return list_of_lists
361
+
362
+ # Pad each sublist with empty strings to match the maximum length
363
+ padded_list = [sublist + [''] * (max_length - len(sublist)) for sublist in list_of_lists]
364
+
365
+ return padded_list
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: listpick
3
- Version: 0.1.13.60
3
+ Version: 0.1.13.62
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
@@ -80,7 +80,7 @@ close_curses(stdscr)
80
80
  Use the listpick binary to generate and display rows based on a list of commands:
81
81
 
82
82
  ```
83
- wget https://raw.githubusercontent.com/grimandgreedy/listpick/refs/heads/master/examples/list_files.toml
83
+ wget https://raw.githubusercontent.com/grimandgreedy/listpick/refs/heads/master/examples/data_generation/list_files.toml
84
84
  listpick -g list_files.py
85
85
  ```
86
86
 
@@ -100,9 +100,9 @@ The application allows you to:
100
100
  ## Examples
101
101
 
102
102
 
103
- ### Identify video duplicates (./examples/video_duplicates.toml):
103
+ ### Identify video duplicates (./examples/data_generation//video_duplicates.toml):
104
104
  ```python
105
- listpick -g ./examples/video_duplicates.toml
105
+ listpick -g ./examples/data_generation/video_duplicates.toml
106
106
  ```
107
107
  - From the list of commands in the toml file we generate the properties we will use to identify the duplicates.
108
108
 
@@ -142,7 +142,7 @@ listpick -i ~/dn.pkl -t pkl
142
142
 
143
143
  2. **Generate data based on an toml file with relevant commands to generate the rows.**
144
144
  ```python
145
- listpick -g ./examples/video_duplicates.toml
145
+ listpick -g ./examples/data_generation/video_duplicates.toml
146
146
  ```
147
147
 
148
148
  - See ./examples/
@@ -1,10 +1,9 @@
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=pduaE0qecZJ9fRYpPNEE-QGhgx7r5g2yHPNZFMZV8zg,165194
3
+ listpick/listpick_app.py,sha256=OE6Py8M0R0TVAb72SCiCk9MSc3TLh4ZgO2qe5xBtNro,166710
4
4
  listpick/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  listpick/ui/build_help.py,sha256=_rVKKrX3HfFJtw-pyeNb2lQWbml4-AAw8sZIUYGn97Y,8731
6
6
  listpick/ui/footer.py,sha256=s1L68MNmhWwbWRy0mn0ChmnE_dMQBAzNlTv917pyHE0,10673
7
- listpick/ui/footer_1.py,sha256=Mpn0gFAnX_Ely-Jl5KPP6sdZRnk8makt8EVBh9wdr4Y,10710
8
7
  listpick/ui/help_screen.py,sha256=zbfGIgb-IXtATpl4_Sx7nPbsnRXZ7eiMYlCKGS9EFmw,5608
9
8
  listpick/ui/input_field.py,sha256=eyoWHoApdZybjfXcp7Eth7xwb-C-856ZVnq5j_Q3Ojs,30412
10
9
  listpick/ui/keys.py,sha256=TzaadgBP_rC7jbp--RFJZDOkHd0EB4K1wToDTiVs6CI,13029
@@ -15,7 +14,7 @@ listpick/utils/clipboard_operations.py,sha256=ORdNm2kgGbfs51xJSvgJPERgoSmBgT11ax
15
14
  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
- listpick/utils/generate_data.py,sha256=ByfEgPywAWp-Nzso5QgFr_Obf-TshZn8Evvb9EwQY3E,3000
17
+ listpick/utils/generate_data.py,sha256=7sv6JRhk0-Gcj4kOlkzx4qPNBJZ-GFWg9vM77GktzpI,3073
19
18
  listpick/utils/options_selectors.py,sha256=Vbv4jRkUsSPs7g-EQAv9Q0nhYy6Z4sFsJqMjUIe1oeQ,2814
20
19
  listpick/utils/paste_operations.py,sha256=7wDXLPlxUgA3CA99gwsm47juWGO2YQ9EJghW06yo9vI,1242
21
20
  listpick/utils/picker_log.py,sha256=SW6GmjxpI7YrSf72fSr4O8Ux0fY_OzaSXUgTFdz6Xo4,805
@@ -23,10 +22,10 @@ listpick/utils/search_and_filter_utils.py,sha256=XxGfkyDVXO9OAKcftPat8IReMTFIuTH
23
22
  listpick/utils/searching.py,sha256=Xk5UIqamNHL2L90z3ACB_Giqdpi9iRKoAJ6pKaqaD7Q,3093
24
23
  listpick/utils/sorting.py,sha256=WZZiVlVA3Zkcpwji3U5SNFlQ14zVEk3cZJtQirBkecQ,5329
25
24
  listpick/utils/table_to_list_of_lists.py,sha256=T-i-nV1p6g8UagdgUPKrhIGpKY_YXZDxf4xZzcPepNA,7635
26
- listpick/utils/utils.py,sha256=_4HW0p1gnundsTJcKsSsIKCZxy3DtolbxjBpuYQyFBw,12948
27
- listpick-0.1.13.60.dist-info/licenses/LICENSE.txt,sha256=2mP-MRHJptADDNE9VInMNg1tE-C6Qv93Z4CCQKrpg9w,1061
28
- listpick-0.1.13.60.dist-info/METADATA,sha256=sg6tgBZDQwpYyBr0y5vFfqcOeC2q7peIHbkibQTV58E,7988
29
- listpick-0.1.13.60.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
- listpick-0.1.13.60.dist-info/entry_points.txt,sha256=-QCf_BKIkUz35Y9nkYpjZWs2Qg0KfRna2PAs5DnF6BE,43
31
- listpick-0.1.13.60.dist-info/top_level.txt,sha256=5mtsGEz86rz3qQDe0D463gGjAfSp6A3EWg4J4AGYr-Q,9
32
- listpick-0.1.13.60.dist-info/RECORD,,
25
+ listpick/utils/utils.py,sha256=3La3uyij4QazWV_8GpXJ7nFFMHY2sRTW2OWXJTX-LeY,13791
26
+ listpick-0.1.13.62.dist-info/licenses/LICENSE.txt,sha256=2mP-MRHJptADDNE9VInMNg1tE-C6Qv93Z4CCQKrpg9w,1061
27
+ listpick-0.1.13.62.dist-info/METADATA,sha256=YNt1kbQKoIp0L_kCs8GZwEWPbt6hDquSrq4GmDlpoW0,8053
28
+ listpick-0.1.13.62.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
+ listpick-0.1.13.62.dist-info/entry_points.txt,sha256=-QCf_BKIkUz35Y9nkYpjZWs2Qg0KfRna2PAs5DnF6BE,43
30
+ listpick-0.1.13.62.dist-info/top_level.txt,sha256=5mtsGEz86rz3qQDe0D463gGjAfSp6A3EWg4J4AGYr-Q,9
31
+ listpick-0.1.13.62.dist-info/RECORD,,
listpick/ui/footer_1.py DELETED
@@ -1,202 +0,0 @@
1
- """
2
- footer.py
3
- Lines to be displayed on the help screen.
4
-
5
- Author: GrimAndGreedy
6
- License: MIT
7
- """
8
-
9
- import curses
10
- import logging
11
-
12
- logger = logging.getLogger('picker_log')
13
-
14
- class Footer:
15
- def __init__(self, stdscr, colours_start, get_state_function):
16
- """
17
- stdscr: curses screen object
18
- colours_start: base colour pair index
19
- get_state_callback: function that returns a dict with all required data for rendering
20
- """
21
- self.stdscr = stdscr
22
- self.colours_start = colours_start
23
- self.get_state = get_state_function
24
- self.height = 0
25
-
26
- def draw(self, h, w):
27
- """
28
- Draw the footer. Must be implemented by subclasses.
29
- """
30
- raise NotImplementedError
31
-
32
- class StandardFooter(Footer):
33
- def __init__(self, stdscr, colours_start, get_state_function):
34
- """
35
- stdscr: curses screen object
36
- colours_start: base colour pair index
37
- get_state_callback: function that returns a dict with all required data for rendering
38
- """
39
- self.stdscr = stdscr
40
- self.colours_start = colours_start
41
- self.get_state = get_state_function
42
- self.height = 3
43
- def draw(self, h, w):
44
- state = self.get_state()
45
-
46
- # Fill background
47
- for i in range(3):
48
- self.stdscr.addstr(h-3+i, 0, ' '*(w-1), curses.color_pair(self.colours_start+20))
49
-
50
- if state["filter_query"]:
51
- self.stdscr.addstr(h - 2, 2, f" Filter: {state['filter_query']} "[:w-40], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
52
- if state["search_query"]:
53
- self.stdscr.addstr(h - 3, 2, f" Search: {state['search_query']} [{state['search_index']}/{state['search_count']}] "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
54
- if state["user_opts"]:
55
- self.stdscr.addstr(h - 1, 2, f" Opts: {state['user_opts']} "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
56
-
57
- # Sort info
58
- sort_column_info = f"{state['sort_column'] if state['sort_column'] is not None else 'None'}"
59
- sort_method_info = f"{state['SORT_METHODS'][state['columns_sort_method'][state['sort_column']]]}" if state['sort_column'] is not None else "NA"
60
- sort_order_info = "Desc." if state["sort_reverse"] else "Asc."
61
- sort_order_info = "▼" if state["sort_reverse"][state['sort_column']] else "▲"
62
- sort_disp_str = f" Sort: ({sort_column_info}, {sort_method_info}, {sort_order_info}) "
63
- self.stdscr.addstr(h - 2, w-35, f"{sort_disp_str:>34}", curses.color_pair(self.colours_start+20))
64
-
65
- if state["footer_string"]:
66
- # footer_string_width = min(w-1, max(len(state["footer_string"]), 50))
67
- # disp_string = f"{state['footer_string'][:footer_string_width]:>{footer_string_width-1}} "
68
- # self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
69
- # self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
70
-
71
- # disp_string = f"{footer_string:>{footer_string_width-1}} "
72
- # self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
73
- # self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
74
-
75
- footer_string_width = min(w-1, len(state["footer_string"])+2)
76
-
77
- disp_string = f"{state["footer_string"][:footer_string_width]}"
78
- disp_string = f" {disp_string:>{footer_string_width-2}} "
79
- self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
80
- self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
81
-
82
-
83
- else:
84
- select_mode = "Cursor"
85
- if state["is_selecting"]: select_mode = "Visual Selection"
86
- elif state["is_deselecting"]: select_mode = "Visual deselection"
87
- self.stdscr.addstr(h - 1, w-35, f"{select_mode:>33} ", curses.color_pair(self.colours_start+20))
88
-
89
- # Cursor & selection info
90
- selected_count = sum(state["selections"].values())
91
- if state["paginate"]:
92
- cursor_disp_str = f" {state['cursor_pos']+1}/{len(state['indexed_items'])} Page {state['cursor_pos']//state['items_per_page']}/{len(state['indexed_items'])} Selected {selected_count}"
93
- else:
94
- cursor_disp_str = f" {state['cursor_pos']+1}/{len(state['indexed_items'])} | Selected {selected_count}"
95
- self.stdscr.addstr(h - 3, w-35, f"{cursor_disp_str:>33} ", curses.color_pair(self.colours_start+20))
96
-
97
- self.stdscr.refresh()
98
-
99
-
100
-
101
- class CompactFooter(Footer):
102
- def __init__(self, stdscr, colours_start, get_state_function):
103
- """
104
- stdscr: curses screen object
105
- colours_start: base colour pair index
106
- get_state_callback: function that returns a dict with all required data for rendering
107
- """
108
- self.stdscr = stdscr
109
- self.colours_start = colours_start
110
- self.get_state = get_state_function
111
- self.height = 1
112
-
113
- def draw(self, h, w):
114
- state = self.get_state()
115
-
116
- # Fill background
117
- if state["search_query"]: self.height = 3
118
- elif state["filter_query"]: self.height = 2
119
- elif state["user_opts"]: self.height = 1
120
- elif state["footer_string"]: self.height = 2
121
- else: self.height = 1
122
- for i in range(self.height):
123
- self.stdscr.addstr(h-(i+1), 0, ' '*(w-1), curses.color_pair(self.colours_start+20))
124
-
125
- if state["user_opts"]:
126
- self.stdscr.addstr(h - 1, 2, f" Opts: {state['user_opts']} "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
127
- if state["filter_query"]:
128
- self.stdscr.addstr(h - 2, 2, f" Filter: {state['filter_query']} "[:w-40], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
129
- if state["search_query"]:
130
- self.stdscr.addstr(h - 3, 2, f" Search: {state['search_query']} [{state['search_index']}/{state['search_count']}] "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
131
-
132
- right_width = 40
133
- # Sort info
134
- sort_column_info = f"{state['sort_column'] if state['sort_column'] is not None else 'None'}"
135
- sort_method_info = f"{state['SORT_METHODS'][state['columns_sort_method'][state['sort_column']]]}" if state['sort_column'] is not None else "NA"
136
- sort_order_info = "Desc." if state["sort_reverse"][state['sort_column']] else "Asc."
137
- sort_order_info = "▼" if state["sort_reverse"][state['sort_column']] else "▲"
138
- sort_disp_str = f" ({sort_column_info}, {sort_method_info}, {sort_order_info}) "
139
- # self.stdscr.addstr(h - 2, w-right_width, f"{sort_disp_str:>{right_width-1}}", curses.color_pair(self.colours_start+20))
140
-
141
- if state["footer_string"]:
142
- footer_string_width = min(w-1, len(state["footer_string"])+2)
143
-
144
- disp_string = f"{state["footer_string"][:footer_string_width]}"
145
- disp_string = f" {disp_string:>{footer_string_width-2}} "
146
- self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
147
- self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
148
- selected_count = sum(state["selections"].values())
149
- if state["paginate"]:
150
- cursor_disp_str = f" {state['cursor_pos']+1}/{len(state['indexed_items'])} Page {state['cursor_pos']//state['items_per_page']}/{len(state['indexed_items'])} Selected {selected_count}"
151
- else:
152
- cursor_disp_str = f"{sort_disp_str} [{selected_count}] {state['cursor_pos']+1}/{len(state['indexed_items'])}"
153
- self.stdscr.addstr(h-2, w-right_width, f"{cursor_disp_str:>{right_width-2}}"[:right_width-1], curses.color_pair(self.colours_start+20))
154
- else:
155
- # Cursor & selection info
156
- selected_count = sum(state["selections"].values())
157
- if state["paginate"]:
158
- cursor_disp_str = f" {state['cursor_pos']+1}/{len(state['indexed_items'])} Page {state['cursor_pos']//state['items_per_page']}/{len(state['indexed_items'])} Selected {selected_count}"
159
- else:
160
- cursor_disp_str = f"{sort_disp_str} [{selected_count}] {state['cursor_pos']+1}/{len(state['indexed_items'])}"
161
- self.stdscr.addstr(h - 1, w-right_width, f"{cursor_disp_str:>{right_width-2}}"[:right_width-1], curses.color_pair(self.colours_start+20))
162
-
163
- self.stdscr.refresh()
164
-
165
- class NoFooter(Footer):
166
- def __init__(self, stdscr, colours_start, get_state_function):
167
- """
168
- stdscr: curses screen object
169
- colours_start: base colour pair index
170
- get_state_callback: function that returns a dict with all required data for rendering
171
- """
172
- self.stdscr = stdscr
173
- self.colours_start = colours_start
174
- self.get_state = get_state_function
175
- self.height = 0
176
- def draw(self, h, w):
177
- state = self.get_state()
178
-
179
- if state["search_query"]: self.height = 3
180
- elif state["filter_query"]: self.height = 2
181
- elif state["user_opts"]: self.height = 1
182
- elif state["footer_string"]: self.height = 1
183
- else: self.height = 0
184
-
185
- for i in range(self.height):
186
- self.stdscr.addstr(h-(i+1), 0, ' '*(w-1), curses.color_pair(self.colours_start+20))
187
-
188
- if state["user_opts"]:
189
- self.stdscr.addstr(h - 1, 2, f" Opts: {state['user_opts']} "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
190
- if state["filter_query"]:
191
- self.stdscr.addstr(h - 2, 2, f" Filter: {state['filter_query']} "[:w-40], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
192
- if state["search_query"]:
193
- self.stdscr.addstr(h - 3, 2, f" Search: {state['search_query']} [{state['search_index']}/{state['search_count']}] "[:w-3], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
194
- self.height = 3
195
-
196
-
197
- if state["footer_string"]:
198
- footer_string_width = min(w-1, len(state["footer_string"])+2)
199
- disp_string = f"{state["footer_string"][:footer_string_width]}"
200
- disp_string = f" {disp_string:>{footer_string_width-2}} "
201
- self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
202
- self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))