mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-27 14:33:08 +00:00
303 lines
12 KiB
QML
303 lines
12 KiB
QML
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
|
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
|
|
|
import QtQuick
|
|
import QtQuick.Layouts
|
|
import QtQuick.Effects
|
|
import QtQuick.Controls as QQC2
|
|
|
|
import org.kde.kirigami 2.20 as Kirigami
|
|
|
|
import org.kde.plasma.private.mobileshell as MobileShell
|
|
import org.kde.private.mobile.homescreen.folio 1.0 as Folio
|
|
|
|
import "./private"
|
|
import "./delegate"
|
|
|
|
Folio.DelegateTouchArea {
|
|
id: root
|
|
property Folio.HomeScreen folio
|
|
|
|
property var homeScreen
|
|
|
|
// the position on the screen for animations to start from
|
|
property real folderPositionX
|
|
property real folderPositionY
|
|
|
|
property Folio.FolioApplicationFolder folder: folio.HomeScreenState.currentFolder
|
|
|
|
onClicked: close();
|
|
|
|
function close() {
|
|
folio.HomeScreenState.closeFolder();
|
|
}
|
|
|
|
Connections {
|
|
target: folio.HomeScreenState
|
|
|
|
function onFolderAboutToOpen(x, y) {
|
|
root.folderPositionX = x - folio.HomeScreenState.viewLeftPadding;
|
|
root.folderPositionY = y - folio.HomeScreenState.viewRightPadding;
|
|
}
|
|
}
|
|
|
|
FolderViewTitle {
|
|
id: titleText
|
|
folio: root.folio
|
|
width: root.width
|
|
|
|
// have to use y instead of anchors to avoid animations
|
|
y: Math.round((root.height / 2) - (folderBackground.height / 2) - Kirigami.Units.gridUnit - height)
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
|
|
folder: root.folder
|
|
|
|
opacity: (root.opacity === 1) ? 1 : 0
|
|
Behavior on opacity {
|
|
NumberAnimation { duration: Kirigami.Units.shortDuration }
|
|
}
|
|
}
|
|
|
|
function updateContentWidth() {
|
|
let margin = folderBackground.margin;
|
|
let columns = Math.floor((folderBackground.width - margin * 2) / folio.HomeScreenState.pageCellWidth);
|
|
folio.HomeScreenState.folderPageContentWidth = columns * folio.HomeScreenState.pageCellWidth;
|
|
}
|
|
|
|
function updateContentHeight() {
|
|
let margin = folderBackground.margin;
|
|
let rows = Math.floor((folderBackground.height - margin * 2) / folio.HomeScreenState.pageCellHeight);
|
|
folio.HomeScreenState.folderPageContentHeight = rows * folio.HomeScreenState.pageCellHeight;
|
|
}
|
|
|
|
Connections {
|
|
target: folio.HomeScreenState
|
|
|
|
function onPageCellWidthChanged() {
|
|
root.updateContentWidth();
|
|
root.updateContentHeight();
|
|
}
|
|
|
|
function onPageCellHeightChanged() {
|
|
root.updateContentWidth();
|
|
root.updateContentHeight();
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: folderBackground
|
|
color: Qt.rgba(255, 255, 255, 0.3)
|
|
radius: Kirigami.Units.gridUnit
|
|
|
|
readonly property real margin: Kirigami.Units.largeSpacing
|
|
readonly property real maxLength: Math.min(root.width * 0.9, root.height * 0.9)
|
|
|
|
width: {
|
|
let perRow = 0;
|
|
if (root.width < root.height) {
|
|
perRow = Math.floor((maxLength - margin * 2) / folio.HomeScreenState.pageCellWidth);
|
|
} else {
|
|
// try to get the same number of rows as columns
|
|
perRow = Math.floor((maxLength - margin * 2) / folio.HomeScreenState.pageCellHeight);
|
|
}
|
|
return Math.min(root.width * 0.9, perRow * folio.HomeScreenState.pageCellWidth + margin * 2);
|
|
}
|
|
height: {
|
|
let perRow = 0;
|
|
if (root.width < root.height) {
|
|
// try to get the same number of rows as columns
|
|
perRow = Math.floor((maxLength - margin * 2) / folio.HomeScreenState.pageCellWidth);
|
|
} else {
|
|
perRow = Math.floor((maxLength - margin * 2) / folio.HomeScreenState.pageCellHeight);
|
|
}
|
|
return Math.min(root.height * 0.9, perRow * folio.HomeScreenState.pageCellHeight + margin * 2);
|
|
}
|
|
|
|
onWidthChanged: {
|
|
folio.HomeScreenState.folderPageWidth = width;
|
|
root.updateContentWidth();
|
|
root.updateContentHeight();
|
|
}
|
|
onHeightChanged: {
|
|
folio.HomeScreenState.folderPageHeight = height;
|
|
root.updateContentWidth();
|
|
root.updateContentHeight();
|
|
}
|
|
|
|
x: {
|
|
const folderPos = root.folderPositionX;
|
|
const centerX = (root.width / 2) - (width / 2);
|
|
return Math.round(folderPos + (centerX - folderPos) * folio.HomeScreenState.folderOpenProgress);
|
|
}
|
|
y: {
|
|
const folderPos = root.folderPositionY;
|
|
const centerY = (root.height / 2) - (height / 2);
|
|
return Math.round(folderPos + (centerY - folderPos) * folio.HomeScreenState.folderOpenProgress);
|
|
}
|
|
|
|
transform: [
|
|
Scale {
|
|
origin.x: 0
|
|
origin.y: 0
|
|
|
|
xScale: {
|
|
const iconSize = folio.FolioSettings.delegateIconSize;
|
|
const fullWidth = folderBackground.width;
|
|
const candidate = iconSize + (fullWidth - iconSize) * folio.HomeScreenState.folderOpenProgress;
|
|
return Math.max(0, Math.min(1, candidate / fullWidth));
|
|
}
|
|
yScale: {
|
|
const iconSize = folio.FolioSettings.delegateIconSize;
|
|
const fullHeight = folderBackground.height;
|
|
const candidate = iconSize + (fullHeight - iconSize) * folio.HomeScreenState.folderOpenProgress;
|
|
return Math.max(0, Math.min(1, candidate / fullHeight));
|
|
}
|
|
}
|
|
]
|
|
|
|
MouseArea {
|
|
id: captureTouches
|
|
anchors.fill: parent
|
|
|
|
// clip the pages
|
|
layer.enabled: true
|
|
|
|
Item {
|
|
id: contentContainer
|
|
x: folio.HomeScreenState.folderViewX
|
|
|
|
Repeater {
|
|
model: root.folder ? root.folder.applications : []
|
|
|
|
delegate: Item {
|
|
id: delegate
|
|
|
|
property var delegateModel: model.delegate
|
|
property int index: model.index
|
|
|
|
property var dragState: folio.HomeScreenState.dragState
|
|
property bool isDropPositionThis: dragState.candidateDropPosition.location === Folio.DelegateDragPosition.Folder &&
|
|
dragState.candidateDropPosition.folderPosition === index
|
|
|
|
x: model.xPosition
|
|
y: model.yPosition
|
|
|
|
Behavior on x {
|
|
NumberAnimation { duration: 250; easing.type: Easing.InOutQuad }
|
|
}
|
|
Behavior on y {
|
|
NumberAnimation { duration: 250; easing.type: Easing.InOutQuad }
|
|
}
|
|
|
|
implicitWidth: folio.HomeScreenState.pageCellWidth
|
|
implicitHeight: folio.HomeScreenState.pageCellHeight
|
|
width: folio.HomeScreenState.pageCellWidth
|
|
height: folio.HomeScreenState.pageCellHeight
|
|
|
|
Loader {
|
|
id: delegateLoader
|
|
anchors.fill: parent
|
|
|
|
sourceComponent: {
|
|
if (delegate.delegateModel.type === Folio.FolioDelegate.Application) {
|
|
return appComponent;
|
|
} else {
|
|
return noneComponent;
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: noneComponent
|
|
|
|
Item {}
|
|
}
|
|
|
|
Component {
|
|
id: appComponent
|
|
|
|
AppDelegate {
|
|
id: appDelegate
|
|
folio: root.folio
|
|
application: delegate.delegateModel.application
|
|
|
|
// do not show if the drop animation is running to this delegate
|
|
visible: !(root.homeScreen.dropAnimationRunning && delegate.isDropPositionThis)
|
|
|
|
// don't show label in drag and drop mode
|
|
labelOpacity: delegate.opacity
|
|
|
|
onPressAndHold: {
|
|
let mappedCoords = root.homeScreen.prepareStartDelegateDrag(delegate.delegateModel, appDelegate.delegateItem);
|
|
folio.HomeScreenState.startDelegateFolderDrag(
|
|
mappedCoords.x,
|
|
mappedCoords.y,
|
|
appDelegate.pressPosition.x,
|
|
appDelegate.pressPosition.y,
|
|
root.folder,
|
|
delegate.index
|
|
);
|
|
|
|
contextMenu.open();
|
|
}
|
|
|
|
onPressAndHoldReleased: {
|
|
// cancel the event if the delegate is not dragged
|
|
if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.AwaitingDraggingDelegate) {
|
|
homeScreen.cancelDelegateDrag();
|
|
}
|
|
}
|
|
|
|
onRightMousePress: {
|
|
contextMenu.open();
|
|
}
|
|
|
|
ContextMenuLoader {
|
|
id: contextMenu
|
|
|
|
// close menu when drag starts
|
|
Connections {
|
|
target: folio.HomeScreenState
|
|
|
|
function onSwipeStateChanged() {
|
|
if (folio.HomeScreenState.swipeState === Folio.HomeScreenState.DraggingDelegate) {
|
|
contextMenu.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
actions: [
|
|
Kirigami.Action {
|
|
icon.name: "emblem-favorite"
|
|
text: i18n("Remove")
|
|
onTriggered: root.folder.removeDelegate(delegate.index)
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QQC2.PageIndicator {
|
|
visible: count > 1
|
|
Kirigami.Theme.inherit: false
|
|
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
|
|
|
// have to use y instead of anchors to avoid animations
|
|
y: Math.round((root.height / 2) + (folderBackground.height / 2) + Kirigami.Units.largeSpacing)
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
currentIndex: folio.HomeScreenState.currentFolderPage
|
|
count: folio.HomeScreenState.currentFolder ? folio.HomeScreenState.currentFolder.applications.numberOfPages : 0
|
|
|
|
opacity: (root.opacity === 1) ? 1 : 0
|
|
Behavior on opacity {
|
|
NumberAnimation { duration: Kirigami.Units.shortDuration }
|
|
}
|
|
}
|
|
}
|