Dekko
Loading...
Searching...
No Matches
ItemRegistry.cpp
1#include "ItemRegistry.h"
2#include <QMetaObject>
3#include <QQuickWindow>
4#include "PluginRegistry.h"
5
6Q_LOGGING_CATEGORY(PLUGIN_ITEM_REGISTRY, "dekko.plugman.itemregistry")
7
8ItemRegistry::ItemRegistry(QObject *parent) : QObject(parent),
9 m_loadMode(LoadAll), m_asynchronous(true)
10{
11}
12
13QQuickItem* ItemRegistry::target() const
14{
15 return m_target;
16}
17
18QString ItemRegistry::location() const
19{
20 return m_location;
21}
22
23QQmlListProperty<QQuickItem> ItemRegistry::defaultItems()
24{
25 return QQmlListProperty<QQuickItem>(this, m_defaultItems);
26}
27
28ItemRegistry::LoadMode ItemRegistry::loadMode() const
29{
30 return m_loadMode;
31}
32
33bool ItemRegistry::asynchronous() const
34{
35 return m_asynchronous;
36}
37
38QString ItemRegistry::findFirstEnabled(const QString &location)
39{
40 if (location.isEmpty()) {
41 return QString();
42 }
43 auto plugins = PluginRegistry::instance()->getByLocation(location);
44 if (plugins.isEmpty()) {
45 return QString();
46 }
47 qCDebug(PLUGIN_ITEM_REGISTRY) << "Found first enabled plugin";
48 auto firstplugin = qobject_cast<DekkoPlugin *>(plugins.first());
49 return firstplugin->component();
50}
51
52void ItemRegistry::setTarget(QQuickItem *target)
53{
54 if (m_target == target)
55 return;
56
57 m_target = target;
58 emit targetChanged(target);
59 loadIfPossible();
60}
61
62void ItemRegistry::setLocation(QString location)
63{
64 if (m_location == location)
65 return;
66
67 m_location = location;
68 emit locationChanged(location);
69 loadIfPossible();
70}
71
72void ItemRegistry::setLoadMode(ItemRegistry::LoadMode loadMode)
73{
74 if (m_loadMode == loadMode)
75 return;
76
77 m_loadMode = loadMode;
78 emit loadModeChanged(loadMode);
79}
80
81void ItemRegistry::setAsynchronous(bool asynchronous)
82{
83 if (m_asynchronous == asynchronous)
84 return;
85
86 m_asynchronous = asynchronous;
87 emit asyncChanged(asynchronous);
88}
89
90void ItemRegistry::loadIfPossible()
91{
92 if (m_location.isEmpty() || m_target.isNull()) {
93 return;
94 }
95
96 auto plugins = PluginRegistry::instance()->getByLocation(m_location);
97
98 switch(m_loadMode) {
99 case DefaultOnly:
100 {
101 qCDebug(PLUGIN_ITEM_REGISTRY) << "Loading default items only";
102 reparentItemsToTarget(m_defaultItems);
103 break;
104 }
105 case LoadFirstEnabled:
106 {
107 if (plugins.isEmpty()) {
108 setLoadMode(DefaultOnly);
109 loadIfPossible();
110 return;
111 }
112 qCDebug(PLUGIN_ITEM_REGISTRY) << "Loading first enabled plugin";
113 auto firstplugin = qobject_cast<DekkoPlugin *>(plugins.first());
114 if (m_asynchronous) {
115 createItemAsync(firstplugin->component());
116 } else {
117 QQuickItem *firstItem = createItemFromUrl(firstplugin->component());
118 reparentItemToTarget(firstItem);
119 }
120 break;
121 }
122 case LoadLastEnabled:
123 {
124 if (plugins.isEmpty()) {
125 setLoadMode(DefaultOnly);
126 loadIfPossible();
127 return;
128 }
129 qCDebug(PLUGIN_ITEM_REGISTRY) << "Loading last enabled plugin";
130 auto lastplugin = qobject_cast<DekkoPlugin *>(plugins.last());
131 if (m_asynchronous) {
132 createItemAsync(lastplugin->component());
133 } else {
134 QQuickItem *lastItem = createItemFromUrl(lastplugin->component());
135 reparentItemToTarget(lastItem);
136 }
137 break;
138 }
139 case LoadAll:
140 {
141 qCDebug(PLUGIN_ITEM_REGISTRY) << "Loading all plugins and default items";
142 reparentItemsToTarget(m_defaultItems); // default items get appended first
143 for (auto plugin : plugins) {
144 if (auto dp = qobject_cast<DekkoPlugin *>(plugin)) {
145 if (m_asynchronous) {
146 createItemAsync(dp->component());
147 } else {
148 QQuickItem *item = createItemFromUrl(dp->component());
149 reparentItemToTarget(item);
150 }
151 }
152 }
153 break;
154 }
155 case LoadById:
156 {
157 if (plugins.isEmpty()) {
158 setLoadMode(DefaultOnly);
159 loadIfPossible();
160 return;
161 }
162 qCDebug(PLUGIN_ITEM_REGISTRY) << "Looking for plugin with id: " << m_pluginId;
163 QQuickItem *item = Q_NULLPTR;
164 for (auto plugin : plugins) {
165 if (plugin->pluginId() == m_pluginId) {
166 if (auto dp = qobject_cast<DekkoPlugin *>(plugin)) {
167 qCDebug(PLUGIN_ITEM_REGISTRY) << "Found plugin with id: " << m_pluginId;
168 item = createItemFromUrl(dp->component());
169 }
170 }
171 }
172 if (item) {
173 reparentItemToTarget(item);
174 } else {
175 qCDebug(PLUGIN_ITEM_REGISTRY) << "No plugin found with id: " << m_pluginId;
176 qCDebug(PLUGIN_ITEM_REGISTRY) << "Falling back to default items";
177 reparentItemsToTarget(m_defaultItems);
178 }
179 break;
180 }
181 }
182}
183
184void ItemRegistry::reparentItemToTarget(QQuickItem *item)
185{
186 if (item) {
187 item->setParentItem(m_target);
188 item->setVisible(true);
189 } else {
190 qCWarning(PLUGIN_ITEM_REGISTRY) << "Invalid item";
191 }
192}
193
194void ItemRegistry::reparentItemsToTarget(QList<QQuickItem *> items)
195{
196 for (auto item : items) {
197 reparentItemToTarget(item);
198 }
199}
200
201QQuickItem *ItemRegistry::createItemFromUrl(const QString &itemUrl)
202{
203 if (itemUrl.isEmpty()) {
204 qCWarning(PLUGIN_ITEM_REGISTRY) << "Invalid component url";
205 return Q_NULLPTR;
206 }
207 auto engine = qmlEngine(this);
208 QQmlComponent itemComponent(engine, QUrl::fromLocalFile(itemUrl), m_target);
209 if (itemComponent.isError()) {
210 for (auto error : itemComponent.errors()) {
211 qCDebug(PLUGIN_ITEM_REGISTRY) << "Failed loading plugin with error:";
212 qCDebug(PLUGIN_ITEM_REGISTRY) << error.toString();
213 }
214 return Q_NULLPTR;
215 }
216
217 return qobject_cast<QQuickItem *>(itemComponent.create(engine->contextForObject(this)));
218}
219
220void ItemRegistry::createItemAsync(const QString &itemUrl)
221{
222 if (itemUrl.isEmpty()) {
223 qCWarning(PLUGIN_ITEM_REGISTRY) << "Invalid component url";
224 return;
225 }
226 PluginIncubator *incubator = new PluginIncubator(this);
227 connect(incubator, &PluginIncubator::objectReady, this, &ItemRegistry::asyncItemReady);
228 connect(incubator, &PluginIncubator::error, this, &ItemRegistry::handleIncubatorError);
229 incubator->setSourceUrl(qmlEngine(this), QUrl::fromLocalFile(itemUrl));
230 m_incubators << incubator;
231}
232
233void ItemRegistry::asyncItemReady()
234{
235 PluginIncubator *incubator = qobject_cast<PluginIncubator *>(sender());
236 if (incubator->status() == QQmlIncubator::Ready) {
237 QObject *itemObject = incubator->object();
238 // We also support loading a QQUickWindow and showing it straight away
239 // if the base component is a Window{} component.
240 if (itemObject->metaObject()->className() == QByteArrayLiteral("QQuickWindowQmlImpl")) {
241 QQuickWindow *window = qobject_cast<QQuickWindow *>(itemObject);
242 window->show();
243 } else {
244 QQuickItem *item = qobject_cast<QQuickItem *>(itemObject);
245 if (item) {
246 reparentItemToTarget(item);
247 } else {
248 qCWarning(PLUGIN_ITEM_REGISTRY) << "Failed casting plugin to qquickitem";
249 incubator->deleteLater();
250 }
251 }
252 } else {
253 incubator->deleteLater();
254 }
255}
256
257void ItemRegistry::handleIncubatorError()
258{
259
260}
261