tksheet 7.0.6__py3-none-any.whl → 7.1.1__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
@@ -4,6 +4,7 @@ import tkinter as tk
4
4
  from collections import defaultdict
5
5
  from collections.abc import (
6
6
  Callable,
7
+ Generator,
7
8
  )
8
9
  from functools import (
9
10
  partial,
@@ -27,7 +28,6 @@ from .formatters import (
27
28
  )
28
29
  from .functions import (
29
30
  consecutive_chunks,
30
- coords_tag_to_int_tuple,
31
31
  ev_stack_dict,
32
32
  event_dict,
33
33
  get_checkbox_points,
@@ -39,6 +39,9 @@ from .functions import (
39
39
  from .other_classes import (
40
40
  DotDict,
41
41
  DraggedRowColumn,
42
+ DropdownStorage,
43
+ Node,
44
+ TextEditorStorage,
42
45
  )
43
46
  from .text_editor import (
44
47
  TextEditor,
@@ -65,12 +68,7 @@ class RowIndex(tk.Canvas):
65
68
  self.popup_menu_loc = None
66
69
  self.extra_begin_edit_cell_func = None
67
70
  self.extra_end_edit_cell_func = None
68
- self.text_editor = None
69
- self.text_editor_id = None
70
- self.text_editor_loc = None
71
71
  self.b1_pressed_loc = None
72
- self.existing_dropdown_canvas_id = None
73
- self.existing_dropdown_window = None
74
72
  self.closed_dropdown = None
75
73
  self.centre_alignment_text_mod_indexes = (slice(1, None), slice(None, -1))
76
74
  self.c_align_cyc = cycle(self.centre_alignment_text_mod_indexes)
@@ -106,6 +104,8 @@ class RowIndex(tk.Canvas):
106
104
  self.currently_resizing_width = False
107
105
  self.currently_resizing_height = False
108
106
  self.ri_rc_popup_menu = None
107
+ self.dropdown = DropdownStorage()
108
+ self.text_editor = TextEditorStorage()
109
109
 
110
110
  self.disp_text = {}
111
111
  self.disp_high = {}
@@ -115,6 +115,7 @@ class RowIndex(tk.Canvas):
115
115
  self.disp_resize_lines = {}
116
116
  self.disp_dropdown = {}
117
117
  self.disp_checkbox = {}
118
+ self.disp_boxes = set()
118
119
  self.hidd_text = {}
119
120
  self.hidd_high = {}
120
121
  self.hidd_grid = {}
@@ -123,9 +124,12 @@ class RowIndex(tk.Canvas):
123
124
  self.hidd_resize_lines = {}
124
125
  self.hidd_dropdown = {}
125
126
  self.hidd_checkbox = {}
127
+ self.hidd_boxes = set()
126
128
 
127
129
  self.align = kwargs["row_index_align"]
128
130
  self.default_index = kwargs["default_row_index"].lower()
131
+
132
+ self.reset_tree()
129
133
  self.basic_bindings()
130
134
 
131
135
  def basic_bindings(self, enable: bool = True) -> None:
@@ -144,6 +148,18 @@ class RowIndex(tk.Canvas):
144
148
  self.unbind("<Double-Button-1>")
145
149
  self.unbind(rc_binding)
146
150
 
151
+ def reset_tree(self) -> None:
152
+ # treeview mode
153
+ self.tree = {}
154
+ self.tree_open_ids = set()
155
+ self.tree_rns = {}
156
+ if self.MT:
157
+ self.MT.displayed_rows = []
158
+ self.MT._row_index = []
159
+ self.MT.data = []
160
+ self.MT.row_positions = [0]
161
+ self.MT.saved_row_heights = {}
162
+
147
163
  def set_width(self, new_width: int, set_TL: bool = False) -> None:
148
164
  self.current_width = new_width
149
165
  try:
@@ -218,14 +234,17 @@ class RowIndex(tk.Canvas):
218
234
  if r < len(self.MT.row_positions) - 1:
219
235
  r_selected = self.MT.row_selected(r)
220
236
  if not r_selected and self.row_selection_enabled:
221
- currently_selected = self.MT.currently_selected()
222
- self.MT.delete_item(self.MT.currently_selected().tags[2])
223
- if currently_selected and currently_selected.type_ == "row":
224
- box = self.get_shift_select_box(r, currently_selected.row)
225
- self.being_drawn_item = self.MT.create_selection_box(*box, set_current=currently_selected)
237
+ if self.MT.selected and self.MT.selected.type_ == "rows":
238
+ self.being_drawn_item = self.MT.recreate_selection_box(
239
+ *self.get_shift_select_box(r, self.MT.selected.row),
240
+ fill_iid=self.MT.selected.fill_iid,
241
+ )
226
242
  else:
227
- self.being_drawn_item = self.add_selection(r, run_binding_func=False, set_as_current=True)
228
- box = self.MT.get_box_from_item(self.being_drawn_item, get_dict=True)
243
+ self.being_drawn_item = self.add_selection(
244
+ r,
245
+ run_binding_func=False,
246
+ set_as_current=True,
247
+ )
229
248
  self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
230
249
  if self.ctrl_selection_binding_func is not None:
231
250
  self.ctrl_selection_binding_func(
@@ -247,14 +266,13 @@ class RowIndex(tk.Canvas):
247
266
  if r < len(self.MT.row_positions) - 1:
248
267
  r_selected = self.MT.row_selected(r)
249
268
  if not r_selected and self.row_selection_enabled:
250
- currently_selected = self.MT.currently_selected()
251
- if currently_selected and currently_selected.type_ == "row":
269
+ if self.MT.selected and self.MT.selected.type_ == "rows":
270
+ r_to_sel, c_to_sel = self.MT.selected.row, self.MT.selected.column
252
271
  self.MT.deselect("all", redraw=False)
253
- box = self.get_shift_select_box(r, currently_selected.row)
254
- self.being_drawn_item = self.MT.create_selection_box(*box, set_current=currently_selected)
272
+ self.being_drawn_item = self.MT.create_selection_box(*self.get_shift_select_box(r, r_to_sel))
273
+ self.MT.set_currently_selected(r_to_sel, c_to_sel, self.being_drawn_item)
255
274
  else:
256
- self.being_drawn_item = self.add_selection(r, run_binding_func=False, set_as_current=True)
257
- box = self.MT.get_box_from_item(self.being_drawn_item, get_dict=True)
275
+ self.being_drawn_item = self.select_row(r, run_binding_func=False)
258
276
  self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
259
277
  if self.shift_selection_binding_func is not None:
260
278
  self.shift_selection_binding_func(
@@ -266,11 +284,11 @@ class RowIndex(tk.Canvas):
266
284
  to_move=sorted(self.MT.get_selected_rows()),
267
285
  )
268
286
 
269
- def get_shift_selection_box(self, r: int, min_r: int) -> tuple[int, int, int, int, str]:
287
+ def get_shift_select_box(self, r: int, min_r: int) -> tuple[int, int, int, int, str]:
270
288
  if r > min_r:
271
- return (min_r, 0, r + 1, len(self.MT.col_positions) - 1, "rows")
289
+ return min_r, 0, r + 1, len(self.MT.col_positions) - 1
272
290
  elif r < min_r:
273
- return (r, 0, min_r + 1, len(self.MT.col_positions) - 1, "rows")
291
+ return r, 0, min_r + 1, len(self.MT.col_positions) - 1
274
292
 
275
293
  def create_resize_line(
276
294
  self,
@@ -388,6 +406,8 @@ class RowIndex(tk.Canvas):
388
406
  or self.edit_cell_enabled
389
407
  ):
390
408
  self.open_cell(event)
409
+ elif (iid := self.event_over_tree_arrow(r, self.canvasy(event.y), event.x)) is not None:
410
+ self.PAR.item(iid, open_=iid not in self.tree_open_ids)
391
411
  self.rsz_h = None
392
412
  self.mouse_motion(event)
393
413
  if self.extra_double_b1_func is not None:
@@ -473,7 +493,7 @@ class RowIndex(tk.Canvas):
473
493
  y = self.canvasy(event.y)
474
494
  size = y - self.MT.row_positions[self.rsz_h - 1]
475
495
  if size >= self.MT.min_row_height and size < self.MT.max_row_height:
476
- self.delete_all_resize_and_ctrl_lines(ctrl_lines=False)
496
+ self.hide_resize_and_ctrl_lines(ctrl_lines=False)
477
497
  line2y = self.MT.row_positions[self.rsz_h - 1]
478
498
  self.create_resize_line(
479
499
  0,
@@ -505,7 +525,7 @@ class RowIndex(tk.Canvas):
505
525
  )
506
526
  elif self.width_resizing_enabled and self.rsz_w is not None and self.currently_resizing_width:
507
527
  evx = event.x
508
- self.delete_all_resize_and_ctrl_lines(ctrl_lines=False)
528
+ self.hide_resize_and_ctrl_lines(ctrl_lines=False)
509
529
  if evx > self.current_width:
510
530
  x = self.MT.canvasx(evx - self.current_width)
511
531
  if evx > self.MT.max_index_width:
@@ -540,20 +560,21 @@ class RowIndex(tk.Canvas):
540
560
  ):
541
561
  need_redraw = False
542
562
  end_row = self.MT.identify_row(y=event.y)
543
- currently_selected = self.MT.currently_selected()
544
- if end_row < len(self.MT.row_positions) - 1 and currently_selected:
545
- if currently_selected.type_ == "row":
546
- box = self.get_b1_motion_box(currently_selected.row, end_row)
563
+ if end_row < len(self.MT.row_positions) - 1 and self.MT.selected:
564
+ if self.MT.selected.type_ == "rows":
565
+ box = self.get_b1_motion_box(self.MT.selected.row, end_row)
547
566
  if (
548
567
  box is not None
549
568
  and self.being_drawn_item is not None
550
- and self.MT.get_box_from_item(self.being_drawn_item) != box
569
+ and self.MT.coords_and_type(self.being_drawn_item) != box
551
570
  ):
552
- self.MT.deselect("all", redraw=False)
553
571
  if box[2] - box[0] != 1:
554
- self.being_drawn_item = self.MT.create_selection_box(*box, set_current=currently_selected)
572
+ self.being_drawn_item = self.MT.recreate_selection_box(
573
+ *box[:-1],
574
+ fill_iid=self.MT.selected.fill_iid,
575
+ )
555
576
  else:
556
- self.being_drawn_item = self.select_row(currently_selected.row, run_binding_func=False)
577
+ self.being_drawn_item = self.select_row(self.MT.selected.row, run_binding_func=False)
557
578
  need_redraw = True
558
579
  if self.drag_selection_binding_func is not None:
559
580
  self.drag_selection_binding_func(
@@ -568,9 +589,9 @@ class RowIndex(tk.Canvas):
568
589
 
569
590
  def get_b1_motion_box(self, start_row, end_row):
570
591
  if end_row >= start_row:
571
- return (start_row, 0, end_row + 1, len(self.MT.col_positions) - 1, "rows")
592
+ return start_row, 0, end_row + 1, len(self.MT.col_positions) - 1, "rows"
572
593
  elif end_row < start_row:
573
- return (end_row, 0, start_row + 1, len(self.MT.col_positions) - 1, "rows")
594
+ return end_row, 0, start_row + 1, len(self.MT.col_positions) - 1, "rows"
574
595
 
575
596
  def ctrl_b1_motion(self, event: object):
576
597
  x1, y1, x2, y2 = self.MT.get_canvas_visible_area()
@@ -599,20 +620,22 @@ class RowIndex(tk.Canvas):
599
620
  ):
600
621
  need_redraw = False
601
622
  end_row = self.MT.identify_row(y=event.y)
602
- currently_selected = self.MT.currently_selected()
603
- if end_row < len(self.MT.row_positions) - 1 and currently_selected:
604
- if currently_selected.type_ == "row":
605
- box = self.get_b1_motion_box(currently_selected.row, end_row)
623
+ if end_row < len(self.MT.row_positions) - 1 and self.MT.selected:
624
+ if self.MT.selected.type_ == "rows":
625
+ box = self.get_b1_motion_box(self.MT.selected.row, end_row)
606
626
  if (
607
627
  box is not None
608
628
  and self.being_drawn_item is not None
609
- and self.MT.get_box_from_item(self.being_drawn_item) != box
629
+ and self.MT.coords_and_type(self.being_drawn_item) != box
610
630
  ):
611
- self.MT.delete_item(self.being_drawn_item)
612
631
  if box[2] - box[0] != 1:
613
- self.being_drawn_item = self.MT.create_selection_box(*box, set_current=currently_selected)
632
+ self.being_drawn_item = self.MT.recreate_selection_box(
633
+ *box[:-1],
634
+ self.MT.selected.fill_iid,
635
+ )
614
636
  else:
615
- self.being_drawn_item = self.add_selection(currently_selected.row, run_binding_func=False)
637
+ self.MT.hide_selection_box(self.MT.selected.fill_iid)
638
+ self.being_drawn_item = self.add_selection(box[0], run_binding_func=False)
616
639
  need_redraw = True
617
640
  if self.drag_selection_binding_func is not None:
618
641
  self.drag_selection_binding_func(
@@ -667,7 +690,7 @@ class RowIndex(tk.Canvas):
667
690
  x2,
668
691
  rows,
669
692
  ):
670
- self.delete_all_resize_and_ctrl_lines()
693
+ self.hide_resize_and_ctrl_lines()
671
694
  self.create_resize_line(
672
695
  0,
673
696
  ypos,
@@ -687,7 +710,7 @@ class RowIndex(tk.Canvas):
687
710
  delete_on_timer=False,
688
711
  )
689
712
 
690
- def delete_all_resize_and_ctrl_lines(self, ctrl_lines=True):
713
+ def hide_resize_and_ctrl_lines(self, ctrl_lines=True):
691
714
  self.delete_resize_lines()
692
715
  self.MT.delete_resize_lines()
693
716
  if ctrl_lines:
@@ -723,7 +746,7 @@ class RowIndex(tk.Canvas):
723
746
  if len(ycheck) > 1 and ycheck[1] > 1:
724
747
  self.MT.set_yviews("moveto", 1)
725
748
 
726
- def event_over_dropdown(self, r, datarn, event: object, canvasy):
749
+ def event_over_dropdown(self, r: int, datarn: int, event: object, canvasy: float) -> bool:
727
750
  if (
728
751
  canvasy < self.MT.row_positions[r] + self.MT.index_txt_height
729
752
  and self.get_cell_kwargs(datarn, key="dropdown")
@@ -732,7 +755,7 @@ class RowIndex(tk.Canvas):
732
755
  return True
733
756
  return False
734
757
 
735
- def event_over_checkbox(self, r, datarn, event: object, canvasy):
758
+ def event_over_checkbox(self, r: int, datarn: int, event: object, canvasy: float) -> bool:
736
759
  if (
737
760
  canvasy < self.MT.row_positions[r] + self.MT.index_txt_height
738
761
  and self.get_cell_kwargs(datarn, key="checkbox")
@@ -741,13 +764,17 @@ class RowIndex(tk.Canvas):
741
764
  return True
742
765
  return False
743
766
 
744
- def b1_release(self, event: object):
767
+ def b1_release(self, event: object) -> None:
745
768
  if self.being_drawn_item is not None:
746
- currently_selected = self.MT.currently_selected()
747
- to_sel = self.MT.get_box_from_item(self.being_drawn_item)
748
- self.MT.delete_item(self.being_drawn_item)
769
+ to_sel = self.MT.coords_and_type(self.being_drawn_item)
770
+ r_to_sel, c_to_sel = self.MT.selected.row, self.MT.selected.column
771
+ self.MT.hide_selection_box(self.being_drawn_item)
749
772
  self.being_drawn_item = None
750
- self.MT.create_selection_box(*to_sel, set_current=currently_selected)
773
+ self.MT.set_currently_selected(
774
+ r_to_sel,
775
+ c_to_sel,
776
+ item=self.MT.create_selection_box(*to_sel, set_current=False),
777
+ )
751
778
  if self.drag_selection_binding_func is not None:
752
779
  self.drag_selection_binding_func(
753
780
  self.MT.get_select_event(being_drawn_item=self.being_drawn_item),
@@ -756,7 +783,7 @@ class RowIndex(tk.Canvas):
756
783
  if self.height_resizing_enabled and self.rsz_h is not None and self.currently_resizing_height:
757
784
  self.currently_resizing_height = False
758
785
  new_row_pos = int(self.coords("rhl")[1])
759
- self.delete_all_resize_and_ctrl_lines(ctrl_lines=False)
786
+ self.hide_resize_and_ctrl_lines(ctrl_lines=False)
760
787
  old_height = self.MT.row_positions[self.rsz_h] - self.MT.row_positions[self.rsz_h - 1]
761
788
  size = new_row_pos - self.MT.row_positions[self.rsz_h - 1]
762
789
  if size < self.MT.min_row_height:
@@ -782,7 +809,7 @@ class RowIndex(tk.Canvas):
782
809
  )
783
810
  elif self.width_resizing_enabled and self.rsz_w is not None and self.currently_resizing_width:
784
811
  self.currently_resizing_width = False
785
- self.delete_all_resize_and_ctrl_lines(ctrl_lines=False)
812
+ self.hide_resize_and_ctrl_lines(ctrl_lines=False)
786
813
  self.set_width(self.new_row_width, set_TL=True)
787
814
  self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
788
815
  if (
@@ -794,7 +821,7 @@ class RowIndex(tk.Canvas):
794
821
  and self.dragged_row is not None
795
822
  and self.find_withtag("move_rows")
796
823
  ):
797
- self.delete_all_resize_and_ctrl_lines()
824
+ self.hide_resize_and_ctrl_lines()
798
825
  r = self.MT.identify_row(y=event.y)
799
826
  totalrows = len(self.dragged_row.to_move)
800
827
  if (
@@ -812,7 +839,7 @@ class RowIndex(tk.Canvas):
812
839
  name="move_rows",
813
840
  sheet=self.PAR.name,
814
841
  boxes=self.MT.get_boxes(),
815
- selected=self.MT.currently_selected(),
842
+ selected=self.MT.selected,
816
843
  value=r,
817
844
  )
818
845
  if try_binding(self.ri_extra_begin_drag_drop_func, event_data, "begin_move_rows"):
@@ -847,6 +874,8 @@ class RowIndex(tk.Canvas):
847
874
  r, datarn, event, canvasy
848
875
  ):
849
876
  self.open_cell(event)
877
+ elif (iid := self.event_over_tree_arrow(r, canvasy, event.x)) is not None:
878
+ self.PAR.item(iid, open_=iid not in self.tree_open_ids)
850
879
  else:
851
880
  self.mouseclick_outside_editor_or_dropdown_all_canvases(inside=True)
852
881
  self.b1_pressed_loc = None
@@ -860,14 +889,30 @@ class RowIndex(tk.Canvas):
860
889
  if self.extra_b1_release_func is not None:
861
890
  self.extra_b1_release_func(event)
862
891
 
892
+ def event_over_tree_arrow(
893
+ self,
894
+ r: int,
895
+ canvasy: float,
896
+ eventx: int,
897
+ ) -> bool:
898
+ if self.PAR.ops.treeview and (
899
+ canvasy < self.MT.row_positions[r] + self.MT.index_txt_height + 3
900
+ and isinstance(self.MT._row_index, list)
901
+ and (datarn := self.MT.datarn(r)) < len(self.MT._row_index)
902
+ and eventx
903
+ < self.get_treeview_indent((iid := self.MT._row_index[datarn].iid)) + self.MT.index_txt_height + 1
904
+ ):
905
+ return iid
906
+ return None
907
+
863
908
  def toggle_select_row(
864
909
  self,
865
- row,
866
- add_selection=True,
867
- redraw=True,
868
- run_binding_func=True,
869
- set_as_current=True,
870
- ):
910
+ row: int,
911
+ add_selection: bool = True,
912
+ redraw: bool = True,
913
+ run_binding_func: bool = True,
914
+ set_as_current: bool = True,
915
+ ) -> int | None:
871
916
  if add_selection:
872
917
  if self.MT.row_selected(row):
873
918
  fill_iid = self.MT.deselect(r=row, redraw=redraw)
@@ -885,7 +930,7 @@ class RowIndex(tk.Canvas):
885
930
  fill_iid = self.select_row(row, redraw=redraw)
886
931
  return fill_iid
887
932
 
888
- def select_row(self, r, redraw=False, run_binding_func=True):
933
+ def select_row(self, r: int, redraw: bool = False, run_binding_func: bool = True) -> int:
889
934
  self.MT.deselect("all", redraw=False)
890
935
  box = (r, 0, r + 1, len(self.MT.col_positions) - 1, "rows")
891
936
  fill_iid = self.MT.create_selection_box(*box)
@@ -895,7 +940,13 @@ class RowIndex(tk.Canvas):
895
940
  self.MT.run_selection_binding("rows")
896
941
  return fill_iid
897
942
 
898
- def add_selection(self, r, redraw=False, run_binding_func=True, set_as_current=True):
943
+ def add_selection(
944
+ self,
945
+ r: int,
946
+ redraw: bool = False,
947
+ run_binding_func: bool = True,
948
+ set_as_current: bool = True,
949
+ ) -> int:
899
950
  box = (r, 0, r + 1, len(self.MT.col_positions) - 1, "rows")
900
951
  fill_iid = self.MT.create_selection_box(*box, set_current=set_as_current)
901
952
  if redraw:
@@ -904,7 +955,33 @@ class RowIndex(tk.Canvas):
904
955
  self.MT.run_selection_binding("rows")
905
956
  return fill_iid
906
957
 
907
- def get_cell_dimensions(self, datarn):
958
+ def display_box(
959
+ self,
960
+ x1: int,
961
+ y1: int,
962
+ x2: int,
963
+ y2: int,
964
+ fill: str,
965
+ outline: str,
966
+ state: str,
967
+ tags: str | tuple[str],
968
+ ) -> int:
969
+ if self.hidd_boxes:
970
+ iid = self.hidd_boxes.pop()
971
+ self.coords(iid, x1, y1, x2, y2)
972
+ self.itemconfig(iid, fill=fill, outline=outline, state=state, tags=tags)
973
+ else:
974
+ iid = self.create_rectangle(x1, y1, x2, y2, fill=fill, outline=outline, state=state, tags=tags)
975
+ self.disp_boxes.add(iid)
976
+ return iid
977
+
978
+ def hide_box(self, item: int | None) -> None:
979
+ if isinstance(item, int):
980
+ self.disp_boxes.discard(item)
981
+ self.hidd_boxes.add(item)
982
+ self.itemconfig(item, state="hidden")
983
+
984
+ def get_cell_dimensions(self, datarn: int) -> tuple[int, int]:
908
985
  txt = self.get_valid_cell_data_as_str(datarn, fix=False)
909
986
  if txt:
910
987
  self.MT.txt_measure_canvas.itemconfig(
@@ -917,18 +994,26 @@ class RowIndex(tk.Canvas):
917
994
  w = self.PAR.ops.default_row_index_width
918
995
  h = self.MT.min_row_height
919
996
  if self.get_cell_kwargs(datarn, key="dropdown") or self.get_cell_kwargs(datarn, key="checkbox"):
920
- return w + self.MT.index_txt_height, h
997
+ w += self.MT.index_txt_height
998
+ if self.PAR.ops.treeview:
999
+ if datarn in self.cell_options and "align" in self.cell_options[datarn]:
1000
+ align = self.cell_options[datarn]["align"]
1001
+ else:
1002
+ align = self.align
1003
+ if align == "w":
1004
+ w += self.MT.index_txt_height
1005
+ w += self.get_treeview_indent(self.MT._row_index[datarn].iid)
921
1006
  return w, h
922
1007
 
923
1008
  def set_row_height(
924
1009
  self,
925
- row,
926
- height=None,
927
- only_set_if_too_small=False,
928
- recreate=True,
929
- return_new_height=False,
930
- displayed_only=False,
931
- ):
1010
+ row: int,
1011
+ height: None | int = None,
1012
+ only_set_if_too_small: bool = False,
1013
+ recreate: bool = True,
1014
+ return_new_height: bool = False,
1015
+ displayed_only: bool = False,
1016
+ ) -> int:
932
1017
  r_norm = row + 1
933
1018
  r_extra = row + 2
934
1019
  min_rh = self.MT.min_row_height
@@ -991,7 +1076,12 @@ class RowIndex(tk.Canvas):
991
1076
  self.MT.recreate_all_selection_boxes()
992
1077
  return new_height
993
1078
 
994
- def set_width_of_index_to_text(self, text=None):
1079
+ def set_width_of_index_to_text(
1080
+ self,
1081
+ text: None | str = None,
1082
+ only_rows: list = [],
1083
+ set_width: bool = True,
1084
+ ) -> None | int:
995
1085
  if (
996
1086
  text is None
997
1087
  and not self.MT._row_index
@@ -1015,7 +1105,9 @@ class RowIndex(tk.Canvas):
1015
1105
  else:
1016
1106
  w = self.PAR.ops.default_row_index_width
1017
1107
  else:
1018
- if self.MT.all_rows_displayed:
1108
+ if only_rows:
1109
+ iterable = only_rows
1110
+ elif self.MT.all_rows_displayed:
1019
1111
  if isinstance(self.MT._row_index, list):
1020
1112
  iterable = range(len(self.MT._row_index))
1021
1113
  else:
@@ -1029,10 +1121,6 @@ class RowIndex(tk.Canvas):
1029
1121
  w = int(self.MT.min_column_width)
1030
1122
  elif w > self.MT.max_index_width:
1031
1123
  w = int(self.MT.max_index_width)
1032
- if self.get_cell_kwargs(datarn, key="checkbox"):
1033
- w += self.MT.index_txt_height + 6
1034
- elif self.get_cell_kwargs(datarn, key="dropdown"):
1035
- w += self.MT.index_txt_height + 4
1036
1124
  if w > new_width:
1037
1125
  new_width = w
1038
1126
  elif isinstance(self.MT._row_index, int):
@@ -1053,8 +1141,10 @@ class RowIndex(tk.Canvas):
1053
1141
  new_width = w
1054
1142
  if new_width == self.MT.min_column_width:
1055
1143
  new_width = self.MT.min_column_width + 10
1056
- self.set_width(new_width, set_TL=True)
1057
- self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
1144
+ if set_width:
1145
+ self.set_width(new_width, set_TL=True)
1146
+ self.MT.main_table_redraw_grid_and_text(redraw_header=True, redraw_row_index=True)
1147
+ return new_width
1058
1148
 
1059
1149
  def set_height_of_all_rows(self, height=None, only_set_if_too_small=False, recreate=True):
1060
1150
  if height is None:
@@ -1074,23 +1164,24 @@ class RowIndex(tk.Canvas):
1074
1164
  if recreate:
1075
1165
  self.MT.recreate_all_selection_boxes()
1076
1166
 
1077
- def auto_set_index_width(self, end_row):
1078
- if not self.MT._row_index and not isinstance(self.MT._row_index, int) and self.PAR.ops.auto_resize_row_index:
1079
- if self.default_index == "letters":
1080
- new_w = self.MT.get_txt_w(f"{num2alpha(end_row)}") + 20
1081
- if self.current_width - new_w > 15 or new_w - self.current_width > 5:
1082
- self.set_width(new_w, set_TL=True)
1083
- return True
1084
- elif self.default_index == "numbers":
1085
- new_w = self.MT.get_txt_w(f"{end_row}") + 20
1086
- if self.current_width - new_w > 15 or new_w - self.current_width > 5:
1087
- self.set_width(new_w, set_TL=True)
1088
- return True
1089
- elif self.default_index == "both":
1090
- new_w = self.MT.get_txt_w(f"{end_row + 1} {num2alpha(end_row)}") + 20
1091
- if self.current_width - new_w > 15 or new_w - self.current_width > 5:
1092
- self.set_width(new_w, set_TL=True)
1093
- return True
1167
+ def auto_set_index_width(self, end_row: int, only_rows: list) -> bool:
1168
+ if self.PAR.ops.auto_resize_row_index:
1169
+ if not isinstance(self.MT._row_index, int) and not self.MT._row_index:
1170
+ if self.default_index == "letters":
1171
+ new_w = self.MT.get_txt_w(f"{num2alpha(end_row)}") + 20
1172
+ elif self.default_index == "numbers":
1173
+ new_w = self.MT.get_txt_w(f"{end_row}") + 20
1174
+ elif self.default_index == "both":
1175
+ new_w = self.MT.get_txt_w(f"{end_row + 1} {num2alpha(end_row)}") + 20
1176
+ elif self.PAR.ops.auto_resize_row_index is True:
1177
+ new_w = self.set_width_of_index_to_text(only_rows=only_rows, set_width=False)
1178
+ else:
1179
+ new_w = None
1180
+ if new_w is not None and (sheet_w := floor(self.PAR.winfo_width() * 0.5)) < new_w:
1181
+ new_w = sheet_w
1182
+ if new_w and (self.current_width - new_w > 15 or new_w - self.current_width > 5):
1183
+ self.set_width(new_w, set_TL=True)
1184
+ return True
1094
1185
  return False
1095
1186
 
1096
1187
  def redraw_highlight_get_text_fg(self, fr, sr, r, c_2, c_3, selections, datarn):
@@ -1185,34 +1276,59 @@ class RowIndex(tk.Canvas):
1185
1276
  fill,
1186
1277
  outline,
1187
1278
  tag,
1188
- draw_outline=True,
1189
- draw_arrow=True,
1190
- dd_is_open=False,
1191
- ):
1279
+ draw_outline: bool = True,
1280
+ draw_arrow: bool = True,
1281
+ open_: bool = False,
1282
+ indent: None | float = None,
1283
+ ) -> None:
1192
1284
  if draw_outline and self.PAR.ops.show_dropdown_borders:
1193
1285
  self.redraw_highlight(x1 + 1, y1 + 1, x2, y2, fill="", outline=self.PAR.ops.index_fg, tag=tag)
1194
1286
  if draw_arrow:
1195
- topysub = floor(self.MT.index_half_txt_height / 2)
1196
- mid_y = y1 + floor(self.MT.min_row_height / 2)
1197
- if mid_y + topysub + 1 >= y1 + self.MT.index_txt_height - 1:
1198
- mid_y -= 1
1199
- if mid_y - topysub + 2 <= y1 + 4 + topysub:
1200
- mid_y -= 1
1201
- ty1 = mid_y + topysub + 1 if dd_is_open else mid_y - topysub + 3
1202
- ty2 = mid_y - topysub + 3 if dd_is_open else mid_y + topysub + 1
1203
- ty3 = mid_y + topysub + 1 if dd_is_open else mid_y - topysub + 3
1287
+ mod = (self.MT.index_txt_height - 1) if self.MT.index_txt_height % 2 else self.MT.index_txt_height
1288
+ half_mod = mod / 2
1289
+ qtr_mod = mod / 4
1290
+ mid_y = (self.MT.index_first_ln_ins - 1) if self.MT.index_first_ln_ins % 2 else self.MT.index_first_ln_ins
1291
+ # treeview
1292
+ if indent is not None:
1293
+ # up arrow
1294
+ if open_:
1295
+ points = (
1296
+ x1 + 2 + indent,
1297
+ y1 + mid_y + qtr_mod,
1298
+ x1 + 2 + half_mod + indent,
1299
+ y1 + mid_y - qtr_mod,
1300
+ x1 + 2 + mod + indent,
1301
+ y1 + mid_y + qtr_mod,
1302
+ )
1303
+ # right pointing arrow
1304
+ else:
1305
+ points = (
1306
+ x1 + half_mod + indent,
1307
+ y1 + mid_y - half_mod + 1,
1308
+ x1 + mod + indent - 1,
1309
+ y1 + mid_y,
1310
+ x1 + half_mod + indent,
1311
+ y1 + mid_y + half_mod - 1,
1312
+ )
1204
1313
  else:
1205
- ty1 = mid_y + topysub + 1 if dd_is_open else mid_y - topysub + 2
1206
- ty2 = mid_y - topysub + 2 if dd_is_open else mid_y + topysub + 1
1207
- ty3 = mid_y + topysub + 1 if dd_is_open else mid_y - topysub + 2
1208
- tx1 = x2 - self.MT.index_txt_height + 1
1209
- tx2 = x2 - self.MT.index_half_txt_height - 1
1210
- tx3 = x2 - 3
1211
- if tx2 - tx1 > tx3 - tx2:
1212
- tx1 += (tx2 - tx1) - (tx3 - tx2)
1213
- elif tx2 - tx1 < tx3 - tx2:
1214
- tx1 -= (tx3 - tx2) - (tx2 - tx1)
1215
- points = (tx1, ty1, tx2, ty2, tx3, ty3)
1314
+ if open_:
1315
+ points = (
1316
+ x2 - 3 - mod,
1317
+ y1 + mid_y + qtr_mod,
1318
+ x2 - 3 - half_mod,
1319
+ y1 + mid_y - qtr_mod,
1320
+ x2 - 3,
1321
+ y1 + mid_y + qtr_mod,
1322
+ )
1323
+ else:
1324
+ points = (
1325
+ x2 - 3 - mod,
1326
+ y1 + mid_y - qtr_mod,
1327
+ x2 - 3 - half_mod,
1328
+ y1 + mid_y + qtr_mod,
1329
+ x2 - 3,
1330
+ y1 + mid_y - qtr_mod,
1331
+ )
1216
1332
  if self.hidd_dropdown:
1217
1333
  t, sh = self.hidd_dropdown.popitem()
1218
1334
  self.coords(t, points)
@@ -1266,14 +1382,14 @@ class RowIndex(tk.Canvas):
1266
1382
 
1267
1383
  def redraw_grid_and_text(
1268
1384
  self,
1269
- last_row_line_pos,
1270
- scrollpos_top,
1271
- y_stop,
1272
- start_row,
1273
- end_row,
1274
- scrollpos_bot,
1275
- row_pos_exists,
1276
- ):
1385
+ last_row_line_pos: int,
1386
+ scrollpos_top: int,
1387
+ y_stop: int,
1388
+ start_row: int,
1389
+ end_row: int,
1390
+ scrollpos_bot: int,
1391
+ row_pos_exists: bool,
1392
+ ) -> None:
1277
1393
  try:
1278
1394
  self.configure(
1279
1395
  scrollregion=(
@@ -1342,7 +1458,9 @@ class RowIndex(tk.Canvas):
1342
1458
  )
1343
1459
  font = self.PAR.ops.index_font
1344
1460
  selections = self.get_redraw_selections(start_row, end_row)
1345
- dd_coords = self.get_existing_dropdown_coords()
1461
+ dd_coords = self.dropdown.get_coords()
1462
+ treeview = self.PAR.ops.treeview
1463
+
1346
1464
  for r in range(start_row, end_row - 1):
1347
1465
  rtopgridln = self.MT.row_positions[r]
1348
1466
  rbotgridln = self.MT.row_positions[r + 1]
@@ -1370,7 +1488,7 @@ class RowIndex(tk.Canvas):
1370
1488
  tag="dd",
1371
1489
  draw_outline=not dd_drawn,
1372
1490
  draw_arrow=mw >= 5,
1373
- dd_is_open=dd_coords == r,
1491
+ open_=dd_coords == r,
1374
1492
  )
1375
1493
  else:
1376
1494
  mw = self.current_width - 2
@@ -1389,7 +1507,7 @@ class RowIndex(tk.Canvas):
1389
1507
  tag="dd",
1390
1508
  draw_outline=not dd_drawn,
1391
1509
  draw_arrow=mw >= 5,
1392
- dd_is_open=dd_coords == r,
1510
+ open_=dd_coords == r,
1393
1511
  )
1394
1512
  else:
1395
1513
  mw = self.current_width - 2
@@ -1409,7 +1527,7 @@ class RowIndex(tk.Canvas):
1409
1527
  tag="dd",
1410
1528
  draw_outline=not dd_drawn,
1411
1529
  draw_arrow=mw >= 5,
1412
- dd_is_open=dd_coords == r,
1530
+ open_=dd_coords == r,
1413
1531
  )
1414
1532
  else:
1415
1533
  mw = self.current_width - 1
@@ -1443,6 +1561,27 @@ class RowIndex(tk.Canvas):
1443
1561
  tag="cb",
1444
1562
  draw_check=draw_check,
1445
1563
  )
1564
+ if treeview and isinstance(self.MT._row_index, list) and len(self.MT._row_index) > datarn:
1565
+ iid = self.MT._row_index[datarn].iid
1566
+ mw -= self.MT.index_txt_height
1567
+ if align == "w":
1568
+ draw_x += self.MT.index_txt_height + 1
1569
+ indent = self.get_treeview_indent(iid)
1570
+ draw_x += indent
1571
+ if self.tree[iid].children:
1572
+ self.redraw_dropdown(
1573
+ 0,
1574
+ rtopgridln,
1575
+ self.current_width - 1,
1576
+ rbotgridln - 1,
1577
+ fill=fill,
1578
+ outline=fill,
1579
+ tag="dd",
1580
+ draw_outline=False,
1581
+ draw_arrow=True,
1582
+ open_=self.MT._row_index[datarn].iid in self.tree_open_ids,
1583
+ indent=indent,
1584
+ )
1446
1585
  lns = self.get_valid_cell_data_as_str(datarn, fix=False)
1447
1586
  if not lns:
1448
1587
  continue
@@ -1530,10 +1669,8 @@ class RowIndex(tk.Canvas):
1530
1669
 
1531
1670
  def get_redraw_selections(self, startr, endr):
1532
1671
  d = defaultdict(list)
1533
- for item in chain(self.find_withtag("cells"), self.find_withtag("rows")):
1534
- tags = self.gettags(item)
1535
- if tags:
1536
- d[tags[0]].append(coords_tag_to_int_tuple(tags[1]))
1672
+ for item, box in self.MT.get_selection_items(columns=False):
1673
+ d[box.type_].append(box.coords)
1537
1674
  d2 = {}
1538
1675
  if "cells" in d:
1539
1676
  d2["cells"] = {r for r in range(startr, endr) for r1, c1, r2, c2 in d["cells"] if r1 <= r and r2 > r}
@@ -1542,12 +1679,11 @@ class RowIndex(tk.Canvas):
1542
1679
  return d2
1543
1680
 
1544
1681
  def open_cell(self, event: object = None, ignore_existing_editor=False):
1545
- if not self.MT.anything_selected() or (not ignore_existing_editor and self.text_editor_id is not None):
1682
+ if not self.MT.anything_selected() or (not ignore_existing_editor and self.text_editor.open):
1546
1683
  return
1547
- currently_selected = self.MT.currently_selected()
1548
- if not currently_selected:
1684
+ if not self.MT.selected:
1549
1685
  return
1550
- r = int(currently_selected[0])
1686
+ r = self.MT.selected.row
1551
1687
  datarn = r if self.MT.all_rows_displayed else self.MT.displayed_rows[r]
1552
1688
  if self.get_cell_kwargs(datarn, key="readonly"):
1553
1689
  return
@@ -1602,7 +1738,6 @@ class RowIndex(tk.Canvas):
1602
1738
  text = event.char
1603
1739
  else:
1604
1740
  return False
1605
- self.text_editor_loc = r
1606
1741
  if self.extra_begin_edit_cell_func is not None:
1607
1742
  try:
1608
1743
  text = self.extra_begin_edit_cell_func(
@@ -1613,7 +1748,7 @@ class RowIndex(tk.Canvas):
1613
1748
  value=text,
1614
1749
  loc=r,
1615
1750
  boxes=self.MT.get_boxes(),
1616
- selected=self.MT.currently_selected(),
1751
+ selected=self.MT.selected,
1617
1752
  )
1618
1753
  )
1619
1754
  except Exception:
@@ -1625,14 +1760,13 @@ class RowIndex(tk.Canvas):
1625
1760
  text = "" if text is None else text
1626
1761
  if self.PAR.ops.cell_auto_resize_enabled:
1627
1762
  self.set_row_height_run_binding(r)
1628
- if r == self.text_editor_loc and self.text_editor is not None:
1763
+ if self.text_editor.open and r == self.text_editor.row:
1629
1764
  self.text_editor.set_text(self.text_editor.get() + "" if not isinstance(text, str) else text)
1630
1765
  return
1631
- if self.text_editor is not None:
1632
- self.destroy_text_editor()
1766
+ if self.text_editor.open:
1767
+ self.hide_text_editor()
1633
1768
  if not self.MT.see(r=r, c=0, keep_yscroll=True, check_cell_visibility=True):
1634
1769
  self.MT.refresh()
1635
- self.text_editor_loc = r
1636
1770
  x = 0
1637
1771
  y = self.MT.row_positions[r] + 1
1638
1772
  w = self.current_width + 1
@@ -1641,54 +1775,60 @@ class RowIndex(tk.Canvas):
1641
1775
  if text is None:
1642
1776
  text = self.get_cell_data(datarn, none_to_empty_str=True, redirect_int=True)
1643
1777
  bg, fg = self.PAR.ops.index_bg, self.PAR.ops.index_fg
1644
- self.text_editor = TextEditor(
1645
- self,
1646
- menu_kwargs=DotDict(
1778
+ kwargs = {
1779
+ "menu_kwargs": DotDict(
1647
1780
  {
1648
- "font": self.PAR.ops.index_font,
1781
+ "font": self.PAR.ops.table_font,
1649
1782
  "foreground": self.PAR.ops.popup_menu_fg,
1650
1783
  "background": self.PAR.ops.popup_menu_bg,
1651
1784
  "activebackground": self.PAR.ops.popup_menu_highlight_bg,
1652
1785
  "activeforeground": self.PAR.ops.popup_menu_highlight_fg,
1653
1786
  }
1654
1787
  ),
1655
- sheet_ops=self.PAR.ops,
1656
- border_color=self.PAR.ops.table_selected_rows_border_fg,
1657
- text=text,
1658
- state=state,
1659
- width=w,
1660
- height=h,
1661
- show_border=False,
1662
- bg=bg,
1663
- fg=fg,
1664
- align=self.get_cell_align(r),
1665
- r=r,
1666
- newline_binding=self.text_editor_newline_binding,
1667
- )
1668
- self.text_editor.update_idletasks()
1669
- self.text_editor_id = self.create_window((x, y), window=self.text_editor, anchor="nw")
1788
+ "sheet_ops": self.PAR.ops,
1789
+ "border_color": self.PAR.ops.table_selected_cells_border_fg,
1790
+ "text": text,
1791
+ "state": state,
1792
+ "width": w,
1793
+ "height": h,
1794
+ "show_border": self.PAR.ops.show_selected_cells_border,
1795
+ "bg": bg,
1796
+ "fg": fg,
1797
+ "align": self.get_cell_align(r),
1798
+ "r": r,
1799
+ }
1800
+ if not self.text_editor.window:
1801
+ self.text_editor.window = TextEditor(self, newline_binding=self.text_editor_newline_binding)
1802
+ self.text_editor.canvas_id = self.create_window((x, y), window=self.text_editor.window, anchor="nw")
1803
+ self.text_editor.window.reset(**kwargs)
1804
+ if not self.text_editor.open:
1805
+ self.itemconfig(self.text_editor.canvas_id, state="normal")
1806
+ self.text_editor.open = True
1807
+ self.coords(self.text_editor.canvas_id, x, y)
1670
1808
  if not dropdown:
1671
- self.text_editor.textedit.focus_set()
1672
- self.text_editor.scroll_to_bottom()
1673
- self.text_editor.textedit.bind("<Alt-Return>", lambda _x: self.text_editor_newline_binding(r=r))
1809
+ self.text_editor.tktext.focus_set()
1810
+ self.text_editor.window.scroll_to_bottom()
1811
+ self.text_editor.tktext.bind("<Alt-Return>", lambda _x: self.text_editor_newline_binding(r=r))
1674
1812
  if USER_OS == "darwin":
1675
- self.text_editor.textedit.bind("<Option-Return>", lambda _x: self.text_editor_newline_binding(r=r))
1813
+ self.text_editor.tktext.bind("<Option-Return>", lambda _x: self.text_editor_newline_binding(r=r))
1676
1814
  for key, func in self.MT.text_editor_user_bound_keys.items():
1677
- self.text_editor.textedit.bind(key, func)
1678
- self.text_editor.textedit.bind("<Tab>", lambda _x: self.close_text_editor((r, "Tab")))
1679
- self.text_editor.textedit.bind("<Return>", lambda _x: self.close_text_editor((r, "Return")))
1815
+ self.text_editor.tktext.bind(key, func)
1816
+ self.text_editor.tktext.bind("<Tab>", lambda _x: self.close_text_editor((r, "Tab")))
1817
+ self.text_editor.tktext.bind("<Return>", lambda _x: self.close_text_editor((r, "Return")))
1680
1818
  if not dropdown:
1681
- self.text_editor.textedit.bind("<FocusOut>", lambda _x: self.close_text_editor((r, "FocusOut")))
1682
- self.text_editor.textedit.bind("<Escape>", lambda _x: self.close_text_editor((r, "Escape")))
1819
+ self.text_editor.tktext.bind("<FocusOut>", lambda _x: self.close_text_editor((r, "FocusOut")))
1820
+ self.text_editor.tktext.bind("<Escape>", lambda _x: self.close_text_editor((r, "Escape")))
1683
1821
  return True
1684
1822
 
1685
1823
  def text_editor_newline_binding(self, r=0, c=0, event: object = None, check_lines=True):
1686
1824
  if self.height_resizing_enabled:
1687
- datarn = r if self.MT.all_rows_displayed else self.MT.displayed_rows[r]
1688
- curr_height = self.text_editor.winfo_height()
1825
+ curr_height = self.text_editor.window.winfo_height()
1689
1826
  if (
1690
1827
  not check_lines
1691
- or self.MT.get_lines_cell_height(self.text_editor.get_num_lines() + 1, font=self.PAR.ops.index_font)
1828
+ or self.MT.get_lines_cell_height(
1829
+ self.text_editor.window.get_num_lines() + 1,
1830
+ font=self.PAR.ops.index_font,
1831
+ )
1692
1832
  > curr_height
1693
1833
  ):
1694
1834
  new_height = curr_height + self.MT.index_xtra_lines_increment
@@ -1698,78 +1838,63 @@ class RowIndex(tk.Canvas):
1698
1838
  if new_height != curr_height:
1699
1839
  self.set_row_height(r, new_height)
1700
1840
  self.MT.refresh()
1701
- self.text_editor.config(height=new_height)
1702
- self.coords(self.text_editor_id, 0, self.MT.row_positions[r] + 1)
1703
- kwargs = self.get_cell_kwargs(datarn, key="dropdown")
1704
- if kwargs:
1705
- text_editor_h = self.text_editor.winfo_height()
1841
+ self.text_editor.window.config(height=new_height)
1842
+ self.coords(self.text_editor.canvas_id, 0, self.MT.row_positions[r] + 1)
1843
+ if self.dropdown.open and self.dropdown.get_coords() == r:
1844
+ text_editor_h = self.text_editor.window.winfo_height()
1706
1845
  win_h, anchor = self.get_dropdown_height_anchor(r, text_editor_h)
1707
1846
  if anchor == "nw":
1708
1847
  self.coords(
1709
- kwargs["canvas_id"],
1848
+ self.dropdown.canvas_id,
1710
1849
  self.MT.col_positions[c],
1711
1850
  self.MT.row_positions[r] + text_editor_h - 1,
1712
1851
  )
1713
- self.itemconfig(kwargs["canvas_id"], anchor=anchor, height=win_h)
1852
+ self.itemconfig(self.dropdown.canvas_id, anchor=anchor, height=win_h)
1714
1853
  elif anchor == "sw":
1715
1854
  self.coords(
1716
- kwargs["canvas_id"],
1855
+ self.dropdown.canvas_id,
1717
1856
  self.MT.col_positions[c],
1718
1857
  self.MT.row_positions[r],
1719
1858
  )
1720
- self.itemconfig(kwargs["canvas_id"], anchor=anchor, height=win_h)
1859
+ self.itemconfig(self.dropdown.canvas_id, anchor=anchor, height=win_h)
1721
1860
 
1722
1861
  def refresh_open_window_positions(self):
1723
- if self.text_editor is not None:
1724
- r = self.text_editor_loc
1725
- self.text_editor.config(height=self.MT.row_positions[r + 1] - self.MT.row_positions[r])
1862
+ if self.text_editor.open:
1863
+ r = self.text_editor.row
1864
+ self.text_editor.window.config(height=self.MT.row_positions[r + 1] - self.MT.row_positions[r])
1726
1865
  self.coords(
1727
- self.text_editor_id,
1866
+ self.text_editor.canvas_id,
1728
1867
  0,
1729
1868
  self.MT.row_positions[r],
1730
1869
  )
1731
- if self.existing_dropdown_window is not None:
1732
- r = self.get_existing_dropdown_coords()
1733
- if self.text_editor is None:
1734
- text_editor_h = self.MT.row_positions[r + 1] - self.MT.row_positions[r]
1735
- anchor = self.itemcget(self.existing_dropdown_canvas_id, "anchor")
1736
- win_h = 0
1737
- else:
1738
- text_editor_h = self.text_editor.winfo_height()
1870
+ if self.dropdown.open:
1871
+ r = self.dropdown.get_coords()
1872
+ if self.text_editor.open:
1873
+ text_editor_h = self.text_editor.window.winfo_height()
1739
1874
  win_h, anchor = self.get_dropdown_height_anchor(r, text_editor_h)
1875
+ else:
1876
+ text_editor_h = self.MT.row_positions[r + 1] - self.MT.row_positions[r]
1877
+ anchor = self.itemcget(self.dropdown.canvas_id, "anchor")
1878
+ # win_h = 0
1740
1879
  if anchor == "nw":
1741
1880
  self.coords(
1742
- self.existing_dropdown_canvas_id,
1881
+ self.dropdown.canvas_id,
1743
1882
  0,
1744
1883
  self.MT.row_positions[r] + text_editor_h - 1,
1745
1884
  )
1746
- # self.itemconfig(self.existing_dropdown_canvas_id, anchor=anchor, height=win_h)
1885
+ # self.itemconfig(self.dropdown.canvas_id, anchor=anchor, height=win_h)
1747
1886
  elif anchor == "sw":
1748
1887
  self.coords(
1749
- self.existing_dropdown_canvas_id,
1888
+ self.dropdown.canvas_id,
1750
1889
  0,
1751
1890
  self.MT.row_positions[r],
1752
1891
  )
1753
- # self.itemconfig(self.existing_dropdown_canvas_id, anchor=anchor, height=win_h)
1754
-
1755
- def bind_text_editor_destroy(self, binding, r):
1756
- self.text_editor.textedit.bind("<Return>", lambda _x: binding((r, "Return")))
1757
- self.text_editor.textedit.bind("<FocusOut>", lambda _x: binding((r, "FocusOut")))
1758
- self.text_editor.textedit.bind("<Escape>", lambda _x: binding((r, "Escape")))
1759
- self.text_editor.textedit.focus_set()
1892
+ # self.itemconfig(self.dropdown.canvas_id, anchor=anchor, height=win_h)
1760
1893
 
1761
- def destroy_text_editor(self, reason: None | str = None) -> None:
1762
- self.text_editor_loc = None
1763
- try:
1764
- self.delete(self.text_editor_id)
1765
- except Exception:
1766
- pass
1767
- try:
1768
- self.text_editor.destroy()
1769
- except Exception:
1770
- pass
1771
- self.text_editor = None
1772
- self.text_editor_id = None
1894
+ def hide_text_editor(self, reason: None | str = None) -> None:
1895
+ if self.text_editor.open:
1896
+ self.itemconfig(self.text_editor.canvas_id, state="hidden")
1897
+ self.text_editor.open = False
1773
1898
  if reason == "Escape":
1774
1899
  self.focus_set()
1775
1900
 
@@ -1780,18 +1905,16 @@ class RowIndex(tk.Canvas):
1780
1905
  ) -> str | None:
1781
1906
  focused = self.focus_get()
1782
1907
  try:
1783
- if focused == self.text_editor.textedit.rc_popup_menu:
1908
+ if focused == self.text_editor.tktext.rc_popup_menu:
1784
1909
  return "break"
1785
1910
  except Exception:
1786
1911
  pass
1787
1912
  if focused is None and editor_info:
1788
1913
  return "break"
1789
1914
  if editor_info is not None and len(editor_info) >= 2 and editor_info[1] == "Escape":
1790
- self.destroy_text_editor("Escape")
1791
- self.close_dropdown_window()
1915
+ self.hide_text_editor_and_dropdown()
1792
1916
  return
1793
1917
  self.text_editor_value = self.text_editor.get()
1794
- self.destroy_text_editor()
1795
1918
  r = editor_info[0]
1796
1919
  datarn = r if self.MT.all_rows_displayed else self.MT.displayed_rows[r]
1797
1920
  event_data = event_dict(
@@ -1802,7 +1925,7 @@ class RowIndex(tk.Canvas):
1802
1925
  value=self.text_editor_value,
1803
1926
  loc=r,
1804
1927
  boxes=self.MT.get_boxes(),
1805
- selected=self.MT.currently_selected(),
1928
+ selected=self.MT.selected,
1806
1929
  )
1807
1930
  edited = False
1808
1931
  set_data = partial(
@@ -1819,9 +1942,8 @@ class RowIndex(tk.Canvas):
1819
1942
  edited = set_data(value=self.text_editor_value)
1820
1943
  if edited:
1821
1944
  try_binding(self.extra_end_edit_cell_func, event_data)
1822
- self.close_dropdown_window(r)
1823
1945
  self.MT.recreate_all_selection_boxes()
1824
- self.MT.refresh()
1946
+ self.hide_text_editor_and_dropdown()
1825
1947
  if editor_info[1] != "FocusOut":
1826
1948
  self.focus_set()
1827
1949
  return "break"
@@ -1863,8 +1985,7 @@ class RowIndex(tk.Canvas):
1863
1985
 
1864
1986
  # r is displayed row
1865
1987
  def open_dropdown_window(self, r, datarn=None, event: object = None):
1866
- self.destroy_text_editor("Escape")
1867
- self.destroy_opened_dropdown_window()
1988
+ self.hide_text_editor("Escape")
1868
1989
  if datarn is None:
1869
1990
  datarn = r if self.MT.all_rows_displayed else self.MT.displayed_rows[r]
1870
1991
  kwargs = self.get_cell_kwargs(datarn, key="dropdown")
@@ -1872,61 +1993,68 @@ class RowIndex(tk.Canvas):
1872
1993
  if not self.open_text_editor(event=event, r=r, dropdown=True):
1873
1994
  return
1874
1995
  win_h, anchor = self.get_dropdown_height_anchor(r)
1875
- window = self.PAR.dropdown_class(
1876
- self.MT.winfo_toplevel(),
1877
- r,
1878
- 0,
1879
- width=self.current_width,
1880
- height=win_h,
1881
- font=self.PAR.ops.index_font,
1882
- colors={
1883
- "bg": self.PAR.ops.popup_menu_bg,
1884
- "fg": self.PAR.ops.popup_menu_fg,
1885
- "highlight_bg": self.PAR.ops.popup_menu_highlight_bg,
1886
- "highlight_fg": self.PAR.ops.popup_menu_highlight_fg,
1887
- },
1888
- outline_color=self.PAR.ops.popup_menu_fg,
1889
- values=kwargs["values"],
1890
- close_dropdown_window=self.close_dropdown_window,
1891
- search_function=kwargs["search_function"],
1892
- arrowkey_RIGHT=self.MT.arrowkey_RIGHT,
1893
- arrowkey_LEFT=self.MT.arrowkey_LEFT,
1894
- align="w",
1895
- single_index="r",
1896
- )
1897
1996
  ypos = self.MT.row_positions[r + 1]
1898
- kwargs["canvas_id"] = self.create_window((0, ypos), window=window, anchor=anchor)
1997
+ if self.dropdown.window is not None:
1998
+ self.dropdown.window.search_function = kwargs["search_function"]
1999
+ self.dropdown.window.r = r
2000
+ self.dropdown.window.row = -1
2001
+ self.dropdown.window.set_options()
2002
+ self.dropdown.window.values(kwargs["values"])
2003
+ if not self.dropdown.open:
2004
+ self.itemconfig(self.dropdown.canvas_id, state="normal")
2005
+ self.coords(self.dropdown.canvas_id, 0, ypos)
2006
+ else:
2007
+ self.dropdown.window = self.PAR.dropdown_class(
2008
+ self.MT.winfo_toplevel(),
2009
+ r,
2010
+ 0,
2011
+ width=self.current_width,
2012
+ height=win_h,
2013
+ font=self.PAR.ops.index_font,
2014
+ ops=self.PAR.ops,
2015
+ outline_color=self.PAR.ops.popup_menu_fg,
2016
+ values=kwargs["values"],
2017
+ close_dropdown_window=self.close_dropdown_window,
2018
+ search_function=kwargs["search_function"],
2019
+ arrowkey_RIGHT=self.MT.arrowkey_RIGHT,
2020
+ arrowkey_LEFT=self.MT.arrowkey_LEFT,
2021
+ align="w",
2022
+ single_index="r",
2023
+ )
2024
+ self.dropdown.canvas_id = self.create_window(
2025
+ (0, ypos),
2026
+ window=self.dropdown.window,
2027
+ anchor=anchor,
2028
+ )
1899
2029
  if kwargs["state"] == "normal":
1900
- self.text_editor.textedit.bind(
2030
+ self.text_editor.tktext.bind(
1901
2031
  "<<TextModified>>",
1902
2032
  lambda _x: self.dropdown_text_editor_modified(
1903
- window,
2033
+ self.dropdown.window,
1904
2034
  event_dict(
1905
2035
  name="index_dropdown_modified",
1906
2036
  sheet=self.PAR.name,
1907
2037
  value=self.text_editor.get(),
1908
2038
  loc=r,
1909
2039
  boxes=self.MT.get_boxes(),
1910
- selected=self.MT.currently_selected(),
2040
+ selected=self.MT.selected,
1911
2041
  ),
1912
2042
  kwargs["modified_function"],
1913
2043
  ),
1914
2044
  )
1915
2045
  self.update_idletasks()
1916
2046
  try:
1917
- self.after(1, lambda: self.text_editor.textedit.focus())
1918
- self.after(2, self.text_editor.scroll_to_bottom())
2047
+ self.after(1, lambda: self.text_editor.tktext.focus())
2048
+ self.after(2, self.text_editor.window.scroll_to_bottom())
1919
2049
  except Exception:
1920
2050
  return
1921
2051
  redraw = False
1922
2052
  else:
1923
- window.bind("<FocusOut>", lambda _x: self.close_dropdown_window(r))
2053
+ self.dropdown.window.bind("<FocusOut>", lambda _x: self.close_dropdown_window(r))
1924
2054
  self.update_idletasks()
1925
- window.focus_set()
2055
+ self.dropdown.window.focus_set()
1926
2056
  redraw = True
1927
- self.existing_dropdown_window = window
1928
- kwargs["window"] = window
1929
- self.existing_dropdown_canvas_id = kwargs["canvas_id"]
2057
+ self.dropdown.open = True
1930
2058
  if redraw:
1931
2059
  self.MT.main_table_redraw_grid_and_text(redraw_header=False, redraw_row_index=True, redraw_table=False)
1932
2060
 
@@ -1945,9 +2073,9 @@ class RowIndex(tk.Canvas):
1945
2073
  value=selection,
1946
2074
  loc=r,
1947
2075
  boxes=self.MT.get_boxes(),
1948
- selected=self.MT.currently_selected(),
2076
+ selected=self.MT.selected,
1949
2077
  )
1950
- if kwargs["select_function"] is not None: # user has specified a selection function
2078
+ if kwargs["select_function"] is not None:
1951
2079
  kwargs["select_function"](event_data)
1952
2080
  if self.MT.edit_validation_func:
1953
2081
  selection = self.MT.edit_validation_func(event_data)
@@ -1959,23 +2087,20 @@ class RowIndex(tk.Canvas):
1959
2087
  try_binding(self.extra_end_edit_cell_func, event_data)
1960
2088
  self.focus_set()
1961
2089
  self.MT.recreate_all_selection_boxes()
1962
- self.destroy_text_editor("Escape")
1963
- self.destroy_opened_dropdown_window(r)
2090
+ self.hide_text_editor_and_dropdown(redraw=redraw)
2091
+
2092
+ def hide_text_editor_and_dropdown(self, redraw: bool = True) -> None:
2093
+ self.hide_text_editor("Escape")
2094
+ self.hide_dropdown_window()
1964
2095
  if redraw:
1965
2096
  self.MT.refresh()
1966
2097
 
1967
- def get_existing_dropdown_coords(self):
1968
- if self.existing_dropdown_window is not None:
1969
- return int(self.existing_dropdown_window.r)
1970
- return None
1971
-
1972
2098
  def mouseclick_outside_editor_or_dropdown(self, inside: bool = False):
1973
- closed_dd_coords = self.get_existing_dropdown_coords()
1974
- if self.text_editor_loc is not None and self.text_editor is not None:
1975
- self.close_text_editor((self.text_editor_loc, "ButtonPress-1"))
2099
+ closed_dd_coords = self.dropdown.get_coords()
2100
+ if self.text_editor.open:
2101
+ self.close_text_editor((self.text_editor.row, "ButtonPress-1"))
1976
2102
  if closed_dd_coords is not None:
1977
- # displayed coords not data, necessary for b1 function
1978
- self.destroy_opened_dropdown_window(closed_dd_coords)
2103
+ self.hide_dropdown_window()
1979
2104
  if inside:
1980
2105
  self.MT.main_table_redraw_grid_and_text(
1981
2106
  redraw_header=False,
@@ -1989,47 +2114,22 @@ class RowIndex(tk.Canvas):
1989
2114
  self.MT.mouseclick_outside_editor_or_dropdown()
1990
2115
  return self.mouseclick_outside_editor_or_dropdown(inside)
1991
2116
 
1992
- # r is displayed row, function can have two None args
1993
- def destroy_opened_dropdown_window(self, r=None, datarn=None):
1994
- if r is None and datarn is None and self.existing_dropdown_window is not None:
1995
- r = self.get_existing_dropdown_coords()
1996
- if r is not None or datarn is not None:
1997
- if datarn is None:
1998
- datarn_ = r if self.MT.all_rows_displayed else self.MT.displayed_rows[r]
1999
- else:
2000
- datarn_ = r
2001
- else:
2002
- datarn_ = None
2003
- try:
2004
- self.delete(self.existing_dropdown_canvas_id)
2005
- except Exception:
2006
- pass
2007
- self.existing_dropdown_canvas_id = None
2008
- try:
2009
- self.existing_dropdown_window.destroy()
2010
- except Exception:
2011
- pass
2012
- kwargs = self.get_cell_kwargs(datarn_, key="dropdown")
2013
- if kwargs:
2014
- kwargs["canvas_id"] = "no dropdown open"
2015
- kwargs["window"] = "no dropdown open"
2016
- try:
2017
- self.delete(kwargs["canvas_id"])
2018
- except Exception:
2019
- pass
2020
- self.existing_dropdown_window = None
2117
+ def hide_dropdown_window(self) -> None:
2118
+ if self.dropdown.open:
2119
+ self.itemconfig(self.dropdown.canvas_id, state="hidden")
2120
+ self.dropdown.open = False
2021
2121
 
2022
2122
  # internal event use
2023
2123
  def set_cell_data_undo(
2024
2124
  self,
2025
- r=0,
2026
- datarn=None,
2027
- value="",
2028
- cell_resize=True,
2029
- undo=True,
2030
- redraw=True,
2031
- check_input_valid=True,
2032
- ):
2125
+ r: int = 0,
2126
+ datarn: None | int = None,
2127
+ value: str = "",
2128
+ cell_resize: bool = True,
2129
+ undo: bool = True,
2130
+ redraw: bool = True,
2131
+ check_input_valid: bool = True,
2132
+ ) -> bool:
2033
2133
  if datarn is None:
2034
2134
  datarn = r if self.MT.all_rows_displayed else self.MT.displayed_rows[r]
2035
2135
  event_data = event_dict(
@@ -2037,7 +2137,7 @@ class RowIndex(tk.Canvas):
2037
2137
  sheet=self.PAR.name,
2038
2138
  cells_index={datarn: self.get_cell_data(datarn)},
2039
2139
  boxes=self.MT.get_boxes(),
2040
- selected=self.MT.currently_selected(),
2140
+ selected=self.MT.selected,
2041
2141
  )
2042
2142
  edited = False
2043
2143
  if isinstance(self.MT._row_index, int):
@@ -2194,7 +2294,7 @@ class RowIndex(tk.Canvas):
2194
2294
  value=value,
2195
2295
  loc=r,
2196
2296
  boxes=self.MT.get_boxes(),
2197
- selected=self.MT.currently_selected(),
2297
+ selected=self.MT.selected,
2198
2298
  )
2199
2299
  if kwargs["check_function"] is not None:
2200
2300
  kwargs["check_function"](event_data)
@@ -2206,3 +2306,57 @@ class RowIndex(tk.Canvas):
2206
2306
  if cell and datarn in self.cell_options and key in self.cell_options[datarn]:
2207
2307
  return self.cell_options[datarn][key]
2208
2308
  return {}
2309
+
2310
+ # Treeview Mode
2311
+
2312
+ def get_node_level(self, node: Node, level: int = 0) -> Generator[int]:
2313
+ yield level
2314
+ if node.parent:
2315
+ yield from self.get_node_level(node.parent, level + 1)
2316
+
2317
+ def ancestors_all_open(self, iid: str, stop_at: str | Node = "") -> bool:
2318
+ if stop_at:
2319
+ stop_at = stop_at.iid
2320
+ for iid in self.get_iid_ancestors(iid):
2321
+ if iid == stop_at:
2322
+ return True
2323
+ if iid not in self.tree_open_ids:
2324
+ return False
2325
+ return True
2326
+ else:
2327
+ return all(iid in self.tree_open_ids for iid in self.get_iid_ancestors(iid))
2328
+
2329
+ def get_iid_ancestors(self, iid: str) -> Generator[str]:
2330
+ if self.tree[iid].parent:
2331
+ yield self.tree[iid].parent.iid
2332
+ yield from self.get_iid_ancestors(self.tree[iid].parent.iid)
2333
+
2334
+ def get_iid_descendants(self, iid: str, check_open: bool = False) -> Generator[str]:
2335
+ for cnode in self.tree[iid].children:
2336
+ yield cnode.iid
2337
+ if (check_open and cnode.children and cnode.iid in self.tree_open_ids) or (
2338
+ not check_open and cnode.children
2339
+ ):
2340
+ yield from self.get_iid_descendants(cnode.iid, check_open)
2341
+
2342
+ def get_treeview_indent(self, iid: str) -> int:
2343
+ if isinstance(self.PAR.ops.treeview_indent, str):
2344
+ indent = self.MT.index_txt_height * int(self.PAR.ops.treeview_indent)
2345
+ else:
2346
+ indent = self.PAR.ops.treeview_indent
2347
+ return indent * max(self.get_node_level(self.tree[iid]))
2348
+
2349
+ def remove_node_from_parents_children(self, node: Node) -> None:
2350
+ if node.parent:
2351
+ node.parent.children.remove(node)
2352
+ if not node.parent.children:
2353
+ self.tree_open_ids.discard(node.parent)
2354
+
2355
+ def pid_causes_recursive_loop(self, iid: str, pid: str) -> bool:
2356
+ return any(
2357
+ i == pid
2358
+ for i in chain(
2359
+ self.get_iid_descendants(iid),
2360
+ islice(self.get_iid_ancestors(iid), 1, None),
2361
+ )
2362
+ )