Compare commits

...

2 Commits

Author SHA1 Message Date
h
f7b8caf86c fix: preserve category/manufacturer selection when focusing and highlighting
- Add internal state tracking (_active_category, _active_manufacturer) to Sidebar
- Cmd+3/4 now restores previously selected category/manufacturer instead of always selecting first
- Manufacturer gray highlight remains when clicking a plugin (filter still active)
- Pressing Enter on plugin or sidebar now highlights plugin's categories
- All focus/selection uses proper selectionModel.setCurrentIndex with ClearAndSelect flag
2026-01-31 13:46:44 +01:00
h
404bde0a9c feat: display categories in user-defined order
Replace alphabetical sorting with user-defined order from logic-plugin-manager.
Categories now display according to their index property, preserving both
top-level and nested category ordering as specified in Logic Pro.
2026-01-31 13:46:33 +01:00
3 changed files with 87 additions and 7 deletions

View File

@@ -290,7 +290,7 @@ class CategoryTreeModel(QAbstractItemModel):
top_level_item = CategoryTreeItem("Top Level", "Top Level", self._root)
self._root.append_child(top_level_item)
for cat_path in sorted(categories):
for cat_path in categories:
parts = cat_path.split(":")
current_path = ""
parent_item = self._root
@@ -305,8 +305,22 @@ class CategoryTreeModel(QAbstractItemModel):
parent_item = category_items[current_path]
self._sort_category_tree(self._root, logic)
self.endResetModel()
def _sort_category_tree(self, item: CategoryTreeItem, logic: Logic) -> None:
def get_sort_key(path: str) -> tuple[int, str]:
if path in logic.categories:
return (logic.categories[path].index, path.lower())
return (2**31 - 1, path.lower())
top_level = [c for c in item.children if c.full_path == "Top Level"]
others = [c for c in item.children if c.full_path != "Top Level"]
others.sort(key=lambda c: get_sort_key(c.full_path))
item.children = top_level + others
for child in item.children:
self._sort_category_tree(child, logic)
def index(
self, row: int, column: int, parent: QModelIndex | None = None
) -> QModelIndex:

View File

@@ -34,6 +34,8 @@ KVK_K = 0x28
class _VimTableView(QTableView):
enter_pressed = Signal()
def _select_row(self, row: int) -> None:
index = self.model().index(row, 0)
self.selectionModel().setCurrentIndex(
@@ -58,6 +60,10 @@ class _VimTableView(QTableView):
self._select_row(current.row() - 1)
event.accept()
return
if key in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
self.enter_pressed.emit()
event.accept()
return
super().keyPressEvent(event)
@@ -131,6 +137,7 @@ class PluginTableView(QWidget):
layout.addWidget(self._table, 1)
self._table.selectionModel().currentChanged.connect(self._on_current_changed)
self._table.enter_pressed.connect(self._on_enter_pressed)
def _on_current_changed(self, current: QModelIndex, _previous: QModelIndex) -> None:
if current.isValid():
@@ -138,6 +145,13 @@ class PluginTableView(QWidget):
if plugin:
self.plugin_selected.emit(plugin)
def _on_enter_pressed(self) -> None:
current = self._table.currentIndex()
if current.isValid():
plugin = self._model.get_plugin(current.row())
if plugin:
self.plugin_selected.emit(plugin)
def set_plugins(self, logic: Logic) -> None:
self._model.set_plugins(logic)
self._resize_columns()
@@ -175,6 +189,12 @@ class PluginTableView(QWidget):
if not has_selection and self._model.rowCount() > 0:
self._table.selectRow(0)
current = self._table.currentIndex()
if current.isValid():
plugin = self._model.get_plugin(current.row())
if plugin:
self.plugin_selected.emit(plugin)
def _on_search_escape(self) -> None:
self._table.setFocus()

View File

@@ -257,6 +257,9 @@ class Sidebar(QWidget):
super().__init__(parent)
self.setMinimumWidth(200)
self._active_category: str | None = None
self._active_manufacturer: str | None = None
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
@@ -392,22 +395,30 @@ class Sidebar(QWidget):
self._uncategorized.set_selected(False)
def _on_show_all_clicked(self) -> None:
self._active_category = "Show All"
self._active_manufacturer = None
self._clear_selections()
self.category_selected.emit("Show All")
def _on_uncategorized_clicked(self) -> None:
self._active_category = None
self._active_manufacturer = None
self._clear_selections()
self.category_selected.emit(None)
def _on_category_clicked(self, index: QModelIndex) -> None:
full_path = index.data(Qt.ItemDataRole.UserRole)
if full_path:
self._active_category = full_path
self._active_manufacturer = None
self._manufacturer_list.clearSelection()
self.category_selected.emit(full_path)
def _on_manufacturer_clicked(self, index: QModelIndex) -> None:
manufacturer = index.data(Qt.ItemDataRole.DisplayRole)
if manufacturer:
self._active_manufacturer = manufacturer
self._active_category = None
self._category_tree.clearSelection()
self.manufacturer_selected.emit(manufacturer)
@@ -431,26 +442,58 @@ class Sidebar(QWidget):
self._manufacturer_search.clear()
def select_show_all(self) -> None:
self._active_category = "Show All"
self._active_manufacturer = None
self._clear_selections()
self.category_selected.emit("Show All")
def select_uncategorized(self) -> None:
self._active_category = None
self._active_manufacturer = None
self._clear_selections()
self._uncategorized.set_selected(True)
self.category_selected.emit(None)
def focus_category_tree(self) -> None:
self._category_tree.setFocus()
top_level_index = self._category_model.index_for_path("Top Level")
if top_level_index.isValid():
self._category_tree.setCurrentIndex(top_level_index)
if self._active_category and self._active_category not in ("Show All", None):
target_path = self._active_category
else:
target_path = "Top Level"
target_index = self._category_model.index_for_path(target_path)
if target_index.isValid():
parent = target_index.parent()
while parent.isValid():
self._category_tree.expand(parent)
parent = parent.parent()
self._category_tree.selectionModel().setCurrentIndex(
target_index,
self._category_tree.selectionModel().SelectionFlag.ClearAndSelect,
)
def focus_manufacturer_list(self) -> None:
self._manufacturer_list.setFocus()
if not self._manufacturer_list.selectionModel().hasSelection():
target_index = None
if self._active_manufacturer:
for row in range(self._manufacturer_proxy.rowCount()):
index = self._manufacturer_proxy.index(row, 0)
if index.data(Qt.ItemDataRole.DisplayRole) == self._active_manufacturer:
target_index = index
break
if target_index is None:
first_index = self._manufacturer_proxy.index(0, 0)
if first_index.isValid():
self._manufacturer_list.setCurrentIndex(first_index)
target_index = first_index
if target_index is not None:
self._manufacturer_list.selectionModel().setCurrentIndex(
target_index,
self._manufacturer_list.selectionModel().SelectionFlag.ClearAndSelect,
)
def _on_header_dragged(self, delta: int) -> None:
sizes = self._splitter.sizes()
@@ -461,7 +504,10 @@ class Sidebar(QWidget):
def highlight_categories(self, category_paths: list[str]) -> None:
self._category_tree.clearSelection()
self._manufacturer_list.clearSelection()
if self._active_manufacturer is None:
self._manufacturer_list.clearSelection()
self._uncategorized.set_selected(False)
if not category_paths: