# Implementation Plan: GUI User-Friendliness Enhancements This document outlines the plan for implementing three key GUI improvements for the Asset Processor Tool, focusing on user-friendliness and workflow efficiency. **Target Audience:** Developers implementing these features. **Status:** Planning Phase ## Feature 1: Editable Asset Name **Goal:** Allow users to edit the name of an asset directly in the main view, and automatically update the 'Target Asset' field of all associated child files to reflect the new name. **Affected Components:** * `gui/unified_view_model.py` (`UnifiedViewModel`) * `gui/delegates.py` (`LineEditDelegate`) * `gui/main_window.py` (or view setup location) * `rule_structure.py` (`AssetRule`, `FileRule`) * Potentially a new handler or modifications to `gui/asset_restructure_handler.py` **Implementation Steps:** 1. **Enable Editing in Model (`UnifiedViewModel`):** * Modify `flags()`: For an index pointing to an `AssetRule`, return `Qt.ItemIsEditable` in addition to default flags when `index.column()` is `COL_NAME`. * Modify `setData()`: * Add logic to handle `isinstance(item, AssetRule)` and `column == self.COL_NAME`. * Get the `new_asset_name` from the `value`. * **Validation:** Before proceeding, check if an `AssetRule` with `new_asset_name` already exists within the same parent `SourceRule`. If so, log a warning and return `False` to prevent duplicate names. * Store the `old_asset_name = item.asset_name`. * If `new_asset_name` is valid and different from `old_asset_name`: * Update `item.asset_name = new_asset_name`. * Set `changed = True`. * **Crucial - Child Update:** Iterate through *all* `SourceRule`s, `AssetRule`s, and `FileRule`s in the model (`self._source_rules`). For each `FileRule` found where `file_rule.target_asset_name_override == old_asset_name`, update `file_rule.target_asset_name_override = new_asset_name`. Emit `dataChanged` for the `COL_TARGET_ASSET` index of each modified `FileRule`. (See Potential Challenges regarding performance). * Emit `dataChanged` for the edited `AssetRule`'s `COL_NAME` index. * Return `changed`. * **(Alternative Signal Approach):** Instead of performing the child update directly in `setData`, emit a new signal like `assetNameChanged = Signal(QModelIndex, str, str)` carrying the `AssetRule` index, old name, and new name. A dedicated handler would connect to this signal to perform the child updates. This improves separation of concerns. 2. **Assign Delegate (`main_window.py` / View Setup):** * Ensure the `LineEditDelegate` is assigned to the view for the `COL_NAME` using `view.setItemDelegateForColumn(UnifiedViewModel.COL_NAME, line_edit_delegate_instance)`. 3. **Handling Child Updates (if using Signal Approach):** * Create a new handler class (e.g., `AssetNameChangeHandler`) or add a slot to `AssetRestructureHandler`. * Connect the `UnifiedViewModel.assetNameChanged` signal to this slot. * The slot receives the `AssetRule` index, old name, and new name. It iterates through the model's `FileRule`s, updates their `target_asset_name_override` where it matches the old name, and emits `dataChanged` for those files. **Data Model Impact:** * `AssetRule.asset_name` becomes directly mutable via the GUI. * The relationship between files and their intended parent asset (represented by `FileRule.target_asset_name_override`) is maintained automatically when the parent asset's name changes. **Potential Challenges/Considerations:** * **Performance:** The child update logic requires iterating through potentially all files in the model. For very large datasets, this could be slow. Consider optimizing by maintaining an index/lookup map (`Dict[str, List[FileRule]]`) mapping target asset override names to the list of `FileRule`s using them. This map would need careful updating whenever overrides change or files are moved. * **Duplicate Asset Names:** The plan includes basic validation in `setData`. Robust handling (e.g., user feedback, preventing the edit) is needed. * **Undo/Redo:** Reversing an asset name change requires reverting the name *and* reverting all the child `target_asset_name_override` changes, adding complexity. * **Scope of Child Update:** The current plan updates *any* `FileRule` whose override matches the old name. Confirm if this update should be restricted only to files originally under the renamed asset or within the same `SourceRule`. The current approach seems most logical based on how `target_asset_name_override` works. ```mermaid sequenceDiagram participant User participant View participant LineEditDelegate participant UnifiedViewModel participant AssetNameChangeHandler User->>View: Edits AssetRule Name in COL_NAME View->>LineEditDelegate: setModelData(editor, model, index) LineEditDelegate->>UnifiedViewModel: setData(index, new_name, EditRole) UnifiedViewModel->>UnifiedViewModel: Validate new_name (no duplicates) UnifiedViewModel->>UnifiedViewModel: Update AssetRule.asset_name alt Signal Approach UnifiedViewModel->>AssetNameChangeHandler: emit assetNameChanged(index, old_name, new_name) AssetNameChangeHandler->>UnifiedViewModel: Iterate through FileRules loop For each FileRule where target_override == old_name AssetNameChangeHandler->>UnifiedViewModel: Update FileRule.target_asset_name_override = new_name UnifiedViewModel->>View: emit dataChanged(file_rule_target_index) end else Direct Approach in setData UnifiedViewModel->>UnifiedViewModel: Iterate through FileRules loop For each FileRule where target_override == old_name UnifiedViewModel->>UnifiedViewModel: Update FileRule.target_asset_name_override = new_name UnifiedViewModel->>View: emit dataChanged(file_rule_target_index) end end UnifiedViewModel->>View: emit dataChanged(asset_rule_name_index) ``` ## Feature 2: Item Type Field Conversion **Goal:** Replace the `QComboBox` delegate for the "Item Type" column (for `FileRule`s) with a `QLineEdit` that provides auto-suggestions based on defined file types, similar to the existing "Supplier" field. **Affected Components:** * `gui/main_window.py` (or view setup location) * `gui/delegates.py` (Requires a new delegate) * `gui/unified_view_model.py` (`UnifiedViewModel`) * `config/app_settings.json` (Source of file type definitions) **Implementation Steps:** 1. **Create New Delegate (`delegates.py`):** * Create a new class `ItemTypeSearchDelegate(QStyledItemDelegate)`. * **`createEditor(self, parent, option, index)`:** * Create a `QLineEdit` instance. * Get the list of valid item type keys: `item_keys = index.model()._file_type_keys` (add error handling). * Create a `QCompleter` using `item_keys` and set it on the `QLineEdit` (configure case sensitivity, filter mode, completion mode as in `SupplierSearchDelegate`). * Return the editor. * **`setEditorData(self, editor, index)`:** * Get the current value using `index.model().data(index, Qt.EditRole)`. * Set the editor's text (`editor.setText(str(value) if value is not None else "")`). * **`setModelData(self, editor, model, index)`:** * Get the `final_text = editor.text().strip()`. * Determine the `value_to_set = final_text if final_text else None`. * Call `model.setData(index, value_to_set, Qt.EditRole)`. * **Important:** Unlike `SupplierSearchDelegate`, do *not* add `final_text` to the list of known types or save anything back to config. Suggestions are strictly based on `config/app_settings.json`. * **`updateEditorGeometry(self, editor, option, index)`:** * Standard implementation: `editor.setGeometry(option.rect)`. 2. **Assign Delegate (`main_window.py` / View Setup):** * Instantiate the new `ItemTypeSearchDelegate`. * Find where delegates are set for the view. * Replace the `ComboBoxDelegate` assignment for `UnifiedViewModel.COL_ITEM_TYPE` with the new `ItemTypeSearchDelegate` instance: `view.setItemDelegateForColumn(UnifiedViewModel.COL_ITEM_TYPE, item_type_search_delegate_instance)`. **Data Model Impact:** * None. The underlying data (`FileRule.item_type_override`) and its handling remain the same. Only the GUI editor changes. **Potential Challenges/Considerations:** * None significant. This is a relatively straightforward replacement of one delegate type with another, leveraging existing patterns from `SupplierSearchDelegate` and data loading from `UnifiedViewModel`. ## Feature 3: Drag-and-Drop File Re-parenting **Goal:** Enable users to drag one or more `FileRule` rows and drop them onto an `AssetRule` row to change the parent asset of the dragged files. **Affected Components:** * `gui/main_panel_widget.py` or `gui/main_window.py` (View management) * `gui/unified_view_model.py` (`UnifiedViewModel`) **Implementation Steps:** 1. **Enable Drag/Drop in View (`main_panel_widget.py` / `main_window.py`):** * Get the `QTreeView` instance (`view`). * `view.setSelectionMode(QAbstractItemView.ExtendedSelection)` (Allow selecting multiple files) * `view.setDragEnabled(True)` * `view.setAcceptDrops(True)` * `view.setDropIndicatorShown(True)` * `view.setDefaultDropAction(Qt.MoveAction)` * `view.setDragDropMode(QAbstractItemView.InternalMove)` 2. **Implement Drag/Drop Support in Model (`UnifiedViewModel`):** * **`flags(self, index)`:** * Modify to include `Qt.ItemIsDragEnabled` if `index.internalPointer()` is a `FileRule`. * Modify to include `Qt.ItemIsDropEnabled` if `index.internalPointer()` is an `AssetRule`. * Return the combined flags. * **`supportedDropActions(self)`:** * Return `Qt.MoveAction`. * **`mimeData(self, indexes)`:** * Create `QMimeData`. * Encode information about the dragged rows (which must be `FileRule`s). Store a list of tuples, each containing `(source_parent_row, source_parent_col, source_row)` for each valid `FileRule` index in `indexes`. Use a custom MIME type (e.g., `"application/x-filerule-index-list"`). * Return the `QMimeData`. * **`canDropMimeData(self, data, action, row, column, parent)`:** * Check if `action == Qt.MoveAction`. * Check if `data.hasFormat("application/x-filerule-index-list")`. * Check if `parent.isValid()` and `parent.internalPointer()` is an `AssetRule`. * Return `True` if all conditions met, `False` otherwise. * **`dropMimeData(self, data, action, row, column, parent)`:** * Check `action` and MIME type again for safety. * Get the target `AssetRule` item: `target_asset = parent.internalPointer()`. If not an `AssetRule`, return `False`. * Decode the `QMimeData` to get the list of source index information. * Create a list `files_to_move = []` containing the actual `QModelIndex` objects for the source `FileRule`s (reconstruct them using the decoded info and `self.index()`). * Iterate through `files_to_move`: * Get the `source_file_index`. * Get the `file_item = source_file_index.internalPointer()`. * Get the `old_parent_asset = getattr(file_item, 'parent_asset', None)`. * If `target_asset != old_parent_asset`: * Call `self.moveFileRule(source_file_index, parent)`. This handles the actual move within the model structure and emits `beginMoveRows`/`endMoveRows`. * **After successful move:** Update the file's override: `file_item.target_asset_name_override = target_asset.asset_name`. * Emit `self.dataChanged.emit(moved_file_index, moved_file_index, [Qt.DisplayRole, Qt.EditRole])` for the `COL_TARGET_ASSET` column of the *now moved* file (get its new index). * **Cleanup:** After the loop, identify any original parent `AssetRule`s that became empty as a result of the moves. Call `self.removeAssetRule(empty_asset_rule)` for each. * Return `True`. **Data Model Impact:** * Changes the parentage of `FileRule` items within the model's internal structure. * Updates `FileRule.target_asset_name_override` to match the `asset_name` of the new parent `AssetRule`, ensuring consistency between the visual structure and the override field. **Potential Challenges/Considerations:** * **MIME Data Encoding/Decoding:** Ensure the index information is reliably encoded and decoded, especially handling potential model changes between drag start and drop. Using persistent IDs instead of row/column numbers might be more robust if available. * **Cleanup Logic:** Reliably identifying and removing empty parent assets after potentially moving multiple files from different original parents requires careful tracking. * **Transactionality:** If moving multiple files and one part fails, should the whole operation roll back? The current plan doesn't explicitly handle this; errors are logged, and subsequent steps might proceed. * **Interaction with `AssetRestructureHandler`:** The plan suggests handling the move and override update directly within `dropMimeData`. This means the existing `AssetRestructureHandler` won't be triggered by the override change *during* the drop. Ensure the cleanup logic (removing empty parents) is correctly handled either in `dropMimeData` or by ensuring `moveFileRule` emits signals that the handler *can* use for cleanup.