tksheet 7.5.5__py3-none-any.whl → 7.5.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tksheet/row_index.py CHANGED
@@ -24,6 +24,7 @@ from .functions import (
24
24
  event_dict,
25
25
  event_has_char_key,
26
26
  event_opens_dropdown_or_checkbox,
27
+ get_bg_fg,
27
28
  get_menu_kwargs,
28
29
  get_n2a,
29
30
  get_new_indexes,
@@ -33,15 +34,20 @@ from .functions import (
33
34
  new_tk_event,
34
35
  num2alpha,
35
36
  push_displayed,
37
+ recursive_bind,
36
38
  rounded_box_coords,
39
+ safe_copy,
37
40
  stored_event_dict,
38
41
  try_b_index,
39
42
  try_binding,
43
+ widget_descendants,
40
44
  wrap_text,
41
45
  )
46
+ from .menus import build_empty_rc_menu, build_index_rc_menu
42
47
  from .other_classes import DraggedRowColumn, DropdownStorage, EventDataDict, Node, TextEditorStorage
43
48
  from .sorting import sort_columns_by_row, sort_row
44
49
  from .text_editor import TextEditor
50
+ from .tooltip import Tooltip
45
51
 
46
52
 
47
53
  class RowIndex(tk.Canvas):
@@ -56,6 +62,18 @@ class RowIndex(tk.Canvas):
56
62
  self.MT = None # is set from within MainTable() __init__
57
63
  self.CH = None # is set from within MainTable() __init__
58
64
  self.TL = None # is set from within TopLeftRectangle() __init__
65
+ self.tooltip = Tooltip(
66
+ **{
67
+ "parent": self,
68
+ "sheet_ops": self.ops,
69
+ "menu_kwargs": get_menu_kwargs(self.ops),
70
+ **get_bg_fg(self.ops),
71
+ "scrollbar_style": f"Sheet{self.PAR.unique_id}.Vertical.TScrollbar",
72
+ }
73
+ )
74
+ self.tooltip_widgets = widget_descendants(self.tooltip)
75
+ self.tooltip_coords, self.tooltip_after_id, self.tooltip_showing = None, None, False
76
+ recursive_bind(self.tooltip, "<Leave>", self.close_tooltip_save)
59
77
  self.current_cursor = ""
60
78
  self.new_iid_ctr = -1
61
79
  self.current_width = None
@@ -109,7 +127,7 @@ class RowIndex(tk.Canvas):
109
127
  self.disp_dropdown = {}
110
128
  self.disp_checkbox = {}
111
129
  self.disp_tree_arrow = {}
112
- self.disp_boxes = set()
130
+ self.disp_corners = set()
113
131
  self.hidd_text = {}
114
132
  self.hidd_high = {}
115
133
  self.hidd_grid = {}
@@ -119,7 +137,7 @@ class RowIndex(tk.Canvas):
119
137
  self.hidd_dropdown = {}
120
138
  self.hidd_checkbox = {}
121
139
  self.hidd_tree_arrow = {}
122
- self.hidd_boxes = set()
140
+ self.hidd_corners = set()
123
141
 
124
142
  self.align = kwargs["row_index_align"]
125
143
 
@@ -169,15 +187,17 @@ class RowIndex(tk.Canvas):
169
187
  popup_menu = None
170
188
  if self.MT.identify_row(y=event.y, allow_end=False) is None:
171
189
  self.MT.deselect("all")
172
- r = len(self.MT.col_positions) - 1
190
+ r = len(self.MT.row_positions) - 1
173
191
  if self.MT.rc_popup_menus_enabled:
174
192
  popup_menu = self.MT.empty_rc_popup_menu
193
+ build_empty_rc_menu(self.MT, popup_menu)
175
194
  elif self.row_selection_enabled and not self.currently_resizing_width and not self.currently_resizing_height:
176
195
  r = self.MT.identify_row(y=event.y)
177
196
  if r < len(self.MT.row_positions) - 1:
178
197
  if self.MT.row_selected(r):
179
198
  if self.MT.rc_popup_menus_enabled:
180
199
  popup_menu = self.ri_rc_popup_menu
200
+ build_index_rc_menu(self.MT, popup_menu, r)
181
201
  else:
182
202
  if self.MT.single_selection_enabled and self.MT.rc_select_enabled:
183
203
  self.select_row(r, redraw=True)
@@ -185,11 +205,10 @@ class RowIndex(tk.Canvas):
185
205
  self.toggle_select_row(r, redraw=True)
186
206
  if self.MT.rc_popup_menus_enabled:
187
207
  popup_menu = self.ri_rc_popup_menu
208
+ build_index_rc_menu(self.MT, popup_menu, r)
188
209
  try_binding(self.extra_rc_func, event)
189
210
  if popup_menu is not None:
190
211
  self.popup_menu_loc = r
191
- self.MT.popup_menu_disable_edit_if_readonly(popup_menu)
192
- self.MT.popup_menu_disable_undo_redo(popup_menu)
193
212
  popup_menu.tk_popup(event.x_root, event.y_root)
194
213
 
195
214
  def ctrl_b1_press(self, event: Any) -> None:
@@ -911,7 +930,7 @@ class RowIndex(tk.Canvas):
911
930
  event_data = self.MT.new_event_dict("edit_table")
912
931
  try_binding(self.MT.extra_begin_sort_cells_func, event_data)
913
932
  if key is None:
914
- key = self.PAR.ops.sort_key
933
+ key = self.ops.sort_key
915
934
  for r in rows:
916
935
  datarn = self.MT.datarn(r)
917
936
  for c, val in enumerate(sort_row(self.MT.data[datarn], reverse=reverse, key=key)):
@@ -957,7 +976,7 @@ class RowIndex(tk.Canvas):
957
976
  row = self.MT.datarn(self.MT.selected.row)
958
977
  if try_binding(self.ri_extra_begin_sort_cols_func, event_data, "begin_move_columns"):
959
978
  if key is None:
960
- key = self.PAR.ops.sort_key
979
+ key = self.ops.sort_key
961
980
  sorted_indices, data_new_idxs = sort_columns_by_row(self.MT.data, row=row, reverse=reverse, key=key)
962
981
  disp_new_idxs = {}
963
982
  if self.MT.all_columns_displayed:
@@ -1295,44 +1314,60 @@ class RowIndex(tk.Canvas):
1295
1314
  redrawn = False
1296
1315
  kwargs = self.get_cell_kwargs(datarn, key="highlight")
1297
1316
  if kwargs:
1298
- fill = kwargs[0]
1299
- if fill and not fill.startswith("#"):
1300
- fill = color_map[fill]
1317
+ high_bg = kwargs[0]
1318
+ if high_bg and not high_bg.startswith("#"):
1319
+ high_bg = color_map[high_bg]
1301
1320
  if "rows" in selections and r in selections["rows"]:
1302
1321
  txtfg = (
1303
1322
  self.ops.index_selected_rows_fg
1304
1323
  if kwargs[1] is None or self.ops.display_selected_fg_over_highlights
1305
1324
  else kwargs[1]
1306
1325
  )
1307
- if fill:
1308
- fill = (
1309
- f"#{int((int(fill[1:3], 16) + int(sel_rows_bg[1:3], 16)) / 2):02X}"
1310
- + f"{int((int(fill[3:5], 16) + int(sel_rows_bg[3:5], 16)) / 2):02X}"
1311
- + f"{int((int(fill[5:], 16) + int(sel_rows_bg[5:], 16)) / 2):02X}"
1312
- )
1326
+ redrawn = self.redraw_highlight(
1327
+ 0,
1328
+ fr + 1,
1329
+ self.current_width - 1,
1330
+ sr,
1331
+ fill=self.ops.index_selected_rows_bg
1332
+ if high_bg is None
1333
+ else (
1334
+ f"#{int((int(high_bg[1:3], 16) + int(sel_rows_bg[1:3], 16)) / 2):02X}"
1335
+ + f"{int((int(high_bg[3:5], 16) + int(sel_rows_bg[3:5], 16)) / 2):02X}"
1336
+ + f"{int((int(high_bg[5:], 16) + int(sel_rows_bg[5:], 16)) / 2):02X}"
1337
+ ),
1338
+ outline=self.ops.index_fg if has_dd and self.ops.show_dropdown_borders else "",
1339
+ )
1313
1340
  elif "cells" in selections and r in selections["cells"]:
1314
1341
  txtfg = (
1315
1342
  self.ops.index_selected_cells_fg
1316
1343
  if kwargs[1] is None or self.ops.display_selected_fg_over_highlights
1317
1344
  else kwargs[1]
1318
1345
  )
1319
- if fill:
1320
- fill = (
1321
- f"#{int((int(fill[1:3], 16) + int(sel_cells_bg[1:3], 16)) / 2):02X}"
1322
- + f"{int((int(fill[3:5], 16) + int(sel_cells_bg[3:5], 16)) / 2):02X}"
1323
- + f"{int((int(fill[5:], 16) + int(sel_cells_bg[5:], 16)) / 2):02X}"
1324
- )
1325
- else:
1326
- txtfg = self.ops.index_fg if kwargs[1] is None else kwargs[1]
1327
- if fill:
1328
1346
  redrawn = self.redraw_highlight(
1329
1347
  0,
1330
1348
  fr + 1,
1331
1349
  self.current_width - 1,
1332
1350
  sr,
1333
- fill=fill,
1351
+ fill=self.ops.index_selected_cells_bg
1352
+ if high_bg is None
1353
+ else (
1354
+ f"#{int((int(high_bg[1:3], 16) + int(sel_cells_bg[1:3], 16)) / 2):02X}"
1355
+ + f"{int((int(high_bg[3:5], 16) + int(sel_cells_bg[3:5], 16)) / 2):02X}"
1356
+ + f"{int((int(high_bg[5:], 16) + int(sel_cells_bg[5:], 16)) / 2):02X}"
1357
+ ),
1334
1358
  outline=self.ops.index_fg if has_dd and self.ops.show_dropdown_borders else "",
1335
1359
  )
1360
+ else:
1361
+ txtfg = self.ops.index_fg if kwargs[1] is None else kwargs[1]
1362
+ if high_bg:
1363
+ redrawn = self.redraw_highlight(
1364
+ 0,
1365
+ fr + 1,
1366
+ self.current_width - 1,
1367
+ sr,
1368
+ fill=high_bg,
1369
+ outline=self.ops.index_fg if has_dd and self.ops.show_dropdown_borders else "",
1370
+ )
1336
1371
  tree_arrow_fg = txtfg
1337
1372
  elif not kwargs:
1338
1373
  if "rows" in selections and r in selections["rows"]:
@@ -1614,6 +1649,17 @@ class RowIndex(tk.Canvas):
1614
1649
  self.MT.char_widths[self.index_font][c] = wd
1615
1650
  return wd
1616
1651
 
1652
+ def redraw_corner(self, x: float, y: float) -> None:
1653
+ if self.hidd_corners:
1654
+ iid = self.hidd_corners.pop()
1655
+ self.coords(iid, x - 10, y, x, y, x, y + 10)
1656
+ self.itemconfig(iid, fill=self.ops.index_grid_fg, state="normal")
1657
+ self.disp_corners.add(iid)
1658
+ else:
1659
+ self.disp_corners.add(
1660
+ self.create_polygon(x - 10, y, x, y, x, y + 10, fill=self.ops.index_grid_fg, tags="lift")
1661
+ )
1662
+
1617
1663
  def redraw_grid_and_text(
1618
1664
  self,
1619
1665
  last_row_line_pos: float,
@@ -1641,6 +1687,8 @@ class RowIndex(tk.Canvas):
1641
1687
  self.disp_checkbox = {}
1642
1688
  self.hidd_tree_arrow.update(self.disp_tree_arrow)
1643
1689
  self.disp_tree_arrow = {}
1690
+ self.hidd_corners.update(self.disp_corners)
1691
+ self.disp_corners = set()
1644
1692
  self.visible_row_dividers = {}
1645
1693
  self.row_width_resize_bbox = (
1646
1694
  self.current_width - 2,
@@ -1692,6 +1740,7 @@ class RowIndex(tk.Canvas):
1692
1740
  dd_coords = self.dropdown.get_coords()
1693
1741
  treeview = self.ops.treeview
1694
1742
  wrap = self.ops.index_wrap
1743
+ note_corners = self.ops.note_corners
1695
1744
  for r in range(text_start_row, text_end_row):
1696
1745
  rtopgridln = self.MT.row_positions[r]
1697
1746
  rbotgridln = self.MT.row_positions[r + 1]
@@ -1787,6 +1836,10 @@ class RowIndex(tk.Canvas):
1787
1836
  open_=self.MT._row_index[datarn].iid in self.tree_open_ids,
1788
1837
  level=level,
1789
1838
  )
1839
+
1840
+ if note_corners and max_width > 5 and datarn in self.cell_options and "note" in self.cell_options[datarn]:
1841
+ self.redraw_corner(self.current_width, rtopgridln)
1842
+
1790
1843
  if max_width <= 1:
1791
1844
  continue
1792
1845
  text = self.cell_str(datarn, fix=False)
@@ -1814,6 +1867,7 @@ class RowIndex(tk.Canvas):
1814
1867
  fill=fill,
1815
1868
  font=font,
1816
1869
  anchor=align,
1870
+ tags=("lift", "t", f"{r}"),
1817
1871
  )
1818
1872
  else:
1819
1873
  self.itemconfig(
@@ -1823,6 +1877,7 @@ class RowIndex(tk.Canvas):
1823
1877
  font=font,
1824
1878
  anchor=align,
1825
1879
  state="normal",
1880
+ tags=("lift", "t", f"{r}"),
1826
1881
  )
1827
1882
  else:
1828
1883
  iid = self.create_text(
@@ -1832,7 +1887,7 @@ class RowIndex(tk.Canvas):
1832
1887
  fill=fill,
1833
1888
  font=font,
1834
1889
  anchor=align,
1835
- tag="lift",
1890
+ tags=("lift", "t", f"{r}"),
1836
1891
  )
1837
1892
  self.disp_text[iid] = True
1838
1893
  else:
@@ -1847,6 +1902,7 @@ class RowIndex(tk.Canvas):
1847
1902
  fill=fill,
1848
1903
  font=font,
1849
1904
  anchor=align,
1905
+ tags=("lift", "t", f"{r}"),
1850
1906
  )
1851
1907
  else:
1852
1908
  self.itemconfig(
@@ -1856,6 +1912,7 @@ class RowIndex(tk.Canvas):
1856
1912
  font=font,
1857
1913
  anchor=align,
1858
1914
  state="normal",
1915
+ tags=("lift", "t", f"{r}"),
1859
1916
  )
1860
1917
  else:
1861
1918
  iid = self.create_text(
@@ -1865,7 +1922,7 @@ class RowIndex(tk.Canvas):
1865
1922
  fill=fill,
1866
1923
  font=font,
1867
1924
  anchor=align,
1868
- tag="lift",
1925
+ tags=("lift", "t", f"{r}"),
1869
1926
  )
1870
1927
  self.disp_text[iid] = True
1871
1928
  draw_y += self.MT.header_txt_height
@@ -1882,11 +1939,115 @@ class RowIndex(tk.Canvas):
1882
1939
  if showing:
1883
1940
  self.itemconfig(iid, state="hidden")
1884
1941
  dct[iid] = False
1942
+ for iid in self.hidd_corners:
1943
+ self.itemconfig(iid, state="hidden")
1885
1944
  self.tag_raise("lift")
1886
1945
  if self.disp_resize_lines:
1887
1946
  self.tag_raise("rh")
1947
+ self.tag_bind("t", "<Enter>", self.enter_text)
1948
+ self.tag_bind("t", "<Leave>", self.leave_text)
1888
1949
  return True
1889
1950
 
1951
+ def enter_text(self, event: tk.Event | None = None) -> None:
1952
+ if self.text_editor.open or self.dropdown.open:
1953
+ return
1954
+ can_x, can_y = self.canvasx(event.x), self.canvasy(event.y)
1955
+ for i in self.find_overlapping(can_x - 1, can_y - 1, can_x + 1, can_y + 1):
1956
+ try:
1957
+ if (coords := self.gettags(i)[2]) == self.tooltip_coords:
1958
+ return
1959
+ self.tooltip_coords = coords
1960
+ self.tooltip_last_x, self.tooltip_last_y = self.winfo_pointerx(), self.winfo_pointery()
1961
+ self.start_tooltip_timer()
1962
+ return
1963
+ except Exception:
1964
+ continue
1965
+
1966
+ def leave_text(self, event: tk.Event | None = None) -> None:
1967
+ if self.tooltip_after_id is not None:
1968
+ self.after_cancel(self.tooltip_after_id)
1969
+ self.tooltip_after_id = None
1970
+ if self.tooltip_showing:
1971
+ if self.winfo_containing(self.winfo_pointerx(), self.winfo_pointery()) not in self.tooltip_widgets:
1972
+ self.close_tooltip_save()
1973
+ else:
1974
+ self.tooltip_coords = None
1975
+
1976
+ def start_tooltip_timer(self) -> None:
1977
+ self.tooltip_after_id = self.after(1000, self.check_and_show_tooltip)
1978
+
1979
+ def check_and_show_tooltip(self, event: tk.Event | None = None) -> None:
1980
+ current_x, current_y = self.winfo_pointerx(), self.winfo_pointery()
1981
+ if current_x < 0 or current_y < 0:
1982
+ return
1983
+ if abs(current_x - self.tooltip_last_x) <= 1 and abs(current_y - self.tooltip_last_y) <= 1:
1984
+ self.show_tooltip()
1985
+ else:
1986
+ self.tooltip_last_x, self.tooltip_last_y = current_x, current_y
1987
+ self.tooltip_after_id = self.after(400, self.check_and_show_tooltip)
1988
+
1989
+ def hide_tooltip(self) -> None:
1990
+ self.tooltip.withdraw()
1991
+ self.tooltip_showing, self.tooltip_coords = False, None
1992
+
1993
+ def show_tooltip(self) -> None:
1994
+ r = int(self.tooltip_coords)
1995
+ datarn = self.MT.datarn(r)
1996
+ kws = self.get_cell_kwargs(datarn, key="note")
1997
+ if not self.ops.tooltips and not kws and not self.ops.user_can_create_notes:
1998
+ return
1999
+ cell_readonly = self.get_cell_kwargs(datarn, "readonly") or not self.MT.index_edit_cell_enabled()
2000
+ if kws:
2001
+ note = kws["note"]
2002
+ note_readonly = kws["readonly"]
2003
+ elif self.ops.user_can_create_notes:
2004
+ note = ""
2005
+ note_readonly = bool(cell_readonly)
2006
+ else:
2007
+ note = None
2008
+ note_readonly = True
2009
+ self.tooltip.reset(
2010
+ **{
2011
+ "text": f"{self.get_cell_data(datarn, none_to_empty_str=True)}",
2012
+ "cell_readonly": cell_readonly,
2013
+ "note": note,
2014
+ "note_readonly": note_readonly,
2015
+ "row": r,
2016
+ "col": 0,
2017
+ "menu_kwargs": get_menu_kwargs(self.ops),
2018
+ **get_bg_fg(self.ops),
2019
+ "user_can_create_notes": self.ops.user_can_create_notes,
2020
+ "note_only": not self.ops.tooltips and isinstance(note, str),
2021
+ "width": self.ops.tooltip_width,
2022
+ "height": self.ops.tooltip_height,
2023
+ }
2024
+ )
2025
+ self.tooltip.set_position(self.tooltip_last_x - 4, self.tooltip_last_y - 4)
2026
+ self.tooltip_showing = True
2027
+
2028
+ def close_tooltip_save(self, event: tk.Event | None = None) -> None:
2029
+ widget = self.winfo_containing(self.winfo_pointerx(), self.winfo_pointery())
2030
+ if any(widget == tw for tw in self.tooltip_widgets):
2031
+ try:
2032
+ if self.tooltip.notebook.index("current") == 0:
2033
+ self.tooltip.content_text.focus_set()
2034
+ else:
2035
+ self.tooltip.note_text.focus_set()
2036
+ except Exception:
2037
+ self.tooltip.content_text.focus_set()
2038
+ return
2039
+ if not self.tooltip.cell_readonly:
2040
+ r, _, cell, note = self.tooltip.get()
2041
+ datarn = self.MT.datarn(r)
2042
+ event_data = self.new_single_edit_event(r, datarn, "??", self.get_cell_data(datarn), cell)
2043
+ self.do_single_edit(r, datarn, event_data, cell)
2044
+ if not self.tooltip.note_readonly:
2045
+ span = self.PAR.span(datarn).options(table=False, index=True)
2046
+ self.PAR.note(span, note=note if note else None, readonly=False)
2047
+ self.hide_tooltip()
2048
+ self.MT.refresh()
2049
+ self.focus_set()
2050
+
1890
2051
  def get_redraw_selections(self, startr: int, endr: int) -> dict[str, set[int]]:
1891
2052
  d = defaultdict(set)
1892
2053
  for _, box in self.MT.get_selection_items():
@@ -2100,6 +2261,18 @@ class RowIndex(tk.Canvas):
2100
2261
  self.itemconfig(self.text_editor.canvas_id, state="hidden")
2101
2262
  self.text_editor.open = False
2102
2263
 
2264
+ def do_single_edit(self, r: int, datarn: int, event_data: EventDataDict, val):
2265
+ edited = False
2266
+ set_data = partial(self.set_cell_data_undo, r=r, datarn=datarn, check_input_valid=False)
2267
+ if self.MT.edit_validation_func:
2268
+ val = self.MT.edit_validation_func(event_data)
2269
+ if val is not None and self.input_valid_for_cell(datarn, val):
2270
+ edited = set_data(value=val)
2271
+ elif self.input_valid_for_cell(datarn, val):
2272
+ edited = set_data(value=val)
2273
+ if edited:
2274
+ try_binding(self.extra_end_edit_cell_func, event_data)
2275
+
2103
2276
  # r is displayed row
2104
2277
  def close_text_editor(self, event: tk.Event) -> Literal["break"] | None:
2105
2278
  # checking if text editor should be closed or not
@@ -2122,33 +2295,8 @@ class RowIndex(tk.Canvas):
2122
2295
  text_editor_value = self.text_editor.get()
2123
2296
  r = self.text_editor.row
2124
2297
  datarn = r if self.MT.all_rows_displayed else self.MT.displayed_rows[r]
2125
- event_data = event_dict(
2126
- name="end_edit_index",
2127
- sheet=self.PAR.name,
2128
- widget=self,
2129
- cells_index={datarn: self.get_cell_data(datarn)},
2130
- key=event.keysym,
2131
- value=text_editor_value,
2132
- loc=r,
2133
- row=r,
2134
- boxes=self.MT.get_boxes(),
2135
- selected=self.MT.selected,
2136
- )
2137
- edited = False
2138
- set_data = partial(
2139
- self.set_cell_data_undo,
2140
- r=r,
2141
- datarn=datarn,
2142
- check_input_valid=False,
2143
- )
2144
- if self.MT.edit_validation_func:
2145
- text_editor_value = self.MT.edit_validation_func(event_data)
2146
- if text_editor_value is not None and self.input_valid_for_cell(datarn, text_editor_value):
2147
- edited = set_data(value=text_editor_value)
2148
- elif self.input_valid_for_cell(datarn, text_editor_value):
2149
- edited = set_data(value=text_editor_value)
2150
- if edited:
2151
- try_binding(self.extra_end_edit_cell_func, event_data)
2298
+ event_data = self.new_single_edit_event(r, datarn, event.keysym, self.get_cell_data(datarn), text_editor_value)
2299
+ self.do_single_edit(r, datarn, event_data, text_editor_value)
2152
2300
  self.MT.recreate_all_selection_boxes()
2153
2301
  self.hide_text_editor_and_dropdown()
2154
2302
  if event.keysym != "FocusOut":
@@ -2284,27 +2432,11 @@ class RowIndex(tk.Canvas):
2284
2432
  self.MT.main_table_redraw_grid_and_text(redraw_header=False, redraw_row_index=True, redraw_table=False)
2285
2433
 
2286
2434
  # r is displayed row
2287
- def close_dropdown_window(
2288
- self,
2289
- r: int | None = None,
2290
- selection: Any = None,
2291
- redraw: bool = True,
2292
- ) -> None:
2435
+ def close_dropdown_window(self, r: int | None = None, selection: Any = None, redraw: bool = True) -> None:
2293
2436
  if r is not None and selection is not None:
2294
2437
  datarn = r if self.MT.all_rows_displayed else self.MT.displayed_rows[r]
2295
2438
  kwargs = self.get_cell_kwargs(datarn, key="dropdown")
2296
- event_data = event_dict(
2297
- name="end_edit_index",
2298
- sheet=self.PAR.name,
2299
- widget=self,
2300
- cells_header={datarn: self.get_cell_data(datarn)},
2301
- key="??",
2302
- value=selection,
2303
- loc=r,
2304
- row=r,
2305
- boxes=self.MT.get_boxes(),
2306
- selected=self.MT.selected,
2307
- )
2439
+ event_data = self.new_single_edit_event(r, datarn, "??", self.get_cell_data(datarn), selection)
2308
2440
  try_binding(kwargs["select_function"], event_data)
2309
2441
  selection = selection if not self.MT.edit_validation_func else self.MT.edit_validation_func(event_data)
2310
2442
  if selection is not None:
@@ -2483,12 +2615,14 @@ class RowIndex(tk.Canvas):
2483
2615
  return Node(text=iid, iid=iid, parent=self.get_row_parent(datarn))
2484
2616
  if self.get_cell_kwargs(datarn, key="checkbox", cell=r_ops):
2485
2617
  return False
2486
- elif (
2487
- (kwargs := self.get_cell_kwargs(datarn, key="dropdown", cell=r_ops))
2488
- and kwargs["validate_input"]
2489
- and kwargs["values"]
2490
- ):
2491
- return kwargs["values"][0]
2618
+ elif (kwargs := self.get_cell_kwargs(datarn, key="dropdown", cell=r_ops)) and kwargs["validate_input"]:
2619
+ if kwargs["default_value"] is None:
2620
+ if kwargs["values"]:
2621
+ return safe_copy(kwargs["values"][0])
2622
+ else:
2623
+ return ""
2624
+ else:
2625
+ return safe_copy(kwargs["default_value"])
2492
2626
  else:
2493
2627
  return ""
2494
2628
 
@@ -2539,18 +2673,7 @@ class RowIndex(tk.Canvas):
2539
2673
  else:
2540
2674
  value = False
2541
2675
  self.set_cell_data_undo(r, datarn=datarn, value=value, cell_resize=False)
2542
- event_data = event_dict(
2543
- name="end_edit_index",
2544
- sheet=self.PAR.name,
2545
- widget=self,
2546
- cells_index={datarn: pre_edit_value},
2547
- key="??",
2548
- value=value,
2549
- loc=r,
2550
- row=r,
2551
- boxes=self.MT.get_boxes(),
2552
- selected=self.MT.selected,
2553
- )
2676
+ event_data = self.new_single_edit_event(r, datarn, "??", pre_edit_value, value)
2554
2677
  if kwargs["check_function"] is not None:
2555
2678
  kwargs["check_function"](event_data)
2556
2679
  try_binding(self.extra_end_edit_cell_func, event_data)
@@ -2563,6 +2686,21 @@ class RowIndex(tk.Canvas):
2563
2686
  else:
2564
2687
  return {}
2565
2688
 
2689
+ def new_single_edit_event(self, r: int, datarn: int, k: str, before_val: Any, after_val: Any) -> EventDataDict:
2690
+ return event_dict(
2691
+ name="end_edit_index",
2692
+ sheet=self.PAR.name,
2693
+ widget=self,
2694
+ cells_index={datarn: before_val},
2695
+ key=k,
2696
+ value=after_val,
2697
+ loc=r,
2698
+ row=r,
2699
+ boxes=self.MT.get_boxes(),
2700
+ selected=self.MT.selected,
2701
+ data={datarn: after_val},
2702
+ )
2703
+
2566
2704
  # Treeview Mode
2567
2705
 
2568
2706
  def tree_reset(self) -> None:
@@ -2711,14 +2849,14 @@ class RowIndex(tk.Canvas):
2711
2849
  # determine insert row
2712
2850
  insert_row = self.PAR._get_id_insert_row(move_to_index, new_parent)
2713
2851
  else:
2852
+ # remove any descendants
2714
2853
  iids = {self.MT._row_index[r].iid for r in event_data["moved"]["rows"]["data"]}
2715
- iids_descendants = {iid: set(self.get_iid_descendants(iid)) for iid in iids}
2716
-
2717
- # remove descendants in iids to move
2718
- iids -= set.union(*iids_descendants.values()) & iids
2719
- moved_rows = sorted(map(self.rns.__getitem__, iids))
2854
+ moved_rows = sorted(
2855
+ self.rns[iid]
2856
+ for iid in iids
2857
+ if not any(ancestor in iids for ancestor in self.get_iid_ancestors(iid))
2858
+ )
2720
2859
  item = self.MT._row_index[moved_rows[0]].iid
2721
-
2722
2860
  if isinstance(event_data.value, int):
2723
2861
  if event_data.value >= len(self.MT.displayed_rows):
2724
2862
  insert_row = len(self.MT._row_index)