listpick 0.1.13.28__tar.gz → 0.1.13.30__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.
- {listpick-0.1.13.28 → listpick-0.1.13.30}/CHANGELOG.md +10 -1
- {listpick-0.1.13.28/src/listpick.egg-info → listpick-0.1.13.30}/PKG-INFO +1 -1
- {listpick-0.1.13.28 → listpick-0.1.13.30}/TODO.md +4 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/setup.py +1 -1
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/listpick_app.py +61 -60
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/utils.py +45 -24
- {listpick-0.1.13.28 → listpick-0.1.13.30/src/listpick.egg-info}/PKG-INFO +1 -1
- {listpick-0.1.13.28 → listpick-0.1.13.30}/.gitignore +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/LICENSE.txt +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/README.md +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/assets/aria2tui_screenshot.png +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/assets/file_compare.png +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/assets/lpfman.png +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/examples/footer_string_example.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/examples/input.toml +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/examples/input.txt +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/examples/list_files.toml +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/examples/list_files_empty.toml +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/examples/picker_example.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/examples/setup.cfg +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/examples/template.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/examples/video_duplicates.toml +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/listpick.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/requirements.txt +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/setup.cfg +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/__init__.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/__main__.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/ui/__init__.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/ui/build_help.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/ui/footer.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/ui/help_screen.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/ui/input_field.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/ui/keys.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/ui/pane_stuff.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/ui/picker_colours.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/__init__.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/clipboard_operations.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/config.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/dump.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/filtering.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/generate_data.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/options_selectors.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/paste_operations.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/picker_log.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/search_and_filter_utils.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/searching.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/sorting.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick/utils/table_to_list_of_lists.py +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick.egg-info/SOURCES.txt +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick.egg-info/dependency_links.txt +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick.egg-info/entry_points.txt +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick.egg-info/requires.txt +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/src/listpick.egg-info/top_level.txt +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/tests/kitty_control.sh +0 -0
- {listpick-0.1.13.28 → listpick-0.1.13.30}/tests/sorting_dates.csv +0 -0
|
@@ -25,8 +25,17 @@
|
|
|
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
|
-
-
|
|
28
|
+
- Reduced flickering when displaying an infobox.
|
|
29
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.
|
|
30
39
|
|
|
31
40
|
## [0.1.13] 2025-07-28
|
|
32
41
|
- Cell-based picker is now supported.
|
|
@@ -5,6 +5,9 @@ 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'
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
|
|
@@ -114,6 +117,7 @@ ASAP
|
|
|
114
117
|
> - [x] Support pasting copied cells into the picker
|
|
115
118
|
> - [ ] Ensure that we can only paste when cells are editable.
|
|
116
119
|
> - [ ] 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
|
|
117
121
|
> - [ ] Support inserting cells/rows.
|
|
118
122
|
> - [ ] Bulk edit in nvim
|
|
119
123
|
> - [ ] 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.
|
|
19
|
+
version = "0.1.13.30",
|
|
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 = False,
|
|
143
144
|
|
|
144
145
|
colours_start: int =0,
|
|
145
146
|
colours_end: int =-1,
|
|
@@ -256,6 +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,
|
|
260
|
+
self.unicode_char_width = unicode_char_width
|
|
259
261
|
|
|
260
262
|
|
|
261
263
|
|
|
@@ -662,12 +664,13 @@ class Picker:
|
|
|
662
664
|
self.stdscr.refresh()
|
|
663
665
|
|
|
664
666
|
def draw_screen(self, indexed_items: list[Tuple[int, list[str]]], highlights: list[dict] = [{}], clear: bool = True) -> None:
|
|
667
|
+
""" Try-except wrapper for the draw_screen_() method to prevent crashes when rapidly resizing the terminal. """
|
|
665
668
|
try:
|
|
666
|
-
self.
|
|
669
|
+
self.draw_screen_(self.indexed_items, self.highlights)
|
|
667
670
|
except:
|
|
668
671
|
self.logger.warning(f"draw_screen function error")
|
|
669
|
-
|
|
670
|
-
def
|
|
672
|
+
|
|
673
|
+
def draw_screen_(self, indexed_items: list[Tuple[int, list[str]]], highlights: list[dict] = [{}], clear: bool = True) -> None:
|
|
671
674
|
""" Draw Picker screen. """
|
|
672
675
|
self.logger.debug("Draw screen.")
|
|
673
676
|
|
|
@@ -696,7 +699,7 @@ class Picker:
|
|
|
696
699
|
# rows = [v[1] for v in self.indexed_items] if len(self.indexed_items) else self.items
|
|
697
700
|
# Determine widths based only on the currently displayed indexed rows
|
|
698
701
|
rows = [v[1] for v in self.indexed_items[start_index:end_index]] if len(self.indexed_items) else self.items
|
|
699
|
-
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)
|
|
702
|
+
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)
|
|
700
703
|
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
701
704
|
visible_columns_total_width = sum(visible_column_widths) + len(self.separator)*(len(visible_column_widths)-1)
|
|
702
705
|
|
|
@@ -795,10 +798,10 @@ class Picker:
|
|
|
795
798
|
else:
|
|
796
799
|
cell_value = self.indexed_items[row][1][col] + self.separator
|
|
797
800
|
# cell_value = cell_value[:min(cell_width, cell_max_width)-len(self.separator)]
|
|
798
|
-
cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width)-len(self.separator))
|
|
801
|
+
cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width)-len(self.separator), self.unicode_char_width)
|
|
799
802
|
cell_value = cell_value + self.separator
|
|
800
803
|
# cell_value = cell_value
|
|
801
|
-
cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width))
|
|
804
|
+
cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width), self.unicode_char_width)
|
|
802
805
|
# row_str = truncate_to_display_width(row_str_left_adj, min(w-self.startx, visible_columns_total_width))
|
|
803
806
|
self.stdscr.addstr(y, cell_pos, cell_value, curses.color_pair(self.colours_start+colour_pair_number) | curses.A_BOLD)
|
|
804
807
|
# Part of the cell is on screen
|
|
@@ -840,7 +843,7 @@ class Picker:
|
|
|
840
843
|
def draw_highlights(highlights: list[dict], idx: int, y: int, item: tuple[int, list[str]]):
|
|
841
844
|
self.logger.debug(f"function: draw_highlights()")
|
|
842
845
|
if len(highlights) == 0: return None
|
|
843
|
-
full_row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)
|
|
846
|
+
full_row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols, self.unicode_char_width)
|
|
844
847
|
row_str = full_row_str[self.leftmost_char:]
|
|
845
848
|
for highlight in highlights:
|
|
846
849
|
if "row" in highlight:
|
|
@@ -856,13 +859,13 @@ class Picker:
|
|
|
856
859
|
continue
|
|
857
860
|
|
|
858
861
|
elif type(highlight["field"]) == type(0) and highlight["field"] not in self.hidden_columns:
|
|
859
|
-
match = re.search(highlight["match"], truncate_to_display_width(item[1][highlight["field"]], self.column_widths[highlight["field"]], centre=False), re.IGNORECASE)
|
|
862
|
+
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)
|
|
860
863
|
if not match: continue
|
|
861
864
|
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)
|
|
862
865
|
|
|
863
866
|
## We want to search the non-centred values but highlight the centred values.
|
|
864
867
|
if self.centre_in_cols:
|
|
865
|
-
tmp = truncate_to_display_width(item[1][highlight["field"]], self.column_widths[highlight["field"]], self.centre_in_cols)
|
|
868
|
+
tmp = truncate_to_display_width(item[1][highlight["field"]], self.column_widths[highlight["field"]], self.centre_in_cols, self.unicode_char_width)
|
|
866
869
|
field_start += (len(tmp) - len(tmp.lstrip()))
|
|
867
870
|
|
|
868
871
|
highlight_start = field_start + match.start()
|
|
@@ -884,11 +887,11 @@ class Picker:
|
|
|
884
887
|
item = self.indexed_items[idx]
|
|
885
888
|
y = idx - start_index + self.top_space
|
|
886
889
|
|
|
887
|
-
row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)[self.leftmost_char:]
|
|
890
|
+
# row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)[self.leftmost_char:]
|
|
888
891
|
# row_str = truncate_to_display_width(row_str, min(w-self.startx, visible_columns_total_width))
|
|
889
|
-
row_str_orig = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)
|
|
892
|
+
row_str_orig = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols, self.unicode_char_width)
|
|
890
893
|
row_str_left_adj = clip_left(row_str_orig, self.leftmost_char)
|
|
891
|
-
row_str = truncate_to_display_width(row_str_left_adj, min(w-self.startx, visible_columns_total_width))
|
|
894
|
+
row_str = truncate_to_display_width(row_str_left_adj, min(w-self.startx, visible_columns_total_width), self.unicode_char_width)
|
|
892
895
|
# row_str = truncate_to_display_width(row_str, min(w-self.startx, visible_columns_total_width))[self.leftmost_char:]
|
|
893
896
|
|
|
894
897
|
## Display the standard row
|
|
@@ -1153,6 +1156,7 @@ class Picker:
|
|
|
1153
1156
|
"debug": self.debug,
|
|
1154
1157
|
"debug_level": self.debug_level,
|
|
1155
1158
|
"reset_colours": self.reset_colours,
|
|
1159
|
+
"unicode_char_width": self.unicode_char_width,
|
|
1156
1160
|
}
|
|
1157
1161
|
return function_data
|
|
1158
1162
|
|
|
@@ -1250,7 +1254,7 @@ class Picker:
|
|
|
1250
1254
|
while True:
|
|
1251
1255
|
h, w = stdscr.getmaxyx()
|
|
1252
1256
|
|
|
1253
|
-
choose_opts_widths = get_column_widths(options)
|
|
1257
|
+
choose_opts_widths = get_column_widths(options, unicode_char_width=self.unicode_char_width)
|
|
1254
1258
|
window_width = min(max(sum(choose_opts_widths) + 6, 50) + 6, w)
|
|
1255
1259
|
window_height = min(h//2, max(6, len(options)+3))
|
|
1256
1260
|
|
|
@@ -1405,6 +1409,8 @@ class Picker:
|
|
|
1405
1409
|
self.initialise_variables()
|
|
1406
1410
|
elif setting == "pc":
|
|
1407
1411
|
self.pin_cursor = not self.pin_cursor
|
|
1412
|
+
elif setting == "unicode":
|
|
1413
|
+
self.unicode_char_width = not self.unicode_char_width
|
|
1408
1414
|
|
|
1409
1415
|
elif setting.startswith("ft"):
|
|
1410
1416
|
if len(setting) > 2 and setting[2:].isnumeric():
|
|
@@ -1468,7 +1474,6 @@ class Picker:
|
|
|
1468
1474
|
self.draw_screen(self.indexed_items, self.highlights)
|
|
1469
1475
|
self.notification(self.stdscr, message=f"Theme {self.colour_theme_number} applied.")
|
|
1470
1476
|
|
|
1471
|
-
|
|
1472
1477
|
else:
|
|
1473
1478
|
self.user_settings = ""
|
|
1474
1479
|
return None
|
|
@@ -1702,12 +1707,10 @@ class Picker:
|
|
|
1702
1707
|
if not acceptable_data_type:
|
|
1703
1708
|
break
|
|
1704
1709
|
if not acceptable_data_type:
|
|
1705
|
-
self.draw_screen(self.indexed_items, self.highlights)
|
|
1706
1710
|
self.notification(self.stdscr, message="Error pasting data.")
|
|
1707
1711
|
return None
|
|
1708
1712
|
|
|
1709
1713
|
except:
|
|
1710
|
-
self.draw_screen(self.indexed_items, self.highlights)
|
|
1711
1714
|
self.notification(self.stdscr, message="Error pasting data.")
|
|
1712
1715
|
return None
|
|
1713
1716
|
if type(pasta) == type([]) and len(pasta) > 0 and type(pasta[0]) == type([]):
|
|
@@ -1813,6 +1816,7 @@ class Picker:
|
|
|
1813
1816
|
with self.data_lock:
|
|
1814
1817
|
self.items, self.header = tmp_items, tmp_header
|
|
1815
1818
|
self.data_ready = True
|
|
1819
|
+
self.draw_screen(self.indexed_items, self.highlights)
|
|
1816
1820
|
|
|
1817
1821
|
def save_input_history(self, file_path: str) -> bool:
|
|
1818
1822
|
""" Save input field history. Returns True if successful save. """
|
|
@@ -2099,6 +2103,7 @@ class Picker:
|
|
|
2099
2103
|
options += [["rh", "Toggle row header"]]
|
|
2100
2104
|
options += [["modes", "Toggle modes"]]
|
|
2101
2105
|
options += [["ft", "Cycle through footer styles (accepts ft#)"]]
|
|
2106
|
+
options += [["unicode", "Toggle b/w using len and wcwidth to calculate char width."]]
|
|
2102
2107
|
options += [[f"s{i}", f"Select col. {i}"] for i in range(len(self.items[0]))]
|
|
2103
2108
|
options += [[f"!{i}", f"Toggle col. {i}"] for i in range(len(self.items[0]))]
|
|
2104
2109
|
options += [["ara", "Add empty row after cursor."]]
|
|
@@ -2203,8 +2208,6 @@ class Picker:
|
|
|
2203
2208
|
if new_pos < len(self.indexed_items):
|
|
2204
2209
|
self.cursor_pos = new_pos
|
|
2205
2210
|
|
|
2206
|
-
self.draw_screen(self.indexed_items, self.highlights)
|
|
2207
|
-
|
|
2208
2211
|
elif self.check_key("cursor_bottom", key, self.keys_dict):
|
|
2209
2212
|
new_pos = len(self.indexed_items)-1
|
|
2210
2213
|
while True:
|
|
@@ -2212,12 +2215,7 @@ class Picker:
|
|
|
2212
2215
|
else: break
|
|
2213
2216
|
if new_pos < len(self.items) and new_pos >= 0:
|
|
2214
2217
|
self.cursor_pos = new_pos
|
|
2215
|
-
|
|
2216
|
-
# current_row = items_per_page - 1
|
|
2217
|
-
# if current_page + 1 == (len(self.indexed_items) + items_per_page - 1) // items_per_page:
|
|
2218
|
-
#
|
|
2219
|
-
# current_row = (len(self.indexed_items) +items_per_page - 1) % items_per_page
|
|
2220
|
-
# self.draw_screen(self.indexed_items, self.highlights)
|
|
2218
|
+
|
|
2221
2219
|
elif self.check_key("enter", key, self.keys_dict):
|
|
2222
2220
|
self.logger.info(f"key_function enter")
|
|
2223
2221
|
# Print the selected indices if any, otherwise print the current index
|
|
@@ -2297,7 +2295,6 @@ class Picker:
|
|
|
2297
2295
|
if len(self.indexed_items) > 0:
|
|
2298
2296
|
current_index = self.indexed_items[self.cursor_pos][0]
|
|
2299
2297
|
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
|
|
2300
|
-
self.draw_screen(self.indexed_items, self.highlights)
|
|
2301
2298
|
self.cursor_pos = [row[0] for row in self.indexed_items].index(current_index)
|
|
2302
2299
|
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]})")
|
|
2303
2300
|
elif self.check_key("col_select", key, self.keys_dict):
|
|
@@ -2321,7 +2318,7 @@ class Picker:
|
|
|
2321
2318
|
|
|
2322
2319
|
## Scroll with column select
|
|
2323
2320
|
rows = self.get_visible_rows()
|
|
2324
|
-
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)
|
|
2321
|
+
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)
|
|
2325
2322
|
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
2326
2323
|
column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
|
|
2327
2324
|
start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
|
|
@@ -2348,7 +2345,7 @@ class Picker:
|
|
|
2348
2345
|
|
|
2349
2346
|
## Scroll with column select
|
|
2350
2347
|
rows = self.get_visible_rows()
|
|
2351
|
-
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)
|
|
2348
|
+
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)
|
|
2352
2349
|
|
|
2353
2350
|
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
2354
2351
|
column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
|
|
@@ -2367,15 +2364,11 @@ class Picker:
|
|
|
2367
2364
|
#
|
|
2368
2365
|
elif self.check_key("scroll_right", key, self.keys_dict):
|
|
2369
2366
|
self.logger.info(f"key_function scroll_right")
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
row_str = format_row(item, self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)[self.leftmost_char:]
|
|
2375
|
-
if len(row_str) > longest_row_str_len: longest_row_str_len=len(row_str)
|
|
2367
|
+
if len(self.indexed_items):
|
|
2368
|
+
row_width = sum(self.column_widths) + len(self.separator)*(len(self.column_widths)-1)
|
|
2369
|
+
if row_width-self.leftmost_char >= w-self.startx:
|
|
2370
|
+
self.leftmost_char = self.leftmost_char+5
|
|
2376
2371
|
|
|
2377
|
-
if longest_row_str_len >= w-self.startx:
|
|
2378
|
-
self.leftmost_char = self.leftmost_char+5
|
|
2379
2372
|
|
|
2380
2373
|
elif self.check_key("scroll_left", key, self.keys_dict):
|
|
2381
2374
|
self.logger.info(f"key_function scroll_left")
|
|
@@ -2392,7 +2385,7 @@ class Picker:
|
|
|
2392
2385
|
rows = self.get_visible_rows()
|
|
2393
2386
|
for i in range(len(rows)):
|
|
2394
2387
|
item = rows[i]
|
|
2395
|
-
row_str = format_row(item, self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)
|
|
2388
|
+
row_str = format_row(item, self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols, self.unicode_char_width)
|
|
2396
2389
|
if len(row_str) > longest_row_str_len: longest_row_str_len=len(row_str)
|
|
2397
2390
|
# for i in range(len(self.indexed_items)):
|
|
2398
2391
|
# item = self.indexed_items[i]
|
|
@@ -2463,11 +2456,10 @@ class Picker:
|
|
|
2463
2456
|
|
|
2464
2457
|
# elif self.check_key("increase_lines_per_page", key, self.keys_dict):
|
|
2465
2458
|
# self.items_per_page += 1
|
|
2466
|
-
# self.draw_screen(self.indexed_items, self.highlights)
|
|
2467
2459
|
# elif self.check_key("decrease_lines_per_page", key, self.keys_dict):
|
|
2468
2460
|
# if self.items_per_page > 1:
|
|
2469
2461
|
# self.items_per_page -= 1
|
|
2470
|
-
|
|
2462
|
+
|
|
2471
2463
|
elif self.check_key("decrease_column_width", key, self.keys_dict):
|
|
2472
2464
|
self.logger.info(f"key_function decrease_column_width")
|
|
2473
2465
|
if self.max_column_width > 10:
|
|
@@ -2489,7 +2481,7 @@ class Picker:
|
|
|
2489
2481
|
elif key == curses.KEY_RESIZE: # Terminal resize signal
|
|
2490
2482
|
|
|
2491
2483
|
self.calculate_section_sizes()
|
|
2492
|
-
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)
|
|
2484
|
+
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)
|
|
2493
2485
|
|
|
2494
2486
|
|
|
2495
2487
|
|
|
@@ -2884,11 +2876,10 @@ class Picker:
|
|
|
2884
2876
|
self.stdscr.clear()
|
|
2885
2877
|
self.stdscr.refresh()
|
|
2886
2878
|
self.initialise_variables()
|
|
2887
|
-
# self.draw_screen(self.indexed_items, self.highlights)
|
|
2888
2879
|
|
|
2889
2880
|
|
|
2890
|
-
|
|
2891
|
-
|
|
2881
|
+
if key != -1:
|
|
2882
|
+
self.draw_screen(self.indexed_items, self.highlights, clear=clear_screen)
|
|
2892
2883
|
|
|
2893
2884
|
|
|
2894
2885
|
|
|
@@ -3128,21 +3119,28 @@ def main() -> None:
|
|
|
3128
3119
|
pass
|
|
3129
3120
|
|
|
3130
3121
|
function_data["colour_theme_number"] = 3
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3122
|
+
function_data["modes"] = [
|
|
3123
|
+
{
|
|
3124
|
+
'filter': '',
|
|
3125
|
+
'sort': 0,
|
|
3126
|
+
'name': 'All',
|
|
3127
|
+
},
|
|
3128
|
+
{
|
|
3129
|
+
'filter': '--2 miss',
|
|
3130
|
+
'name': 'miss',
|
|
3131
|
+
},
|
|
3132
|
+
{
|
|
3133
|
+
'filter': '--2 mp4',
|
|
3134
|
+
'name': 'mp4',
|
|
3135
|
+
},
|
|
3136
|
+
]
|
|
3137
|
+
highlights = [
|
|
3138
|
+
{
|
|
3139
|
+
"field": 1,
|
|
3140
|
+
"match": "a",
|
|
3141
|
+
"color": 8,
|
|
3142
|
+
}
|
|
3143
|
+
]
|
|
3146
3144
|
function_data["cell_cursor"] = True
|
|
3147
3145
|
function_data["display_modes"] = True
|
|
3148
3146
|
function_data["centre_in_cols"] = True
|
|
@@ -3153,15 +3151,18 @@ def main() -> None:
|
|
|
3153
3151
|
function_data["centre_in_terminal_vertical"] = True
|
|
3154
3152
|
function_data["highlight_full_row"] = True
|
|
3155
3153
|
function_data["pin_cursor"] = True
|
|
3156
|
-
function_data["display_infobox"] = True
|
|
3157
|
-
function_data["infobox_items"] = [["1"], ["2"], ["3"]]
|
|
3158
|
-
function_data["infobox_title"] = "Title"
|
|
3154
|
+
# function_data["display_infobox"] = True
|
|
3155
|
+
# function_data["infobox_items"] = [["1"], ["2"], ["3"]]
|
|
3156
|
+
# function_data["infobox_title"] = "Title"
|
|
3159
3157
|
function_data["footer_string"] = "Title"
|
|
3158
|
+
function_data["highlights"] = highlights
|
|
3160
3159
|
|
|
3161
3160
|
|
|
3162
3161
|
# function_data["debug"] = True
|
|
3163
3162
|
# function_data["debug_level"] = 1
|
|
3164
3163
|
stdscr = start_curses()
|
|
3164
|
+
# h, w = stdscr.getmaxyx()
|
|
3165
|
+
# win = stdscr.derwin(h, w//2, 0, 0)
|
|
3165
3166
|
try:
|
|
3166
3167
|
# Run the Picker
|
|
3167
3168
|
# h, w = stdscr.getmaxyx()
|
|
@@ -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) -> str:
|
|
39
|
+
def truncate_to_display_width(text: str, max_column_width: int, centre=False, unicode_char_width:bool = True) -> 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,19 +46,31 @@ def truncate_to_display_width(text: str, max_column_width: int, centre=False) ->
|
|
|
46
46
|
|
|
47
47
|
"""
|
|
48
48
|
# logger.debug("function: truncate_to_display_width (utils.py)")
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
if unicode_char_width:
|
|
50
|
+
result = ''
|
|
51
|
+
width = 0
|
|
52
|
+
test_str = text[:max_column_width]
|
|
53
|
+
while True:
|
|
54
|
+
width = wcswidth(test_str)
|
|
55
|
+
if width < max_column_width or width == 0:
|
|
56
|
+
break
|
|
57
|
+
test_str = test_str[:-1]
|
|
58
|
+
# for char in text:
|
|
59
|
+
# w = wcwidth(char)
|
|
60
|
+
# if w < 0:
|
|
61
|
+
# continue
|
|
62
|
+
# if width + w > max_column_width:
|
|
63
|
+
# break
|
|
64
|
+
# result += char
|
|
65
|
+
# width += w
|
|
66
|
+
# Pad if it's shorter
|
|
67
|
+
# padding = max_column_width - wcswidth(result)
|
|
68
|
+
result = test_str
|
|
69
|
+
# return result + ' ' * padding
|
|
70
|
+
else:
|
|
71
|
+
result = text[:max_column_width]
|
|
72
|
+
width = len(result)
|
|
73
|
+
padding = max_column_width - width
|
|
62
74
|
if centre:
|
|
63
75
|
result = ' '*(padding//2) + result + ' '*(padding//2 + padding%2)
|
|
64
76
|
else:
|
|
@@ -83,7 +95,7 @@ def format_full_row(row:str) -> str:
|
|
|
83
95
|
return '\t'.join(row)
|
|
84
96
|
|
|
85
97
|
|
|
86
|
-
def format_row(row: list[str], hidden_columns: list, column_widths: list[int], separator: str, centre:bool=False) -> str:
|
|
98
|
+
def format_row(row: list[str], hidden_columns: list, column_widths: list[int], separator: str, centre:bool=False, unicode_char_width:bool = True) -> str:
|
|
87
99
|
""" Format list of strings as a single string. Requires separator string and the maximum width of the columns. """
|
|
88
100
|
row_str = ""
|
|
89
101
|
for i, cell in enumerate(row):
|
|
@@ -91,20 +103,22 @@ def format_row(row: list[str], hidden_columns: list, column_widths: list[int], s
|
|
|
91
103
|
# if is_formula_cell(cell):
|
|
92
104
|
# cell = evaluate_cell(cell)
|
|
93
105
|
|
|
94
|
-
val = truncate_to_display_width(str(cell), column_widths[i], centre)
|
|
106
|
+
val = truncate_to_display_width(str(cell), column_widths[i], centre, unicode_char_width)
|
|
95
107
|
row_str += val + separator
|
|
96
108
|
return row_str
|
|
97
|
-
# return row_str.strip()
|
|
98
109
|
|
|
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]:
|
|
110
|
+
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]:
|
|
100
111
|
""" Calculate maximum width of each column with clipping. """
|
|
101
112
|
if len(items) == 0: return [0]
|
|
102
113
|
assert len(items) > 0
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
114
|
+
|
|
115
|
+
if unicode_char_width:
|
|
116
|
+
widths = [max(wcswidth(str(row[i])) for row in items) for i in range(len(items[0]))]
|
|
117
|
+
# widths = [max(len(str(row[i])) for row in items) for i in range(len(items[0]))]
|
|
118
|
+
if header:
|
|
119
|
+
header_widths = [wcswidth(f"{i}. {str(h)}") if number_columns else wcswidth(str(h)) for i, h in enumerate(header)]
|
|
120
|
+
col_widths = [min(max_column_width, max(widths[i], header_widths[i])) for i in range(len(header))]
|
|
121
|
+
|
|
108
122
|
# actual_max_widths = [max(header_widths[i], widths[i]) for i in range(len(widths))]
|
|
109
123
|
#
|
|
110
124
|
# if sum(col_widths) + len(separator)*(len(col_widths)-1) < max_total_width:
|
|
@@ -124,8 +138,15 @@ def get_column_widths(items: list[list[str]], header: list[str]=[], max_column_w
|
|
|
124
138
|
# else:
|
|
125
139
|
# # Maximise balance.....
|
|
126
140
|
# pass
|
|
141
|
+
else:
|
|
142
|
+
col_widths = [min(max_column_width, width) for width in widths]
|
|
127
143
|
else:
|
|
128
|
-
|
|
144
|
+
widths = [max(len(str(row[i])) for row in items) for i in range(len(items[0]))]
|
|
145
|
+
if header:
|
|
146
|
+
header_widths = [len(f"{i}. {str(h)}") if number_columns else len(str(h)) for i, h in enumerate(header)]
|
|
147
|
+
col_widths = [min(max_column_width, max(widths[i], header_widths[i])) for i in range(len(header))]
|
|
148
|
+
else:
|
|
149
|
+
col_widths = [min(max_column_width, width) for width in widths]
|
|
129
150
|
return col_widths
|
|
130
151
|
|
|
131
152
|
def get_mode_widths(item_list: list[str]) -> list[int]:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|