navigationpanel: Add ability to toggle between gesture only and shown navigation panel modes

Addresses: https://invent.kde.org/plasma/plasma-phone-components/-/issues/140
This commit is contained in:
Devin Lin 2022-02-13 04:23:57 +00:00
parent 6d6b38ac46
commit 8f9f722ca7
27 changed files with 690 additions and 151 deletions

View file

@ -69,6 +69,7 @@ add_subdirectory(bin)
add_subdirectory(containments)
add_subdirectory(components)
add_subdirectory(quicksettings)
add_subdirectory(kcms)
find_program(PlasmaOpenSettings plasma-open-settings)
set_package_properties(PlasmaOpenSettings PROPERTIES

21
LICENSES/MIT.txt Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) [year] [fullname]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -33,8 +33,8 @@ Item {
appDrawerFlickable: appDrawer.flickable
availableScreenHeight: height - (MobileShell.TaskPanelControls.isPortrait ? MobileShell.TaskPanelControls.panelHeight : 0)
availableScreenWidth: width - (MobileShell.TaskPanelControls.isPortrait ? 0 : MobileShell.TaskPanelControls.panelWidth)
availableScreenHeight: height - MobileShell.Shell.bottomMargin
availableScreenWidth: width - MobileShell.Shell.leftMargin - MobileShell.Shell.rightMargin
appDrawerBottomOffset: favoriteStrip.height
}
@ -73,9 +73,10 @@ Item {
// account for panels
anchors.fill: parent
anchors.topMargin: MobileShell.TopPanelControls.panelHeight
anchors.rightMargin: MobileShell.TaskPanelControls.isPortrait ? 0 : MobileShell.TaskPanelControls.panelWidth
anchors.bottomMargin: MobileShell.TaskPanelControls.isPortrait ? MobileShell.TaskPanelControls.panelHeight : 0
anchors.topMargin: MobileShell.Shell.topMargin
anchors.bottomMargin: MobileShell.Shell.bottomMargin
anchors.leftMargin: MobileShell.Shell.leftMargin
anchors.rightMargin: MobileShell.Shell.rightMargin
// animation when app drawer is being shown
opacity: root.appDrawer ? 1 - root.appDrawer.openFactor : 1
@ -129,9 +130,10 @@ Item {
homeScreenState: root.homeScreenState
// account for panels
topPadding: MobileShell.TopPanelControls.panelHeight
rightPadding: MobileShell.TaskPanelControls.isPortrait ? 0 : MobileShell.TaskPanelControls.panelWidth
bottomPadding: MobileShell.TaskPanelControls.isPortrait ? MobileShell.TaskPanelControls.panelHeight : 0
topPadding: MobileShell.Shell.topMargin
bottomPadding: MobileShell.Shell.bottomMargin
leftPadding: MobileShell.Shell.leftMargin
rightPadding: MobileShell.Shell.rightMargin
}
}
}

View file

@ -9,6 +9,7 @@ set(mobileshellplugin_SRCS
mobileshellplugin.cpp
shellutil.cpp
quicksettingsmodel.cpp
mobileshellsettings.cpp
vkbdinterface.cpp
displaysmodel.cpp

View file

@ -10,6 +10,7 @@
#include <QQuickItem>
#include "displaysmodel.h"
#include "mobileshellsettings.h"
#include "notifications/notificationfilemenu.h"
#include "notifications/notificationthumbnailer.h"
#include "quicksettingsmodel.h"
@ -25,6 +26,10 @@ void MobileShellPlugin::registerTypes(const char *uri)
return ShellUtil::instance();
});
qmlRegisterSingletonType<MobileShellSettings>(uri, 1, 0, "MobileShellSettings", [](QQmlEngine *, QJSEngine *) -> QObject * {
return MobileShellSettings::self();
});
qmlRegisterType<QuickSetting>(uri, 1, 0, "QuickSetting");
qmlRegisterType<QuickSettingsModel>(uri, 1, 0, "QuickSettingsModel");

View file

@ -0,0 +1,35 @@
/*
* SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "mobileshellsettings.h"
const QString CONFIG_FILE = QStringLiteral("plasmamobilerc");
const QString GENERAL_CONFIG_GROUP = QStringLiteral("General");
MobileShellSettings *MobileShellSettings::self()
{
static MobileShellSettings *singleton = new MobileShellSettings();
return singleton;
}
MobileShellSettings::MobileShellSettings(QObject *parent)
: QObject{parent}
{
m_config = KSharedConfig::openConfig(CONFIG_FILE, KConfig::SimpleConfig);
m_configWatcher = KConfigWatcher::create(m_config);
connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) -> void {
if (group.name() == GENERAL_CONFIG_GROUP) {
Q_EMIT navigationPanelEnabledChanged();
}
});
}
bool MobileShellSettings::navigationPanelEnabled() const
{
auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP};
return group.readEntry("navigationPanelEnabled", true);
}

View file

@ -0,0 +1,32 @@
/*
* SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <KConfigGroup>
#include <KConfigWatcher>
#include <KSharedConfig>
#include <QObject>
class MobileShellSettings : public QObject
{
Q_OBJECT
Q_PROPERTY(bool navigationPanelEnabled READ navigationPanelEnabled NOTIFY navigationPanelEnabledChanged)
public:
static MobileShellSettings *self();
MobileShellSettings(QObject *parent = nullptr);
bool navigationPanelEnabled() const;
Q_SIGNALS:
void navigationPanelEnabledChanged();
private:
KConfigWatcher::Ptr m_configWatcher;
KSharedConfig::Ptr m_config;
};

View file

@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Window 2.15
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
pragma Singleton
/**
* Provides access to the homescreen plasmoid containment within the shell.
*/
QtObject {
id: delegate
/**
* Top margin from the screen edge where application windows would display.
*/
readonly property real topMargin: TopPanelControls.panelHeight
/**
* Bottom margin from the screen edge where application windows would display.
*/
readonly property real bottomMargin: TaskPanelControls.isPortrait ? TaskPanelControls.panelHeight : 0
/**
* Left margin from the screen edge where application windows would display.
*/
readonly property real leftMargin: 0
/**
* Right margin from the screen edge where application windows would display.
*/
readonly property real rightMargin: !TaskPanelControls.isPortrait ? TaskPanelControls.panelWidth : 0
/**
* Orientation of the mobile device.
*/
readonly property int orientation: TaskPanelControls.isPortrait ? Shell.Portrait : Shell.Landscape
enum Orientation {
Landscape,
Portrait
}
}

View file

@ -11,6 +11,7 @@ pragma Singleton
/**
* Provides access to the taskpanel plasmoid containment within the shell.
* Properties are updated by the taskpanel containment.
*/
QtObject {
id: root

View file

@ -0,0 +1,74 @@
/*
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
* SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.4
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
import QtGraphicalEffects 1.12
import org.kde.taskmanager 0.1 as TaskManager
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.kquickcontrolsaddons 2.0
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Item {
id: root
property var taskSwitcher
MouseArea {
id: mouseArea
anchors.fill: parent
drag.filterChildren: true
// drag gesture
property int oldMouseY: 0
property int startMouseY: 0
property int oldMouseX: 0
property int startMouseX: 0
property bool opening: false
onPressed: {
startMouseX = oldMouseX = mouse.y;
startMouseY = oldMouseY = mouse.y;
}
onPositionChanged: {
if (!opening && Math.abs(startMouseY - oldMouseY) < root.height) {
oldMouseY = mouse.y;
return;
} else if (mouseArea.pressed) {
opening = true;
}
if (root.taskSwitcher.visible) {
// update task switcher drag
let offsetY = (mouse.y - oldMouseY) * 0.5; // we want to make the gesture take a longer swipe than it being pixel perfect
let offsetX = (mouse.x - oldMouseX) * 0.7; // we want to make the gesture not too hard to swipe, but not too easy
taskSwitcher.taskSwitcherState.yPosition = Math.max(0, taskSwitcher.taskSwitcherState.yPosition - offsetY);
taskSwitcher.taskSwitcherState.xPosition -= offsetX;
}
if (!root.taskSwitcher.visible && Math.abs(startMouseY - mouse.y) > PlasmaCore.Units.gridUnit && taskSwitcher.tasksCount) {
// start task switcher gesture
root.taskSwitcher.show(false);
}
oldMouseY = mouse.y;
oldMouseX = mouse.x;
}
onReleased: {
if (taskSwitcher.taskSwitcherState.currentlyBeingOpened) {
taskSwitcher.taskSwitcherState.updateState();
}
}
}
}

View file

@ -20,6 +20,7 @@ import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Item {
id: root
property bool shadow: false
property color backgroundColor
property var foregroundColorGroup
@ -32,7 +33,7 @@ Item {
DropShadow {
anchors.fill: mouseArea
visible: !showingApp
visible: shadow
cached: true
horizontalOffset: 0
verticalOffset: 1
@ -166,7 +167,7 @@ Item {
states: [
State {
name: "landscape"
when: Screen.width > Screen.height
when: root.width < root.height
PropertyChanges {
target: icons
buttonLength: icons.height * 0.8 / 3
@ -204,7 +205,7 @@ Item {
}
}, State {
name: "portrait"
when: Screen.width <= Screen.height
when: root.width >= root.height
PropertyChanges {
target: icons
buttonLength: icons.width * 0.8 / 3

View file

@ -23,6 +23,7 @@ singleton VolumeProvider 1.0 dataproviders/VolumeProvider.qml
singleton WifiProvider 1.0 dataproviders/WifiProvider.qml
# /navigationpanel
NavigationGestureArea 1.0 navigationpanel/NavigationGestureArea.qml
NavigationPanel 1.0 navigationpanel/NavigationPanel.qml
NavigationPanelAction 1.0 navigationpanel/NavigationPanelAction.qml
@ -40,5 +41,6 @@ NotificationsModelType 1.0 widgets/notifications/NotificationsModelType.qml
# /
singleton HomeScreenControls 1.0 HomeScreenControls.qml
singleton Shell 1.0 Shell.qml
singleton TaskPanelControls 1.0 TaskPanelControls.qml
singleton TopPanelControls 1.0 TopPanelControls.qml

View file

@ -71,8 +71,8 @@ Item {
// account for system header and footer offset (center the preview image)
y: {
let headerHeight = MobileShell.TopPanelControls.panelHeight;
let footerHeight = MobileShell.TaskPanelControls.isPortrait ? MobileShell.TaskPanelControls.panelHeight : 0;
let headerHeight = MobileShell.Shell.topMargin;
let footerHeight = MobileShell.Shell.bottomMargin;
let diff = headerHeight - footerHeight;
let baseY = (taskSwitcher.height / 2) - (height / 2) - (taskSwitcherState.taskHeaderHeight / 2)

View file

@ -166,9 +166,10 @@ Item {
// provide shell margins
anchors.fill: parent
anchors.rightMargin: MobileShell.TaskPanelControls.isPortrait ? 0 : MobileShell.TaskPanelControls.panelWidth
anchors.bottomMargin: MobileShell.TaskPanelControls.isPortrait ? MobileShell.TaskPanelControls.panelHeight : 0
anchors.topMargin: MobileShell.TopPanelControls.panelHeight
anchors.leftMargin: MobileShell.Shell.leftMargin
anchors.rightMargin: MobileShell.Shell.rightMargin
anchors.bottomMargin: MobileShell.Shell.bottomMargin
anchors.topMargin: MobileShell.Shell.topMargin
FlickContainer {
id: flickable

View file

@ -70,8 +70,8 @@ QtObject {
// ~~ measurement constants ~~
// dimensions of a real window on the screen
readonly property real windowHeight: taskSwitcher.height - (MobileShell.TaskPanelControls.isPortrait ? MobileShell.TaskPanelControls.panelHeight : 0) - MobileShell.TopPanelControls.panelHeight
readonly property real windowWidth: taskSwitcher.width - (MobileShell.TaskPanelControls.isPortrait ? 0 : MobileShell.TaskPanelControls.panelWidth)
readonly property real windowHeight: taskSwitcher.height - MobileShell.Shell.topMargin - MobileShell.Shell.bottomMargin
readonly property real windowWidth: taskSwitcher.width - MobileShell.Shell.leftMargin - MobileShell.Shell.rightMargin
// dimensions of the task previews
readonly property real previewHeight: windowHeight * scalingFactor

View file

@ -75,9 +75,10 @@ Item {
id: flickable
anchors.fill: parent
anchors.topMargin: MobileShell.TopPanelControls.panelHeight
anchors.bottomMargin: MobileShell.TaskPanelControls.isPortrait ? MobileShell.TaskPanelControls.panelHeight : 0
anchors.rightMargin: MobileShell.TaskPanelControls.isPortrait ? 0 : MobileShell.TaskPanelControls.panelWidth
anchors.topMargin: MobileShell.Shell.topMargin
anchors.bottomMargin: MobileShell.Shell.bottomMargin
anchors.leftMargin: MobileShell.Shell.leftMargin
anchors.rightMargin: MobileShell.Shell.rightMargin
contentHeight: flickable.height + root.closedContentY + 999999
contentY: root.closedContentY

View file

@ -0,0 +1,106 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Devin Lin <devin@kde.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.4
import QtQuick.Layouts 1.1
import QtQuick.Window 2.15
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
MobileShell.NavigationPanel {
id: root
property bool appIsShown: !plasmoid.nativeInterface.allMinimized
// background is:
// - opaque if an app is shown
// - translucent if the task switcher is open
// - transparent if on the homescreen
backgroundColor: {
if (root.taskSwitcher.visible) {
return Qt.rgba(0, 0, 0, 0.1);
} else {
return appIsShown ? PlasmaCore.ColorScope.backgroundColor : "transparent";
}
}
foregroundColorGroup: (!root.taskSwitcher.visible && appIsShown) ? PlasmaCore.Theme.NormalColorGroup : PlasmaCore.Theme.ComplementaryColorGroup
shadow: !appIsShown
// do not enable drag gesture when task switcher is already open
// also don't disable drag gesture mid-drag
dragGestureEnabled: !root.taskSwitcher.visible || root.taskSwitcher.taskSwitcherState.currentlyBeingOpened
// ~~~~
// navigation panel actions
// toggle task switcher button
leftAction: MobileShell.NavigationPanelAction {
id: taskSwitcherAction
enabled: (root.taskSwitcher.tasksCount > 0) || root.taskSwitcher.visible
iconSource: "mobile-task-switcher"
iconSizeFactor: 0.75
onTriggered: {
plasmoid.nativeInterface.showDesktop = false;
if (!root.taskSwitcher.visible) {
root.taskSwitcher.show(true);
} else {
// when task switcher is open
if (root.taskSwitcher.taskSwitcherState.wasInActiveTask) {
// restore active window
root.taskSwitcher.activateWindow(taskSwitcher.taskSwitcherState.currentTaskIndex);
} else {
root.taskSwitcher.hide();
}
}
}
}
// home button
middleAction: MobileShell.NavigationPanelAction {
id: homeAction
enabled: true
iconSource: "start-here-kde"
iconSizeFactor: 1
onTriggered: {
MobileShell.HomeScreenControls.openHomeScreen();
plasmoid.nativeInterface.allMinimizedChanged();
}
}
// close app/keyboard button
rightAction: MobileShell.NavigationPanelAction {
id: closeAppAction
enabled: MobileShell.KWinVirtualKeyboard.visible || root.taskSwitcher.visible || plasmoid.nativeInterface.hasCloseableActiveWindow
iconSource: MobileShell.KWinVirtualKeyboard.visible ? "go-down-symbolic" : "mobile-close-app"
// mobile-close-app (from plasma-frameworks) seems to have less margins than icons from breeze-icons
iconSizeFactor: MobileShell.KWinVirtualKeyboard.visible ? 1 : 0.75
onTriggered: {
if (MobileShell.KWinVirtualKeyboard.active) {
// close keyboard if it is open
MobileShell.KWinVirtualKeyboard.active = false;
} else if (taskSwitcher.visible) {
// if task switcher is open, close the current window shown
let indexToClose = root.taskSwitcher.tasksModel.index(root.taskSwitcher.currentTaskIndex, 0);
root.taskSwitcher.tasksModel.requestClose(indexToClose);
} else if (plasmoid.nativeInterface.hasCloseableActiveWindow) {
// if task switcher is closed, but there is an active window
if (root.taskSwitcher.tasksModel.activeTask !== 0) {
root.taskSwitcher.tasksModel.requestClose(root.taskSwitcher.tasksModel.activeTask);
}
}
}
}
}

View file

@ -7,7 +7,7 @@
import QtQuick 2.4
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
import QtQuick.Window 2.15
import QtGraphicalEffects 1.12
import org.kde.taskmanager 0.1 as TaskManager
@ -21,16 +21,31 @@ import org.kde.plasma.phone.taskpanel 1.0 as TaskPanel
PlasmaCore.ColorScope {
id: root
colorGroup: showingApp ? PlasmaCore.Theme.HeaderColorGroup : PlasmaCore.Theme.ComplementaryColorGroup
width: 360
// contrasting colour
colorGroup: !plasmoid.nativeInterface.allMinimized ? PlasmaCore.Theme.HeaderColorGroup : PlasmaCore.Theme.ComplementaryColorGroup
readonly property color backgroundColor: PlasmaCore.ColorScope.backgroundColor
Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground
readonly property color backgroundColor: PlasmaCore.ColorScope.backgroundColor
readonly property bool showingApp: !plasmoid.nativeInterface.allMinimized
readonly property bool hasTasks: tasksModel.count > 0
property var taskSwitcher: MobileShell.HomeScreenControls.taskSwitcher
// toggle visibility of navigation bar (show, or use gestures only)
Binding {
target: plasmoid.Window.window // assumed to be plasma-workspace "PanelView" component
property: "visibilityMode"
// 0 - VisibilityMode.NormalPanel
// 3 - VisibilityMode.WindowsGoBelow
value: MobileShell.MobileShellSettings.navigationPanelEnabled ? 0 : 3
}
Binding {
target: plasmoid.Window.window // assumed to be plasma-workspace "PanelView" component
property: "thickness"
// height of panel:
// - if navigation panel is enabled: PlasmaCore.Units.gridUnit * 2
// - if gestures only is enabled: 8 (just large enough for touch swipe to register, without blocking app content)
value: MobileShell.MobileShellSettings.navigationPanelEnabled ? PlasmaCore.Units.gridUnit * 2 : 8
}
//BEGIN API implementation
@ -42,16 +57,14 @@ PlasmaCore.ColorScope {
Binding {
target: MobileShell.TaskPanelControls
property: "panelHeight"
value: root.height
value: MobileShell.MobileShellSettings.navigationPanelEnabled ? root.height : 0
}
Binding {
target: MobileShell.TaskPanelControls
property: "panelWidth"
value: root.width
value: MobileShell.MobileShellSettings.navigationPanelEnabled ? root.width : 0
}
//END API implementation
Connections {
target: plasmoid.nativeInterface
function onAllMinimizedChanged() {
@ -59,143 +72,54 @@ PlasmaCore.ColorScope {
}
}
TaskManager.TasksModel {
id: tasksModel
groupMode: TaskManager.TasksModel.GroupDisabled
screenGeometry: plasmoid.screenGeometry
sortMode: TaskManager.TasksModel.SortAlpha
virtualDesktop: virtualDesktopInfo.currentDesktop
activity: activityInfo.currentActivity
}
TaskManager.VirtualDesktopInfo {
id: virtualDesktopInfo
}
TaskManager.ActivityInfo {
id: activityInfo
}
//END API implementation
Window.onWindowChanged: {
if (!Window.window)
return;
// ensure that Plasma sets the correct offset
Window.window.offset = Qt.binding(() => {
return plasmoid.formFactor === PlasmaCore.Types.Vertical ? MobileShell.TopPanelControls.panelHeight : 0
return (plasmoid.formFactor === PlasmaCore.Types.Vertical) ? MobileShell.TopPanelControls.panelHeight : MobileShell.TopPanelControls.panelWidth
});
}
// navigation panel actions
MobileShell.NavigationPanelAction {
id: taskSwitcherAction
enabled: hasTasks || taskSwitcher.visible
iconSource: "mobile-task-switcher"
iconSizeFactor: 0.75
onTriggered: {
plasmoid.nativeInterface.showDesktop = false;
if (!taskSwitcher.visible) {
taskSwitcher.show(true);
} else {
// when task switcher is open
if (taskSwitcher.taskSwitcherState.wasInActiveTask) {
// restore active window
taskSwitcher.activateWindow(taskSwitcher.taskSwitcherState.currentTaskIndex);
} else {
taskSwitcher.hide();
}
}
// bottom navigation panel component
Component {
id: navigationPanel
NavigationPanelComponent {
taskSwitcher: MobileShell.HomeScreenControls.taskSwitcher
}
}
MobileShell.NavigationPanelAction {
id: homeAction
enabled: true
iconSource: "start-here-kde"
iconSizeFactor: 1
onTriggered: {
MobileShell.HomeScreenControls.openHomeScreen();
plasmoid.nativeInterface.allMinimizedChanged();
// bottom navigation gesture area component
Component {
id: navigationGesture
MobileShell.NavigationGestureArea {
taskSwitcher: MobileShell.HomeScreenControls.taskSwitcher
}
}
MobileShell.NavigationPanelAction {
id: closeAppAction
enabled: MobileShell.KWinVirtualKeyboard.visible || taskSwitcher.visible || plasmoid.nativeInterface.hasCloseableActiveWindow
// mobile-close-app (from plasma-frameworks) seems to have less margins than icons from breeze-icons
iconSizeFactor: MobileShell.KWinVirtualKeyboard.visible ? 1 : 0.75
iconSource: MobileShell.KWinVirtualKeyboard.visible ? "go-down-symbolic" : "mobile-close-app"
onTriggered: {
if (MobileShell.KWinVirtualKeyboard.active) {
// close keyboard if it is open
MobileShell.KWinVirtualKeyboard.active = false;
} else if (taskSwitcher.visible) {
// if task switcher is open, close the current window shown
taskSwitcher.tasksModel.requestClose(taskSwitcher.tasksModel.index(taskSwitcher.currentTaskIndex, 0));
} else if (plasmoid.nativeInterface.hasCloseableActiveWindow) {
// if task switcher is closed, but there is an active window
var index = taskSwitcher.tasksModel.activeTask;
if (index) {
taskSwitcher.tasksModel.requestClose(index);
}
}
}
}
// bottom navigation panel
MobileShell.NavigationPanel {
id: panel
// load appropriate system navigation component
Loader {
id: navigationLoader
anchors.fill: parent
taskSwitcher: root.taskSwitcher
backgroundColor: {
if (taskSwitcher.visible) {
return Qt.rgba(0, 0, 0, 0.1);
} else {
return root.showingApp ? root.backgroundColor : "transparent";
}
}
foregroundColorGroup: (!taskSwitcher.visible && root.showingApp) ? PlasmaCore.Theme.NormalColorGroup : PlasmaCore.Theme.ComplementaryColorGroup
// do not enable drag gesture when task switcher is already open
// also don't disable drag gesture mid-drag
dragGestureEnabled: !taskSwitcher.visible || taskSwitcher.taskSwitcherState.currentlyBeingOpened
leftAction: taskSwitcherAction
middleAction: homeAction
rightAction: closeAppAction
sourceComponent: MobileShell.MobileShellSettings.navigationPanelEnabled ? navigationPanel : navigationGesture
}
// landscape vs. portrait orientation of panel
states: [
State {
name: "landscape"
when: Screen.width > Screen.height
when: MobileShell.Shell.orientation === MobileShell.Shell.Landscape
PropertyChanges {
target: plasmoid.nativeInterface
location: PlasmaCore.Types.RightEdge
}
PropertyChanges {
target: plasmoid
width: PlasmaCore.Units.gridUnit
height: PlasmaCore.Units.gridUnit
// only show on right edge if gestures are not enabled
location: MobileShell.MobileShellSettings.navigationPanelEnabled ? PlasmaCore.Types.RightEdge : PlasmaCore.Types.BottomEdge
}
}, State {
name: "portrait"
when: Screen.width <= Screen.height
PropertyChanges {
target: plasmoid
height: PlasmaCore.Units.gridUnit
}
when: MobileShell.Shell.orientation === MobileShell.Shell.Portrait
PropertyChanges {
target: plasmoid.nativeInterface
location: PlasmaCore.Types.BottomEdge

View file

@ -76,7 +76,7 @@ void TaskPanel::initWayland()
return;
}
m_showingDesktop = showing;
emit showingDesktopChanged(m_showingDesktop);
Q_EMIT showingDesktopChanged(m_showingDesktop);
});
connect(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::activeWindowChanged, m_activeTimer, qOverload<>(&QTimer::start));
@ -115,10 +115,17 @@ void TaskPanel::setPanel(QWindow *panel)
}
m_panel = panel;
connect(m_panel, &QWindow::visibilityChanged, this, &TaskPanel::updatePanelVisibility, Qt::QueuedConnection);
emit panelChanged();
Q_EMIT panelChanged();
updatePanelVisibility();
}
void TaskPanel::setPanelHeight(qreal height)
{
if (m_panel) {
m_panel->setHeight(height);
}
}
void TaskPanel::updatePanelVisibility()
{
using namespace KWayland::Client;
@ -163,10 +170,10 @@ void TaskPanel::updateActiveWindow()
}
if (newAllMinimized != m_allMinimized) {
m_allMinimized = newAllMinimized;
emit allMinimizedChanged();
Q_EMIT allMinimizedChanged();
}
// TODO: connect to closeableChanged, not needed right now as KWin doesn't provide this changeable
emit hasCloseableActiveWindowChanged();
Q_EMIT hasCloseableActiveWindowChanged();
}
bool TaskPanel::hasCloseableActiveWindow() const
@ -181,7 +188,7 @@ void TaskPanel::forgetActiveWindow()
disconnect(m_activeWindow.data(), &KWayland::Client::PlasmaWindow::unmapped, this, &TaskPanel::forgetActiveWindow);
}
m_activeWindow.clear();
emit hasCloseableActiveWindowChanged();
Q_EMIT hasCloseableActiveWindowChanged();
}
void TaskPanel::closeActiveWindow()

View file

@ -41,6 +41,8 @@ public:
QWindow *panel();
void setPanel(QWindow *panel);
Q_INVOKABLE void setPanelHeight(qreal height);
Q_INVOKABLE void closeActiveWindow();
bool isShowingDesktop() const

4
kcms/CMakeLists.txt Normal file
View file

@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
# SPDX-License-Identifier: GPL-2.0-or-later
add_subdirectory(mobileshell)

View file

@ -0,0 +1,88 @@
---
# SPDX-FileCopyrightText: 2019 Christoph Cullmann <cullmann@kde.org>
# SPDX-FileCopyrightText: 2019 Gernot Gebhard <gebhard@absint.com>
#
# SPDX-License-Identifier: MIT
# This file got automatically created by ECM, do not edit
# See https://clang.llvm.org/docs/ClangFormatStyleOptions.html for the config options
# and https://community.kde.org/Policies/Frameworks_Coding_Style#Clang-format_automatic_code_formatting
# for clang-format tips & tricks
---
Language: JavaScript
DisableFormat: true
---
# Style for C++
Language: Cpp
# base is WebKit coding style: https://webkit.org/code-style-guidelines/
# below are only things set that diverge from this style!
BasedOnStyle: WebKit
# enforce C++11 (e.g. for std::vector<std::vector<lala>>
Standard: Cpp11
# 4 spaces indent
TabWidth: 4
# 2 * 80 wide lines
ColumnLimit: 160
# sort includes inside line separated groups
SortIncludes: true
# break before braces on function, namespace and class definitions.
BreakBeforeBraces: Linux
# CrlInstruction *a;
PointerAlignment: Right
# horizontally aligns arguments after an open bracket.
AlignAfterOpenBracket: Align
# don't move all parameters to new line
AllowAllParametersOfDeclarationOnNextLine: false
# no single line functions
AllowShortFunctionsOnASingleLine: None
# always break before you encounter multi line strings
AlwaysBreakBeforeMultilineStrings: true
# don't move arguments to own lines if they are not all on the same
BinPackArguments: false
# don't move parameters to own lines if they are not all on the same
BinPackParameters: false
# In case we have an if statement with multiple lines the operator should be at the beginning of the line
# but we do not want to break assignments
BreakBeforeBinaryOperators: NonAssignment
# format C++11 braced lists like function calls
Cpp11BracedListStyle: true
# do not put a space before C++11 braced lists
SpaceBeforeCpp11BracedList: false
# remove empty lines
KeepEmptyLinesAtTheStartOfBlocks: false
# no namespace indentation to keep indent level low
NamespaceIndentation: None
# we use template< without space.
SpaceAfterTemplateKeyword: false
# Always break after template declaration
AlwaysBreakTemplateDeclarations: true
# macros for which the opening brace stays attached.
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE , wl_resource_for_each, wl_resource_for_each_safe ]
# keep lambda formatting multi-line if not empty
AllowShortLambdasOnASingleLine: Empty
# We do not want clang-format to put all arguments on a new line
AllowAllArgumentsOnNextLine: false

View file

@ -0,0 +1,50 @@
# SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
# SPDX-License-Identifier: GPL-2.0-or-later
cmake_minimum_required(VERSION 3.0)
project(mobileshellkcm)
set(QT_MIN_VERSION "5.15.0")
set(KF5_MIN_VERSION "5.86.0")
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
Quick
Svg
)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
I18n
KCMUtils
Declarative
Config
)
set(mobileshellsettings_SRCS
kcm.cpp
)
add_library(kcm_mobileshell MODULE ${mobileshellsettings_SRCS})
target_link_libraries(kcm_mobileshell
Qt5::Core
KF5::CoreAddons
KF5::KCMUtils
KF5::I18n
KF5::QuickAddons
)
kcoreaddons_desktop_to_json(kcm_mobileshell "package/metadata.desktop")
install(TARGETS kcm_mobileshell DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms)
install(FILES package/metadata.desktop RENAME kcm_mobileshell.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) # Install the desktop file
kpackage_install_package(package kcm_mobileshell kcms) # Install our QML kpackage.

35
kcms/mobileshell/kcm.cpp Normal file
View file

@ -0,0 +1,35 @@
/**
* SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "kcm.h"
#include <KPluginFactory>
K_PLUGIN_CLASS_WITH_JSON(KCMMobileShell, "metadata.json")
const QString CONFIG_FILE = QStringLiteral("plasmamobilerc");
const QString GENERAL_CONFIG_GROUP = QStringLiteral("General");
KCMMobileShell::KCMMobileShell(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
: KQuickAddons::ManagedConfigModule(parent, data, args)
, m_config{KSharedConfig::openConfig("plasmamobilerc", KConfig::SimpleConfig)}
{
setButtons(0);
}
bool KCMMobileShell::navigationPanelEnabled() const
{
auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP};
return group.readEntry("navigationPanelEnabled", true);
}
void KCMMobileShell::setNavigationPanelEnabled(bool navigationPanelEnabled)
{
auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP};
group.writeEntry("navigationPanelEnabled", navigationPanelEnabled, KConfigGroup::Notify);
m_config->sync();
}
#include "kcm.moc"

29
kcms/mobileshell/kcm.h Normal file
View file

@ -0,0 +1,29 @@
/**
* SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <KConfigGroup>
#include <KQuickAddons/ManagedConfigModule>
#include <KSharedConfig>
class KCMMobileShell : public KQuickAddons::ManagedConfigModule
{
Q_OBJECT
Q_PROPERTY(bool navigationPanelEnabled READ navigationPanelEnabled WRITE setNavigationPanelEnabled NOTIFY navigationPanelEnabledChanged)
public:
KCMMobileShell(QObject *parent, const KPluginMetaData &data, const QVariantList &args);
virtual ~KCMMobileShell() override = default;
bool navigationPanelEnabled() const;
void setNavigationPanelEnabled(bool navigationPanelEnabled);
Q_SIGNALS:
void navigationPanelEnabledChanged();
private:
KSharedConfig::Ptr m_config;
};

View file

@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.kirigami 2.19 as Kirigami
import org.kde.kcm 1.3 as KCM
KCM.SimpleKCM {
id: root
title: i18n("Shell")
leftPadding: Kirigami.Units.largeSpacing
rightPadding: Kirigami.Units.largeSpacing
Kirigami.FormLayout {
id: form
wideMode: false
Item {
Layout.fillWidth: true
Kirigami.FormData.label: i18n("Navigation Panel")
Kirigami.FormData.isSection: true
}
QQC2.CheckBox {
Kirigami.FormData.label: i18n("Remove panel (only use gestures):")
Layout.maximumWidth: form.width
text: checked ? i18n("On") : i18n("Off")
checked: !kcm.navigationPanelEnabled
onCheckStateChanged: {
if (checked != !kcm.navigationPanelEnabled) {
kcm.navigationPanelEnabled = !checked;
}
}
}
}
}

View file

@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
# SPDX-License-Identifier: GPL-2.0-or-later
[Desktop Entry]
Name=Shell
Comment=Configure the system shell
Encoding=UTF-8
Type=Service
Icon=preferences-desktop-plasma
X-KDE-Library=kcm_mobileshell
X-KDE-ServiceTypes=KCModule
X-KDE-FormFactors=desktop,handset,tablet,mediacenter
X-Plasma-MainScript=ui/main.qml
X-KDE-System-Settings-Parent-Category=personalization
X-KDE-PluginInfo-Author=Devin Lin
X-KDE-PluginInfo-Email=espidev@gmail.com
X-KDE-PluginInfo-Name=kcm_cellular_network
X-KDE-PluginInfo-Version=0.1
X-KDE-PluginInfo-Website=https://plasma-mobile.org/
X-KDE-PluginInfo-Category=System Information
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=true
X-KDE-Keywords=system,shell,panel
X-KDE-ParentApp=kcontrol
X-KDE-PluginInfo-Name=kcm_mobileshell