listpick 0.1.13.55__tar.gz → 0.1.13.56__tar.gz

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.
Files changed (58) hide show
  1. {listpick-0.1.13.55 → listpick-0.1.13.56}/CHANGELOG.md +0 -11
  2. {listpick-0.1.13.55/src/listpick.egg-info → listpick-0.1.13.56}/PKG-INFO +1 -1
  3. {listpick-0.1.13.55 → listpick-0.1.13.56}/TODO.md +0 -4
  4. {listpick-0.1.13.55 → listpick-0.1.13.56}/setup.py +1 -1
  5. listpick-0.1.13.55/src/listpick/lpapp2.py → listpick-0.1.13.56/src/listpick/listpick_app.py +49 -61
  6. listpick-0.1.13.55/src/listpick/ui/footer_1.py → listpick-0.1.13.56/src/listpick/ui/footer.py +6 -4
  7. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/utils.py +24 -50
  8. {listpick-0.1.13.55 → listpick-0.1.13.56/src/listpick.egg-info}/PKG-INFO +1 -1
  9. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick.egg-info/SOURCES.txt +0 -3
  10. listpick-0.1.13.55/src/listpick/listpick_app.py +0 -3250
  11. listpick-0.1.13.55/src/listpick/listpick_app_1.py +0 -3250
  12. listpick-0.1.13.55/src/listpick/ui/footer.py +0 -202
  13. {listpick-0.1.13.55 → listpick-0.1.13.56}/.gitignore +0 -0
  14. {listpick-0.1.13.55 → listpick-0.1.13.56}/LICENSE.txt +0 -0
  15. {listpick-0.1.13.55 → listpick-0.1.13.56}/README.md +0 -0
  16. {listpick-0.1.13.55 → listpick-0.1.13.56}/assets/aria2tui_screenshot.png +0 -0
  17. {listpick-0.1.13.55 → listpick-0.1.13.56}/assets/file_compare.png +0 -0
  18. {listpick-0.1.13.55 → listpick-0.1.13.56}/assets/lpfman.png +0 -0
  19. {listpick-0.1.13.55 → listpick-0.1.13.56}/examples/footer_string_example.py +0 -0
  20. {listpick-0.1.13.55 → listpick-0.1.13.56}/examples/input.toml +0 -0
  21. {listpick-0.1.13.55 → listpick-0.1.13.56}/examples/input.txt +0 -0
  22. {listpick-0.1.13.55 → listpick-0.1.13.56}/examples/list_files.toml +0 -0
  23. {listpick-0.1.13.55 → listpick-0.1.13.56}/examples/list_files_empty.toml +0 -0
  24. {listpick-0.1.13.55 → listpick-0.1.13.56}/examples/picker_example.py +0 -0
  25. {listpick-0.1.13.55 → listpick-0.1.13.56}/examples/setup.cfg +0 -0
  26. {listpick-0.1.13.55 → listpick-0.1.13.56}/examples/template.py +0 -0
  27. {listpick-0.1.13.55 → listpick-0.1.13.56}/examples/video_duplicates.toml +0 -0
  28. {listpick-0.1.13.55 → listpick-0.1.13.56}/listpick.py +0 -0
  29. {listpick-0.1.13.55 → listpick-0.1.13.56}/requirements.txt +0 -0
  30. {listpick-0.1.13.55 → listpick-0.1.13.56}/setup.cfg +0 -0
  31. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/__init__.py +0 -0
  32. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/__main__.py +0 -0
  33. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/ui/__init__.py +0 -0
  34. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/ui/build_help.py +0 -0
  35. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/ui/help_screen.py +0 -0
  36. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/ui/input_field.py +0 -0
  37. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/ui/keys.py +0 -0
  38. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/ui/pane_stuff.py +0 -0
  39. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/ui/picker_colours.py +0 -0
  40. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/__init__.py +0 -0
  41. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/clipboard_operations.py +0 -0
  42. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/config.py +0 -0
  43. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/dump.py +0 -0
  44. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/filtering.py +0 -0
  45. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/generate_data.py +0 -0
  46. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/options_selectors.py +0 -0
  47. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/paste_operations.py +0 -0
  48. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/picker_log.py +0 -0
  49. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/search_and_filter_utils.py +0 -0
  50. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/searching.py +0 -0
  51. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/sorting.py +0 -0
  52. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick/utils/table_to_list_of_lists.py +0 -0
  53. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick.egg-info/dependency_links.txt +0 -0
  54. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick.egg-info/entry_points.txt +0 -0
  55. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick.egg-info/requires.txt +0 -0
  56. {listpick-0.1.13.55 → listpick-0.1.13.56}/src/listpick.egg-info/top_level.txt +0 -0
  57. {listpick-0.1.13.55 → listpick-0.1.13.56}/tests/kitty_control.sh +0 -0
  58. {listpick-0.1.13.55 → listpick-0.1.13.56}/tests/sorting_dates.csv +0 -0
@@ -25,17 +25,6 @@
25
25
  - Setup logging for the Picker class. Currently still very basic but will track to the last function that was run before crash if the --debug flag is passed to the picker.
26
26
  - Added pin_cursor options which keeps the cursor on the same row during a refresh rather than tracking the id of the highlighted row.
27
27
  - Updated StandardFooter. Now shows information on two lines rather than three; all cursor and selection information on the first line and the sort information on the second. Cursor, Visual (de)selection now abreviated to C, VS, and VDS.
28
- - Reduced flickering when displaying an infobox.
29
- - Optimised cpu usage by created a class variable selected_cells_by_row which updates with selection/deselection rather than being determined each time the draw_screen loop is run.
30
- - Added unicode_char_width option which will set the calculating character widths. If True then wcwidth will be used to calculate the display with if False then len will be used.
31
- - unicode_char_width should be set to true if special characters will be displayed
32
- - Special characters=East Asian scripts, emojis, etc.
33
- - With data that has several thousand columns wcwidth is extremely slow so unicode_char_width should be set to False.
34
- - Speed improvements:
35
- - get_column_widths calculated only once when drawing the screen.
36
- - unicode_char_width option to improve performance with large (in particular wide) data sets.
37
- - truncate_to_display width now guesses the target width and then adjusts rather than adding one display-width character at a time. 5-10 times faster.
38
- - Reduced number of calls to draw_screen. It is called (1) when a key is pressed, (2) before showing a notification, and (3) when the data is refreshed.
39
28
 
40
29
  ## [0.1.13] 2025-07-28
41
30
  - Cell-based picker is now supported.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: listpick
3
- Version: 0.1.13.55
3
+ Version: 0.1.13.56
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
@@ -5,9 +5,6 @@ ASAP
5
5
  > - [ ] Unify in-app load and command-line input file
6
6
  > - [ ] Implement default_option_selector and pass the picker options (!!!)
7
7
  > - [ ] Make sure that all class initialisation variables are returned in the get_function_variables function.
8
- > - [ ] Adjust leftmost_char when:
9
- > - [ ] cursor_down
10
- > - [ ] 'g', 'G'
11
8
 
12
9
 
13
10
 
@@ -117,7 +114,6 @@ ASAP
117
114
  > - [x] Support pasting copied cells into the picker
118
115
  > - [ ] Ensure that we can only paste when cells are editable.
119
116
  > - [ ] Add option to insert cells; e.g., insert two columns and move the data to the right of the selected column across
120
- > - [ ] Add support for pasting as a string
121
117
  > - [ ] Support inserting cells/rows.
122
118
  > - [ ] Bulk edit in nvim
123
119
  > - [ ] Add formula and display value...
@@ -16,7 +16,7 @@ with open("README.md", "r", encoding = "utf-8") as fh:
16
16
 
17
17
  setuptools.setup(
18
18
  name = "listpick",
19
- version = "0.1.13.55",
19
+ version = "0.1.13.56",
20
20
  author = "Grim",
21
21
  author_email = "grimandgreedy@protonmail.com",
22
22
  description = "Listpick is a powerful TUI data tool for creating TUI apps or viewing/comparing tabulated data.",
@@ -140,7 +140,6 @@ class Picker:
140
140
  footer_string_refresh_function: Optional[Callable] = None,
141
141
  footer_timer: float=1,
142
142
  get_footer_string_startup=False,
143
- unicode_char_width: bool = True,
144
143
 
145
144
  colours_start: int =0,
146
145
  colours_end: int =-1,
@@ -257,7 +256,7 @@ class Picker:
257
256
  self.footer_string_refresh_function = footer_string_refresh_function
258
257
  self.footer_timer = footer_timer
259
258
  self.get_footer_string_startup = get_footer_string_startup,
260
- self.unicode_char_width = unicode_char_width
259
+
261
260
 
262
261
 
263
262
  self.colours_start = colours_start
@@ -313,6 +312,8 @@ class Picker:
313
312
  self.debug_level = debug_level
314
313
 
315
314
 
315
+
316
+
316
317
  self.initialise_picker_state(reset_colours=self.reset_colours)
317
318
 
318
319
 
@@ -323,7 +324,6 @@ class Picker:
323
324
  # self.footer = CompactFooter(self.stdscr, colours_start, self.get_function_data)
324
325
 
325
326
 
326
-
327
327
  def calculate_section_sizes(self):
328
328
  """
329
329
  Calculte the following for the Picker:
@@ -384,6 +384,7 @@ class Picker:
384
384
  def initialise_picker_state(self, reset_colours=False) -> None:
385
385
  """ Initialise state variables for the picker. These are: debugging and colours. """
386
386
 
387
+
387
388
  if curses.has_colors() and self.colours != None:
388
389
  # raise Exception("Terminal does not support color")
389
390
  curses.start_color()
@@ -473,6 +474,7 @@ class Picker:
473
474
 
474
475
  if len(self.indexed_items) > 0 and len(self.indexed_items) >= self.cursor_pos and len(self.indexed_items[0][1]) >= self.id_column:
475
476
  self.cursor_pos_id = self.indexed_items[self.cursor_pos][1][self.id_column]
477
+ self.cursor_pos_prev = self.cursor_pos
476
478
 
477
479
  self.items, self.header = self.refresh_function()
478
480
 
@@ -679,12 +681,12 @@ class Picker:
679
681
  end_index = min(start_index + self.items_per_page, len(self.indexed_items))
680
682
  if len(self.indexed_items) == 0: start_index, end_index = 0, 0
681
683
 
682
- # self.column_widths = get_column_widths(self.items, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns)
684
+ # self.column_widths = get_column_widths(self.items, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w)
683
685
  # Determine widths based only on the currently indexed rows
684
686
  # rows = [v[1] for v in self.indexed_items] if len(self.indexed_items) else self.items
685
687
  # Determine widths based only on the currently displayed indexed rows
686
688
  rows = [v[1] for v in self.indexed_items[start_index:end_index]] if len(self.indexed_items) else self.items
687
- self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns)
689
+ self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w)
688
690
  visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
689
691
  visible_columns_total_width = sum(visible_column_widths) + len(self.separator)*(len(visible_column_widths)-1)
690
692
 
@@ -982,7 +984,6 @@ class Picker:
982
984
  disp_string = f" {self.footer_string[:footer_string_width]:>{footer_string_width-2}} "
983
985
  self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
984
986
  self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
985
- self.stdscr.refresh()
986
987
 
987
988
  ## Display infobox
988
989
  if self.display_infobox:
@@ -1131,7 +1132,6 @@ class Picker:
1131
1132
  "debug": self.debug,
1132
1133
  "debug_level": self.debug_level,
1133
1134
  "reset_colours": self.reset_colours,
1134
- "unicode_char_width": self.unicode_char_width,
1135
1135
  }
1136
1136
  return function_data
1137
1137
 
@@ -1385,8 +1385,6 @@ class Picker:
1385
1385
  self.initialise_variables()
1386
1386
  elif setting == "pc":
1387
1387
  self.pin_cursor = not self.pin_cursor
1388
- elif setting == "unicode":
1389
- self.unicode_char_width = not self.unicode_char_width
1390
1388
 
1391
1389
  elif setting.startswith("ft"):
1392
1390
  if len(setting) > 2 and setting[2:].isnumeric():
@@ -1450,6 +1448,7 @@ class Picker:
1450
1448
  self.draw_screen(self.indexed_items, self.highlights)
1451
1449
  self.notification(self.stdscr, message=f"Theme {self.colour_theme_number} applied.")
1452
1450
 
1451
+
1453
1452
  else:
1454
1453
  self.user_settings = ""
1455
1454
  return None
@@ -1520,18 +1519,9 @@ class Picker:
1520
1519
  xend = max(self.start_selection_col, self.selected_column)
1521
1520
  for i in range(ystart, yend + 1):
1522
1521
  if self.indexed_items[i][0] not in self.unselectable_indices:
1523
- row = self.indexed_items[i][0]
1524
- if row not in self.selected_cells_by_row:
1525
- self.selected_cells_by_row[row] = []
1526
-
1527
- for col in range(xstart, xend+1):
1528
- cell_index = (row, col)
1522
+ for j in range(xstart, xend+1):
1523
+ cell_index = (self.indexed_items[i][0], j)
1529
1524
  self.cell_selections[cell_index] = True
1530
-
1531
- self.selected_cells_by_row[row].append(col)
1532
- # Remove duplicates
1533
- self.selected_cells_by_row[row] = list(set(self.selected_cells_by_row[row]))
1534
-
1535
1525
  self.start_selection = -1
1536
1526
  self.end_selection = -1
1537
1527
  self.is_selecting = False
@@ -1565,6 +1555,8 @@ class Picker:
1565
1555
  def cursor_down(self, count=1) -> bool:
1566
1556
  """ Move cursor down. """
1567
1557
  self.logger.info(f"function: cursor_down()")
1558
+ if len(self.indexed_items) == 0 or self.cursor_pos == len(self.indexed_items) -1:
1559
+ return False
1568
1560
  # Returns: whether page is turned
1569
1561
  new_pos = self.cursor_pos + 1
1570
1562
  new_pos = min(self.cursor_pos+count, len(self.indexed_items)-1)
@@ -1783,6 +1775,7 @@ class Picker:
1783
1775
 
1784
1776
  if len(self.indexed_items) > 0 and len(self.indexed_items) >= self.cursor_pos and len(self.indexed_items[0][1]) >= self.id_column:
1785
1777
  self.cursor_pos_id = self.indexed_items[self.cursor_pos][1][self.id_column]
1778
+ self.cursor_pos_prev = self.cursor_pos
1786
1779
  with self.data_lock:
1787
1780
  self.items, self.header = tmp_items, tmp_header
1788
1781
  self.data_ready = True
@@ -1832,7 +1825,7 @@ class Picker:
1832
1825
 
1833
1826
  def get_word_list(self) -> list[str]:
1834
1827
  """ Get a list of all words used in any cell of the picker. Used for completion in search/filter input_field. """
1835
- self.logger.info(f"function: get_word_list()")
1828
+ self.logger.info(f"function: infobox()")
1836
1829
  translator = str.maketrans('', '', string.punctuation)
1837
1830
 
1838
1831
  words = []
@@ -1940,6 +1933,7 @@ class Picker:
1940
1933
 
1941
1934
  while True:
1942
1935
  key = self.stdscr.getch()
1936
+ h, w = self.stdscr.getmaxyx()
1943
1937
  if key in self.disabled_keys: continue
1944
1938
  clear_screen=True
1945
1939
 
@@ -1960,7 +1954,6 @@ class Picker:
1960
1954
 
1961
1955
  elif self.check_key("refresh", key, self.keys_dict) or self.remapped_key(key, curses.KEY_F5, self.key_remappings) or (self.auto_refresh and (time.time() - initial_time) >= self.timer):
1962
1956
  self.logger.debug(f"Get new data (refresh).")
1963
- h, w = self.stdscr.getmaxyx()
1964
1957
  self.stdscr.addstr(0,w-3,"  ", curses.color_pair(self.colours_start+21) | curses.A_BOLD)
1965
1958
  self.stdscr.refresh()
1966
1959
  if self.get_new_data and self.refresh_function:
@@ -1974,7 +1967,6 @@ class Picker:
1974
1967
 
1975
1968
  # Refresh data synchronously
1976
1969
  # if self.check_key("refresh", key, self.keys_dict) or self.remapped_key(key, curses.KEY_F5, self.key_remappings) or (self.auto_refresh and (time.time() - initial_time) > self.timer):
1977
- # h, w = self.stdscr.getmaxyx()
1978
1970
  # self.stdscr.addstr(0,w-3,"  ", curses.color_pair(self.colours_start+21) | curses.A_BOLD)
1979
1971
  # self.stdscr.refresh()
1980
1972
  # if self.get_new_data and self.refresh_function:
@@ -2076,7 +2068,6 @@ class Picker:
2076
2068
  options += [["rh", "Toggle row header"]]
2077
2069
  options += [["modes", "Toggle modes"]]
2078
2070
  options += [["ft", "Cycle through footer styles (accepts ft#)"]]
2079
- options += [["unicode", "Toggle b/w using len and wcwidth to calculate char width."]]
2080
2071
  options += [[f"s{i}", f"Select col. {i}"] for i in range(len(self.items[0]))]
2081
2072
  options += [[f"!{i}", f"Toggle col. {i}"] for i in range(len(self.items[0]))]
2082
2073
  options += [["ara", "Add empty row after cursor."]]
@@ -2102,7 +2093,7 @@ class Picker:
2102
2093
  # self.selected_column = (self.selected_column-1)%len(self.column_indices)
2103
2094
  # # self.notification(self.stdscr, f"{str(self.column_indices)}, {tmp1}, {tmp2}")
2104
2095
  # self.initialise_variables()
2105
- # self.column_widths = get_column_widths([v[1] for v in self.indexed_items], header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns)
2096
+ # self.column_widths = get_column_widths([v[1] for v in self.indexed_items], header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w)
2106
2097
  # self.draw_screen(self.indexed_items, self.highlights)
2107
2098
  # # self.move_column(direction=-1)
2108
2099
  #
@@ -2120,19 +2111,22 @@ class Picker:
2120
2111
  page_turned = self.cursor_down()
2121
2112
  if not page_turned: clear_screen = False
2122
2113
  elif self.check_key("half_page_down", key, self.keys_dict):
2123
- clear_screen = False
2124
2114
  self.cursor_down(count=self.items_per_page//2)
2115
+ clear_screen = True
2125
2116
  elif self.check_key("five_down", key, self.keys_dict):
2126
2117
  clear_screen = False
2127
2118
  self.cursor_down(count=5)
2119
+ clear_screen = True
2128
2120
  elif self.check_key("cursor_up", key, self.keys_dict):
2129
2121
  page_turned = self.cursor_up()
2130
2122
  if not page_turned: clear_screen = False
2131
2123
  elif self.check_key("five_up", key, self.keys_dict):
2132
2124
  # if self.cursor_up(count=5): clear_screen = True
2133
2125
  self.cursor_up(count=5)
2126
+ clear_screen = True
2134
2127
  elif self.check_key("half_page_up", key, self.keys_dict):
2135
2128
  self.cursor_up(count=self.items_per_page//2)
2129
+ clear_screen = True
2136
2130
 
2137
2131
  elif self.check_key("toggle_select", key, self.keys_dict):
2138
2132
  if len(self.indexed_items) > 0:
@@ -2143,25 +2137,6 @@ class Picker:
2143
2137
  self.toggle_item(item_index)
2144
2138
 
2145
2139
  self.cell_selections[cell_index] = not self.cell_selections[cell_index]
2146
- ## Set self.selected_cells_by_row
2147
- # If any cells in the current row are selected
2148
- if row in self.selected_cells_by_row:
2149
- # If the current cell is selected then remove it
2150
- if col in self.selected_cells_by_row[row]:
2151
- # If the current cell is the only cell in the row that is selected then remove the row from the dict
2152
- if len(self.selected_cells_by_row[row]) == 1:
2153
-
2154
- del self.selected_cells_by_row[row]
2155
- # else remove only the index of the current cell
2156
- else:
2157
- self.selected_cells_by_row[row].remove(col)
2158
- # If there are cells in the row that are selected then append the current cell to the row
2159
- else:
2160
- self.selected_cells_by_row[row].append(col)
2161
- # Add the a list containing only the current column
2162
- else:
2163
- self.selected_cells_by_row[row] = [col]
2164
-
2165
2140
  self.cursor_down()
2166
2141
  elif self.check_key("select_all", key, self.keys_dict): # Select all (m or ctrl-a)
2167
2142
  self.select_all()
@@ -2186,7 +2161,12 @@ class Picker:
2186
2161
  else: break
2187
2162
  if new_pos < len(self.items) and new_pos >= 0:
2188
2163
  self.cursor_pos = new_pos
2189
-
2164
+ self.draw_screen(self.indexed_items, self.highlights)
2165
+ # current_row = items_per_page - 1
2166
+ # if current_page + 1 == (len(self.indexed_items) + items_per_page - 1) // items_per_page:
2167
+ #
2168
+ # current_row = (len(self.indexed_items) +items_per_page - 1) % items_per_page
2169
+ # self.draw_screen(self.indexed_items, self.highlights)
2190
2170
  elif self.check_key("enter", key, self.keys_dict):
2191
2171
  self.logger.info(f"key_function enter")
2192
2172
  # Print the selected indices if any, otherwise print the current index
@@ -2266,6 +2246,7 @@ class Picker:
2266
2246
  if len(self.indexed_items) > 0:
2267
2247
  current_index = self.indexed_items[self.cursor_pos][0]
2268
2248
  sort_items(self.indexed_items, sort_method=self.columns_sort_method[self.sort_column], sort_column=self.sort_column, sort_reverse=self.sort_reverse[self.sort_column]) # Re-sort self.items based on new column
2249
+ self.draw_screen(self.indexed_items, self.highlights)
2269
2250
  self.cursor_pos = [row[0] for row in self.indexed_items].index(current_index)
2270
2251
  self.logger.info(f"key_function cycle_sort_order. (sort_column, sort_method, sort_reverse) = ({self.sort_column}, {self.columns_sort_method[self.sort_column]}, {self.sort_reverse[self.sort_column]})")
2271
2252
  elif self.check_key("col_select", key, self.keys_dict):
@@ -2289,10 +2270,10 @@ class Picker:
2289
2270
 
2290
2271
  ## Scroll with column select
2291
2272
  rows = self.get_visible_rows()
2292
- self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns)
2273
+ self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w)
2293
2274
  visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
2294
- visible_columns_total_width = sum(visible_column_widths) + len(self.separator)*(len(visible_column_widths)-1)
2295
- h, w = self.stdscr.getmaxyx()
2275
+ column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
2276
+ start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
2296
2277
  end_of_cell = sum(visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
2297
2278
  display_width = w-self.startx
2298
2279
  # If the full column is within the current display then don't do anything
@@ -2316,7 +2297,8 @@ class Picker:
2316
2297
 
2317
2298
  ## Scroll with column select
2318
2299
  rows = self.get_visible_rows()
2319
- self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w, unicode_char_width=self.unicode_char_width)
2300
+ self.column_widths = get_column_widths(rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w)
2301
+
2320
2302
  visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
2321
2303
  column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
2322
2304
  start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
@@ -2331,13 +2313,18 @@ class Picker:
2331
2313
  self.leftmost_char = start_of_cell
2332
2314
 
2333
2315
  self.leftmost_char = max(0, min(column_set_width - display_width + 5, self.leftmost_char))
2334
-
2316
+ #
2335
2317
  elif self.check_key("scroll_right", key, self.keys_dict):
2336
2318
  self.logger.info(f"key_function scroll_right")
2337
- if len(self.indexed_items):
2338
- row_width = sum(self.column_widths) + len(self.separator)*(len(self.column_widths)-1)
2339
- if row_width-self.leftmost_char >= w-self.startx:
2340
- self.leftmost_char = self.leftmost_char+5
2319
+ rows = self.get_visible_rows()
2320
+ longest_row_str_len = 0
2321
+ for i in range(len(rows)):
2322
+ item = rows[i]
2323
+ row_str = format_row(item, self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)[self.leftmost_char:]
2324
+ if len(row_str) > longest_row_str_len: longest_row_str_len=len(row_str)
2325
+
2326
+ if longest_row_str_len >= w-self.startx:
2327
+ self.leftmost_char = self.leftmost_char+5
2341
2328
 
2342
2329
  elif self.check_key("scroll_left", key, self.keys_dict):
2343
2330
  self.logger.info(f"key_function scroll_left")
@@ -2434,13 +2421,13 @@ class Picker:
2434
2421
  self.logger.info(f"key_function decrease_column_width")
2435
2422
  if self.max_column_width > 10:
2436
2423
  self.max_column_width -= 10
2437
- self.column_widths[:] = get_column_widths(self.items, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns)
2424
+ # self.column_widths = get_column_widths(self.items, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=2)
2438
2425
  self.draw_screen(self.indexed_items, self.highlights)
2439
2426
  elif self.check_key("increase_column_width", key, self.keys_dict):
2440
2427
  self.logger.info(f"key_function increase_column_width")
2441
2428
  if self.max_column_width < 1000:
2442
2429
  self.max_column_width += 10
2443
- self.column_widths = get_column_widths(self.items, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns)
2430
+ # self.column_widths = get_column_widths(self.items, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w)
2444
2431
  self.draw_screen(self.indexed_items, self.highlights)
2445
2432
  elif self.check_key("visual_selection_toggle", key, self.keys_dict):
2446
2433
  self.logger.info(f"key_function visual_selection_toggle")
@@ -2455,7 +2442,7 @@ class Picker:
2455
2442
  elif key == curses.KEY_RESIZE: # Terminal resize signal
2456
2443
 
2457
2444
  self.calculate_section_sizes()
2458
- self.column_widths = get_column_widths(self.items, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns)
2445
+ self.column_widths = get_column_widths(self.items, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w)
2459
2446
 
2460
2447
  self.draw_screen(self.indexed_items, self.highlights)
2461
2448
 
@@ -2982,7 +2969,7 @@ def set_colours(pick: int = 0, start: int = 0) -> Optional[int]:
2982
2969
  def parse_arguments() -> Tuple[argparse.Namespace, dict]:
2983
2970
  """ Parse command line arguments. """
2984
2971
  parser = argparse.ArgumentParser(description='Convert table to list of lists.')
2985
- parser.add_argument('filename', type=str, help='The file to process')
2972
+ # parser.add_argument('filename', type=str, help='The file to process')
2986
2973
  parser.add_argument('-i', dest='file', help='File containing the table to be converted.')
2987
2974
  parser.add_argument('--load', '-l', dest='load', type=str, help='Load file from Picker dump.')
2988
2975
  parser.add_argument('--stdin', dest='stdin', action='store_true', help='Table passed on stdin')
@@ -3010,8 +2997,8 @@ def parse_arguments() -> Tuple[argparse.Namespace, dict]:
3010
2997
  input_arg = '--stdin'
3011
2998
  elif args.stdin2:
3012
2999
  input_arg = '--stdin2'
3013
- elif args.filename:
3014
- input_arg = args.filename
3000
+ # elif args.filename:
3001
+ # input_arg = args.filename
3015
3002
 
3016
3003
  elif args.generate:
3017
3004
  function_data["refresh_function"] = lambda : generate_picker_data(args.generate)
@@ -3122,6 +3109,7 @@ def main() -> None:
3122
3109
  function_data["track_entries_upon_refresh"] = True
3123
3110
  function_data["centre_in_terminal_vertical"] = True
3124
3111
  function_data["highlight_full_row"] = True
3112
+ function_data["pin_cursor"] = True
3125
3113
  # function_data["debug"] = True
3126
3114
  # function_data["debug_level"] = 1
3127
3115
  stdscr = start_curses()
@@ -50,9 +50,6 @@ class StandardFooter(Footer):
50
50
  def draw(self, h, w):
51
51
  state = self.get_state()
52
52
  # Fill background
53
- if "footer_string" in state and state["footer_string"]: self.height = 3
54
- else: self.height = 2
55
-
56
53
  for i in range(self.height):
57
54
  self.stdscr.addstr(h-self.height+i, 0, ' '*(w-1), curses.color_pair(self.colours_start+20))
58
55
 
@@ -66,14 +63,19 @@ class StandardFooter(Footer):
66
63
 
67
64
  picker_info_y = h-3
68
65
  sort_info_y = h-2
66
+ self.height = 3
67
+
69
68
  else:
70
69
  picker_info_y = h-2
71
70
  sort_info_y = h-1
71
+ ""
72
72
  select_mode = "C"
73
73
  if state["is_selecting"]: select_mode = "VS"
74
74
  elif state["is_deselecting"]: select_mode = "VDS"
75
75
  if state["pin_cursor"]: select_mode = f"{select_mode} "
76
76
  self.stdscr.addstr(h - 1, w-35, f"{select_mode:>33} ", curses.color_pair(self.colours_start+20))
77
+ self.height = 2
78
+
77
79
 
78
80
  if state["filter_query"]:
79
81
  self.stdscr.addstr(h - 2, 2, f" Filter: {state['filter_query']} "[:w-40], curses.color_pair(self.colours_start+20) | curses.A_BOLD)
@@ -105,7 +107,7 @@ class StandardFooter(Footer):
105
107
  sort_disp_str = f" Sort: ({sort_column_info}, {sort_method_info}, {sort_order_info}) "
106
108
  self.stdscr.addstr(sort_info_y, w-35, f"{sort_disp_str:>34}", curses.color_pair(self.colours_start+20))
107
109
 
108
- # self.stdscr.refresh()
110
+ self.stdscr.refresh()
109
111
 
110
112
 
111
113
 
@@ -36,7 +36,7 @@ def clip_left(text, n):
36
36
  width += char_width
37
37
  return text # If the total width is less than n, return the full string
38
38
 
39
- def truncate_to_display_width(text: str, max_column_width: int, centre=False, unicode_char_width:bool = True) -> str:
39
+ def truncate_to_display_width(text: str, max_column_width: int, centre=False) -> str:
40
40
  """
41
41
  Truncate and/or pad text to max_column_width using wcwidth to ensure visual width is correct
42
42
  with foreign character sets.
@@ -46,36 +46,19 @@ def truncate_to_display_width(text: str, max_column_width: int, centre=False, un
46
46
 
47
47
  """
48
48
  # logger.debug("function: truncate_to_display_width (utils.py)")
49
- if unicode_char_width:
50
- result = ''
51
- width = 0
52
-
53
-
54
- for char in text:
55
- w = wcwidth(char)
56
- if w < 0:
57
- continue
58
- if width + w > max_column_width:
59
- break
60
- result += char
61
- width += w
62
-
63
- # test_str = text[:max_column_width]
64
- # while True:
65
- # width = wcswidth(test_str)
66
- # if width < max_column_width or width == 0:
67
- # break
68
- # test_str = test_str[:-1]
69
- # result = test_str
70
-
71
-
72
- # Pad if it's shorter
73
- # padding = max_column_width - wcswidth(result)
74
- # return result + ' ' * padding
75
- else:
76
- result = text[:max_column_width]
77
- width = len(result)
78
- padding = max_column_width - width
49
+ result = ''
50
+ width = 0
51
+ for char in text:
52
+ w = wcwidth(char)
53
+ if w < 0:
54
+ continue
55
+ if width + w > max_column_width:
56
+ break
57
+ result += char
58
+ width += w
59
+ # Pad if it's shorter
60
+ padding = max_column_width - wcswidth(result)
61
+ # return result + ' ' * padding
79
62
  if centre:
80
63
  result = ' '*(padding//2) + result + ' '*(padding//2 + padding%2)
81
64
  else:
@@ -100,7 +83,7 @@ def format_full_row(row:str) -> str:
100
83
  return '\t'.join(row)
101
84
 
102
85
 
103
- def format_row(row: list[str], hidden_columns: list, column_widths: list[int], separator: str, centre:bool=False, unicode_char_width:bool = True) -> str:
86
+ def format_row(row: list[str], hidden_columns: list, column_widths: list[int], separator: str, centre:bool=False) -> str:
104
87
  """ Format list of strings as a single string. Requires separator string and the maximum width of the columns. """
105
88
  row_str = ""
106
89
  for i, cell in enumerate(row):
@@ -108,22 +91,20 @@ def format_row(row: list[str], hidden_columns: list, column_widths: list[int], s
108
91
  # if is_formula_cell(cell):
109
92
  # cell = evaluate_cell(cell)
110
93
 
111
- val = truncate_to_display_width(str(cell), column_widths[i], centre, unicode_char_width)
94
+ val = truncate_to_display_width(str(cell), column_widths[i], centre)
112
95
  row_str += val + separator
113
96
  return row_str
97
+ # return row_str.strip()
114
98
 
115
- def get_column_widths(items: list[list[str]], header: list[str]=[], max_column_width:int=70, number_columns:bool=True, max_total_width=-1, separator = " ", unicode_char_width:bool=True) -> list[int]:
99
+ def get_column_widths(items: list[list[str]], header: list[str]=[], max_column_width:int=70, number_columns:bool=True, max_total_width=-1, separator = " ") -> list[int]:
116
100
  """ Calculate maximum width of each column with clipping. """
117
101
  if len(items) == 0: return [0]
118
102
  assert len(items) > 0
119
-
120
- if unicode_char_width:
121
- widths = [max(wcswidth(str(row[i])) for row in items) for i in range(len(items[0]))]
122
- # widths = [max(len(str(row[i])) for row in items) for i in range(len(items[0]))]
123
- if header:
124
- header_widths = [wcswidth(f"{i}. {str(h)}") if number_columns else wcswidth(str(h)) for i, h in enumerate(header)]
125
- col_widths = [min(max_column_width, max(widths[i], header_widths[i])) for i in range(len(header))]
126
-
103
+ widths = [max(wcswidth(str(row[i])) for row in items) for i in range(len(items[0]))]
104
+ # widths = [max(len(str(row[i])) for row in items) for i in range(len(items[0]))]
105
+ if header:
106
+ header_widths = [wcswidth(f"{i}. {str(h)}") if number_columns else wcswidth(str(h)) for i, h in enumerate(header)]
107
+ col_widths = [min(max_column_width, max(widths[i], header_widths[i])) for i in range(len(header))]
127
108
  # actual_max_widths = [max(header_widths[i], widths[i]) for i in range(len(widths))]
128
109
  #
129
110
  # if sum(col_widths) + len(separator)*(len(col_widths)-1) < max_total_width:
@@ -143,15 +124,8 @@ def get_column_widths(items: list[list[str]], header: list[str]=[], max_column_w
143
124
  # else:
144
125
  # # Maximise balance.....
145
126
  # pass
146
- else:
147
- col_widths = [min(max_column_width, width) for width in widths]
148
127
  else:
149
- widths = [max(len(str(row[i])) for row in items) for i in range(len(items[0]))]
150
- if header:
151
- header_widths = [len(f"{i}. {str(h)}") if number_columns else len(str(h)) for i, h in enumerate(header)]
152
- col_widths = [min(max_column_width, max(widths[i], header_widths[i])) for i in range(len(header))]
153
- else:
154
- col_widths = [min(max_column_width, width) for width in widths]
128
+ col_widths = [min(max_column_width, width) for width in widths]
155
129
  return col_widths
156
130
 
157
131
  def get_mode_widths(item_list: list[str]) -> list[int]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: listpick
3
- Version: 0.1.13.55
3
+ Version: 0.1.13.56
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
@@ -21,8 +21,6 @@ examples/video_duplicates.toml
21
21
  src/listpick/__init__.py
22
22
  src/listpick/__main__.py
23
23
  src/listpick/listpick_app.py
24
- src/listpick/listpick_app_1.py
25
- src/listpick/lpapp2.py
26
24
  src/listpick.egg-info/PKG-INFO
27
25
  src/listpick.egg-info/SOURCES.txt
28
26
  src/listpick.egg-info/dependency_links.txt
@@ -32,7 +30,6 @@ src/listpick.egg-info/top_level.txt
32
30
  src/listpick/ui/__init__.py
33
31
  src/listpick/ui/build_help.py
34
32
  src/listpick/ui/footer.py
35
- src/listpick/ui/footer_1.py
36
33
  src/listpick/ui/help_screen.py
37
34
  src/listpick/ui/input_field.py
38
35
  src/listpick/ui/keys.py