diff --git a/pype/settings/entities/base_entity.py b/pype/settings/entities/base_entity.py index b2be6819d5..9003a66d76 100644 --- a/pype/settings/entities/base_entity.py +++ b/pype/settings/entities/base_entity.py @@ -695,6 +695,12 @@ class ItemEntity(BaseItemEntity): is_dynamic_item (bool): Entity should behave like dynamically created entity. """ + _default_label_wrap = { + "use_label_wrap": False, + "collapsible": True, + "collapsed": False + } + def __init__(self, schema_data, parent, is_dynamic_item=False): super(ItemEntity, self).__init__(schema_data) @@ -736,12 +742,50 @@ class ItemEntity(BaseItemEntity): self.key = self.schema_data.get("key") self.label = self.schema_data.get("label") + # GUI attributes + _default_label_wrap = self.__class__._default_label_wrap + for key, value in ItemEntity._default_label_wrap.items(): + if key not in _default_label_wrap: + self.log.warning( + "Class {} miss default label wrap key \"{}\"".format( + self.__class__.__name__, key + ) + ) + _default_label_wrap[key] = value + + use_label_wrap = self.schema_data.get("use_label_wrap") + if use_label_wrap is None: + if not self.label: + use_label_wrap = False + else: + use_label_wrap = _default_label_wrap["use_label_wrap"] + self.use_label_wrap = use_label_wrap + + # Used only if `use_label_wrap` is set to True + self.collapsible = self.schema_data.get( + "collapsible", + _default_label_wrap["collapsible"] + ) + self.collapsed = self.schema_data.get( + "collapsed", + _default_label_wrap["collapsed"] + ) + self._item_initalization() def save(self): """Call save on root item.""" self.root_item.save() + def schema_validations(self): + if not self.label and self.use_label_wrap: + raise ValueError(( + "{} Entity has set `use_label_wrap` to true but" + " does not have set `label`." + ).format(self.path)) + + super(ItemEntity, self).schema_validations() + def create_schema_object(self, *args, **kwargs): """Reference method for creation of entities defined in RootEntity.""" return self.root_item.create_schema_object(*args, **kwargs) diff --git a/pype/settings/entities/dict_immutable_keys_entity.py b/pype/settings/entities/dict_immutable_keys_entity.py index 0e6a98bdde..af0ddcb758 100644 --- a/pype/settings/entities/dict_immutable_keys_entity.py +++ b/pype/settings/entities/dict_immutable_keys_entity.py @@ -27,6 +27,11 @@ class DictImmutableKeysEntity(ItemEntity): are not real settings values but entities representing the value. """ schema_types = ["dict"] + _default_label_wrap = { + "use_label_wrap": True, + "collapsible": True, + "collapsed": True + } def __getitem__(self, key): """Return entity inder key.""" @@ -169,11 +174,6 @@ class DictImmutableKeysEntity(ItemEntity): "highlight_content", False ) self.show_borders = self.schema_data.get("show_borders", True) - self.collapsible = self.schema_data.get("collapsible", True) - self.collapsed = self.schema_data.get("collapsed", True) - - # Not yet implemented - self.use_label_wrap = self.schema_data.get("use_label_wrap") or True def get_child_path(self, child_obj): """Get hierarchical path of child entity. diff --git a/pype/settings/entities/dict_mutable_keys_entity.py b/pype/settings/entities/dict_mutable_keys_entity.py index 8fe71db2a3..2fd2b87311 100644 --- a/pype/settings/entities/dict_mutable_keys_entity.py +++ b/pype/settings/entities/dict_mutable_keys_entity.py @@ -29,6 +29,12 @@ class DictMutableKeysEntity(EndpointEntity): - clear callbacks """ schema_types = ["dict-modifiable"] + _default_label_wrap = { + "use_label_wrap": True, + "collapsible": True, + "collapsed": True + } + _miss_arg = object() def __getitem__(self, key): @@ -174,8 +180,6 @@ class DictMutableKeysEntity(EndpointEntity): self.hightlight_content = ( self.schema_data.get("highlight_content") or False ) - self.collapsible = self.schema_data.get("collapsible", True) - self.collapsed = self.schema_data.get("collapsed", True) object_type = self.schema_data["object_type"] if not isinstance(object_type, dict): diff --git a/pype/settings/entities/list_entity.py b/pype/settings/entities/list_entity.py index 07221929b7..752347489a 100644 --- a/pype/settings/entities/list_entity.py +++ b/pype/settings/entities/list_entity.py @@ -15,6 +15,11 @@ from .exceptions import ( class ListEntity(EndpointEntity): schema_types = ["list"] + _default_label_wrap = { + "use_label_wrap": False, + "collapsible": True, + "collapsed": False + } def __iter__(self): for item in self.children: @@ -139,12 +144,6 @@ class ListEntity(EndpointEntity): # Value that was set on set_override_state self.initial_value = [] - # GUI attributes - self.use_label_wrap = self.schema_data.get("use_label_wrap") or False - # Used only if `use_label_wrap` is set to True - self.collapsible = self.schema_data.get("collapsible") or True - self.collapsed = self.schema_data.get("collapsed") or False - def schema_validations(self): super(ListEntity, self).schema_validations() diff --git a/pype/settings/entities/schemas/projects_schema/schema_project_global.json b/pype/settings/entities/schemas/projects_schema/schema_project_global.json index e6a1a6ff96..1733e04f67 100644 --- a/pype/settings/entities/schemas/projects_schema/schema_project_global.json +++ b/pype/settings/entities/schemas/projects_schema/schema_project_global.json @@ -14,15 +14,10 @@ "name": "schema_global_tools" }, { - "type": "collapsible-wrap", + "type": "raw-json", "label": "Project Folder Structure", - "children": [ - { - "type": "raw-json", - "label": " ", - "key": "project_folder_structure" - } - ] + "key": "project_folder_structure", + "use_label_wrap": true }, { "type": "schema", diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 692e9a9859..4010b8ab20 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -1,4 +1,6 @@ from Qt import QtWidgets, QtGui, QtCore +from .lib import CHILD_OFFSET +from .widgets import ExpandingWidget class BaseWidget(QtWidgets.QWidget): @@ -161,27 +163,92 @@ class BaseWidget(QtWidgets.QWidget): class InputWidget(BaseWidget): + def create_ui(self): + if self.entity.use_label_wrap: + label = None + self._create_label_wrap_ui() + else: + label = self.entity.label + self.label_widget = None + self.body_widget = None + self.content_widget = self + self.content_layout = self._create_layout(self) + + self._add_inputs_to_layout() + + self.entity_widget.add_widget_to_layout(self, label) + + def _create_label_wrap_ui(self): + content_widget = QtWidgets.QWidget(self) + content_widget.setObjectName("ContentWidget") + + content_widget.setProperty("content_state", "") + content_layout_margins = (CHILD_OFFSET, 5, 0, 0) + + body_widget = ExpandingWidget(self.entity.label, self) + label_widget = body_widget.label_widget + body_widget.set_content_widget(content_widget) + + content_layout = self._create_layout(content_widget) + content_layout.setContentsMargins(*content_layout_margins) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(0) + main_layout.addWidget(body_widget) + + self.label_widget = label_widget + self.body_widget = body_widget + self.content_widget = content_widget + self.content_layout = content_layout + + def _create_layout(self, parent_widget): + layout = QtWidgets.QHBoxLayout(parent_widget) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(5) + return layout + + def _add_inputs_to_layout(self): + raise NotImplementedError( + "Method `_add_inputs_to_layout` not implemented {}".format( + self.__class__.__name__ + ) + ) + def update_style(self): has_unsaved_changes = self.entity.has_unsaved_changes if not has_unsaved_changes and self.entity.group_item: has_unsaved_changes = self.entity.group_item.has_unsaved_changes - state = self.get_style_state( + style_state = self.get_style_state( self.is_invalid, has_unsaved_changes, self.entity.has_project_override, self.entity.has_studio_override ) - if self._style_state == state: + if self._style_state == style_state: return - self._style_state = state + self._style_state = style_state - self.input_field.setProperty("input-state", state) + self.input_field.setProperty("input-state", style_state) self.input_field.style().polish(self.input_field) if self.label_widget: - self.label_widget.setProperty("state", state) + self.label_widget.setProperty("state", style_state) self.label_widget.style().polish(self.label_widget) + if self.body_widget: + if style_state: + child_style_state = "child-{}".format(style_state) + else: + child_style_state = "" + + self.body_widget.side_line_widget.setProperty( + "state", child_style_state + ) + self.body_widget.side_line_widget.style().polish( + self.body_widget.side_line_widget + ) + @property def child_invalid(self): return self.is_invalid diff --git a/pype/tools/settings/settings/widgets/item_widgets.py b/pype/tools/settings/settings/widgets/item_widgets.py index e0c97e0c80..cf290eb417 100644 --- a/pype/tools/settings/settings/widgets/item_widgets.py +++ b/pype/tools/settings/settings/widgets/item_widgets.py @@ -24,18 +24,27 @@ from .lib import CHILD_OFFSET class DictImmutableKeysWidget(BaseWidget): def create_ui(self): - self._child_style_state = "" self.input_fields = [] self.checkbox_child = None - if not self.entity.is_dynamic_item and not self.entity.label: - self._ui_item_without_label() + + self.label_widget = None + self.body_widget = None + self.content_widget = None + self.content_layout = None + + label = None + if self.entity.is_dynamic_item: + self._ui_as_dynamic_item() + + elif self.entity.use_label_wrap: + self._ui_label_wrap() + self.checkbox_child = self.entity.non_gui_children.get( + self.entity.checkbox_key + ) else: - self._ui_item_or_as_widget() - if not self.entity.is_dynamic_item: - self.checkbox_child = self.entity.non_gui_children.get( - self.entity.checkbox_key - ) + self._ui_item_base() + label = self.entity.label self._parent_widget_by_entity_id = {} self._added_wrapper_ids = set() @@ -50,7 +59,7 @@ class DictImmutableKeysWidget(BaseWidget): ) ) - self.entity_widget.add_widget_to_layout(self) + self.entity_widget.add_widget_to_layout(self, label) def _prepare_entity_layouts(self, children, widget): for child in children: @@ -74,68 +83,73 @@ class DictImmutableKeysWidget(BaseWidget): self._prepare_entity_layouts(child["children"], wrapper) - def _ui_item_without_label(self): + def _ui_item_base(self): self.setObjectName("DictInvisible") - self.body_widget = None self.content_widget = self self.content_layout = QtWidgets.QGridLayout(self) self.content_layout.setContentsMargins(0, 0, 0, 0) self.content_layout.setSpacing(5) - def _ui_item_or_as_widget(self): + def _ui_as_dynamic_item(self): content_widget = QtWidgets.QWidget(self) + content_widget.setObjectName("DictAsWidgetBody") - if self.entity.is_dynamic_item: - content_widget.setObjectName("DictAsWidgetBody") - show_borders = str(int(self.entity.show_borders)) - content_widget.setProperty("show_borders", show_borders) - content_layout_margins = (5, 5, 5, 5) - main_layout_spacing = 5 - body_widget = None - label_widget = QtWidgets.QLabel(self.entity.label) + show_borders = str(int(self.entity.show_borders)) + content_widget.setProperty("show_borders", show_borders) + label_widget = QtWidgets.QLabel(self.entity.label) + + content_layout = QtWidgets.QGridLayout(content_widget) + content_layout.setContentsMargins(5, 5, 5, 5) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(5) + main_layout.addWidget(content_widget) + + self.label_widget = label_widget + self.content_widget = content_widget + self.content_layout = content_layout + + def _ui_label_wrap(self): + content_widget = QtWidgets.QWidget(self) + content_widget.setObjectName("ContentWidget") + + if self.entity.highlight_content: + content_state = "hightlighted" + bottom_margin = 5 else: - content_widget.setObjectName("ContentWidget") - if self.entity.highlight_content: - content_state = "hightlighted" - bottom_margin = 5 - else: - content_state = "" - bottom_margin = 0 - content_widget.setProperty("content_state", content_state) - content_layout_margins = (CHILD_OFFSET, 5, 0, bottom_margin) - main_layout_spacing = 0 + content_state = "" + bottom_margin = 0 + content_widget.setProperty("content_state", content_state) + content_layout_margins = (CHILD_OFFSET, 5, 0, bottom_margin) - body_widget = ExpandingWidget(self.entity.label, self) - label_widget = body_widget.label_widget - body_widget.set_content_widget(content_widget) + body_widget = ExpandingWidget(self.entity.label, self) + label_widget = body_widget.label_widget + body_widget.set_content_widget(content_widget) content_layout = QtWidgets.QGridLayout(content_widget) content_layout.setContentsMargins(*content_layout_margins) main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) - main_layout.setSpacing(main_layout_spacing) - if not body_widget: - main_layout.addWidget(content_widget) - else: - main_layout.addWidget(body_widget) + main_layout.setSpacing(0) + main_layout.addWidget(body_widget) self.label_widget = label_widget self.body_widget = body_widget self.content_widget = content_widget self.content_layout = content_layout - if body_widget: - if len(self.input_fields) == 1 and self.checkbox_widget: - body_widget.hide_toolbox(hide_content=True) + if len(self.input_fields) == 1 and self.checkbox_widget: + body_widget.hide_toolbox(hide_content=True) - elif self.entity.collapsible: - if not self.entity.collapsed: - body_widget.toggle_content() - else: - body_widget.hide_toolbox(hide_content=False) + elif self.entity.collapsible: + if not self.entity.collapsed: + body_widget.toggle_content() + else: + body_widget.hide_toolbox(hide_content=False) def add_widget_to_layout(self, widget, label=None): if self.checkbox_child and widget.entity is self.checkbox_child: @@ -174,7 +188,7 @@ class DictImmutableKeysWidget(BaseWidget): for input_field in self.input_fields: input_field.hierarchical_style_update() - def update_style(self, is_overriden=None): + def update_style(self): if not self.body_widget and not self.label_widget: return @@ -188,36 +202,8 @@ class DictImmutableKeysWidget(BaseWidget): has_project_override = self.entity.has_project_override has_studio_override = self.entity.has_studio_override - is_invalid = self.is_invalid - if self.body_widget: - child_style_state = self.get_style_state( - is_invalid, - has_unsaved_changes, - has_project_override, - has_studio_override - ) - - if child_style_state: - child_style_state = "child-{}".format(child_style_state) - - if self._child_style_state != child_style_state: - self.body_widget.side_line_widget.setProperty( - "state", child_style_state - ) - self.body_widget.side_line_widget.style().polish( - self.body_widget.side_line_widget - ) - self._child_style_state = child_style_state - - # There is nothing to care if there is no label - if not self.label_widget: - return - # Don't change label if is not group or under group item - if not self.entity.is_group and not self.entity.group_item: - return - style_state = self.get_style_state( - is_invalid, + self.is_invalid, has_unsaved_changes, has_project_override, has_studio_override @@ -225,11 +211,32 @@ class DictImmutableKeysWidget(BaseWidget): if self._style_state == style_state: return + self._style_state = style_state + + if self.body_widget: + if style_state: + child_style_state = "child-{}".format(style_state) + else: + child_style_state = "" + + self.body_widget.side_line_widget.setProperty( + "state", child_style_state + ) + self.body_widget.side_line_widget.style().polish( + self.body_widget.side_line_widget + ) + + # There is nothing to care if there is no label + if not self.label_widget: + return + + # Don't change label if is not group or under group item + if not self.entity.is_group and not self.entity.group_item: + return + self.label_widget.setProperty("state", style_state) self.label_widget.style().polish(self.label_widget) - self._style_state = style_state - def _on_entity_change(self): pass @@ -252,26 +259,23 @@ class DictImmutableKeysWidget(BaseWidget): class BoolWidget(InputWidget): - def create_ui(self): + def _add_inputs_to_layout(self): checkbox_height = self.style().pixelMetric( QtWidgets.QStyle.PM_IndicatorHeight ) - self.input_field = NiceCheckbox(height=checkbox_height, parent=self) + self.input_field = NiceCheckbox( + height=checkbox_height, parent=self.content_widget + ) - spacer = QtWidgets.QWidget(self) + spacer = QtWidgets.QWidget(self.content_widget) spacer.setAttribute(QtCore.Qt.WA_TranslucentBackground) - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - layout.addWidget(self.input_field, 0) - layout.addWidget(spacer, 1) + self.content_layout.addWidget(self.input_field, 0) + self.content_layout.addWidget(spacer, 1) self.setFocusProxy(self.input_field) self.input_field.stateChanged.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) def _on_entity_change(self): if self.entity.value != self.input_field.isChecked(): @@ -287,12 +291,12 @@ class BoolWidget(InputWidget): class TextWidget(InputWidget): - def create_ui(self): + def _add_inputs_to_layout(self): multiline = self.entity.multiline if multiline: - self.input_field = QtWidgets.QPlainTextEdit(self) + self.input_field = QtWidgets.QPlainTextEdit(self.content_widget) else: - self.input_field = QtWidgets.QLineEdit(self) + self.input_field = QtWidgets.QLineEdit(self.content_widget) placeholder_text = self.entity.placeholder_text if placeholder_text: @@ -304,15 +308,10 @@ class TextWidget(InputWidget): if multiline: layout_kwargs["alignment"] = QtCore.Qt.AlignTop - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - layout.addWidget(self.input_field, 1, **layout_kwargs) + self.content_layout.addWidget(self.input_field, 1, **layout_kwargs) self.input_field.textChanged.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) - def _on_entity_change(self): if self.entity.value != self.input_value(): self.set_entity_value() @@ -337,26 +336,20 @@ class TextWidget(InputWidget): class NumberWidget(InputWidget): - def create_ui(self): - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - + def _add_inputs_to_layout(self): kwargs = { "minimum": self.entity.minimum, "maximum": self.entity.maximum, "decimal": self.entity.decimal } - self.input_field = NumberSpinBox(self, **kwargs) + self.input_field = NumberSpinBox(self.content_widget, **kwargs) self.setFocusProxy(self.input_field) - layout.addWidget(self.input_field, 1) + self.content_layout.addWidget(self.input_field, 1) self.input_field.valueChanged.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) - def _on_entity_change(self): if self.entity.value != self.input_field.value(): self.set_entity_value() @@ -421,12 +414,8 @@ class RawJsonInput(QtWidgets.QPlainTextEdit): class RawJsonWidget(InputWidget): - def create_ui(self): - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - self.input_field = RawJsonInput(self) + def _add_inputs_to_layout(self): + self.input_field = RawJsonInput(self.content_widget) self.input_field.setSizePolicy( QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding @@ -434,10 +423,11 @@ class RawJsonWidget(InputWidget): self.setFocusProxy(self.input_field) - layout.addWidget(self.input_field, 1, alignment=QtCore.Qt.AlignTop) + self.content_layout.addWidget( + self.input_field, 1, alignment=QtCore.Qt.AlignTop + ) self.input_field.textChanged.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) def set_entity_value(self): self.input_field.set_value(self.entity.value) @@ -465,31 +455,26 @@ class RawJsonWidget(InputWidget): class EnumeratorWidget(InputWidget): - def create_ui(self): - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - + def _add_inputs_to_layout(self): if self.entity.multiselection: self.input_field = MultiSelectionComboBox( - placeholder=self.entity.placeholder, parent=self + placeholder=self.entity.placeholder, parent=self.content_widget ) model = self.input_field.model() for idx in range(self.input_field.count()): model.item(idx).setCheckable(True) else: - self.input_field = ComboBox(self) + self.input_field = ComboBox(self.content_widget) for enum_item in self.entity.enum_items: for value, label in enum_item.items(): self.input_field.addItem(label, value) - layout.addWidget(self.input_field, 0) + self.content_layout.addWidget(self.input_field, 0) self.setFocusProxy(self.input_field) self.input_field.value_changed.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) def _on_entity_change(self): if self.entity.value != self.input_field.value(): @@ -574,12 +559,8 @@ class PathWidget(BaseWidget): class PathInputWidget(InputWidget): - def create_ui(self, label_widget=None): - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - - self.input_field = QtWidgets.QLineEdit(self) + def _add_inputs_to_layout(self): + self.input_field = QtWidgets.QLineEdit(self.content_widget) self.args_input_field = None if self.entity.with_arguments: self.input_field.setPlaceholderText("Executable path") @@ -587,15 +568,13 @@ class PathInputWidget(InputWidget): self.args_input_field.setPlaceholderText("Arguments") self.setFocusProxy(self.input_field) - layout.addWidget(self.input_field, 8) + self.content_layout.addWidget(self.input_field, 8) self.input_field.textChanged.connect(self._on_value_change) if self.args_input_field: - layout.addWidget(self.args_input_field, 2) + self.content_layout.addWidget(self.args_input_field, 2) self.args_input_field.textChanged.connect(self._on_value_change) - self.entity_widget.add_widget_to_layout(self, self.entity.label) - def _on_entity_change(self): if self.entity.value != self.input_value(): self.set_entity_value()