mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 14:23:09 +00:00
panel: Overlay over lockscreen
Overlay the shell's status panel and quicksettings panel over the lockscreen, instead of rendering a second copy in the lockscreen theme. This will allow us to improve the lockscreen loading speed. Key changes: - Overlay quicksettings window and the status bar over the lockscreen when it is shown - Refactor the top panel's showing logic to be cleaner (as it supports various overlay modes over fullscreen apps already) - Implement lockscreen support to the status bar and quicksettings panel in the to panel - Forward quicksettings panel requests for "unlock" over DBus to the lockscreen - Add "raiselockscreen" QML plugin to easily request a window to be raised over the lockscreen Notes: - Now that we are sharing the quicksettings panel from the shell, notifications that are already there will be shown on the lockscreen (compared to right now, where only new notifications would be shown) Depends on: - https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/2339 - https://invent.kde.org/plasma/kscreenlocker/-/merge_requests/283 - https://invent.kde.org/plasma/kwin/-/merge_requests/7839 Implements: https://invent.kde.org/plasma/plasma-mobile/-/issues/199 
This commit is contained in:
parent
aa368499f1
commit
3b6951bf1e
22 changed files with 823 additions and 531 deletions
|
|
@ -34,6 +34,7 @@ include(ECMMarkNonGuiExecutable)
|
|||
include(ECMGenerateHeaders)
|
||||
include(ECMFindQmlModule)
|
||||
include(ECMGenerateQmlTypes)
|
||||
include(ECMConfiguredInstall)
|
||||
include(ECMFindQmlModule)
|
||||
include(GenerateExportHeader)
|
||||
include(KDEGitCommitHooks)
|
||||
|
|
@ -100,6 +101,14 @@ find_package(KWin ${PROJECT_DEP_VERSION} REQUIRED COMPONENTS
|
|||
)
|
||||
|
||||
find_package(LayerShellQt REQUIRED)
|
||||
find_package(Wayland REQUIRED)
|
||||
find_package(PlasmaWaylandProtocols 1.8 CONFIG)
|
||||
set_package_properties(PlasmaWaylandProtocols PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Collection of Plasma-specific Wayland protocols"
|
||||
URL "https://invent.kde.org/libraries/plasma-wayland-protocols/"
|
||||
)
|
||||
|
||||
|
||||
find_package(LibKWorkspace CONFIG REQUIRED)
|
||||
find_package(Libudev REQUIRED)
|
||||
|
|
|
|||
|
|
@ -13,3 +13,4 @@ add_subdirectory(shellsettingsplugin)
|
|||
add_subdirectory(wallpaperimageplugin)
|
||||
add_subdirectory(dpmsplugin)
|
||||
add_subdirectory(screenbrightnessplugin)
|
||||
add_subdirectory(raiselockscreenplugin)
|
||||
|
|
|
|||
|
|
@ -111,7 +111,10 @@ Item {
|
|||
* Run pending action that was pending for authentication when unlockRequested() was emitted.
|
||||
*/
|
||||
function runPendingAction() {
|
||||
if (list.pendingNotificationWithAction) {
|
||||
list.pendingNotificationWithAction.runPendingAction();
|
||||
list.pendingNotificationWithAction = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -175,7 +178,7 @@ Item {
|
|||
|
||||
currentIndex: 0
|
||||
|
||||
property var pendingNotificationWithAction
|
||||
property NotificationItem pendingNotificationWithAction: null
|
||||
|
||||
readonly property int animationDuration: ShellSettings.Settings.animationsEnabled ? Kirigami.Units.longDuration : 0
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ LockscreenDBusClient::LockscreenDBusClient(QObject *parent)
|
|||
QStringLiteral("org.freedesktop.ScreenSaver"),
|
||||
QStringLiteral("GetActive"));
|
||||
|
||||
const QDBusReply<bool> response = QDBusConnection::sessionBus().call(request);
|
||||
|
||||
QDBusConnection::sessionBus().callWithCallback(request, this, SLOT(slotLockscreenActiveChanged(bool)), SLOT(dbusError(QDBusError)));
|
||||
|
||||
QDBusConnection::sessionBus().connect(QStringLiteral("org.freedesktop.ScreenSaver"),
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ void ShellDBusClient::connectSignals()
|
|||
connect(m_interface, &OrgKdePlasmashellInterface::openHomeScreenRequested, this, &ShellDBusClient::openHomeScreenRequested);
|
||||
connect(m_interface, &OrgKdePlasmashellInterface::resetHomeScreenPositionRequested, this, &ShellDBusClient::resetHomeScreenPositionRequested);
|
||||
connect(m_interface, &OrgKdePlasmashellInterface::showVolumeOSDRequested, this, &ShellDBusClient::showVolumeOSDRequested);
|
||||
connect(m_interface, &OrgKdePlasmashellInterface::openLockScreenKeypadRequested, this, &ShellDBusClient::openLockScreenKeypadRequested);
|
||||
|
||||
updateDoNotDisturb();
|
||||
updateIsTaskSwitcherVisible();
|
||||
|
|
@ -151,6 +152,11 @@ void ShellDBusClient::showVolumeOSD()
|
|||
m_interface->showVolumeOSD();
|
||||
}
|
||||
|
||||
void ShellDBusClient::openLockScreenKeypad()
|
||||
{
|
||||
m_interface->openLockScreenKeypad();
|
||||
}
|
||||
|
||||
void ShellDBusClient::updatePanelState()
|
||||
{
|
||||
auto reply = m_interface->panelState();
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ public:
|
|||
Q_INVOKABLE void openHomeScreen();
|
||||
Q_INVOKABLE void resetHomeScreenPosition();
|
||||
Q_INVOKABLE void showVolumeOSD();
|
||||
Q_INVOKABLE void openLockScreenKeypad();
|
||||
|
||||
Q_SIGNALS:
|
||||
void panelStateChanged();
|
||||
|
|
@ -68,6 +69,7 @@ Q_SIGNALS:
|
|||
void openHomeScreenRequested();
|
||||
void resetHomeScreenPositionRequested();
|
||||
void showVolumeOSDRequested();
|
||||
void openLockScreenKeypadRequested();
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateDoNotDisturb();
|
||||
|
|
|
|||
|
|
@ -150,3 +150,8 @@ void ShellDBusObject::showVolumeOSD()
|
|||
{
|
||||
Q_EMIT showVolumeOSDRequested();
|
||||
}
|
||||
|
||||
void ShellDBusObject::openLockScreenKeypad()
|
||||
{
|
||||
Q_EMIT openLockScreenKeypadRequested();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ Q_SIGNALS:
|
|||
Q_SCRIPTABLE void openHomeScreenRequested();
|
||||
Q_SCRIPTABLE void resetHomeScreenPositionRequested();
|
||||
Q_SCRIPTABLE void showVolumeOSDRequested();
|
||||
Q_SCRIPTABLE void openLockScreenKeypadRequested();
|
||||
|
||||
public Q_SLOTS:
|
||||
Q_SCRIPTABLE bool doNotDisturb();
|
||||
|
|
@ -70,6 +71,7 @@ public Q_SLOTS:
|
|||
Q_SCRIPTABLE void openHomeScreen();
|
||||
Q_SCRIPTABLE void resetHomeScreenPosition();
|
||||
Q_SCRIPTABLE void showVolumeOSD();
|
||||
Q_SCRIPTABLE void openLockScreenKeypad();
|
||||
|
||||
private:
|
||||
bool m_initialized{false};
|
||||
|
|
|
|||
29
components/raiselockscreenplugin/CMakeLists.txt
Normal file
29
components/raiselockscreenplugin/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
|
||||
# SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
ecm_add_qml_module(raiselockscreenplugin URI org.kde.plasma.private.mobileshell.raiselockscreenplugin GENERATE_PLUGIN_SOURCE)
|
||||
target_sources(raiselockscreenplugin PRIVATE
|
||||
raiselockscreen.cpp
|
||||
utils.h
|
||||
)
|
||||
|
||||
target_link_libraries(raiselockscreenplugin PRIVATE
|
||||
Qt::Qml
|
||||
Qt::Quick
|
||||
Qt::WaylandClient
|
||||
Qt::WaylandClientPrivate
|
||||
KF6::Service
|
||||
KF6::Package
|
||||
KF6::I18n
|
||||
KF6::ConfigCore
|
||||
KF6::Service
|
||||
KF6::WindowSystem
|
||||
Wayland::Client
|
||||
)
|
||||
|
||||
qt6_generate_wayland_protocol_client_sources(raiselockscreenplugin FILES
|
||||
${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-lockscreen-overlay-v1.xml)
|
||||
|
||||
ecm_finalize_qml_module(raiselockscreenplugin)
|
||||
|
||||
|
||||
140
components/raiselockscreenplugin/raiselockscreen.cpp
Normal file
140
components/raiselockscreenplugin/raiselockscreen.cpp
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "raiselockscreen.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <QWaylandClientExtensionTemplate>
|
||||
#include <qpa/qplatformwindow_p.h>
|
||||
|
||||
#include <KWaylandExtras>
|
||||
#include <KWindowSystem>
|
||||
|
||||
#include "qwayland-kde-lockscreen-overlay-v1.h"
|
||||
|
||||
class WaylandAboveLockscreen : public QWaylandClientExtensionTemplate<WaylandAboveLockscreen>, public QtWayland::kde_lockscreen_overlay_v1
|
||||
{
|
||||
public:
|
||||
WaylandAboveLockscreen()
|
||||
: QWaylandClientExtensionTemplate<WaylandAboveLockscreen>(1)
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
};
|
||||
|
||||
RaiseLockscreen::RaiseLockscreen(QObject *parent)
|
||||
: QObject{parent}
|
||||
, m_implementation(std::make_unique<WaylandAboveLockscreen>())
|
||||
{
|
||||
QObject::connect(KWaylandExtras::self(), &KWaylandExtras::xdgActivationTokenArrived, this, [this](int serial, const QString &token) {
|
||||
if (!m_window || serial != m_serial) {
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(LOGGING_CATEGORY) << "XDG activation token arrived, activating window:" << m_window;
|
||||
// Activate window over lockscreen once we have activation token
|
||||
KWindowSystem::setCurrentXdgActivationToken(token);
|
||||
KWindowSystem::activateWindow(m_window);
|
||||
});
|
||||
}
|
||||
|
||||
RaiseLockscreen::~RaiseLockscreen()
|
||||
{
|
||||
}
|
||||
|
||||
QWindow *RaiseLockscreen::window() const
|
||||
{
|
||||
return m_window;
|
||||
}
|
||||
|
||||
void RaiseLockscreen::setWindow(QWindow *window)
|
||||
{
|
||||
m_window = window;
|
||||
Q_EMIT windowChanged();
|
||||
}
|
||||
|
||||
bool RaiseLockscreen::initialized() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
void RaiseLockscreen::setInitialized(bool initialized)
|
||||
{
|
||||
m_initialized = initialized;
|
||||
Q_EMIT initializedChanged();
|
||||
}
|
||||
|
||||
void RaiseLockscreen::initializeOverlay(QQuickWindow *window)
|
||||
{
|
||||
if (!window || window == m_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
setWindow(window);
|
||||
setOverlay();
|
||||
|
||||
// also re-set the overlay when the compositor gets restarted
|
||||
connect(m_implementation.get(), &WaylandAboveLockscreen::activeChanged, this, &RaiseLockscreen::setOverlay);
|
||||
}
|
||||
|
||||
void RaiseLockscreen::setOverlay()
|
||||
{
|
||||
if (!m_implementation->isActive()) {
|
||||
setInitialized(false);
|
||||
qCWarning(LOGGING_CATEGORY) << "Unable to set overlay: wayland protocol is not active";
|
||||
return;
|
||||
}
|
||||
auto waylandWindow = m_window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
|
||||
if (!waylandWindow) {
|
||||
// Add event filter to listen for when wayland window appears, and try again
|
||||
m_window->installEventFilter(this);
|
||||
setInitialized(false);
|
||||
qCWarning(LOGGING_CATEGORY) << "Unable to set overlay: unable to get wayland window";
|
||||
return;
|
||||
}
|
||||
|
||||
// Listen to when new surface roles are created, and re-allow again.
|
||||
// This can happen when a window is hidden, and then shown again (same surface, different surface role)
|
||||
connect(waylandWindow, &QNativeInterface::Private::QWaylandWindow::surfaceRoleCreated, this, [this, waylandWindow]() {
|
||||
m_implementation->allow(waylandWindow->surface());
|
||||
setInitialized(true);
|
||||
qCDebug(LOGGING_CATEGORY) << "Initialized overlay successfully";
|
||||
});
|
||||
|
||||
if (waylandWindow->surface()) {
|
||||
m_implementation->allow(waylandWindow->surface());
|
||||
setInitialized(true);
|
||||
qCDebug(LOGGING_CATEGORY) << "Initialized overlay successfully";
|
||||
}
|
||||
}
|
||||
|
||||
bool RaiseLockscreen::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
auto window = qobject_cast<QQuickWindow *>(watched);
|
||||
if (window && event->type() == QEvent::PlatformSurface) {
|
||||
auto surfaceEvent = static_cast<QPlatformSurfaceEvent *>(event);
|
||||
if (surfaceEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
|
||||
m_window->removeEventFilter(this);
|
||||
setOverlay();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RaiseLockscreen::raiseOverlay()
|
||||
{
|
||||
if (!m_window) {
|
||||
qCWarning(LOGGING_CATEGORY) << "Unable to raise overlay: no window set";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_initialized) {
|
||||
qCWarning(LOGGING_CATEGORY) << "Unable to raise overlay: window is not initialized for lockscreen overlaying, trying anyway...";
|
||||
}
|
||||
|
||||
m_serial = KWaylandExtras::lastInputSerial(m_window);
|
||||
|
||||
qCDebug(LOGGING_CATEGORY) << "Attempting to raise overlay: " << m_window << m_initialized;
|
||||
KWaylandExtras::requestXdgActivationToken(m_window, m_serial, QStringLiteral("org.kde.plasmashell.desktop"));
|
||||
}
|
||||
49
components/raiselockscreenplugin/raiselockscreen.h
Normal file
49
components/raiselockscreenplugin/raiselockscreen.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQuickWindow>
|
||||
#include <QWindow>
|
||||
|
||||
#include "qqml.h"
|
||||
|
||||
class WaylandAboveLockscreen;
|
||||
|
||||
/**
|
||||
* A plugin to implement raising windows over the lockscreen.
|
||||
*/
|
||||
class RaiseLockscreen : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QWindow *window READ window WRITE setWindow NOTIFY windowChanged)
|
||||
Q_PROPERTY(bool initialized READ initialized NOTIFY initializedChanged)
|
||||
QML_ELEMENT
|
||||
|
||||
public:
|
||||
RaiseLockscreen(QObject *parent = nullptr);
|
||||
~RaiseLockscreen() override;
|
||||
|
||||
QWindow *window() const;
|
||||
void setWindow(QWindow *window);
|
||||
|
||||
bool initialized() const;
|
||||
|
||||
Q_INVOKABLE void initializeOverlay(QQuickWindow *window);
|
||||
Q_INVOKABLE void raiseOverlay();
|
||||
|
||||
Q_SIGNALS:
|
||||
void windowChanged();
|
||||
void initializedChanged();
|
||||
|
||||
private:
|
||||
void setInitialized(bool initialized);
|
||||
void setOverlay();
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
bool m_initialized = false;
|
||||
QWindow *m_window = nullptr;
|
||||
int m_serial = 0;
|
||||
std::unique_ptr<WaylandAboveLockscreen> m_implementation;
|
||||
};
|
||||
12
components/raiselockscreenplugin/utils.h
Normal file
12
components/raiselockscreenplugin/utils.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
static const QLoggingCategory &LOGGING_CATEGORY()
|
||||
{
|
||||
static const QLoggingCategory category("raiselockscreenplugin");
|
||||
return category;
|
||||
}
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
# SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
|
||||
# SPDX-FileCopyrightText: 2021 Aleix Pol <apol@kde.org>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@kde.org>
|
||||
# SPDX-FileCopyrightText: 2022 Alexey Andreyev <aa13q@ya.ru>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
plasma_add_applet(org.kde.plasma.mobile.panel
|
||||
QML_SOURCES
|
||||
qml/main.qml
|
||||
qml/LockscreenOverlay.qml
|
||||
qml/StatusBarWrapper.qml
|
||||
qml/StatusPanel.qml
|
||||
CPP_SOURCES
|
||||
phonepanel.cpp
|
||||
)
|
||||
|
|
@ -16,4 +20,5 @@ target_link_libraries(org.kde.plasma.mobile.panel PRIVATE
|
|||
Plasma::Plasma
|
||||
KF6::I18n
|
||||
KF6::Service
|
||||
KF6::WindowSystem
|
||||
)
|
||||
|
|
|
|||
57
containments/panel/qml/LockscreenOverlay.qml
Normal file
57
containments/panel/qml/LockscreenOverlay.qml
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
||||
import org.kde.plasma.private.mobileshell.raiselockscreenplugin as RaiseLockscreenPlugin
|
||||
|
||||
import org.kde.layershell 1.0 as LayerShell
|
||||
|
||||
// Raise panel window over the lockscreen when it is shown
|
||||
QtObject {
|
||||
id: root
|
||||
required property var window
|
||||
|
||||
onWindowChanged: {
|
||||
// Window.window may start out null, we need to wait for it to exist
|
||||
if (root.window && !raiseLockscreen.initialized) {
|
||||
initializeLockscreenOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
function raiseOverlay() {
|
||||
if (MobileShellState.LockscreenDBusClient.lockscreenActive) {
|
||||
console.log('Raising top panel over the lockscreen');
|
||||
raiseLockscreen.raiseOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
function initializeLockscreenOverlay() {
|
||||
if (!root.window) {
|
||||
return;
|
||||
}
|
||||
|
||||
raiseLockscreen.initializeOverlay(root.window);
|
||||
|
||||
// Raise panel if lockscreen is already active
|
||||
raiseOverlay();
|
||||
}
|
||||
|
||||
// Raise panel over the lockscreen when it is enabled
|
||||
readonly property var raiseLockscreen: RaiseLockscreenPlugin.RaiseLockscreen {
|
||||
id: raiseLockscreen
|
||||
Component.onCompleted: root.initializeLockscreenOverlay()
|
||||
}
|
||||
|
||||
readonly property Connections lockscreenConnections: Connections {
|
||||
target: MobileShellState.LockscreenDBusClient
|
||||
|
||||
function onLockscreenLocked() {
|
||||
root.raiseOverlay();
|
||||
}
|
||||
}
|
||||
}
|
||||
100
containments/panel/qml/StatusBarWrapper.qml
Normal file
100
containments/panel/qml/StatusBarWrapper.qml
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// SPDX-FileCopyrightText: 2021-2025 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.plasma.plasmoid
|
||||
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
||||
import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// The full intended height of the status panel.
|
||||
required property real statusPanelHeight
|
||||
|
||||
// Whether the background should be transparent, with content using a complementary theme on top.
|
||||
required property bool transparentBackground
|
||||
|
||||
// Request the panel itself to reapply settings (ex. for updating touch area).
|
||||
signal updatePanelPropertiesRequested()
|
||||
|
||||
|
||||
Kirigami.Theme.colorSet: transparentBackground ? Kirigami.Theme.Complementary : Kirigami.Theme.Header
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
property real offset: 0
|
||||
|
||||
MobileShell.StatusBar {
|
||||
id: topPanel
|
||||
anchors.fill: parent
|
||||
|
||||
showSecondRow: false
|
||||
showTime: !MobileShellState.LockscreenDBusClient.lockscreenActive // Don't show time on the lockscreen, since we already have a massive clock
|
||||
|
||||
showDropShadow: root.transparentBackground
|
||||
backgroundColor: {
|
||||
if (root.transparentBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
if (state == "default") {
|
||||
return Kirigami.Theme.backgroundColor;
|
||||
} else {
|
||||
return Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.95);
|
||||
}
|
||||
}
|
||||
|
||||
transform: [
|
||||
Translate {
|
||||
y: root.offset
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
// Default panel state, which is shown in the UI.
|
||||
name: "default"
|
||||
PropertyChanges {
|
||||
target: root; offset: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
// Panel is forced to be visible and overlaid over content (will be automatically hidden after a duration).
|
||||
name: "visible"
|
||||
PropertyChanges {
|
||||
target: root; offset: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
// Panel is hidden and requires a gesture to be shown.
|
||||
name: "hidden"
|
||||
PropertyChanges {
|
||||
target: root; offset: -root.statusPanelHeight
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: Transition {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
PropertyAnimation {
|
||||
properties: "offset"
|
||||
easing.type: root.state === "hidden" ? Easing.InExpo : Easing.OutExpo
|
||||
duration: Kirigami.Units.longDuration
|
||||
}
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
root.updatePanelPropertiesRequested();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
185
containments/panel/qml/StatusPanel.qml
Normal file
185
containments/panel/qml/StatusPanel.qml
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
// SPDX-FileCopyrightText: 2021-2023 Devin Lin <devin@kde.org>
|
||||
// SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
import QtQml.Models
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
||||
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
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// The base containment item
|
||||
property ContainmentItem containmentItem
|
||||
|
||||
//BEGIN API implementation
|
||||
|
||||
Connections {
|
||||
target: MobileShellState.ShellDBusClient
|
||||
|
||||
function onOpenActionDrawerRequested() {
|
||||
drawer.actionDrawer.open();
|
||||
}
|
||||
|
||||
function onCloseActionDrawerRequested() {
|
||||
drawer.actionDrawer.close();
|
||||
}
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: MobileShellState.ShellDBusClient
|
||||
property: "isActionDrawerOpen"
|
||||
value: drawer.visible
|
||||
}
|
||||
|
||||
//END API implementation
|
||||
|
||||
// Startup feedback fill animation
|
||||
MobileShell.StartupFeedbackPanelFill {
|
||||
id: startupFeedbackColorAnimation
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
fullHeight: containmentItem.height
|
||||
screen: Plasmoid.screen
|
||||
maximizedTracker: containmentItem.windowMaximizedTracker
|
||||
|
||||
visible: !containmentItem.fullscreen
|
||||
}
|
||||
|
||||
// Status bar component
|
||||
StatusBarWrapper {
|
||||
id: statusBarWrapper
|
||||
anchors.fill: parent
|
||||
|
||||
statusPanelHeight: MobileShell.Constants.topPanelHeight
|
||||
transparentBackground: {
|
||||
// If we are over the lockscreen, always have a transparent background.
|
||||
if (MobileShellState.LockscreenDBusClient.lockscreenActive) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !containmentItem.showingApp && !containmentItem.fullscreen;
|
||||
}
|
||||
|
||||
state: {
|
||||
// If we are on the lockscreen, always show the status panel.
|
||||
if (MobileShellState.LockscreenDBusClient.lockscreenActive) {
|
||||
return "default";
|
||||
}
|
||||
|
||||
return MobileShellState.ShellDBusClient.panelState;
|
||||
}
|
||||
onStateChanged: {
|
||||
if (state != "hidden") {
|
||||
containmentItem.setWindowProperties();
|
||||
hiddenTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
onUpdatePanelPropertiesRequested: containmentItem.setWindowProperties()
|
||||
|
||||
// Hide status bar panel if it is visible for 3 seconds (in forced "visible" mode).
|
||||
Timer {
|
||||
id: hiddenTimer
|
||||
running: false
|
||||
interval: 3000
|
||||
onTriggered: {
|
||||
if (statusBarWrapper.state == "visible") {
|
||||
MobileShellState.ShellDBusClient.panelState = "hidden";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// if in a fullscreen app, the panels are visible, and the action drawer is opened
|
||||
// set the panels to a hidden state
|
||||
onDrawerVisibleChanged: {
|
||||
if (statusBarWrapper.state == "visible") {
|
||||
MobileShellState.ShellDBusClient.panelState = "hidden";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Swipe-down drawer component
|
||||
MobileShell.ActionDrawerWindow {
|
||||
id: drawer
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible && MobileShellState.LockscreenDBusClient.lockscreenActive) {
|
||||
// This works as long the wayland surface is the same (no window.close(), just window.visible = false)
|
||||
lockScreenOverlay.raiseOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
LockscreenOverlay {
|
||||
id: lockScreenOverlay
|
||||
window: drawer
|
||||
}
|
||||
|
||||
actionDrawer.restrictedPermissions: MobileShellState.LockscreenDBusClient.lockscreenActive
|
||||
|
||||
actionDrawer.notificationSettings: NotificationManager.Settings {}
|
||||
actionDrawer.notificationModel: NotificationManager.Notifications {
|
||||
showExpired: true
|
||||
showDismissed: true
|
||||
showJobs: drawer.actionDrawer.notificationSettings.jobsInNotifications
|
||||
sortMode: NotificationManager.Notifications.SortByTypeAndUrgency
|
||||
groupMode: NotificationManager.Notifications.GroupApplicationsFlat
|
||||
groupLimit: 2
|
||||
expandUnread: true
|
||||
blacklistedDesktopEntries: drawer.actionDrawer.notificationSettings.historyBlacklistedApplications
|
||||
blacklistedNotifyRcNames: drawer.actionDrawer.notificationSettings.historyBlacklistedServices
|
||||
urgencies: {
|
||||
var urgencies = NotificationManager.Notifications.CriticalUrgency
|
||||
| NotificationManager.Notifications.NormalUrgency;
|
||||
if (drawer.actionDrawer.notificationSettings.lowPriorityHistory) {
|
||||
urgencies |= NotificationManager.Notifications.LowUrgency;
|
||||
}
|
||||
return urgencies;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: drawer.actionDrawer
|
||||
|
||||
function onPermissionsRequested() {
|
||||
MobileShellState.ShellDBusClient.openLockScreenKeypad();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: MobileShellState.LockscreenDBusClient
|
||||
|
||||
function onLockscreenUnlocked() {
|
||||
// Run pending actions after the lockscreen gets unlocked
|
||||
drawer.actionDrawer.runPendingNotificationAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ import org.kde.kirigami as Kirigami
|
|||
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||
|
|
@ -24,249 +23,97 @@ 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
|
||||
|
||||
// filled in by the shell (Panel.qml) with the plasma-workspace PanelView
|
||||
Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground
|
||||
Plasmoid.status: PlasmaCore.Types.PassiveStatus // Ensure that the panel never takes focus away from the running app
|
||||
|
||||
// Filled in by the shell (Panel.qml) with the plasma-workspace PanelView
|
||||
property var panel: null
|
||||
onPanelChanged: setWindowProperties()
|
||||
|
||||
MobileShell.HapticsEffect {
|
||||
id: haptics
|
||||
// Whether the startup feedback is showing
|
||||
readonly property bool showingStartupFeedback: MobileShellState.ShellDBusObject.startupFeedbackModel.activeWindowIsStartupFeedback
|
||||
|
||||
// Whether an app is maximized and showing (does not include startup feedback)
|
||||
readonly property bool showingApp: windowMaximizedTracker.showingWindow && !showingStartupFeedback
|
||||
|
||||
// Whether the currently showing app is in "fullscreen"
|
||||
readonly property bool fullscreen: {
|
||||
if (windowMaximizedTracker.isCurrentWindowFullscreen) {
|
||||
return true;
|
||||
}
|
||||
|
||||
readonly property real statusPanelHeight: MobileShell.Constants.topPanelHeight
|
||||
readonly property real intendedWindowThickness: statusPanelHeight
|
||||
// The "autoHidePanelsEnabled" settings option treats every app as a fullscreen window
|
||||
return (ShellSettings.Settings.autoHidePanelsEnabled && showingApp);
|
||||
}
|
||||
onFullscreenChanged: {
|
||||
MobileShellState.ShellDBusClient.panelState = fullscreen ? "hidden" : "default";
|
||||
}
|
||||
|
||||
// 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()
|
||||
property WindowPlugin.WindowMaximizedTracker windowMaximizedTracker: WindowPlugin.WindowMaximizedTracker {
|
||||
id: windowMaximizedTracker
|
||||
screenGeometry: Plasmoid.containment.screenGeometry
|
||||
|
||||
onShowingWindowChanged: {
|
||||
// Hide panel when we open the task switcher and an app is "fullscreen"
|
||||
if (windowMaximizedTracker.showingWindow
|
||||
&& MobileShellState.ShellDBusClient.isTaskSwitcherVisible
|
||||
&& (ShellSettings.Settings.autoHidePanelsEnabled || fullscreen)) {
|
||||
MobileShellState.ShellDBusClient.panelState = "hidden";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
root.panel.thickness = MobileShell.Constants.topPanelHeight;
|
||||
root.panel.visibilityMode = ShellSettings.Settings.autoHidePanelsEnabled ? 3 : 0;
|
||||
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
|
||||
// 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") {
|
||||
if (MobileShellState.ShellDBusClient.panelState == "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));
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ShellSettings.Settings
|
||||
|
||||
function onAutoHidePanelsEnabled() {
|
||||
root.setWindowProperties();
|
||||
}
|
||||
// Overlay the panel over the lockscreen when brought up
|
||||
LockscreenOverlay {
|
||||
window: root.Window.window
|
||||
}
|
||||
|
||||
// only opaque if there are no maximized windows on this screen
|
||||
readonly property bool showingStartupFeedback: MobileShellState.ShellDBusObject.startupFeedbackModel.activeWindowIsStartupFeedback && startupFeedbackColorAnimation.visible && windowMaximizedTracker.windowCount === 1
|
||||
readonly property bool showingApp: windowMaximizedTracker.showingWindow && !showingStartupFeedback
|
||||
readonly property color backgroundColor: topPanel.colorScopeColor
|
||||
readonly property alias isCurrentWindowFullscreen: windowMaximizedTracker.isCurrentWindowFullscreen
|
||||
readonly property bool fullscreen: isCurrentWindowFullscreen || (ShellSettings.Settings.autoHidePanelsEnabled && showingApp)
|
||||
onFullscreenChanged: {
|
||||
MobileShellState.ShellDBusClient.panelState = fullscreen ? "hidden" : "default";
|
||||
}
|
||||
|
||||
WindowPlugin.WindowMaximizedTracker {
|
||||
id: windowMaximizedTracker
|
||||
screenGeometry: Plasmoid.containment.screenGeometry
|
||||
|
||||
onShowingWindowChanged: {
|
||||
if (windowMaximizedTracker.showingWindow && MobileShellState.ShellDBusClient.isTaskSwitcherVisible && (ShellSettings.Settings.autoHidePanelsEnabled || fullscreen)) {
|
||||
MobileShellState.ShellDBusClient.panelState = "hidden";
|
||||
statusPanel.offset = -root.statusPanelHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// enforce thickness
|
||||
// Enforce thickness of panel
|
||||
Binding {
|
||||
target: panel // assumed to be plasma-workspace "PanelView" component
|
||||
property: "thickness"
|
||||
value: MobileShell.Constants.topPanelHeight
|
||||
}
|
||||
|
||||
//BEGIN API implementation
|
||||
|
||||
Connections {
|
||||
target: MobileShellState.ShellDBusClient
|
||||
target: ShellSettings.Settings
|
||||
|
||||
function onOpenActionDrawerRequested() {
|
||||
drawer.actionDrawer.open();
|
||||
}
|
||||
|
||||
function onCloseActionDrawerRequested() {
|
||||
drawer.actionDrawer.close();
|
||||
function onAutoHidePanelsEnabledChanged() {
|
||||
root.setWindowProperties();
|
||||
}
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: MobileShellState.ShellDBusClient
|
||||
property: "isActionDrawerOpen"
|
||||
value: drawer.visible
|
||||
}
|
||||
|
||||
//END API implementation
|
||||
|
||||
Component.onCompleted: {
|
||||
root.setWindowProperties();
|
||||
}
|
||||
|
||||
MobileShell.StartupFeedbackPanelFill {
|
||||
id: startupFeedbackColorAnimation
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
fullHeight: root.height
|
||||
screen: Plasmoid.screen
|
||||
maximizedTracker: windowMaximizedTracker
|
||||
|
||||
visible: !root.fullscreen
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
// Visual panel component
|
||||
StatusPanel {
|
||||
id: statusPanel
|
||||
anchors.fill: parent
|
||||
Kirigami.Theme.colorSet: root.showingApp ? Kirigami.Theme.Header : Kirigami.Theme.Complementary
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
color: statusPanel.state == "default" && (root.showingApp || root.fullscreen) ? 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" && root.showingApp ? 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
|
||||
|
||||
// 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
|
||||
MobileShell.ActionDrawerWindow {
|
||||
id: drawer
|
||||
|
||||
actionDrawer.notificationSettings: NotificationManager.Settings {}
|
||||
actionDrawer.notificationModel: NotificationManager.Notifications {
|
||||
showExpired: true
|
||||
showDismissed: true
|
||||
showJobs: drawer.actionDrawer.notificationSettings.jobsInNotifications
|
||||
sortMode: NotificationManager.Notifications.SortByTypeAndUrgency
|
||||
groupMode: NotificationManager.Notifications.GroupApplicationsFlat
|
||||
groupLimit: 2
|
||||
expandUnread: true
|
||||
blacklistedDesktopEntries: drawer.actionDrawer.notificationSettings.historyBlacklistedApplications
|
||||
blacklistedNotifyRcNames: drawer.actionDrawer.notificationSettings.historyBlacklistedServices
|
||||
urgencies: {
|
||||
var urgencies = NotificationManager.Notifications.CriticalUrgency
|
||||
| NotificationManager.Notifications.NormalUrgency;
|
||||
if (drawer.actionDrawer.notificationSettings.lowPriorityHistory) {
|
||||
urgencies |= NotificationManager.Notifications.LowUrgency;
|
||||
}
|
||||
return urgencies;
|
||||
}
|
||||
}
|
||||
containmentItem: root
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ ContainmentItem {
|
|||
Connections {
|
||||
target: ShellSettings.Settings
|
||||
|
||||
function onAutoHidePanelsEnabled() {
|
||||
function onAutoHidePanelsEnabledChanged() {
|
||||
root.setWindowProperties();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,145 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Devin Lin <espidev@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
import org.kde.plasma.components 3.0 as PC3
|
||||
|
||||
import org.kde.notificationmanager as NotificationManager
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property real openFactor
|
||||
required property real statusBarHeight
|
||||
|
||||
property var notificationsModel: []
|
||||
|
||||
readonly property bool actionDrawerVisible: swipeArea.actionDrawer.intendedToBeVisible
|
||||
|
||||
signal passwordRequested()
|
||||
|
||||
// The status bar and quicksettings take a while to load, don't pause initial lockscreen loading for it
|
||||
Timer {
|
||||
id: loadTimer
|
||||
running: true
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
statusBarLoader.active = true
|
||||
actionDrawerLoader.active = true
|
||||
}
|
||||
}
|
||||
|
||||
// Add loading indicator when status bar has not loaded yet
|
||||
PC3.BusyIndicator {
|
||||
id: statusBarLoadingIndication
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: Kirigami.Units.smallSpacing
|
||||
anchors.rightMargin: Kirigami.Units.smallSpacing
|
||||
visible: statusBarLoader.status != Loader.Ready
|
||||
|
||||
implicitHeight: root.statusBarHeight
|
||||
implicitWidth: root.statusBarHeight
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
}
|
||||
|
||||
// Status bar
|
||||
Loader {
|
||||
id: statusBarLoader
|
||||
active: false
|
||||
asynchronous: true
|
||||
visible: status == Loader.Ready
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
height: root.statusBarHeight
|
||||
|
||||
sourceComponent: MobileShell.StatusBar {
|
||||
id: statusBar
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: root.statusBarHeight
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
|
||||
backgroundColor: "transparent"
|
||||
|
||||
showSecondRow: false
|
||||
showDropShadow: true
|
||||
showTime: false
|
||||
disableSystemTray: true // prevent SIGABRT, since loading the system tray on the lockscreen leads to bad... things
|
||||
}
|
||||
}
|
||||
|
||||
// Drag down gesture to open action drawer
|
||||
MobileShell.ActionDrawerOpenSurface {
|
||||
id: swipeArea
|
||||
actionDrawer: actionDrawerLoader.item ? actionDrawerLoader.item.actionDrawer : null
|
||||
|
||||
anchors.fill: statusBarLoader
|
||||
}
|
||||
|
||||
// Dynamically load on swipe-down to avoid having to load at start
|
||||
Loader {
|
||||
id: actionDrawerLoader
|
||||
active: false
|
||||
asynchronous: true
|
||||
visible: status == Loader.Ready
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
sourceComponent: Item {
|
||||
property var actionDrawer: drawer
|
||||
|
||||
// Action drawer component
|
||||
MobileShell.ActionDrawer {
|
||||
id: drawer
|
||||
anchors.fill: parent
|
||||
|
||||
visible: offset !== 0
|
||||
restrictedPermissions: true
|
||||
|
||||
notificationSettings: NotificationManager.Settings {}
|
||||
notificationModel: root.notificationsModel
|
||||
notificationModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel
|
||||
|
||||
property bool requestNotificationAction: false
|
||||
|
||||
// notification button clicked, requesting auth
|
||||
onPermissionsRequested: {
|
||||
requestNotificationAction = true;
|
||||
drawer.close();
|
||||
root.passwordRequested();
|
||||
}
|
||||
}
|
||||
|
||||
// listen to authentication events
|
||||
Connections {
|
||||
target: authenticator
|
||||
function onSucceeded() {
|
||||
// run pending action if successfully unlocked
|
||||
if (drawer.requestNotificationAction) {
|
||||
drawer.runPendingAction();
|
||||
drawer.requestNotificationAction = false;
|
||||
}
|
||||
}
|
||||
function onFailed() {
|
||||
drawer.requestNotificationAction = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import QtQuick.Layouts
|
|||
import org.kde.plasma.core as PlasmaCore
|
||||
import org.kde.notificationmanager as Notifications
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
||||
import org.kde.plasma.private.mobileshell.dpmsplugin as DPMS
|
||||
import org.kde.plasma.components 3.0 as PC3
|
||||
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||
|
|
@ -30,7 +31,7 @@ Item {
|
|||
readonly property bool isWidescreen: root.height < Kirigami.Units.gridUnit * 25 && (root.height < root.width * 0.75)
|
||||
property bool notificationsShown: false
|
||||
|
||||
property var passwordBar: flickableLoader.item ? flickableLoader.item.flickable.passwordBar : null
|
||||
property var passwordBar: flickable.passwordBar
|
||||
|
||||
Component.onCompleted: {
|
||||
forceActiveFocus();
|
||||
|
|
@ -38,13 +39,19 @@ Item {
|
|||
|
||||
// Listen for keyboard events, and focus on input area
|
||||
Keys.onPressed: (event) => {
|
||||
if (flickableLoader.item) {
|
||||
root.lockScreenState.isKeyboardMode = true;
|
||||
flickableLoader.item.flickable.goToOpenPosition();
|
||||
flickable.goToOpenPosition();
|
||||
passwordBar.textField.forceActiveFocus();
|
||||
|
||||
passwordBar.keyPress(event.text);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: MobileShellState.ShellDBusClient
|
||||
|
||||
function onOpenLockScreenKeypadRequested() {
|
||||
flickable.goToOpenPosition();
|
||||
}
|
||||
}
|
||||
|
||||
// Wallpaper blur
|
||||
|
|
@ -63,7 +70,7 @@ Item {
|
|||
|
||||
sourceComponent: WallpaperBlur {
|
||||
source: wallpaper
|
||||
opacity: flickableLoader.item ? flickableLoader.item.flickable.openFactor : 0
|
||||
opacity: flickable.openFactor
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,8 +79,8 @@ Item {
|
|||
|
||||
// Ensure keypad is opened when password is updated (ex. keyboard)
|
||||
function onPasswordChanged() {
|
||||
if (root.lockScreenState.password !== "" && flickableLoader.item) {
|
||||
flickableLoader.item.flickable.goToOpenPosition();
|
||||
if (root.lockScreenState.password !== "") {
|
||||
flickable.goToOpenPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -84,70 +91,17 @@ Item {
|
|||
|
||||
onDpmsTurnedOff: (screen) => {
|
||||
if (screen.name === Screen.name) {
|
||||
if (flickableLoader.item) {
|
||||
flickableLoader.item.flickable.goToClosePosition();
|
||||
}
|
||||
flickable.goToClosePosition();
|
||||
lockScreenState.resetPassword();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Container for lockscreen contents
|
||||
Item {
|
||||
id: lockscreenContainer
|
||||
anchors.fill: parent
|
||||
|
||||
// Header bar and action drawer
|
||||
HeaderComponent {
|
||||
id: headerBar
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
statusBarHeight: MobileShell.Constants.topPanelHeight
|
||||
openFactor: flickableLoader.item ? flickableLoader.item.flickable.openFactor : 0
|
||||
notificationsModel: root.notifModel
|
||||
onPasswordRequested: root.askPassword()
|
||||
}
|
||||
|
||||
// Add loading indicator when status bar has not loaded yet
|
||||
PC3.BusyIndicator {
|
||||
id: flickableLoadingBusyIndicator
|
||||
anchors.centerIn: parent
|
||||
visible: flickableLoader.status != Loader.Ready
|
||||
|
||||
implicitHeight: Kirigami.Units.iconSizes.huge
|
||||
implicitWidth: Kirigami.Units.iconSizes.huge
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
}
|
||||
|
||||
// Load flickable async
|
||||
Loader {
|
||||
id: flickableLoader
|
||||
|
||||
active: false
|
||||
asynchronous: true
|
||||
opacity: status == Loader.Ready ? 1 : 0
|
||||
visible: opacity > 0
|
||||
anchors.fill: parent
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {}
|
||||
}
|
||||
|
||||
// This take a while to load, don't pause initial lockscreen and wallpaper loading for it
|
||||
Timer {
|
||||
id: loadTimer
|
||||
running: true
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
flickableLoader.active = true
|
||||
}
|
||||
}
|
||||
|
||||
// Container for lockscreen contents
|
||||
sourceComponent: Item {
|
||||
id: item
|
||||
property alias flickable: flickable
|
||||
FlickContainer {
|
||||
id: flickable
|
||||
anchors.fill: parent
|
||||
|
|
@ -257,18 +211,10 @@ Item {
|
|||
onNotificationsShownChanged: root.notificationsShown = notificationsShown
|
||||
onPasswordRequested: flickable.goToOpenPosition()
|
||||
|
||||
scrollLock: headerBar.actionDrawerVisible || (flickableLoader.item ? flickableLoader.item.flickable.openFactor > 0.2 : false)
|
||||
scrollLock: flickable.openFactor > 0.2
|
||||
z: scrollLock ? -1 : 0
|
||||
|
||||
anchors {
|
||||
//topMargin: headerBar.statusBarHeight
|
||||
top: item.top
|
||||
bottom: item.bottom
|
||||
left: item.left
|
||||
right: item.right
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ ApplicationWindow {
|
|||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
height: Kirigami.Units.gridUnit * 1.25
|
||||
height: MobileShell.Constants.topPanelHeight
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
// SPDX-FileCopyrightText: 2022 Devin LIn <devin@kde.org>
|
||||
// SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.components 3.0 as PC3
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
import org.kde.notificationmanager as NotificationManager
|
||||
|
||||
import "../shell/contents/lockscreen" as LockScreen
|
||||
|
||||
|
|
@ -61,9 +63,48 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
|
||||
// component to test
|
||||
// Component to test
|
||||
LockScreen.LockScreen {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
// Simulate "overlaid" status bar and quick settings panel
|
||||
MobileShell.StatusBar {
|
||||
id: statusBar
|
||||
z: 1
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
height: Kirigami.Units.gridUnit * 1.25
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||
|
||||
backgroundColor: "transparent"
|
||||
|
||||
showSecondRow: false
|
||||
showDropShadow: true
|
||||
showTime: true
|
||||
disableSystemTray: true // prevent SIGABRT, since loading the system tray leads to bad... things
|
||||
}
|
||||
|
||||
MobileShell.ActionDrawerOpenSurface {
|
||||
anchors.fill: statusBar
|
||||
actionDrawer: drawer
|
||||
z: 1
|
||||
}
|
||||
|
||||
MobileShell.ActionDrawer {
|
||||
id: drawer
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
visible: offset !== 0
|
||||
|
||||
notificationSettings: NotificationManager.Settings {}
|
||||
notificationModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel
|
||||
notificationModel: NotificationManager.WatchedNotificationsModel {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue