mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-06-25 07:37:42 +00:00
Compare commits
No commits in common. "a09f349a3448bac53c1fac4b0b0f706f0198a671" and "8f3a94b1048add03351f04d2bfc788f62b559ffe" have entirely different histories.
a09f349a34
...
8f3a94b104
29 changed files with 50 additions and 765 deletions
2
CHANGELOG.md.license
Normal file
2
CHANGELOG.md.license
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
SPDX-FileCopyrightText: 2025 Marco A.
|
||||||
|
SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
SPDX-FileCopyrightText: 2026 Marco Allegretti
|
|
||||||
SPDX-License-Identifier: EUPL-1.2
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
Shift is a fork of [plasma-mobile](https://invent.kde.org/plasma/plasma-mobile).
|
Shift is a fork of [plasma-mobile](https://invent.kde.org/plasma/plasma-mobile).
|
||||||
The upstream phone UI is untouched; convergence adds a layer on top.
|
The upstream phone UI is untouched; convergence adds a layer on top.
|
||||||
|
|
|
||||||
|
|
@ -91,18 +91,7 @@ QQuickItem *AppletHost::fullRepresentationFor(const QString &pluginId)
|
||||||
|
|
||||||
item->setPreloadFullRepresentation(true);
|
item->setPreloadFullRepresentation(true);
|
||||||
|
|
||||||
auto *fullRepItem = item->fullRepresentationItem();
|
return item->fullRepresentationItem();
|
||||||
if (!fullRepItem) {
|
|
||||||
connect(
|
|
||||||
item,
|
|
||||||
&PlasmaQuick::AppletQuickItem::fullRepresentationItemChanged,
|
|
||||||
this,
|
|
||||||
[this, pluginId]() {
|
|
||||||
Q_EMIT appletReady(pluginId);
|
|
||||||
},
|
|
||||||
static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::SingleShotConnection));
|
|
||||||
}
|
|
||||||
return fullRepItem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "applethost.moc"
|
#include "applethost.moc"
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,6 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE QQuickItem *fullRepresentationFor(const QString &pluginId);
|
Q_INVOKABLE QQuickItem *fullRepresentationFor(const QString &pluginId);
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void appletReady(const QString &pluginId);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ensureCorona();
|
void ensureCorona();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,6 @@ Item {
|
||||||
|
|
||||||
// In convergence, cap the height so it doesn't stretch full-screen
|
// In convergence, cap the height so it doesn't stretch full-screen
|
||||||
maximumHeight: isConvergence ? root.height * 0.6 : -1
|
maximumHeight: isConvergence ? root.height * 0.6 : -1
|
||||||
toolButtonsItem: toolButtons
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secondary swipe area for uses in portrait.
|
// Secondary swipe area for uses in portrait.
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,6 @@ Item {
|
||||||
property alias notificationWidget: notificationWidget
|
property alias notificationWidget: notificationWidget
|
||||||
property real contentY: notificationWidget.listView.contentY
|
property real contentY: notificationWidget.listView.contentY
|
||||||
|
|
||||||
// The sibling toolbar whose height must be subtracted from the available space.
|
|
||||||
property Item toolButtonsItem: null
|
|
||||||
|
|
||||||
property real topPadding: {
|
property real topPadding: {
|
||||||
if (actionDrawer.mode == MobileShell.ActionDrawer.Portrait)
|
if (actionDrawer.mode == MobileShell.ActionDrawer.Portrait)
|
||||||
return Kirigami.Units.largeSpacing;
|
return Kirigami.Units.largeSpacing;
|
||||||
|
|
@ -45,8 +42,7 @@ Item {
|
||||||
property real maximumHeight: -1
|
property real maximumHeight: -1
|
||||||
|
|
||||||
height: {
|
height: {
|
||||||
let toolH = toolButtonsItem ? toolButtonsItem.height : 0;
|
let h = Math.min(actionDrawer.height - toolButtons.height, notificationWidget.listView.contentHeight + 10 + topMargin);
|
||||||
let h = Math.min(actionDrawer.height - toolH, notificationWidget.listView.contentHeight + Kirigami.Units.largeSpacing + topMargin);
|
|
||||||
return maximumHeight > 0 ? Math.min(h, maximumHeight) : h;
|
return maximumHeight > 0 ? Math.min(h, maximumHeight) : h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -241,22 +241,10 @@ Item {
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: PageIndicator {
|
sourceComponent: PageIndicator {
|
||||||
id: pageIndicatorItem
|
|
||||||
count: swipeView.count
|
count: swipeView.count
|
||||||
currentIndex: swipeView.currentIndex
|
currentIndex: swipeView.currentIndex
|
||||||
interactive: true
|
interactive: true
|
||||||
onCurrentIndexChanged: {
|
onCurrentIndexChanged: swipeView.currentIndex = currentIndex
|
||||||
if (swipeView.currentIndex !== currentIndex)
|
|
||||||
swipeView.currentIndex = currentIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: swipeView
|
|
||||||
function onCurrentIndexChanged() {
|
|
||||||
if (pageIndicatorItem.currentIndex !== swipeView.currentIndex)
|
|
||||||
pageIndicatorItem.currentIndex = swipeView.currentIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
implicitWidth: 8
|
implicitWidth: 8
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ Item {
|
||||||
orientation: root.isVertical ? ListView.Vertical : ListView.Horizontal
|
orientation: root.isVertical ? ListView.Vertical : ListView.Horizontal
|
||||||
spacing: Kirigami.Units.smallSpacing
|
spacing: Kirigami.Units.smallSpacing
|
||||||
clip: true
|
clip: true
|
||||||
interactive: root.isVertical ? contentHeight > height : contentWidth > width
|
interactive: contentWidth > width
|
||||||
model: root.taskModel
|
model: root.taskModel
|
||||||
|
|
||||||
delegate: NavigationPanelButton {
|
delegate: NavigationPanelButton {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ Item {
|
||||||
// Hover highlight in convergence mode to indicate the bar is clickable
|
// Hover highlight in convergence mode to indicate the bar is clickable
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.1)
|
color: Qt.rgba(255, 255, 255, 0.1)
|
||||||
visible: ShellSettings.Settings.convergenceModeEnabled && statusBarHover.hovered
|
visible: ShellSettings.Settings.convergenceModeEnabled && statusBarHover.hovered
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,9 +50,6 @@ Item {
|
||||||
|
|
||||||
var operationName = mouse.button === Qt.RightButton ? "ContextMenu" : "Activate";
|
var operationName = mouse.button === Qt.RightButton ? "ContextMenu" : "Activate";
|
||||||
var operation = model.service.operationDescription(operationName);
|
var operation = model.service.operationDescription(operationName);
|
||||||
if (!operation) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
operation.x = taskIcon.mapToGlobal(0, 0).x;
|
operation.x = taskIcon.mapToGlobal(0, 0).x;
|
||||||
operation.y = taskIcon.mapToGlobal(0, taskIcon.height).y;
|
operation.y = taskIcon.mapToGlobal(0, taskIcon.height).y;
|
||||||
model.service.startOperationCall(operation);
|
model.service.startOperationCall(operation);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
# SPDX-FileCopyrightText: 2025 Florian RICHER <florian.richer@protonmail.com>
|
# SPDX-FileCopyrightText: 2025 Florian RICHER <florian.richer@protonmail.com>
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
if(NOT TARGET KF6::AuthCore)
|
|
||||||
find_package(KF6Auth NO_MODULE REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(waydroidhelper)
|
add_executable(waydroidhelper)
|
||||||
target_sources(waydroidhelper PRIVATE waydroidhelper.cpp)
|
target_sources(waydroidhelper PRIVATE waydroidhelper.cpp)
|
||||||
|
|
||||||
|
|
@ -16,6 +12,10 @@ target_link_libraries(waydroidhelper
|
||||||
|
|
||||||
install(TARGETS waydroidhelper DESTINATION ${KAUTH_HELPER_INSTALL_DIR})
|
install(TARGETS waydroidhelper DESTINATION ${KAUTH_HELPER_INSTALL_DIR})
|
||||||
|
|
||||||
|
if(NOT TARGET KF6::AuthCore)
|
||||||
|
find_package(KF6Auth NO_MODULE REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
kauth_install_helper_files(waydroidhelper org.kde.plasma.mobileshell.waydroidhelper root)
|
kauth_install_helper_files(waydroidhelper org.kde.plasma.mobileshell.waydroidhelper root)
|
||||||
kauth_install_actions(org.kde.plasma.mobileshell.waydroidhelper waydroidhelper.actions)
|
kauth_install_actions(org.kde.plasma.mobileshell.waydroidhelper waydroidhelper.actions)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ plasma_add_applet(org.kde.plasma.mobile.homescreen.folio
|
||||||
qml/AppDrawer.qml
|
qml/AppDrawer.qml
|
||||||
qml/AppDrawerGrid.qml
|
qml/AppDrawerGrid.qml
|
||||||
qml/AppDrawerHeader.qml
|
qml/AppDrawerHeader.qml
|
||||||
qml/CategoryPanel.qml
|
|
||||||
qml/DelegateDragItem.qml
|
qml/DelegateDragItem.qml
|
||||||
qml/DelegateDropArea.qml
|
qml/DelegateDropArea.qml
|
||||||
qml/FavouritesBar.qml
|
qml/FavouritesBar.qml
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,7 @@ ApplicationListModel::~ApplicationListModel() = default;
|
||||||
|
|
||||||
QHash<int, QByteArray> ApplicationListModel::roleNames() const
|
QHash<int, QByteArray> ApplicationListModel::roleNames() const
|
||||||
{
|
{
|
||||||
return {
|
return {{DelegateRole, QByteArrayLiteral("delegate")}};
|
||||||
{DelegateRole, QByteArrayLiteral("delegate")},
|
|
||||||
{NameRole, QByteArrayLiteral("name")},
|
|
||||||
{CategoriesRole, QByteArrayLiteral("categories")},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplicationListModel::sycocaDbChanged()
|
void ApplicationListModel::sycocaDbChanged()
|
||||||
|
|
@ -161,11 +157,6 @@ QVariant ApplicationListModel::data(const QModelIndex &index, int role) const
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
return delegate->application()->name();
|
return delegate->application()->name();
|
||||||
case CategoriesRole:
|
|
||||||
if (!delegate->application()) {
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
return QVariant::fromValue(delegate->application()->categories());
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
@ -180,54 +171,8 @@ int ApplicationListModel::rowCount(const QModelIndex &parent) const
|
||||||
return m_delegates.count();
|
return m_delegates.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sub-categories merged into their canonical parent, mirroring Kickoff's grouping.
|
|
||||||
static QString normalizeCategory(const QString &cat)
|
|
||||||
{
|
|
||||||
if (cat == QLatin1String("Audio") || cat == QLatin1String("Video"))
|
|
||||||
return QStringLiteral("AudioVideo");
|
|
||||||
if (cat == QLatin1String("Settings"))
|
|
||||||
return QStringLiteral("System");
|
|
||||||
return cat;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const QSet<QString> &mainCategories()
|
|
||||||
{
|
|
||||||
static const QSet<QString> s = {
|
|
||||||
QStringLiteral("AudioVideo"),
|
|
||||||
QStringLiteral("Development"),
|
|
||||||
QStringLiteral("Education"),
|
|
||||||
QStringLiteral("Game"),
|
|
||||||
QStringLiteral("Graphics"),
|
|
||||||
QStringLiteral("Network"),
|
|
||||||
QStringLiteral("Office"),
|
|
||||||
QStringLiteral("Science"),
|
|
||||||
QStringLiteral("System"),
|
|
||||||
QStringLiteral("Utility"),
|
|
||||||
};
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList ApplicationListModel::allCategories() const
|
|
||||||
{
|
|
||||||
QSet<QString> found;
|
|
||||||
for (const auto &del : m_delegates) {
|
|
||||||
if (!del->application())
|
|
||||||
continue;
|
|
||||||
for (const QString &raw : del->application()->categories()) {
|
|
||||||
const QString cat = normalizeCategory(raw);
|
|
||||||
if (mainCategories().contains(cat))
|
|
||||||
found.insert(cat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList result = found.values();
|
|
||||||
result.sort();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ApplicationListSearchModel::ApplicationListSearchModel(HomeScreen *parent, ApplicationListModel *model)
|
ApplicationListSearchModel::ApplicationListSearchModel(HomeScreen *parent, ApplicationListModel *model)
|
||||||
: QSortFilterProxyModel(parent)
|
: QSortFilterProxyModel(parent)
|
||||||
, m_homeScreen{parent}
|
|
||||||
{
|
{
|
||||||
setSourceModel(model);
|
setSourceModel(model);
|
||||||
|
|
||||||
|
|
@ -240,52 +185,3 @@ ApplicationListSearchModel::ApplicationListSearchModel(HomeScreen *parent, Appli
|
||||||
|
|
||||||
sort(0, Qt::AscendingOrder);
|
sort(0, Qt::AscendingOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ApplicationListSearchModel::categoryFilter() const
|
|
||||||
{
|
|
||||||
return m_categoryFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApplicationListSearchModel::setCategoryFilter(const QString &filter)
|
|
||||||
{
|
|
||||||
if (m_categoryFilter == filter)
|
|
||||||
return;
|
|
||||||
beginFilterChange();
|
|
||||||
m_categoryFilter = filter;
|
|
||||||
Q_EMIT categoryFilterChanged();
|
|
||||||
endFilterChange(QSortFilterProxyModel::Direction::Rows);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ApplicationListSearchModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
|
||||||
{
|
|
||||||
if (!QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (m_categoryFilter.isEmpty())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
auto *src = static_cast<ApplicationListModel *>(sourceModel());
|
|
||||||
if (!src)
|
|
||||||
return false;
|
|
||||||
const QModelIndex idx = src->index(sourceRow, 0, sourceParent);
|
|
||||||
auto *del = src->data(idx, ApplicationListModel::DelegateRole).value<FolioDelegate *>();
|
|
||||||
if (!del || !del->application())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (m_categoryFilter == QLatin1String("__favorites__")) {
|
|
||||||
if (!m_homeScreen)
|
|
||||||
return false;
|
|
||||||
auto *favModel = m_homeScreen->favouritesModel();
|
|
||||||
if (!favModel)
|
|
||||||
return false;
|
|
||||||
return favModel->containsApplication(del->application()->storageId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match both the canonical name and any raw aliases it absorbs.
|
|
||||||
const QStringList &cats = del->application()->categories();
|
|
||||||
for (const QString &raw : cats) {
|
|
||||||
if (normalizeCategory(raw) == m_categoryFilter)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QQuickItem>
|
#include <QQuickItem>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
#include <KService>
|
#include <KService>
|
||||||
|
|
||||||
|
|
@ -33,7 +31,6 @@ public:
|
||||||
enum Roles {
|
enum Roles {
|
||||||
DelegateRole = Qt::UserRole + 1,
|
DelegateRole = Qt::UserRole + 1,
|
||||||
NameRole,
|
NameRole,
|
||||||
CategoriesRole,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ApplicationListModel(HomeScreen *parent = nullptr);
|
ApplicationListModel(HomeScreen *parent = nullptr);
|
||||||
|
|
@ -45,8 +42,6 @@ public:
|
||||||
|
|
||||||
void load();
|
void load();
|
||||||
|
|
||||||
Q_INVOKABLE QStringList allCategories() const;
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
// Emitted when an application was detected to have been removed from the system
|
// Emitted when an application was detected to have been removed from the system
|
||||||
void applicationRemoved(QString storageId);
|
void applicationRemoved(QString storageId);
|
||||||
|
|
@ -69,21 +64,6 @@ class ApplicationListSearchModel : public QSortFilterProxyModel
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_UNCREATABLE("")
|
QML_UNCREATABLE("")
|
||||||
|
|
||||||
Q_PROPERTY(QString categoryFilter READ categoryFilter WRITE setCategoryFilter NOTIFY categoryFilterChanged)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ApplicationListSearchModel(HomeScreen *parent = nullptr, ApplicationListModel *model = nullptr);
|
ApplicationListSearchModel(HomeScreen *parent = nullptr, ApplicationListModel *model = nullptr);
|
||||||
|
|
||||||
QString categoryFilter() const;
|
|
||||||
void setCategoryFilter(const QString &filter);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void categoryFilterChanged();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
HomeScreen *m_homeScreen{nullptr};
|
|
||||||
QString m_categoryFilter;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ FolioApplication::FolioApplication(KService::Ptr service, QObject *parent)
|
||||||
, m_name{service->name()}
|
, m_name{service->name()}
|
||||||
, m_icon{service->icon()}
|
, m_icon{service->icon()}
|
||||||
, m_storageId{service->storageId()}
|
, m_storageId{service->storageId()}
|
||||||
, m_categories{service->categories()}
|
|
||||||
{
|
{
|
||||||
if (service->property<bool>(QStringLiteral("X-KDE-PlasmaMobile-UseGenericName"))) {
|
if (service->property<bool>(QStringLiteral("X-KDE-PlasmaMobile-UseGenericName"))) {
|
||||||
m_name = service->genericName();
|
m_name = service->genericName();
|
||||||
|
|
@ -77,11 +76,6 @@ QString FolioApplication::storageId() const
|
||||||
return m_storageId;
|
return m_storageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList FolioApplication::categories() const
|
|
||||||
{
|
|
||||||
return m_categories;
|
|
||||||
}
|
|
||||||
|
|
||||||
KWayland::Client::PlasmaWindow *FolioApplication::window() const
|
KWayland::Client::PlasmaWindow *FolioApplication::window() const
|
||||||
{
|
{
|
||||||
return m_window;
|
return m_window;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QQuickItem>
|
#include <QQuickItem>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
#include <KIO/ApplicationLauncherJob>
|
#include <KIO/ApplicationLauncherJob>
|
||||||
#include <KService>
|
#include <KService>
|
||||||
|
|
@ -34,7 +33,6 @@ class FolioApplication : public QObject, public std::enable_shared_from_this<Fol
|
||||||
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
|
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
|
||||||
Q_PROPERTY(QString icon READ icon NOTIFY iconChanged)
|
Q_PROPERTY(QString icon READ icon NOTIFY iconChanged)
|
||||||
Q_PROPERTY(QString storageId READ storageId NOTIFY storageIdChanged)
|
Q_PROPERTY(QString storageId READ storageId NOTIFY storageIdChanged)
|
||||||
Q_PROPERTY(QStringList categories READ categories CONSTANT)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::shared_ptr<FolioApplication> Ptr;
|
typedef std::shared_ptr<FolioApplication> Ptr;
|
||||||
|
|
@ -48,7 +46,6 @@ public:
|
||||||
QString name() const;
|
QString name() const;
|
||||||
QString icon() const;
|
QString icon() const;
|
||||||
QString storageId() const;
|
QString storageId() const;
|
||||||
QStringList categories() const;
|
|
||||||
KWayland::Client::PlasmaWindow *window() const;
|
KWayland::Client::PlasmaWindow *window() const;
|
||||||
|
|
||||||
void setName(QString &name);
|
void setName(QString &name);
|
||||||
|
|
@ -70,6 +67,5 @@ private:
|
||||||
QString m_name;
|
QString m_name;
|
||||||
QString m_icon;
|
QString m_icon;
|
||||||
QString m_storageId;
|
QString m_storageId;
|
||||||
QStringList m_categories;
|
|
||||||
KWayland::Client::PlasmaWindow *m_window{nullptr};
|
KWayland::Client::PlasmaWindow *m_window{nullptr};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import QtQuick.Layouts
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||||
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
|
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
|
||||||
import './delegate'
|
import './delegate'
|
||||||
|
|
||||||
|
|
@ -89,6 +90,21 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close button for convergence mode
|
||||||
|
QQC2.ToolButton {
|
||||||
|
visible: ShellSettings.Settings.convergenceModeEnabled
|
||||||
|
icon.name: "window-close-symbolic"
|
||||||
|
icon.color: "white"
|
||||||
|
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
|
||||||
|
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
|
||||||
|
onClicked: folio.HomeScreenState.closeAppDrawer()
|
||||||
|
|
||||||
|
QQC2.ToolTip.text: i18n("Close")
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
|
||||||
|
Kirigami.Theme.inherit: false
|
||||||
|
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: Marco Allegretti
|
|
||||||
// SPDX-License-Identifier: EUPL 1.2
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls as QQC2
|
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
|
||||||
|
|
||||||
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
required property Folio.HomeScreen folio
|
|
||||||
|
|
||||||
// Emitted when the user taps a tile.
|
|
||||||
signal categorySelected(string categoryId)
|
|
||||||
|
|
||||||
color: Kirigami.Theme.backgroundColor
|
|
||||||
radius: Kirigami.Units.cornerRadius
|
|
||||||
|
|
||||||
// Swallow clicks so the dismiss area underneath is not triggered.
|
|
||||||
MouseArea { anchors.fill: parent }
|
|
||||||
|
|
||||||
// ---------- helpers ----------
|
|
||||||
|
|
||||||
function catDisplayName(cat) {
|
|
||||||
switch (cat) {
|
|
||||||
case "AudioVideo": return i18n("Multimedia")
|
|
||||||
case "Development": return i18n("Development")
|
|
||||||
case "Education": return i18n("Education")
|
|
||||||
case "Game": return i18n("Games")
|
|
||||||
case "Graphics": return i18n("Graphics")
|
|
||||||
case "Network": return i18n("Internet")
|
|
||||||
case "Office": return i18n("Office")
|
|
||||||
case "Science": return i18n("Science")
|
|
||||||
case "System": return i18n("System")
|
|
||||||
case "Utility": return i18n("Utilities")
|
|
||||||
default: return cat
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function catIconName(cat) {
|
|
||||||
switch (cat) {
|
|
||||||
case "AudioVideo": return "applications-multimedia"
|
|
||||||
case "Development": return "applications-development"
|
|
||||||
case "Education": return "applications-education"
|
|
||||||
case "Game": return "applications-games"
|
|
||||||
case "Graphics": return "applications-graphics"
|
|
||||||
case "Network": return "applications-internet"
|
|
||||||
case "Office": return "applications-office"
|
|
||||||
case "Science": return "applications-science"
|
|
||||||
case "System": return "applications-system"
|
|
||||||
case "Utility": return "applications-utilities"
|
|
||||||
default: return "applications-other"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- model ----------
|
|
||||||
|
|
||||||
ListModel { id: categoryModel }
|
|
||||||
|
|
||||||
function populate() {
|
|
||||||
categoryModel.clear()
|
|
||||||
categoryModel.append({ catId: "", catName: i18n("All Apps"), catIcon: "applications-all" })
|
|
||||||
const cats = folio.ApplicationListModel.allCategories()
|
|
||||||
for (let i = 0; i < cats.length; i++) {
|
|
||||||
categoryModel.append({
|
|
||||||
catId: cats[i],
|
|
||||||
catName: root.catDisplayName(cats[i]),
|
|
||||||
catIcon: root.catIconName(cats[i]),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: populate()
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: folio.ApplicationListModel
|
|
||||||
function onRowsInserted() { root.populate() }
|
|
||||||
function onRowsRemoved() { root.populate() }
|
|
||||||
function onModelReset() { root.populate() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- tile list ----------
|
|
||||||
|
|
||||||
QQC2.ScrollView {
|
|
||||||
id: scrollView
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Kirigami.Units.smallSpacing
|
|
||||||
contentWidth: availableWidth
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
QQC2.ScrollBar.vertical.policy: QQC2.ScrollBar.AsNeeded
|
|
||||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: scrollView.availableWidth
|
|
||||||
spacing: Kirigami.Units.smallSpacing
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: categoryModel
|
|
||||||
|
|
||||||
delegate: Rectangle {
|
|
||||||
id: tile
|
|
||||||
|
|
||||||
required property string catId
|
|
||||||
required property string catName
|
|
||||||
required property string catIcon
|
|
||||||
|
|
||||||
readonly property bool isActive:
|
|
||||||
folio.ApplicationListSearchModel.categoryFilter === catId
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: Kirigami.Units.iconSizes.medium + 2 * Kirigami.Units.largeSpacing
|
|
||||||
radius: Kirigami.Units.cornerRadius
|
|
||||||
|
|
||||||
color: isActive
|
|
||||||
? Qt.rgba(Kirigami.Theme.highlightColor.r,
|
|
||||||
Kirigami.Theme.highlightColor.g,
|
|
||||||
Kirigami.Theme.highlightColor.b, 0.2)
|
|
||||||
: tileArea.containsPress
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r,
|
|
||||||
Kirigami.Theme.textColor.g,
|
|
||||||
Kirigami.Theme.textColor.b, 0.2)
|
|
||||||
: tileArea.containsMouse
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r,
|
|
||||||
Kirigami.Theme.textColor.g,
|
|
||||||
Kirigami.Theme.textColor.b, 0.1)
|
|
||||||
: "transparent"
|
|
||||||
|
|
||||||
// Active accent bar on left edge
|
|
||||||
Rectangle {
|
|
||||||
visible: tile.isActive
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.topMargin: Kirigami.Units.smallSpacing
|
|
||||||
anchors.bottomMargin: Kirigami.Units.smallSpacing
|
|
||||||
width: 3
|
|
||||||
radius: 2
|
|
||||||
color: Kirigami.Theme.highlightColor
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
leftMargin: Kirigami.Units.largeSpacing
|
|
||||||
rightMargin: Kirigami.Units.smallSpacing
|
|
||||||
}
|
|
||||||
spacing: Kirigami.Units.largeSpacing
|
|
||||||
|
|
||||||
Kirigami.Icon {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
width: Kirigami.Units.iconSizes.medium
|
|
||||||
height: width
|
|
||||||
source: tile.catIcon
|
|
||||||
active: tileArea.containsMouse || tile.isActive
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaComponents.Label {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
width: parent.width
|
|
||||||
- Kirigami.Units.iconSizes.medium
|
|
||||||
- Kirigami.Units.largeSpacing * 2
|
|
||||||
- Kirigami.Units.smallSpacing
|
|
||||||
text: tile.catName
|
|
||||||
elide: Text.ElideRight
|
|
||||||
font.weight: tile.isActive ? Font.Medium : Font.Normal
|
|
||||||
color: tile.isActive
|
|
||||||
? Kirigami.Theme.highlightColor
|
|
||||||
: Kirigami.Theme.textColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: tileArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: root.categorySelected(tile.catId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -130,8 +130,8 @@ MouseArea {
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
width: root.navButtonWidth
|
width: root.navButtonWidth
|
||||||
color: homeMouseArea.containsPress
|
color: homeMouseArea.containsPress
|
||||||
? Qt.rgba(1, 1, 1, 0.2)
|
? Qt.rgba(255, 255, 255, 0.2)
|
||||||
: (homeMouseArea.containsMouse ? Qt.rgba(1, 1, 1, 0.1) : "transparent")
|
: (homeMouseArea.containsMouse ? Qt.rgba(255, 255, 255, 0.1) : "transparent")
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
|
||||||
Kirigami.Icon {
|
Kirigami.Icon {
|
||||||
|
|
@ -160,8 +160,8 @@ MouseArea {
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
width: root.navButtonWidth
|
width: root.navButtonWidth
|
||||||
color: overviewMouseArea.containsPress
|
color: overviewMouseArea.containsPress
|
||||||
? Qt.rgba(1, 1, 1, 0.2)
|
? Qt.rgba(255, 255, 255, 0.2)
|
||||||
: (overviewMouseArea.containsMouse ? Qt.rgba(1, 1, 1, 0.1) : "transparent")
|
: (overviewMouseArea.containsMouse ? Qt.rgba(255, 255, 255, 0.1) : "transparent")
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
|
||||||
Kirigami.Icon {
|
Kirigami.Icon {
|
||||||
|
|
@ -854,8 +854,8 @@ MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
color: taskMouseArea.containsPress
|
color: taskMouseArea.containsPress
|
||||||
? Qt.rgba(1.0, 1.0, 1.0, 0.2)
|
? Qt.rgba(255, 255, 255, 0.2)
|
||||||
: (taskMouseArea.containsMouse ? Qt.rgba(1.0, 1.0, 1.0, 0.1) : "transparent")
|
: (taskMouseArea.containsMouse ? Qt.rgba(255, 255, 255, 0.1) : "transparent")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Task icon
|
// Task icon
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ Folio.DelegateTouchArea {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.1)
|
color: Qt.rgba(255, 255, 255, 0.1)
|
||||||
visible: ShellSettings.Settings.convergenceModeEnabled && root.hovered
|
visible: ShellSettings.Settings.convergenceModeEnabled && root.hovered
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,6 @@ import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin
|
||||||
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||||
|
|
||||||
import org.kde.layershell 1.0 as LayerShell
|
import org.kde.layershell 1.0 as LayerShell
|
||||||
import org.kde.plasma.private.sessions 2.0
|
|
||||||
import org.kde.coreaddons as KCoreAddons
|
|
||||||
import org.kde.kcmutils as KCM
|
|
||||||
import org.kde.kirigamiaddons.components as KirigamiAddonsComponents
|
|
||||||
|
|
||||||
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
|
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
|
||||||
|
|
||||||
|
|
@ -198,19 +194,12 @@ ContainmentItem {
|
||||||
LayerShell.Window.keyboardInteractivity: LayerShell.Window.KeyboardInteractivityOnDemand
|
LayerShell.Window.keyboardInteractivity: LayerShell.Window.KeyboardInteractivityOnDemand
|
||||||
|
|
||||||
// Auto-hide: slide dock content off-screen when a window is
|
// Auto-hide: slide dock content off-screen when a window is
|
||||||
// maximized. The reveal strip at the screen edge brings it back.
|
// maximized. A HoverHandler brings it back on mouse proximity.
|
||||||
property real dockOffset: 0
|
property real dockOffset: 0
|
||||||
readonly property real dockHeight: Kirigami.Units.gridUnit * 3
|
readonly property real dockHeight: Kirigami.Units.gridUnit * 3
|
||||||
|
readonly property bool dockHovered: dockHoverHandler.hovered
|
||||||
// Height of the input-receive strip kept at the screen edge when
|
|
||||||
// the dock is hidden. Matches the navigation panel convention.
|
|
||||||
readonly property real revealStripHeight: Kirigami.Units.gridUnit
|
|
||||||
|
|
||||||
// True once the hover-reveal timer fires; cleared on hover-exit.
|
|
||||||
property bool hoverRevealing: false
|
|
||||||
|
|
||||||
readonly property bool shouldHide: ShellSettings.Settings.autoHidePanelsEnabled
|
readonly property bool shouldHide: ShellSettings.Settings.autoHidePanelsEnabled
|
||||||
&& windowMaximizedTracker.showingWindow && !hoverRevealing
|
&& windowMaximizedTracker.showingWindow && !dockHovered
|
||||||
|
|
||||||
onShouldHideChanged: {
|
onShouldHideChanged: {
|
||||||
if (shouldHide) {
|
if (shouldHide) {
|
||||||
|
|
@ -220,38 +209,8 @@ ContainmentItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Narrow the input region to a strip at the screen edge when hidden
|
|
||||||
// so that app controls near the bottom edge are not accidentally
|
|
||||||
// intercepted. Mirrors the same pattern used by NavigationPanel.
|
|
||||||
onDockOffsetChanged: {
|
|
||||||
if (dockOffset >= dockHeight) {
|
|
||||||
MobileShell.ShellUtil.setInputRegion(dockOverlay,
|
|
||||||
Qt.rect(0, dockOverlay.height - revealStripHeight,
|
|
||||||
dockOverlay.width, revealStripHeight))
|
|
||||||
} else if (dockOffset === 0) {
|
|
||||||
MobileShell.ShellUtil.setInputRegion(dockOverlay, Qt.rect(0, 0, 0, 0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delay reveal by 300 ms so a quick edge graze does not pop the
|
|
||||||
// dock up mid-interaction with the underlying application.
|
|
||||||
Timer {
|
|
||||||
id: hoverRevealTimer
|
|
||||||
interval: 300
|
|
||||||
repeat: false
|
|
||||||
onTriggered: dockOverlay.hoverRevealing = true
|
|
||||||
}
|
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: dockHoverHandler
|
id: dockHoverHandler
|
||||||
onHoveredChanged: {
|
|
||||||
if (hovered) {
|
|
||||||
hoverRevealTimer.start()
|
|
||||||
} else {
|
|
||||||
hoverRevealTimer.stop()
|
|
||||||
dockOverlay.hoverRevealing = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on dockOffset {
|
Behavior on dockOffset {
|
||||||
|
|
@ -263,7 +222,9 @@ ContainmentItem {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Qt.rgba(0, 0, 0, 0.5)
|
Kirigami.Theme.inherit: false
|
||||||
|
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||||
|
color: Kirigami.Theme.backgroundColor
|
||||||
transform: Translate { y: dockOverlay.dockOffset }
|
transform: Translate { y: dockOverlay.dockOffset }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -347,297 +308,7 @@ ContainmentItem {
|
||||||
target: folio.HomeScreenState
|
target: folio.HomeScreenState
|
||||||
|
|
||||||
function onAppDrawerOpened() {
|
function onAppDrawerOpened() {
|
||||||
folio.ApplicationListSearchModel.categoryFilter = ""
|
overlayDrawer.forceActiveFocus();
|
||||||
overlayDrawer.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop shadow rendered separately so categoryPanel itself needs no
|
|
||||||
// layer FBO (which would rasterize and blur the icons inside).
|
|
||||||
Rectangle {
|
|
||||||
id: categoryPanelShadow
|
|
||||||
width: categoryPanel.width
|
|
||||||
height: categoryPanel.height
|
|
||||||
x: categoryPanel.x
|
|
||||||
y: categoryPanel.y
|
|
||||||
radius: categoryPanel.radius
|
|
||||||
color: categoryPanel.color
|
|
||||||
opacity: categoryPanel.opacity
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: DropShadow {
|
|
||||||
transparentBorder: true
|
|
||||||
horizontalOffset: 0
|
|
||||||
verticalOffset: 2
|
|
||||||
radius: 12
|
|
||||||
samples: 25
|
|
||||||
color: Qt.rgba(0, 0, 0, 0.4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CategoryPanel {
|
|
||||||
id: categoryPanel
|
|
||||||
folio: root.folio
|
|
||||||
|
|
||||||
width: Kirigami.Units.gridUnit * 9
|
|
||||||
height: overlayDrawer.popupHeight
|
|
||||||
x: overlayDrawer.x + overlayDrawer.width + Kirigami.Units.smallSpacing
|
|
||||||
y: overlayDrawer.y
|
|
||||||
opacity: overlayDrawer.opacity
|
|
||||||
|
|
||||||
onCategorySelected: (catId) => {
|
|
||||||
folio.ApplicationListSearchModel.categoryFilter = catId
|
|
||||||
overlayDrawerHeader.clearSearchText()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop shadow rendered separately so powerPanel itself needs no layer FBO,
|
|
||||||
// which would rasterize and blur the icons inside.
|
|
||||||
Rectangle {
|
|
||||||
id: powerPanelShadow
|
|
||||||
width: powerPanel.width
|
|
||||||
height: powerPanel.height
|
|
||||||
x: powerPanel.x
|
|
||||||
y: powerPanel.y
|
|
||||||
radius: powerPanel.radius
|
|
||||||
color: powerPanel.color
|
|
||||||
opacity: powerPanel.opacity
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: DropShadow {
|
|
||||||
transparentBorder: true
|
|
||||||
horizontalOffset: 0
|
|
||||||
verticalOffset: 2
|
|
||||||
radius: 12
|
|
||||||
samples: 25
|
|
||||||
color: Qt.rgba(0, 0, 0, 0.4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: powerPanel
|
|
||||||
|
|
||||||
// Width: just enough for one icon button + side margins
|
|
||||||
readonly property real tileSize: Kirigami.Units.iconSizes.medium + 2 * Kirigami.Units.largeSpacing
|
|
||||||
|
|
||||||
width: tileSize
|
|
||||||
height: overlayDrawer.popupHeight
|
|
||||||
x: categoryPanel.x + categoryPanel.width + Kirigami.Units.smallSpacing
|
|
||||||
y: overlayDrawer.y
|
|
||||||
opacity: overlayDrawer.opacity
|
|
||||||
radius: Kirigami.Units.cornerRadius
|
|
||||||
color: Kirigami.Theme.backgroundColor
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
KCoreAddons.KUser {
|
|
||||||
id: kuser
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionManagement {
|
|
||||||
id: powerSession
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close button anchored to top — smaller than power icons
|
|
||||||
Rectangle {
|
|
||||||
id: closeButton
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: Kirigami.Units.smallSpacing
|
|
||||||
height: Kirigami.Units.iconSizes.smallMedium + 2 * Kirigami.Units.smallSpacing
|
|
||||||
radius: Kirigami.Units.cornerRadius
|
|
||||||
color: closeArea.containsPress
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.2)
|
|
||||||
: closeArea.containsMouse
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
|
||||||
: "transparent"
|
|
||||||
Kirigami.Icon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: Kirigami.Units.iconSizes.smallMedium
|
|
||||||
height: width
|
|
||||||
source: "window-close-symbolic"
|
|
||||||
active: closeArea.containsMouse
|
|
||||||
}
|
|
||||||
PlasmaComponents.ToolTip {
|
|
||||||
text: i18n("Close")
|
|
||||||
visible: closeArea.containsMouse
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
id: closeArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: folio.HomeScreenState.closeAppDrawer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Separator below close button
|
|
||||||
Rectangle {
|
|
||||||
anchors.top: closeButton.bottom
|
|
||||||
anchors.topMargin: Kirigami.Units.smallSpacing
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.leftMargin: Kirigami.Units.smallSpacing
|
|
||||||
anchors.rightMargin: Kirigami.Units.smallSpacing
|
|
||||||
height: 1
|
|
||||||
color: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.15)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Power buttons centred vertically in the panel
|
|
||||||
Column {
|
|
||||||
id: powerColumn
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: Kirigami.Units.smallSpacing
|
|
||||||
spacing: Kirigami.Units.smallSpacing
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: width
|
|
||||||
radius: Kirigami.Units.cornerRadius
|
|
||||||
color: lockArea.containsPress
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.2)
|
|
||||||
: lockArea.containsMouse
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
|
||||||
: "transparent"
|
|
||||||
Kirigami.Icon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: Kirigami.Units.iconSizes.medium
|
|
||||||
height: width
|
|
||||||
source: "system-lock-screen"
|
|
||||||
active: lockArea.containsMouse
|
|
||||||
}
|
|
||||||
PlasmaComponents.ToolTip {
|
|
||||||
text: i18n("Lock Screen")
|
|
||||||
visible: lockArea.containsMouse
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
id: lockArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
powerSession.lock()
|
|
||||||
folio.HomeScreenState.closeAppDrawer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: width
|
|
||||||
radius: Kirigami.Units.cornerRadius
|
|
||||||
color: rebootArea.containsPress
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.2)
|
|
||||||
: rebootArea.containsMouse
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
|
||||||
: "transparent"
|
|
||||||
Kirigami.Icon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: Kirigami.Units.iconSizes.medium
|
|
||||||
height: width
|
|
||||||
source: "system-reboot"
|
|
||||||
active: rebootArea.containsMouse
|
|
||||||
}
|
|
||||||
PlasmaComponents.ToolTip {
|
|
||||||
text: i18n("Restart")
|
|
||||||
visible: rebootArea.containsMouse
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
id: rebootArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
folio.HomeScreenState.closeAppDrawer()
|
|
||||||
powerSession.requestReboot()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: width
|
|
||||||
radius: Kirigami.Units.cornerRadius
|
|
||||||
color: shutdownArea.containsPress
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.2)
|
|
||||||
: shutdownArea.containsMouse
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
|
||||||
: "transparent"
|
|
||||||
Kirigami.Icon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: Kirigami.Units.iconSizes.medium
|
|
||||||
height: width
|
|
||||||
source: "system-shutdown"
|
|
||||||
active: shutdownArea.containsMouse
|
|
||||||
}
|
|
||||||
PlasmaComponents.ToolTip {
|
|
||||||
text: i18n("Shut Down")
|
|
||||||
visible: shutdownArea.containsMouse
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
id: shutdownArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
folio.HomeScreenState.closeAppDrawer()
|
|
||||||
powerSession.requestShutdown()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Separator above user avatar
|
|
||||||
Rectangle {
|
|
||||||
anchors.bottom: userSection.top
|
|
||||||
anchors.bottomMargin: Kirigami.Units.smallSpacing
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.leftMargin: Kirigami.Units.smallSpacing
|
|
||||||
anchors.rightMargin: Kirigami.Units.smallSpacing
|
|
||||||
height: 1
|
|
||||||
color: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.15)
|
|
||||||
}
|
|
||||||
|
|
||||||
// User avatar anchored to bottom — tooltip shows name, click opens user settings
|
|
||||||
Rectangle {
|
|
||||||
id: userSection
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: Kirigami.Units.smallSpacing
|
|
||||||
height: width
|
|
||||||
radius: Kirigami.Units.cornerRadius
|
|
||||||
color: userArea.containsPress
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.2)
|
|
||||||
: userArea.containsMouse
|
|
||||||
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
|
||||||
: "transparent"
|
|
||||||
|
|
||||||
KirigamiAddonsComponents.Avatar {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: Kirigami.Units.iconSizes.medium
|
|
||||||
height: width
|
|
||||||
source: kuser.faceIconUrl
|
|
||||||
name: kuser.fullName || kuser.loginName
|
|
||||||
}
|
|
||||||
PlasmaComponents.ToolTip {
|
|
||||||
text: kuser.fullName || kuser.loginName
|
|
||||||
visible: userArea.containsMouse
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
id: userArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
KCM.KCMLauncher.openSystemSettings("kcm_users")
|
|
||||||
folio.HomeScreenState.closeAppDrawer()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,13 +158,13 @@ ContainmentItem {
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
flags: Qt.FramelessWindowHint | Qt.WindowTransparentForInput
|
flags: Qt.FramelessWindowHint | Qt.WindowTransparentForInput
|
||||||
// height is set by layer-shell anchoring; provide a fallback.
|
// height is set by layer-shell anchoring; provide a fallback.
|
||||||
height: root.navigationPanelHeight
|
height: Kirigami.Units.gridUnit * 3
|
||||||
width: 1 // layer-shell stretches it via AnchorLeft|AnchorRight
|
width: 1 // layer-shell stretches it via AnchorLeft|AnchorRight
|
||||||
|
|
||||||
LayerShell.Window.scope: "dock-space"
|
LayerShell.Window.scope: "dock-space"
|
||||||
LayerShell.Window.layer: LayerShell.Window.LayerBottom
|
LayerShell.Window.layer: LayerShell.Window.LayerBottom
|
||||||
LayerShell.Window.anchors: LayerShell.Window.AnchorBottom | LayerShell.Window.AnchorLeft | LayerShell.Window.AnchorRight
|
LayerShell.Window.anchors: LayerShell.Window.AnchorBottom | LayerShell.Window.AnchorLeft | LayerShell.Window.AnchorRight
|
||||||
LayerShell.Window.exclusionZone: root.navigationPanelHeight
|
LayerShell.Window.exclusionZone: Kirigami.Units.gridUnit * 3
|
||||||
LayerShell.Window.keyboardInteractivity: LayerShell.Window.KeyboardInteractivityNone
|
LayerShell.Window.keyboardInteractivity: LayerShell.Window.KeyboardInteractivityNone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,43 +11,6 @@ Loader {
|
||||||
|
|
||||||
property var currentWindow
|
property var currentWindow
|
||||||
|
|
||||||
// Window that needs geometry clamping after un-maximize in convergence
|
|
||||||
// mode. Set in onMaximizedChanged and consumed by the timer below.
|
|
||||||
property var pendingConstrainWindow: null
|
|
||||||
|
|
||||||
// After a window is un-maximized in convergence mode, the dockSpaceReserver
|
|
||||||
// LayerShell surface needs one Wayland roundtrip to (re)commit its exclusive
|
|
||||||
// zone so that KWin updates MaximizeArea. We wait 200 ms — well within the
|
|
||||||
// dock slide-in animation — then clamp the window bottom to MaximizeArea so
|
|
||||||
// it cannot overlap the dock.
|
|
||||||
Timer {
|
|
||||||
id: constrainAfterRestoreTimer
|
|
||||||
interval: 200
|
|
||||||
onTriggered: {
|
|
||||||
const window = root.pendingConstrainWindow
|
|
||||||
root.pendingConstrainWindow = null
|
|
||||||
if (!window || window.deleted || !window.normalWindow) return
|
|
||||||
if (!ShellSettings.Settings.convergenceModeEnabled) return
|
|
||||||
|
|
||||||
const output = window.output
|
|
||||||
const desktop = window.desktops[0]
|
|
||||||
if (!desktop) return
|
|
||||||
|
|
||||||
const maxRect = KWinComponents.Workspace.clientArea(
|
|
||||||
KWinComponents.Workspace.MaximizeArea, output, desktop)
|
|
||||||
const geo = window.frameGeometry
|
|
||||||
const maxBottom = maxRect.y + maxRect.height
|
|
||||||
|
|
||||||
if (geo.y + geo.height > maxBottom) {
|
|
||||||
// Clip the bottom edge to MaximizeArea; preserve top position
|
|
||||||
// and width. Ensure height is at least 100px to avoid
|
|
||||||
// pathological cases where the window starts above maxRect.
|
|
||||||
const newH = Math.max(100, maxBottom - geo.y)
|
|
||||||
window.frameGeometry = Qt.rect(geo.x, geo.y, geo.width, newH)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function run(window) {
|
function run(window) {
|
||||||
// HACK: don't maximize xwaylandvideobridge
|
// HACK: don't maximize xwaylandvideobridge
|
||||||
// see: https://invent.kde.org/plasma/plasma-mobile/-/issues/324
|
// see: https://invent.kde.org/plasma/plasma-mobile/-/issues/324
|
||||||
|
|
@ -104,14 +67,6 @@ Loader {
|
||||||
root.run(currentWindow);
|
root.run(currentWindow);
|
||||||
});
|
});
|
||||||
root.run(currentWindow);
|
root.run(currentWindow);
|
||||||
// Schedule a deferred geometry clamp so that the restored window
|
|
||||||
// doesn't overlap the dock after the dockSpaceReserver exclusive
|
|
||||||
// zone is re-committed over a Wayland roundtrip.
|
|
||||||
if (ShellSettings.Settings.convergenceModeEnabled
|
|
||||||
&& ShellSettings.Settings.autoHidePanelsEnabled) {
|
|
||||||
root.pendingConstrainWindow = currentWindow
|
|
||||||
constrainAfterRestoreTimer.restart()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,6 @@
|
||||||
add_executable(flashlighthelper)
|
add_executable(flashlighthelper)
|
||||||
target_sources(flashlighthelper PRIVATE flashlighthelper.cpp)
|
target_sources(flashlighthelper PRIVATE flashlighthelper.cpp)
|
||||||
|
|
||||||
if(NOT TARGET KF6::AuthCore)
|
|
||||||
find_package(KF6Auth NO_MODULE REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(flashlighthelper
|
target_link_libraries(flashlighthelper
|
||||||
Qt6::Core
|
Qt6::Core
|
||||||
KF6::AuthCore
|
KF6::AuthCore
|
||||||
|
|
@ -17,6 +13,10 @@ target_link_libraries(flashlighthelper
|
||||||
|
|
||||||
install(TARGETS flashlighthelper DESTINATION ${KAUTH_HELPER_INSTALL_DIR})
|
install(TARGETS flashlighthelper DESTINATION ${KAUTH_HELPER_INSTALL_DIR})
|
||||||
|
|
||||||
|
if(NOT TARGET KF6::AuthCore)
|
||||||
|
find_package(KF6Auth NO_MODULE REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
kauth_install_helper_files(flashlighthelper org.kde.plasma.mobileshell.flashlighthelper root)
|
kauth_install_helper_files(flashlighthelper org.kde.plasma.mobileshell.flashlighthelper root)
|
||||||
kauth_install_actions(org.kde.plasma.mobileshell.flashlighthelper flashlighthelper.actions)
|
kauth_install_actions(org.kde.plasma.mobileshell.flashlighthelper flashlighthelper.actions)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
SPDX-FileCopyrightText: 2026 Marco Allegretti
|
SPDX-FileCopyrightText: 2026 Marco Allegretti.
|
||||||
SPDX-License-Identifier: EUPL-1.2
|
SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue