listpick 0.1.13.56__tar.gz → 0.1.13.58__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 (56) hide show
  1. {listpick-0.1.13.56 → listpick-0.1.13.58}/CHANGELOG.md +1 -0
  2. {listpick-0.1.13.56/src/listpick.egg-info → listpick-0.1.13.58}/PKG-INFO +1 -1
  3. {listpick-0.1.13.56 → listpick-0.1.13.58}/setup.py +1 -1
  4. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/listpick_app.py +94 -41
  5. listpick-0.1.13.58/src/listpick/listpick_app_problem_branch.py +3248 -0
  6. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/utils.py +5 -5
  7. {listpick-0.1.13.56 → listpick-0.1.13.58/src/listpick.egg-info}/PKG-INFO +1 -1
  8. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick.egg-info/SOURCES.txt +1 -0
  9. {listpick-0.1.13.56 → listpick-0.1.13.58}/.gitignore +0 -0
  10. {listpick-0.1.13.56 → listpick-0.1.13.58}/LICENSE.txt +0 -0
  11. {listpick-0.1.13.56 → listpick-0.1.13.58}/README.md +0 -0
  12. {listpick-0.1.13.56 → listpick-0.1.13.58}/TODO.md +0 -0
  13. {listpick-0.1.13.56 → listpick-0.1.13.58}/assets/aria2tui_screenshot.png +0 -0
  14. {listpick-0.1.13.56 → listpick-0.1.13.58}/assets/file_compare.png +0 -0
  15. {listpick-0.1.13.56 → listpick-0.1.13.58}/assets/lpfman.png +0 -0
  16. {listpick-0.1.13.56 → listpick-0.1.13.58}/examples/footer_string_example.py +0 -0
  17. {listpick-0.1.13.56 → listpick-0.1.13.58}/examples/input.toml +0 -0
  18. {listpick-0.1.13.56 → listpick-0.1.13.58}/examples/input.txt +0 -0
  19. {listpick-0.1.13.56 → listpick-0.1.13.58}/examples/list_files.toml +0 -0
  20. {listpick-0.1.13.56 → listpick-0.1.13.58}/examples/list_files_empty.toml +0 -0
  21. {listpick-0.1.13.56 → listpick-0.1.13.58}/examples/picker_example.py +0 -0
  22. {listpick-0.1.13.56 → listpick-0.1.13.58}/examples/setup.cfg +0 -0
  23. {listpick-0.1.13.56 → listpick-0.1.13.58}/examples/template.py +0 -0
  24. {listpick-0.1.13.56 → listpick-0.1.13.58}/examples/video_duplicates.toml +0 -0
  25. {listpick-0.1.13.56 → listpick-0.1.13.58}/listpick.py +0 -0
  26. {listpick-0.1.13.56 → listpick-0.1.13.58}/requirements.txt +0 -0
  27. {listpick-0.1.13.56 → listpick-0.1.13.58}/setup.cfg +0 -0
  28. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/__init__.py +0 -0
  29. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/__main__.py +0 -0
  30. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/ui/__init__.py +0 -0
  31. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/ui/build_help.py +0 -0
  32. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/ui/footer.py +0 -0
  33. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/ui/help_screen.py +0 -0
  34. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/ui/input_field.py +0 -0
  35. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/ui/keys.py +0 -0
  36. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/ui/pane_stuff.py +0 -0
  37. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/ui/picker_colours.py +0 -0
  38. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/__init__.py +0 -0
  39. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/clipboard_operations.py +0 -0
  40. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/config.py +0 -0
  41. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/dump.py +0 -0
  42. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/filtering.py +0 -0
  43. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/generate_data.py +0 -0
  44. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/options_selectors.py +0 -0
  45. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/paste_operations.py +0 -0
  46. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/picker_log.py +0 -0
  47. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/search_and_filter_utils.py +0 -0
  48. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/searching.py +0 -0
  49. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/sorting.py +0 -0
  50. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick/utils/table_to_list_of_lists.py +0 -0
  51. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick.egg-info/dependency_links.txt +0 -0
  52. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick.egg-info/entry_points.txt +0 -0
  53. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick.egg-info/requires.txt +0 -0
  54. {listpick-0.1.13.56 → listpick-0.1.13.58}/src/listpick.egg-info/top_level.txt +0 -0
  55. {listpick-0.1.13.56 → listpick-0.1.13.58}/tests/kitty_control.sh +0 -0
  56. {listpick-0.1.13.56 → listpick-0.1.13.58}/tests/sorting_dates.csv +0 -0
@@ -25,6 +25,7 @@
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
+ - Added try-except wrapper to draw_screen function to prevent crashes during rapid resizing.
28
29
 
29
30
  ## [0.1.13] 2025-07-28
30
31
  - 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.56
3
+ Version: 0.1.13.58
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
@@ -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.56",
19
+ version = "0.1.13.58",
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,6 +140,7 @@ 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,
143
144
 
144
145
  colours_start: int =0,
145
146
  colours_end: int =-1,
@@ -256,7 +257,7 @@ class Picker:
256
257
  self.footer_string_refresh_function = footer_string_refresh_function
257
258
  self.footer_timer = footer_timer
258
259
  self.get_footer_string_startup = get_footer_string_startup,
259
-
260
+ self.unicode_char_width = unicode_char_width
260
261
 
261
262
 
262
263
  self.colours_start = colours_start
@@ -312,17 +313,12 @@ class Picker:
312
313
  self.debug_level = debug_level
313
314
 
314
315
 
315
-
316
-
317
316
  self.initialise_picker_state(reset_colours=self.reset_colours)
318
317
 
319
-
320
318
  # Note: We have to set the footer after initialising the picker state so that the footer can use the get_function_data method
321
319
  self.footer_options = [StandardFooter(self.stdscr, colours_start, self.get_function_data), CompactFooter(self.stdscr, colours_start, self.get_function_data), NoFooter(self.stdscr, colours_start, self.get_function_data)]
322
320
  self.footer = self.footer_options[self.footer_style]
323
321
 
324
- # self.footer = CompactFooter(self.stdscr, colours_start, self.get_function_data)
325
-
326
322
 
327
323
  def calculate_section_sizes(self):
328
324
  """
@@ -384,7 +380,6 @@ class Picker:
384
380
  def initialise_picker_state(self, reset_colours=False) -> None:
385
381
  """ Initialise state variables for the picker. These are: debugging and colours. """
386
382
 
387
-
388
383
  if curses.has_colors() and self.colours != None:
389
384
  # raise Exception("Terminal does not support color")
390
385
  curses.start_color()
@@ -658,6 +653,14 @@ class Picker:
658
653
  self.stdscr.refresh()
659
654
 
660
655
  def draw_screen(self, indexed_items: list[Tuple[int, list[str]]], highlights: list[dict] = [{}], clear: bool = True) -> None:
656
+ """ Try-except wrapper for the draw_screen_ function. """
657
+ try:
658
+ self.draw_screen_(self.indexed_items, self.highlights)
659
+ except Exception as e:
660
+ self.logger.warning(f"self.draw_screen_() error. {e}")
661
+ pass
662
+
663
+ def draw_screen_(self, indexed_items: list[Tuple[int, list[str]]], highlights: list[dict] = [{}], clear: bool = True) -> None:
661
664
  """ Draw Picker screen. """
662
665
  self.logger.debug("Draw screen.")
663
666
 
@@ -686,7 +689,7 @@ class Picker:
686
689
  # rows = [v[1] for v in self.indexed_items] if len(self.indexed_items) else self.items
687
690
  # Determine widths based only on the currently displayed indexed rows
688
691
  rows = [v[1] for v in self.indexed_items[start_index:end_index]] if len(self.indexed_items) else self.items
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)
692
+ 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)
690
693
  visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
691
694
  visible_columns_total_width = sum(visible_column_widths) + len(self.separator)*(len(visible_column_widths)-1)
692
695
 
@@ -785,10 +788,10 @@ class Picker:
785
788
  else:
786
789
  cell_value = self.indexed_items[row][1][col] + self.separator
787
790
  # cell_value = cell_value[:min(cell_width, cell_max_width)-len(self.separator)]
788
- cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width)-len(self.separator))
791
+ cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width)-len(self.separator), self.unicode_char_width)
789
792
  cell_value = cell_value + self.separator
790
793
  # cell_value = cell_value
791
- cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width))
794
+ cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width), self.unicode_char_width)
792
795
  # row_str = truncate_to_display_width(row_str_left_adj, min(w-self.startx, visible_columns_total_width))
793
796
  self.stdscr.addstr(y, cell_pos, cell_value, curses.color_pair(self.colours_start+colour_pair_number) | curses.A_BOLD)
794
797
  # Part of the cell is on screen
@@ -830,7 +833,7 @@ class Picker:
830
833
  def draw_highlights(highlights: list[dict], idx: int, y: int, item: tuple[int, list[str]]):
831
834
  self.logger.debug(f"function: draw_highlights()")
832
835
  if len(highlights) == 0: return None
833
- full_row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)
836
+ full_row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols, self.unicode_char_width)
834
837
  row_str = full_row_str[self.leftmost_char:]
835
838
  for highlight in highlights:
836
839
  if "row" in highlight:
@@ -846,13 +849,13 @@ class Picker:
846
849
  continue
847
850
 
848
851
  elif type(highlight["field"]) == type(0) and highlight["field"] not in self.hidden_columns:
849
- match = re.search(highlight["match"], truncate_to_display_width(item[1][highlight["field"]], self.column_widths[highlight["field"]], centre=False), re.IGNORECASE)
852
+ match = re.search(highlight["match"], truncate_to_display_width(item[1][highlight["field"]], self.column_widths[highlight["field"]], centre=False, unicode_char_width=self.unicode_char_width), re.IGNORECASE)
850
853
  if not match: continue
851
854
  field_start = sum([width for i, width in enumerate(self.column_widths[:highlight["field"]]) if i not in self.hidden_columns]) + sum([1 for i in range(highlight["field"]) if i not in self.hidden_columns])*wcswidth(self.separator)
852
855
 
853
856
  ## We want to search the non-centred values but highlight the centred values.
854
857
  if self.centre_in_cols:
855
- tmp = truncate_to_display_width(item[1][highlight["field"]], self.column_widths[highlight["field"]], self.centre_in_cols)
858
+ tmp = truncate_to_display_width(item[1][highlight["field"]], self.column_widths[highlight["field"]], self.centre_in_cols, self.unicode_char_width)
856
859
  field_start += (len(tmp) - len(tmp.lstrip()))
857
860
 
858
861
  highlight_start = field_start + match.start()
@@ -874,11 +877,11 @@ class Picker:
874
877
  item = self.indexed_items[idx]
875
878
  y = idx - start_index + self.top_space
876
879
 
877
- row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)[self.leftmost_char:]
880
+ # row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)[self.leftmost_char:]
878
881
  # row_str = truncate_to_display_width(row_str, min(w-self.startx, visible_columns_total_width))
879
- row_str_orig = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)
882
+ row_str_orig = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols, self.unicode_char_width)
880
883
  row_str_left_adj = clip_left(row_str_orig, self.leftmost_char)
881
- row_str = truncate_to_display_width(row_str_left_adj, min(w-self.startx, visible_columns_total_width))
884
+ row_str = truncate_to_display_width(row_str_left_adj, min(w-self.startx, visible_columns_total_width), self.unicode_char_width)
882
885
  # row_str = truncate_to_display_width(row_str, min(w-self.startx, visible_columns_total_width))[self.leftmost_char:]
883
886
 
884
887
  ## Display the standard row
@@ -891,7 +894,7 @@ class Picker:
891
894
 
892
895
  # Higlight cursor cell and selected cells
893
896
  if self.cell_cursor:
894
- self.selected_cells_by_row = get_selected_cells_by_row(self.cell_selections)
897
+ # self.selected_cells_by_row = get_selected_cells_by_row(self.cell_selections)
895
898
  if item[0] in self.selected_cells_by_row:
896
899
  for j in self.selected_cells_by_row[item[0]]:
897
900
  highlight_cell(idx, j, visible_column_widths, colour_pair_number=25)
@@ -985,6 +988,7 @@ class Picker:
985
988
  self.stdscr.addstr(h - 1, w-footer_string_width-1, " "*footer_string_width, curses.color_pair(self.colours_start+24))
986
989
  self.stdscr.addstr(h - 1, w-footer_string_width-1, f"{disp_string}", curses.color_pair(self.colours_start+24))
987
990
 
991
+ self.stdscr.refresh()
988
992
  ## Display infobox
989
993
  if self.display_infobox:
990
994
  self.infobox(self.stdscr, message=self.infobox_items, title=self.infobox_title)
@@ -1132,6 +1136,7 @@ class Picker:
1132
1136
  "debug": self.debug,
1133
1137
  "debug_level": self.debug_level,
1134
1138
  "reset_colours": self.reset_colours,
1139
+ "unicode_char_width": self.unicode_char_width,
1135
1140
  }
1136
1141
  return function_data
1137
1142
 
@@ -1385,6 +1390,8 @@ class Picker:
1385
1390
  self.initialise_variables()
1386
1391
  elif setting == "pc":
1387
1392
  self.pin_cursor = not self.pin_cursor
1393
+ elif setting == "unicode":
1394
+ self.unicode_char_width = not self.unicode_char_width
1388
1395
 
1389
1396
  elif setting.startswith("ft"):
1390
1397
  if len(setting) > 2 and setting[2:].isnumeric():
@@ -1448,7 +1455,6 @@ class Picker:
1448
1455
  self.draw_screen(self.indexed_items, self.highlights)
1449
1456
  self.notification(self.stdscr, message=f"Theme {self.colour_theme_number} applied.")
1450
1457
 
1451
-
1452
1458
  else:
1453
1459
  self.user_settings = ""
1454
1460
  return None
@@ -1481,7 +1487,8 @@ class Picker:
1481
1487
  self.selections[self.indexed_items[i][0]] = True
1482
1488
  for i in self.cell_selections.keys():
1483
1489
  self.cell_selections[i] = True
1484
-
1490
+ for row in range(len(self.indexed_items)):
1491
+ self.selected_cells_by_row[row] = list(range(len(self.indexed_items[row][1])))
1485
1492
  self.draw_screen(self.indexed_items, self.highlights)
1486
1493
 
1487
1494
  def deselect_all(self) -> None:
@@ -1491,6 +1498,7 @@ class Picker:
1491
1498
  self.selections[i] = False
1492
1499
  for i in self.cell_selections.keys():
1493
1500
  self.cell_selections[i] = False
1501
+ self.selected_cells_by_row = {}
1494
1502
  self.draw_screen(self.indexed_items, self.highlights)
1495
1503
 
1496
1504
  def handle_visual_selection(self, selecting:bool = True) -> None:
@@ -1519,9 +1527,18 @@ class Picker:
1519
1527
  xend = max(self.start_selection_col, self.selected_column)
1520
1528
  for i in range(ystart, yend + 1):
1521
1529
  if self.indexed_items[i][0] not in self.unselectable_indices:
1522
- for j in range(xstart, xend+1):
1523
- cell_index = (self.indexed_items[i][0], j)
1530
+ row = self.indexed_items[i][0]
1531
+ if row not in self.selected_cells_by_row:
1532
+ self.selected_cells_by_row[row] = []
1533
+
1534
+ for col in range(xstart, xend+1):
1535
+ cell_index = (row, col)
1524
1536
  self.cell_selections[cell_index] = True
1537
+
1538
+ self.selected_cells_by_row[row].append(col)
1539
+ # Remove duplicates
1540
+ self.selected_cells_by_row[row] = list(set(self.selected_cells_by_row[row]))
1541
+
1525
1542
  self.start_selection = -1
1526
1543
  self.end_selection = -1
1527
1544
  self.is_selecting = False
@@ -1543,10 +1560,19 @@ class Picker:
1543
1560
  xstart = min(self.start_selection_col, self.selected_column)
1544
1561
  xend = max(self.start_selection_col, self.selected_column)
1545
1562
  for i in range(ystart, yend + 1):
1563
+ row = self.indexed_items[i][0]
1546
1564
  if self.indexed_items[i][0] not in self.unselectable_indices:
1547
- for j in range(xstart, xend+1):
1548
- cell_index = (self.indexed_items[i][0], j)
1549
- self.cell_selections[cell_index] = False
1565
+ if row in self.selected_cells_by_row:
1566
+ for col in range(xstart, xend+1):
1567
+ try:
1568
+ self.selected_cells_by_row[row].remove(col)
1569
+ except:
1570
+ pass
1571
+ cell_index = (row, col)
1572
+ self.cell_selections[cell_index] = False
1573
+ if self.selected_cells_by_row[row] == []:
1574
+ del self.selected_cells_by_row[row]
1575
+
1550
1576
  self.start_selection = -1
1551
1577
  self.end_selection = -1
1552
1578
  self.is_deselecting = False
@@ -1825,7 +1851,7 @@ class Picker:
1825
1851
 
1826
1852
  def get_word_list(self) -> list[str]:
1827
1853
  """ Get a list of all words used in any cell of the picker. Used for completion in search/filter input_field. """
1828
- self.logger.info(f"function: infobox()")
1854
+ self.logger.info(f"function: get_word_list()")
1829
1855
  translator = str.maketrans('', '', string.punctuation)
1830
1856
 
1831
1857
  words = []
@@ -2068,6 +2094,7 @@ class Picker:
2068
2094
  options += [["rh", "Toggle row header"]]
2069
2095
  options += [["modes", "Toggle modes"]]
2070
2096
  options += [["ft", "Cycle through footer styles (accepts ft#)"]]
2097
+ options += [["unicode", "Toggle b/w using len and wcwidth to calculate char width."]]
2071
2098
  options += [[f"s{i}", f"Select col. {i}"] for i in range(len(self.items[0]))]
2072
2099
  options += [[f"!{i}", f"Toggle col. {i}"] for i in range(len(self.items[0]))]
2073
2100
  options += [["ara", "Add empty row after cursor."]]
@@ -2132,11 +2159,31 @@ class Picker:
2132
2159
  if len(self.indexed_items) > 0:
2133
2160
  item_index = self.indexed_items[self.cursor_pos][0]
2134
2161
  cell_index = (self.indexed_items[self.cursor_pos][0], self.selected_column)
2162
+ row, col = cell_index
2135
2163
  selected_count = sum(self.selections.values())
2136
2164
  if self.max_selected == -1 or selected_count >= self.max_selected:
2137
2165
  self.toggle_item(item_index)
2138
2166
 
2139
2167
  self.cell_selections[cell_index] = not self.cell_selections[cell_index]
2168
+ ## Set self.selected_cells_by_row
2169
+ # If any cells in the current row are selected
2170
+ if row in self.selected_cells_by_row:
2171
+ # If the current cell is selected then remove it
2172
+ if col in self.selected_cells_by_row[row]:
2173
+ # If the current cell is the only cell in the row that is selected then remove the row from the dict
2174
+ if len(self.selected_cells_by_row[row]) == 1:
2175
+
2176
+ del self.selected_cells_by_row[row]
2177
+ # else remove only the index of the current cell
2178
+ else:
2179
+ self.selected_cells_by_row[row].remove(col)
2180
+ # If there are cells in the row that are selected then append the current cell to the row
2181
+ else:
2182
+ self.selected_cells_by_row[row].append(col)
2183
+ # Add the a list containing only the current column
2184
+ else:
2185
+ self.selected_cells_by_row[row] = [col]
2186
+
2140
2187
  self.cursor_down()
2141
2188
  elif self.check_key("select_all", key, self.keys_dict): # Select all (m or ctrl-a)
2142
2189
  self.select_all()
@@ -2270,7 +2317,7 @@ class Picker:
2270
2317
 
2271
2318
  ## Scroll with column select
2272
2319
  rows = self.get_visible_rows()
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)
2320
+ 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)
2274
2321
  visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
2275
2322
  column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
2276
2323
  start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
@@ -2297,8 +2344,7 @@ class Picker:
2297
2344
 
2298
2345
  ## Scroll with column select
2299
2346
  rows = self.get_visible_rows()
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
-
2347
+ 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)
2302
2348
  visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
2303
2349
  column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
2304
2350
  start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
@@ -2313,18 +2359,13 @@ class Picker:
2313
2359
  self.leftmost_char = start_of_cell
2314
2360
 
2315
2361
  self.leftmost_char = max(0, min(column_set_width - display_width + 5, self.leftmost_char))
2316
- #
2362
+
2317
2363
  elif self.check_key("scroll_right", key, self.keys_dict):
2318
2364
  self.logger.info(f"key_function scroll_right")
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
2365
+ if len(self.indexed_items):
2366
+ row_width = sum(self.column_widths) + len(self.separator)*(len(self.column_widths)-1)
2367
+ if row_width-self.leftmost_char >= w-self.startx:
2368
+ self.leftmost_char = self.leftmost_char+5
2328
2369
 
2329
2370
  elif self.check_key("scroll_left", key, self.keys_dict):
2330
2371
  self.logger.info(f"key_function scroll_left")
@@ -2341,7 +2382,7 @@ class Picker:
2341
2382
  rows = self.get_visible_rows()
2342
2383
  for i in range(len(rows)):
2343
2384
  item = rows[i]
2344
- row_str = format_row(item, self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)
2385
+ row_str = format_row(item, self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols, self.unicode_char_width)
2345
2386
  if len(row_str) > longest_row_str_len: longest_row_str_len=len(row_str)
2346
2387
  # for i in range(len(self.indexed_items)):
2347
2388
  # item = self.indexed_items[i]
@@ -2442,7 +2483,7 @@ class Picker:
2442
2483
  elif key == curses.KEY_RESIZE: # Terminal resize signal
2443
2484
 
2444
2485
  self.calculate_section_sizes()
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)
2486
+ 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, unicode_char_width=self.unicode_char_width)
2446
2487
 
2447
2488
  self.draw_screen(self.indexed_items, self.highlights)
2448
2489
 
@@ -2852,7 +2893,6 @@ class Picker:
2852
2893
  def set_colours(pick: int = 0, start: int = 0) -> Optional[int]:
2853
2894
  """ Initialise curses colour pairs from dictionary. """
2854
2895
 
2855
-
2856
2896
  global COLOURS_SET, notification_colours, help_colours
2857
2897
  if COLOURS_SET: return None
2858
2898
  if start == None: start = 0
@@ -3100,6 +3140,13 @@ def main() -> None:
3100
3140
  # 'name': 'mp4',
3101
3141
  # },
3102
3142
  # ]
3143
+ highlights = [
3144
+ {
3145
+ "field": 1,
3146
+ "match": "a",
3147
+ "color": 8,
3148
+ }
3149
+ ]
3103
3150
  function_data["cell_cursor"] = True
3104
3151
  function_data["display_modes"] = True
3105
3152
  function_data["centre_in_cols"] = True
@@ -3110,6 +3157,12 @@ def main() -> None:
3110
3157
  function_data["centre_in_terminal_vertical"] = True
3111
3158
  function_data["highlight_full_row"] = True
3112
3159
  function_data["pin_cursor"] = True
3160
+ function_data["display_infobox"] = True
3161
+ function_data["infobox_items"] = [["1"], ["2"], ["3"]]
3162
+ function_data["infobox_title"] = "Title"
3163
+ function_data["footer_string"] = "Title"
3164
+ function_data["highlights"] = highlights
3165
+ function_data["show_footer"] = False
3113
3166
  # function_data["debug"] = True
3114
3167
  # function_data["debug_level"] = 1
3115
3168
  stdscr = start_curses()