Dekko
ItemRegistry.cpp
1 #include "ItemRegistry.h"
2 #include <QMetaObject>
3 #include <QQuickWindow>
4 #include "PluginRegistry.h"
5 
6 Q_LOGGING_CATEGORY(PLUGIN_ITEM_REGISTRY, "dekko.plugman.itemregistry")
7 
8 ItemRegistry::ItemRegistry(QObject *parent) : QObject(parent),
9  m_loadMode(LoadAll), m_asynchronous(true)
10 {
11 }
12 
13 QQuickItem* ItemRegistry::target() const
14 {
15  return m_target;
16 }
17 
18 QString ItemRegistry::location() const
19 {
20  return m_location;
21 }
22 
23 QQmlListProperty<QQuickItem> ItemRegistry::defaultItems()
24 {
25  return QQmlListProperty<QQuickItem>(this, m_defaultItems);
26 }
27 
28 ItemRegistry::LoadMode ItemRegistry::loadMode() const
29 {
30  return m_loadMode;
31 }
32 
33 bool ItemRegistry::asynchronous() const
34 {
35  return m_asynchronous;
36 }
37 
38 QString 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 
52 void 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 
62 void 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 
72 void ItemRegistry::setLoadMode(ItemRegistry::LoadMode loadMode)
73 {
74  if (m_loadMode == loadMode)
75  return;
76 
77  m_loadMode = loadMode;
78  emit loadModeChanged(loadMode);
79 }
80 
81 void ItemRegistry::setAsynchronous(bool asynchronous)
82 {
83  if (m_asynchronous == asynchronous)
84  return;
85 
86  m_asynchronous = asynchronous;
87  emit asyncChanged(asynchronous);
88 }
89 
90 void 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 
184 void 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 
194 void ItemRegistry::reparentItemsToTarget(QList<QQuickItem *> items)
195 {
196  for (auto item : items) {
197  reparentItemToTarget(item);
198  }
199 }
200 
201 QQuickItem *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 
220 void 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 
233 void 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 
257 void ItemRegistry::handleIncubatorError()
258 {
259 
260 }
261 
ItemRegistry
Definition: ItemRegistry.h:30
PluginIncubator
Definition: PluginIncubator.h:30