mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-29 15:03:09 +00:00
This converts many of the animation durations to Kirigami units so that they can be controlled system wide. It also speeds up several of the animation durations (ex. in folio) from 800ms to 400ms to improve the feel and responsiveness of the shell.
311 lines
12 KiB
QML
311 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 plasma.applet.org.kde.plasma.mobile.homescreen.folio 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
|
|
|
|
MobileShell.HapticsEffect {
|
|
id: haptics
|
|
}
|
|
|
|
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)) * 0.9 - 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;
|
|
folio.HomeScreenState.folderPageContentWidth = (folderBackground.width - margin * 2);
|
|
}
|
|
|
|
function updateContentHeight() {
|
|
let margin = folderBackground.margin;
|
|
folio.HomeScreenState.folderPageContentHeight = (folderBackground.height - margin * 2);
|
|
}
|
|
|
|
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 int gridLength: folio.HomeScreenState.folderGridLength
|
|
|
|
readonly property int margin: Kirigami.Units.largeSpacing
|
|
readonly property int maxLength: Math.min(root.width - Kirigami.Units.gridUnit * 4, root.height - Kirigami.Units.gridUnit * 2)
|
|
|
|
readonly property int pageSize: Math.min(maxLength, (folio.FolioSettings.delegateIconSize + Kirigami.Units.gridUnit * 3) * gridLength + Kirigami.Units.gridUnit * 2)
|
|
|
|
width: pageSize - margin * 2
|
|
height: pageSize
|
|
|
|
QQC2.PageIndicator {
|
|
visible: count > 1
|
|
Kirigami.Theme.inherit: false
|
|
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.bottom: parent.bottom
|
|
|
|
currentIndex: folio.HomeScreenState.currentFolderPage
|
|
count: folio.HomeScreenState.currentFolder ? folio.HomeScreenState.currentFolder.applications.numberOfPages : 0
|
|
}
|
|
|
|
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
|
|
|
|
readonly property var delegateModel: model.delegate
|
|
readonly property int index: model.index
|
|
|
|
readonly property int folderCellSize: folio.HomeScreenState.folderPageContentWidth / folderBackground.gridLength
|
|
readonly property int cellWidth: folio.HomeScreenState.pageCellWidth
|
|
readonly property int cellHeight: folio.HomeScreenState.pageCellHeight
|
|
|
|
readonly property bool outsideView: {
|
|
const appPosition = x + (cellWidth - folderCellSize) / 2 + folio.HomeScreenState.folderViewX;
|
|
return (appPosition <= -folderCellSize || appPosition >= folderBackground.width);
|
|
}
|
|
|
|
readonly property var dragState: folio.HomeScreenState.dragState
|
|
readonly property bool isDropPositionThis: dragState.candidateDropPosition.location === Folio.DelegateDragPosition.Folder &&
|
|
dragState.candidateDropPosition.folderPosition === index
|
|
|
|
// get the index position value so we can animate them
|
|
property double columnValue: model.columnIndex
|
|
property double rowValue: model.rowIndex
|
|
property double pageValue: model.pageIndex
|
|
Behavior on columnValue {
|
|
NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad }
|
|
}
|
|
Behavior on rowValue {
|
|
NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad }
|
|
}
|
|
Behavior on pageValue {
|
|
NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad }
|
|
}
|
|
|
|
// multiply the index values by the cell size to get the actual position
|
|
readonly property int positionColumn: folderCellSize * columnValue
|
|
readonly property int positionRow: folderCellSize * rowValue
|
|
|
|
x: (folderCellSize - cellWidth) / 2 + folderBackground.margin + pageValue * folderBackground.width + positionColumn
|
|
y: (folderCellSize - cellHeight) / 2 + folderBackground.margin + positionRow
|
|
|
|
implicitWidth: cellWidth
|
|
implicitHeight: cellHeight
|
|
width: cellWidth
|
|
height: cellHeight
|
|
|
|
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)
|
|
enabled: !delegate.outsideView
|
|
|
|
// don't show label in drag and drop mode
|
|
labelOpacity: delegate.opacity
|
|
|
|
onPressAndHold: {
|
|
// prevent editing if lock layout is enabled
|
|
if (folio.FolioSettings.lockLayout) return;
|
|
|
|
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();
|
|
haptics.buttonVibrate();
|
|
}
|
|
|
|
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")
|
|
enabled: !folio.FolioSettings.lockLayout
|
|
onTriggered: root.folder.removeDelegate(delegate.index)
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|