2727#include  < QFileInfo> 
2828#include  < QFileSystemWatcher> 
2929#include  < QQmlComponent> 
30+ #include  < QQuickWindow> 
3031
3132#include  < wobjectimpl.h> 
3233
@@ -76,6 +77,76 @@ Script {
7677  {
7778    setScript (data);
7879  }
80+ 
81+   setUiScript (R"_( import QtQuick
82+ import QtQuick3D 
83+ 
84+     View3D { 
85+         id: view 
86+         anchors.fill: parent 
87+ 
88+         //! [environment] 
89+         environment: SceneEnvironment { 
90+             clearColor: "skyblue" 
91+             backgroundMode: SceneEnvironment.Color 
92+         } 
93+         //! [environment] 
94+ 
95+         //! [camera] 
96+         PerspectiveCamera { 
97+             position: Qt.vector3d(0, 200, 300) 
98+             eulerRotation.x: -30 
99+         } 
100+         //! [camera] 
101+ 
102+         //! [light] 
103+         DirectionalLight { 
104+             eulerRotation.x: -30 
105+             eulerRotation.y: -70 
106+         } 
107+         //! [light] 
108+ 
109+         //! [objects] 
110+         Model { 
111+             position: Qt.vector3d(0, -200, 0) 
112+             source: "#Cylinder" 
113+             scale: Qt.vector3d(2, 0.2, 1) 
114+             materials: [ DefaultMaterial { 
115+                     diffuseColor: "red" 
116+                 } 
117+             ] 
118+         } 
119+ 
120+         Model { 
121+             position: Qt.vector3d(0, 150, 0) 
122+             source: "#Sphere" 
123+ 
124+             materials: [ DefaultMaterial { 
125+                     diffuseColor: "blue" 
126+                 } 
127+             ] 
128+ 
129+             //! [animation] 
130+             SequentialAnimation on y { 
131+                 loops: Animation.Infinite 
132+                 NumberAnimation { 
133+                     duration: 3000 
134+                     to: -150 
135+                     from: 150 
136+                     easing.type:Easing.InQuad 
137+                 } 
138+                 NumberAnimation { 
139+                     duration: 3000 
140+                     to: 150 
141+                     from: -150 
142+                     easing.type:Easing.OutQuad 
143+                 } 
144+             } 
145+             //! [animation] 
146+         } 
147+         //! [objects] 
148+     } 
149+ )_"  );
79150  metadata ().setInstanceName (*this );
80151}
81152
@@ -100,11 +171,49 @@ bool ProcessModel::validate(const QString& script) const noexcept
100171  }
101172}
102173
174+ [[nodiscard]] Process::ScriptChangeResult
175+ ProcessModel::setUiScript (const  QString& script)
176+ {
177+   Process::ScriptChangeResult res;
178+   const  auto  trimmed = script.trimmed ();
179+   const  QByteArray data = trimmed.toUtf8 ();
180+ 
181+   if (res = setUiQmlData (data); !res.valid )
182+     return  res;
183+ 
184+   m_ui_script = script;
185+   uiScriptChanged (script);
186+   res.valid  = true ;
187+   return  res;
188+ }
189+ 
103190QString ProcessModel::effect () const  noexcept 
104191{
105192  return  m_qmlData;
106193}
107194
195+ Process::ScriptChangeResult ProcessModel::setUiQmlData (const  QByteArray& data)
196+ {
197+   Process::ScriptChangeResult res;
198+   if (!data.contains (" import QtQuick"  ))
199+     return  res;
200+ 
201+   auto  script = m_cache.getUi (*this , data);
202+   if (!script)
203+     return  res;
204+ 
205+   m_ui_qmlData = data;
206+   res.valid  = true ;
207+   //  FIXME signal?
208+ 
209+   auto  win = new  QQuickWindow;
210+   win->setWidth (640 );
211+   win->setHeight (640 );
212+   script->setParentItem (win->contentItem ());
213+   win->show ();
214+   return  res;
215+ }
216+ 
108217[[nodiscard]] Process::ScriptChangeResult ProcessModel::setScript (const  QString& script)
109218{
110219  Process::ScriptChangeResult res;
@@ -132,7 +241,7 @@ QString ProcessModel::effect() const noexcept
132241Process::ScriptChangeResult ProcessModel::setQmlData (const  QByteArray& data, bool  isFile)
133242{
134243  Process::ScriptChangeResult res;
135-   if (!isFile && !data.startsWith (" import"  ))
244+   if (!isFile && !data.contains (" import  "  ))
136245    return  res;
137246
138247  auto  script = m_cache.get (*this , data, isFile);
@@ -199,6 +308,11 @@ Script* ProcessModel::currentObject() const noexcept
199308  return  m_cache.tryGet (m_qmlData, m_isFile);
200309}
201310
311+ QQuickItem* ProcessModel::currentUI () const  noexcept 
312+ {
313+   return  m_cache.tryGet (m_ui_qmlData);
314+ }
315+ 
202316bool  ProcessModel::isGpu () const  noexcept 
203317{
204318#if  defined(SCORE_HAS_GPU_JS)
@@ -238,6 +352,19 @@ Script* ComponentCache::tryGet(const QByteArray& str, bool isFile) const noexcep
238352  }
239353}
240354
355+ QQuickItem* ComponentCache::tryGet (const  QByteArray& str) const  noexcept 
356+ {
357+   auto  it = ossia::find_if (m_map, [&](const  auto & k) { return  k.key  == str; });
358+   if (it != m_map.end ())
359+   {
360+     return  it->item .get ();
361+   }
362+   else 
363+   {
364+     return  nullptr ;
365+   }
366+ }
367+ 
241368static  std::once_flag qml_dummy_engine_setup{};
242369Script* ComponentCache::get (
243370    const  ProcessModel& process, const  QByteArray& str, bool  isFile) noexcept 
@@ -312,6 +439,58 @@ Script* ComponentCache::get(
312439    }
313440  }
314441}
442+ QQuickItem*
443+ ComponentCache::getUi (const  ProcessModel& process, const  QByteArray& str) noexcept 
444+ {
445+   auto  it = ossia::find_if (m_map, [&](const  auto & k) { return  k.key  == str; });
446+   if (it != m_map.end ())
447+   {
448+     return  it->item .get ();
449+   }
450+   else 
451+   {
452+     static  QQmlEngine dummyEngine;
453+     std::call_once (qml_dummy_engine_setup, [] { setupEngineImportPaths (dummyEngine); });
454+ 
455+     auto  comp = std::make_unique<QQmlComponent>(&dummyEngine);
456+     {
457+       auto & lib = score::AppContext ().settings <Library::Settings::Model>();
458+       //  FIXME QTBUG-107204
459+       QString path = lib.getDefaultLibraryPath () + QDir::separator () + " Scripts" 
460+                      + QDir::separator () + " include"   + QDir::separator () + " Script.qml"  ;
461+       comp->setData (str, QUrl::fromLocalFile (path));
462+     }
463+     const  auto & errs = comp->errors ();
464+     if (!errs.empty ())
465+     {
466+       const  auto & err = errs.first ();
467+       qDebug () << err.line () << err.toString ();
468+       auto  str = err.toString ();
469+       str.remove (" <Unknown File>:"  );
470+       process.errorMessage (err.line (), str);
471+       return  nullptr ;
472+     }
473+ 
474+     auto  obj = comp->create ();
475+     auto  script = qobject_cast<QQuickItem*>(obj);
476+     if (script)
477+     {
478+       if (m_map.size () > 5 )
479+         m_map.erase (m_map.begin ());
480+ 
481+       m_map.emplace_back (
482+           Cache{str, std::move (comp), {}, std::unique_ptr<QQuickItem>(script)});
483+       return  script;
484+     }
485+     else 
486+     {
487+       process.errorMessage (0 , " The component must be of type Script"  );
488+       if (obj)
489+         delete  obj;
490+       return  nullptr ;
491+     }
492+   }
493+ }
315494
316495void  ProcessModel::loadPreset (const  Process::Preset& preset)
317496{
0 commit comments