mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 06:13:09 +00:00
taskpanel/panel: make navbar and statusbar accessible from within fullscreen windows
These changes make the navigation and status bar accessible from within fullscreen applications by allowing the user to tap on the top or tap or drag on the bottom of the screen. Also, since the top panel is now over top of all fullscreen applications, this moves the action drawer swipe area back into the status bar. 
This commit is contained in:
parent
6aa5a2e0e0
commit
48dedcd546
14 changed files with 495 additions and 67 deletions
|
|
@ -44,6 +44,7 @@ public:
|
|||
bool pressed() const;
|
||||
|
||||
Q_INVOKABLE void setSkipSwipeThreshold(bool value);
|
||||
Q_INVOKABLE void resetSwipe();
|
||||
|
||||
Q_SIGNALS:
|
||||
void modeChanged();
|
||||
|
|
@ -82,8 +83,6 @@ private:
|
|||
void handleReleaseEvent(QPointerEvent *event, QPointF point);
|
||||
void handleMoveEvent(QPointerEvent *event, QPointF point);
|
||||
|
||||
void resetSwipe();
|
||||
|
||||
Mode m_mode = Mode::BothAxis;
|
||||
bool m_interactive = true;
|
||||
bool m_pressed = false;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import QtQuick.Controls 2.15
|
|||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.2
|
||||
|
||||
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
|
|
@ -153,7 +152,7 @@ Item {
|
|||
|
||||
function cancelAnimations() {
|
||||
root.state = "";
|
||||
}
|
||||
}
|
||||
|
||||
function open() {
|
||||
cancelAnimations();
|
||||
|
|
@ -273,35 +272,15 @@ Item {
|
|||
mode: MobileShell.SwipeArea.VerticalOnly
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
function startSwipeWithPoint(point) {
|
||||
function startSwipe() {
|
||||
root.cancelAnimations();
|
||||
root.dragging = true;
|
||||
|
||||
// Immediately open action drawer if we interact with it and it's already open
|
||||
// This allows us to have 2 quick flicks from minimized -> expanded
|
||||
if (root.intendedToBeVisible && !root.opened) {
|
||||
if (root.visible && !root.opened) {
|
||||
root.opened = true;
|
||||
}
|
||||
|
||||
// if the user swiped from the top left, otherwise it's from the top right
|
||||
if (!root.intendedToBeVisible) {
|
||||
if (point.x < root.width / 2) {
|
||||
root.openToPinnedMode = ShellSettings.Settings.actionDrawerTopLeftMode == ShellSettings.Settings.Pinned;
|
||||
} else {
|
||||
root.openToPinnedMode = ShellSettings.Settings.actionDrawerTopRightMode == ShellSettings.Settings.Pinned;
|
||||
}
|
||||
|
||||
if (root.intendedToBeVisible) {
|
||||
// ensure the action drawer state is consistent
|
||||
root.closeImmediately();
|
||||
}
|
||||
|
||||
actionDrawer.offset = 0;
|
||||
actionDrawer.oldOffset = 0;
|
||||
|
||||
intendedToBeVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
function endSwipe() {
|
||||
|
|
@ -313,7 +292,7 @@ Item {
|
|||
root.offset += deltaY;
|
||||
}
|
||||
|
||||
onSwipeStarted: (point) => startSwipeWithPoint(point)
|
||||
onSwipeStarted: startSwipe()
|
||||
onSwipeEnded: endSwipe()
|
||||
onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => moveSwipe(totalDeltaX, totalDeltaY, deltaX, deltaY)
|
||||
|
||||
|
|
@ -325,8 +304,6 @@ Item {
|
|||
id: contentContainer
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: root.opened || swipeArea.moving || drawerAnimation.running || root.offset > 0 || root.intendedToBeVisible ? 1 : 0
|
||||
|
||||
actionDrawer: root
|
||||
quickSettingsModel: root.quickSettingsModel
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,28 +33,21 @@ Window {
|
|||
* The ActionDrawer component.
|
||||
*/
|
||||
property alias actionDrawer: drawer
|
||||
property alias intendedToBeVisible: drawer.intendedToBeVisible
|
||||
property alias state: drawer.state
|
||||
|
||||
visible: true
|
||||
visible: drawer.intendedToBeVisible
|
||||
|
||||
color: "transparent"
|
||||
|
||||
Component.onCompleted: updateTouchArea()
|
||||
// set input to transparent when closing to prevent window from taking unwanted touch inputs
|
||||
onStateChanged: ShellUtil.setInputTransparent(window, state == "close")
|
||||
|
||||
function updateTouchArea() {
|
||||
if (state != "" && state != "close") {
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
window.raise();
|
||||
ShellUtil.setInputRegion(window, Qt.rect(0, 0, 0, 0));
|
||||
} else {
|
||||
ShellUtil.setInputRegion(window, Qt.rect(0, 0, window.width, MobileShell.Constants.topPanelHeight));
|
||||
}
|
||||
}
|
||||
|
||||
onWidthChanged: updateTouchArea()
|
||||
onIntendedToBeVisibleChanged: updateTouchArea()
|
||||
onStateChanged: updateTouchArea()
|
||||
|
||||
onActiveChanged: {
|
||||
if (!active) {
|
||||
drawer.close();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
// SPDX-FileCopyrightText: 2024 Micah Stanley <stanleymicah@proton.me>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.4
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.15
|
||||
import QtQuick.Shapes 1.8
|
||||
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool isHorizontal: false
|
||||
property real length: Kirigami.Units.gridUnit * 10
|
||||
property real offsetLimit: Kirigami.Units.gridUnit * 2
|
||||
|
||||
property real startPoint: 0
|
||||
property real sidePoint: 0
|
||||
property real offsetPoint: 0
|
||||
|
||||
visible: offsetPoint != 0
|
||||
|
||||
Shape {
|
||||
id: shape
|
||||
|
||||
readonly property int flip: root.offsetPoint > 0 ? -1 : 1
|
||||
readonly property real position: root.startPoint - root.length / 2
|
||||
readonly property real sp: Math.max(Math.min(root.sidePoint, root.length), -Kirigami.Units.gridUnit * 10)
|
||||
readonly property real op: Math.max(Math.min(-shape.calculateResistance(-root.offsetPoint, 0), 0), -root.offsetLimit + 3)
|
||||
|
||||
transform: [
|
||||
Translate {
|
||||
x: root.isHorizontal ? 0 : shape.position
|
||||
y: root.isHorizontal ? shape.position : 0
|
||||
}
|
||||
]
|
||||
|
||||
function calculateResistance(value : double, threshold : int) : double {
|
||||
if (value > threshold) {
|
||||
return threshold + Math.pow(value - threshold + 1, Math.max(0.8 - (value - threshold) / ((root.isHorizontal ? Screen.width : Screen.height - threshold) * 2), 0.65));
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var shapPath: [
|
||||
Qt.point(3 * shape.flip, 0),
|
||||
Qt.point(2 * shape.flip, 0),
|
||||
Qt.point(0, root.length * 0.2 + shape.sp * 0.16),
|
||||
Qt.point(shape.op, root.length * 0.5 + shape.sp * 0.35),
|
||||
Qt.point(0, root.length * 0.8 + shape.sp * 0.16),
|
||||
Qt.point(2 * shape.flip, root.length),
|
||||
Qt.point(3 * shape.flip, root.length),
|
||||
]
|
||||
|
||||
ShapePath {
|
||||
id: shapeVertical
|
||||
fillColor: "black"
|
||||
strokeColor: "black"
|
||||
|
||||
startX: shape.shapPath[0].x; startY: shape.shapPath[0].y
|
||||
PathCurve { x: root.isHorizontal ? shape.shapPath[1].x : shape.shapPath[1].y; y: root.isHorizontal ? shape.shapPath[1].y : shape.shapPath[1].x}
|
||||
PathCurve { x: root.isHorizontal ? shape.shapPath[2].x : shape.shapPath[2].y; y: root.isHorizontal ? shape.shapPath[2].y : shape.shapPath[2].x}
|
||||
PathCurve { x: root.isHorizontal ? shape.shapPath[3].x : shape.shapPath[3].y; y: root.isHorizontal ? shape.shapPath[3].y : shape.shapPath[3].x}
|
||||
PathCurve { x: root.isHorizontal ? shape.shapPath[4].x : shape.shapPath[4].y; y: root.isHorizontal ? shape.shapPath[4].y : shape.shapPath[4].x}
|
||||
PathCurve { x: root.isHorizontal ? shape.shapPath[5].x : shape.shapPath[5].y; y: root.isHorizontal ? shape.shapPath[5].y : shape.shapPath[5].x}
|
||||
PathCurve { x: root.isHorizontal ? shape.shapPath[6].x : shape.shapPath[6].y; y: root.isHorizontal ? shape.shapPath[6].y : shape.shapPath[6].x}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <QTextDocumentFragment>
|
||||
#include <QtWaylandClient/private/qwaylandwindow_p.h>
|
||||
#include <LayerShellQt/Window>
|
||||
|
||||
|
||||
#define FORMAT24H "HH:mm:ss"
|
||||
|
|
@ -105,6 +106,13 @@ void ShellUtil::setInputTransparent(QQuickWindow *window, bool transparent) {
|
|||
}
|
||||
}
|
||||
|
||||
void ShellUtil::setWindowLayer(QQuickWindow *window, LayerShellQt::Window::Layer layer) {
|
||||
if (window) {
|
||||
auto layerShellWindow = LayerShellQt::Window::get(window);
|
||||
layerShellWindow->setLayer(layer);
|
||||
}
|
||||
}
|
||||
|
||||
void ShellUtil::setInputRegion(QWindow *window, const QRect ®ion) {
|
||||
auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle());
|
||||
if (!waylandWindow) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <KConfigWatcher>
|
||||
#include <KIO/ApplicationLauncherJob>
|
||||
#include <KSharedConfig>
|
||||
#include <LayerShellQt/Window>
|
||||
|
||||
/**
|
||||
* Miscellaneous class to put utility functions used in the shell.
|
||||
|
|
@ -71,6 +72,11 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void setInputTransparent(QQuickWindow *window, bool transparent);
|
||||
|
||||
/**
|
||||
* Set the window layer
|
||||
*/
|
||||
Q_INVOKABLE void setWindowLayer(QQuickWindow *window, LayerShellQt::Window::Layer layer);
|
||||
|
||||
/**
|
||||
* Sets a region where inputs will get registered on a window.
|
||||
* Inputs outside the region will pass through to the surface below.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ ShellDBusClient::ShellDBusClient(QObject *parent)
|
|||
|
||||
void ShellDBusClient::connectSignals()
|
||||
{
|
||||
connect(m_interface, &OrgKdePlasmashellInterface::panelStateChanged, this, &ShellDBusClient::updatePanelState);
|
||||
connect(m_interface, &OrgKdePlasmashellInterface::isActionDrawerOpenChanged, this, &ShellDBusClient::updateIsActionDrawerOpen);
|
||||
connect(m_interface, &OrgKdePlasmashellInterface::doNotDisturbChanged, this, &ShellDBusClient::updateDoNotDisturb);
|
||||
connect(m_interface, &OrgKdePlasmashellInterface::isTaskSwitcherVisibleChanged, this, &ShellDBusClient::updateIsTaskSwitcherVisible);
|
||||
|
|
@ -47,6 +48,16 @@ void ShellDBusClient::connectSignals()
|
|||
updateIsTaskSwitcherVisible();
|
||||
}
|
||||
|
||||
QString ShellDBusClient::panelState() const
|
||||
{
|
||||
return m_panelState;
|
||||
}
|
||||
|
||||
void ShellDBusClient::setPanelState(QString state)
|
||||
{
|
||||
m_interface->setPanelState(state);
|
||||
}
|
||||
|
||||
bool ShellDBusClient::doNotDisturb() const
|
||||
{
|
||||
return m_doNotDisturb;
|
||||
|
|
@ -113,6 +124,18 @@ void ShellDBusClient::showVolumeOSD()
|
|||
m_interface->showVolumeOSD();
|
||||
}
|
||||
|
||||
void ShellDBusClient::updatePanelState()
|
||||
{
|
||||
auto reply = m_interface->panelState();
|
||||
auto watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
|
||||
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](auto watcher) {
|
||||
QDBusPendingReply<QString> reply = *watcher;
|
||||
m_panelState = reply.argumentAt<0>();
|
||||
Q_EMIT panelStateChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void ShellDBusClient::updateDoNotDisturb()
|
||||
{
|
||||
auto reply = m_interface->doNotDisturb();
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class ShellDBusClient : public QObject
|
|||
Q_PROPERTY(bool doNotDisturb READ doNotDisturb WRITE setDoNotDisturb NOTIFY doNotDisturbChanged)
|
||||
Q_PROPERTY(bool isActionDrawerOpen READ isActionDrawerOpen WRITE setIsActionDrawerOpen NOTIFY isActionDrawerOpenChanged)
|
||||
Q_PROPERTY(bool isTaskSwitcherVisible READ isTaskSwitcherVisible NOTIFY isTaskSwitcherVisibleChanged)
|
||||
Q_PROPERTY(QString panelState READ panelState WRITE setPanelState NOTIFY panelStateChanged)
|
||||
|
||||
public:
|
||||
explicit ShellDBusClient(QObject *parent = nullptr);
|
||||
|
|
@ -31,6 +32,10 @@ public:
|
|||
|
||||
bool isTaskSwitcherVisible() const;
|
||||
|
||||
QString panelState() const;
|
||||
void setPanelState(QString state);
|
||||
|
||||
|
||||
Q_INVOKABLE void openActionDrawer();
|
||||
Q_INVOKABLE void closeActionDrawer();
|
||||
|
||||
|
|
@ -43,6 +48,7 @@ public:
|
|||
Q_INVOKABLE void showVolumeOSD();
|
||||
|
||||
Q_SIGNALS:
|
||||
void panelStateChanged();
|
||||
void isActionDrawerOpenChanged();
|
||||
void doNotDisturbChanged();
|
||||
void isTaskSwitcherVisibleChanged();
|
||||
|
|
@ -57,6 +63,7 @@ private Q_SLOTS:
|
|||
void updateDoNotDisturb();
|
||||
void updateIsActionDrawerOpen();
|
||||
void updateIsTaskSwitcherVisible();
|
||||
void updatePanelState();
|
||||
|
||||
private:
|
||||
void connectSignals();
|
||||
|
|
@ -64,6 +71,8 @@ private:
|
|||
OrgKdePlasmashellInterface *m_interface;
|
||||
QDBusServiceWatcher *m_watcher;
|
||||
|
||||
QString m_panelState = "default";
|
||||
|
||||
bool m_doNotDisturb = false;
|
||||
bool m_isActionDrawerOpen = false;
|
||||
bool m_isTaskSwitcherVisible = false;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,20 @@ void ShellDBusObject::setDoNotDisturb(bool value)
|
|||
}
|
||||
}
|
||||
|
||||
QString ShellDBusObject::panelState()
|
||||
{
|
||||
return m_panelState;
|
||||
}
|
||||
|
||||
void ShellDBusObject::setPanelState(QString state)
|
||||
{
|
||||
if (state != m_panelState) {
|
||||
m_panelState = state;
|
||||
Q_EMIT panelStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ShellDBusObject::isActionDrawerOpen()
|
||||
{
|
||||
return m_isActionDrawerOpen;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ public:
|
|||
Q_SIGNALS:
|
||||
Q_SCRIPTABLE void doNotDisturbChanged();
|
||||
Q_SCRIPTABLE void isActionDrawerOpenChanged();
|
||||
Q_SCRIPTABLE void panelStateChanged();
|
||||
Q_SCRIPTABLE void isTaskSwitcherVisibleChanged();
|
||||
Q_SCRIPTABLE void openActionDrawerRequested();
|
||||
Q_SCRIPTABLE void closeActionDrawerRequested();
|
||||
|
|
@ -44,6 +45,10 @@ public Q_SLOTS:
|
|||
Q_SCRIPTABLE bool isActionDrawerOpen();
|
||||
Q_SCRIPTABLE void setIsActionDrawerOpen(bool value);
|
||||
|
||||
Q_SCRIPTABLE QString panelState();
|
||||
Q_SCRIPTABLE void setPanelState(QString state);
|
||||
|
||||
|
||||
Q_SCRIPTABLE bool isTaskSwitcherVisible();
|
||||
Q_SCRIPTABLE void setIsTaskSwitcherVisible(bool value);
|
||||
|
||||
|
|
@ -65,5 +70,7 @@ private:
|
|||
bool m_isActionDrawerOpen{false};
|
||||
bool m_isTaskSwitcherVisible{false};
|
||||
|
||||
QString m_panelState{};
|
||||
|
||||
StartupFeedbackModel *m_startupFeedbackModel{nullptr};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,24 +14,39 @@ QtObject {
|
|||
// Set it to Plasmoid.containment.screenGeometry in a plasmoid to accomplish this.
|
||||
property alias screenGeometry: tasksModel.screenGeometry
|
||||
|
||||
property bool isCurrentWindowFullscreen: __internal.count > 0 && visibleWindowsModel.currentFullscreen && !WindowPlugin.WindowUtil.isShowingDesktop
|
||||
|
||||
readonly property bool showingWindow: __internal.count > 0 && !WindowPlugin.WindowUtil.isShowingDesktop
|
||||
readonly property int windowCount: __internal.count
|
||||
|
||||
property var __internal: KItemModels.KSortFilterProxyModel {
|
||||
id: visibleMaximizedWindowsModel
|
||||
filterRoleName: 'IsMinimized'
|
||||
filterString: 'false'
|
||||
sourceModel: TaskManager.TasksModel {
|
||||
id: visibleWindowsModel
|
||||
sourceModel: taskModel
|
||||
filterRowCallback: (sourceRow, sourceParent) => {
|
||||
const task = sourceModel.index(sourceRow, 0, sourceParent);
|
||||
let isFullScreen = sourceModel.data(task, TaskManager.AbstractTasksModel.IsFullScreen);
|
||||
let isMaximized = sourceModel.data(task, TaskManager.AbstractTasksModel.IsMaximized);
|
||||
if (sourceRow == 0) {
|
||||
visibleWindowsModel.currentFullscreen = isFullScreen;
|
||||
}
|
||||
return isFullScreen || isMaximized;
|
||||
}
|
||||
|
||||
property bool currentFullscreen: false
|
||||
|
||||
property var taskModel: TaskManager.TasksModel {
|
||||
id: tasksModel
|
||||
filterByVirtualDesktop: true
|
||||
filterByActivity: true
|
||||
filterNotMaximized: true
|
||||
filterMinimized: true
|
||||
filterByScreen: true
|
||||
filterHidden: true
|
||||
|
||||
virtualDesktop: virtualDesktopInfo.currentDesktop
|
||||
activity: activityInfo.currentActivity
|
||||
|
||||
sortMode: TaskManager.TasksModel.SortLastActivated
|
||||
|
||||
groupMode: TaskManager.TasksModel.GroupDisabled
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,34 +19,70 @@ import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin
|
|||
|
||||
import org.kde.taskmanager as TaskManager
|
||||
import org.kde.notificationmanager as NotificationManager
|
||||
import org.kde.layershell 1.0 as LayerShell
|
||||
|
||||
ContainmentItem {
|
||||
id: root
|
||||
Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground
|
||||
Plasmoid.status: PlasmaCore.Types.PassiveStatus // ensure that the panel never takes focus away from the running app
|
||||
|
||||
onWidthChanged: maximizeTimer.restart()
|
||||
|
||||
// filled in by the shell (Panel.qml) with the plasma-workspace PanelView
|
||||
property var panel: null
|
||||
onPanelChanged: {
|
||||
if (panel) {
|
||||
panel.floating = false;
|
||||
}
|
||||
onPanelChanged: setWindowProperties()
|
||||
|
||||
MobileShell.HapticsEffect {
|
||||
id: haptics
|
||||
}
|
||||
|
||||
// Ensure that panel is always the full width of the screen
|
||||
readonly property real statusPanelHeight: MobileShell.Constants.topPanelHeight
|
||||
readonly property real intendedWindowThickness: statusPanelHeight
|
||||
|
||||
// use a timer so we don't have to maximize for every single pixel
|
||||
// - improves performance if the shell is run in a window, and can be resized
|
||||
Timer {
|
||||
id: maximizeTimer
|
||||
running: false
|
||||
interval: 100
|
||||
onTriggered: root.panel.maximize()
|
||||
onTriggered: root.panel.maximize()
|
||||
}
|
||||
|
||||
function setWindowProperties() {
|
||||
if (root.panel) {
|
||||
root.panel.floating = false;
|
||||
root.panel.maximize(); // maximize first, then we can apply offsets (otherwise they are overridden)
|
||||
root.panel.thickness = statusPanelHeight;
|
||||
MobileShell.ShellUtil.setWindowLayer(root.panel, LayerShell.Window.LayerOverlay)
|
||||
root.updateTouchArea();
|
||||
}
|
||||
}
|
||||
|
||||
// update the touch area when hidden to minimize the space the panel takes for touch input
|
||||
function updateTouchArea() {
|
||||
const hiddenTouchAreaThickness = Kirigami.Units.gridUnit;
|
||||
|
||||
if (statusPanel.state == "hidden") {
|
||||
MobileShell.ShellUtil.setInputRegion(root.panel, Qt.rect(0, 0, root.panel.width, hiddenTouchAreaThickness));
|
||||
} else {
|
||||
MobileShell.ShellUtil.setInputRegion(root.panel, Qt.rect(0, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binding {
|
||||
target: MobileShellState.ShellDBusClient
|
||||
property: "isActionDrawerOpen"
|
||||
value: drawer.visible
|
||||
}
|
||||
|
||||
|
||||
// only opaque if there are no maximized windows on this screen
|
||||
readonly property bool showingStartupFeedback: MobileShellState.ShellDBusObject.startupFeedbackModel.activeWindowIsStartupFeedback && windowMaximizedTracker.windowCount === 1
|
||||
readonly property bool showingApp: windowMaximizedTracker.showingWindow && !showingStartupFeedback
|
||||
readonly property color backgroundColor: topPanel.colorScopeColor
|
||||
readonly property alias isCurrentWindowFullscreen: windowMaximizedTracker.isCurrentWindowFullscreen
|
||||
onIsCurrentWindowFullscreenChanged: {
|
||||
MobileShellState.ShellDBusClient.panelState = isCurrentWindowFullscreen ? "hidden" : "default";
|
||||
}
|
||||
|
||||
WindowPlugin.WindowMaximizedTracker {
|
||||
id: windowMaximizedTracker
|
||||
|
|
@ -89,6 +125,8 @@ ContainmentItem {
|
|||
//END API implementation
|
||||
|
||||
Component.onCompleted: {
|
||||
root.setWindowProperties();
|
||||
|
||||
// register dbus
|
||||
MobileShellState.ShellDBusObject.registerObject();
|
||||
|
||||
|
|
@ -108,18 +146,119 @@ ContainmentItem {
|
|||
fullHeight: root.height
|
||||
screen: Plasmoid.screen
|
||||
maximizedTracker: windowMaximizedTracker
|
||||
|
||||
visible: !root.isCurrentWindowFullscreen
|
||||
}
|
||||
|
||||
// top panel component
|
||||
MobileShell.StatusBar {
|
||||
id: topPanel
|
||||
Rectangle {
|
||||
id: statusPanel
|
||||
anchors.fill: parent
|
||||
|
||||
Kirigami.Theme.colorSet: root.showingApp ? Kirigami.Theme.Header : Kirigami.Theme.Complementary
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
showDropShadow: !root.showingApp
|
||||
backgroundColor: !root.showingApp ? "transparent" : root.backgroundColor
|
||||
color: statusPanel.state == "default" && root.showingApp ? Kirigami.Theme.backgroundColor : "transparent"
|
||||
|
||||
property real offset: 0
|
||||
|
||||
// top panel component
|
||||
MobileShell.StatusBar {
|
||||
id: topPanel
|
||||
anchors.fill: parent
|
||||
|
||||
showDropShadow: !root.showingApp
|
||||
backgroundColor: statusPanel.state != "default" ? Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.95) : "transparent"
|
||||
|
||||
transform: [
|
||||
Translate {
|
||||
y: statusPanel.offset
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
state: MobileShellState.ShellDBusClient.panelState
|
||||
onStateChanged: {
|
||||
if (statusPanel.state != "hidden") {
|
||||
root.setWindowProperties();
|
||||
hiddenTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hiddenTimer
|
||||
running: false
|
||||
interval: 3000
|
||||
onTriggered: {
|
||||
if (statusPanel.state == "visible") {
|
||||
MobileShellState.ShellDBusClient.panelState = "hidden";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "default"
|
||||
PropertyChanges {
|
||||
target: statusPanel; offset: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "visible"
|
||||
PropertyChanges {
|
||||
target: statusPanel; offset: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "hidden"
|
||||
PropertyChanges {
|
||||
target: statusPanel; offset: -root.statusPanelHeight
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: Transition {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
PropertyAnimation {
|
||||
properties: "offset"; easing.type: statusPanel.state == "hidden" ? Easing.InExpo : Easing.OutExpo; duration: Kirigami.Units.longDuration
|
||||
}
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
root.setWindowProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// swiping area for swipe-down drawer
|
||||
MobileShell.ActionDrawerOpenSurface {
|
||||
id: swipeArea
|
||||
actionDrawer: drawer.actionDrawer
|
||||
anchors.fill: parent
|
||||
|
||||
readonly property alias drawerVisible: drawer.visible
|
||||
readonly property alias offset: drawer.actionDrawer.offset
|
||||
property bool surfacePressed: false
|
||||
onOffsetChanged: surfacePressed = false
|
||||
|
||||
// allow tapping to bring back up the status bar when it is hidden
|
||||
onPressedChanged: {
|
||||
if (!pressed && surfacePressed && root.isCurrentWindowFullscreen) {
|
||||
haptics.buttonVibrate();
|
||||
MobileShellState.ShellDBusClient.panelState = "visible";
|
||||
} else {
|
||||
surfacePressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if in a fullscreen app, the panels are visible, and the action drawer is opened
|
||||
// set the panels to a hidden state
|
||||
onDrawerVisibleChanged: {
|
||||
if (statusPanel.state == "visible") {
|
||||
MobileShellState.ShellDBusClient.panelState = "hidden";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// swipe-down drawer component
|
||||
|
|
|
|||
|
|
@ -20,12 +20,13 @@ import org.kde.kirigami as Kirigami
|
|||
MobileShell.NavigationPanel {
|
||||
id: root
|
||||
required property bool opaqueBar
|
||||
required property var navbarState
|
||||
|
||||
// background is:
|
||||
// - opaque if an app is shown or vkbd is shown
|
||||
// - translucent if the task switcher is open
|
||||
// - transparent if on the homescreen
|
||||
backgroundColor: (Keyboards.KWinVirtualKeyboard.active || opaqueBar) ? Kirigami.Theme.backgroundColor : "transparent";
|
||||
backgroundColor: navbarState != "default" ? Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.95) : "transparent"
|
||||
foregroundColorGroup: opaqueBar ? Kirigami.Theme.Window : Kirigami.Theme.Complementary
|
||||
shadow: !opaqueBar
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.15
|
||||
import QtQuick.Shapes 1.8
|
||||
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
|
|
@ -17,6 +18,8 @@ import org.kde.plasma.private.mobileshell as MobileShell
|
|||
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||
import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin
|
||||
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
||||
import org.kde.plasma.workspace.keyboardlayout as Keyboards
|
||||
import org.kde.layershell 1.0 as LayerShell
|
||||
|
||||
ContainmentItem {
|
||||
id: root
|
||||
|
|
@ -37,6 +40,10 @@ ContainmentItem {
|
|||
}
|
||||
}
|
||||
|
||||
MobileShell.HapticsEffect {
|
||||
id: haptics
|
||||
}
|
||||
|
||||
readonly property bool inLandscape: MobileShell.Constants.navigationPanelOnSide(Screen.width, Screen.height)
|
||||
|
||||
readonly property real navigationPanelHeight: MobileShell.Constants.navigationPanelThickness
|
||||
|
|
@ -73,7 +80,7 @@ ContainmentItem {
|
|||
interval: 100
|
||||
onTriggered: {
|
||||
// maximize first, then we can apply offsets (otherwise they are overridden)
|
||||
root.panel.maximize()
|
||||
root.panel.maximize();
|
||||
root.panel.offset = intendedWindowOffset;
|
||||
}
|
||||
}
|
||||
|
|
@ -86,6 +93,23 @@ ContainmentItem {
|
|||
root.panel.offset = intendedWindowOffset;
|
||||
root.panel.thickness = navigationPanelHeight;
|
||||
root.panel.location = intendedWindowLocation;
|
||||
MobileShell.ShellUtil.setWindowLayer(root.panel, LayerShell.Window.LayerOverlay);
|
||||
root.updateTouchArea();
|
||||
}
|
||||
}
|
||||
|
||||
// update the touch area when hidden to minimize the space the panel takes for touch input
|
||||
function updateTouchArea() {
|
||||
const hiddenTouchAreaThickness = Kirigami.Units.gridUnit;
|
||||
|
||||
if (navigationPanel.state == "hidden") {
|
||||
if (inLandscape) {
|
||||
MobileShell.ShellUtil.setInputRegion(root.panel, Qt.rect(root.panel.width - hiddenTouchAreaThickness, 0, hiddenTouchAreaThickness, root.panel.height));
|
||||
} else {
|
||||
MobileShell.ShellUtil.setInputRegion(root.panel, Qt.rect(0, root.panel.height - hiddenTouchAreaThickness, root.panel.width, hiddenTouchAreaThickness));
|
||||
}
|
||||
} else {
|
||||
MobileShell.ShellUtil.setInputRegion(root.panel, Qt.rect(0, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +136,9 @@ ContainmentItem {
|
|||
|
||||
// only opaque if there are no maximized windows on this screen
|
||||
readonly property bool showingStartupFeedback: MobileShellState.ShellDBusObject.startupFeedbackModel.activeWindowIsStartupFeedback && windowMaximizedTracker.windowCount === 1
|
||||
readonly property bool opaqueBar: windowMaximizedTracker.showingWindow && !showingStartupFeedback
|
||||
readonly property bool opaqueBar: (windowMaximizedTracker.showingWindow || isCurrentWindowFullscreen) && !showingStartupFeedback
|
||||
|
||||
readonly property alias isCurrentWindowFullscreen: windowMaximizedTracker.isCurrentWindowFullscreen
|
||||
|
||||
WindowPlugin.WindowMaximizedTracker {
|
||||
id: windowMaximizedTracker
|
||||
|
|
@ -128,21 +154,161 @@ ContainmentItem {
|
|||
fullHeight: root.height
|
||||
screen: Plasmoid.screen
|
||||
maximizedTracker: windowMaximizedTracker
|
||||
|
||||
visible: !root.isCurrentWindowFullscreen
|
||||
}
|
||||
|
||||
Item {
|
||||
Rectangle {
|
||||
id: navigationPanel
|
||||
anchors.fill: parent
|
||||
|
||||
// contrasting colour
|
||||
Kirigami.Theme.colorSet: opaqueBar ? Kirigami.Theme.Window : Kirigami.Theme.Complementary
|
||||
Kirigami.Theme.colorSet: root.opaqueBar ? Kirigami.Theme.Window : Kirigami.Theme.Complementary
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
color: navigationPanel.state == "default" && (Keyboards.KWinVirtualKeyboard.active || root.opaqueBar) ? Kirigami.Theme.backgroundColor : "transparent"
|
||||
|
||||
property real offset: 0
|
||||
|
||||
// load appropriate system navigation component
|
||||
NavigationPanelComponent {
|
||||
id: navigationPanel
|
||||
anchors.fill: parent
|
||||
opaqueBar: root.opaqueBar
|
||||
isVertical: root.inLandscape
|
||||
navbarState: navigationPanel.state
|
||||
|
||||
transform: [
|
||||
Translate {
|
||||
y: inLandscape ? 0 : navigationPanel.offset
|
||||
x: inLandscape ? navigationPanel.offset : 0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
state: MobileShellState.ShellDBusClient.panelState
|
||||
onStateChanged: {
|
||||
if (navigationPanel.state != "hidden") {
|
||||
root.setWindowProperties();
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "default"
|
||||
PropertyChanges {
|
||||
target: navigationPanel; offset: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "visible"
|
||||
PropertyChanges {
|
||||
target: navigationPanel; offset: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "hidden"
|
||||
PropertyChanges {
|
||||
target: navigationPanel; offset: root.navigationPanelHeight
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: Transition {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
PropertyAnimation {
|
||||
properties: "offset"; easing.type: navigationPanel.state == "hidden" ? Easing.InExpo : Easing.OutExpo; duration: Kirigami.Units.longDuration
|
||||
}
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
root.setWindowProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MobileShell.SwipeArea {
|
||||
id: swipeArea
|
||||
mode: inLandscape ? MobileShell.SwipeArea.HorizontalOnly : MobileShell.SwipeArea.VerticalOnly
|
||||
anchors.fill: navigationPanel
|
||||
enabled: navigationPanel.state == "hidden"
|
||||
|
||||
function startSwipeWithPoint(point) {
|
||||
root.setWindowProperties();
|
||||
resetAn.stop();
|
||||
dragEffect.startPoint = inLandscape ? point.y - Screen.height / 2 : point.x - Screen.width / 2;
|
||||
dragEffect.sidePoint = 0
|
||||
dragEffect.offsetPoint = 0;
|
||||
}
|
||||
|
||||
function updateOffset(offsetX, offsetY) {
|
||||
dragEffect.sidePoint = inLandscape ? offsetY : offsetX;
|
||||
dragEffect.offsetPoint = Math.min(0, inLandscape ? offsetX : offsetY);
|
||||
if (dragEffect.offsetPoint < -Kirigami.Units.gridUnit * 5 && navigationPanel.state == "hidden") {
|
||||
swipeArea.resetSwipe();
|
||||
resetAn.restart();
|
||||
haptics.buttonVibrate();
|
||||
MobileShellState.ShellDBusClient.panelState = "visible";
|
||||
}
|
||||
}
|
||||
|
||||
onSwipeStarted: (point) => startSwipeWithPoint(point)
|
||||
onSwipeEnded: resetAn.start()
|
||||
onSwipeMove: (totalDeltaX, totalDeltaY, deltaX, deltaY) => updateOffset(totalDeltaX, totalDeltaY);
|
||||
|
||||
onPressedChanged: {
|
||||
if (!pressed && dragEffect.offsetPoint == 0) {
|
||||
haptics.buttonVibrate();
|
||||
MobileShellState.ShellDBusClient.panelState = "visible";
|
||||
}
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: resetAn
|
||||
running: false
|
||||
target: dragEffect
|
||||
property: "offsetPoint"
|
||||
to: 0
|
||||
duration: Kirigami.Units.longDuration * 1.5
|
||||
easing.type: Easing.OutExpo
|
||||
onRunningChanged: {
|
||||
if (!running && navigationPanel.state == "hidden") {
|
||||
root.setWindowProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MobileShell.ScreenEdgeDragEffect {
|
||||
id: dragEffect
|
||||
|
||||
offsetLimit: root.inLandscape ? swipeArea.width : swipeArea.height
|
||||
isHorizontal: root.inLandscape
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "vertical"
|
||||
when: !root.inLandscape
|
||||
AnchorChanges {
|
||||
target: dragEffect
|
||||
anchors.right: undefined
|
||||
anchors.bottom: swipeArea.bottom
|
||||
anchors.horizontalCenter: swipeArea.horizontalCenter
|
||||
anchors.verticalCenter: undefined
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "horizontal"
|
||||
when: root.inLandscape
|
||||
AnchorChanges {
|
||||
target: dragEffect
|
||||
anchors.right: swipeArea.right
|
||||
anchors.bottom: undefined
|
||||
anchors.horizontalCenter: undefined
|
||||
anchors.verticalCenter: swipeArea.verticalCenter
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue