mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
use tooltip widget to show subactions
This commit is contained in:
parent
0be94fc2d8
commit
d312101573
2 changed files with 267 additions and 102 deletions
|
|
@ -829,6 +829,11 @@ HintedLineEditButton {
|
|||
}
|
||||
|
||||
/* Launcher specific stylesheets */
|
||||
ActionMenuToolTip {
|
||||
border: 1px solid #555555;
|
||||
background: {color:bg-inputs};
|
||||
}
|
||||
|
||||
ActionVariantWidget {
|
||||
background: transparent;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -304,13 +304,219 @@ class ActionsQtModel(QtGui.QStandardItemModel):
|
|||
self.refresh()
|
||||
|
||||
|
||||
class ActionMenuToolTip(QtWidgets.QFrame):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
self.setWindowFlags(QtCore.Qt.ToolTip)
|
||||
self.setAttribute(QtCore.Qt.WA_ShowWithoutActivating, True)
|
||||
self.setAutoFillBackground(True)
|
||||
self.setBackgroundRole(QtGui.QPalette.Base)
|
||||
|
||||
# Update size on show
|
||||
show_timer = QtCore.QTimer()
|
||||
show_timer.setSingleShot(True)
|
||||
show_timer.setInterval(5)
|
||||
|
||||
# Close widget if is not updated by event
|
||||
close_timer = QtCore.QTimer()
|
||||
close_timer.setSingleShot(True)
|
||||
close_timer.setInterval(100)
|
||||
|
||||
update_state_timer = QtCore.QTimer()
|
||||
update_state_timer.setInterval(500)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
|
||||
close_timer.timeout.connect(self.close)
|
||||
show_timer.timeout.connect(self._on_show_timer)
|
||||
update_state_timer.timeout.connect(self._on_update_state)
|
||||
|
||||
self._main_layout = main_layout
|
||||
self._show_timer = show_timer
|
||||
self._close_timer = close_timer
|
||||
self._update_state_timer = update_state_timer
|
||||
|
||||
self._showed = False
|
||||
self._mouse_entered = False
|
||||
self._view_hovered = False
|
||||
self._current_id = None
|
||||
self._view = None
|
||||
self._last_pos = QtCore.QPoint(0, 0)
|
||||
self._widgets_by_id = {}
|
||||
|
||||
def showEvent(self, event):
|
||||
self._showed = True
|
||||
self._update_state_timer.start()
|
||||
super().showEvent(event)
|
||||
|
||||
def closeEvent(self, event):
|
||||
self._showed = False
|
||||
self._update_state_timer.stop()
|
||||
self._mouse_entered = False
|
||||
super().closeEvent(event)
|
||||
|
||||
def enterEvent(self, event):
|
||||
self._mouse_entered = True
|
||||
self._close_timer.stop()
|
||||
super().leaveEvent(event)
|
||||
|
||||
def leaveEvent(self, event):
|
||||
self._mouse_entered = False
|
||||
super().leaveEvent(event)
|
||||
if not self._view_hovered:
|
||||
self._close_timer.start()
|
||||
|
||||
def mouse_entered_view(self):
|
||||
self._view_hovered = True
|
||||
|
||||
def mouse_left_view(self):
|
||||
self._view_hovered = False
|
||||
if not self._mouse_entered:
|
||||
self._close_timer.start()
|
||||
|
||||
def show_on_event(self, action_id, action_items, view, event):
|
||||
self._close_timer.stop()
|
||||
|
||||
self._view_hovered = True
|
||||
|
||||
is_current = action_id == self._current_id
|
||||
if not is_current:
|
||||
self._current_id = action_id
|
||||
self._view = view
|
||||
self._update_items(view, action_items)
|
||||
|
||||
# Nothing to show
|
||||
if not self._widgets_by_id:
|
||||
if self._showed:
|
||||
self.close()
|
||||
return
|
||||
|
||||
# Make sure is visible
|
||||
update_position = not is_current
|
||||
if not self._showed:
|
||||
update_position = True
|
||||
self.show()
|
||||
|
||||
self._last_pos = QtCore.QPoint(event.globalPos())
|
||||
if not update_position:
|
||||
# Only resize if is current
|
||||
self.resize(self.sizeHint())
|
||||
else:
|
||||
# Set geometry to position
|
||||
# - first make sure widget changes from '_update_items'
|
||||
# are recalculated
|
||||
app = QtWidgets.QApplication.instance()
|
||||
app.processEvents()
|
||||
self._on_update_state()
|
||||
|
||||
self.raise_()
|
||||
self._show_timer.start()
|
||||
|
||||
def _on_show_timer(self):
|
||||
size = self.sizeHint()
|
||||
self.resize(size)
|
||||
|
||||
def _on_update_state(self):
|
||||
if not self._view_hovered:
|
||||
return
|
||||
size = self.sizeHint()
|
||||
pos = self._last_pos
|
||||
offset = 4
|
||||
self.setGeometry(
|
||||
pos.x() + offset, pos.y() + offset,
|
||||
size.width(), size.height()
|
||||
)
|
||||
|
||||
def _update_items(self, view, action_items):
|
||||
"""Update items in the tooltip."""
|
||||
# This method can be used to update the content of the tooltip
|
||||
# with new icon, text and settings button visibility.
|
||||
|
||||
remove_ids = set(self._widgets_by_id.keys())
|
||||
new_ids = set()
|
||||
widgets = []
|
||||
|
||||
any_has_settings = False
|
||||
prepared_items = []
|
||||
for idx, action_item in enumerate(action_items):
|
||||
has_settings = bool(action_item.config_fields)
|
||||
if has_settings:
|
||||
any_has_settings = True
|
||||
prepared_items.append((idx, action_item, has_settings))
|
||||
|
||||
if any_has_settings or len(action_items) > 1:
|
||||
for idx, action_item, has_settings in prepared_items:
|
||||
widget = self._widgets_by_id.get(action_item.identifier)
|
||||
icon = get_qt_icon(action_item.icon)
|
||||
label = action_item.full_label
|
||||
if widget is None:
|
||||
widget = ActionVariantWidget(
|
||||
action_item.identifier, label, has_settings, self
|
||||
)
|
||||
widget.settings_requested.connect(
|
||||
view.settings_requested
|
||||
)
|
||||
new_ids.add(action_item.identifier)
|
||||
self._widgets_by_id[action_item.identifier] = widget
|
||||
else:
|
||||
remove_ids.discard(action_item.identifier)
|
||||
widgets.append((idx, widget))
|
||||
|
||||
for action_id in remove_ids:
|
||||
widget = self._widgets_by_id.pop(action_id)
|
||||
widget.setVisible(False)
|
||||
self._main_layout.removeWidget(widget)
|
||||
widget.deleteLater()
|
||||
|
||||
for idx, widget in widgets:
|
||||
self._main_layout.insertWidget(idx, widget, 0)
|
||||
|
||||
|
||||
class ActionDelegate(QtWidgets.QStyledItemDelegate):
|
||||
_cached_extender = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ActionDelegate, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self._anim_start_color = QtGui.QColor(178, 255, 246)
|
||||
self._anim_end_color = QtGui.QColor(5, 44, 50)
|
||||
self._tooltip_widget = None
|
||||
|
||||
def helpEvent(self, event, view, option, index):
|
||||
if not index.isValid():
|
||||
if self._tooltip_widget is not None:
|
||||
self._tooltip_widget.close()
|
||||
return False
|
||||
|
||||
action_id = index.data(ACTION_ID_ROLE)
|
||||
model = index.model()
|
||||
source_model = model.sourceModel()
|
||||
if index.data(ACTION_IS_GROUP_ROLE):
|
||||
action_items = source_model.get_group_items(action_id)
|
||||
else:
|
||||
action_items = [source_model.get_action_item_by_id(action_id)]
|
||||
if self._tooltip_widget is None:
|
||||
self._tooltip_widget = ActionMenuToolTip(view)
|
||||
|
||||
self._tooltip_widget.show_on_event(
|
||||
action_id, action_items, view, event
|
||||
)
|
||||
event.setAccepted(True)
|
||||
return True
|
||||
|
||||
def close_tooltip(self):
|
||||
if self._tooltip_widget is not None:
|
||||
self._tooltip_widget.close()
|
||||
|
||||
def mouse_entered_view(self):
|
||||
if self._tooltip_widget is not None:
|
||||
self._tooltip_widget.mouse_entered_view()
|
||||
|
||||
def mouse_left_view(self):
|
||||
if self._tooltip_widget is not None:
|
||||
self._tooltip_widget.mouse_left_view()
|
||||
|
||||
def _draw_animation(self, painter, option, index):
|
||||
grid_size = option.widget.gridSize()
|
||||
|
|
@ -430,29 +636,51 @@ class ActionsProxyModel(QtCore.QSortFilterProxyModel):
|
|||
return True
|
||||
|
||||
|
||||
class ActionsView(QtWidgets.QListView):
|
||||
settings_requested = QtCore.Signal(str)
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.setProperty("mode", "icon")
|
||||
self.setObjectName("IconView")
|
||||
self.setViewMode(QtWidgets.QListView.IconMode)
|
||||
self.setResizeMode(QtWidgets.QListView.Adjust)
|
||||
self.setSelectionMode(QtWidgets.QListView.NoSelection)
|
||||
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||
self.setWrapping(True)
|
||||
self.setGridSize(QtCore.QSize(70, 75))
|
||||
self.setIconSize(QtCore.QSize(30, 30))
|
||||
self.setSpacing(0)
|
||||
self.setWordWrap(True)
|
||||
self.setToolTipDuration(150)
|
||||
|
||||
delegate = ActionDelegate(self)
|
||||
self.setItemDelegate(delegate)
|
||||
|
||||
# Make view flickable
|
||||
flick = FlickCharm(parent=self)
|
||||
flick.activateOn(self)
|
||||
|
||||
self._flick = flick
|
||||
self._delegate = delegate
|
||||
|
||||
def enterEvent(self, event):
|
||||
super().enterEvent(event)
|
||||
self._delegate.mouse_entered_view()
|
||||
|
||||
def leaveEvent(self, event):
|
||||
super().leaveEvent(event)
|
||||
self._delegate.mouse_left_view()
|
||||
|
||||
|
||||
class ActionsWidget(QtWidgets.QWidget):
|
||||
def __init__(self, controller, parent):
|
||||
super(ActionsWidget, self).__init__(parent)
|
||||
super().__init__(parent)
|
||||
|
||||
self._controller = controller
|
||||
|
||||
view = QtWidgets.QListView(self)
|
||||
view.setProperty("mode", "icon")
|
||||
view.setObjectName("IconView")
|
||||
view.setViewMode(QtWidgets.QListView.IconMode)
|
||||
view.setResizeMode(QtWidgets.QListView.Adjust)
|
||||
view.setSelectionMode(QtWidgets.QListView.NoSelection)
|
||||
view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||
view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||
view.setWrapping(True)
|
||||
view.setGridSize(QtCore.QSize(70, 75))
|
||||
view.setIconSize(QtCore.QSize(30, 30))
|
||||
view.setSpacing(0)
|
||||
view.setWordWrap(True)
|
||||
|
||||
# Make view flickable
|
||||
flick = FlickCharm(parent=view)
|
||||
flick.activateOn(view)
|
||||
view = ActionsView(self)
|
||||
|
||||
model = ActionsQtModel(controller)
|
||||
|
||||
|
|
@ -460,9 +688,6 @@ class ActionsWidget(QtWidgets.QWidget):
|
|||
proxy_model.setSourceModel(model)
|
||||
view.setModel(proxy_model)
|
||||
|
||||
delegate = ActionDelegate(self)
|
||||
view.setItemDelegate(delegate)
|
||||
|
||||
layout = QtWidgets.QHBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.addWidget(view)
|
||||
|
|
@ -472,13 +697,12 @@ class ActionsWidget(QtWidgets.QWidget):
|
|||
animation_timer.timeout.connect(self._on_animation)
|
||||
|
||||
view.clicked.connect(self._on_clicked)
|
||||
view.customContextMenuRequested.connect(self._on_context_menu)
|
||||
view.settings_requested.connect(self._show_config_dialog)
|
||||
model.refreshed.connect(self._on_model_refresh)
|
||||
|
||||
self._animated_items = set()
|
||||
self._animation_timer = animation_timer
|
||||
|
||||
self._flick = flick
|
||||
self._view = view
|
||||
self._model = model
|
||||
self._proxy_model = proxy_model
|
||||
|
|
@ -572,37 +796,27 @@ class ActionsWidget(QtWidgets.QWidget):
|
|||
return
|
||||
|
||||
is_group = index.data(ACTION_IS_GROUP_ROLE)
|
||||
# TODO define and store what is default action for a group
|
||||
action_id = index.data(ACTION_ID_ROLE)
|
||||
|
||||
project_name = self._model.get_selected_project_name()
|
||||
folder_id = self._model.get_selected_folder_id()
|
||||
task_id = self._model.get_selected_task_id()
|
||||
if is_group:
|
||||
action_item = self._show_menu_on_group(action_id)
|
||||
if action_item is None:
|
||||
return
|
||||
|
||||
action_id = action_item.identifier
|
||||
action_label = action_item.full_label
|
||||
action_type = action_item.action_type
|
||||
addon_name = action_item.addon_name
|
||||
addon_version = action_item.addon_version
|
||||
else:
|
||||
action_label = index.data(QtCore.Qt.DisplayRole)
|
||||
action_type = index.data(ACTION_TYPE_ROLE)
|
||||
addon_name = index.data(ACTION_ADDON_NAME_ROLE)
|
||||
addon_version = index.data(ACTION_ADDON_VERSION_ROLE)
|
||||
|
||||
action_type = index.data(ACTION_TYPE_ROLE)
|
||||
if action_type == "webaction":
|
||||
action_item = self._model.get_action_item_by_id(action_id)
|
||||
context = WebactionContext(
|
||||
action_id,
|
||||
project_name,
|
||||
folder_id,
|
||||
task_id,
|
||||
addon_name,
|
||||
addon_version
|
||||
action_item.addon_name,
|
||||
action_item.add_version
|
||||
)
|
||||
self._controller.trigger_webaction(
|
||||
context, action_item.full_label
|
||||
)
|
||||
self._controller.trigger_webaction(context, action_label)
|
||||
else:
|
||||
self._controller.trigger_action(
|
||||
action_id, project_name, folder_id, task_id
|
||||
|
|
@ -610,57 +824,12 @@ class ActionsWidget(QtWidgets.QWidget):
|
|||
|
||||
self._start_animation(index)
|
||||
|
||||
def _show_menu_on_group(self, action_id):
|
||||
action_items = self._model.get_group_items(action_id)
|
||||
|
||||
menu = QtWidgets.QMenu(self)
|
||||
actions_mapping = {}
|
||||
|
||||
def on_settings_clicked(identifier):
|
||||
# Close menu
|
||||
menu.close()
|
||||
|
||||
# Show config dialog
|
||||
self._show_config_dialog(identifier, False)
|
||||
|
||||
for action_item in action_items:
|
||||
menu_action = ActionVariantAction(
|
||||
action_item.identifier,
|
||||
action_item.full_label,
|
||||
bool(action_item.config_fields),
|
||||
menu,
|
||||
)
|
||||
menu_action.settings_requested.connect(on_settings_clicked)
|
||||
menu.addAction(menu_action)
|
||||
actions_mapping[menu_action] = action_item
|
||||
|
||||
result = menu.exec_(QtGui.QCursor.pos())
|
||||
if not result:
|
||||
return None
|
||||
|
||||
return actions_mapping[result]
|
||||
|
||||
def _on_context_menu(self, point):
|
||||
"""Creates menu to force skip opening last workfile."""
|
||||
index = self._view.indexAt(point)
|
||||
if not index.isValid():
|
||||
return
|
||||
|
||||
action_id = index.data(ACTION_ID_ROLE)
|
||||
if not action_id:
|
||||
return
|
||||
is_group = index.data(ACTION_IS_GROUP_ROLE)
|
||||
self._show_config_dialog(action_id, is_group)
|
||||
|
||||
def _show_config_dialog(self, action_id, is_group):
|
||||
item = self._model.get_item_by_id(action_id)
|
||||
def _show_config_dialog(self, action_id):
|
||||
action_item = self._model.get_action_item_by_id(action_id)
|
||||
config_fields = self._model.get_action_config_fields(action_id)
|
||||
if not config_fields:
|
||||
return
|
||||
|
||||
addon_name = item.data(ACTION_ADDON_NAME_ROLE)
|
||||
addon_version = item.data(ACTION_ADDON_VERSION_ROLE)
|
||||
|
||||
project_name = self._model.get_selected_project_name()
|
||||
folder_id = self._model.get_selected_folder_id()
|
||||
task_id = self._model.get_selected_task_id()
|
||||
|
|
@ -669,8 +838,8 @@ class ActionsWidget(QtWidgets.QWidget):
|
|||
project_name=project_name,
|
||||
folder_id=folder_id,
|
||||
task_id=task_id,
|
||||
addon_name=addon_name,
|
||||
addon_version=addon_version,
|
||||
addon_name=action_item.addon_name,
|
||||
addon_version=action_item.addon_version,
|
||||
)
|
||||
values = self._controller.get_action_config_values(context)
|
||||
|
||||
|
|
@ -682,17 +851,8 @@ class ActionsWidget(QtWidgets.QWidget):
|
|||
)
|
||||
dialog.set_values(values)
|
||||
result = dialog.exec_()
|
||||
if result != QtWidgets.QDialog.Accepted:
|
||||
return
|
||||
new_values = dialog.get_values()
|
||||
if is_group:
|
||||
action_items = self._model.get_group_items(action_id)
|
||||
action_ids = [item.identifier for item in action_items]
|
||||
else:
|
||||
action_ids = [action_id]
|
||||
|
||||
for action_id in action_ids:
|
||||
context.identifier = action_id
|
||||
if result == QtWidgets.QDialog.Accepted:
|
||||
new_values = dialog.get_values()
|
||||
self._controller.set_action_config_values(context, new_values)
|
||||
|
||||
def _create_attrs_dialog(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue