From 360c9e2d623be8c8a7479d3e170114fe75dcc4c7 Mon Sep 17 00:00:00 2001 From: Alexander Kavon Date: Thu, 6 Nov 2025 01:30:43 -0500 Subject: kue a kanban todo app for kde plasma --- src/CMakeLists.txt | 57 ++++++++++++ src/kueapplication.cpp | 73 +++++++++++++++ src/kueapplication.h | 37 ++++++++ src/kueconfig.kcfg | 16 ++++ src/kueconfig.kcfgc | 10 ++ src/main.cpp | 74 +++++++++++++++ src/qml/Main.qml | 168 ++++++++++++++++++++++++++++++++++ src/settings/CMakeLists.txt | 11 +++ src/settings/GeneralPage.qml | 27 ++++++ src/settings/KueConfigurationView.qml | 20 ++++ 10 files changed, 493 insertions(+) create mode 100644 src/CMakeLists.txt create mode 100644 src/kueapplication.cpp create mode 100644 src/kueapplication.h create mode 100644 src/kueconfig.kcfg create mode 100644 src/kueconfig.kcfgc create mode 100644 src/main.cpp create mode 100644 src/qml/Main.qml create mode 100644 src/settings/CMakeLists.txt create mode 100644 src/settings/GeneralPage.qml create mode 100644 src/settings/KueConfigurationView.qml (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..e28db18 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: BSD-2-Clause +# SPDX-FileCopyrightText: 2024 Alexander Kavon + +add_subdirectory(settings) + +qt_add_library(kue_static STATIC) +ecm_add_qml_module(kue_static + URI org.kde.kue + GENERATE_PLUGIN_SOURCE + VERSION 1.0 + QML_FILES + qml/Main.qml + DEPENDENCIES + org.kde.kirigamiaddons.statefulapp + org.kde.kirigami + org.kde.kue.settings +) + +target_sources(kue_static PUBLIC + kueapplication.cpp + kueapplication.h +) + +target_link_libraries(kue_static PUBLIC + Qt6::Core + Qt6::Gui + Qt6::Qml + Qt6::Quick + Qt6::QuickControls2 + Qt6::Svg + KF6::I18n + KF6::CoreAddons + KF6::ConfigCore + KF6::ConfigGui + KirigamiAddonsStatefulApp + KPim6::AkonadiCore + KPim6::AkonadiAgentBase + KPim6::AkonadiWidgets + KPim6::AkonadiXml + KPim6::AkonadiMime + KPim6::Mime + KPim6::MailCommon +) +target_include_directories(kue_static PUBLIC ${CMAKE_BINARY_DIR} /home/hawk/kde/usr/include) + +target_link_libraries(kue_static PUBLIC Qt::Widgets) + +kconfig_add_kcfg_files(kue_static GENERATE_MOC kueconfig.kcfgc) + +add_executable(kue main.cpp) +target_link_libraries(kue PUBLIC + kue_static + kue_staticplugin + kue_settings + kue_settingsplugin +) +install(TARGETS kue ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/kueapplication.cpp b/src/kueapplication.cpp new file mode 100644 index 0000000..b459acf --- /dev/null +++ b/src/kueapplication.cpp @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2024 Alexander Kavon + +#include "kueapplication.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Qt::StringLiterals; + +KueApplication::KueApplication(QObject *parent) + : AbstractKirigamiApplication(parent) + , m_loading(true) +{ + m_session = new Akonadi::Session(QByteArrayLiteral("Kue Kernel"), this); + + auto monitor = new Akonadi::Monitor(this); + + auto treeModel = new Akonadi::EntityTreeModel(monitor, this); + treeModel->setItemPopulationStrategy(Akonadi::EntityTreeModel::LazyPopulation); + + auto entityTreeModel = new Akonadi::CollectionFilterProxyModel(this); + entityTreeModel->setSourceModel(treeModel); + entityTreeModel->addMimeTypeFilter(KMime::Message::mimeType()); + + m_descendantsProxyModel = new KDescendantsProxyModel(this); + m_descendantsProxyModel->setSourceModel(entityTreeModel); + qDebug() << m_descendantsProxyModel->rowCount(); + + if (Akonadi::ServerManager::isRunning()) { + m_loading = false; + } else { + connect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, [this](Akonadi::ServerManager::State state) { + if (state == Akonadi::ServerManager::Broken) { + qDebug() << "ServerManager Broken"; + qApp->exit(-1); + return; + } + bool loading = state != Akonadi::ServerManager::State::Running; + if (loading == m_loading) { + return; + } + m_loading = loading; + Q_EMIT loadingChanged(); + disconnect(Akonadi::ServerManager::self(), &Akonadi::ServerManager::stateChanged, this, nullptr); + }); + } +} + +bool KueApplication::loading() const +{ + return m_loading; +} + +KDescendantsProxyModel *KueApplication::descendantsProxyModel() const +{ + return m_descendantsProxyModel; +} + +Akonadi::Session *KueApplication::session() const +{ + return m_session; +} + +#include "moc_kueapplication.cpp" diff --git a/src/kueapplication.h b/src/kueapplication.h new file mode 100644 index 0000000..888c592 --- /dev/null +++ b/src/kueapplication.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2024 Alexander Kavon +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +using namespace Qt::StringLiterals; + +namespace Akonadi { + class Session; +} + +class KueApplication : public AbstractKirigamiApplication +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged) + Q_PROPERTY(KDescendantsProxyModel *descendantsProxyModel READ descendantsProxyModel CONSTANT) + + bool loading() const; + Akonadi::Session *session() const; + +public: + explicit KueApplication(QObject *parent = nullptr); + KDescendantsProxyModel *descendantsProxyModel() const; + +Q_SIGNALS: + void loadingChanged(); + +private: + bool m_loading; + Akonadi::Session *m_session; + KDescendantsProxyModel *m_descendantsProxyModel; +}; diff --git a/src/kueconfig.kcfg b/src/kueconfig.kcfg new file mode 100644 index 0000000..1ad1fd2 --- /dev/null +++ b/src/kueconfig.kcfg @@ -0,0 +1,16 @@ + + + + + + + true + + + diff --git a/src/kueconfig.kcfgc b/src/kueconfig.kcfgc new file mode 100644 index 0000000..0a02a78 --- /dev/null +++ b/src/kueconfig.kcfgc @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2024 Alexander Kavon +# SPDX-License-Identifier: LGPL-2.0-or-later + +File=kueconfig.kcfg +ClassName=KueConfig +Mutators=true +DefaultValueGetters=true +GenerateProperties=true +ParentInConstructor=true +Singleton=true diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..41f7fef --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,74 @@ +/* + SPDX-License-Identifier: GPL-2.0-or-later + SPDX-FileCopyrightText: 2024 Alexander Kavon +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "version-kue.h" +#include +#include +#include + +#include "kueconfig.h" + +using namespace Qt::Literals::StringLiterals; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + // Default to org.kde.desktop style unless the user forces another style + if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) { + QQuickStyle::setStyle(u"org.kde.desktop"_s); + } + + KLocalizedString::setApplicationDomain("kue"); + QCoreApplication::setOrganizationName(u"KDE"_s); + + KAboutData aboutData( + // The program name used internally. + u"kue"_s, + // A displayable program name string. + i18nc("@title", "Kue"), + // The program version string. + QStringLiteral(KUE_VERSION_STRING), + // Short description of what the app does. + i18n("Application Description"), + // The license this code is released under. + KAboutLicense::GPL, + // Copyright Statement. + i18n("(c) 2024")); + aboutData.addAuthor(i18nc("@info:credit", "Alexander Kavon"), + i18nc("@info:credit", "Maintainer"), + u"me+kue@alexkavon.com"_s, + u"https://yourwebsite.com"_s); + aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails")); + KAboutData::setApplicationData(aboutData); + QGuiApplication::setWindowIcon(QIcon::fromTheme(u"org.kde.kue"_s)); + + QQmlApplicationEngine engine; + + auto config = KueConfig::self(); + + qRegisterMetaType("KDescendantsProxyModel*"); + qmlRegisterSingletonInstance("org.kde.kue.private", 1, 0, "Kue", config); + + engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); + engine.loadFromModule("org.kde.kue", u"Main"_s); + + if (engine.rootObjects().isEmpty()) { + return -1; + } + + return app.exec(); +} diff --git a/src/qml/Main.qml b/src/qml/Main.qml new file mode 100644 index 0000000..cef37e9 --- /dev/null +++ b/src/qml/Main.qml @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2024 Alexander Kavon + +import QtQuick +import QtQuick.Controls as QQC2 +import QtQuick.Layouts + +import org.kde.kirigami as Kirigami +import org.kde.kirigamiaddons.statefulapp as StatefulApp + +import org.kde.kue +import org.kde.kue.settings as Settings + +StatefulApp.StatefulWindow { + id: root + + property int counter: 0 + + title: i18nc("@title:window", "Kue") + + windowName: "Kue" + + minimumWidth: Kirigami.Units.gridUnit * 20 + minimumHeight: Kirigami.Units.gridUnit * 20 + + application: KueApplication { + configurationView: Settings.KueConfigurationView {} + } + + // Connections { + // target: root.application + + // function onIncrementCounter(): void { + // root.counter += 1; + // } + // } + + // globalDrawer: Kirigami.GlobalDrawer { + // // isMenu: !Kirigami.Settings.isMobile + // actions: [ + // Kirigami.Action { + // id: incrementCounterAction + // fromQAction: root.application.action("increment_counter") + // }, + // Kirigami.Action { + // separator: true + // }, + // Kirigami.Action { + // fromQAction: root.application.action("options_configure") + // }, + // Kirigami.Action { + // fromQAction: root.application.action("options_configure_keybinding") + // }, + // Kirigami.Action { + // separator: true + // }, + // Kirigami.Action { + // id: aboutAction + // fromQAction: root.application.action("open_about_page") + // }, + // Kirigami.Action { + // fromQAction: root.application.action("open_about_kde_page") + // }, + // Kirigami.Action { + // fromQAction: root.application.action("file_quit") + // } + // ] + // } + + pageStack.initialPage: KueApplication.loading ? loadingPage : mainPage + + Component { + id: loadingPage + Kirigami.Page { + Kirigami.PlaceholderMessage { + anchors.centerIn: parent + text: i18n("Loading, please wait...") + } + } + } + + Kirigami.ScrollablePage { + id: mainPage + + title: i18nc("@title", "Kue") + + supportsRefreshing: true + + ListView { + model: KueApplication.descendantsProxyModel + highlight: Rectangle { + width: parent.width + color: "blue" + } + delegate: Kirigami.SwipeListItem { + id: task + + required property bool completed + + contentItem: RowLayout { + QQC2.CheckBox { + id: control + checked: task.completed + onCheckedChanged: { + if (control.checked != task.completed) { + task.completed = control.checked; + } + } + indicator: Rectangle { + implicitWidth: 30 + implicitHeight: 30 + x: control.leftPadding + y: parent.height / 2 - height / 2 + border.color: control.down ? "darkblue" : "gray" + border.width: 2 + Rectangle { + visible: control.checked + color: "red" + radius: 1 + width: 15 + height: 15 + anchors.margins: 5 + anchors.fill: parent + } + } + } + + QQC2.TextField { + id: taskEdit + Layout.fillWidth: true + height: Math.max(implicitHeight, Kirigami.Units.iconSizes.smallMedium) + placeholderText: "Task Info..." + text: model.display + " : " + task.completed + wrapMode: TextInput.Wrap + background: Rectangle { + id: taskEditBg + border.color: "transparent" + color: "transparent" + Rectangle { + visible: taskEdit.activeFocus + anchors.bottom: taskEditBg.bottom + height: 2 + width: parent.width + color: "green" + } + } + } + + Kirigami.Icon { + source: "view-list-details" + visible: taskEdit.activeFocus + implicitHeight: 20 + implicitWidth: 20 + } + } + } + section { + property: "section" + delegate: Kirigami.ListSectionHeader { + required property string section + + text: qsTr("Section %1").arg(parseInt(section) + 1) + width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin + } + } + } + } +} diff --git a/src/settings/CMakeLists.txt b/src/settings/CMakeLists.txt new file mode 100644 index 0000000..d4847a6 --- /dev/null +++ b/src/settings/CMakeLists.txt @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2024 Alexander Kavon +# SPDX-License-Identifier: BSD-2-Clause + +qt_add_library(kue_settings STATIC) +ecm_add_qml_module(kue_settings + GENERATE_PLUGIN_SOURCE + URI org.kde.kue.settings + QML_FILES + KueConfigurationView.qml + GeneralPage.qml +) diff --git a/src/settings/GeneralPage.qml b/src/settings/GeneralPage.qml new file mode 100644 index 0000000..3324c95 --- /dev/null +++ b/src/settings/GeneralPage.qml @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2024 Alexander Kavon +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma ComponentBehavior: Bound + +import QtQuick +import org.kde.kirigamiaddons.formcard as FormCard + +FormCard.FormCardPage { + id: root + + title: i18nc("@title", "General") + + FormCard.FormHeader { + title: i18nc("@title:group", "My Group") + } + + FormCard.FormHeader { + title: i18nc("@title:group", "My Group:") + } + + FormCard.FormCard { + FormCard.FormTextFieldDelegate { + label: i18nc("@label:textbox", "My Label:") + } + } +} diff --git a/src/settings/KueConfigurationView.qml b/src/settings/KueConfigurationView.qml new file mode 100644 index 0000000..c93ec52 --- /dev/null +++ b/src/settings/KueConfigurationView.qml @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2024 Alexander Kavon +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma ComponentBehavior: Bound + +import QtQuick +import org.kde.kirigamiaddons.settings as KirigamiSettings + +KirigamiSettings.ConfigurationView { + id: root + + modules: [ + KirigamiSettings.ConfigurationModule { + moduleId: "general" + text: i18nc("@action:button", "General") + icon.name: "preferences-system-symbolic" + page: () => Qt.createComponent("org.kde.kue.settings", "GeneralPage") + } + ] +} -- cgit v1.2.3