shift-shell/containments/panel/package/contents/ui/SlidingContainer.qml
Marco Martin 5da75348f7 Use Plasma Theme and layout fixes
This moves all background rendering of elements in the top sliding panel to use the plasma theme, making full use of themes that can be downloaded from the store, doing so decreases the usage of the Qt.GraphicalEffects import which is slow and not in Qt6. There are some layout and behavioral fixes especially in widescreen mode
2021-06-09 08:44:48 +00:00

230 lines
7.4 KiB
QML

/*
* SPDX-FileCopyrightText: 2014 Marco Martin <notmart@gmail.com>
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
NanoShell.FullScreenOverlay {
id: window
property int offset: 0 // slide progress
property int openThreshold: PlasmaCore.Units.gridUnit * 2
property bool userInteracting: false
property bool initiallyOpened: false // whether the panel is already open after a touch release (then don't restrict to collapsed height)
// height when quicksettings is fully open
required property int fullyOpenHeight
// flickable contentY
readonly property int openedContentY: wideScreen || offset > (collapsedHeight + openThreshold) ? -topEmptyAreaHeight : offsetToContentY(collapsedHeight)
readonly property int closedContentY: mainFlickable.contentHeight
readonly property bool wideScreen: false//width > height || width > units.gridUnit * 45
readonly property int drawerWidth: 400//wideScreen ? contentItem.implicitWidth : width
property int drawerX: 0
property alias fixedArea: mainScope
property alias flickable: mainFlickable
color: "transparent"
property alias contentItem: contentArea.contentItem
property int topPanelHeight
property int collapsedHeight
property real topEmptyAreaHeight
property bool appletsShown: false // whether notifications or media player applets are shown
signal closed
width: Screen.width
height: Screen.height
Component.onCompleted: plasmoid.nativeInterface.panel = window;
onInitiallyOpenedChanged: {
if (initiallyOpened) mainFlickable.focus = true;
}
function offsetToContentY(num) { return -num + window.fullyOpenHeight; }
function contentYToOffset(num) { return offsetToContentY(num); }
// avoids binding loops
function updateOffset(delta) {
// only go to collapsed height for mousearea when not widescreen
let maximum = window.wideScreen ? window.fullyOpenHeight : collapsedHeight + openThreshold / 2;
offset = Math.max(0, Math.min(maximum, offset + delta));
if (!mainFlickable.moving && !mainFlickable.dragging && !mainFlickable.flicking) {
mainFlickable.contentY = offsetToContentY(window.offset);
}
}
enum MovementDirection {
None = 0,
Up,
Down
}
property int direction: SlidingContainer.MovementDirection.None
function cancelAnimations() {
closeAnim.stop();
openAnim.stop();
}
function open() {
cancelAnimations();
openAnim.restart();
initiallyOpened = true;
}
function close() {
cancelAnimations();
closeAnim.restart();
initiallyOpened = false;
}
function expand() {
cancelAnimations();
expandAnim.restart();
initiallyOpened = true;
}
function updateState() {
cancelAnimations();
if (window.offset <= 0) {
// close immediately, so that we don't have to wait units.longDuration
window.visible = false;
window.closed();
close();
} else if (window.direction === SlidingContainer.MovementDirection.None) {
if (window.offset < openThreshold) {
close();
} else {
open();
}
} else if (offset > openThreshold && window.direction === SlidingContainer.MovementDirection.Down) {
open();
} else if (mainFlickable.contentY > openThreshold) {
close();
} else {
open();
}
}
Timer {
id: updateStateTimer
interval: 0
onTriggered: updateState()
}
onActiveChanged: {
if (!active) {
close();
}
}
PropertyAnimation {
id: closeAnim
target: mainFlickable
properties: "contentY"
duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad
to: window.closedContentY
onFinished: {
window.visible = false;
}
}
PropertyAnimation {
id: openAnim
target: mainFlickable
properties: "contentY"
duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad
to: window.openedContentY
}
PropertyAnimation {
id: expandAnim
target: mainFlickable
properties: "contentY"
duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad
to: 0
}
// fullscreen background
Rectangle {
anchors.fill: parent
color: Qt.rgba(0, 0, 0, 0.75)
opacity: (appletsShown ? 0.85 : 0.6) * Math.max(0, Math.min(1, offset / window.collapsedHeight))
Behavior on opacity { // smooth opacity changes
NumberAnimation { duration: 70 }
}
}
PlasmaCore.ColorScope {
id: mainScope
colorGroup: PlasmaCore.Theme.ViewColorGroup
anchors.fill: parent
Flickable {
id: mainFlickable
anchors.fill: parent
property real oldContentY
contentY: contentHeight
onContentYChanged: {
if (contentY === oldContentY) {
window.direction = SlidingContainer.MovementDirection.None;
} else {
window.direction = contentY > oldContentY ? SlidingContainer.MovementDirection.Up : SlidingContainer.MovementDirection.Down;
}
window.offset = contentYToOffset(contentY);
oldContentY = contentY;
// close panel immediately after panel is not shown, and the flickable is not being dragged
if (initiallyOpened && window.offset <= 0 && !mainFlickable.dragging && !closeAnim.running && !openAnim.running) {
window.updateState();
focus = false;
}
}
boundsMovement: Flickable.StopAtBounds
contentWidth: window.width
contentHeight: window.height
bottomMargin: window.height
onMovementStarted: {
window.cancelAnimations();
window.userInteracting = true;
}
onFlickStarted: window.userInteracting = true;
onMovementEnded: {
window.userInteracting = false;
window.updateState();
}
onFlickEnded: {
window.userInteracting = true;
window.updateState();
}
MouseArea {
id: dismissArea
z: 2
width: parent.width
height: mainFlickable.contentHeight
onClicked: window.close();
// actual sliding contents
PlasmaComponents.Control {
id: contentArea
z: 1
x: Math.max(0, Math.min(window.drawerX, window.width - window.drawerWidth))
width: Math.min(window.width, window.drawerWidth)
}
}
}
}
}