feat: multi-select support in toc editor

This commit is contained in:
midrare 2024-12-14 18:46:27 -08:00
parent e4ab29ac03
commit d2deef5be0

View File

@ -532,102 +532,227 @@ class TreeWidget(QTreeWidget): # {{{
self.setCurrentItem(item, 0, QItemSelectionModel.SelectionFlag.ClearAndSelect) self.setCurrentItem(item, 0, QItemSelectionModel.SelectionFlag.ClearAndSelect)
self.scrollToItem(item) self.scrollToItem(item)
def check_multi_selection(self): def _sort_items_by_index(self, items, reverse=False):
if len(self.selectedItems()) > 1: def get_index(item):
info_dialog(self, _('Multiple items selected'), _( result = []
'You are trying to move multiple items at once, this is not supported. Instead use'
' Drag and Drop to move multiple items'), show=True) parent = item.parent()
return False while parent is not None:
return True result.append(parent.indexOfChild(item))
item, parent = parent, parent.parent()
result.reverse()
return result
items.sort(key=get_index, reverse=reverse)
def _get_root_items(self, items):
items_ = []
for item in items:
if item is None:
continue
parent = item
while (parent := parent.parent()) is not None:
if parent in items:
break
else:
items_.append(item)
return items_
def _move_indent_left(self, items):
self._sort_items_by_index(items)
for item in items:
old_parent = item.parent()
if old_parent is None:
return
old_index = old_parent.indexOfChild(item)
was_expanded = item.isExpanded() or item.childCount() == 0
new_parent = old_parent.parent() or self.invisibleRootItem()
new_index = new_parent.indexOfChild(old_parent) + 1
# all former lower siblings become children of indented item
for _ in range(old_parent.childCount() - old_index - 1):
lower_sibling = old_parent.child(old_index+1)
old_parent.removeChild(lower_sibling)
item.addChild(lower_sibling)
if lower_sibling not in items:
was_expanded = True
old_parent.removeChild(item)
new_parent.insertChild(new_index, item)
self.expandItem(new_parent)
if was_expanded:
self.expandItem(item)
def _move_indent_right(self, items):
self._sort_items_by_index(items)
failed_parent = None
for item in items:
# indent right == become child of upper sibling
old_parent = item.parent() or self.invisibleRootItem()
old_idx = old_parent.indexOfChild(item)
was_expanded = item.isExpanded()
if old_idx <= 0:
# no upper sibling exists; cannot become child
failed_parent = old_parent
continue
elif failed_parent and old_parent is failed_parent:
# this prevents siblings at the same level from
# nesting into each other forming a "staircase"
continue
else:
failed_parent = None
new_parent = old_parent.child(old_idx-1)
old_parent.removeChild(item)
new_parent.addChild(item)
self.expandItem(new_parent)
if was_expanded:
self.expandItem(item)
def _move_indent(self, items, indent):
if not items or indent == 0:
return
# indent offsets are absolute (as opposed to relative to parent)
# child indented with parent automatically, no need for manual
items_ = self._get_root_items(items)
if len(items_) <= 0 or indent == 0:
return
focus_item = self.currentItem()
self.push_history()
if indent < 0:
self._move_indent_left(items_)
elif indent > 0:
self._move_indent_right(items_)
# restore previous focused item
if focus_item is None and items_:
focus_item = items_[0]
if focus_item:
self.setCurrentItem(
focus_item,
0,
QItemSelectionModel.SelectionFlag.ClearAndSelect
)
for item in items:
item.setSelected(True)
def move_left(self): def move_left(self):
if not self.check_multi_selection(): selected_items = self.selectedItems()
if len(selected_items) <= 0:
return return
self.push_history()
item = self.currentItem() self._move_indent(selected_items, -1)
if item is not None:
parent = item.parent()
if parent is not None:
is_expanded = item.isExpanded() or item.childCount() == 0
gp = parent.parent() or self.invisibleRootItem()
idx = gp.indexOfChild(parent)
for gc in [parent.child(i) for i in range(parent.indexOfChild(item)+1, parent.childCount())]:
parent.removeChild(gc)
item.addChild(gc)
parent.removeChild(item)
gp.insertChild(idx+1, item)
if is_expanded:
self.expandItem(item)
self.highlight_item(item)
def move_right(self): def move_right(self):
if not self.check_multi_selection(): selected_items = self.selectedItems()
if len(selected_items) <= 0:
return return
self.push_history()
item = self.currentItem() self._move_indent(selected_items, 1)
if item is not None:
parent = item.parent() or self.invisibleRootItem()
idx = parent.indexOfChild(item)
if idx > 0:
is_expanded = item.isExpanded()
np = parent.child(idx-1)
parent.removeChild(item)
np.addChild(item)
if is_expanded:
self.expandItem(item)
self.highlight_item(item)
def move_down(self): def move_down(self):
if not self.check_multi_selection(): selected_items = self.selectedItems()
if len(selected_items) <= 0:
return return
self.push_history() self.push_history()
item = self.currentItem()
if item is None: items_ = self._get_root_items(selected_items)
if self.root.childCount() == 0: self._sort_items_by_index(items_)
return
item = self.root.child(0) focus_item = self.currentItem()
self.highlight_item(item)
return for item in reversed(items_):
parent = item.parent() or self.root old_parent = item.parent() or self.invisibleRootItem()
idx = parent.indexOfChild(item) old_index = old_parent.indexOfChild(item)
if idx == parent.childCount() - 1: was_expanded = item.isExpanded() or item.childCount() == 0
# At end of parent, need to become sibling of parent
if parent is self.root: new_parent = None
return if old_index + 1 < old_parent.childCount():
gp = parent.parent() or self.root # there is still space in parent; move down in same parent
parent.removeChild(item) old_parent.removeChild(item)
gp.insertChild(gp.indexOfChild(parent)+1, item) old_parent.insertChild(old_index + 1, item)
else: elif old_parent is not self.invisibleRootItem():
sibling = parent.child(idx+1) # move down past bottom of parent, become child of grandparent
parent.removeChild(item) new_parent = old_parent.parent() or self.invisibleRootItem()
sibling.insertChild(0, item) old_parent.removeChild(item)
self.highlight_item(item) new_index = new_parent.indexOfChild(old_parent) + 1
new_parent.insertChild(new_index, item)
self.expandItem(new_parent)
self.expandItem(new_parent or old_parent)
if was_expanded:
self.expandItem(item)
if focus_item:
self.setCurrentItem(
focus_item,
0,
QItemSelectionModel.SelectionFlag.ClearAndSelect
)
for item in selected_items:
item.setSelected(True)
def move_up(self): def move_up(self):
if not self.check_multi_selection(): selected_items = self.selectedItems()
if len(selected_items) <= 0:
return return
self.push_history() self.push_history()
item = self.currentItem()
if item is None: items_ = self._get_root_items(selected_items)
if self.root.childCount() == 0: self._sort_items_by_index(items_)
return
item = self.root.child(self.root.childCount()-1) focus_item = self.currentItem()
self.highlight_item(item)
return for item in items_:
parent = item.parent() or self.root old_parent = item.parent() or self.invisibleRootItem()
idx = parent.indexOfChild(item) old_index = old_parent.indexOfChild(item)
if idx == 0: was_expanded = item.isExpanded() or item.childCount() == 0
# At end of parent, need to become sibling of parent
if parent is self.root: new_parent = None
return if old_index - 1 >= 0:
gp = parent.parent() or self.root # there is still space in parent; move up within parent
parent.removeChild(item) old_parent.removeChild(item)
gp.insertChild(gp.indexOfChild(parent), item) old_parent.insertChild(old_index - 1, item)
else: elif old_parent is not self.invisibleRootItem():
sibling = parent.child(idx-1) # move up past top of parent, become upper sibling of parent
parent.removeChild(item) new_parent = old_parent.parent() or self.invisibleRootItem()
sibling.addChild(item) old_parent.removeChild(item)
self.highlight_item(item) new_index = new_parent.indexOfChild(old_parent)
new_parent.insertChild(new_index, item)
self.expandItem(new_parent)
self.expandItem(new_parent or old_parent)
if was_expanded:
self.expandItem(item)
if focus_item:
self.setCurrentItem(
focus_item,
0,
QItemSelectionModel.SelectionFlag.ClearAndSelect
)
for item in selected_items:
item.setSelected(True)
def del_items(self): def del_items(self):
self.push_history() self.push_history()