Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multiselect support #24

Merged
merged 15 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hoverset/ui/dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ def __init__(self, master, render_routine=None, **kw):
"SHOW_PROGRESS": self._show_progress,
"BUILDER": self._builder # Allows building custom dialogs
}
self.enable_centering()
if render_routine in routines:
# Completely custom dialogs
routines[render_routine](**kw) # noqa
elif render_routine is not None:
render_routine(self)
self.enable_centering()
self.value = None
# quirk that prevents explicit window centering on linux for best results
add = "+" if platform_is(WINDOWS, MAC) else None
Expand Down
14 changes: 8 additions & 6 deletions hoverset/ui/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2558,6 +2558,10 @@ def _load_images(self):
cls.BLANK = get_icon_image("blank", 14, 14)
cls.__icons_loaded = True

@property
def selected(self):
return self._selected

@property
def depth(self):
return self._depth
Expand Down Expand Up @@ -2710,16 +2714,15 @@ def remove(self, node=None):
self.collapse()
self.nodes.remove(node)
node.pack_forget()
if was_expanded:
if was_expanded and len(self.nodes) > 0:
# If the parent was expanded when we began removal we expand it again
self.expand()
if len(self.nodes) == 0:
if not self.nodes:
# remove the expansion icon
self._set_expander(self.BLANK)

def expand(self):
if len(self.nodes) == 0:
# There is nothing to expand
if self._expanded:
return
self.pack_propagate(True)
for node in filter(lambda n: n._visible, self.nodes):
Expand All @@ -2728,8 +2731,7 @@ def expand(self):
self._expanded = True

def collapse(self):
if len(self.nodes) == 0:
# There is nothing to collapse
if not self._expanded:
return
for node in self.nodes:
node.pack_forget()
Expand Down
32 changes: 15 additions & 17 deletions studio/feature/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,28 +118,26 @@ def set_pref(cls, short_path, value):
def get_instance(cls):
return cls._instance

def on_select(self, widget):
def on_widgets_change(self, widgets):
"""
Called when a widget is selected in the designer
:param widget: selected widget
:return:None
Called when the widgets in the designer are changed
:param widgets: list of widgets
:return: None
"""
pass

def on_widget_change(self, old_widget, new_widget=None):
def on_widgets_layout_change(self, widgets):
"""
Called when a widget is fundamentally altered
:param old_widget: Altered widget
:param new_widget: The new widget taking the older widgets place
Called when layout options of a widgets are changed
:param widgets: Widgets with altered layout options
:return: None
"""
pass

def on_widget_layout_change(self, widget):
def on_widgets_reorder(self, indices):
"""
Called when layout options of a widget are changed
:param widget: Widget with altered layout options
:return: None
Called when the widgets in the designer are reordered within their parent.
Used to change stacking order of widgets
"""
pass

Expand All @@ -152,21 +150,21 @@ def on_widget_add(self, widget, parent):
"""
pass

def on_widget_delete(self, widget, silently=False):
def on_widgets_delete(self, widgets, silently=False):
"""
Called when a widget is deleted from the designer
:param widget: deleted widget
Called when widgets are deleted from the designer
:param widgets: deleted widgets
:param silently: flag indicating whether the deletion should be treated implicitly
which is useful for instance when you don't want the deletion to be logged in the
undo stack
:return: None
"""
pass

def on_widget_restore(self, widget):
def on_widgets_restore(self, widgets):
"""
Called when a deleted widget is restored
:param widget: restored widget
:param widgets: widgets to be restored
:return: None
"""
pass
Expand Down
81 changes: 48 additions & 33 deletions studio/feature/component_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ def __init__(self, master, studio=None, **cnf):
self.body = Frame(self, **self.style.surface)
self.body.pack(side="top", fill="both", expand=True)
self._empty_label = Label(self.body, **self.style.text_passive)
self.studio.bind("<<SelectionChanged>>", self._select, "+")

self._selected = None
self._expanded = False
self._tree = None

Expand All @@ -92,6 +92,7 @@ def on_context_switch(self):
self._tree = self.studio.designer.node
else:
self._tree = ComponentTreeView(self.body)
self._tree.allow_multi_select(True)
self._tree.on_select(self._trigger_select)
self.studio.designer.node = self._tree
self._tree.pack(fill="both", expand=True)
Expand Down Expand Up @@ -142,38 +143,52 @@ def on_widget_add(self, widget: PseudoWidget, parent=None):
)

def _trigger_select(self):
if self._selected and self._selected.widget == self._tree.get().widget:
if self.studio.selection == self.selection():
return
self.studio.select(self._tree.get().widget, self)
self._selected = self._tree.get()

def select(self, widget):
if widget:
self.studio.selection.set(self.selection())

def _select(self, _):
if self.studio.selection == self.selection():
return

if not self._tree:
return
nodes = self.studio_selection()

for node in list(self._tree.get()):
if node not in nodes:
self._tree.deselect(node)

for node in nodes:
if not node.selected:
node.select(silently=True)

def selection(self):
if not self._tree:
return []
return [i.widget for i in self._tree.get()]

def studio_selection(self):
return [i.node for i in self.studio.selection]

def on_widgets_delete(self, widgets, silently=False):
for widget in widgets:
widget.node.remove()

def on_widgets_restore(self, widgets):
for widget in widgets:
widget.layout.node.add(widget.node)

def on_widgets_layout_change(self, widgets):
for widget in widgets:
node = widget.node
self._selected = node
node.select(None, True) # Select node silently to avoid triggering a duplicate selection event
elif widget is None:
if self._selected:
self._selected.deselect()
self._selected = None

def on_select(self, widget):
self.select(widget)

def on_widget_delete(self, widget, silently=False):
widget.node.remove()

def on_widget_restore(self, widget):
widget.layout.node.add(widget.node)

def on_widget_layout_change(self, widget):
node = widget.node
if widget.layout == self.studio.designer:
parent = self._tree
else:
parent = widget.layout.node
if node.parent_node != parent:
parent.insert(None, node)
if widget.layout == self.studio.designer:
parent = self._tree
else:
parent = widget.layout.node
if node.parent_node != parent:
parent.insert(None, node)

def on_context_close(self, context):
if hasattr(context, "designer"):
Expand All @@ -184,9 +199,9 @@ def on_context_close(self, context):
def on_session_clear(self):
self._tree.clear()

def on_widget_change(self, old_widget, new_widget=None):
new_widget = new_widget if new_widget else old_widget
new_widget.node.widget_modified(new_widget)
def on_widgets_change(self, widgets):
for widget in widgets:
widget.node.widget_modified(widget)

def on_search_query(self, query: str):
self._tree.search(query)
Expand Down
8 changes: 4 additions & 4 deletions studio/feature/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def on_drag_end(self, event):
widget = self.event_first(event, self, Container)
if isinstance(widget, Container):
widget.add_new(self.component, *self.window.drag_window.get_center())
widget.clear_highlight()


class SelectableComponent(Component):
Expand Down Expand Up @@ -274,12 +275,12 @@ def __init__(self, master, studio=None, **cnf):
self._selected = None
self._component_cache = None
self._extern_groups = []
self._widget = None
self.collect_groups(self.get_pref("widget_set"))
# add custom widgets config to settings
templates.update(_widget_pref_template)
self._custom_group = None
self._custom_widgets = []
self.studio.bind("<<SelectionChanged>>", self.on_widget_select, add='+')
Preferences.acquire().add_listener(self._custom_pref_path, self._init_custom)
self._reload_custom()

Expand Down Expand Up @@ -438,7 +439,7 @@ def render_groups(self):

def render_extern_groups(self):
for group in self._extern_groups:
if group.supports(self._widget):
if self.studio.selection and all(group.supports(w) for w in self.studio.selection):
self.add_selector(group.selector)
else:
self.remove_selector(group.selector)
Expand Down Expand Up @@ -480,8 +481,7 @@ def unregister_group(self, group):
self._extern_groups.remove(group)
self._auto_select()

def on_select(self, widget):
self._widget = widget
def on_widget_select(self, _):
self.render_extern_groups()

def start_search(self, *_):
Expand Down
Loading
Loading