diff --git a/qml/windowed/AppList.qml b/qml/windowed/AppList.qml index e31dd085..a2e5d65a 100644 --- a/qml/windowed/AppList.qml +++ b/qml/windowed/AppList.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -18,50 +18,45 @@ ColumnLayout { signal freeSortViewFolderClicked(string folderId, string folderName, point triggerPosition) - property Item keyTabTarget: loader.item + property bool isFreeSort: CategorizedSortProxyModel.categoryType === CategorizedSortProxyModel.FreeCategory + property Item keyTabTarget: isFreeSort ? freeSortView.keyTabTarget : categoryView.keyTabTarget property Item nextKeyTabTarget onFocusChanged: () => { - loader.item.focus = true + (isFreeSort ? freeSortView : categoryView).focus = true } - function resetViewState() { - loader.item.resetViewState() + onIsFreeSortChanged: { + (isFreeSort ? freeSortView : categoryView).resetViewState() } - function switchToFreeSort(freeSort) { - if (freeSort) { - loader.sourceComponent = freeSortListView - } else { - loader.sourceComponent = categoryListView - } - // 切换排序模式后立即重置视图到顶部,无动画 - if (loader.item) { - loader.item.resetViewState() - } + function resetViewState() { + (isFreeSort ? freeSortView : categoryView).resetViewState() } - Loader { - id: loader - sourceComponent: CategorizedSortProxyModel.categoryType === CategorizedSortProxyModel.FreeCategory ? freeSortListView : categoryListView + // Both views are always instantiated; switching is done via `visible` + // to avoid the costly Loader component destruction/creation (~120ms -> ~0ms). + // The hidden view's ListView retains its delegates because it keeps a valid + // size from anchors.fill, so re-showing is instant. + // Extra memory: ~60-80KB for hidden view delegates (measured). + Item { + id: viewContainer Layout.fillWidth: true Layout.fillHeight: true - } - Component { - id: categoryListView AppListView { - id: appCategoryListView + id: categoryView + anchors.fill: viewContainer + visible: !isFreeSort MouseAreaCom {} KeyNavigation.tab: nextKeyTabTarget } - } - Component { - id: freeSortListView FreeSortListView { - id: appFreeSortListView + id: freeSortView + anchors.fill: viewContainer + visible: isFreeSort onFolderClicked: { freeSortViewFolderClicked(folderId, folderName, triggerPosition) @@ -84,4 +79,3 @@ ColumnLayout { } } } - diff --git a/qml/windowed/WindowedFrame.qml b/qml/windowed/WindowedFrame.qml index 10df3b08..15049d2b 100644 --- a/qml/windowed/WindowedFrame.qml +++ b/qml/windowed/WindowedFrame.qml @@ -325,13 +325,6 @@ InputEventItem { } } - Connections { - target: sideBar - function onSwitchToFreeSort(isFreeSort) { - appList.switchToFreeSort(isFreeSort) - } - } - Connections { target: appList function onFreeSortViewFolderClicked(folderId, folderName, triggerPosition) { diff --git a/src/models/categorizedsortproxymodel.cpp b/src/models/categorizedsortproxymodel.cpp index 11955e19..9838c659 100644 --- a/src/models/categorizedsortproxymodel.cpp +++ b/src/models/categorizedsortproxymodel.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -18,8 +18,13 @@ DCORE_USE_NAMESPACE void CategorizedSortProxyModel::setCategoryType(CategoryType categoryType) { CategoryType oldCategoryType = this->categoryType(); - - beginResetModel(); + + // Temporarily disable dynamic sort to prevent setSortRole from triggering + // a redundant sort. We trigger a single sort below via setDynamicSortFilter, + // which uses layoutAboutToBeChanged/layoutChanged instead of modelReset, + // preserving delegates. + const bool wasDynamic = dynamicSortFilter(); + setDynamicSortFilter(false); isFreeSort = (categoryType == FreeCategory); switch (categoryType) { case Alphabetary: @@ -37,8 +42,14 @@ void CategorizedSortProxyModel::setCategoryType(CategoryType categoryType) config->setValue("categoryType", categoryType); } - sort(0); - endResetModel(); + // Re-enable dynamic sort filter to trigger a single d->sort() internally, + // then restore the original setting. d->sort() emits + // layoutAboutToBeChanged/layoutChanged (not modelReset), so the view moves + // existing delegates instead of destroying and recreating them. + setDynamicSortFilter(true); + if (!wasDynamic) { + setDynamicSortFilter(false); + } qCInfo(logModels) << "Category type changed to:" << categoryType; emit categoryTypeChanged();