diff --git a/qml/AppUI/EditText.qml b/qml/AppUI/EditText.qml index dc93aa0..4db642e 100644 --- a/qml/AppUI/EditText.qml +++ b/qml/AppUI/EditText.qml @@ -28,9 +28,7 @@ Item { property bool textCenterIn: false property bool editStatus: false property string textEdited: title - property bool isFullScreen: false - property bool isFolder: false - property real textInputSize: 14 + property real textSize signal textEditingFinished(string text) Component { @@ -41,21 +39,14 @@ Item { horizontalAlignment: contain.textCenterIn ? Text.AlignHCenter : Text.AlignLeft elide: Text.ElideRight text: contain.textEdited - paletteRole: isFullScreen ? Platform.Theme.HighlightedText : Platform.Theme.Text - font.bold: isFolder - // AppControls2 StyleText - property real textUltra: 2*systemFontSize - property real systemFontSize: 16 - - font.pointSize:(isFolder && isFullScreen) ? textUltra : systemFontSize + paletteRole: Platform.Theme.Text + font.bold: true + font.pointSize: textSize MouseArea { id: textArea anchors.fill: parent - onDoubleClicked: { - contain.editStatus = true; - contain.textInputSize = textShow.font.pointSize; - } + onDoubleClicked: contain.editStatus = true; } } } @@ -84,8 +75,7 @@ Item { text: contain.textEdited selectByMouse: true maximumLength: 14 - font.bold: !isFolder - font.pointSize: contain.textInputSize + font.pointSize: textSize onEditingFinished: { // modelManager.getFolderModel().renameFolder(text); @@ -95,7 +85,7 @@ Item { } function updateTextInputColor() { - color = isFullScreen ? Platform.Theme.highlightedText() : Platform.Theme.text(); + color = Platform.Theme.text(); selectionColor = Platform.Theme.highlight(); } diff --git a/qml/AppUI/FullScreenAppItem.qml b/qml/AppUI/FullScreenAppItem.qml index 6e8dae1..92403be 100644 --- a/qml/AppUI/FullScreenAppItem.qml +++ b/qml/AppUI/FullScreenAppItem.qml @@ -43,29 +43,25 @@ MouseArea { ColumnLayout { anchors.fill: parent anchors.topMargin: 16 - spacing: 0 + anchors.bottomMargin: 16 + spacing: 8 UkuiItems.Icon { id: appIcon - Layout.minimumWidth: 32 - Layout.minimumHeight: 32 - Layout.maximumWidth: 96 - Layout.maximumHeight: 96 - - Layout.preferredWidth: 96 - Layout.preferredHeight: 96 - Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + Layout.preferredWidth: styleBackground.width * 0.6 + Layout.preferredHeight: width + Layout.alignment: Qt.AlignHCenter } UkuiItems.StyleText { id: appName Layout.fillWidth: true - Layout.fillHeight: true + Layout.preferredHeight: contentHeight + Layout.maximumHeight: contentHeight elide: Text.ElideRight - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter paletteRole: Platform.Theme.Text + horizontalAlignment: Text.AlignHCenter } } } diff --git a/qml/AppUI/FullScreenAppList.qml b/qml/AppUI/FullScreenAppList.qml index ea383c3..d58e5a5 100644 --- a/qml/AppUI/FullScreenAppList.qml +++ b/qml/AppUI/FullScreenAppList.qml @@ -24,6 +24,7 @@ import QtQuick.Layouts 1.12 import org.ukui.menu.core 1.0 import org.ukui.menu.extension 1.0 +import "../extensions" as Extension import AppControls2 1.0 as AppControls2 import org.ukui.quick.items 1.0 as UkuiItems @@ -31,8 +32,14 @@ import org.ukui.quick.platform 1.0 as Platform ListView { id: root + signal openFolderSignal(string folderId, string folderName, int x, int y) + signal contentShowFinished() + property bool isContentShow + property int itemHeight: 40 property alias sourceModel: appGroupModel.sourceModel + // TODO: 动态计算尺寸 + property int column: 10 spacing: 5 clip: true @@ -61,8 +68,7 @@ ListView { GridView { width: parent.width height: childrenRect.height - // TODO: 动态计算尺寸 - cellWidth: width / 8 + cellWidth: width / root.column cellHeight: cellWidth interactive: false @@ -98,67 +104,68 @@ ListView { } header: Item { - width: ListView.view.width - height: childrenRect.height + ListView.view.spacing + width: root.width + height: childrenRect.height - property alias widgets: widgetView.widgets - property alias widgetCount: widgetView.count - property alias widgetInfos: widgetView.widgetInfos + property var widgets: [] + property var widgetInfos: [] + property int widgetCount: 1 - ListView { - id: widgetView + Component.onCompleted: { + widgetInfos.push({label: "favorite", display: "non-starred-symbolic", type: WidgetMetadata.Widget}); + widgets.push("favorite"); + } - property var widgets: [] - property var widgetInfos: [] - - anchors.top: parent.top + Column { width: parent.width - height: childrenRect.height - interactive: false - spacing: parent.ListView.view.spacing + height: childrenRect.height + spacing + spacing: root.spacing - onCountChanged: { - widgets = []; - widgetInfos = []; - for (let i = 0; i < count; ++i) { - let item = itemAtIndex(i); - widgetInfos.push({label: item.widgetId, display: item.icon, type: LabelItem.Icon}); - widgets.push(item.widgetId); - } + AppControls2.LabelItem { + width: parent.width + height: root.itemHeight + displayName: qsTr("Favorite") } - model: WidgetModel { - flags: WidgetMetadata.OnlyFullScreen - } - delegate: Column { - property string icon: model.icon - property string widgetId: model.id + GridView { + id: favoriteView + width: parent.width + height: contentHeight + property string mergeToAppId: "" + property bool isMergeToFolder: false + property bool dragTypeIsMerge: false + property int exchangedStartIndex: 0 + property alias viewModel: visualModel - width: ListView.view.width - height: childrenRect.height - spacing: ListView.view.spacing + cellWidth: width / root.column + cellHeight: cellWidth + model: DelegateModel { + id: visualModel + model: favoriteModel + delegate: Item { + id: container + width: favoriteView.cellWidth + height: favoriteView.cellHeight + Extension.FavoriteDelegate { + anchors.fill: parent + anchors.margins: 12 - AppControls2.LabelItem { - width: parent.width - height: root.itemHeight - displayName: model.name - } + visualIndex: container.DelegateModel.itemsIndex + delegateLayout.anchors.topMargin: 16 + delegateLayout.anchors.bottomMargin: 16 + delegateLayout.spacing: 8 + mergePrompt.anchors.topMargin: 10 + mergePrompt.width: width*0.67 + mergePrompt.radius: 32 - Loader { - width: parent.width - // height: item === null ? 0 : item.height - height: item === null ? 0 : 200 - - property var extensionData: model.data - onExtensionDataChanged: { - if (item !== null) { - item.extensionData = extensionData; + Component.onCompleted: contentShowFinished.connect(resetOpacity) + Component.onDestruction: contentShowFinished.disconnect(resetOpacity) } } + } - Component.onCompleted: { - setSource(model.main, {extensionData: extensionData}); - } + displaced: Transition { + NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad; duration: 200 } } } } diff --git a/qml/AppUI/FullScreenUI.qml b/qml/AppUI/FullScreenUI.qml index 87c18df..01cbe07 100644 --- a/qml/AppUI/FullScreenUI.qml +++ b/qml/AppUI/FullScreenUI.qml @@ -3,6 +3,7 @@ import QtQuick.Layouts 1.12 import QtQml.Models 2.12 import org.ukui.menu.core 1.0 +import "../extensions" as Extension import AppControls2 1.0 as AppControls2 import org.ukui.quick.items 1.0 as UkuiItems @@ -19,16 +20,54 @@ UkuiItems.StyleBackground { forceActiveFocus(); mainWindow.hide(); } else { - mainContainer.visible = true; + folderLoader.isFolderOpened = false; } } } + Extension.FullScreenFolder { + id: folderLoader + anchors.fill: parent + + folderModel: FolderModel + Component.onCompleted: fullScreenAppList.openFolderSignal.connect(initFolder) + Component.onDestruction: fullScreenAppList.openFolderSignal.disconnect(initFolder) + } + Item { id: mainContainer anchors.fill: parent z: 10 + state: !fullScreenAppList.isContentShow ? "contentShow" : "contentHidden" + states: [ + State { + name: "contentHidden" + PropertyChanges { target: mainContainer; opacity: 0; scale: 0.95 } + }, + State { + name: "contentShow" + PropertyChanges { target: mainContainer; opacity: 1; scale: 1 } + } + ] + + transitions: [ + Transition { + to:"contentHidden" + SequentialAnimation { + PropertyAnimation { properties: "opacity, scale"; duration: 300; easing.type: Easing.InOutCubic } + ScriptAction { script: mainContainer.visible = false } + } + }, + Transition { + to: "contentShow" + SequentialAnimation { + ScriptAction { script: mainContainer.visible = true } + PropertyAnimation { properties: "opacity, scale"; duration: 300; easing.type: Easing.InOutCubic } + } + } + ] + // 两行三列 GridLayout { anchors.fill: parent @@ -168,6 +207,7 @@ UkuiItems.StyleBackground { Layout.fillHeight: true sourceModel: AppPageBackend.appModel + isContentShow: folderLoader.isFolderOpened function positionLabel(label) { // 如果是Widget那么直接滚动到最顶上 @@ -195,7 +235,9 @@ UkuiItems.StyleBackground { Component.onCompleted: { positionViewAtBeginning(); + folderLoader.turnPageFinished.connect(contentShowFinished) } + Component.onDestruction: folderLoader.turnPageFinished.disconnect(contentShowFinished) } } } diff --git a/qml/extensions/FavoriteDelegate.qml b/qml/extensions/FavoriteDelegate.qml index e172923..0a2a07f 100644 --- a/qml/extensions/FavoriteDelegate.qml +++ b/qml/extensions/FavoriteDelegate.qml @@ -10,33 +10,100 @@ import AppControls2 1.0 as AppControls2 UkuiItems.StyleBackground { id: iconItem property bool hold: false - property alias draggedIcon: itemLoader + property alias delegateLayout: itemLayout + property alias mergePrompt: mergePrompt radius: 8 useStyleTransparency: false paletteRole: Platform.Theme.Text alpha: hold ? 0 : control.containsPress ? 0.15 : control.containsMouse ? 0.08 : 0 - // 合并提升框 52*52 + property int visualIndex + Binding { target: itemLoader; property: "visualIndex"; value: visualIndex } + + // 合并提示框 52*52 UkuiItems.StyleBackground { - width: 52 + id: mergePrompt height: width - radius: 14 anchors.top: parent.top - anchors.margins: 6 anchors.horizontalCenter: parent.horizontalCenter paletteRole: Platform.Theme.Text useStyleTransparency: false alpha: (delegateDropArea.enterSourceId !== model.id) - && delegateDropArea.containsDrag && dragTypeIsMerge ? 0.15 : 0 + && delegateDropArea.containsDrag && favoriteView.dragTypeIsMerge ? 0.15 : 0 z: -1 } + DropArea { + id: delegateDropArea + anchors.fill: parent + anchors.margins: 10 + property string enterSourceId: "" + + // drag.source [itemLoader] + onEntered: { + if (drag.source.isFolder) { + return; + } + + enterSourceId = drag.source.sourceId; + delegateDropTimer.running = true; + } + onExited: { + if (delegateDropTimer.timeOutCount < 1) { + favoriteView.dragTypeIsMerge = false; + favoriteView.viewModel.items.move(drag.source.visualIndex, iconItem.visualIndex); + } + + delegateDropTimer.running = false; + enterSourceId = ""; + } + } + + Timer { + id: delegateDropTimer + property int timeOutCount: 0 + interval: 300 + repeat: true + + onTriggered: { + ++timeOutCount; + if (timeOutCount == 1) { + favoriteView.mergeToAppId = model.id; + favoriteView.isMergeToFolder = (model.type === DataType.Folder); + favoriteView.dragTypeIsMerge = true; + } + } + onRunningChanged: timeOutCount = 0 + } + + MouseArea { + id: control + anchors.fill: parent + hoverEnabled: true + pressAndHoldInterval: 300 + acceptedButtons: Qt.LeftButton | Qt.RightButton + + onClicked: { + itemLoader.item.itemClicked(mouse.button); + } + onPressAndHold: { + if (mouse.button === Qt.LeftButton) { + drag.target = itemLoader; + iconItem.hold = true; + favoriteView.exchangedStartIndex = itemLoader.visualIndex; + itemLoader.sourceId = model.id; + } + } + onReleased: { + drag.target = null; + } + } + ColumnLayout { id: itemLayout - width: parent.width - anchors.top: parent.top + anchors.fill: parent anchors.topMargin: 8 - anchors.horizontalCenter: parent.horizontalCenter + anchors.bottomMargin: 14 spacing: 6 ToolTip.visible: iconText.truncated && control.containsMouse @@ -45,8 +112,8 @@ UkuiItems.StyleBackground { Item { id: loaderBase - Layout.preferredHeight: 48 - Layout.preferredWidth: 48 + Layout.preferredWidth: iconItem.width * 0.6 + Layout.preferredHeight: width Layout.alignment: Qt.AlignHCenter Loader { @@ -63,7 +130,9 @@ UkuiItems.StyleBackground { if (Drag.active) { itemLoader.parent = favoriteView; } else { - if (dragTypeIsMerge && (model.id !== mergeToAppId) && (model.type === DataType.Normal)) { + if (favoriteView.dragTypeIsMerge + && (model.id !== favoriteView.mergeToAppId) + && (model.type === DataType.Normal)) { iconItem.hold = false; } else { @@ -116,6 +185,8 @@ UkuiItems.StyleBackground { UkuiItems.StyleText { id: iconText Layout.fillWidth: true + Layout.preferredHeight: contentHeight + Layout.maximumHeight: contentHeight text: name elide: Text.ElideRight @@ -140,7 +211,7 @@ UkuiItems.StyleBackground { function itemClicked(mouseButton) { if (mouseButton === Qt.RightButton) { - visualModel.model.openMenu(index) + favoriteModel.openMenu(index) } else { var data = {"id": model.id}; send(data); @@ -154,24 +225,26 @@ UkuiItems.StyleBackground { Item { AppControls2.FolderIcon { id: folderIcon - width: 40 - height: 40 + height: width anchors.centerIn: parent - - rows: 2; columns: 2 - spacing: 2; padding: 2 + alpha: 0.10 icons: icon - radius: 4; alpha: 0.10 + columns: rows + + width: mainWindow.isFullScreen ? 84: 40 + rows: mainWindow.isFullScreen ? 4 : 2 + spacing: mainWindow.isFullScreen ? 4 : 2 + padding: mainWindow.isFullScreen ? 8 : 2 + radius: mainWindow.isFullScreen ? 16 :4 } function itemClicked(mouseButton) { if (mouseButton === Qt.RightButton) { - visualModel.model.openMenu(index); + favoriteModel.openMenu(index); } else { var x = mapToGlobal(folderIcon.x, folderIcon.y).x; var y = mapToGlobal(folderIcon.x, folderIcon.y).y openFolderSignal(id, name, x, y); // 执行隐藏动画,并且当前图标消失且鼠标区域不可用 - favoriteView.isContentShow = false; opacity = 0; control.enabled = false; control.hoverEnabled = false; @@ -180,52 +253,27 @@ UkuiItems.StyleBackground { } } - MouseArea { - id: control - anchors.fill: parent - hoverEnabled: true - pressAndHoldInterval: 300 - acceptedButtons: Qt.LeftButton | Qt.RightButton - - onClicked: { - itemLoader.item.itemClicked(mouse.button); - } - onPressAndHold: { - if (mouse.button === Qt.LeftButton) { - drag.target = itemLoader; - iconItem.hold = true; - exchangedStartIndex = itemLoader.visualIndex; - itemLoader.sourceId = model.id; - } - } - onReleased: { - drag.target = null; - } - - } // folderFunction function resetOpacity() { itemLoader.item.opacity = 1; control.enabled = true; control.hoverEnabled = true; } - Component.onCompleted: favoriteView.contentShowFinished.connect(resetOpacity) - Component.onDestruction: favoriteView.contentShowFinished.disconnect(resetOpacity) onHoldChanged: { if (hold) { favoriteView.interactive = false; } else { favoriteView.interactive = contentHeight > favoriteView.parent.height; - if (dragTypeIsMerge && (model.id !== mergeToAppId) && (model.type === DataType.Normal)) { - if (isMergeToFolder) { - visualModel.model.addAppToFolder(model.id, mergeToAppId); + if (favoriteView.dragTypeIsMerge && (model.id !== favoriteView.mergeToAppId) && (model.type === DataType.Normal)) { + if (favoriteView.isMergeToFolder) { + favoriteModel.addAppToFolder(model.id, favoriteView.mergeToAppId); } else { - visualModel.model.addAppsToNewFolder(model.id, mergeToAppId); + favoriteModel.addAppsToNewFolder(model.id, favoriteView.mergeToAppId); } - } else if (exchangedStartIndex !== itemLoader.visualIndex) { - visualModel.model.exchangedAppsOrder(exchangedStartIndex, itemLoader.visualIndex); + } else if (favoriteView.exchangedStartIndex !== itemLoader.visualIndex) { + favoriteModel.exchangedAppsOrder(favoriteView.exchangedStartIndex, itemLoader.visualIndex); } } } diff --git a/qml/extensions/FavoriteExtension.qml b/qml/extensions/FavoriteExtension.qml index 610fcd8..1544fbb 100644 --- a/qml/extensions/FavoriteExtension.qml +++ b/qml/extensions/FavoriteExtension.qml @@ -42,11 +42,9 @@ UkuiMenuExtension { } folderLoader.isFolderOpened = false; - favoriteView.visible = true; - favoriteView.isContentShow = true; } - } + UkuiItems.Menu { id: menu content: [ @@ -105,6 +103,7 @@ UkuiMenuExtension { width: parent.width height: (contentHeight > parent.height) ? parent.height : contentHeight interactive: contentHeight > parent.height + isContentShow: !folderLoader.isFolderOpened Component.onCompleted: { favoriteView.viewModel.model = extensionData.favoriteAppsModel diff --git a/qml/extensions/FavoriteGridView.qml b/qml/extensions/FavoriteGridView.qml index fc2bba6..fdf2146 100644 --- a/qml/extensions/FavoriteGridView.qml +++ b/qml/extensions/FavoriteGridView.qml @@ -32,10 +32,9 @@ GridView { cellHeight: cellWidth + 12 signal openFolderSignal(string folderId, string folderName, int x, int y) signal contentShowFinished() - property bool isContentShow: true + property bool isContentShow property int spacing: 4 - property int itemHeight: 84 property int column: 5 property alias viewModel: visualModel @@ -66,7 +65,10 @@ GridView { }, Transition { to: "contentShow" - PropertyAnimation { properties: "opacity, scale"; duration: 300; easing.type: Easing.InOutCubic } + SequentialAnimation { + ScriptAction { script: favoriteView.visible = true } + PropertyAnimation { properties: "opacity, scale"; duration: 300; easing.type: Easing.InOutCubic } + } } ] // 按键导航处理(左右键可以循环) @@ -100,12 +102,6 @@ GridView { currentIndex = currentIndex - column; } - ScrollBar.vertical: AppControls2.ScrollBar { - id: scrollBar - visible: viewMouseArea.containsMouse - width: 14; height: favoriteView.height - } - displaced: Transition { NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad; duration: 200 } } @@ -117,8 +113,7 @@ GridView { focus: true width: favoriteView.cellWidth height: favoriteView.cellHeight - property int visualIndex: DelegateModel.itemsIndex - Binding { target: iconItem.draggedIcon; property: "visualIndex"; value: visualIndex } + states: State { when: activeFocus PropertyChanges { @@ -131,87 +126,48 @@ GridView { send(data); } - DropArea { - id: delegateDropArea - anchors.fill: parent - anchors.margins: 14 - property string enterSourceId: "" - - // drag.source [iconItem.draggedIcon] - onEntered: { - if (drag.source.isFolder) { - return; - } - - enterSourceId = drag.source.sourceId; - delegateDropTimer.running = true; - } - onExited: { - if (delegateDropTimer.timeOutCount < 1) { - dragTypeIsMerge = false; - visualModel.items.move(drag.source.visualIndex, iconItem.visualIndex); - } - - delegateDropTimer.running = false; - enterSourceId = ""; - } - } - - Item { + FavoriteDelegate { + id: iconItem anchors.fill: parent anchors.margins: 2 - Timer { - id: delegateDropTimer - property int timeOutCount: 0 - interval: 300 - repeat: true + visualIndex: container.DelegateModel.itemsIndex + mergePrompt.anchors.topMargin: 6 + mergePrompt.width: 52 + mergePrompt.radius: 14 - onTriggered: { - ++timeOutCount; - if (timeOutCount == 1) { - mergeToAppId = model.id; - isMergeToFolder = (model.type === DataType.Folder); - dragTypeIsMerge = true; + Component.onCompleted: favoriteView.contentShowFinished.connect(resetOpacity) + Component.onDestruction: favoriteView.contentShowFinished.disconnect(resetOpacity) + } + + + //编辑模式标志 + Loader { + id: tag + anchors.top: parent.top + anchors.right: parent.right + anchors.rightMargin: 10 + Component { + id: editImage + UkuiItems.Button { + width: 28 + height: 28 + icon.width: 16 + icon.height: 16 + background.paletteRole: Platform.Theme.Light + background.alpha: 1 + activeFocusOnTab: false + + onClicked: { + visualModel.model.removeAppFromFavorites(id); } + + background.radius: width / 2 + icon.source: "ukui-cancel-star-symbolic" } - onRunningChanged: timeOutCount = 0 } - FavoriteDelegate { - id: iconItem - anchors.fill: parent - property int visualIndex: container.DelegateModel.itemsIndex - } - - //编辑模式标志 - Loader { - id: tag - anchors.top: parent.top - anchors.right: parent.right - anchors.rightMargin: 10 - Component { - id: editImage - UkuiItems.Button { - width: 28 - height: 28 - icon.width: 16 - icon.height: 16 - background.paletteRole: Platform.Theme.Light - background.alpha: 1 - activeFocusOnTab: false - - onClicked: { - visualModel.model.removeAppFromFavorites(id); - } - - background.radius: width / 2 - icon.source: "ukui-cancel-star-symbolic" - } - } - - sourceComponent: mainWindow.editMode && (type === DataType.Normal) ? editImage : null - } + sourceComponent: mainWindow.editMode && (type === DataType.Normal) ? editImage : null } } } diff --git a/qml/extensions/FolderContent.qml b/qml/extensions/FolderContent.qml index 8d55fa2..560d99a 100644 --- a/qml/extensions/FolderContent.qml +++ b/qml/extensions/FolderContent.qml @@ -23,164 +23,152 @@ import QtQuick.Controls 2.5 import org.ukui.menu.core 1.0 import org.ukui.quick.platform 1.0 as Platform import org.ukui.quick.items 1.0 as UkuiItems -Item { + +SwipeView { + id: folderSwipeView + anchors.margins: contentMargins + property bool needTurnPage: false + property int contentX: contentItem.contentX + property int startContentX: 0 + property int startIndex: 0 + property var folderContentModel property alias folderSwipeView: folderSwipeView property bool mouseEnable: false property int contentMargins property int labelMagrins + property int labelSpacing property bool isFullScreen: false - PageIndicator { - id: pageIndicator - interactive: true - count: folderSwipeView.count - visible: count > 1 - currentIndex: folderSwipeView.currentIndex - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - - delegate: Rectangle { - color: "grey" - width: (index === folderSwipeView.currentIndex )? 20 : 6; height: 6; - radius: width / 2 - opacity: (index === folderSwipeView.currentIndex )? 1 : 0.6 - Behavior on width { - NumberAnimation { duration: 300; easing.type: Easing.InOutCubic } - } + function hideFolder() { + // 在非第一页时,执行翻页动画 + if (currentIndex > 0) { + interactive = false; + needTurnPage = true; + pageIndicator.visible = false; + contentItem.highlightFollowsCurrentItem = true; + contentItem.highlightMoveDuration = 300; + contentItem.highlightMoveVelocity = -1; + startContentX = contentX; + startIndex = currentIndex; + currentIndex = 0; + // 在第一页或单页时,全屏文件夹直接隐藏 + } else { + reset(); } } - SwipeView { - id: folderSwipeView - anchors.fill: parent - anchors.margins: contentMargins - property bool needTurnPage: false - property int contentX: contentItem.contentX - property int startContentX: 0 - property int startIndex: 0 + function reset() { + interactive = true; + root.active = false; + pageIndicator.visible = false; + turnPageFinished(); + } - function hideFolder() { - // 在非第一页时,执行翻页动画 - if (currentIndex > 0) { - interactive = false; - needTurnPage = true; - pageIndicator.visible = false; - contentItem.highlightFollowsCurrentItem = true; - contentItem.highlightMoveDuration = 300; - contentItem.highlightMoveVelocity = -1; - startContentX = contentX; - startIndex = currentIndex; - currentIndex = 0; - // 在第一页或单页时,全屏文件夹直接隐藏 - } else { - reset(); - } + // 判断执行翻页结束后,全屏文件夹隐藏 + onContentXChanged: { + // 对比翻页过程的位移 + if (needTurnPage && startContentX - contentX === startIndex * width) { + needTurnPage = false; + reset(); } + } - function reset() { - interactive = true; - root.active = false; - pageIndicator.visible = false; - turnPageFinished(); - } + Repeater { + model: Math.ceil(folderModel.count / 16) - // 判断执行翻页结束后,全屏文件夹隐藏 - onContentXChanged: { - // 对比翻页过程的位移 - if (needTurnPage && startContentX - contentX === startIndex * 86) { - needTurnPage = false; - reset(); - } - } + Item { + id: base + property int currentPage: SwipeView.index - Repeater { - model: Math.ceil(folderModel.count / 16) + GridView { + id: folderGridView + cellHeight: isFullScreen ? cellWidth : (cellWidth + 12) + cellWidth: width / 4 + anchors.fill: parent + interactive: false - Item { - id: base - property int currentPage: SwipeView.index + model: DelegateModel { + property int maxItemNumPerPage: 16 + filterOnGroup: "folderContent" + groups: DelegateModelGroup { + name: "folderContent" + } - GridView { - id: folderGridView - cellHeight: isFullScreen ? cellWidth : (cellWidth + 12) - cellWidth: width / 4 - anchors.fill: parent - interactive: false - - model: DelegateModel { - property int maxItemNumPerPage: 16 - filterOnGroup: "folderContent" - groups: DelegateModelGroup { - name: "folderContent" + items.onChanged: { + var groupIndex = groups.length - 1; + if (groups[groupIndex].count !== 0) { + groups[groupIndex].remove(0, groups[groupIndex].count); } - items.onChanged: { - var groupIndex = groups.length - 1; - if (groups[groupIndex].count !== 0) { - groups[groupIndex].remove(0, groups[groupIndex].count); - } - - for (var i = base.currentPage * maxItemNumPerPage; - i < Math.min((base.currentPage + 1) * maxItemNumPerPage, items.count); i ++) { - items.addGroups(i, 1, "folderContent"); - } + for (var i = base.currentPage * maxItemNumPerPage; + i < Math.min((base.currentPage + 1) * maxItemNumPerPage, items.count); i ++) { + items.addGroups(i, 1, "folderContent"); } + } + onCountChanged: { + if (count === 0) { + reset(); + folderLoader.isFolderOpened = false; + } + } - model: folderContentModel - delegate: Item { - width: folderGridView.cellWidth - height: folderGridView.cellHeight + model: folderContentModel + delegate: Item { + width: folderGridView.cellWidth + height: folderGridView.cellHeight - MouseArea { - hoverEnabled: true - enabled: mouseEnable + MouseArea { + hoverEnabled: true + enabled: mouseEnable + anchors.fill: parent + anchors.margins: labelMagrins + acceptedButtons: Qt.LeftButton | Qt.RightButton + + UkuiItems.StyleBackground { anchors.fill: parent - anchors.margins: 2 - acceptedButtons: Qt.LeftButton | Qt.RightButton + useStyleTransparency: false + paletteRole: Platform.Theme.Text + radius: Platform.Theme.maxRadius + alpha: parent.containsPress ? 0.15 : parent.containsMouse ? 0.08 : 0.00 + clip: false - UkuiItems.StyleBackground { + ColumnLayout { anchors.fill: parent - useStyleTransparency: false - paletteRole: isFullScreen ? Platform.Theme.Light : Platform.Theme.Text - radius: Platform.Theme.maxRadius - alpha: parent.containsPress ? 0.25 : parent.containsMouse ? 0.15 : 0.00 - clip: false + anchors.margins: labelSpacing + spacing: (parent.width > 40) ? 4 : 0 + Image { + id: iconImage + source: icon + Layout.minimumHeight: 16 + Layout.minimumWidth: 16 + Layout.maximumWidth: 96 + Layout.maximumHeight: 96 - ColumnLayout { - anchors.fill: parent - anchors.margins: labelMagrins - spacing: (parent.width > 40) ? 4 : 0 - Image { - id: iconImage - source: icon - Layout.minimumHeight: 16 - Layout.minimumWidth: 16 - Layout.fillHeight: true - Layout.preferredWidth: height - Layout.alignment: Qt.AlignHCenter - } + Layout.fillHeight: true + Layout.preferredWidth: height + Layout.alignment: Qt.AlignHCenter + } - UkuiItems.StyleText { - text: name - elide: Text.ElideRight - paletteRole: isFullScreen ? Platform.Theme.HighlightedText : Platform.Theme.Text + UkuiItems.StyleText { + text: name + elide: Text.ElideRight + paletteRole: Platform.Theme.Text - Layout.preferredHeight: contentHeight - Layout.fillWidth: true - horizontalAlignment: Text.AlignHCenter - } + Layout.preferredHeight: contentHeight + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter } } - onClicked: { - if (mouse.button === Qt.RightButton) { - menuManager.showMenu(id, MenuInfo.Folder); - return; - } - if (mouse.button === Qt.LeftButton) { - appManager.launchApp(id); - return; - } + } + onClicked: { + if (mouse.button === Qt.RightButton) { + menuManager.showMenu(id, MenuInfo.Folder); + return; + } + if (mouse.button === Qt.LeftButton) { + appManager.launchApp(id); + return; } } } @@ -190,4 +178,3 @@ Item { } } } - diff --git a/qml/extensions/FolderGridView.qml b/qml/extensions/FolderGridView.qml index 6f5f186..a666390 100644 --- a/qml/extensions/FolderGridView.qml +++ b/qml/extensions/FolderGridView.qml @@ -55,7 +55,7 @@ Loader { Component { id: folderComponent Item { - UkuiItems.StyleBackground { + UkuiItems.StyleBackground { id: folderIconBase paletteRole: Platform.Theme.Text useStyleTransparency: false @@ -72,7 +72,7 @@ Loader { PropertyChanges { target: folderIconBase width: 348 - height: viewMaxRow * 100 + height: viewMaxRow * 100 // itemHeight:96 + spacing:4 radius: Platform.Theme.maxRadius x: (parent.width - width) / 2 y: 82 @@ -81,7 +81,8 @@ Loader { PropertyChanges { target: content contentMargins: 0 - labelMagrins: 8 + labelSpacing: 8 + labelMagrins: 2 } PropertyChanges { target: folderNameText; opacity: 1 } }, @@ -99,6 +100,7 @@ Loader { PropertyChanges { target: content contentMargins: 3 + labelSpacing: 0 labelMagrins: 0 } PropertyChanges { target: folderNameText; opacity: 0 } @@ -120,7 +122,7 @@ Loader { } PropertyAnimation { duration: animationDuration; easing.type: Easing.OutQuint - properties: "contentMargins, labelMagrins" + properties: "contentMargins, labelMagrins, labelSpacing" } } ScriptAction { script: content.folderSwipeView.hideFolder() } @@ -140,7 +142,7 @@ Loader { } PropertyAnimation { duration: animationDuration; easing.type: Easing.OutQuint - properties: "contentMargins, labelMagrins" + properties: "contentMargins, labelMagrins, labelSpacing" } } ScriptAction { @@ -160,6 +162,23 @@ Loader { } } + PageIndicator { + id: pageIndicator + interactive: true + count: content.count + visible: count > 1 + currentIndex: content.currentIndex + anchors.top: folderIconBase.bottom + anchors.horizontalCenter: folderIconBase.horizontalCenter + + delegate: Rectangle { + color: "black" + width: 6; height: width; + radius: width / 2 + opacity: (index === content.currentIndex )? 0.9 : 0.3 + } + } + AppUI.EditText { id: folderNameText anchors.bottom: folderIconBase.top @@ -169,9 +188,7 @@ Loader { height: 47; width: folderIconBase.width textEdited: folderName textCenterIn: true - isFolder: true - isFullScreen: isFullScreen - textInputSize: 16 + textSize: 16 onTextEditingFinished: text=> { folderModel.renameFolder(text); diff --git a/qml/extensions/FullScreenFolder.qml b/qml/extensions/FullScreenFolder.qml index 34713f9..f059a36 100644 --- a/qml/extensions/FullScreenFolder.qml +++ b/qml/extensions/FullScreenFolder.qml @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023, KylinSoft Co., Ltd. + * Copyright (C) 2024, KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ import QtQuick.Controls 2.5 import org.ukui.menu.core 1.0 import org.ukui.quick.platform 1.0 as Platform import org.ukui.quick.items 1.0 as UkuiItems +import AppUI 1.0 as AppUI Loader { id: root @@ -34,13 +35,12 @@ Loader { property int viewMaxRow: 0 property bool isFolderOpened: false - property bool isFullScreen: true - property int margins: isFullScreen? 20 : 0 + property int margins: 20 property int animationDuration: 300 signal turnPageFinished() function initFolder(id, name, x, y) { - extensionData.folderModel.setFolderId(id); + FolderModel.setFolderId(id); folderName = name; folderX = x; folderY = y; @@ -58,12 +58,13 @@ Loader { id: folderIconBase paletteRole: Platform.Theme.Text useStyleTransparency: false + alpha: 0.1 + property int folderIconSize: 0 property int iconSpacing: 0 property int imageX: 0 property int imageY: 0 property int gridViewMargin: 8 - alpha: 0.25 state: isFolderOpened ? "folderOpened" : "folderHidden" states: [ @@ -71,17 +72,17 @@ Loader { name: "folderOpened" PropertyChanges { target: folderIconBase - width: isFullScreen ? 720 : 348 - height: isFullScreen ? (viewMaxRow * 170 + margins * 2) : (viewMaxRow * 85 + margins * 2) - radius: Platform.Theme.maxRadius - gridViewMargin: margins - x: isFullScreen ? (parent.width - width) / 2 : 56 - y: isFullScreen ? (parent.height - height) / 2 : 104 - // 内部图标尺寸和布局 - folderIconSize:isFullScreen ? 96 : 48 - iconSpacing: 8 - imageX: isFullScreen ? 37 : 13 - imageY: isFullScreen ? 17 : 8 + width: 720 // margins: 16 + height: viewMaxRow*176 + content.contentMargins*2 // itemHeight: 160 + spacing: 16 + margins: 24 + radius: Platform.Theme.maxRadius //? 32 + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + } + PropertyChanges { + target: content + contentMargins: 8 + labelSpacing: 8 + labelMagrins: 8 } PropertyChanges { target: folderNameText; opacity: 1 } }, @@ -89,17 +90,17 @@ Loader { name: "folderHidden" PropertyChanges { target: folderIconBase - width: 86 - height: 86 + width: 84 + height: 84 radius: Platform.Theme.maxRadius - gridViewMargin: 8 x: root.mapFromGlobal(folderX, 0).x y: root.mapFromGlobal(0, folderY).y - // 内部图标尺寸和布局 - folderIconSize: 16 - iconSpacing: 0 - imageX: 0 - imageY: 0 + } + PropertyChanges { + target: content + contentMargins: 0 + labelSpacing: 0 + labelMagrins: 0 } PropertyChanges { target: folderNameText; opacity: 0 } } @@ -112,12 +113,16 @@ Loader { ParallelAnimation { PropertyAnimation { duration: animationDuration; easing.type: Easing.InOutCubic - properties: "x, y, width, height, folderIconSize, iconSpacing, radius, imageX, imageY, gridViewMargin" + properties: "x, y, width, height, radius, alpha" } PropertyAnimation { duration: animationDuration; easing.type: Easing.OutQuint properties: "opacity" } + PropertyAnimation { + duration: animationDuration; easing.type: Easing.OutQuint + properties: "contentMargins, labelMagrins, labelSpacing" + } } ScriptAction { script: content.folderSwipeView.hideFolder() } } @@ -128,12 +133,16 @@ Loader { ParallelAnimation { PropertyAnimation { duration: animationDuration; easing.type: Easing.InOutCubic - properties: "x, y, width, height, folderIconSize, iconSpacing, radius, imageX, imageY, gridViewMargin" + properties: "x, y, width, height, radius, alpha" } PropertyAnimation { duration: animationDuration; easing.type: Easing.InQuint properties: "opacity" } + PropertyAnimation { + duration: animationDuration; easing.type: Easing.OutQuint + properties: "contentMargins, labelMagrins, labelSpacing" + } } ScriptAction { script: { @@ -148,23 +157,42 @@ Loader { id: content anchors.fill: parent folderContentModel: folderModel - contentMargins: folderIconBase.gridViewMargin - labelMagrins: isFullScreen ? 16 : 8 + isFullScreen: true } } - EditText { + PageIndicator { + id: pageIndicator + interactive: true + count: content.count + visible: count > 1 + currentIndex: content.currentIndex + anchors.bottom: folderIconBase.bottom + anchors.horizontalCenter: folderIconBase.horizontalCenter + + delegate: Rectangle { + color: "black" + width: 6; height: width; + radius: width / 2 + opacity: (index === content.currentIndex )? 0.9 : 0.3 + } + } + + AppUI.EditText { id: folderNameText anchors.bottom: folderIconBase.top anchors.bottomMargin: 30 anchors.horizontalCenter: folderIconBase.horizontalCenter + height: 47; width: folderIconBase.width textEdited: folderName textCenterIn: true - isFolder: true - isFullScreen: isFullScreen + textSize: 32 + + onTextEditingFinished: text=> { + folderModel.renameFolder(text); + } } } } - } diff --git a/src/extension/favorite/favorite-widget.cpp b/src/extension/favorite/favorite-widget.cpp index ceeab8e..92d6a11 100644 --- a/src/extension/favorite/favorite-widget.cpp +++ b/src/extension/favorite/favorite-widget.cpp @@ -36,9 +36,8 @@ FavoriteWidget::FavoriteWidget(QObject *parent) : WidgetExtension(parent) FavoritesModel::instance().setSourceModel(&AppFavoritesModel::instance()); FavoritesModel::instance().sort(0); - m_folderModel = new FolderModel; m_data.insert("favoriteAppsModel", QVariant::fromValue(&FavoritesModel::instance())); - m_data.insert("folderModel", QVariant::fromValue(m_folderModel)); + m_data.insert("folderModel", QVariant::fromValue(&FolderModel::instance())); } int FavoriteWidget::index() const diff --git a/src/extension/favorite/favorite-widget.h b/src/extension/favorite/favorite-widget.h index ba24214..d617e13 100644 --- a/src/extension/favorite/favorite-widget.h +++ b/src/extension/favorite/favorite-widget.h @@ -38,7 +38,6 @@ public: private: MetadataMap m_metadata; QVariantMap m_data; - FolderModel *m_folderModel; }; } // UkuiMenu diff --git a/src/extension/favorite/folder-model.cpp b/src/extension/favorite/folder-model.cpp index 2d0b111..d71fdd9 100644 --- a/src/extension/favorite/folder-model.cpp +++ b/src/extension/favorite/folder-model.cpp @@ -117,4 +117,10 @@ int FolderModel::count() return m_apps.count(); } +FolderModel &FolderModel::instance() +{ + static FolderModel folderModel; + return folderModel; +} + } // UkuiMenu diff --git a/src/extension/favorite/folder-model.h b/src/extension/favorite/folder-model.h index 1d2ed2c..cbbdc70 100644 --- a/src/extension/favorite/folder-model.h +++ b/src/extension/favorite/folder-model.h @@ -29,7 +29,8 @@ class FolderModel : public QAbstractListModel Q_OBJECT Q_PROPERTY(int count READ count NOTIFY countChanged) public: - explicit FolderModel(QObject *parent = nullptr); + static FolderModel &instance(); + Q_INVOKABLE void setFolderId(const QString &folderId); Q_INVOKABLE void renameFolder(const QString &folderName); @@ -45,6 +46,8 @@ private Q_SLOTS: void loadFolderData(int id); private: + explicit FolderModel(QObject *parent = nullptr); + int m_folderId; QStringList m_apps; }; diff --git a/src/ukui-menu-application.cpp b/src/ukui-menu-application.cpp index 7a03f23..1956968 100644 --- a/src/ukui-menu-application.cpp +++ b/src/ukui-menu-application.cpp @@ -29,6 +29,8 @@ #include "widget-model.h" #include "app-page-backend.h" #include "app-group-model.h" +#include "favorite/favorites-model.h" +#include "favorite/folder-model.h" #include #include @@ -91,6 +93,8 @@ void UkuiMenuApplication::initQmlEngine() context->setContextProperty("menuManager", ContextMenuManager::instance()); context->setContextProperty("appManager", AppManager::instance()); + context->setContextProperty("favoriteModel", &FavoritesModel::instance()); + context->setContextProperty("FolderModel", &FolderModel::instance()); // MenuMainWindow // const QUrl url(QStringLiteral("qrc:/qml/MenuMainWindow.qml")); // QQmlApplicationEngine *m_applicationEngine{nullptr};