[graphics/drawy] src: Add custom element widget
Laurent Montel <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 9577867c0da40e0db718398ae2fa52fa1dd1c951 by Laurent Montel.
Committed on 21/07/2026 at 05:49.
Pushed by mlaurent into branch 'master'.
Add custom element widget
M +2 -0 src/gui/CMakeLists.txt
M +14 -6 src/gui/customelements/customelementsmanager.cpp
M +4 -2 src/gui/customelements/customelementsmanager.hpp
A +62 -0 src/gui/customelements/customelementsmodel.cpp [License: GPL(v3.0+)]
A +37 -0 src/gui/customelements/customelementsmodel.hpp [License: GPL(v3.0+)]
M +2 -0 src/widgets/CMakeLists.txt
M +1 -0 src/widgets/autotests/CMakeLists.txt
A +24 -0 src/widgets/autotests/customelementswidgettest.cpp [License: GPL(v3.0+)]
A +19 -0 src/widgets/autotests/customelementswidgettest.hpp [License: GPL(v3.0+)]
M +9 -0 src/widgets/context/uicontext.cpp
M +3 -0 src/widgets/context/uicontext.hpp
A +27 -0 src/widgets/customelementswidget/customelementswidget.cpp [License: GPL(v3.0+)]
A +20 -0 src/widgets/customelementswidget/customelementswidget.hpp [License: GPL(v3.0+)]
M +13 -0 src/widgets/keybindings/actionmanager.cpp
M +2 -0 src/widgets/keybindings/actionmanager.hpp
https://invent.kde.org/graphics/drawy/-/commit/9577867c0da40e0db718398ae2fa52fa1dd1c951
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 57cbfd82..6cd11cf3 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -146,6 +146,8 @@ target_sources(
customelements/customelement.cpp
customelements/customelementsutils.hpp
customelements/customelementsutils.cpp
+ customelements/customelementsmodel.hpp
+ customelements/customelementsmodel.cpp
)
ecm_qt_declare_logging_category(libdrawygui HEADER drawy_gui_debug.h IDENTIFIER DRAWY_GUI_LOG
diff --git a/src/gui/customelements/customelementsmanager.cpp b/src/gui/customelements/customelementsmanager.cpp
index be6dde1f..0b05af7e 100644
--- a/src/gui/customelements/customelementsmanager.cpp
+++ b/src/gui/customelements/customelementsmanager.cpp
@@ -5,6 +5,7 @@
*/
#include "customelementsmanager.hpp"
+#include "customelements/customelementsmodel.hpp"
#include "customelements/customelementsutils.hpp"
#include "drawy_gui_debug.h"
#include "item/item.hpp"
@@ -15,9 +16,15 @@
using namespace Qt::Literals::StringLiterals;
CustomElementsManager::CustomElementsManager(QObject *parent)
: QObject{parent}
+ , mCustomElementsModel(new CustomElementsModel(this))
{
}
+CustomElementsModel *CustomElementsManager::customElementsModel() const
+{
+ return mCustomElementsModel;
+}
+
CustomElementsManager::~CustomElementsManager() = default;
CustomElementsManager *CustomElementsManager::self()
@@ -42,15 +49,16 @@ void CustomElementsManager::loadCustomElements()
qCWarning(DRAWY_GUI_LOG) << "Invalid version" << version;
return;
}
- mCustomElements.clear();
+ QList<CustomElement> customElements;
const QJsonArray array = obj["librairies"_L1].toArray();
for (const auto &r : array) {
CustomElement e;
e.load(r.toObject());
if (e.isValid()) {
- mCustomElements.append(e);
+ customElements.append(e);
}
}
+ mCustomElementsModel->setCustomElements(customElements);
}
}
@@ -59,7 +67,7 @@ void CustomElementsManager::saveCustomElements(const QString &fileName)
QJsonObject obj;
obj["version"_L1] = QString::number(1);
QJsonArray elementsObj;
- for (const auto &element : std::as_const(mCustomElements)) {
+ for (const auto &element : mCustomElementsModel->customElements()) {
elementsObj.append(element.save());
}
obj["librairies"_L1] = elementsObj;
@@ -78,7 +86,7 @@ void CustomElementsManager::saveCustomElements(const QString &fileName)
void CustomElementsManager::exportToFile(const QString &fileName)
{
- if (mCustomElements.isEmpty()) {
+ if (mCustomElementsModel->customElements().isEmpty()) {
qCWarning(DRAWY_GUI_LOG) << "Custom Elements list is empty";
return;
}
@@ -89,13 +97,13 @@ void CustomElementsManager::addItem(const std::shared_ptr<Item> &item)
{
CustomElement element;
element.setItem(item);
- mCustomElements.append(element);
+ mCustomElementsModel->addCustomElement(element);
saveCustomElements();
}
bool CustomElementsManager::isEmpty() const
{
- return mCustomElements.isEmpty();
+ return mCustomElementsModel->isEmpty();
}
#include "moc_customelementsmanager.cpp"
diff --git a/src/gui/customelements/customelementsmanager.hpp b/src/gui/customelements/customelementsmanager.hpp
index ab0315d7..758900a2 100644
--- a/src/gui/customelements/customelementsmanager.hpp
+++ b/src/gui/customelements/customelementsmanager.hpp
@@ -5,13 +5,13 @@
*/
#pragma once
-#include "customelements/customelement.hpp"
#include "libdrawygui_export.h"
#include <QList>
#include <QObject>
#include <memory>
class Item;
+class CustomElementsModel;
class LIBDRAWYGUI_EXPORT CustomElementsManager : public QObject
{
Q_OBJECT
@@ -27,7 +27,9 @@ public:
void exportToFile(const QString &fileName);
void addItem(const std::shared_ptr<Item> &item);
+ [[nodiscard]] CustomElementsModel *customElementsModel() const;
+
private:
explicit CustomElementsManager(QObject *parent = nullptr);
- QList<CustomElement> mCustomElements;
+ CustomElementsModel *const mCustomElementsModel;
};
diff --git a/src/gui/customelements/customelementsmodel.cpp b/src/gui/customelements/customelementsmodel.cpp
new file mode 100644
index 00000000..6373da88
--- /dev/null
+++ b/src/gui/customelements/customelementsmodel.cpp
@@ -0,0 +1,62 @@
+/*
+ * SPDX-FileCopyrightText: 2026 Laurent Montel <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "customelementsmodel.hpp"
+
+CustomElementsModel::CustomElementsModel(QObject *parent)
+ : QAbstractListModel{parent}
+{
+}
+
+CustomElementsModel::~CustomElementsModel() = default;
+
+int CustomElementsModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid()) { // flat model
+ return 0;
+ }
+ return mCustomElements.count();
+}
+
+QVariant CustomElementsModel::data([[maybe_unused]] const QModelIndex &index, [[maybe_unused]] int role) const
+{
+ if (index.row() < 0 || index.row() >= mCustomElements.count()) {
+ return {};
+ }
+ const auto customElement = mCustomElements.at(index.row());
+ switch (role) {
+ case TimeStamp:
+ return customElement.timeStamp();
+ }
+ return {};
+}
+
+QList<CustomElement> CustomElementsModel::customElements() const
+{
+ return mCustomElements;
+}
+
+void CustomElementsModel::addCustomElement(const CustomElement &element)
+{
+ const int numberOfElement = mCustomElements.count();
+ beginInsertRows(QModelIndex(), numberOfElement, numberOfElement);
+ mCustomElements.append(element);
+ endInsertRows();
+}
+
+void CustomElementsModel::setCustomElements(const QList<CustomElement> &newCustomElements)
+{
+ beginResetModel();
+ mCustomElements = newCustomElements;
+ endResetModel();
+}
+
+bool CustomElementsModel::isEmpty() const
+{
+ return mCustomElements.isEmpty();
+}
+
+#include "moc_customelementsmodel.cpp"
diff --git a/src/gui/customelements/customelementsmodel.hpp b/src/gui/customelements/customelementsmodel.hpp
new file mode 100644
index 00000000..a5dc041d
--- /dev/null
+++ b/src/gui/customelements/customelementsmodel.hpp
@@ -0,0 +1,37 @@
+/*
+ * SPDX-FileCopyrightText: 2026 Laurent Montel <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+#include "customelement.hpp"
+#include "libdrawygui_export.h"
+#include <QAbstractListModel>
+#include <QList>
+
+class LIBDRAWYGUI_EXPORT CustomElementsModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ enum CustomElementRoles {
+ TimeStamp = Qt::UserRole + 1,
+ };
+ Q_ENUM(CustomElementRoles)
+
+ explicit CustomElementsModel(QObject *parent = nullptr);
+ ~CustomElementsModel() override;
+
+ [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ [[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
+
+ [[nodiscard]] QList<CustomElement> customElements() const;
+ void setCustomElements(const QList<CustomElement> &newCustomElements);
+
+ [[nodiscard]] bool isEmpty() const;
+
+ void addCustomElement(const CustomElement &element);
+
+private:
+ QList<CustomElement> mCustomElements;
+};
diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt
index 9734040f..1626bbcf 100644
--- a/src/widgets/CMakeLists.txt
+++ b/src/widgets/CMakeLists.txt
@@ -252,6 +252,8 @@ target_sources(
libdrawywidgets_private_export.h
common/renderitems.cpp
common/renderitems.hpp
+ customelementswidget/customelementswidget.hpp
+ customelementswidget/customelementswidget.cpp
)
if(USE_DBUS)
diff --git a/src/widgets/autotests/CMakeLists.txt b/src/widgets/autotests/CMakeLists.txt
index cda94434..0fe04a0b 100644
--- a/src/widgets/autotests/CMakeLists.txt
+++ b/src/widgets/autotests/CMakeLists.txt
@@ -77,3 +77,4 @@ set_tests_properties(
)
add_drawy_unittest(cornerrectangletypewidgettest.cpp)
+add_drawy_unittest(customelementswidgettest.cpp)
diff --git a/src/widgets/autotests/customelementswidgettest.cpp b/src/widgets/autotests/customelementswidgettest.cpp
new file mode 100644
index 00000000..eb8f730e
--- /dev/null
+++ b/src/widgets/autotests/customelementswidgettest.cpp
@@ -0,0 +1,24 @@
+/*
+ * SPDX-FileCopyrightText: 2026 Laurent Montel <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+#include "customelementswidgettest.hpp"
+#include "customelementswidget/customelementswidget.hpp"
+#include <QTest>
+#include <QVBoxLayout>
+QTEST_MAIN(CustomElementsWidgetTest)
+using namespace Qt::Literals::StringLiterals;
+CustomElementsWidgetTest::CustomElementsWidgetTest(QObject *parent)
+ : QObject{parent}
+{
+}
+
+void CustomElementsWidgetTest::shouldHaveDefaultValues()
+{
+ const CustomElementsWidget w(nullptr);
+
+ auto mainLayout = w.findChild<QVBoxLayout *>(u"mainLayout"_s);
+ QVERIFY(mainLayout);
+}
+#include "moc_customelementswidgettest.cpp"
diff --git a/src/widgets/autotests/customelementswidgettest.hpp b/src/widgets/autotests/customelementswidgettest.hpp
new file mode 100644
index 00000000..3d096fab
--- /dev/null
+++ b/src/widgets/autotests/customelementswidgettest.hpp
@@ -0,0 +1,19 @@
+/*
+ * SPDX-FileCopyrightText: 2026 Laurent Montel <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+#pragma once
+
+#include <QObject>
+
+class CustomElementsWidgetTest : public QObject
+{
+ Q_OBJECT
+public:
+ explicit CustomElementsWidgetTest(QObject *parent = nullptr);
+ ~CustomElementsWidgetTest() override = default;
+
+private Q_SLOTS:
+ void shouldHaveDefaultValues();
+};
diff --git a/src/widgets/context/uicontext.cpp b/src/widgets/context/uicontext.cpp
index b6599cc5..75f88ff4 100644
--- a/src/widgets/context/uicontext.cpp
+++ b/src/widgets/context/uicontext.cpp
@@ -14,6 +14,7 @@
#include "components/toolbar.hpp"
#include "components/topleftwidgets.hpp"
#include "components/topwidgets.hpp"
+#include "customelements/customelementsmanager.hpp"
#include "data-structures/quadtree.hpp"
#include "drawy_debug.h"
#include "drawyglobalconfig.h"
@@ -47,6 +48,7 @@ UIContext::UIContext(ApplicationContext *context)
, m_keybindManager(new KeybindManager(this))
, m_event(new Event())
, mPluginFormManager(PluginFormManager::self())
+ , mCustomElementsManager(CustomElementsManager::self())
, m_applicationContext{context}
{
}
@@ -126,6 +128,11 @@ void UIContext::slotThemeChanged()
Q_EMIT themeChanged(isDark);
}
+CustomElementsManager *UIContext::customElementsManager() const
+{
+ return mCustomElementsManager;
+}
+
ToolBar *UIContext::toolBar() const
{
return m_toolBar;
@@ -282,6 +289,8 @@ void UIContext::showContextMenu() const
} else {
menu->addAction(actionManager()->action(ActionManager::Action::LockItems));
}
+ menu->addSeparator();
+ menu->addAction(actionManager()->action(ActionManager::Action::AddCustomElement));
}
menu->addSeparator();
diff --git a/src/widgets/context/uicontext.hpp b/src/widgets/context/uicontext.hpp
index 15abf683..07481fde 100644
--- a/src/widgets/context/uicontext.hpp
+++ b/src/widgets/context/uicontext.hpp
@@ -17,6 +17,7 @@ class TopWidgets;
class TopLeftWidgets;
class BottomLeftWidgets;
class PluginFormManager;
+class CustomElementsManager;
class UIContext : public QObject
{
@@ -36,6 +37,7 @@ public:
[[nodiscard]] KeybindManager *keybindManager() const;
[[nodiscard]] ActionManager *actionManager() const;
[[nodiscard]] PropertyManager *propertyManager() const;
+ [[nodiscard]] CustomElementsManager *customElementsManager() const;
void showContextMenu() const;
void reset();
@@ -60,6 +62,7 @@ private:
Event *const m_event;
Tool *m_lastTool = nullptr; // Used to call the cleanup function of the last tool
PluginFormManager *const mPluginFormManager;
+ CustomElementsManager *const mCustomElementsManager;
ApplicationContext *const m_applicationContext;
};
diff --git a/src/widgets/customelementswidget/customelementswidget.cpp b/src/widgets/customelementswidget/customelementswidget.cpp
new file mode 100644
index 00000000..db0c7a15
--- /dev/null
+++ b/src/widgets/customelementswidget/customelementswidget.cpp
@@ -0,0 +1,27 @@
+/*
+ * SPDX-FileCopyrightText: 2026 Laurent Montel <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+#include "customelementswidget.hpp"
+#include "customelements/customelementsmanager.hpp"
+#include "customelements/customelementsmodel.hpp"
+#include <QListView>
+#include <QVBoxLayout>
+using namespace Qt::Literals::StringLiterals;
+CustomElementsWidget::CustomElementsWidget(CustomElementsManager *manager, QWidget *parent)
+ : QWidget{parent}
+ , mListView(new QListView(this))
+{
+ auto mainLayout = new QVBoxLayout(this);
+ mainLayout->setObjectName(u"mainLayout"_s);
+
+ mListView->setObjectName(u"mListView"_s);
+ if (manager) {
+ mListView->setModel(manager->customElementsModel());
+ }
+ mainLayout->addWidget(mListView);
+}
+
+CustomElementsWidget::~CustomElementsWidget() = default;
+#include "moc_customelementswidget.cpp"
diff --git a/src/widgets/customelementswidget/customelementswidget.hpp b/src/widgets/customelementswidget/customelementswidget.hpp
new file mode 100644
index 00000000..8c181a90
--- /dev/null
+++ b/src/widgets/customelementswidget/customelementswidget.hpp
@@ -0,0 +1,20 @@
+/*
+ * SPDX-FileCopyrightText: 2026 Laurent Montel <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+#pragma once
+#include "libdrawywidgets_private_export.h"
+#include <QWidget>
+class QListView;
+class CustomElementsManager;
+class LIBDRAWYWIDGETS_TESTS_EXPORT CustomElementsWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit CustomElementsWidget(CustomElementsManager *manager, QWidget *parent = nullptr);
+ ~CustomElementsWidget() override;
+
+private:
+ QListView *const mListView;
+};
diff --git a/src/widgets/keybindings/actionmanager.cpp b/src/widgets/keybindings/actionmanager.cpp
index ffd41629..0c69696b 100644
--- a/src/widgets/keybindings/actionmanager.cpp
+++ b/src/widgets/keybindings/actionmanager.cpp
@@ -153,6 +153,8 @@ ActionManager::ActionManager(KActionCollection *actionCollection, ApplicationCon
createAction(Action::UnLockItem, i18nc("@action", "Unlock"), {}, this, &ActionManager::slotUnLock)->setIcon(QIcon::fromTheme(u"unlock"_s));
+ createAction(Action::AddCustomElement, i18nc("@action", "Add to Librairy"), {}, this, &ActionManager::slotAddCustomElement);
+
createAction(Action::Debug, i18nc("@action", "Debug"), {}, this, &ActionManager::slotDebug);
#if HAVE_WHATSNEWSNGSUPPORT
createAction(Action::WhatsNew, i18nc("@action", "What's new"), {}, this, &ActionManager::slotShowWhatsNew);
@@ -249,6 +251,8 @@ QString ActionManager::actionName(Action type) const
return u"lock_items"_s;
case Action::UnLockItem:
return u"unlock_item"_s;
+ case Action::AddCustomElement:
+ return u"add_custom_element"_s;
}
Q_UNREACHABLE();
return u""_s;
@@ -808,4 +812,13 @@ void ActionManager::slotUpdateZorderAndGroupButtons()
action(Action::UngroupItems)->setEnabled(atLeastOneGroup);
}
+void ActionManager::slotAddCustomElement()
+{
+ const QList<std::shared_ptr<Item>> items = dynamic_cast<SelectionTool &>(m_context->uiContext()->toolBar()->curTool()).getItemsUnderCursor(m_context);
+ if (items.size() == 1 && items.at(0)->locked()) {
+ // TODO
+ }
+ // TODO
+}
+
#include "moc_actionmanager.cpp"
diff --git a/src/widgets/keybindings/actionmanager.hpp b/src/widgets/keybindings/actionmanager.hpp
index dfcdb0f0..22963927 100644
--- a/src/widgets/keybindings/actionmanager.hpp
+++ b/src/widgets/keybindings/actionmanager.hpp
@@ -56,6 +56,7 @@ public:
WhatsNew,
LockItems,
UnLockItem,
+ AddCustomElement,
};
Q_ENUM(Action)
@@ -117,6 +118,7 @@ private:
void configureShortcuts();
void exportSelectedElementsToImage();
void exportToImageElements(const QList<std::shared_ptr<Item>> &items);
+ void slotAddCustomElement();
QAction *createAction(const Action &actionType, const QString &title, const QList<QKeySequence> &keys);
QAction *createToolAction(const Action &actionType, const QString &title, const QList<QKeySequence> &keys, Tool::Type toolType);