feat(qml):按照设计稿优化收藏界面显示,添加空白区域右键菜单

This commit is contained in:
qiqi49 2024-01-30 09:50:09 +08:00 committed by hewenfei
parent 379b07fc7a
commit fbc3f02c10
7 changed files with 361 additions and 297 deletions

View File

@ -0,0 +1,234 @@
import QtQuick 2.15
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.15
import org.ukui.menu.core 1.0
import org.ukui.menu.extension 1.0
import org.ukui.quick.platform 1.0 as Platform
import org.ukui.quick.items 1.0 as UkuiItems
import AppControls2 1.0 as AppControls2
UkuiItems.StyleBackground {
id: iconItem
property bool hold: false
property alias draggedIcon: itemLoader
radius: 8
useStyleTransparency: false
paletteRole: Platform.Theme.Text
alpha: hold ? 0 : control.containsPress ? 0.15 : control.containsMouse ? 0.08 : 0
// 52*52
UkuiItems.StyleBackground {
width: 52
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
z: -1
}
ColumnLayout {
id: itemLayout
width: parent.width
anchors.top: parent.top
anchors.topMargin: 8
anchors.horizontalCenter: parent.horizontalCenter
spacing: 6
ToolTip.visible: iconText.truncated && control.containsMouse
ToolTip.text: model.name
ToolTip.delay: 500
Item {
id: loaderBase
Layout.preferredHeight: 48
Layout.preferredWidth: 48
Layout.alignment: Qt.AlignHCenter
Loader {
id: itemLoader
width: loaderBase.width
height: loaderBase.height
x: 0; y: 0
Drag.active: control.drag.active
Drag.source: itemLoader
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
Drag.onActiveChanged: {
if (Drag.active) {
itemLoader.parent = favoriteView;
} else {
if (dragTypeIsMerge && (model.id !== mergeToAppId) && (model.type === DataType.Normal)) {
iconItem.hold = false;
} else {
iconResetAnimation.start();
}
}
}
ParallelAnimation {
id: iconResetAnimation
NumberAnimation {
target: itemLoader
property: "x"
to: container.x + loaderBase.x
easing.type: Easing.OutQuad
duration: 300
}
NumberAnimation {
target: itemLoader
property: "y"
to: (container.y + 8) - favoriteView.contentY
easing.type: Easing.OutQuad
duration: 300
}
onFinished: {
iconItem.hold = false;
itemLoader.parent = loaderBase;
itemLoader.x = 0; itemLoader.y = 0;
}
}
property int visualIndex: 0
property string sourceId: ""
property bool isFolder: model.type === DataType.Folder
property string icon: model.icon
property bool isFavorite: true
sourceComponent: {
if (type === DataType.Normal) {
return appIconComponent;
}
if (type === DataType.Folder) {
return folderIconComponent;
}
}
}
}
UkuiItems.StyleText {
id: iconText
Layout.fillWidth: true
text: name
elide: Text.ElideRight
paletteRole: Platform.Theme.Text
horizontalAlignment: Text.AlignHCenter
opacity: !itemLoader.Drag.active
Behavior on opacity {
NumberAnimation { duration: 150}
}
}
}
Component {
id: appIconComponent
Image {
id: iconImage
sourceSize.height: height
sourceSize.width: width
source: icon
cache: false
function itemClicked(mouseButton) {
if (mouseButton === Qt.RightButton) {
visualModel.model.openMenu(index)
} else {
var data = {"id": model.id};
send(data);
}
}
}
}
Component {
id: folderIconComponent
Item {
AppControls2.FolderIcon {
id: folderIcon
width: 40
height: 40
anchors.centerIn: parent
rows: 2; columns: 2
spacing: 2; padding: 2
icons: icon
radius: 4; alpha: 0.25
}
function itemClicked(mouseButton) {
if (mouseButton === Qt.RightButton) {
visualModel.model.openMenu(index);
} else {
var x = mapToGlobal(0,0).x;
var y = mapToGlobal(0,0).y
openFolderSignal(id, name, x, y);
//
favoriteView.isContentShow = false;
opacity = 0;
control.enabled = false;
control.hoverEnabled = false;
}
}
}
}
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);
} else {
visualModel.model.addAppsToNewFolder(model.id, mergeToAppId);
}
} else if (exchangedStartIndex !== itemLoader.visualIndex) {
visualModel.model.exchangedAppsOrder(exchangedStartIndex, itemLoader.visualIndex);
}
}
}
}

View File

@ -31,13 +31,35 @@ UkuiMenuExtension {
id: viewMouseArea id: viewMouseArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: { onClicked: {
folderLoader.isFolderOpened = false; if (mouse.button === Qt.RightButton) {
favoriteView.visible = true; menu.open();
favoriteView.isContentShow = true; } else {
} if (mainWindow.editMode) {
mainWindow.editMode = false;
}
folderLoader.isFolderOpened = false;
favoriteView.visible = true;
favoriteView.isContentShow = true;
}
}
UkuiItems.Menu {
id: menu
content: [
UkuiItems.MenuItem {
text: qsTr("Enable editing mode")
onClicked: mainWindow.editMode = true;
},
UkuiItems.MenuItem {
text: qsTr("Remove all favorite apps")
onClicked: extensionData.favoriteAppsModel.clearFavorites();
}
]
}
UkuiItems.StyleBackground { UkuiItems.StyleBackground {
anchors.top: parent.top anchors.top: parent.top
width: parent.width; height: 1 width: parent.width; height: 1
@ -58,17 +80,38 @@ UkuiMenuExtension {
Component.onDestruction: favoriteView.openFolderSignal.disconnect(initFolder) Component.onDestruction: favoriteView.openFolderSignal.disconnect(initFolder)
} }
FavoriteGridView { Item {
id: favoriteView
anchors.fill: parent anchors.fill: parent
anchors.bottomMargin: 8
anchors.leftMargin: 16 anchors.leftMargin: 16
anchors.topMargin: 12 anchors.rightMargin: 16
anchors.bottomMargin: 6 anchors.topMargin: 8
Component.onCompleted: {
favoriteView.viewModel.model = extensionData.favoriteAppsModel //
folderLoader.turnPageFinished.connect(contentShowFinished) DropArea {
anchors.fill: parent
onEntered: {
if (drag.source.isFavorite) {
favoriteView.dragTypeIsMerge = false;
} else {
var id = drag.source.id;
extensionData.favoriteAppsModel.addAppToFavorites(id);
}
}
} }
Component.onDestruction: folderLoader.turnPageFinished.disconnect(contentShowFinished)
} FavoriteGridView {
id: favoriteView
width: parent.width
height: (contentHeight > parent.height) ? parent.height : contentHeight
interactive: contentHeight > parent.height
Component.onCompleted: {
favoriteView.viewModel.model = extensionData.favoriteAppsModel
folderLoader.turnPageFinished.connect(contentShowFinished)
}
Component.onDestruction: folderLoader.turnPageFinished.disconnect(contentShowFinished)
}
}
} }
} }

View File

@ -28,19 +28,21 @@ import AppControls2 1.0 as AppControls2
GridView { GridView {
id: favoriteView id: favoriteView
cellWidth: itemHeight + spacing; cellHeight: cellWidth cellWidth: width / column
cellHeight: cellWidth + 8
signal openFolderSignal(string folderId, string folderName, int x, int y) signal openFolderSignal(string folderId, string folderName, int x, int y)
signal contentShowFinished() signal contentShowFinished()
property bool isContentShow: true property bool isContentShow: true
property int exchangedStartIndex: 0
property int spacing: 4 property int spacing: 4
property int itemHeight: 84 property int itemHeight: 84
property int column: Math.floor(width / cellWidth) property int column: 5
property alias viewModel: visualModel property alias viewModel: visualModel
property string mergeToAppId: "" property string mergeToAppId: ""
property bool isMergeToFolder: false property bool isMergeToFolder: false
property bool dragTypeIsMerge: false property bool dragTypeIsMerge: false
property int exchangedStartIndex: 0
state: isContentShow ? "contentShow" : "contentHidden" state: isContentShow ? "contentShow" : "contentHidden"
states: [ states: [
@ -116,7 +118,7 @@ GridView {
width: favoriteView.cellWidth width: favoriteView.cellWidth
height: favoriteView.cellHeight height: favoriteView.cellHeight
property int visualIndex: DelegateModel.itemsIndex property int visualIndex: DelegateModel.itemsIndex
Binding { target: iconItem; property: "visualIndex"; value: visualIndex } Binding { target: iconItem.draggedIcon; property: "visualIndex"; value: visualIndex }
states: State { states: State {
when: activeFocus when: activeFocus
PropertyChanges { PropertyChanges {
@ -129,31 +131,13 @@ GridView {
send(data); send(data);
} }
Timer {
id: delegateDropTimer
property int timeOutCount: 0
interval: 300
repeat: true
onTriggered: {
++timeOutCount;
if (timeOutCount == 1) {
mergeToAppId = model.id;
isMergeToFolder = (model.type === DataType.Folder);
dragTypeIsMerge = true;
}
}
onRunningChanged:timeOutCount = 0
}
DropArea { DropArea {
id: delegateDropArea id: delegateDropArea
width: 48; height: 48 anchors.fill: parent
anchors.top: parent.top anchors.margins: 14
anchors.topMargin: 8
anchors.horizontalCenter: parent.horizontalCenter
property string enterSourceId: "" property string enterSourceId: ""
// drag.source [iconItem.draggedIcon]
onEntered: { onEntered: {
if (drag.source.isFolder) { if (drag.source.isFolder) {
return; return;
@ -173,277 +157,60 @@ GridView {
} }
} }
UkuiItems.StyleBackground { Item {
id: iconItem anchors.fill: parent
height: favoriteView.itemHeight; width: height anchors.margins: 2
property bool hold: false
property int visualIndex: 0
property string sourceId: ""
property bool isFolder: model.type === DataType.Folder
x: 0; y: 0
radius: 8
useStyleTransparency: false
paletteRole: Platform.Theme.Text
alpha: hold ? 0 : control.containsPress ? 0.15 : control.containsMouse ? 0.08 : 0
Behavior on scale { Timer {
NumberAnimation { duration: 300; easing.type: Easing.InOutCubic } id: delegateDropTimer
} property int timeOutCount: 0
interval: 300
repeat: true
UkuiItems.StyleBackground { onTriggered: {
width: 52 ++timeOutCount;
height: width if (timeOutCount == 1) {
radius: 14 mergeToAppId = model.id;
anchors.top: parent.top isMergeToFolder = (model.type === DataType.Folder);
anchors.margins: 6 dragTypeIsMerge = true;
anchors.horizontalCenter: parent.horizontalCenter
paletteRole: Platform.Theme.Text
useStyleTransparency: false
alpha: (delegateDropArea.enterSourceId !== model.id)
&& delegateDropArea.containsDrag && dragTypeIsMerge ? 0.15 : 0
z: -1
}
Loader {
id: itemLoader
anchors.centerIn: parent
property int index: model.index
property int type: model.type
property string id: model.id
property string name: model.name
property string icon: model.icon
sourceComponent: {
if (type === DataType.Normal) {
return appItemComponent;
}
if (type === DataType.Folder) {
return folderItemComponent;
}
}
}
Component {
id: appItemComponent
Item {
height: iconItem.height
width: iconItem.width
ToolTip.visible: iconText.truncated && control.containsMouse
ToolTip.text: model.name
ToolTip.delay: 500
function itemClicked(mouseButton) {
if (mouseButton === Qt.RightButton) {
visualModel.model.openMenu(index)
} else {
var data = {"id": model.id};
send(data);
}
}
ColumnLayout {
width: parent.width
anchors.top: parent.top
anchors.topMargin: 8
anchors.horizontalCenter: parent.horizontalCenter
spacing: 6
Image {
id: iconImage
Layout.preferredHeight: 48
Layout.preferredWidth: 48
sourceSize.height: height
sourceSize.width: width
source: icon
cache: false
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
}
UkuiItems.StyleText {
id: iconText
Layout.fillWidth: true
text: name
elide: Text.ElideRight
paletteRole: Platform.Theme.Text
horizontalAlignment: Text.AlignHCenter
}
}
Loader {
id: tag
visible: mainWindow.editMode
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 ? editImage : null
}
}
}
Component {
id: folderItemComponent
UkuiItems.StyleBackground {
height: iconItem.height
width: iconItem.width - 14
useStyleTransparency: false
paletteRole: Platform.Theme.Light
radius: 6
alpha: parent.containsPress ? 0.25 : parent.containsMouse ? 0.15 : 0.00
function itemClicked(mouseButton) {
if (mouseButton === Qt.RightButton) {
visualModel.model.openMenu(index);
} else {
var x = mapToGlobal(0,0).x;
var y = mapToGlobal(0,0).y
openFolderSignal(id, name, x, y);
//
favoriteView.isContentShow = false;
opacity = 0;
control.enabled = false;
control.hoverEnabled = false;
}
}
Item {
id: folderItem
property bool isSelect: false
anchors.horizontalCenter: parent.horizontalCenter
height: 40; width: 40
anchors.top: parent.top
anchors.topMargin: 12
UkuiItems.StyleBackground {
anchors.fill: parent
paletteRole: Palette.Text
useStyleTransparency: false
alpha: 0.25
radius: 24
visible: folderItem.isSelect
}
AppControls2.FolderIcon {
id: folderIcon
anchors.fill: parent
rows: 2; columns: 2
spacing: 2; padding: 2
icons: icon
radius: 4; alpha: folderItem.isSelect ? 0 : 0.25
anchors.centerIn: parent
}
}
UkuiItems.StyleText {
id: folderText
anchors.bottom: parent.bottom
anchors.bottomMargin: 14
width: parent.width
horizontalAlignment: Text.AlignHCenter
anchors.horizontalCenter: parent.horizontalCenter
elide: Text.ElideRight
text: name
} }
} }
onRunningChanged: timeOutCount = 0
} }
MouseArea { FavoriteDelegate {
id: control id: iconItem
anchors.fill: parent anchors.fill: parent
hoverEnabled: true property int visualIndex: container.DelegateModel.itemsIndex
pressAndHoldInterval: 300
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
itemLoader.item.itemClicked(mouse.button);
}
onPressAndHold: {
if (mouse.button === Qt.LeftButton) {
drag.target = iconItem;
iconItem.hold = true;
exchangedStartIndex = iconItem.visualIndex;
iconItem.sourceId = model.id;
}
}
onReleased: {
iconItem.hold = false;
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) { Loader {
favoriteView.interactive = false; id: tag
} else { anchors.top: parent.top
favoriteView.interactive = true; anchors.right: parent.right
if (dragTypeIsMerge && (model.id !== mergeToAppId) && (model.type === DataType.Normal)) { anchors.rightMargin: 10
if (isMergeToFolder) { Component {
visualModel.model.addAppToFolder(model.id, mergeToAppId); id: editImage
} else { UkuiItems.Button {
visualModel.model.addAppsToNewFolder(model.id, mergeToAppId); 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);
} }
} else if (exchangedStartIndex != iconItem.visualIndex) { background.radius: width / 2
visualModel.model.exchangedAppsOrder(exchangedStartIndex, iconItem.visualIndex); icon.source: "ukui-cancel-star-symbolic"
} }
} }
}
Drag.active: control.drag.active sourceComponent: mainWindow.editMode && (type === DataType.Normal) ? editImage : null
Drag.source: iconItem
Drag.hotSpot.x: iconItem.width / 2
Drag.hotSpot.y: iconItem.height / 2
Drag.onActiveChanged: {
if (Drag.active) {
iconItem.parent = favoriteView;
} else {
iconResetAnimation.start();
}
}
ParallelAnimation {
id: iconResetAnimation
NumberAnimation {
target: iconItem
property: "x"
to: container.x
easing.type: Easing.OutQuad
duration: 300
}
NumberAnimation {
target: iconItem
property: "y"
to: container.y - favoriteView.contentY
easing.type: Easing.OutQuad
duration: 300
}
onFinished: {
iconItem.parent = container;
iconItem.x = 0; iconItem.y = 0;
}
} }
} }
} }

View File

@ -24,6 +24,7 @@
<file>AppControls2/LabelItem.qml</file> <file>AppControls2/LabelItem.qml</file>
<file>AppControls2/FolderItem.qml</file> <file>AppControls2/FolderItem.qml</file>
<file>extensions/FavoriteExtension.qml</file> <file>extensions/FavoriteExtension.qml</file>
<file>extensions/FavoriteDelegate.qml</file>
<file>extensions/FavoriteGridView.qml</file> <file>extensions/FavoriteGridView.qml</file>
<file>extensions/FolderGridView.qml</file> <file>extensions/FolderGridView.qml</file>
<file>AppControls2/RoundButton.qml</file> <file>AppControls2/RoundButton.qml</file>

View File

@ -103,6 +103,7 @@ void AppFavoritesModel::getFoldersId()
QVector<int> foldersId; QVector<int> foldersId;
for (const auto &folder : FavoriteFolderHelper::instance()->folderData()) { for (const auto &folder : FavoriteFolderHelper::instance()->folderData()) {
foldersId.append(folder.getId()); foldersId.append(folder.getId());
FavoritesConfig::instance().insertValue(FOLDER_ID_SCHEME + QString::number(folder.getId()));
} }
m_folders.swap(foldersId); m_folders.swap(foldersId);
} }

View File

@ -148,7 +148,17 @@ void FavoriteFolderHelper::addAppsToNewFolder(const QString &idFrom, const QStri
folder.apps.append(idTo); folder.apps.append(idTo);
insertFolder(folder); insertFolder(folder);
Q_EMIT folderAdded(folder.id, FavoritesConfig::instance().getOrderById(APP_ID_SCHEME + idTo)); // 确定folder位置
int orderTo = FavoritesConfig::instance().getOrderById(APP_ID_SCHEME + idTo);
int orderFrom = FavoritesConfig::instance().getOrderById(APP_ID_SCHEME + idFrom);
int folderOrder;
if (orderFrom > orderTo) {
folderOrder = orderTo;
} else {
folderOrder = orderTo - 1;
}
Q_EMIT folderAdded(folder.id, folderOrder);
forceSync(); forceSync();
} }

View File

@ -138,11 +138,19 @@ void FavoritesConfig::initConfig()
m_favoritesList.clear(); m_favoritesList.clear();
QJsonArray array = jsonDocument.array(); QJsonArray array = jsonDocument.array();
QJsonArray newArray;
for (int i = 0; i < array.size(); i++) { for (int i = 0; i < array.size(); i++) {
if (array.at(i).toObject().contains("id")) { if (array.at(i).isString()) {
m_favoritesList.append(array.at(i).toString()); m_favoritesList.append(array.at(i).toString());
newArray.append(array.at(i));
} }
} }
file.open(QFile::WriteOnly);
jsonDocument.setArray(newArray);
file.write(jsonDocument.toJson());
file.flush();
file.close();
} }
} // UkuiMenu } // UkuiMenu