homescreens/halcyon: Port to plasma_add_applet

This commit is contained in:
Devin Lin 2025-07-14 22:05:18 -04:00
parent 78366a62ac
commit 8a0b3d4f56
31 changed files with 103 additions and 133 deletions

View file

@ -1,16 +1,35 @@
# SPDX-FileCopyrightText: 2022-2023 Devin Lin <devin@kde.org> # SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
add_definitions(-DTRANSLATION_DOMAIN=\"plasma_applet_org.kde.plasma.mobile.homescreen.halcyon\") plasma_add_applet(org.kde.plasma.mobile.homescreen.halcyon
QML_SOURCES
set(homescreen_SRCS qml/Clock.qml
homescreen.cpp qml/FavoritesAppDelegate.qml
halcyonsettings.cpp qml/FavoritesGrid.qml
qml/FavoritesView.qml
qml/FolderGrid.qml
qml/GridAppDelegate.qml
qml/GridAppList.qml
qml/HomeScreen.qml
qml/main.qml
qml/SearchWidget.qml
CPP_SOURCES
application.cpp
applicationfolder.cpp
applicationlistmodel.cpp
halcyonsettings.cpp
homescreen.cpp
pinnedmodel.cpp
windowlistener.cpp
) )
add_library(org.kde.plasma.mobile.homescreen.halcyon MODULE ${homescreen_SRCS}) ecm_target_qml_sources(org.kde.plasma.mobile.homescreen.halcyon SOURCES
qml/settings/SettingsScreen.qml
qml/settings/SettingsWindow.qml
PATH settings
)
target_link_libraries(org.kde.plasma.mobile.homescreen.halcyon target_link_libraries(org.kde.plasma.mobile.homescreen.halcyon PRIVATE
Qt::Gui Qt::Gui
Qt::Qml Qt::Qml
Qt::Quick Qt::Quick
@ -19,12 +38,5 @@ target_link_libraries(org.kde.plasma.mobile.homescreen.halcyon
KF6::I18n KF6::I18n
KF6::Service KF6::Service
KF6::KIOGui KF6::KIOGui
KF6::Notifications KF6::JobWidgets
KF6::WindowSystem KF6::WindowSystem)
)
install(TARGETS org.kde.plasma.mobile.homescreen.halcyon DESTINATION ${KDE_INSTALL_PLUGINDIR}/plasma/applets)
plasma_install_package(package org.kde.plasma.mobile.homescreen.halcyon)
add_subdirectory(plugin)

View file

@ -16,12 +16,16 @@
#include <KWayland/Client/registry.h> #include <KWayland/Client/registry.h>
#include <KWayland/Client/surface.h> #include <KWayland/Client/surface.h>
#include <qqmlregistration.h>
/** /**
* @short Object that represents an application. * @short Object that represents an application.
*/ */
class Application : public QObject class Application : public QObject
{ {
Q_OBJECT Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("Managed by ApplicationListModel")
Q_PROPERTY(bool running READ running NOTIFY windowChanged) Q_PROPERTY(bool running READ running NOTIFY windowChanged)
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)

View file

@ -16,6 +16,8 @@
#include <KWayland/Client/registry.h> #include <KWayland/Client/registry.h>
#include <KWayland/Client/surface.h> #include <KWayland/Client/surface.h>
#include <qqmlregistration.h>
/** /**
* @short Object that represents an application folder on the main page. * @short Object that represents an application folder on the main page.
*/ */
@ -24,6 +26,8 @@ class ApplicationFolderModel;
class ApplicationFolder : public QObject class ApplicationFolder : public QObject
{ {
Q_OBJECT Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("Managed by ApplicationListModel")
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QList<Application *> appPreviews READ appPreviews NOTIFY applicationsChanged) Q_PROPERTY(QList<Application *> appPreviews READ appPreviews NOTIFY applicationsChanged)
Q_PROPERTY(ApplicationFolderModel *applications READ applications NOTIFY applicationsReset) Q_PROPERTY(ApplicationFolderModel *applications READ applications NOTIFY applicationsReset)
@ -67,7 +71,9 @@ class ApplicationFolderModel : public QAbstractListModel
Q_OBJECT Q_OBJECT
public: public:
enum Roles { ApplicationRole = Qt::UserRole + 1 }; enum Roles {
ApplicationRole = Qt::UserRole + 1
};
ApplicationFolderModel(ApplicationFolder *folder); ApplicationFolderModel(ApplicationFolder *folder);
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;

View file

@ -8,6 +8,7 @@
#include <QDebug> #include <QDebug>
#include <QModelIndex> #include <QModelIndex>
#include <QProcess> #include <QProcess>
#include <QQmlEngine>
#include <QQuickWindow> #include <QQuickWindow>
#include <KApplicationTrader> #include <KApplicationTrader>
@ -41,6 +42,15 @@ ApplicationListModel *ApplicationListModel::self()
return inst; return inst;
} }
ApplicationListModel *ApplicationListModel::create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
{
Q_UNUSED(qmlEngine);
Q_UNUSED(jsEngine);
auto *model = self();
QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
return model;
}
QHash<int, QByteArray> ApplicationListModel::roleNames() const QHash<int, QByteArray> ApplicationListModel::roleNames() const
{ {
return {{ApplicationRole, QByteArrayLiteral("application")}}; return {{ApplicationRole, QByteArrayLiteral("application")}};

View file

@ -13,19 +13,29 @@
#include <QSet> #include <QSet>
#include <QTimer> #include <QTimer>
#include <qqmlregistration.h>
class QJSEngine;
class QQmlEngine;
/** /**
* @short The base application list, used directly by the full app list page. * @short The base application list, used directly by the full app list page.
*/ */
class ApplicationListModel : public QAbstractListModel class ApplicationListModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public: public:
enum Roles { ApplicationRole = Qt::UserRole + 1 }; enum Roles {
ApplicationRole = Qt::UserRole + 1
};
ApplicationListModel(QObject *parent = nullptr); ApplicationListModel(QObject *parent = nullptr);
~ApplicationListModel() override; ~ApplicationListModel() override;
static ApplicationListModel *self(); static ApplicationListModel *self();
static ApplicationListModel *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

View file

@ -7,9 +7,13 @@
#include <KConfigGroup> #include <KConfigGroup>
#include <qqmlregistration.h>
class HalcyonSettings : public QObject class HalcyonSettings : public QObject
{ {
Q_OBJECT Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Q_PROPERTY(HalcyonSettings::WallpaperBlurEffect wallpaperBlurEffect READ wallpaperBlurEffect WRITE setWallpaperBlurEffect NOTIFY wallpaperBlurEffectChanged) Q_PROPERTY(HalcyonSettings::WallpaperBlurEffect wallpaperBlurEffect READ wallpaperBlurEffect WRITE setWallpaperBlurEffect NOTIFY wallpaperBlurEffectChanged)
Q_PROPERTY(bool doubleTapToLock READ doubleTapToLock WRITE setDoubleTapToLock NOTIFY doubleTapToLockChanged) Q_PROPERTY(bool doubleTapToLock READ doubleTapToLock WRITE setDoubleTapToLock NOTIFY doubleTapToLockChanged)

View file

@ -9,9 +9,12 @@
#include <QDebug> #include <QDebug>
#include <QQuickItem> #include <QQuickItem>
K_PLUGIN_CLASS_WITH_JSON(HomeScreen, "metadata.json")
HomeScreen::HomeScreen(QObject *parent, const KPluginMetaData &data, const QVariantList &args) HomeScreen::HomeScreen(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
: Plasma::Containment{parent, data, args} : Plasma::Containment{parent, data, args}
, m_settings{new HalcyonSettings{this, config()}} , m_settings{new HalcyonSettings{this, config()}}
, m_pinnedModel{new PinnedModel{this}}
{ {
setHasConfigurationInterface(true); setHasConfigurationInterface(true);
} }
@ -23,6 +26,9 @@ HalcyonSettings *HomeScreen::settings() const
return m_settings; return m_settings;
} }
K_PLUGIN_CLASS(HomeScreen) PinnedModel *HomeScreen::pinnedModel() const
{
return m_pinnedModel;
}
#include "homescreen.moc" #include "homescreen.moc"

View file

@ -6,21 +6,25 @@
#include <Plasma/Containment> #include <Plasma/Containment>
#include "halcyonsettings.h" #include "halcyonsettings.h"
#include "pinnedmodel.h"
class HomeScreen : public Plasma::Containment class HomeScreen : public Plasma::Containment
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(HalcyonSettings *settings READ settings CONSTANT) Q_PROPERTY(HalcyonSettings *settings READ settings CONSTANT)
Q_PROPERTY(PinnedModel *pinnedModel READ pinnedModel CONSTANT)
public: public:
HomeScreen(QObject *parent, const KPluginMetaData &data, const QVariantList &args); HomeScreen(QObject *parent, const KPluginMetaData &data, const QVariantList &args);
~HomeScreen() override; ~HomeScreen() override;
HalcyonSettings *settings() const; HalcyonSettings *settings() const;
PinnedModel *pinnedModel() const;
Q_SIGNALS: Q_SIGNALS:
void showingDesktopChanged(bool showingDesktop); void showingDesktopChanged(bool showingDesktop);
private: private:
HalcyonSettings *m_settings{nullptr}; HalcyonSettings *m_settings{nullptr};
PinnedModel *m_pinnedModel{nullptr};
}; };

View file

@ -1,5 +1,4 @@
{ {
"KPackageStructure": "Plasma/Applet",
"KPlugin": { "KPlugin": {
"Authors": [ "Authors": [
{ {
@ -83,7 +82,6 @@
"Description[x-test]": "xxA mobile homescreen focused on simplicity and ease-of-use.xx", "Description[x-test]": "xxA mobile homescreen focused on simplicity and ease-of-use.xx",
"Description[zh_CN]": "简约易用的手机主屏幕方案。", "Description[zh_CN]": "简约易用的手机主屏幕方案。",
"Description[zh_TW]": "著重於簡潔與易用性的手機主畫面。", "Description[zh_TW]": "著重於簡潔與易用性的手機主畫面。",
"Id": "org.kde.plasma.mobile.homescreen.halcyon",
"License": "GPLv2+", "License": "GPLv2+",
"Name": "Halcyon", "Name": "Halcyon",
"Name[ar]": "قيون", "Name[ar]": "قيون",

View file

@ -8,19 +8,15 @@
#include <KLocalizedString> #include <KLocalizedString>
PinnedModel::PinnedModel(QObject *parent) PinnedModel::PinnedModel(Plasma::Applet *parent)
: QAbstractListModel{parent} : QAbstractListModel{parent}
, m_applet{parent}
{ {
load();
} }
PinnedModel::~PinnedModel() = default; PinnedModel::~PinnedModel() = default;
PinnedModel *PinnedModel::self()
{
static PinnedModel *inst = new PinnedModel();
return inst;
}
int PinnedModel::rowCount(const QModelIndex &parent) const int PinnedModel::rowCount(const QModelIndex &parent) const
{ {
Q_UNUSED(parent) Q_UNUSED(parent)
@ -249,10 +245,3 @@ Plasma::Applet *PinnedModel::applet()
{ {
return m_applet; return m_applet;
} }
void PinnedModel::setApplet(Plasma::Applet *applet)
{
m_applet = applet;
Q_EMIT appletChanged();
load();
}

View file

@ -19,20 +19,26 @@
#include <KWayland/Client/registry.h> #include <KWayland/Client/registry.h>
#include <KWayland/Client/surface.h> #include <KWayland/Client/surface.h>
#include <qqmlregistration.h>
/** /**
* @short The applications and folders model on the main page. * @short The applications and folders model on the main page.
*/ */
class PinnedModel : public QAbstractListModel class PinnedModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(Plasma::Applet *applet READ applet WRITE setApplet NOTIFY appletChanged) QML_ELEMENT
QML_UNCREATABLE("")
public: public:
enum Roles { IsFolderRole = Qt::UserRole + 1, ApplicationRole, FolderRole }; enum Roles {
IsFolderRole = Qt::UserRole + 1,
ApplicationRole,
FolderRole
};
PinnedModel(QObject *parent = nullptr); PinnedModel(Plasma::Applet *parent = nullptr);
~PinnedModel() override; ~PinnedModel() override;
static PinnedModel *self();
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@ -54,9 +60,6 @@ public:
public Q_SLOTS: public Q_SLOTS:
void addAppFromFolder(const QString &storageId); void addAppFromFolder(const QString &storageId);
Q_SIGNALS:
void appletChanged();
private: private:
void load(); void load();

View file

@ -1,30 +0,0 @@
# SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
# SPDX-License-Identifier: GPL-2.0-or-later
set(halcyonplugin_SRCS
halcyonplugin.cpp
application.cpp
applicationfolder.cpp
applicationlistmodel.cpp
pinnedmodel.cpp
windowlistener.cpp
)
install(FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/private/mobile/homescreen/halcyon)
add_library(halcyonplugin SHARED ${halcyonplugin_SRCS})
target_link_libraries(halcyonplugin
Qt::Gui
Qt::Qml
Qt::Quick
Plasma::Plasma
KF6::I18n
KF6::Service
KF6::KIOGui
KF6::JobWidgets
Plasma::KWaylandClient
KF6::WindowSystem)
set_property(TARGET halcyonplugin PROPERTY LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/org/kde/private/mobile/homescreen/halcyon)
install(TARGETS halcyonplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/private/mobile/homescreen/halcyon)

View file

@ -1,27 +0,0 @@
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "halcyonplugin.h"
#include "application.h"
#include "applicationfolder.h"
#include "applicationlistmodel.h"
#include "pinnedmodel.h"
#include "windowlistener.h"
void HalcyonPlugin::registerTypes(const char *uri)
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.private.mobile.homescreen.halcyon"));
WindowListener::instance(); // ensure it is created
qmlRegisterSingletonType<ApplicationListModel>(uri, 1, 0, "ApplicationListModel", [](QQmlEngine *, QJSEngine *) -> QObject * {
return ApplicationListModel::self();
});
qmlRegisterSingletonType<PinnedModel>(uri, 1, 0, "PinnedModel", [](QQmlEngine *, QJSEngine *) -> QObject * {
return PinnedModel::self();
});
qmlRegisterType<Application>(uri, 1, 0, "Application");
qmlRegisterType<ApplicationFolder>(uri, 1, 0, "ApplicationFolder");
}

View file

@ -1,16 +0,0 @@
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QQmlEngine>
#include <QQmlExtensionPlugin>
class HalcyonPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
public:
void registerTypes(const char *uri) override;
};

View file

@ -1,5 +0,0 @@
# SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
# SPDX-License-Identifier: GPL-2.0-or-later
module org.kde.private.mobile.homescreen.halcyon
plugin halcyonplugin

View file

@ -8,10 +8,11 @@ import QtQml.Models
import org.kde.plasma.components 3.0 as PC3 import org.kde.plasma.components 3.0 as PC3
import org.kde.draganddrop as DragDrop import org.kde.draganddrop as DragDrop
import org.kde.plasma.plasmoid
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.plasma.private.mobileshell as MobileShell import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.private.mobile.homescreen.halcyon as Halcyon import plasma.applet.org.kde.plasma.mobile.homescreen.halcyon as Halcyon
MobileShell.GridView { MobileShell.GridView {
id: root id: root
@ -74,7 +75,7 @@ MobileShell.GridView {
Keys.onReturnPressed: currentItem.appDelegate.launch() Keys.onReturnPressed: currentItem.appDelegate.launch()
model: DelegateModel { model: DelegateModel {
id: visualModel id: visualModel
model: Halcyon.PinnedModel model: Plasmoid.pinnedModel
delegate: Item { delegate: Item {
id: delegateRoot id: delegateRoot
@ -87,7 +88,7 @@ MobileShell.GridView {
function moveDragToCurrentPos(from, to) { function moveDragToCurrentPos(from, to) {
if (from !== to) { if (from !== to) {
visualModel.items.move(from, to); visualModel.items.move(from, to);
Halcyon.PinnedModel.moveEntry(from, to); Plasmoid.pinnedModel.moveEntry(from, to);
} }
} }
@ -189,9 +190,9 @@ MobileShell.GridView {
onDropped: (drop) => { onDropped: (drop) => {
if (transitionAnim.running || appDelegate.drag.active || drag.source.isFolder) return; // don't do anything when reordering if (transitionAnim.running || appDelegate.drag.active || drag.source.isFolder) return; // don't do anything when reordering
if (appDelegate.isFolder) { if (appDelegate.isFolder) {
Halcyon.PinnedModel.addAppToFolder(drop.source.visualIndex, appDelegate.visualIndex); Plasmoid.pinnedModel.addAppToFolder(drop.source.visualIndex, appDelegate.visualIndex);
} else { } else {
Halcyon.PinnedModel.createFolderFromApps(drop.source.visualIndex, appDelegate.visualIndex); Plasmoid.pinnedModel.createFolderFromApps(drop.source.visualIndex, appDelegate.visualIndex);
} }
folderAnim.to = 0; folderAnim.to = 0;
folderAnim.restart(); folderAnim.restart();
@ -221,7 +222,7 @@ MobileShell.GridView {
Kirigami.Action { Kirigami.Action {
icon.name: "emblem-favorite" icon.name: "emblem-favorite"
text: i18n("Remove from favourites") text: i18n("Remove from favourites")
onTriggered: Halcyon.PinnedModel.removeEntry(model.index) onTriggered: Plasmoid.pinnedModel.removeEntry(model.index)
} }
] ]

View file

@ -12,7 +12,7 @@ import org.kde.draganddrop as DragDrop
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.plasma.private.mobileshell as MobileShell import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.private.mobile.homescreen.halcyon as Halcyon import plasma.applet.org.kde.plasma.mobile.homescreen.halcyon as Halcyon
MobileShell.GridView { MobileShell.GridView {
id: root id: root

View file

@ -12,10 +12,11 @@ import QtQuick.Controls as Controls
import org.kde.plasma.core as PlasmaCore import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.kquickcontrolsaddons import org.kde.kquickcontrolsaddons
import org.kde.plasma.plasmoid
import org.kde.plasma.private.mobileshell as MobileShell import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
import org.kde.private.mobile.homescreen.halcyon as Halcyon import plasma.applet.org.kde.plasma.mobile.homescreen.halcyon as Halcyon
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
@ -63,7 +64,7 @@ MouseArea {
icon.name: "emblem-favorite" icon.name: "emblem-favorite"
text: i18n("Add to favourites") text: i18n("Add to favourites")
onClicked: { onClicked: {
Halcyon.PinnedModel.addApp(application.storageId, 0); Plasmoid.pinnedModel.addApp(application.storageId, 0);
} }
} }
onClosed: dialogLoader.active = false onClosed: dialogLoader.active = false

View file

@ -12,7 +12,7 @@ import org.kde.kirigami 2.10 as Kirigami
import org.kde.plasma.private.mobileshell as MobileShell import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.state as MobileShellState import org.kde.plasma.private.mobileshell.state as MobileShellState
import org.kde.private.mobile.homescreen.halcyon 1.0 as Halcyon import plasma.applet.org.kde.plasma.mobile.homescreen.halcyon as Halcyon
import org.kde.plasma.plasmoid import org.kde.plasma.plasmoid
MobileShell.GridView { MobileShell.GridView {

View file

@ -11,15 +11,15 @@ import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.private.mobileshell as MobileShell import org.kde.plasma.private.mobileshell as MobileShell
import org.kde.plasma.private.mobileshell.state as MobileShellState import org.kde.plasma.private.mobileshell.state as MobileShellState
import org.kde.private.mobile.homescreen.halcyon as Halcyon
import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin
import plasma.applet.org.kde.plasma.mobile.homescreen.halcyon as Halcyon
ContainmentItem { ContainmentItem {
id: root id: root
Component.onCompleted: { Component.onCompleted: {
Halcyon.ApplicationListModel.loadApplications(); Halcyon.ApplicationListModel.loadApplications();
Halcyon.PinnedModel.applet = root.plasmoid;
forceActiveFocus(); forceActiveFocus();
} }