Merge remote-tracking branch 'origin/qds/dev'

Change-Id: If6812125747b4e090063536215f0d6b79dde27e4
This commit is contained in:
Tim Jenssen
2024-10-22 12:50:25 +02:00
156 changed files with 2942 additions and 2209 deletions

View File

@@ -61,7 +61,7 @@ function(setup_dependencies_component)
endfunction() endfunction()
function(configure_qml_designer Qt6_VERSION) function(configure_qml_designer Qt6_VERSION)
set(QMLDESIGNER_QT6_REQUIRED_VERSION 6.5.4) set(QMLDESIGNER_QT6_REQUIRED_VERSION 6.7.3)
set(QMLDESIGNER_GCC_REQUIRED_VERSION 10.0) set(QMLDESIGNER_GCC_REQUIRED_VERSION 10.0)
set(QMLDESIGNER_CLANG_REQUIRED_VERSION 13.0) set(QMLDESIGNER_CLANG_REQUIRED_VERSION 13.0)
set(QMLDESIGNER_APPLECLANG_REQUIRED_VERSION 15.0) set(QMLDESIGNER_APPLECLANG_REQUIRED_VERSION 15.0)

View File

@@ -32,6 +32,7 @@ set(DESIGNSTUDIO_PLUGINS
DiffEditor DiffEditor
EffectComposer EffectComposer
FakeVim FakeVim
Git
Help Help
Insight Insight
LanguageClient LanguageClient

View File

@@ -105,6 +105,10 @@
\externalpage https://doc.qt.io/qt/linguist-id-based-i18n.html \externalpage https://doc.qt.io/qt/linguist-id-based-i18n.html
\title Text ID based translations \title Text ID based translations
*/ */
/*!
\externalpage https://www.qt.io/blog/qt-design-studio-4.6-released
\title Qt Design Studio 4.6 released
*/
/*! /*!
\externalpage https://www.qt.io/blog/qt-design-studio-4.5.1-released \externalpage https://www.qt.io/blog/qt-design-studio-4.5.1-released
\title Qt Design Studio 4.5.1 released \title Qt Design Studio 4.5.1 released

View File

@@ -12,103 +12,444 @@
<div class="heading"> <div class="heading">
<h2>Getting Started</h2> <h2>Getting Started</h2>
</div> </div>
<div class="indexboxcont indexboxbar"> <ul>
<ul> <li><a href="studio-getting-started.html">Overview</a></li>
<li><a href="studio-getting-started.html">Overview</a></li> <li><a href="studio-installation.html">Installing Qt Design Studio</a></li>
<li><a href="studio-installation.html">Installing Qt Design Studio</a></li> <li><a href="studio-projects.html">Creating Projects</a></li>
<li><a href="gstutorials.html">Tutorials</a></li> <li><a href="creator-quick-tour.html">User Interface</a></li>
<li><a href="creator-quick-tour.html">User Interface</a></li> </ul>
<li><a href="studio-projects.html">Creating Projects</a></li>
<li><a href="studio-use-cases.html">Use Cases</a></li>
<li><a href="studio-terms.html">Concepts and Terms</a></li>
<li><a href="best-practices.html">Best Practices</a></li>
<li><a href="studioexamples.html">Examples</a></li>
</ul>
</div>
</div> </div>
<div class="sectionlist normallist"> <div class="sectionlist normallist">
<div class="heading"> <div class="heading">
<h2>Wireframing</h2> <h2>Key Concepts</h2>
</div>
<div class="indexboxcont indexboxbar">
<ul>
<li><a href="quick-uis.html">Overview</a></li>
<li><a href="studio-app-flows.html">Designing Application Flows</a></li>
<li><a href="quick-components.html">Using Components</a></li>
<li><a href="qtquick-properties.html">Specifying Component Properties</a></li>
<li><a href="qtquick-positioning.html">Scalable Layouts</a></li>
<li><a href="qtquick-annotations.html">Annotating Designs</a></li>
</ul>
</div>
</div>
<div class="sectionlist normallist">
<div class="heading">
<h2>Prototyping</h2>
</div> </div>
<ul> <ul>
<li><a href="qtquick-prototyping.html">Overview</a></li> <li><a href="quick-animation-overview.html">Animation Techniques</a></li>
<li><a href="studio-exporting-and-importing.html">Creating Assets with Other Tools</a></li>
<li><a href="qtquick-creating-ui-logic.html">Creating UI Logic</a></li> <li><a href="qtquick-creating-ui-logic.html">Creating UI Logic</a></li>
<li><a href="studio-simulation-overview.html">Simulating Complex Experiences</a></li> <li><a href="quick-components.html">Components</a></li>
<li><a href="qtquick-adding-dynamics.html">Dynamic Behaviors</a></li>
<li><a href="creator-live-preview.html">Validating with Target Hardware</a></li>
<li><a href="studio-exporting-and-importing.html">Asset Creation with Other Tools</a></li>
</ul>
</div>
<div class="sectionlist normallist">
<div class="heading">
<h2>Motion Design</h2>
</div>
<ul>
<li><a href="qtquick-motion-design.html">Overview</a></li>
<li><a href="quick-animation-overview.html">Introduction to Animation Techniques</a></li>
<li><a href="studio-timeline.html">Creating Timeline Animations</a></li>
<li><a href="qtquick-editing-easing-curves.html">Editing Easing Curves</a></li>
<li><a href="qtquick-production-quality-animation.html">Production Quality</a></li>
<li><a href="qtquick-optimizing-designs.html">Optimizing Designs</a></li>
</ul>
</div>
<div class="sectionlist normallist">
<div class="heading">
<h2>Implementing Applications</h2>
</div>
<ul>
<li><a href="studio-implementing-applications.html">Overview</a></li>
<li><a href="studio-designer-developer-workflow.html">Designer-Developer Workflow</a></li> <li><a href="studio-designer-developer-workflow.html">Designer-Developer Workflow</a></li>
<li><a href="qtquick-text-editor.html">Coding</a></li>
<li><a href="studio-debugging.html">Debugging and Profiling</a></li>
</ul>
</div>
<div class="sectionlist normallist">
<div class="heading">
<h2>Advanced Designer Topics</h2>
</div>
<ul>
<li><a href="studio-advanced.html">Overview</a></li>
<li><a href="creator-quick-ui-forms.html">UI Files</a></li>
<li><a href="creator-telemetry.html">Manage Data Collection</a></li>
<li><a href="studio-packaging.html">Packaging Applications</a></li>
</ul>
</div>
<div class="sectionlist normallist">
<div class="heading">
<h2>Developer Topics</h2>
</div>
<ul>
<li><a href="studio-developer-topics.html">Overview</a></li>
<li><a href="creator-vcs-git.html">Using Git</a></li>
<li><a href="studio-porting-projects.html">Converting Qt 5 Projects into Qt 6 Projects</a></li>
<li><a href="quick-converting-ui-projects.html">Converting UI Projects to Applications</a></li>
<li><a href="creator-editor-external.html">Using External Tools</a></li>
<li><a href="studio-on-mcus.html">Qt Design Studio on MCUs</a></li> <li><a href="studio-on-mcus.html">Qt Design Studio on MCUs</a></li>
</ul> </ul>
</div> </div>
<!--Working With-->
<div class="sectionlist normallist"> <div class="sectionlist normallist">
<div class="heading"> <div class="heading">
<h2>Help</h2> <h2>Working with</h2>
</div> </div>
<ul> <ul>
<li><a href="studio-help.html">Overview</a></li> <li><a>2D Assets</a>
<li><a href="creator-how-to-get-help.html">Getting Help</a></li> <ul>
<li><a href="studio-platforms.html">Supported Platforms</a></li> <li><a href="qtbridge-ai.html">Adobe Illustrator Assets</a></li>
<li><a href="psqtbridge.html">Adobe Photoshop Assets</a></li>
<li><a href="xdqtbridge.html">Adobe XD Assets</a></li>
<li><a href="figmaqtbridge.html">Figma Assets</a></li>
<li><a href="qtbridge-figma-template.html">Figma Bridge Template</a></li>
<li><a href="quick-images.html">Images</a></li>
<li><a href="studio-importing-2d.html">Importing 2D Assets</a></li>
<li><a href="sketchqtbridge.html">Sketch Assets</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>3D Assets</a>
<ul>
<li><a href="exporting-3d-assets.html">Exporting 3D Assets</a></li>
<li><a href="exporting-from-blender.html">Exporting from Blender</a></li>
<li><a href="exporting-from-maya.html">Exporting from Maya</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>3D Scenes</a>
<ul>
<li><a href="studio-3d-camera.html">Cameras</a></li>
<li><a href="studio-3d-lights.html">Lights</a></li>
<li><a href="studio-3d-scene-environment.html">Scene Environments</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Animations</a>
<ul>
<li><a href="studio-timeline.html">Creating Timeline Animations</a></li>
<li><a href="qtquick-editing-easing-curves.html">Editing Easing Curves</a></li>
<li><a href="studio-3d-morph-target.html">Morph Animations</a></li>
<li><a href="studio-skeletal-components.html">Skeletal Animations</a></li>
<li><a href="qtquick-transition-editor.html#animating-transitions-between-states">Transitions between States</a></li>
<li><a href="qtquick-curve-editor.html#editing-animation-curves">Editing Animation Curves</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Code</a>
<ul>
<li><a href="qtquick-text-editor.html">Code</a></li>
<li><a href="creator-jump-to-the-code.html">Jump to Code</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Components</a>
<ul>
<li><a href="quick-components.html">Overview</a></li>
<li><a href="qtquick-annotations.html">Annotating Designs</a></li>
<li><a href="quick-components-view.html#adding-and-removing-modules">Adding and Removing Modules</a></li>
<li><a href="quick-component-instances.html">Creating Component Instances</a></li>
<li><a href="quick-components-creating.html">Creating Custom Components</a></li>
<li><a href="studio-3d-group.html">Grouping Components</a></li>
<li><a href="qtquick-properties.html">Specifying Component Properties</a></li>
<li><a href="quick-dynamic-properties.html">Specifying Custom Properties</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Connections</a>
<ul>
<li><a href="quick-property-bindings.html">Adding Bindings Between Properties</a></li>
<li><a href="quick-signals.html">Connecting Components to Signals</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Data</a>
<ul>
<li><a href="studio-model-editor.html">Data Models</a></li>
<li><a href="quick-data-models.html">Lists and Other Data Models</a></li>
<li><a href="qtquick-placeholder-data.html">Loading Placeholder Data</a></li>
<li><a href="studio-javascript.html">Simulating Application Logic</a></li>
<li><a href="studio-simulink.html">Simulating Dynamic Systems</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Debugging and Profiling</a>
<ul>
<li><a href="creator-qml-debugging-example.html">Debugging Qt Quick Applications</a></li>
<li><a href="creator-debugging-qml.html">Debugging Qt Quick Projects</a></li>
<li><a href="creator-qml-performance-monitor.html">Profiling QML Applications</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Effects</a>
<ul>
<li><a href="working-with-effects.html">Overview</a></li>
<li><a href="quick-2d-effects.html">2D Effects</a></li>
<li><a href="studio-3d-effects.html">3D Effects</a></li>
<li><a href="studio-3d-custom-effects-materials.html">Custom Effects and Materials</a></li>
<li><a href="quick-design-effects.html">Design Effects</a></li>
<li><a href="qtquick-effect-composer-view.html">Effect Composer</a></li>
<li><a href="studio-3d-particles.html">Particles</a></li>
<li><a href="studio-content-library.html#adding-an-effect-to-your-project">Using Content Library Effects</a></li>
<li><a href="qt-using-effect-maker-effects.html">Using Qt Quick Effect Maker Effects</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>External Tools</a>
<ul>
<li><a href="creator-editor-external.html">Using External Tools</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Git</a>
<ul>
<li><a href="creator-vcs-git.html">Using Git</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Internationalization</a>
<ul>
<li><a href="studio-translations.html#importing-and-exporting-translations">Translations</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Layouts</a>
<ul>
<li><a href="qtquick-positioning.html">Scalable Layouts</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Logical Helpers</a>
<ul>
<li><a href="quick-logic-helpers.html">Logical Helpers</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Materials</a>
<ul>
<li><a href="studio-3d-custom-effects-materials.html">Custom Effects and Materials</a></li>
<li><a href="studio-material-editor.html">Materials</a></li>
<li><a href="studio-3d-materials.html">Materials and Shaders</a></li>
<li><a href="studio-content-library.html#adding-a-material-to-your-project">Using Content Library Materials</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Previewing</a>
<ul>
<li><a href="studio-live-preview-desktop.html">Previewing on Desktop</a></li>
<li><a href="creator-live-preview-devices.html">Previewing on Devices</a></li>
<li><a href="qt-design-viewer.html">Sharing Applications Online</a></li>
<li><a href="qt-ui-viewer.html">Viewing Applications on Android</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Projects</a>
<ul>
<li><a href="studio-projects.html">Creating Projects</a></li>
<li><a href="studio-designer-developer-workflow.html">Designer-Developer Workflow</a></li>
<li><a href="studio-packaging.html">Packaging Applications</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Qt Design Studio on MCUs</a>
<ul>
<li><a href="studio-on-mcus.html">Overview</li>
<li><a href="studio-connecting-mcus-with-creator.html">Connecting MCUs with Qt Creator</a></li>
<li><a href="studio-projects-for-mcus.html">Creating Projects for MCUs</a></li>
<li><a href="studio-creating-uis-for-mcus.html">Creating UIs for MCUs</a></li>
<li><a href="studio-developing-apps-for-mcus.html">Developing Applications for MCUs</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Rendering</a>
<ul>
<li><a href="studio-3d-instancing.html">Instanced Rendering</a></li>
<li><a href="studio-3d-repeater-3d.html">The Repeater3D Component</a></li>
<li><a href="studio-3d-loader-3d.html">The Loader3D Component</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Shaders</a>
<ul>
<li><a href="studio-3d-custom-shaders.html">Custom Shaders</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>States</a>
<ul>
<li><a href="quick-states.html">States</li>
<li><a href="qtquick-transition-editor.html#animating-transitions-between-states">Transition between states</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Textures</a>
<ul>
<li><a href="studio-texture-editor.html">Textures</a></li>
<li><a href="studio-3d-texture.html">The Texture Component</a></li>
<li><a href="studio-content-library.html#adding-a-texture-or-environment-to-your-project">Using Content Library Textures</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>UI Controls and Elements</a>
<ul>
<li><a href="quick-shapes.html">Shapes</a></li>
<li><a href="quick-text.html">Text</a></li>
<li><a href="quick-controls.html">UI Controls</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>User Data</a>
<ul>
<li><a href="collecting-usage-statistics.html">Collecting Usage Statistics</a></li>
<li><a href="studio-user-feedback.html">Collecting User Feedback</a></li>
<li><a href="creator-telemetry.html">Managing Data Collection</a></li>
<li><a href="studio-crashpad.html">Reporting Crashes</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>User Interaction</a>
<ul>
<li><a href="quick-user-interaction-methods.html">User Interaction Methods</a></li>
</ul>
</li>
</ul>
</div>
<div class="sectionlist normallist">
<div class="heading">
<h2>Best Practices</h2>
</div>
<ul>
<li><a>Graphics</a>
<ul>
<li><a href="best-practices-glow.html">Creating Glow and Bloom Effects</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Performance</a>
<ul>
<li><a href="studio-optimized-3d-scenes.html">Creating an Optimized 3D Scenes</a></li>
<li><a href="qtquick-optimizing-designs.html">Optimizing Designs</a></li>
<li><a href="qtquick-production-quality-animation.html">Production Quality</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Projects</a>
<ul>
<li><a href="studio-porting-projects.html">Converting Qt 5 Projects into Qt 6 Projects</a></li>
<li><a href="quick-converting-ui-projects.html">Converting UI Projects to Applications</a></li>
</ul>
</li>
</ul>
</div>
<div class="sectionlist normallist">
<div class="heading">
<h2>Tutorials</h2>
</div>
<ul>
<li><a href="gstutorials.html">Overview</a></li>
<li><a href="multilanguage-tutorial.html">Adding Multi-lanugage support to Your Project</a></li>
<li><a href="state-transition-animations.html">Animated State Transitions</a></li>
<li><a href="robotarm-example.html">Creating a C++ Backend for a Qt Design Studio Application</a></li>
<li><a href="qtdesignstudio-loginui1-example.html">Log In UI - Components</a></li>
<li><a href="qtdesignstudio-loginui2-example.html">Log In UI - Positioning</a></li>
<li><a href="qtdesignstudio-loginui3-example.html">Log In UI - States</a></li>
<li><a href="qtdesignstudio-loginui4-example.html">Log In UI - Timeline</a></li>
<li><a href="fire-particle-effect.html">Particle System: Fire Effect</a></li>
<li><a href="rain-snow-particle-effect.html">Particle System: Rain and Snow Effect</a></li>
<li><a href="3d-scene-tutorial.html">Setting up a 3D Scene</a></li>
<li><a href="animation-tutorial.html">Timeline Animation Tutorial</a></li>
</ul>
</div>
<div class="sectionlist normallist">
<div class="heading">
<h2>Examples</h2>
</div>
<ul>
<li><a href="studioexamples.html">Overview</a></li>
<li><a href="qtdesignstudio-coffeemachine-example.html">Coffee Machine</a></li>
<li><a href="qtdesignstudio-ebikedesign-example.html">E-Bike Design</a></li>
<li><a href="effect-composer-example.html">Effect Composer Example</a></li>
<li><a href="fresnel-effect-example.html">Fresnel Example</a></li>
<li><a href="material-bundle-example.html">Material Bundle</a></li>
<li><a href="qtdesignstudio-optimal3dscene-example.html">Optimal 3D Scene</a></li>
<li><a href="qtdesignstudio-progressbar-example.html">Progress Bar</a></li>
<li><a href="qtdesignstudio-sidemenu-example.html">Side Menu</a></li>
<li><a href="qtdesignstudio-simplekeyboard-example.html">Simple Keyboard</a></li>
<li><a href="qtdesignstudio-washingmachineui-example.html">Washing Machine UI</a></li>
<li><a href="qtdesignstudio-webinardemo-example.html">Webinar Demo</a></li>
</ul>
</div>
<div class="sectionlist normallist">
<div class="heading">
<h2>Reference</h2>
</div>
<ul>
<li><a>2D Components</a>
<ul>
<li><a href="quick-preset-components.html#2d-components">Overview</a></li>
<li><a href="quick-animations.html">Animations</a></li>
<li><a href="quick-design-effects.html">Design Effects</a></li>
<li><a href="quick-2d-effects.html">Effects</a></li>
<li><a href="quick-images.html">Images</a></li>
<li><a href="quick-data-models.html">Lists and Other Data Models</a></li>
<li><a href="quick-logic-helpers.html">Logical Helpers</a></li>
<li><a href="quick-shapes.html">Shapes</a></li>
<li><a href="quick-text.html">Text</a></li>
<li><a href="quick-user-interaction-methods.html">User Interaction Methods</a></li>
<li><a href="quick-controls.html">UI Controls</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>3D Components</a>
<ul>
<li><a href="quick-preset-components.html#3d-components">Overview</a></li>
<li><a href="studio-3d-camera.html">Cameras</a></li>
<li><a href="studio-3d-custom-effects-materials.html">Custom Effects and Materials</a></li>
<li><a href="studio-3d-custom-shaders.html">Custom Shaders</a></li>
<li><a href="studio-3d-effects.html">Effects</a></li>
<li><a href="studio-3d-group.html">Group</a></li>
<li><a href="studio-3d-instancing.html">Instanced Rendering</a></li>
<li><a href="studio-3d-lights.html">Lights</a></li>
<li><a href="studio-3d-loader-3d.html">Loader3D</a></li>
<li><a href="studio-3d-materials-types.html">Materials</a></li>
<li><a href="studio-3d-materials.html">Materials and Shaders</a></li>
<li><a href="studio-3d-model.html">Models</a></li>
<li><a href="studio-3d-morph-target.html">Morph Target</a></li>
<li><a href="studio-3d-view.html">Views</a></li>
<li><a href="studio-3d-node.html">Node</a></li>
<li><a href="studio-3d-particles.html">Particles</a></li>
<li><a href="studio-3d-repeater-3d.html">Repeater3D</a></li>
<li><a href="studio-3d-scene-environment.html">Scene Environments</a></li>
<li><a href="studio-skeletal-components.html">Skeletal Animation</a></li>
<li><a href="studio-3d-texture.html">Textures</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Files</a>
<ul>
<li><a href="creator-quick-ui-forms.html">UI Files</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Help</a>
<ul>
<li><a href="studio-terms.html">Concept and Terms</a></li>
<li><a href="studio-keyboard-shortcuts.html">Keyboard Shortcuts</a></li>
<li><a href="creator-how-to-get-help.html">Getting Help</a></li>
<li><a href="studio-platforms.html">Supported Platforms</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Qt Design Studio on MCUs</a>
<ul>
<li><a href="studio-features-on-mcu-projects.html">Supported Features</a></li>
<li><a href="studio-compatibility-with-mcu-sdks.html">Version Compatibility</a></li>
</ul>
</li>
</ul>
<ul>
<li><a>Views</a>
<ul>
<li><a href="creator-using-qt-quick-designer.html">Overview</a></li>
<li><a href="qtquick-form-editor.html">2D</a></li>
<li><a href="studio-3d-editor.html">3D</a></li>
<li><a href="quick-assets.html">Assets</a></li>
<li><a href="qtquick-text-editor.html">Code</a></li>
<li><a href="quick-components-view.html">Components</a></li>
<li><a href="qtquick-connection-view.html">Connections</a></li>
<li><a href="studio-content-library.html">Content Library</a></li>
<li><a href="qtquick-curve-editor.html">Curves</a></li>
<li><a href="qtquick-effect-composer-view.html">Effect Composer</a></li>
<li><a href="creator-file-system-view.html">File System</a></li>
<li><a href="studio-material-editor.html">Material Editor and Browser</a></li>
<li><a href="qtquick-navigator.html">Navigator</a></li>
<li><a href="creator-open-documents-view.html">Open Documents</a></li>
<li><a href="creator-projects-view.html">Projects</a></li>
<li><a href="qtquick-properties-view.html">Properties</a></li>
<li><a href="studio-qt-insight.html">Qt Insight</a></li>
<li><a href="qtquick-states-view.html">States</a></li>
<li><a href="studio-texture-editor.html">Texture Editor</a></li>
<li><a href="qtquick-timeline-view.html">Timeline</a></li>
<li><a href="qtquick-transition-editor.html">Transitions</a></li>
<li><a href="studio-translations.html">Translations</a></li>
</ul>
</li>
</ul> </ul>
</div> </div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -1,9 +1,9 @@
// Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*! /*!
\page quick-components.html \page quick-components.html
\previouspage studio-flow-external-events.html \previouspage quick-uis.html
\nextpage quick-preset-components.html \nextpage quick-preset-components.html
\title Using Components \title Using Components

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*! /*!
@@ -147,12 +147,12 @@
\uicontrol {Default Components} > \uicontrol Views to the \uicontrol {Default Components} > \uicontrol Views to the
\uicontrol Navigator or \uicontrol {2D} view. \uicontrol Navigator or \uicontrol {2D} view.
\li Right-click the view in \uicontrol Navigator, and select \li Right-click the view in \uicontrol Navigator, and select
\uicontrol {Edit Model} in the context-menu to open the \uicontrol {Edit List Model} in the context-menu to open
\uicontrol {Model Editor} view. the list model editor.
\image edit-list-model-model-editor.webp "List view in Model Editor" \image studio-edit-list-model.webp "List view in the list model editor"
\li Double-click a cell to edit its value. \li Double-click the column headings and cells to change their values.
\li Use the toolbar buttons to add or remove rows and columns. \li Use the toolbar buttons to add, remove, or move rows and columns.
In a list, each column represents a property, and each row adds a In a list, each column represents a property and each row adds a
list item. list item.
\endlist \endlist

View File

@@ -39,6 +39,9 @@
\list \list
\li Qt Creator 13.0 or above. \li Qt Creator 13.0 or above.
\li \QDS 4.5 or above. \li \QDS 4.5 or above.
\li Git.
\note Learn more about getting Git \l {https://wiki.qt.io/Git_Installation} {here}.
\endlist \endlist
\list 1 \list 1

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*! /*!
@@ -15,7 +15,7 @@
\list \list
\li Common motion design techniques for 2D and 3D \li Common motion design techniques for 2D and 3D
\li Screen-to-screen or state-to-state animations \li State-to-state animations
\li Data-driven UI logic animations \li Data-driven UI logic animations
\endlist \endlist
@@ -70,7 +70,7 @@
\section2 Animation Curves \section2 Animation Curves
While easing curves work well for most simple UI animations, more complex While easing curves work well for most simple UI animations, more complex
3D animations require several keyframes so it becomes necessary to visualize 3D animations require several keyframes, so it becomes necessary to visualize
the value and the interpolation of a keyframe simultaneously. The the value and the interpolation of a keyframe simultaneously. The
\l {Curves} view visualizes the whole animation of a property at once and \l {Curves} view visualizes the whole animation of a property at once and
shows the effective values of a keyframe together with the interpolation shows the effective values of a keyframe together with the interpolation
@@ -78,36 +78,11 @@
simultaneously so that you can see the animation for the x position simultaneously so that you can see the animation for the x position
and the animation for the y position side-by-side. and the animation for the y position side-by-side.
\section1 Screen-to-Screen or State-to-State Animations \section1 State-to-State Animations
The following table summarizes techniques used for navigating between To navigate between UI states, use transitions between different states of the UI
screens and UI states. using a transition timeline that is based on keyframes. You can apply easing
curves to the keyframes.
\table
\header
\li Technique
\li Use Case
\row
\li \l{Designing Application Flows}{Application flows}
\li An interactive prototype that can be clicked through to simulate
the user experience of the application.
\row
\li \l{Transitions}{Transitions between states}
\li Transitions between different states of the UI using a transition
timeline that is based on keyframes. You can apply easing curves
to the keyframes.
\endtable
\section2 Application Flows
You can design an application in the form of a \e {schematic diagram}
that shows all the significant components of the application UI and their
interconnections by means of symbols. This results in an interactive
prototype that can be clicked through to simulate the user experience of
the application. Code is created in the background and can be used
as the base of the production version of the application.
For more information, see \l{Designing Application Flows}.
\section2 Transitions Between States \section2 Transitions Between States

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*! /*!
@@ -14,8 +14,8 @@
\li You can use different animation techniques for different \li You can use different animation techniques for different
purposes. \QDS supports common motion design techniques, purposes. \QDS supports common motion design techniques,
such as timeline and keyframe based animation and easing such as timeline and keyframe based animation and easing
curves, as well as screen-to-screen or state-to-state curves, as well as state-to-state application flows and
application flows and data-driven UI logic animation. data-driven UI logic animation.
\endtable \endtable
\list \list

View File

@@ -1,10 +1,10 @@
// Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*! /*!
\page quick-uis.html \page quick-uis.html
\previouspage {Examples} \previouspage {Examples}
\nextpage studio-app-flows.html \nextpage quick-components.html
\title Wireframing \title Wireframing
@@ -38,15 +38,6 @@
the developer documentation by pressing \key F1. the developer documentation by pressing \key F1.
\list \list
\li \l {Designing Application Flows}
You can design an application in the form of a \e {schematic diagram}
that shows all significant components of an application UI and their
interconnections by means of symbols. This results in an
interactive prototype that can be clicked through to simulate
the user experience of the application.
\li \l {Using Components} \li \l {Using Components}
\QDS comes with \e {preset components} that you can use in \QDS comes with \e {preset components} that you can use in

View File

@@ -16,7 +16,7 @@
\uicontrol Skip or \uicontrol Submit, the pop-up survey will not appear for \uicontrol Skip or \uicontrol Submit, the pop-up survey will not appear for
the same feature again. the same feature again.
\image studio-feedback-popup.png "User feedback pop-up survey for Flow Editor" \image studio-feedback-popup-material.png "User feedback pop-up survey for Material Browser"
For the pop-up survey to appear, you must enable collecting statistics, and For the pop-up survey to appear, you must enable collecting statistics, and
also allow collecting \uicontrol {4 - Detailed usage statistics} in also allow collecting \uicontrol {4 - Detailed usage statistics} in

View File

@@ -1,707 +0,0 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page studio-app-flows.html
\previouspage quick-uis.html
\nextpage studio-flow-view.html
\title Designing Application Flows
\image studio-flow-view.webp "Application flow in the 2D view"
In \QDS, a \e {flow view} represents a schematic diagram. It consists of
\e {flow items} that represent the screens in the UI and \e {transition
lines} that connect them, thus illustrating the possible user pathways
through the UI. \e {Action areas} are clickable starting points for transition
lines. Attach effects to transition lines, such as fade or push,
to determine what users see when one flow item changes into another.
Use \e {flow decisions} to set up alternative pathways between flow items in the UI. For
example, if user input determines which flow item should open next, test the different
scenarios in the prototype with the decision dialog where you can select which flow item to
show next.
Especially on mobile and embedded platforms, the application might need to
react to external events from the platform, such as notifications or other
applications requiring the users' attention. Use \e {flow wildcards}
to determine the priority of flow items by adding them to allow and
block lists.
To design application flows:
\image studio-flow-steps.png "Designing application flows"
\list 1
\li Use a project wizard template to add a \uicontrol {Flow View}
component, as described in \l{Adding Flow Views}.
\li Use a project wizard template to add a \uicontrol {Flow Item}
component for each screen in the UI, as described in
\l{Adding Flow Items}.
\li Use context menu commands to add action areas and transitions,
as described in \l{Adding Action Areas and Transitions}.
\endlist
Additionally, to create a more advanced application flow:
\list
\li Use context menu commands to apply effects to transitions,
as described in \l{Applying Effects to Transitions}.
\li Use the event list simulator to replace transition lines with connections to real
signals from UI controls, as described in \l{Simulating Events}.
\li Use \uicontrol {Flow Decision} components from \uicontrol Components > \uicontrol {Flow
View} to set up alternative pathways between flow items, as described in
\l{Simulating Conditions}.
\li Use \l{Working with States}{states} in flows to modify the appearance
of components on screens in response to user interaction, as
described in \l{Applying States in Flows}.
\li Use \uicontrol {Flow Wildcard} components from
\uicontrol Components > \uicontrol {Flow View} to prioritize events
from other applications and to stop some screens from appearing on
others, as described in \l{Reacting to External Events}.
\endlist
*/
/*!
\page studio-flow-view.html
\previouspage studio-app-flows.html
\nextpage studio-flow-item.html
\title Adding Flow Views
A flow view is the base component of the flow diagram that you can use to wireframe
the UI of your application. For more information, see \l{Designing Application Flows}.
Add a flow view to an existing project or create a new project for it, as described in
\l {Creating Projects}.
To create the flow view, select \uicontrol File >
\uicontrol {New File} >
\uicontrol {Qt Quick Files} > \uicontrol {Flow View}
and follow the instructions of the wizard.
\image studio-flow-view-create.png "Create Flow View wizard template"
If you want to add an event simulator to the flow view, select the
\uicontrol {Use Event Simulator} checkbox. In this case, select also the
\uicontrol {Use Application Import} checkbox to import the project to the flow view
as the event simulator requires it to work correctly. You need the
import also for access to the project \c Constants.qml file that contains
global settings for the project. For more information, see \l {Simulating Events}.
You can adjust the appearance of all the items in the flow: action areas,
transition lines, decisions, and wildcards. Change the global settings for all items
by editing the flow view properties. To add additional semantics to the flow diagram
design, select an individual action area or transition line and change the appearance
of just that component.
\section1 Flow View Properties
You can specify basic properties for a \uicontrol {Flow View} component
in the \l {Type}{Component}, \l {2D Geometry}{Geometry - 2D}, and
\l Visibility sections in the \l Properties view. Specify flow view
properties in the \uicontrol {Flow View} section.
\image studio-flow-view-properties.webp "Flow View component properties"
To specify the \uicontrol {Flow Item} that is currently visible in the
flow view, set its index in the \uicontrol {Current index} field.
Use the \l{Picking Colors}{color picker} to set colors for:
\list
\li Transition lines
\li Area outlines
\li Area fills
\li Block items
\endlist
You can set some additional global properties for drawing transition lines:
\image studio-flow-view-properties-transition.png "Flow View transition properties"
\list
\li In the \uicontrol {Type} field, select \uicontrol Bezier to draw
transition lines as bezier curves.
\li In the \uicontrol {Radius} field, specify the corner radius for
default curves.
\li In the \uicontrol {Bezier factor} field, specify the factor that
modifies the positions of the control points used for bezier curves.
\endlist
For more information about changing the appearance of a particular action
area or transition line, see \l{Flow Action Area Properties} and
\l{Flow Transition Properties}.
In the \uicontrol Advanced section, you can manage the more
\l{Specifying Developer Properties}{advanced properties}
of components.
*/
/*!
\page studio-flow-item.html
\previouspage studio-flow-view.html
\nextpage studio-flow-action-area.html
\title Adding Flow Items
After you create a \l{Adding Flow Views}{Flow View} component, use a project wizard
template to add a \uicontrol {Flow Item} component for each screen in the UI.
If you \l{Importing 2D Assets}{imported} your screen designs from a
design tool as individual \l{glossary-component}{components}
(\e {.ui.qml} files), you can use them as content for flow items like any other components.
The imported components are listed in \uicontrol Components
> \uicontrol {My Components}.
If you are building your UI from scratch in \QDS, add components to the flow items
first to create the screens as you would any components. For more information, see
\l {Using Components}. The flow items that you attach the components to are listed under
\uicontrol {My Components}.
\image studio-flow-item.webp "Custom Flow Item in Components"
\note You must use the wizard to create the flow items. After you create
a flow view, the \uicontrol {Flow View} module is added to
\uicontrol Components. It contains the \uicontrol {Flow Item} component for
\l{Applying States in Flows}{applying states to flow items}, and solely for that purpose.
To add flow items:
\list 1
\li Select \uicontrol File > \uicontrol {New File} >
\uicontrol {Qt Quick Files} >
\uicontrol {Flow Item} and follow the instructions of the wizard
to create flow items for each screen in the UI.
\li Add content to the flow item in one of the following ways:
\list
\li Drag components from \uicontrol Components to a
flow item in the \l {2D} view or \l Navigator.
\li Drag a screen from \uicontrol Components
> \uicontrol {My Components} to a flow item in
the \uicontrol {2D} view or \uicontrol Navigator.
\endlist
\li In \l Properties, edit the properties of each flow item.
\endlist
Now, drag the flow items from \uicontrol Components > \uicontrol {My Components} to the
flow view in the \uicontrol {2D} or \uicontrol Navigator view. When you have all the flow
items in place, \l{Adding Action Areas and Transitions}{add action areas} to them to create
transitions between them.
\section1 Flow Item Properties
You can specify basic properties for a \uicontrol {Flow Item} component
in the \l {Type}{Component}, \l {2D Geometry}{Geometry - 2D}, and
\l Visibility sections in the \uicontrol Properties view. Specify flow item
properties in the \uicontrol {Flow Item} section.
\image studio-flow-item-properties.png "Flow Item properties"
The \uicontrol {State change target} and \uicontrol {Target state}
properties are used to \l{Applying States in Flows}{apply states}
in flows.
To include another flow view as a flow item into a flow view, select the UI file (.ui.qml)
that specifies the flow view in the \uicontrol {Loader source} field.
Usually, a flow item is inactive and invisible when it is not currently
selected in the flow. Especially, all events from the flow item are ignored.
To make a flow item always active, so that another flow item within it
can respond to events and trigger the opening of a dialog, for example,
select the \uicontrol {Force active} checkbox.
In the flow view, transitions are drawn from action areas to the target flow item by default.
To draw the transitions from the edges of flow items instead, select the
\uicontrol {Join lines} checkbox in the \uicontrol {Transition Lines}
section.
In the \uicontrol Advanced section, you can manage the more
\l{Specifying Developer Properties}{advanced properties} of components.
*/
/*!
\page studio-flow-action-area.html
\previouspage studio-flow-item.html
\nextpage studio-flow-effects.html
\title Adding Action Areas and Transitions
\e {Action areas} are clickable areas that initiate transitions between flow items or
\l{Connecting and Releasing Signals}{create connections} to any signal from any component in a
\l{Adding Flow Items}{flow item}. For example, you could connect an
action to the \c onPressed signal of a button in your flow item to
determine what should happen when users press the button.
\image studio-flow-action-area.webp "Flow Action Area in the 2D view"
Select the type of the mouse or touch input to use for triggering
events, such as click, double-click, flick, pinch, or long press.
Typically, a flow item is connected to several other flow items in the
flow with two-way connections. To avoid clutter, set an action area
as \e {go back} instead of adding explicit transition lines to and from
every potentially connected flow item. When the \uicontrol {Go back} option
is enabled, the transition will always take the user back to the previous
flow item.
You can specify the appearance of each action area or transition line,
including the color, line thickness, dotted or solid lines, and even
the curve of the transition lines. You can change some of these properties
globally, as instructed in \l{Flow View Properties}.
To create action areas:
\list 1
\li Select the flow item in the \l {2D} view, then right-click it, and select
\uicontrol {Flow} > \uicontrol {Create Flow Action} in
the context menu.
\li Drag the action area to the UI control that you want to connect
to the other flow item. For example, to a button that opens another
flow item when clicked.
\li Double-click the action area and drag the transition line to the flow item you want to
connect to. Alternatively, select the action area, right-click it to open the
context-menu, and then select \uicontrol Connect and the flow item from the list.
\li In \l Properties, modify the properties of the action area
and transition line.
\endlist
To preview the flow, select the
\uicontrol {Live Preview} button on the top toolbar or press \key Alt +
\key P.
\section1 Common Properties
Specify basic properties for \uicontrol {Flow Action Area}
and \uicontrol {Flow Transition} components in the \l {Type}{Component},
\l {2D Geometry}{Geometry - 2D}, and \l Visibility sections in the
\uicontrol Properties view.
Use \l{Setting Anchors and Margins}{anchors} in the \uicontrol Layout tab to position
the component.
Manage the more \l{Specifying Developer Properties}{advanced properties} of components
in the \uicontrol Advanced section.
\section1 Flow Action Area Properties
Use the \l{Picking Colors}{color picker} in the \uicontrol {Flow Action Area} section
to set line and fill color.
\image studio-flow-action-area-properties.webp "Flow Action Area properties"
Specify additional properties for action areas in the \uicontrol {Flow Action} and
\uicontrol {Action Area} sections:
\list
\li Select the \uicontrol {Go back} checkbox to specify that the
transition will always take the user back to the previous flow item.
\li In the \uicontrol {Event IDs} field, specify the IDs of the
events to connect to, such as mouse, touch or keyboard events.
\li In the \uicontrol {Action type} field, select the type of the
mouse or touch input to use for triggering events.
\li In the \uicontrol {Line width} field, set the width of the
action area outline.
\li Select the \uicontrol {Dashed line} checkbox to draw a dashed
action area outline.
\li Select the \uicontrol Enabled checkbox to enable interaction
with the action area during preview.
\endlist
\section1 Flow Transition Properties
Specify additional properties for transitions between \l{Adding Flow Items}{flow items}
in the \uicontrol Transition section:
\image studio-flow-transition-properties.webp "Flow Transition properties"
\list
\li Select the \uicontrol Condition checkbox to activate the
transition. Select \inlineimage icons/action-icon.png
to \l{Adding Bindings Between Properties}{bind} a condition
to the transition.
\li In the \uicontrol Question field, enter the text that will appear
next to the transition line. If the transition represents the
connection to a \uicontrol {Flow Decision} component, the
text will also be visible in the selection dialog that opens when
the \l{Simulating Conditions}{condition} is triggered.
\li In the \uicontrol {Event IDs} field, specify the IDs of the
events to connect to, such as mouse, touch or keyboard events.
\li In the \uicontrol From and \uicontrol To fields, select the
flow item where the transition starts and the one where it
ends.
\endlist
Specify the following properties to change the appearance of transition lines in
the \uicontrol {2D} view:
\image studio-flow-transition-line-properties.webp "Flow Transition Line properties"
\list
\li In the \uicontrol {Line width} field, set the width of the
transition line.
\li In the \uicontrol {Offset} and \uicontrol {Break offset} fields, set
the start point (\uicontrol Out) or end point (\uicontrol In) of a
transition line or a break to the specified offset. This enables
you to move them up and down or left and right.
\li Select the \uicontrol {Dashed line} checkbox to draw a dashed line.
\li In the \uicontrol Type field, select \uicontrol Bezier to draw
transition lines as bezier curves.
\li In the \uicontrol Radius field, specify the corner radius for
default curves.
\li In the \uicontrol {Bezier factor} field, specify the factor that
modifies the positions of the control points used for a bezier
curve.
\li In the \uicontrol {Label position} field, set the position of
the value of the \uicontrol Question field in respect to the
transition start point.
\li Select the \uicontrol {Label flip side} checkbox to move the
\uicontrol Question value to the opposite side of the transition
line.
\endlist
\section1 Connecting and Releasing Signals
To connect components to \l{Connecting Components to Signals}{signals}, export the
components first as \l{Adding Property Aliases}{aliases} in \l Navigator. To create
and release connections, select \uicontrol {Open Signal Dialog} in the context menu.
The \uicontrol {Signal List} dialog displays the signals for all components.
\image studio-flow-signal-list.webp "Signal List dialog"
To connect a component to a signal, select \uicontrol Connect next to one
in the list. To release a connected signal, select \uicontrol Release.
*/
/*!
\page studio-flow-effects.html
\previouspage studio-flow-action-area.html
\nextpage studio-flow-events.html
\title Applying Effects to Transitions
You can apply effects, such as fade, move, or push, to
\l{Adding Action Areas and Transitions}{transitions} between
\l{Adding Flow Items}{flow items}. A fade effect makes the first
flow item appear to fade out, while the next flow item fades in.
A move effect makes the second flow item appear to move in over the
first flow item. The push effect makes a flow item appear to push out the previous one.
You can also use your own custom effects that you have created with some other tool.
The transition direction determines the direction the new flow item appears
from: left, right, top, bottom. You can set the duration of the effect and
\l{Editing Easing Curves}{attach an easing curve} to the effect.
To add effects:
\list 1
\li Select a transition line in the \l {2D} view.
\li Right-click the transition line to open the context menu, select \uicontrol {Flow} >
\uicontrol {Flow Effects}, and then select the effect to apply.
\li In \l Properties, modify the properties of the effect.
\endlist
To edit effect properties later, select a transition, open the context menu, and then select
\uicontrol {Flow} > \uicontrol {Select Effect}.
To use your own custom effects, select a transition, open the context menu, and then select
\uicontrol {Flow} > \uicontrol {Flow Effects} > \uicontrol {Assign Custom FlowEffect}.
Then specify the path to your custom effect file.
To remove the current effect from a transition, select a transition, open the context menu,
and then select \uicontrol {Flow} > \uicontrol {Flow Effects} >
\uicontrol {Assign FlowEffect None}.
\section1 Flow Effect Properties
Specify basic properties for a \uicontrol {Flow Effect} component in the \l Type and
\l ID fields in the \uicontrol Component section in the \uicontrol Properties view.
\image studio-flow-effect-properties.png "Flow Effect properties"
Set the duration and easing curve of flow effects in the \uicontrol {Transition Effect}
section:
\list
\li In the \uicontrol Duration field, specify the duration of the
effect.
\li Select the \inlineimage icons/curve_editor.png
button to open \uicontrol {Easing Curve Editor} to attach an
\l{Editing Easing Curves}{easing curve} to the effect.
\endlist
Set some additional properties for a move or push effect in the \uicontrol {Push Effect}
or \uicontrol {Move Effect} section:
\image studio-flow-effect-push-properties.png "Flow Push Effect properties"
\list
\li In the \uicontrol Direction field, specify the direction that
the target \uicontrol {Flow Item} appears from: left, right, top,
or bottom.
\li In the \uicontrol Scale field, set scaling for the effect.
\li In the \uicontrol {Incoming opacity} and
\uicontrol {Outgoing opacity} fields, specify the opacity of
the effect as a number between 0 and 1.
\li Select the \uicontrol Reveal checkbox to reveal the
\uicontrol {Flow Item} where the transition starts.
\endlist
*/
/*!
\page studio-flow-events.html
\previouspage studio-flow-effects.html
\nextpage studio-flow-conditions.html
\title Simulating Events
While \l{Adding Action Areas and Transitions}{transition lines}
are useful for prototyping, in production you need to use the real
\l{Connecting and Releasing Signals}{signals} from UI
\l{glossary-component}{components} to control the flow of the application.
For this purpose, you can use action areas in a more advanced way, by
having them listen to signals from flow items or the controls in them and
by connecting these to the \l{Adding Flow Views}{flow view}. You can use
keyboard shortcuts to simulate these events when you preview the UI.
When you use the wizard to create a \uicontrol {Flow View} component, select
the \uicontrol {Use event simulator} checkbox to add an event simulator to
the flow view.
You can create an event list where you assign keyboard shortcuts to events,
and then use context-menu commands to attach the events to action areas or
transition lines.
\section1 Creating Event Lists
To create an event list:
\list 1
\li Right-click in the \uicontrol 2D or \uicontrol Navigator view and select
\uicontrol {Event List} > \uicontrol {Show Event List}.
\li In the \uicontrol {Event List} dialog, select \inlineimage icons/plus.png
to add a keyboard shortcut for triggering an event to the list.
\image studio-flow-event-list.png "Event List dialog"
\li In the \uicontrol {Event ID} field, enter an identifier for the event. To search
for existing events, enter search criteria in the \uicontrol Filter field.
\li In the \uicontrol Description field, describe the keyboard shortcut.
\li In the \uicontrol Shortcut field, press the keyboard key that will
trigger the event, and then select \uicontrol R to record the
keyboard shortcut. The key identifier appears in the field.
\endlist
You can now assign the events to action areas and transitions.
\section1 Assigning Events to Actions
To assign events to actions:
\list 1
\li Select \uicontrol eventListSimulator in \uicontrol Navigator, and in
\uicontrol Properties > \uicontrol {Exposed Custom Properties}, select the
\uicontrol active checkbox. If \uicontrol eventListSimulator is not visible
in the \uicontrol Navigator, select \inlineimage icons/visibilityon.png.
\li In \uicontrol Navigator, select an action area or transition line.
\li In the context menu, select \uicontrol {Event List} >
\uicontrol {Assign Events to Actions}.
\image studio-flow-events-assign.webp "Assign Events to Actions dialog"
\li To connect an event, select \uicontrol Connect next to an event in
the list. To release a connected event, select \uicontrol Release.
\li Press \key {Alt+P} to preview the UI.
\li Select action areas in the preview, double-click events in the
event list, or use the keyboard shortcuts to trigger events.
\image studio-flow-events-event-list.webp "Event list in Live Preview"
\endlist
*/
/*!
\page studio-flow-conditions.html
\previouspage studio-flow-events.html
\nextpage studio-flow-states.html
\title Simulating Conditions
Part of any complex UI is the conditional logic it uses to present its
state to users or to collect and process data from various sources. Data
can be produced by user interaction from a variety of inputs, such as
buttons and controls, sensor readings from arrays of equipment, or general
values received from backend or service APIs.
The \uicontrol {Flow Decision} component simulates conditions by displaying a
list of options you can choose from when you preview the flow. This enables
you to prototype complex interactions before you have access to the physical
controls, backend, or sensor data that will be required for the production
version.
\image studio-flow-decision.webp "Flow Decision in the 2D view"
To simulate conditions:
\list 1
\li Drag a \uicontrol {Flow Decision} component from
\uicontrol Components > \uicontrol {Flow View} to a
\l{Adding Flow Views}{flow view} in the \l Navigator or \l {2D} view.
\li Select the flow item where you want the application to start in
the \uicontrol Navigator or \uicontrol {2D} view. Then right-click the component
to open the context menu, and select \uicontrol Flow > \uicontrol {Set Flow Start}.
\li Create an \l{Adding Action Areas and Transitions}{action area} for
the component that will trigger the condition and connect it to the
flow decision.
\li Select the flow decision, and then select \uicontrol Connect in the
context menu to create connections to the flow items that will open
depending on whether the condition is met.
\li In the \l Properties view, \uicontrol {Dialog title} field, enter a
title for the selection dialog that opens when the condition is
triggered.
\li Select a transition line in the \uicontrol Navigator or
\uicontrol {2D} view, and add a descriptive text in the
\uicontrol {Question} field in \uicontrol Properties to represent
a choice in the selection dialog.
\image studio-flow-transition-properties-question.webp "Flow Transition properties"
\li Press \key {Alt+P} to preview the UI.
\li Select action areas in the preview, double-click events in the
event list, or use the keyboard shortcuts to trigger events.
\endlist
Flow decisions are listed in a dialog where you can select which condition
is met to see the results.
\image studio-flow-decision-preview.webp "Selection dialog for flow decision"
\section1 Flow Decision Properties
Specify basic properties for a \uicontrol {Flow Decision} component
in the \l Type and \l ID fields in the \uicontrol Component section in the
\uicontrol Properties view. Specify properties for flow decisions in the
\uicontrol {Flow Decision} section.
\image studio-flow-decision-properties.webp "Flow Decision properties"
In the \uicontrol {Dialog title} field, enter a title for the selection
dialog that opens when the condition is triggered.
Specify the following properties to change the appearance of the
flow decision icon \inlineimage icons/flow-decision-icon.png
:
\list
\li Select \inlineimage icons/visibility-off.png
to display the ID of the \uicontrol {Flow Decision}
component in the \l {2D} view.
\li In the \uicontrol {Label position} field, select the corner of
the flow decision icon to place the label in.
\li Use the \l{Picking Colors}{color picker} to set \uicontrol {Outline color} and
\uicontrol {Fill color} of the flow decision icon.
\li In the \uicontrol Size field, specify the size of the flow
decision icon.
\li In the \uicontrol Radius field, specify the radius of the flow
decision icon corners.
\endlist
*/
/*!
\page studio-flow-states.html
\previouspage studio-flow-conditions.html
\nextpage studio-flow-external-events.html
\title Applying States in Flows
Use \l{Working with States}{states} in flows to modify how the \l{glossary-component}
{component} properties change in flow items. For this purpose, use the \uicontrol {Flow Item}
component available in \uicontrol Components > \uicontrol {Flow View}.
To apply states in flows:
\list 1
\li Select \uicontrol File > \uicontrol {New File} >
\uicontrol {Qt Quick Files} >
\uicontrol {Flow Item} to create a flow item.
\li In the \l States view, add states to the flow item.
\li In the \l Projects view, open the .ui.qml file that contains the \l{Adding Flow Views}
{flow view}, and drag the flow item from \uicontrol Components >
\uicontrol {My Components} to the flow view in the \l Navigator or \l 2D view.
\li From \uicontrol Components > \uicontrol {Flow View}, drag an empty
\uicontrol {Flow Item} component (1) to the flow view for each state that you added.
\image studio-flow-item-component.webp "Flow Item in the Components view."
\li In the \uicontrol Navigator or \uicontrol 2D view, select an empty
\uicontrol {Flow Item} to open the \l Properties view.
\li In the \uicontrol {State change target} field, select the flow item that you created
using the wizard.
\li In the \uicontrol {Target state} field, select the state to apply to the flow item.
\image studio-flow-states-item-properties.webp "Flow Item properties"
\endlist
To apply the different states to your application flow, add
\l{Adding Action Areas and Transitions}{action areas} and
\l{Simulating Conditions}{flow decisions} to the flow items.
*/
/*!
\page studio-flow-external-events.html
\previouspage studio-flow-states.html
\nextpage quick-components.html
\title Reacting to External Events
On mobile and embedded platforms, applications are usually integrated into
the platform and therefore screens might pop-up from anywhere or at any
time, based on a conditional event. For example, push notifications
appear on mobile devices and incoming call screens on a car's HMI.
Use the \uicontrol {Flow Wildcard} component to model this type of
screens in your \l{Adding Flow Views}{flow view} using real or simulated events.
Add \l{Adding Flow Items}{flow items} to an \uicontrol {Allow
list} to prioritize them or to a \uicontrol {Block list} to stop some screens from
appearing on others. For example, you could block the incoming call screen
from appearing on a warning screen for the engine if you consider the
warning more important.
To use wildcards:
\list 1
\li Drag a \uicontrol {Flow Wildcard} component from
\uicontrol Components > \uicontrol {Flow View} to the \l {2D} view.
\li To connect the wildcard to a flow item with a \l{Adding Action Areas and Transitions}
{transition line}, double-click the wildcard and drag the transition line to the flow
item.
\li To add logic to the \uicontrol {Flow Wildcard} component, use
\l{Simulating Events}{events}, \l{Simulating Conditions}{Flow Decision} components,
or \l{Connecting and Releasing Signals}{signals}.
\li To manage the priority of your flow items, add flow items to
\uicontrol {Allow list} or \uicontrol {Block list} in \l Properties.
\image studio-flow-wildcard.webp "The wildcard component in 2D view."
\endlist
\section1 Flow Wildcard Properties
Specify basic properties for a \uicontrol {Flow Wildcard} component
in the \l Type and \l ID fields in the \uicontrol Component section in the
\uicontrol Properties view. Specify properties for flow wildcards in the
\uicontrol {Flow Wildcard} section.
\image studio-flow-wildcard-properties.webp "Flow Wildcard properties"
In the \uicontrol {Event IDs} field, specify the IDs of the events to
connect to, such as mouse, touch or keyboard events.
Select the \uicontrol {Global wildcard} checkbox to enable triggering
the wildcard from several flows.
To give flow items high priority, select them in the
\uicontrol {Allow list} field. To block flow items,
select them in the \uicontrol {Block list} field.
Specify the following properties to change the appearance of the
wildcard icon \inlineimage icons/flow-wildcard-icon.png:
\list
\li To set the outline and fill color of the wildcard icon, use the
\l{Picking Colors}{color picker}.
\li In the \uicontrol Size field, specify the size of the wildcard icon.
\li In the \uicontrol Radius field, specify the radius of the wildcard
icon corners.
\endlist
*/

View File

@@ -247,11 +247,7 @@
\li Wizard Template \li Wizard Template
\li Purpose \li Purpose
\row \row
\li {1,5} Qt Quick Files \li {1,4} Qt Quick Files
\li Flow Item and Flow View
\li Generate components that you can use to design the
\l{Designing Application Flows}{application flow}.
\row
\li Qt Quick File \li Qt Quick File
\li Generates a component with one of the following default components \li Generates a component with one of the following default components
or \l{Using Positioners}{positioners} as the root component: or \l{Using Positioners}{positioners} as the root component:

View File

@@ -29,7 +29,6 @@
\li \l{Effect Composer} \li \l{Effect Composer}
\li \l{File System} \li \l{File System}
\li \l{Material Editor and Browser} \li \l{Material Editor and Browser}
\li \l{Model Editor}
\li \l{Navigator} \li \l{Navigator}
\li \l{Open Documents} \li \l{Open Documents}
\li \l{Projects} \li \l{Projects}
@@ -53,17 +52,6 @@
\endlist \endlist
\li \l{Wireframing} \li \l{Wireframing}
\list \list
\li \l{Designing Application Flows}
\list
\li \l{Adding Flow Views}
\li \l{Adding Flow Items}
\li \l{Adding Action Areas and Transitions}
\li \l{Applying Effects to Transitions}
\li \l{Simulating Events}
\li \l{Simulating Conditions}
\li \l{Applying States in Flows}
\li \l{Reacting to External Events}
\endlist
\li \l {Using Components} \li \l {Using Components}
\list \list
\li \l{Preset Components} \li \l{Preset Components}
@@ -134,7 +122,6 @@
\li\l{Connecting Components to Signals} \li\l{Connecting Components to Signals}
\li\l{Adding Bindings Between Properties} \li\l{Adding Bindings Between Properties}
\li\l{Specifying Custom Properties} \li\l{Specifying Custom Properties}
\li\l{Connecting Properties to JSON Data Source}
\endlist \endlist
\li \l{Working with States} \li \l{Working with States}
\endlist \endlist

View File

@@ -48,8 +48,8 @@
click-through mockup. Test, preview, and fine-tune your designs to pixel-perfection, click-through mockup. Test, preview, and fine-tune your designs to pixel-perfection,
live on target devices. live on target devices.
A single unified framework, one common language, fewer feedback loops, and faster iterations, With a single unified framework, one common language, fewer feedback loops, and faster
\QDS closes the gap between designers and developers. iterations, \QDS closes the gap between designers and developers.
\b {LEARN MORE} \b {LEARN MORE}
@@ -87,11 +87,11 @@
\endlist \endlist
\li Online Courses \li Online Courses
\list \list
\li \l{https://qurious.qt.io/enrollments/197683855/details}{Getting Started} \li \l{https://www.qt.io/academy/course-catalog#getting-started-with-qt-design-studio}{Getting Started}
\li \l{https://qurious.qt.io/catalog/courses/3910783}{Creating Your First App} \li \l{https://www.qt.io/academy/course-catalog#creating-your-first-app-with-qt-design-studio}{Creating Your First App}
\li \l{https://qurious.qt.io/enrollments/154647839/details}{Introduction to 2D UI Design} \li \l{https://www.qt.io/academy/course-catalog#2d-with-qt-design-studio}{2D with Qt Design Studio}
\li \l{https://qurious.qt.io/enrollments/167005403/details}{Introduction to 3D Design} \li \l{https://www.qt.io/academy/course-catalog#3d-with-qt-design-studio}{3D with Qt Design Studio}
\li \l{https://qurious.qt.io/catalog/courses/3910791}{Using Qt Bridge for Figma} \li \l{https://www.qt.io/academy/course-catalog#qt-design-studio:-blur-effect}{Creating a Blur Effect}
\endlist \endlist
\endtable \endtable
\enddiv \enddiv

View File

@@ -96,44 +96,75 @@
\endlist \endlist
\image repeater3d-numeric-model.webp \image repeater3d-numeric-model.webp
\section1 Adding a Repeater3D Component with a Model \section1 Adding a Repeater3D Component with a List Model
This section explains how to add a \uicontrol Repeater3D component with This section explains how to add a \uicontrol Repeater3D component with
a model to your \QDS project: a list model to your \QDS project:
To add a \uicontrol Repeater3D component: To add a \uicontrol Repeater3D component:
\list 1 \list 1
\li Drag a \uicontrol Repeater3D component from \uicontrol Components to \li Drag a \uicontrol Repeater3D component from \uicontrol Components to
\e scene in \uicontrol Navigator. \e scene in \uicontrol Navigator.
\li Go to \uicontrol {Model Editor} and create a new model with the name \li You need to enter the QML code for the \uicontrol ListModel manually.
\e planetModel. Go to the \uicontrol {Code} view and enter the following code somewhere
\li Add the following columns and data to the model. inside the root object:
\raw HTML \code qml
<table> ListModel {
<tr> id: planetModel
<th>name (<i>String</i>)</th> ListElement {
<th>radius (<i>Real</i>)</th> name: "Mars"
</tr> radius: 3.39
<tr> }
<td>Mars</td> ListElement {
<td>3.39</td> name: "Earth"
</tr> radius: 6.37
<tr> }
<td>Earth</td> ListElement {
<td>6.37</td> name: "Venus"
</tr> radius: 6.05
<tr> }
<td>Venus</td> }
<td>6.05</td> \endcode
</tr> The default root object for a \QDS project is \uicontrol Rectangle, so
</table> you can paste the \uicontrol ListModel code, for example, like this:
\endraw \code qml
\note You can also import a model in JSON or CSV format. See \l {Importing a Data Model}. Rectangle {
\image repeater3d-model-editor.webp width: Constants.width
\li In \uicontrol Navigator, select \e{_3DRepeater}. height: Constants.height
\li In \uicontrol Properties, set \uicontrol Model to \e {DataStore.planetModel}. color: Constants.backgroundColor
ListModel {
id: planetModel
ListElement {
name: "Mars"
radius: 3.39
}
ListElement {
name: "Earth"
radius: 6.37
}
ListElement {
name: "Venus"
radius: 6.05
}
}
View3D {
id: view3D
anchors.fill: parent
...
\endcode
\li In the \uicontrol {Code} view, add \c {model: planetModel} to the
\uicontrol Repeater3D object to tell that you want to use your
\uicontrol ListModel as the model for the \uicontrol Repeater3D object.
\endlist \endlist
\code qml
Repeater3D {
id: repeater3D
model: planetModel
}
\endcode
Now, you have set up the \uicontrol Repeater3D component to use a Now, you have set up the \uicontrol Repeater3D component to use a
\uicontrol ListModel to draw the items. Next, you need to add the \uicontrol ListModel to draw the items. Next, you need to add the
item to draw. In this example, you are using a \uicontrol Sphere. item to draw. In this example, you are using a \uicontrol Sphere.
@@ -147,7 +178,7 @@
next to \uicontrol Scale > \uicontrol X. next to \uicontrol Scale > \uicontrol X.
\li Select \uicontrol {Set binding} to open \uicontrol {Binding Editor}. \li Select \uicontrol {Set binding} to open \uicontrol {Binding Editor}.
\li In the binding editor, enter \c{radius}. This sets the X \li In the binding editor, enter \c{radius}. This sets the X
scale to the radius value defined in the model for each of the sphere scale to the radius value defined in the list model for each of the sphere
instances. instances.
\image repeater3d-radius-binding.png \image repeater3d-radius-binding.png
\li Select \uicontrol OK. \li Select \uicontrol OK.
@@ -170,6 +201,6 @@
result. You need to zoom out to see all the spheres. result. You need to zoom out to see all the spheres.
\endlist \endlist
\image repeater3d-list-model.webp \image repeater3d-list-model.webp "Spheres in Repeater3D with a ListModel"
*/ */

View File

@@ -1,63 +0,0 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page quick-json-data-properties.html
\previouspage quick-dynamic-properties.html
\nextpage quick-states.html
\title Connecting Properties to JSON Data Source
Connect properties to data from a JSON file. You need two files in your project to do this:
\table
\row
\li \c {data.json}
\li A data file.
\row
\li \c {JsonData.qml}
\li A singleton that reads data from \c {data.json}.
\endtable
To create these files, you need to create a new data model:
\list 1
\li In \uicontrol {Model Editor}, select \inlineimage {icons/zoomIn.png}.
\li Select \uicontrol{Create}.
\endlist
The files are created in the \e {/imports/<projectName>/} folder of the project.
\section1 Connecting a Text Property to a Data Source
To connect a text property to a corresponding field in a JSON file:
\list 1
\li In the \uicontrol Navigator or \uicontrol 2D view, select a component
that has a text property, for example, a text field.
\li In the \uicontrol Connections view, go to the \uicontrol Bindings
tab.
\li Select \inlineimage {icons/plus.png}.
\li In the first \uicontrol From field, select \uicontrol {DataStore}, and in the second field,
select the JSON entry you want to use. In this example, \uicontrol {backend.name} is
selected. This corresponds to the \e name entry in \c {data.json}.
\li In the \uicontrol To field, ensure that \uicontrol text is selected.
\image json-text-binding.webp
\endlist
Now, the text field is populated with data from the JSON file.
\section1 Adding Data Fields to the JSON File
If you add data fields to the JSON file, you need to manually do the same
updates to \c {JsonData.qml}.
\list 1
\li Go to the \uicontrol Projects view and open \c {JsonData.qml}.
\image project-jasondata.webp
\li In the \uicontrol Properties view, create a new local custom property.
\image json-new-property.webp
\li Ensure that the name of the property matches the data entry in the JSON file.
\endlist
*/

View File

@@ -32,11 +32,6 @@
can specify values for. You can add custom properties that would can specify values for. You can add custom properties that would
not otherwise exist for a particular \l{Component Types} not otherwise exist for a particular \l{Component Types}
{component type} or your custom components. {component type} or your custom components.
\li \l{Connecting Properties to JSON Data Source}
You can add bindings between properties and data from a JSON file.
\endlist \endlist
For an example of using properties, bindings, and connections to create a For an example of using properties, bindings, and connections to create a

View File

@@ -1,62 +0,0 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page studio-model-editor.html
\previouspage qtquick-effect-composer-view.html
\nextpage creator-project-managing-workspaces.html
\ingroup studio-views
\title Model Editor
\brief Create, manage, import, and export data models.
In the \uicontrol {Model Editor} view, you can create, manage, import, and export
data models. With data models, you can, for example, populate views with data.
\image edit-list-model-model-editor.webp
For examples of how to use data models, see
\l {Adding a Repeater3D Component with a Model}.
\section1 Creating a Data Model
To create a data model:
\list 1
\li In \uicontrol {Model Editor}, select \inlineimage {icons/zoomIn.png}.
\li Enter a name and select \uicontrol {Create}.
\endlist
This creates a single-cell table.
\image model-editor-new-model.webp
Next, add columns, rows, and data to the model.
\note You must manually save the table after you have made changes. To do this,
select \inlineimage {icons/save-effect-composer.png}.
\section1 Editing a Data Model
Edit a data model in one of the following ways:
\list
\li Right-click a column name to edit its name and type, delete, or sort it.
\li Double-click a cell to edit its content.
\li Use the toolbar to add and remove columns and rows.
\endlist
\note You must manually save the table after you have made changes. To do this,
select \inlineimage {icons/save-effect-composer.png}.
\section1 Importing a Data Model
Import data models from JSON or CSV files. To do this, select \inlineimage {icons/import.png}
in \uicontrol {Model Editor}.
\section1 Exporting a Data Model
Export data models to JSON or CSV files. To do this, select \inlineimage {icons/export.png}
in \uicontrol {Model Editor}.
*/

View File

@@ -14,6 +14,7 @@
\section2 \QDS 4 \section2 \QDS 4
\list \list
\li \l{Qt Design Studio 4.6 released}
\li \l{Qt Design Studio 4.5.1 released} \li \l{Qt Design Studio 4.5.1 released}
\li \l{Qt Design Studio 4.5 released} \li \l{Qt Design Studio 4.5 released}
\li \l{Qt Design Studio 4.4 released} \li \l{Qt Design Studio 4.4 released}

View File

@@ -101,7 +101,7 @@ Item {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
onClicked: { onClicked: {
if (assetsModel.hasFiles) { if (!assetsModel.isEmpty) {
function onFolderCreated(path) { function onFolderCreated(path) {
assetsView.addCreatedFolder(path) assetsView.addCreatedFolder(path)
} }
@@ -189,13 +189,13 @@ Item {
leftPadding: 10 leftPadding: 10
color: StudioTheme.Values.themeTextColor color: StudioTheme.Values.themeTextColor
font.pixelSize: StudioTheme.Values.baseFont font.pixelSize: StudioTheme.Values.baseFont
visible: !assetsModel.hasFiles && !root.__searchBoxEmpty visible: assetsModel.isEmpty && !root.__searchBoxEmpty
} }
Item { // placeholder when the assets library is empty Item { // placeholder when the assets library is empty
width: parent.width width: parent.width
height: parent.height - toolbar.height - column.spacing height: parent.height - toolbar.height - column.spacing
visible: !assetsModel.hasFiles && root.__searchBoxEmpty visible: assetsModel.isEmpty && root.__searchBoxEmpty
clip: true clip: true
MouseArea { // right clicking the empty area of the view MouseArea { // right clicking the empty area of the view

View File

@@ -192,7 +192,7 @@ StudioControls.Menu {
StudioControls.MenuItem { StudioControls.MenuItem {
text: qsTr("New Folder") text: qsTr("New Folder")
visible: root.assetsModel.hasFiles visible: !root.assetsModel.isEmpty
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
NewFolderDialog { NewFolderDialog {

View File

@@ -70,9 +70,9 @@ TreeView {
model: assetsModel model: assetsModel
onRowsChanged: { onRowsChanged: {
if (root.rows > root.rootPathRow + 1 && !assetsModel.hasFiles || if (root.rows > root.rootPathRow + 1 && assetsModel.isEmpty ||
root.rows <= root.rootPathRow + 1 && assetsModel.hasFiles) { root.rows <= root.rootPathRow + 1 && !assetsModel.isEmpty) {
assetsModel.syncHasFiles() assetsModel.syncIsEmpty()
} }
root.updateRows() root.updateRows()
@@ -328,7 +328,7 @@ TreeView {
function moveSelection(amount) function moveSelection(amount)
{ {
if (!assetsModel.hasFiles || !amount) if (assetsModel.isEmpty || !amount)
return return
let index = root.currentFilePath ? assetsModel.indexForPath(root.currentFilePath) let index = root.currentFilePath ? assetsModel.indexForPath(root.currentFilePath)

View File

@@ -133,6 +133,8 @@ HelperWidgets.ScrollView {
topPadding: 10 topPadding: 10
leftPadding: 10 leftPadding: 10
visible: root.materialsModel.isEmpty visible: root.materialsModel.isEmpty
wrapMode: Text.WordWrap
width: root.width - x
} }
} }
} }

View File

@@ -2,11 +2,12 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick import QtQuick
import QtQuick.Layouts
import HelperWidgets 2.0 as HelperWidgets import HelperWidgets 2.0 as HelperWidgets
import StudioControls 1.0 as StudioControls import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme import StudioTheme 1.0 as StudioTheme
Row { RowLayout {
id: root id: root
property int currIndex: 0 property int currIndex: 0
@@ -24,6 +25,7 @@ Row {
icon: modelData.icon icon: modelData.icon
selected: root.currIndex === index selected: root.currIndex === index
onClicked: root.currIndex = index onClicked: root.currIndex = index
Layout.fillWidth: true
} }
} }
} }

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick import QtQuick
import QtQuick.Controls
import HelperWidgets 2.0 as HelperWidgets import HelperWidgets 2.0 as HelperWidgets
import StudioControls 1.0 as StudioControls import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme import StudioTheme 1.0 as StudioTheme
@@ -16,7 +17,6 @@ Rectangle {
property bool selected: false property bool selected: false
height: button.height height: button.height
width: button.width + label.width + contentRow.spacing + 6
color: StudioTheme.Values.themeToolbarBackground color: StudioTheme.Values.themeToolbarBackground
radius: StudioTheme.Values.smallRadius radius: StudioTheme.Values.smallRadius
@@ -43,9 +43,9 @@ Rectangle {
color: StudioTheme.Values.themeTextColor color: StudioTheme.Values.themeTextColor
text: qsTr("Materials") text: qsTr("Materials")
font.pixelSize: StudioTheme.Values.baseFontSize font.pixelSize: StudioTheme.Values.baseFontSize
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight elide: Text.ElideRight
width: root.width - x
} }
} }
@@ -56,6 +56,12 @@ Rectangle {
onClicked: root.clicked() onClicked: root.clicked()
} }
StudioControls.ToolTip {
visible: mouseArea.containsMouse
text: label.text
delay: 1000
}
states: [ states: [
State { State {
name: "default" name: "default"

View File

@@ -241,6 +241,9 @@
"LocalOrientIcon": { "LocalOrientIcon": {
"iconName": "localOrient_medium" "iconName": "localOrient_medium"
}, },
"LiveUpdateIcon": {
"iconName": "restartParticles_medium"
},
"MoveToolIcon": { "MoveToolIcon": {
"iconName": "move_medium" "iconName": "move_medium"
}, },
@@ -276,6 +279,9 @@
"SplitViewIcon": { "SplitViewIcon": {
"iconName": "splitScreen_medium" "iconName": "splitScreen_medium"
}, },
"SyncIcon": {
"iconName": "updateContent_medium"
},
"ToggleGroupIcon": { "ToggleGroupIcon": {
"Off": { "Off": {
"iconName": "selectOutline_medium" "iconName": "selectOutline_medium"

View File

@@ -195,6 +195,10 @@ Item {
onAssignToSelectedClicked: { onAssignToSelectedClicked: {
root.backendModel.assignToSelected() root.backendModel.assignToSelected()
} }
onOpenShadersCodeEditor: {
root.backendModel.openMainShadersCodeEditor()
}
} }
SplitView { SplitView {
@@ -366,6 +370,8 @@ Item {
expanded = wasExpanded expanded = wasExpanded
dragAnimation.enabled = true dragAnimation.enabled = true
} }
onOpenShadersCodeEditor: (idx) => root.backendModel.openShadersCodeEditor(idx)
} }
} // Repeater } // Repeater
} // Column } // Column

View File

@@ -19,6 +19,7 @@ Rectangle {
signal saveClicked signal saveClicked
signal saveAsClicked signal saveAsClicked
signal assignToSelectedClicked signal assignToSelectedClicked
signal openShadersCodeEditor
Row { Row {
spacing: 5 spacing: 5
@@ -48,12 +49,24 @@ Rectangle {
style: StudioTheme.Values.viewBarButtonStyle style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.saveAs_medium buttonIcon: StudioTheme.Constants.saveAs_medium
tooltip: qsTr("Save current composition with a new name") tooltip: qsTr("Save current composition with a new name")
enabled: root.backendModel ? root.backendModel.isEnabled && root.backendModel.currentComposition !== "" enabled: root.backendModel ? root.backendModel.isEnabled
&& root.backendModel.currentComposition !== ""
: false : false
onClicked: root.saveAsClicked() onClicked: root.saveAsClicked()
} }
HelperWidgets.AbstractButton {
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.codeEditor_medium
tooltip: qsTr("Open Code")
enabled: root.backendModel ? root.backendModel.isEnabled
&& root.backendModel.currentComposition !== ""
: false
onClicked: root.openShadersCodeEditor()
}
HelperWidgets.AbstractButton { HelperWidgets.AbstractButton {
style: StudioTheme.Values.viewBarButtonStyle style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.assignTo_medium buttonIcon: StudioTheme.Constants.assignTo_medium

View File

@@ -30,10 +30,34 @@ HelperWidgets.Section {
eyeEnabled: nodeEnabled eyeEnabled: nodeEnabled
eyeButtonToolTip: qsTr("Enable/Disable Node") eyeButtonToolTip: qsTr("Enable/Disable Node")
signal openShadersCodeEditor(index: int)
onEyeButtonClicked: { onEyeButtonClicked: {
nodeEnabled = root.eyeEnabled nodeEnabled = root.eyeEnabled
} }
icons: HelperWidgets.IconButton {
icon: StudioTheme.Constants.codeEditor_medium
transparentBg: true
buttonSize: 21
iconSize: StudioTheme.Values.smallIconFontSize
iconColor: StudioTheme.Values.themeTextColor
iconScale: containsMouse ? 1.2 : 1
implicitWidth: width
onClicked: root.openShadersCodeEditor(index)
}
content: Label {
text: root.caption
color: root.labelColor
elide: Text.ElideRight
font.pixelSize: root.sectionFontSize
font.capitalization: root.labelCapitalization
anchors.verticalCenter: parent?.verticalCenter
textFormat: Text.RichText
leftPadding: StudioTheme.Values.toolbarSpacing
}
Column { Column {
spacing: 10 spacing: 10

View File

@@ -3,6 +3,7 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Controls.impl
import HelperWidgets import HelperWidgets
import StudioControls as StudioControls import StudioControls as StudioControls
import StudioTheme as StudioTheme import StudioTheme as StudioTheme

View File

@@ -22,12 +22,17 @@ StudioControls.ComboBox {
required property Item mainRoot required property Item mainRoot
property var images: ["images/preview0.png", property var images: [Qt.url(""),
"images/preview1.png", Qt.url("images/preview0.png"),
"images/preview2.png", Qt.url("images/preview1.png"),
"images/preview3.png", Qt.url("images/preview2.png"),
"images/preview4.png"] Qt.url("images/preview3.png"),
property string selectedImage: images[0] Qt.url("images/preview4.png")]
property url selectedImage: EffectComposerBackend.effectComposerModel.currentPreviewImage != Qt.url("")
? EffectComposerBackend.effectComposerModel.currentPreviewImage
: images[1]
Component.onCompleted: EffectComposerBackend.effectComposerModel.currentPreviewImage = images[1]
readonly property int popupHeight: Math.min(800, col.height + 2) readonly property int popupHeight: Math.min(800, col.height + 2)
@@ -122,45 +127,80 @@ StudioControls.ComboBox {
border.width: 1 border.width: 1
focus: true focus: true
HelperWidgets.ScrollView { Column {
anchors.fill: parent anchors.fill: parent
anchors.margins: 1
clip: true
Column { Item {
id: col id: setCustomItem
width: parent.width
height: 50
padding: 10 HelperWidgets.Button {
spacing: 10 anchors.fill: parent
anchors.bottomMargin: 2
anchors.topMargin: col.padding
anchors.leftMargin: col.padding
anchors.rightMargin: col.padding
text: qsTr("Set Custom Image")
onClicked: {
EffectComposerBackend.effectComposerModel.chooseCustomPreviewImage()
root.popup.close()
}
}
}
Repeater {
model: root.images
Rectangle { HelperWidgets.ScrollView {
required property int index width: parent.width - 2
required property var modelData height: parent.height - setCustomItem.height
color: "transparent" clip: true
border.color: root.selectedImage === modelData ? StudioTheme.Values.themeInteraction
: "transparent"
width: 200 Column {
height: 200 id: col
Image { padding: 10
source: modelData spacing: 10
anchors.fill: parent
fillMode: Image.PreserveAspectFit
smooth: true
anchors.margins: 1
}
MouseArea { Repeater {
anchors.fill: parent model: root.images
onClicked: { Rectangle {
root.selectedImage = root.images[index] required property int index
root.popup.close() required property var modelData
color: "transparent"
border.color: root.selectedImage === modelData ? StudioTheme.Values.themeInteraction
: "transparent"
width: 200
height: 200
visible: index > 0
|| EffectComposerBackend.effectComposerModel.customPreviewImage !== Qt.url("")
Image {
source: index > 0
? parent.modelData
: EffectComposerBackend.effectComposerModel.customPreviewImage
anchors.fill: parent
fillMode: Image.PreserveAspectFit
smooth: true
anchors.margins: 1
}
MouseArea {
anchors.fill: parent
onClicked: {
if (parent.index > 0) {
EffectComposerBackend.effectComposerModel.currentPreviewImage
= root.images[index]
} else {
EffectComposerBackend.effectComposerModel.currentPreviewImage
= EffectComposerBackend.effectComposerModel.customPreviewImage
}
root.popup.close()
}
} }
} }
} }

View File

@@ -391,6 +391,21 @@ Item {
visible: BackendApi.haveVirtualKeyboard visible: BackendApi.haveVirtualKeyboard
} }
StudioControls.CheckBox {
id: enableCMakeGeneration
actionIndicatorVisible: false
text: qsTr("Enable Cmake Generation")
font.pixelSize: DialogValues.defaultPixelSize
checked: BackendApi.enableCMakeGeneration
visible: BackendApi.hasCMakeGeneration
}
Binding {
target: BackendApi
property: "enableCMakeGeneration"
value: enableCMakeGeneration.checked
}
RowLayout { // Target Qt Version RowLayout { // Target Qt Version
width: parent.width width: parent.width
visible: BackendApi.haveTargetQtVersion visible: BackendApi.haveTargetQtVersion

View File

@@ -39,6 +39,8 @@ Item {
textFormat: Text.RichText textFormat: Text.RichText
} }
property Item icons
property int leftPadding: StudioTheme.Values.sectionLeftPadding property int leftPadding: StudioTheme.Values.sectionLeftPadding
property int rightPadding: 0 property int rightPadding: 0
property int topPadding: StudioTheme.Values.sectionHeadSpacerHeight property int topPadding: StudioTheme.Values.sectionHeadSpacerHeight
@@ -214,6 +216,13 @@ Item {
} }
} }
Item {
id: iconsContent
height: header.height
children: [ section.icons ]
Layout.preferredWidth: childrenRect.width
}
IconButton { IconButton {
id: arrow id: arrow
icon: StudioTheme.Constants.sectionToggle icon: StudioTheme.Constants.sectionToggle

View File

@@ -32,6 +32,7 @@
{ "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" },
{ "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" },
{ "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" },
{ "key": "EnableCMakeGenerationDefault", "value": "%{JS: true}" },
{ "key": "QtQuick3DVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuick3DVersion}" } { "key": "QtQuick3DVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuick3DVersion}" }
], ],
@@ -208,6 +209,15 @@
"checked": "%{UseVirtualKeyboardDefault}" "checked": "%{UseVirtualKeyboardDefault}"
} }
}, },
{
"name": "EnableCMakeGeneration",
"trDisplayName": "Enable CMake Genertion",
"type": "CheckBox",
"data":
{
"checked": "%{EnableCMakeGenerationDefault}"
}
},
{ {
"name": "CustomScreenWidth", "name": "CustomScreenWidth",
"trDisplayName": "Custom screen width:", "trDisplayName": "Custom screen width:",

View File

@@ -32,6 +32,7 @@
{ "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" },
{ "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" },
{ "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" },
{ "key": "EnableCMakeGenerationDefault", "value": "%{JS: true}" },
{ "key": "QtQuick3DVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuick3DVersion}" } { "key": "QtQuick3DVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuick3DVersion}" }
], ],
@@ -208,6 +209,15 @@
"checked": "%{UseVirtualKeyboardDefault}" "checked": "%{UseVirtualKeyboardDefault}"
} }
}, },
{
"name": "EnableCMakeGeneration",
"trDisplayName": "Enable CMake Genertion",
"type": "CheckBox",
"data":
{
"checked": "%{EnableCMakeGenerationDefault}"
}
},
{ {
"name": "CustomScreenWidth", "name": "CustomScreenWidth",
"trDisplayName": "Custom screen width:", "trDisplayName": "Custom screen width:",

View File

@@ -31,6 +31,7 @@
{ "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" },
{ "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" },
{ "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" },
{ "key": "EnableCMakeGenerationDefault", "value": "%{JS: true}" },
{ "key": "DefaultStyle", "value": "Basic" } { "key": "DefaultStyle", "value": "Basic" }
], ],
@@ -207,6 +208,15 @@
"checked": "%{UseVirtualKeyboardDefault}" "checked": "%{UseVirtualKeyboardDefault}"
} }
}, },
{
"name": "EnableCMakeGeneration",
"trDisplayName": "Enable CMake Genertion",
"type": "CheckBox",
"data":
{
"checked": "%{EnableCMakeGenerationDefault}"
}
},
{ {
"name": "CustomScreenWidth", "name": "CustomScreenWidth",
"trDisplayName": "Custom screen width:", "trDisplayName": "Custom screen width:",

View File

@@ -22,7 +22,7 @@ Project {
} }
JavaScriptFiles { JavaScriptFiles {
directory: "%{ProjectName}" directory: "%{ContentDir}"
} }
ImageFiles { ImageFiles {
@@ -103,6 +103,10 @@ Project {
/* Required for deployment */ /* Required for deployment */
targetDirectory: "/opt/%{ProjectName}" targetDirectory: "/opt/%{ProjectName}"
@if %{EnableCMakeGeneration}
enableCMakeGeneration: true
@endif
qdsVersion: "4.6" qdsVersion: "4.6"
quickVersion: "%{QtQuickVersion}" quickVersion: "%{QtQuickVersion}"

View File

@@ -32,6 +32,7 @@ Item {
} }
Text { Text {
visible: !Constants.projectModel.liteDesignerEnabled
id: brandLabel id: brandLabel
color: Constants.currentBrand color: Constants.currentBrand
text: qsTr("Qt Design Studio") text: qsTr("Qt Design Studio")
@@ -44,6 +45,20 @@ Item {
} }
Text { Text {
visible: Constants.projectModel.liteDesignerEnabled
id: brandLabelLite
color: Constants.currentBrand
text: qsTr("Lite QML Designer")
anchors.verticalCenter: parent.verticalCenter
anchors.left: welcomeTo.right
anchors.leftMargin: 8
verticalAlignment: Text.AlignVCenter
font.pixelSize: 36
font.family: "titillium web"
}
Text {
visible: !Constants.projectModel.liteDesignerEnabled
width: 291 width: 291
height: 55 height: 55
color: Constants.currentGlobalText color: Constants.currentGlobalText

View File

@@ -56,6 +56,8 @@ using namespace Utils;
namespace Core { namespace Core {
namespace Internal { namespace Internal {
namespace {
bool optionsPageLessThan(const IOptionsPage *p1, const IOptionsPage *p2) bool optionsPageLessThan(const IOptionsPage *p1, const IOptionsPage *p2)
{ {
if (p1->category() != p2->category()) if (p1->category() != p2->category())
@@ -127,7 +129,7 @@ CategoryModel::~CategoryModel()
int CategoryModel::rowCount(const QModelIndex &parent) const int CategoryModel::rowCount(const QModelIndex &parent) const
{ {
return parent.isValid() ? 0 : m_categories.size(); return parent.isValid() ? 0 : static_cast<int>(m_categories.size());
} }
QVariant CategoryModel::data(const QModelIndex &index, int role) const QVariant CategoryModel::data(const QModelIndex &index, int role) const
@@ -249,7 +251,7 @@ protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
}; };
static bool categoryVisible(const Id &id) static bool categoryVisible([[maybe_unused]] const Id &id)
{ {
#ifdef QT_NO_DEBUG #ifdef QT_NO_DEBUG
@@ -806,6 +808,8 @@ bool SettingsDialog::execDialog()
return m_applied; return m_applied;
} }
} // namespace
bool executeSettingsDialog(QWidget *parent, Id initialPage) bool executeSettingsDialog(QWidget *parent, Id initialPage)
{ {
if (!ExtensionSystem::PluginManager::isInitializationDone()) { if (!ExtensionSystem::PluginManager::isInitializationDone()) {

View File

@@ -6,12 +6,14 @@ add_qtc_plugin(EffectComposer
Qt::Core Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick Qt::Core Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick
QtCreator::Utils QtCreator::Utils
SOURCES SOURCES
effectcodeeditorwidget.cpp effectcodeeditorwidget.h
effectcomposerplugin.cpp effectcomposerplugin.cpp
effectcomposerwidget.cpp effectcomposerwidget.h effectcomposerwidget.cpp effectcomposerwidget.h
effectcomposerview.cpp effectcomposerview.h effectcomposerview.cpp effectcomposerview.h
effectcomposermodel.cpp effectcomposermodel.h effectcomposermodel.cpp effectcomposermodel.h
effectcomposernodesmodel.cpp effectcomposernodesmodel.h effectcomposernodesmodel.cpp effectcomposernodesmodel.h
effectcomposeruniformsmodel.cpp effectcomposeruniformsmodel.h effectcomposeruniformsmodel.cpp effectcomposeruniformsmodel.h
effectshaderscodeeditor.cpp effectshaderscodeeditor.h
effectnode.cpp effectnode.h effectnode.cpp effectnode.h
effectnodescategory.cpp effectnodescategory.h effectnodescategory.cpp effectnodescategory.h
compositionnode.cpp compositionnode.h compositionnode.cpp compositionnode.h

View File

@@ -3,8 +3,9 @@
#include "compositionnode.h" #include "compositionnode.h"
#include "effectutils.h"
#include "effectcomposeruniformsmodel.h" #include "effectcomposeruniformsmodel.h"
#include "effectshaderscodeeditor.h"
#include "effectutils.h"
#include "propertyhandler.h" #include "propertyhandler.h"
#include "uniform.h" #include "uniform.h"
@@ -44,6 +45,8 @@ CompositionNode::CompositionNode(const QString &effectName, const QString &qenPa
} }
} }
CompositionNode::~CompositionNode() = default;
QString CompositionNode::fragmentCode() const QString CompositionNode::fragmentCode() const
{ {
return m_fragmentCode; return m_fragmentCode;
@@ -110,8 +113,8 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
m_name = json.value("name").toString(); m_name = json.value("name").toString();
m_description = json.value("description").toString(); m_description = json.value("description").toString();
m_fragmentCode = EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray()); setFragmentCode(EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray()));
m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray()); setVertexCode(EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray()));
if (json.contains("extraMargin")) if (json.contains("extraMargin"))
m_extraMargin = json.value("extraMargin").toInt(); m_extraMargin = json.value("extraMargin").toInt();
@@ -154,6 +157,36 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
} }
} }
void CompositionNode::ensureShadersCodeEditor()
{
if (m_shadersCodeEditor)
return;
m_shadersCodeEditor = Utils::makeUniqueObjectLatePtr<EffectShadersCodeEditor>(name());
m_shadersCodeEditor->setFragmentValue(fragmentCode());
m_shadersCodeEditor->setVertexValue(vertexCode());
connect(m_shadersCodeEditor.get(), &EffectShadersCodeEditor::vertexValueChanged, this, [this] {
setVertexCode(m_shadersCodeEditor->vertexValue());
});
connect(m_shadersCodeEditor.get(), &EffectShadersCodeEditor::fragmentValueChanged, this, [this] {
setFragmentCode(m_shadersCodeEditor->fragmentValue());
});
connect(
m_shadersCodeEditor.get(),
&EffectShadersCodeEditor::rebakeRequested,
this,
&CompositionNode::rebakeRequested);
}
void CompositionNode::requestRebakeIfLiveUpdateMode()
{
if (m_shadersCodeEditor && m_shadersCodeEditor->liveUpdate())
emit rebakeRequested();
}
QList<Uniform *> CompositionNode::uniforms() const QList<Uniform *> CompositionNode::uniforms() const
{ {
return m_uniforms; return m_uniforms;
@@ -189,6 +222,34 @@ void CompositionNode::setRefCount(int count)
emit isDepencyChanged(); emit isDepencyChanged();
} }
void CompositionNode::setFragmentCode(const QString &fragmentCode)
{
if (m_fragmentCode == fragmentCode)
return;
m_fragmentCode = fragmentCode;
emit fragmentCodeChanged();
requestRebakeIfLiveUpdateMode();
}
void CompositionNode::setVertexCode(const QString &vertexCode)
{
if (m_vertexCode == vertexCode)
return;
m_vertexCode = vertexCode;
emit vertexCodeChanged();
requestRebakeIfLiveUpdateMode();
}
void CompositionNode::openShadersCodeEditor()
{
ensureShadersCodeEditor();
m_shadersCodeEditor->showWidget();
}
QString CompositionNode::name() const QString CompositionNode::name() const
{ {
return m_name; return m_name;

View File

@@ -5,11 +5,15 @@
#include "effectcomposeruniformsmodel.h" #include "effectcomposeruniformsmodel.h"
#include <utils/uniqueobjectptr.h>
#include <QJsonObject> #include <QJsonObject>
#include <QObject> #include <QObject>
namespace EffectComposer { namespace EffectComposer {
class EffectShadersCodeEditor;
class CompositionNode : public QObject class CompositionNode : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -18,6 +22,12 @@ class CompositionNode : public QObject
Q_PROPERTY(bool nodeEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged) Q_PROPERTY(bool nodeEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged)
Q_PROPERTY(bool isDependency READ isDependency NOTIFY isDepencyChanged) Q_PROPERTY(bool isDependency READ isDependency NOTIFY isDepencyChanged)
Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged) Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged)
Q_PROPERTY(
QString fragmentCode
READ fragmentCode
WRITE setFragmentCode
NOTIFY fragmentCodeChanged)
Q_PROPERTY(QString vertexCode READ vertexCode WRITE setVertexCode NOTIFY vertexCodeChanged)
public: public:
enum NodeType { enum NodeType {
@@ -27,6 +37,7 @@ public:
}; };
CompositionNode(const QString &effectName, const QString &qenPath, const QJsonObject &json = {}); CompositionNode(const QString &effectName, const QString &qenPath, const QJsonObject &json = {});
virtual ~CompositionNode();
QString fragmentCode() const; QString fragmentCode() const;
QString vertexCode() const; QString vertexCode() const;
@@ -54,14 +65,23 @@ public:
int extraMargin() const { return m_extraMargin; } int extraMargin() const { return m_extraMargin; }
void setFragmentCode(const QString &fragmentCode);
void setVertexCode(const QString &vertexCode);
void openShadersCodeEditor();
signals: signals:
void uniformsModelChanged(); void uniformsModelChanged();
void isEnabledChanged(); void isEnabledChanged();
void isDepencyChanged(); void isDepencyChanged();
void rebakeRequested(); void rebakeRequested();
void fragmentCodeChanged();
void vertexCodeChanged();
private: private:
void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json); void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json);
void ensureShadersCodeEditor();
void requestRebakeIfLiveUpdateMode();
QString m_name; QString m_name;
NodeType m_type = CustomNode; NodeType m_type = CustomNode;
@@ -77,6 +97,7 @@ private:
QList<Uniform *> m_uniforms; QList<Uniform *> m_uniforms;
EffectComposerUniformsModel m_unifomrsModel; EffectComposerUniformsModel m_unifomrsModel;
Utils::UniqueObjectLatePtr<EffectShadersCodeEditor> m_shadersCodeEditor;
}; };
} // namespace EffectComposer } // namespace EffectComposer

View File

@@ -0,0 +1,142 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "effectcodeeditorwidget.h"
#include <qmldesigner/textmodifier/indentingtexteditormodifier.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreplugintr.h>
#include <coreplugin/icore.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <qmljseditor/qmljsautocompleter.h>
#include <qmljseditor/qmljscompletionassist.h>
#include <qmljseditor/qmljshighlighter.h>
#include <qmljseditor/qmljshoverhandler.h>
#include <qmljseditor/qmljssemantichighlighter.h>
#include <qmljstools/qmljsindenter.h>
#include <utils/mimeconstants.h>
#include <utils/transientscroll.h>
#include <QAction>
namespace EffectComposer {
constexpr char EFFECTEDITOR_CONTEXT_ID[] = "EffectEditor.EffectEditorContext";
EffectCodeEditorWidget::EffectCodeEditorWidget()
: m_context(new Core::IContext(this))
{
Core::Context context(EFFECTEDITOR_CONTEXT_ID, ProjectExplorer::Constants::QMLJS_LANGUAGE_ID);
m_context->setWidget(this);
m_context->setContext(context);
Core::ICore::addContextObject(m_context);
Utils::TransientScrollAreaSupport::support(this);
/*
* We have to register our own active auto completion shortcut, because the original shortcut will
* use the cursor position of the original editor in the editor manager.
*/
m_completionAction = new QAction(tr("Trigger Completion"), this);
Core::Command *command = Core::ActionManager::registerAction(
m_completionAction, TextEditor::Constants::COMPLETE_THIS, context);
command->setDefaultKeySequence(QKeySequence(
Core::useMacShortcuts
? tr("Meta+Space")
: tr("Ctrl+Space")));
connect(m_completionAction, &QAction::triggered, this, [this] {
invokeAssist(TextEditor::Completion);
});
}
EffectCodeEditorWidget::~EffectCodeEditorWidget()
{
unregisterAutoCompletion();
}
void EffectCodeEditorWidget::unregisterAutoCompletion()
{
if (m_completionAction) {
Core::ActionManager::unregisterAction(m_completionAction, TextEditor::Constants::COMPLETE_THIS);
delete m_completionAction;
m_completionAction = nullptr;
}
}
void EffectCodeEditorWidget::setEditorTextWithIndentation(const QString &text)
{
auto *doc = document();
doc->setPlainText(text);
// We don't need to indent an empty text but is also needed for safer text.length()-1 below
if (text.isEmpty())
return;
auto modifier = std::make_unique<QmlDesigner::IndentingTextEditModifier>(doc, QTextCursor{doc});
modifier->indent(0, text.length()-1);
}
EffectDocument::EffectDocument()
: QmlJSEditor::QmlJSEditorDocument(EFFECTEDITOR_CONTEXT_ID)
, m_semanticHighlighter(new QmlJSEditor::SemanticHighlighter(this))
{}
EffectDocument::~EffectDocument()
{
delete m_semanticHighlighter;
}
void EffectDocument::applyFontSettings()
{
TextDocument::applyFontSettings();
m_semanticHighlighter->updateFontSettings(fontSettings());
if (!isSemanticInfoOutdated() && semanticInfo().isValid())
m_semanticHighlighter->rerun(semanticInfo());
}
void EffectDocument::triggerPendingUpdates()
{
TextDocument::triggerPendingUpdates(); // Calls applyFontSettings if necessary
if (!isSemanticInfoOutdated() && semanticInfo().isValid())
m_semanticHighlighter->rerun(semanticInfo());
}
EffectCodeEditorFactory::EffectCodeEditorFactory()
{
setId(EFFECTEDITOR_CONTEXT_ID);
setDisplayName(::Core::Tr::tr("Effect Code Editor"));
addMimeType(EFFECTEDITOR_CONTEXT_ID);
addMimeType(Utils::Constants::QML_MIMETYPE);
addMimeType(Utils::Constants::QMLTYPES_MIMETYPE);
addMimeType(Utils::Constants::JS_MIMETYPE);
setDocumentCreator([]() { return new EffectDocument; });
setEditorWidgetCreator([]() { return new EffectCodeEditorWidget; });
setEditorCreator([]() { return new QmlJSEditor::QmlJSEditor; });
setAutoCompleterCreator([]() { return new QmlJSEditor::AutoCompleter; });
setCommentDefinition(Utils::CommentDefinition::CppStyle);
setParenthesesMatchingEnabled(true);
setCodeFoldingSupported(true);
addHoverHandler(new QmlJSEditor::QmlJSHoverHandler);
setCompletionAssistProvider(new QmlJSEditor::QmlJSCompletionAssistProvider);
}
void EffectCodeEditorFactory::decorateEditor(TextEditor::TextEditorWidget *editor)
{
editor->textDocument()->resetSyntaxHighlighter(
[] { return new QmlJSEditor::QmlJSHighlighter(); });
editor->textDocument()->setIndenter(QmlJSEditor::createQmlJsIndenter(
editor->textDocument()->document()));
editor->setAutoCompleter(new QmlJSEditor::AutoCompleter);
}
} // namespace EffectComposer

View File

@@ -0,0 +1,63 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <qmljseditor/qmljseditor.h>
#include <qmljseditor/qmljseditordocument.h>
QT_FORWARD_DECLARE_CLASS(QAction)
namespace QmlJSEditor {
class SemanticHighlighter;
}
namespace Core {
class IContext;
}
namespace EffectComposer {
class EffectCodeEditorWidget : public QmlJSEditor::QmlJSEditorWidget
{
Q_OBJECT
public:
EffectCodeEditorWidget();
~EffectCodeEditorWidget() override;
void unregisterAutoCompletion();
void setEditorTextWithIndentation(const QString &text);
signals:
void returnKeyClicked();
public:
Core::IContext *m_context = nullptr;
QAction *m_completionAction = nullptr;
bool m_isMultiline = true;
};
class EffectDocument : public QmlJSEditor::QmlJSEditorDocument
{
public:
EffectDocument();
~EffectDocument();
protected:
void applyFontSettings() final;
void triggerPendingUpdates() final;
private:
QmlJSEditor::SemanticHighlighter *m_semanticHighlighter = nullptr;
};
class EffectCodeEditorFactory : public TextEditor::TextEditorFactory
{
public:
EffectCodeEditorFactory();
static void decorateEditor(TextEditor::TextEditorWidget *editor);
};
} // namespace EffectComposer

View File

@@ -168,11 +168,11 @@ int EffectComposerContextObject::devicePixelRatio()
QStringList EffectComposerContextObject::allStatesForId(const QString &id) QStringList EffectComposerContextObject::allStatesForId(const QString &id)
{ {
if (m_model && m_model->rewriterView()) { if (m_model) {
const QmlDesigner::QmlObjectNode node = m_model->rewriterView()->modelNodeForId(id); const QmlDesigner::QmlObjectNode node = m_model->modelNodeForId(id);
if (node.isValid()) if (node.isValid())
return node.allStateNames(); return node.allStateNames();
} }
return {}; return {};
} }

View File

@@ -4,27 +4,36 @@
#include "effectcomposermodel.h" #include "effectcomposermodel.h"
#include "compositionnode.h" #include "compositionnode.h"
#include "effectshaderscodeeditor.h"
#include "effectutils.h" #include "effectutils.h"
#include "propertyhandler.h" #include "propertyhandler.h"
#include "syntaxhighlighterdata.h" #include "syntaxhighlighterdata.h"
#include "uniform.h" #include "uniform.h"
#include <asset.h>
#include <designdocument.h>
#include <modelnodeoperations.h>
#include <qmldesignerplugin.h>
#include <uniquename.h>
#include <coreplugin/icore.h>
#include <projectexplorer/projecttree.h> #include <projectexplorer/projecttree.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtkitaspect.h>
#include <uniquename.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/qtcprocess.h> #include <utils/qtcprocess.h>
#include <modelnodeoperations.h>
#include <QByteArrayView> #include <QByteArrayView>
#include <QFileDialog>
#include <QLibraryInfo> #include <QLibraryInfo>
#include <QTemporaryDir> #include <QTemporaryDir>
#include <QVector2D> #include <QVector2D>
using namespace Qt::StringLiterals;
namespace EffectComposer { namespace EffectComposer {
enum class FileType enum class FileType
@@ -58,11 +67,12 @@ EffectComposerModel::EffectComposerModel(QObject *parent)
QHash<int, QByteArray> EffectComposerModel::roleNames() const QHash<int, QByteArray> EffectComposerModel::roleNames() const
{ {
QHash<int, QByteArray> roles; static const QHash<int, QByteArray> roles = {
roles[NameRole] = "nodeName"; {NameRole, "nodeName"},
roles[EnabledRole] = "nodeEnabled"; {EnabledRole, "nodeEnabled"},
roles[UniformsRole] = "nodeUniformsModel"; {UniformsRole, "nodeUniformsModel"},
roles[Dependency] = "isDependency"; {Dependency, "isDependency"},
};
return roles; return roles;
} }
@@ -206,6 +216,8 @@ void EffectComposerModel::clear(bool clearName)
if (clearName) { if (clearName) {
setCurrentComposition(""); setCurrentComposition("");
setCompositionPath(""); setCompositionPath("");
resetRootFragmentShader();
resetRootVertexShader();
} }
setHasUnsavedChanges(!m_currentComposition.isEmpty()); setHasUnsavedChanges(!m_currentComposition.isEmpty());
@@ -241,6 +253,47 @@ bool EffectComposerModel::nameExists(const QString &name) const
return QFile::exists(path.arg(name)); return QFile::exists(path.arg(name));
} }
void EffectComposerModel::chooseCustomPreviewImage()
{
QTimer::singleShot(0, this, [&]() {
using Utils::FilePath;
static FilePath lastDir;
const QStringList &suffixes = QmlDesigner::Asset::supportedImageSuffixes();
QmlDesigner::DesignDocument *document = QmlDesigner::QmlDesignerPlugin::instance()->currentDesignDocument();
const FilePath currentDir = lastDir.isEmpty() ? document->fileName().parentDir()
: lastDir;
const QStringList fileNames = QFileDialog::getOpenFileNames(Core::ICore::dialogParent(),
tr("Select custom effect background image"),
currentDir.toFSPathString(),
tr("Image Files (%1)").arg(suffixes.join(" ")));
if (!fileNames.isEmpty()) {
FilePath imageFile = FilePath::fromString(fileNames.first());
lastDir = imageFile.absolutePath();
if (imageFile.exists()) {
FilePath projDir = QmlDesigner::QmlDesignerPlugin::instance()->documentManager()
.currentProjectDirPath();
if (!imageFile.isChildOf(projDir)) {
FilePath imagesDir = QmlDesigner::ModelNodeOperations::getImagesDefaultDirectory();
FilePath targetFile = imagesDir.pathAppended(imageFile.fileName());
if (!targetFile.exists())
imageFile.copyFile(targetFile);
if (targetFile.exists())
imageFile = targetFile;
}
m_customPreviewImage = QUrl::fromLocalFile(imageFile.toFSPathString());
m_currentPreviewImage = m_customPreviewImage;
setHasUnsavedChanges(true);
emit currentPreviewImageChanged();
emit customPreviewImageChanged();
}
}
});
}
QString EffectComposerModel::fragmentShader() const QString EffectComposerModel::fragmentShader() const
{ {
return m_fragmentShader; return m_fragmentShader;
@@ -267,6 +320,40 @@ void EffectComposerModel::setVertexShader(const QString &newVertexShader)
m_vertexShader = newVertexShader; m_vertexShader = newVertexShader;
} }
void EffectComposerModel::setRootFragmentShader(const QString &shader)
{
m_rootFragmentShader = shader;
}
void EffectComposerModel::resetRootFragmentShader()
{
static const QString defaultRootFragmentShader = {
"void main() {\n"
" fragColor = texture(iSource, texCoord);\n"
" @nodes\n"
" fragColor = fragColor * qt_Opacity;\n"
"}\n"};
setRootFragmentShader(defaultRootFragmentShader);
}
void EffectComposerModel::setRootVertexShader(const QString &shader)
{
m_rootVertexShader = shader;
}
void EffectComposerModel::resetRootVertexShader()
{
static const QString defaultRootVertexShader = {
"void main() {\n"
" texCoord = qt_MultiTexCoord0;\n"
" fragCoord = qt_Vertex.xy;\n"
" vec2 vertCoord = qt_Vertex.xy;\n"
" @nodes\n"
" gl_Position = qt_Matrix * vec4(vertCoord, 0.0, 1.0);\n"
"}\n"};
setRootVertexShader(defaultRootVertexShader);
}
QString EffectComposerModel::qmlComponentString() const QString EffectComposerModel::qmlComponentString() const
{ {
return m_qmlComponentString; return m_qmlComponentString;
@@ -960,11 +1047,26 @@ void EffectComposerModel::saveComposition(const QString &name)
return; return;
} }
const Utils::FilePath compositionPath = Utils::FilePath::fromString(path);
const Utils::FilePath compositionDir = compositionPath.absolutePath();
updateExtraMargin(); updateExtraMargin();
QJsonObject json; QJsonObject json;
// File format version // File format version
json.insert("version", 1); json.insert("version", 1);
json.insert("tool", "EffectComposer");
Utils::FilePath customPreviewPath = Utils::FilePath::fromUrl(m_customPreviewImage);
if (m_customPreviewImage.isLocalFile())
customPreviewPath = customPreviewPath.relativePathFrom(compositionDir);
json.insert("customPreviewImage", customPreviewPath.toUrl().toString());
QUrl previewUrl = m_currentPreviewImage;
if (m_currentPreviewImage == m_customPreviewImage)
previewUrl = customPreviewPath.toUrl();
json.insert("previewImage", previewUrl.toString());
// Add nodes // Add nodes
QJsonArray nodesArray; QJsonArray nodesArray;
@@ -973,9 +1075,18 @@ void EffectComposerModel::saveComposition(const QString &name)
nodesArray.append(nodeObject); nodesArray.append(nodeObject);
} }
auto toJsonArray = [](const QString &code) -> QJsonArray {
if (code.isEmpty())
return {};
return QJsonArray::fromStringList(code.split('\n'));
};
if (!nodesArray.isEmpty()) if (!nodesArray.isEmpty())
json.insert("nodes", nodesArray); json.insert("nodes", nodesArray);
json.insert("vertexCode", toJsonArray(m_rootVertexShader));
json.insert("fragmentCode", toJsonArray(m_rootFragmentShader));
QJsonObject rootJson; QJsonObject rootJson;
rootJson.insert("QEP", json); rootJson.insert("QEP", json);
QJsonDocument jsonDoc(rootJson); QJsonDocument jsonDoc(rootJson);
@@ -984,20 +1095,60 @@ void EffectComposerModel::saveComposition(const QString &name)
saveFile.close(); saveFile.close();
setCurrentComposition(name); setCurrentComposition(name);
setCompositionPath(Utils::FilePath::fromString(path)); setCompositionPath(compositionPath);
saveResources(name); saveResources(name);
setHasUnsavedChanges(false); setHasUnsavedChanges(false);
} }
void EffectComposerModel::openShadersCodeEditor(int idx)
{
if (m_nodes.size() < idx || idx < 0)
return;
CompositionNode *node = m_nodes.at(idx);
node->openShadersCodeEditor();
}
void EffectComposerModel::openMainShadersCodeEditor()
{
if (!m_shadersCodeEditor) {
m_shadersCodeEditor = Utils::makeUniqueObjectLatePtr<EffectShadersCodeEditor>(
currentComposition());
m_shadersCodeEditor->setFragmentValue(m_rootFragmentShader);
m_shadersCodeEditor->setVertexValue(m_rootVertexShader);
connect(m_shadersCodeEditor.get(), &EffectShadersCodeEditor::vertexValueChanged, this, [this] {
setRootVertexShader(m_shadersCodeEditor->vertexValue());
setHasUnsavedChanges(true);
rebakeIfLiveUpdateMode();
});
connect(
m_shadersCodeEditor.get(), &EffectShadersCodeEditor::fragmentValueChanged, this, [this] {
setRootFragmentShader(m_shadersCodeEditor->fragmentValue());
setHasUnsavedChanges(true);
rebakeIfLiveUpdateMode();
});
connect(
m_shadersCodeEditor.get(),
&EffectShadersCodeEditor::rebakeRequested,
this,
&EffectComposerModel::startRebakeTimer);
}
m_shadersCodeEditor->showWidget();
}
void EffectComposerModel::openComposition(const QString &path) void EffectComposerModel::openComposition(const QString &path)
{ {
clear(true); clear(true);
const QString effectName = QFileInfo(path).baseName(); Utils::FilePath effectPath = Utils::FilePath::fromString(path);
const QString effectName = effectPath.baseName();
setCurrentComposition(effectName); setCurrentComposition(effectName);
setCompositionPath(Utils::FilePath::fromString(path)); setCompositionPath(effectPath);
QFile compFile(path); QFile compFile(path);
if (!compFile.open(QIODevice::ReadOnly)) { if (!compFile.open(QIODevice::ReadOnly)) {
@@ -1030,6 +1181,18 @@ void EffectComposerModel::openComposition(const QString &path)
QJsonObject json = rootJson["QEP"].toObject(); QJsonObject json = rootJson["QEP"].toObject();
const QString toolName = json.contains("tool") ? json["tool"].toString()
: json.contains("QQEM") ? "QQEM"_L1
: ""_L1;
if (!toolName.isEmpty() && toolName != "EffectComposer") {
const QString error
= tr("Error: '%1' effects are not compatible with 'Effect Composer'").arg(toolName);
qWarning() << error;
setEffectError(error);
return;
}
int version = -1; int version = -1;
if (json.contains("version")) if (json.contains("version"))
version = json["version"].toInt(-1); version = json["version"].toInt(-1);
@@ -1041,6 +1204,63 @@ void EffectComposerModel::openComposition(const QString &path)
return; return;
} }
auto toCodeBlock = [](const QJsonValue &jsonValue) -> QString {
if (!jsonValue.isArray())
return {};
QString code;
const QJsonArray array = jsonValue.toArray();
for (const QJsonValue &lineValue : array) {
if (lineValue.isString())
code += lineValue.toString() + '\n';
}
return code;
};
if (json.contains("vertexCode"))
setRootVertexShader(toCodeBlock(json["vertexCode"]));
else
resetRootVertexShader();
if (json.contains("fragmentCode"))
setRootFragmentShader(toCodeBlock(json["fragmentCode"]));
else
resetRootFragmentShader();
m_currentPreviewImage.clear();
if (json.contains("previewImage")) {
const QString imageStr = json["previewImage"].toString();
if (!imageStr.isEmpty()) {
const QUrl imageUrl{imageStr};
Utils::FilePath imagePath = Utils::FilePath::fromUrl(imageUrl);
if (imageStr.startsWith("images/preview")) { // built-in preview image
m_currentPreviewImage = imageUrl;
} else if (imagePath.isAbsolutePath()) {
if (imagePath.exists())
m_currentPreviewImage = imageUrl;
} else {
imagePath = effectPath.absolutePath().resolvePath(imagePath);
if (imagePath.exists())
m_currentPreviewImage = imagePath.toUrl();
}
}
}
m_customPreviewImage.clear();
if (json.contains("customPreviewImage")) {
QUrl imageUrl{json["customPreviewImage"].toString()};
Utils::FilePath imagePath = Utils::FilePath::fromUrl(imageUrl);
if (imagePath.isAbsolutePath()) {
if (imagePath.exists())
m_customPreviewImage = imageUrl;
} else {
imagePath = effectPath.absolutePath().resolvePath(imagePath);
if (imagePath.exists())
m_customPreviewImage = imagePath.toUrl();
}
}
if (json.contains("nodes") && json["nodes"].isArray()) { if (json.contains("nodes") && json["nodes"].isArray()) {
beginResetModel(); beginResetModel();
QHash<QString, int> refCounts; QHash<QString, int> refCounts;
@@ -1069,6 +1289,8 @@ void EffectComposerModel::openComposition(const QString &path)
setHasUnsavedChanges(false); setHasUnsavedChanges(false);
emit nodesChanged(); emit nodesChanged();
emit currentPreviewImageChanged();
emit customPreviewImageChanged();
} }
void EffectComposerModel::saveResources(const QString &name) void EffectComposerModel::saveResources(const QString &name)
@@ -1354,8 +1576,6 @@ QString EffectComposerModel::valueAsVariable(const Uniform &uniform)
// Return name for the image property Image element // Return name for the image property Image element
QString EffectComposerModel::getImageElementName(const Uniform &uniform, bool localFiles) QString EffectComposerModel::getImageElementName(const Uniform &uniform, bool localFiles)
{ {
if (localFiles && uniform.value().toString().isEmpty())
return QStringLiteral("null");
QString simplifiedName = uniform.name().simplified(); QString simplifiedName = uniform.name().simplified();
simplifiedName = simplifiedName.remove(' '); simplifiedName = simplifiedName.remove(' ');
return QStringLiteral("imageItem") + simplifiedName; return QStringLiteral("imageItem") + simplifiedName;
@@ -1439,32 +1659,6 @@ QString EffectComposerModel::processFragmentRootLine(const QString &line)
return output; return output;
} }
QStringList EffectComposerModel::getDefaultRootVertexShader()
{
if (m_defaultRootVertexShader.isEmpty()) {
m_defaultRootVertexShader << "void main() {";
m_defaultRootVertexShader << " texCoord = qt_MultiTexCoord0;";
m_defaultRootVertexShader << " fragCoord = qt_Vertex.xy;";
m_defaultRootVertexShader << " vec2 vertCoord = qt_Vertex.xy;";
m_defaultRootVertexShader << " @nodes";
m_defaultRootVertexShader << " gl_Position = qt_Matrix * vec4(vertCoord, 0.0, 1.0);";
m_defaultRootVertexShader << "}";
}
return m_defaultRootVertexShader;
}
QStringList EffectComposerModel::getDefaultRootFragmentShader()
{
if (m_defaultRootFragmentShader.isEmpty()) {
m_defaultRootFragmentShader << "void main() {";
m_defaultRootFragmentShader << " fragColor = texture(iSource, texCoord);";
m_defaultRootFragmentShader << " @nodes";
m_defaultRootFragmentShader << " fragColor = fragColor * qt_Opacity;";
m_defaultRootFragmentShader << "}";
}
return m_defaultRootFragmentShader;
}
// Remove all post-processing tags ("@tag") from the code. // Remove all post-processing tags ("@tag") from the code.
// Except "@nodes" tag as that is handled later. // Except "@nodes" tag as that is handled later.
QStringList EffectComposerModel::removeTagsFromCode(const QStringList &codeLines) QStringList EffectComposerModel::removeTagsFromCode(const QStringList &codeLines)
@@ -1527,7 +1721,7 @@ QString EffectComposerModel::generateVertexShader(bool includeUniforms)
// split to root and main parts // split to root and main parts
QString s_root; QString s_root;
QString s_main; QString s_main;
QStringList s_sourceCode; QStringList s_sourceCode = m_rootVertexShader.split('\n');
m_shaderVaryingVariables.clear(); m_shaderVaryingVariables.clear();
for (const CompositionNode *n : std::as_const(m_nodes)) { for (const CompositionNode *n : std::as_const(m_nodes)) {
if (!n->vertexCode().isEmpty() && n->isEnabled()) { if (!n->vertexCode().isEmpty() && n->isEnabled()) {
@@ -1544,11 +1738,6 @@ QString EffectComposerModel::generateVertexShader(bool includeUniforms)
} }
} }
if (s_sourceCode.isEmpty()) {
// If source nodes doesn't contain any code, use default one
s_sourceCode << getDefaultRootVertexShader();
}
if (removeTags) { if (removeTags) {
s_sourceCode = removeTagsFromCode(s_sourceCode); s_sourceCode = removeTagsFromCode(s_sourceCode);
s_root = removeTagsFromCode(s_root); s_root = removeTagsFromCode(s_root);
@@ -1583,7 +1772,7 @@ QString EffectComposerModel::generateFragmentShader(bool includeUniforms)
// split to root and main parts // split to root and main parts
QString s_root; QString s_root;
QString s_main; QString s_main;
QStringList s_sourceCode; QStringList s_sourceCode = m_rootFragmentShader.split('\n');
for (const CompositionNode *n : std::as_const(m_nodes)) { for (const CompositionNode *n : std::as_const(m_nodes)) {
if (!n->fragmentCode().isEmpty() && n->isEnabled()) { if (!n->fragmentCode().isEmpty() && n->isEnabled()) {
const QStringList fragmentCode = n->fragmentCode().split('\n'); const QStringList fragmentCode = n->fragmentCode().split('\n');
@@ -1599,11 +1788,6 @@ QString EffectComposerModel::generateFragmentShader(bool includeUniforms)
} }
} }
if (s_sourceCode.isEmpty()) {
// If source nodes doesn't contain any code, use default one
s_sourceCode << getDefaultRootFragmentShader();
}
if (removeTags) { if (removeTags) {
s_sourceCode = removeTagsFromCode(s_sourceCode); s_sourceCode = removeTagsFromCode(s_sourceCode);
s_root = removeTagsFromCode(s_root); s_root = removeTagsFromCode(s_root);
@@ -1828,7 +2012,6 @@ void EffectComposerModel::bakeShaders()
runQsb(qsbPath, outPaths, false); runQsb(qsbPath, outPaths, false);
runQsb(qsbPrevPath, outPrevPaths, true); runQsb(qsbPrevPath, outPrevPaths, true);
} }
bool EffectComposerModel::shadersUpToDate() const bool EffectComposerModel::shadersUpToDate() const
@@ -1877,11 +2060,7 @@ QString EffectComposerModel::getQmlImagesString(bool localFiles)
for (Uniform *uniform : uniforms) { for (Uniform *uniform : uniforms) {
if (uniform->type() == Uniform::Type::Sampler) { if (uniform->type() == Uniform::Type::Sampler) {
QString imagePath = uniform->value().toString(); QString imagePath = uniform->value().toString();
// For preview, generate image element even if path is empty, as changing uniform values
// will not trigger qml code regeneration
if (localFiles) { if (localFiles) {
if (imagePath.isEmpty())
continue;
QFileInfo fi(imagePath); QFileInfo fi(imagePath);
imagePath = fi.fileName(); imagePath = fi.fileName();
imagesString += QString(" property url %1Url: \"%2\"\n") imagesString += QString(" property url %1Url: \"%2\"\n")
@@ -2003,14 +2182,16 @@ QString EffectComposerModel::getQmlComponentString(bool localFiles)
void EffectComposerModel::connectCompositionNode(CompositionNode *node) void EffectComposerModel::connectCompositionNode(CompositionNode *node)
{ {
connect(qobject_cast<EffectComposerUniformsModel *>(node->uniformsModel()), auto setUnsaved = std::bind(&EffectComposerModel::setHasUnsavedChanges, this, true);
&EffectComposerUniformsModel::dataChanged, this, [this] { connect(
setHasUnsavedChanges(true); qobject_cast<EffectComposerUniformsModel *>(node->uniformsModel()),
}); &EffectComposerUniformsModel::dataChanged,
connect(node, &CompositionNode::rebakeRequested, this, [this] { this,
// This can come multiple times in a row in response to property changes, so let's buffer it setUnsaved);
m_rebakeTimer.start(200);
}); connect(node, &CompositionNode::rebakeRequested, this, &EffectComposerModel::startRebakeTimer);
connect(node, &CompositionNode::fragmentCodeChanged, this, setUnsaved);
connect(node, &CompositionNode::vertexCodeChanged, this, setUnsaved);
} }
void EffectComposerModel::updateExtraMargin() void EffectComposerModel::updateExtraMargin()
@@ -2020,6 +2201,18 @@ void EffectComposerModel::updateExtraMargin()
m_extraMargin = qMax(node->extraMargin(), m_extraMargin); m_extraMargin = qMax(node->extraMargin(), m_extraMargin);
} }
void EffectComposerModel::startRebakeTimer()
{
// This can come multiple times in a row in response to property changes, so let's buffer it
m_rebakeTimer.start(200);
}
void EffectComposerModel::rebakeIfLiveUpdateMode()
{
if (m_shadersCodeEditor && m_shadersCodeEditor->liveUpdate())
startRebakeTimer();
}
QSet<QByteArray> EffectComposerModel::getExposedProperties(const QByteArray &qmlContent) QSet<QByteArray> EffectComposerModel::getExposedProperties(const QByteArray &qmlContent)
{ {
QSet<QByteArray> returnSet; QSet<QByteArray> returnSet;
@@ -2051,6 +2244,30 @@ void EffectComposerModel::setCurrentComposition(const QString &newCurrentComposi
m_currentComposition = newCurrentComposition; m_currentComposition = newCurrentComposition;
emit currentCompositionChanged(); emit currentCompositionChanged();
m_shadersCodeEditor.reset();
}
QUrl EffectComposerModel::customPreviewImage() const
{
return m_customPreviewImage;
}
QUrl EffectComposerModel::currentPreviewImage() const
{
return m_currentPreviewImage;
}
void EffectComposerModel::setCurrentPreviewImage(const QUrl &path)
{
if (m_currentPreviewImage == path)
return;
if (!m_nodes.isEmpty())
setHasUnsavedChanges(true);
m_currentPreviewImage = path;
emit currentPreviewImageChanged();
} }
Utils::FilePath EffectComposerModel::compositionPath() const Utils::FilePath EffectComposerModel::compositionPath() const

View File

@@ -6,6 +6,7 @@
#include "shaderfeatures.h" #include "shaderfeatures.h"
#include <utils/filepath.h> #include <utils/filepath.h>
#include <utils/uniqueobjectptr.h>
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
@@ -14,6 +15,7 @@
#include <QSet> #include <QSet>
#include <QTemporaryDir> #include <QTemporaryDir>
#include <QTimer> #include <QTimer>
#include <QUrl>
namespace ProjectExplorer { namespace ProjectExplorer {
class Target; class Target;
@@ -26,6 +28,7 @@ class Process;
namespace EffectComposer { namespace EffectComposer {
class CompositionNode; class CompositionNode;
class EffectShadersCodeEditor;
class Uniform; class Uniform;
struct EffectError { struct EffectError {
@@ -51,6 +54,8 @@ class EffectComposerModel : public QAbstractListModel
Q_PROPERTY(bool isEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged) Q_PROPERTY(bool isEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged)
Q_PROPERTY(bool hasValidTarget READ hasValidTarget WRITE setHasValidTarget NOTIFY hasValidTargetChanged) Q_PROPERTY(bool hasValidTarget READ hasValidTarget WRITE setHasValidTarget NOTIFY hasValidTargetChanged)
Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged) Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged)
Q_PROPERTY(QUrl currentPreviewImage READ currentPreviewImage WRITE setCurrentPreviewImage NOTIFY currentPreviewImageChanged)
Q_PROPERTY(QUrl customPreviewImage READ customPreviewImage NOTIFY customPreviewImageChanged)
public: public:
EffectComposerModel(QObject *parent = nullptr); EffectComposerModel(QObject *parent = nullptr);
@@ -75,6 +80,7 @@ public:
Q_INVOKABLE void assignToSelected(); Q_INVOKABLE void assignToSelected();
Q_INVOKABLE QString getUniqueEffectName() const; Q_INVOKABLE QString getUniqueEffectName() const;
Q_INVOKABLE bool nameExists(const QString &name) const; Q_INVOKABLE bool nameExists(const QString &name) const;
Q_INVOKABLE void chooseCustomPreviewImage();
bool shadersUpToDate() const; bool shadersUpToDate() const;
void setShadersUpToDate(bool newShadersUpToDate); void setShadersUpToDate(bool newShadersUpToDate);
@@ -91,6 +97,12 @@ public:
QString vertexShader() const; QString vertexShader() const;
void setVertexShader(const QString &newVertexShader); void setVertexShader(const QString &newVertexShader);
void setRootFragmentShader(const QString &shader);
void resetRootFragmentShader();
void setRootVertexShader(const QString &shader);
void resetRootVertexShader();
Q_INVOKABLE QString qmlComponentString() const; Q_INVOKABLE QString qmlComponentString() const;
Q_INVOKABLE void updateQmlComponent(); Q_INVOKABLE void updateQmlComponent();
@@ -100,11 +112,18 @@ public:
Q_INVOKABLE void saveComposition(const QString &name); Q_INVOKABLE void saveComposition(const QString &name);
Q_INVOKABLE void openShadersCodeEditor(int idx);
Q_INVOKABLE void openMainShadersCodeEditor();
void openComposition(const QString &path); void openComposition(const QString &path);
QString currentComposition() const; QString currentComposition() const;
void setCurrentComposition(const QString &newCurrentComposition); void setCurrentComposition(const QString &newCurrentComposition);
QUrl customPreviewImage() const;
QUrl currentPreviewImage() const;
void setCurrentPreviewImage(const QUrl &path);
Utils::FilePath compositionPath() const; Utils::FilePath compositionPath() const;
void setCompositionPath(const Utils::FilePath &newCompositionPath); void setCompositionPath(const Utils::FilePath &newCompositionPath);
@@ -129,6 +148,8 @@ signals:
void hasUnsavedChangesChanged(); void hasUnsavedChangesChanged();
void assignToSelectedTriggered(const QString &effectPath); void assignToSelectedTriggered(const QString &effectPath);
void removePropertiesFromScene(QSet<QByteArray> props, const QString &typeName); void removePropertiesFromScene(QSet<QByteArray> props, const QString &typeName);
void currentPreviewImageChanged();
void customPreviewImageChanged();
private: private:
enum Roles { enum Roles {
@@ -167,8 +188,6 @@ private:
int getTagIndex(const QStringList &code, const QString &tag); int getTagIndex(const QStringList &code, const QString &tag);
QString processVertexRootLine(const QString &line); QString processVertexRootLine(const QString &line);
QString processFragmentRootLine(const QString &line); QString processFragmentRootLine(const QString &line);
QStringList getDefaultRootVertexShader();
QStringList getDefaultRootFragmentShader();
QStringList removeTagsFromCode(const QStringList &codeLines); QStringList removeTagsFromCode(const QStringList &codeLines);
QString removeTagsFromCode(const QString &code); QString removeTagsFromCode(const QString &code);
QString getCustomShaderVaryings(bool outState); QString getCustomShaderVaryings(bool outState);
@@ -190,6 +209,8 @@ private:
void connectCompositionNode(CompositionNode *node); void connectCompositionNode(CompositionNode *node);
void updateExtraMargin(); void updateExtraMargin();
void startRebakeTimer();
void rebakeIfLiveUpdateMode();
QSet<QByteArray> getExposedProperties(const QByteArray &qmlContent); QSet<QByteArray> getExposedProperties(const QByteArray &qmlContent);
QList<CompositionNode *> m_nodes; QList<CompositionNode *> m_nodes;
@@ -205,8 +226,8 @@ private:
QStringList m_shaderVaryingVariables; QStringList m_shaderVaryingVariables;
QString m_fragmentShader; QString m_fragmentShader;
QString m_vertexShader; QString m_vertexShader;
QStringList m_defaultRootVertexShader; QString m_rootVertexShader;
QStringList m_defaultRootFragmentShader; QString m_rootFragmentShader;
// Temp files to store shaders sources and binary data // Temp files to store shaders sources and binary data
QTemporaryDir m_shaderDir; QTemporaryDir m_shaderDir;
QString m_fragmentSourceFilename; QString m_fragmentSourceFilename;
@@ -230,6 +251,9 @@ private:
int m_extraMargin = 0; int m_extraMargin = 0;
QString m_effectTypePrefix; QString m_effectTypePrefix;
Utils::FilePath m_compositionPath; Utils::FilePath m_compositionPath;
Utils::UniqueObjectLatePtr<EffectShadersCodeEditor> m_shadersCodeEditor;
QUrl m_currentPreviewImage;
QUrl m_customPreviewImage;
const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); const QRegularExpression m_spaceReg = QRegularExpression("\\s+");
}; };

View File

@@ -6,6 +6,7 @@
#include "effectcomposermodel.h" #include "effectcomposermodel.h"
#include "effectcomposernodesmodel.h" #include "effectcomposernodesmodel.h"
#include "effectcomposerwidget.h" #include "effectcomposerwidget.h"
#include "studioquickwidget.h"
#include <designermcumanager.h> #include <designermcumanager.h>
#include <documentmanager.h> #include <documentmanager.h>
@@ -218,4 +219,27 @@ void EffectComposerView::removeUnusedEffectImports()
} }
} }
void EffectComposerView::highlightSupportedProperties(bool highlight, const QString &suffix)
{
QQmlContext *ctxObj = m_widget->quickWidget()->rootContext();
ctxObj->setContextProperty("activeDragSuffix", suffix);
ctxObj->setContextProperty("hasActiveDrag", highlight);
}
void EffectComposerView::dragStarted(QMimeData *mimeData)
{
if (mimeData->hasFormat(QmlDesigner::Constants::MIME_TYPE_ASSETS)) {
QString format = mimeData->formats()[0];
const QString assetPath = QString::fromUtf8(mimeData->data(format)).split(',')[0];
const QString suffix = "*." + assetPath.split('.').last().toLower();
highlightSupportedProperties(true, suffix);
}
}
void EffectComposerView::dragEnded()
{
highlightSupportedProperties(false);
}
} // namespace EffectComposer } // namespace EffectComposer

View File

@@ -31,6 +31,11 @@ public:
const QList<QmlDesigner::ModelNode> &lastSelectedNodeList) override; const QList<QmlDesigner::ModelNode> &lastSelectedNodeList) override;
void nodeAboutToBeRemoved(const QmlDesigner::ModelNode &removedNode) override; void nodeAboutToBeRemoved(const QmlDesigner::ModelNode &removedNode) override;
void dragStarted(QMimeData *mimeData) override;
void dragEnded() override;
void highlightSupportedProperties(bool highlight, const QString &suffix = {});
private: private:
void customNotification(const AbstractView *view, const QString &identifier, void customNotification(const AbstractView *view, const QString &identifier,
const QList<QmlDesigner::ModelNode> &nodeList, const QList<QVariant> &data) override; const QList<QmlDesigner::ModelNode> &nodeList, const QList<QVariant> &data) override;

View File

@@ -0,0 +1,226 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "effectshaderscodeeditor.h"
#include "effectcodeeditorwidget.h"
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
#include <componentcore/designeractionmanager.h>
#include <componentcore/designericons.h>
#include <componentcore/theme.h>
#include <qmldesigner/qmldesignerplugin.h>
#include <qmljseditor/qmljseditor.h>
#include <qmljseditor/qmljseditordocument.h>
#include <qmldesignerplugin.h>
#include <QPlainTextEdit>
#include <QSettings>
#include <QTabWidget>
#include <QToolBar>
#include <QVBoxLayout>
namespace {
using IconId = QmlDesigner::DesignerIcons::IconId;
inline constexpr char EFFECTCOMPOSER_LIVE_UPDATE_KEY[] = "EffectComposer/CodeEditor/LiveUpdate";
QIcon toolbarIcon(IconId iconId)
{
return QmlDesigner::DesignerActionManager::instance().toolbarIcon(iconId);
};
} // namespace
namespace EffectComposer {
EffectShadersCodeEditor::EffectShadersCodeEditor(const QString &title, QWidget *parent)
: QWidget(parent)
, m_settings(new QSettings(qApp->organizationName(), qApp->applicationName(), this))
{
setWindowFlag(Qt::Tool, true);
setWindowTitle(title);
m_fragmentEditor = createJSEditor();
m_vertexEditor = createJSEditor();
connect(
m_fragmentEditor,
&QPlainTextEdit::textChanged,
this,
&EffectShadersCodeEditor::fragmentValueChanged);
connect(
m_vertexEditor,
&QPlainTextEdit::textChanged,
this,
&EffectShadersCodeEditor::vertexValueChanged);
setupUIComponents();
}
EffectShadersCodeEditor::~EffectShadersCodeEditor()
{
m_fragmentEditor->deleteLater();
m_vertexEditor->deleteLater();
}
void EffectShadersCodeEditor::showWidget()
{
readAndApplyLiveUpdateSettings();
show();
raise();
m_vertexEditor->setFocus();
}
void EffectShadersCodeEditor::showWidget(int x, int y)
{
showWidget();
move(QPoint(x, y));
}
QString EffectShadersCodeEditor::fragmentValue() const
{
if (!m_fragmentEditor)
return {};
return m_fragmentEditor->document()->toPlainText();
}
void EffectShadersCodeEditor::setFragmentValue(const QString &text)
{
if (m_fragmentEditor)
m_fragmentEditor->setEditorTextWithIndentation(text);
}
QString EffectShadersCodeEditor::vertexValue() const
{
if (!m_vertexEditor)
return {};
return m_vertexEditor->document()->toPlainText();
}
void EffectShadersCodeEditor::setVertexValue(const QString &text)
{
if (m_vertexEditor)
m_vertexEditor->setEditorTextWithIndentation(text);
}
bool EffectShadersCodeEditor::liveUpdate() const
{
return m_liveUpdate;
}
void EffectShadersCodeEditor::setLiveUpdate(bool liveUpdate)
{
if (m_liveUpdate == liveUpdate)
return;
m_liveUpdate = liveUpdate;
writeLiveUpdateSettings();
emit liveUpdateChanged(m_liveUpdate);
if (m_liveUpdate)
emit rebakeRequested();
}
EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor()
{
static EffectCodeEditorFactory f;
TextEditor::BaseTextEditor *editor = qobject_cast<TextEditor::BaseTextEditor *>(
f.createEditor());
Q_ASSERT(editor);
editor->setParent(this);
EffectCodeEditorWidget *editorWidget = qobject_cast<EffectCodeEditorWidget *>(
editor->editorWidget());
Q_ASSERT(editorWidget);
editorWidget->setLineNumbersVisible(false);
editorWidget->setMarksVisible(false);
editorWidget->setCodeFoldingSupported(false);
editorWidget->setTabChangesFocus(true);
editorWidget->unregisterAutoCompletion();
editorWidget->setParent(this);
editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
return editorWidget;
}
void EffectShadersCodeEditor::setupUIComponents()
{
QVBoxLayout *verticalLayout = new QVBoxLayout(this);
QTabWidget *tabWidget = new QTabWidget(this);
tabWidget->addTab(m_fragmentEditor, tr("Fragment Shader"));
tabWidget->addTab(m_vertexEditor, tr("Vertex Shader"));
verticalLayout->setContentsMargins(0, 0, 0, 0);
verticalLayout->addWidget(createToolbar());
verticalLayout->addWidget(tabWidget);
this->resize(660, 240);
}
void EffectShadersCodeEditor::closeEvent(QCloseEvent *event)
{
QWidget::closeEvent(event);
if (!liveUpdate())
emit rebakeRequested();
}
void EffectShadersCodeEditor::writeLiveUpdateSettings()
{
m_settings->setValue(EFFECTCOMPOSER_LIVE_UPDATE_KEY, m_liveUpdate);
}
void EffectShadersCodeEditor::readAndApplyLiveUpdateSettings()
{
bool liveUpdateStatus = m_settings->value(EFFECTCOMPOSER_LIVE_UPDATE_KEY, false).toBool();
setLiveUpdate(liveUpdateStatus);
}
QToolBar *EffectShadersCodeEditor::createToolbar()
{
using QmlDesigner::Theme;
QToolBar *toolbar = new QToolBar(this);
toolbar->setFixedHeight(Theme::toolbarSize());
toolbar->setFloatable(false);
toolbar->setContentsMargins(0, 0, 0, 0);
toolbar->setStyleSheet(Theme::replaceCssColors(
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
QAction *liveUpdateButton
= toolbar->addAction(toolbarIcon(IconId::LiveUpdateIcon), tr("Live Update"));
liveUpdateButton->setCheckable(true);
connect(liveUpdateButton, &QAction::toggled, this, &EffectShadersCodeEditor::setLiveUpdate);
QAction *applyAction = toolbar->addAction(toolbarIcon(IconId::SyncIcon), tr("Apply"));
connect(applyAction, &QAction::triggered, this, &EffectShadersCodeEditor::rebakeRequested);
auto syncLive = [liveUpdateButton, applyAction](bool liveState) {
liveUpdateButton->setChecked(liveState);
applyAction->setDisabled(liveState);
};
connect(this, &EffectShadersCodeEditor::liveUpdateChanged, this, syncLive);
syncLive(liveUpdate());
toolbar->addAction(liveUpdateButton);
toolbar->addAction(applyAction);
return toolbar;
}
} // namespace EffectComposer

View File

@@ -0,0 +1,61 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <texteditor/texteditor.h>
QT_FORWARD_DECLARE_CLASS(QSettings)
QT_FORWARD_DECLARE_CLASS(QToolBar)
namespace EffectComposer {
class EffectCodeEditorWidget;
class EffectShadersCodeEditor : public QWidget
{
Q_OBJECT
Q_PROPERTY(bool liveUpdate READ liveUpdate WRITE setLiveUpdate NOTIFY liveUpdateChanged)
public:
EffectShadersCodeEditor(const QString &title = tr("Untitled Editor"), QWidget *parent = nullptr);
~EffectShadersCodeEditor() override;
void showWidget();
void showWidget(int x, int y);
QString fragmentValue() const;
void setFragmentValue(const QString &text);
QString vertexValue() const;
void setVertexValue(const QString &text);
bool liveUpdate() const;
void setLiveUpdate(bool liveUpdate);
signals:
void liveUpdateChanged(bool);
void fragmentValueChanged();
void vertexValueChanged();
void rebakeRequested();
protected:
using QWidget::show;
EffectCodeEditorWidget *createJSEditor();
void setupUIComponents();
void closeEvent(QCloseEvent *event) override;
private:
void writeLiveUpdateSettings();
void readAndApplyLiveUpdateSettings();
QToolBar *createToolbar();
QSettings *m_settings = nullptr;
QPointer<EffectCodeEditorWidget> m_fragmentEditor;
QPointer<EffectCodeEditorWidget> m_vertexEditor;
bool m_liveUpdate = false;
};
} // namespace EffectComposer

View File

@@ -57,6 +57,14 @@ Uniform::Uniform(const QString &effectName, const QJsonObject &propObj, const QS
m_backendValue = new QmlDesigner::PropertyEditorValue(this); m_backendValue = new QmlDesigner::PropertyEditorValue(this);
m_backendValue->setValue(value); m_backendValue->setValue(value);
connect(m_backendValue, &QmlDesigner::PropertyEditorValue::dropCommitted,
this, [this](const QString &dropData) {
m_backendValue->setValue(dropData);
auto *model = QmlDesigner::QmlDesignerPlugin::instance()
->currentDesignDocument()->currentModel();
model->endDrag();
});
} }
Uniform::Type Uniform::type() const Uniform::Type Uniform::type() const

View File

@@ -7,9 +7,10 @@
#include <auxiliarydataproperties.h> #include <auxiliarydataproperties.h>
#include <externaldependenciesinterface.h> #include <externaldependenciesinterface.h>
#include <plaintexteditmodifier.h> #include <plaintexteditmodifier.h>
#include <qmldesignerplugin.h>
#include <qmldesignerprojectmanager.h>
#include <rewriterview.h> #include <rewriterview.h>
#include <signalhandlerproperty.h> #include <signalhandlerproperty.h>
#include <qmldesignerplugin.h>
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <projectexplorer/projectmanager.h> #include <projectexplorer/projectmanager.h>
@@ -192,7 +193,9 @@ Qt::CheckState checkState(const std::vector<std::string> &a, const std::vector<s
struct ModelBuilder struct ModelBuilder
{ {
ModelBuilder(const QString &filePath, ExternalDependenciesInterface &externalDependencies) ModelBuilder(const QString &filePath,
ExternalDependenciesInterface &externalDependencies,
[[maybe_unused]] ProjectStorageDependencies projectStorageDependencies)
{ {
const QString fileContent = fileToString(filePath); const QString fileContent = fileToString(filePath);
if (fileContent.isEmpty()) { if (fileContent.isEmpty()) {
@@ -209,7 +212,14 @@ struct ModelBuilder
rewriter->setCheckLinkErrors(false); rewriter->setCheckLinkErrors(false);
rewriter->setTextModifier(modifier.get()); rewriter->setTextModifier(modifier.get());
#ifdef QDS_USE_PROJECTSTORAGE
model = QmlDesigner::Model::create(projectStorageDependencies,
"Item",
{Import::createLibraryImport("QtQuick")},
filePath);
#else
model = QmlDesigner::Model::create("QtQuick.Item", 2, 1); model = QmlDesigner::Model::create("QtQuick.Item", 2, 1);
#endif
model->setRewriterView(rewriter.get()); model->setRewriterView(rewriter.get());
} }
@@ -221,9 +231,12 @@ struct ModelBuilder
} // namespace } // namespace
InsightModel::InsightModel(InsightView *view, ExternalDependenciesInterface &externalDependencies) InsightModel::InsightModel(InsightView *view,
ExternalDependenciesInterface &externalDependencies,
QmlDesignerProjectManager &projectManager)
: m_insightView(view) : m_insightView(view)
, m_externalDependencies(externalDependencies) , m_externalDependencies(externalDependencies)
, m_projectManager(projectManager)
, m_fileSystemWatcher(new Utils::FileSystemWatcher(this)) , m_fileSystemWatcher(new Utils::FileSystemWatcher(this))
{ {
QObject::connect(ProjectExplorer::ProjectManager::instance(), QObject::connect(ProjectExplorer::ProjectManager::instance(),
@@ -446,7 +459,9 @@ void InsightModel::setEnabled(bool value)
return; return;
} }
ModelBuilder builder(m_mainQmlInfo.absoluteFilePath(), m_externalDependencies); ModelBuilder builder(m_mainQmlInfo.absoluteFilePath(),
m_externalDependencies,
m_projectManager.projectStorageDependencies());
if (!builder.model) { if (!builder.model) {
qWarning() << "Could not create model" << m_mainQmlInfo.absoluteFilePath(); qWarning() << "Could not create model" << m_mainQmlInfo.absoluteFilePath();
@@ -613,7 +628,9 @@ int InsightModel::devicePixelRatio()
void InsightModel::parseMainQml() void InsightModel::parseMainQml()
{ {
ModelBuilder builder(m_mainQmlInfo.absoluteFilePath(), m_externalDependencies); ModelBuilder builder(m_mainQmlInfo.absoluteFilePath(),
m_externalDependencies,
m_projectManager.projectStorageDependencies());
if (!builder.model) if (!builder.model)
return; return;

View File

@@ -39,7 +39,9 @@ class InsightModel : public QAbstractListModel
}; };
public: public:
InsightModel(InsightView *view, class ExternalDependenciesInterface &externalDependencies); InsightModel(InsightView *view,
class ExternalDependenciesInterface &externalDependencies,
class QmlDesignerProjectManager &projectManager);
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@@ -109,6 +111,7 @@ private:
private: private:
QPointer<InsightView> m_insightView; QPointer<InsightView> m_insightView;
ExternalDependenciesInterface &m_externalDependencies; ExternalDependenciesInterface &m_externalDependencies;
QmlDesignerProjectManager &m_projectManager;
Utils::FileSystemWatcher *m_fileSystemWatcher; Utils::FileSystemWatcher *m_fileSystemWatcher;

View File

@@ -20,7 +20,8 @@ class InsightPlugin final : public ExtensionSystem::IPlugin
auto *designerPlugin = QmlDesignerPlugin::instance(); auto *designerPlugin = QmlDesignerPlugin::instance();
auto &viewManager = designerPlugin->viewManager(); auto &viewManager = designerPlugin->viewManager();
viewManager.registerView(std::make_unique<InsightView>( viewManager.registerView(std::make_unique<InsightView>(
QmlDesignerPlugin::externalDependenciesForPluginInitializationOnly())); QmlDesignerPlugin::externalDependenciesForPluginInitializationOnly(),
QmlDesignerPlugin::projectManagerForPluginInitializationOnly()));
return true; return true;
} }

View File

@@ -14,9 +14,10 @@
namespace QmlDesigner { namespace QmlDesigner {
InsightView::InsightView(ExternalDependenciesInterface &externalDependencies) InsightView::InsightView(ExternalDependenciesInterface &externalDependencies,
QmlDesignerProjectManager &projectManager)
: AbstractView(externalDependencies) : AbstractView(externalDependencies)
, m_insightModel(std::make_unique<InsightModel>(this, externalDependencies)) , m_insightModel(std::make_unique<InsightModel>(this, externalDependencies, projectManager))
{ {
Q_ASSERT(m_insightModel); Q_ASSERT(m_insightModel);
} }

View File

@@ -15,13 +15,15 @@ namespace QmlDesigner {
class InsightModel; class InsightModel;
class InsightWidget; class InsightWidget;
class QmlDesignerProjectManager;
class InsightView : public AbstractView class InsightView : public AbstractView
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit InsightView(ExternalDependenciesInterface &externalDependencies); explicit InsightView(ExternalDependenciesInterface &externalDependencies,
QmlDesignerProjectManager &projectManager);
~InsightView() override; ~InsightView() override;
// AbstractView // AbstractView

View File

@@ -52,13 +52,14 @@ add_qtc_plugin(QmlDesigner
QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem
Qt::QuickWidgets Qt::CorePrivate Qt::Xml Qt::Svg Sqlite Zip Qt::GuiPrivate Qt::QuickWidgets Qt::CorePrivate Qt::Xml Qt::Svg Sqlite Zip Qt::GuiPrivate
PUBLIC_DEPENDS PUBLIC_DEPENDS
QmlDesignerUtils QmlPuppetCommunication QmlDesignerCore QmlDesignerUtils QmlPuppetCommunication QmlDesignerCore DesignSystem
DEFINES DEFINES
IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\" IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\"
SHARE_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../../share/qtcreator/qmldesigner" SHARE_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../../share/qtcreator/qmldesigner"
$<$<BOOL:${USE_PROJECTSTORAGE}>:QDS_USE_PROJECTSTORAGE>
$<$<BOOL:${QTC_USE_QML_DESIGNER_LITE}>:QTC_USE_QML_DESIGNER_LITE> $<$<BOOL:${QTC_USE_QML_DESIGNER_LITE}>:QTC_USE_QML_DESIGNER_LITE>
$<$<BOOL:${DETACH_DISABLED_VIEWS}>:DETACH_DISABLED_VIEWS> $<$<BOOL:${DETACH_DISABLED_VIEWS}>:DETACH_DISABLED_VIEWS>
PUBLIC_DEFINES
$<$<BOOL:${USE_PROJECTSTORAGE}>:QDS_USE_PROJECTSTORAGE>
INCLUDES INCLUDES
${CMAKE_CURRENT_LIST_DIR}/libs ${CMAKE_CURRENT_LIST_DIR}/libs
${CMAKE_CURRENT_LIST_DIR}/components ${CMAKE_CURRENT_LIST_DIR}/components
@@ -725,14 +726,6 @@ extend_qtc_plugin(QmlDesigner
messagemodel.h messagemodel.h
) )
extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/designsystem
SOURCES
dsconstants.h
dsthememanager.h dsthememanager.cpp
dsthemegroup.cpp dsthemegroup.h
)
add_qtc_plugin(assetexporterplugin add_qtc_plugin(assetexporterplugin
PLUGIN_CLASS AssetExporterPlugin PLUGIN_CLASS AssetExporterPlugin
CONDITION TARGET QmlDesigner CONDITION TARGET QmlDesigner

View File

@@ -85,13 +85,13 @@ private:
std::atomic<bool> m_quitDumper; std::atomic<bool> m_quitDumper;
}; };
AssetExporter::AssetExporter(AssetExporterView *view,
ProjectExplorer::Project *project,
AssetExporter::AssetExporter(AssetExporterView *view, ProjectExplorer::Project *project, QObject *parent) : ProjectStorageDependencies projectStorageDependencies)
QObject(parent), : m_currentState(*this)
m_currentState(*this), , m_project(project)
m_project(project), , m_view(view)
m_view(view) , m_projectStorageDependencies{projectStorageDependencies}
{ {
connect(m_view, &AssetExporterView::loadingFinished, this, &AssetExporter::onQmlFileLoaded); connect(m_view, &AssetExporterView::loadingFinished, this, &AssetExporter::onQmlFileLoaded);
connect(m_view, &AssetExporterView::loadingError, this, &AssetExporter::notifyLoadError); connect(m_view, &AssetExporterView::loadingError, this, &AssetExporter::notifyLoadError);
@@ -260,7 +260,14 @@ void AssetExporter::preprocessQmlFile(const Utils::FilePath &path)
{ {
// Load the QML file and assign UUIDs to items having none. // Load the QML file and assign UUIDs to items having none.
// Meanwhile cache the Component UUIDs as well // Meanwhile cache the Component UUIDs as well
ModelPointer model(Model::create("Item", 2, 7)); #ifdef QDS_USE_PROJECTSTORAGE
ModelPointer model = Model::create(m_projectStorageDependencies,
"Item",
{Import::createLibraryImport("QtQuick")},
path.path());
#else
ModelPointer model = Model::create("Item", 2, 7);
#endif
Utils::FileReader reader; Utils::FileReader reader;
if (!reader.fetch(path)) { if (!reader.fetch(path)) {
ExportNotification::addError(tr("Cannot preprocess file: %1. Error %2") ExportNotification::addError(tr("Cannot preprocess file: %1. Error %2")

View File

@@ -32,8 +32,9 @@ public:
ExportingDone ExportingDone
}; };
AssetExporter(AssetExporterView *view, ProjectExplorer::Project *project, AssetExporter(AssetExporterView *view,
QObject *parent = nullptr); ProjectExplorer::Project *project,
ProjectStorageDependencies projectStorageDependencies);
~AssetExporter(); ~AssetExporter();
void exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath, void exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath,
@@ -89,6 +90,7 @@ private:
QHash<QString, QString> m_componentUuidCache; QHash<QString, QString> m_componentUuidCache;
QSet<QByteArray> m_usedHashes; QSet<QByteArray> m_usedHashes;
QHash<QString, QPixmap> m_assets; QHash<QString, QPixmap> m_assets;
ProjectStorageDependencies m_projectStorageDependencies;
std::unique_ptr<AssetDumper> m_assetDumper; std::unique_ptr<AssetDumper> m_assetDumper;
bool m_cancelled = false; bool m_cancelled = false;
}; };

View File

@@ -3,12 +3,13 @@
#include "assetexporterplugin.h" #include "assetexporterplugin.h"
#include "assetexportpluginconstants.h"
#include "assetexportdialog.h" #include "assetexportdialog.h"
#include "assetexporter.h" #include "assetexporter.h"
#include "assetexporterview.h" #include "assetexporterview.h"
#include "filepathmodel.h" #include "assetexportpluginconstants.h"
#include "componentexporter.h" #include "componentexporter.h"
#include "filepathmodel.h"
#include <qmldesignerprojectmanager.h>
#include "dumpers/itemnodedumper.h" #include "dumpers/itemnodedumper.h"
#include "dumpers/textnodedumper.h" #include "dumpers/textnodedumper.h"
@@ -37,6 +38,7 @@
namespace QmlDesigner { namespace QmlDesigner {
AssetExporterPlugin::AssetExporterPlugin() AssetExporterPlugin::AssetExporterPlugin()
: m_projectManager{QmlDesigner::QmlDesignerPlugin::projectManagerForPluginInitializationOnly()}
{ {
ProjectExplorer::TaskHub::addCategory({Constants::TASK_CATEGORY_ASSET_EXPORT, ProjectExplorer::TaskHub::addCategory({Constants::TASK_CATEGORY_ASSET_EXPORT,
tr("Asset Export"), tr("Asset Export"),
@@ -44,6 +46,7 @@ AssetExporterPlugin::AssetExporterPlugin()
false}); false});
auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance(); auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance();
auto &viewManager = designerPlugin->viewManager(); auto &viewManager = designerPlugin->viewManager();
m_view = viewManager.registerView(std::make_unique<AssetExporterView>( m_view = viewManager.registerView(std::make_unique<AssetExporterView>(
designerPlugin->externalDependenciesForPluginInitializationOnly())); designerPlugin->externalDependenciesForPluginInitializationOnly()));
@@ -79,7 +82,7 @@ void AssetExporterPlugin::onExport()
if (!exportDir.parentDir().isEmpty()) if (!exportDir.parentDir().isEmpty())
exportDir = exportDir.parentDir(); exportDir = exportDir.parentDir();
exportDir = exportDir.pathAppended(startupProject->displayName() + "_export"); exportDir = exportDir.pathAppended(startupProject->displayName() + "_export");
AssetExporter assetExporter(m_view, startupProject); AssetExporter assetExporter(m_view, startupProject, m_projectManager.projectStorageDependencies());
AssetExportDialog assetExporterDialog(exportDir, assetExporter, model); AssetExportDialog assetExporterDialog(exportDir, assetExporter, model);
assetExporterDialog.exec(); assetExporterDialog.exec();
} }

View File

@@ -29,6 +29,7 @@ private:
void updateActions(); void updateActions();
AssetExporterView *m_view = nullptr; AssetExporterView *m_view = nullptr;
class QmlDesignerProjectManager &m_projectManager;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -21,19 +21,6 @@
namespace { namespace {
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.modelExporter", QtInfoMsg) Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.modelExporter", QtInfoMsg)
static QByteArrayList populateLineage(const QmlDesigner::ModelNode &node)
{
QByteArrayList lineage;
if (!node.isValid() || node.type().isEmpty())
return {};
for (auto &info : node.metaInfo().prototypes())
lineage.append(info.typeName());
return lineage;
}
} }
namespace QmlDesigner { namespace QmlDesigner {
@@ -78,10 +65,9 @@ const QString &Component::name() const
NodeDumper *Component::createNodeDumper(const ModelNode &node) const NodeDumper *Component::createNodeDumper(const ModelNode &node) const
{ {
QByteArrayList lineage = populateLineage(node);
std::unique_ptr<NodeDumper> reader; std::unique_ptr<NodeDumper> reader;
for (auto &dumperCreator: m_readers) { for (auto &dumperCreator: m_readers) {
std::unique_ptr<NodeDumper> r(dumperCreator->instance(lineage, node)); std::unique_ptr<NodeDumper> r(dumperCreator->instance(node));
if (r->isExportable()) { if (r->isExportable()) {
if (reader) { if (reader) {
if (reader->priority() < r->priority()) if (reader->priority() < r->priority())

View File

@@ -27,7 +27,7 @@ class NodeDumperCreatorBase
public: public:
virtual ~NodeDumperCreatorBase() {} virtual ~NodeDumperCreatorBase() {}
protected: protected:
virtual NodeDumper *instance(const QByteArrayList &, const ModelNode &) const = 0; virtual NodeDumper *instance(const ModelNode &) const = 0;
friend Component; friend Component;
}; };
@@ -39,9 +39,7 @@ public:
~NodeDumperCreator() = default; ~NodeDumperCreator() = default;
protected: protected:
NodeDumper *instance(const QByteArrayList &lineage, const ModelNode &node) const { NodeDumper *instance(const ModelNode &node) const { return new T(node); }
return new T(lineage, node);
}
}; };
} //Internal } //Internal

View File

@@ -14,18 +14,18 @@
namespace QmlDesigner { namespace QmlDesigner {
using namespace Constants; using namespace Constants;
AssetNodeDumper::AssetNodeDumper(const QByteArrayList &lineage, const ModelNode &node) :
ItemNodeDumper(lineage, node) AssetNodeDumper::AssetNodeDumper(const ModelNode &node)
: ItemNodeDumper(node)
{ {
} }
bool AssetNodeDumper::isExportable() const bool AssetNodeDumper::isExportable() const
{ {
auto hasType = [this](const QByteArray &type) { auto qtQuickImageMetaInfo = model()->qtQuickImageMetaInfo();
return lineage().contains(type); auto qtQuickRectangleMetaInfo = model()->qtQuickRectangleMetaInfo();
}; return metaInfo().isBasedOn(qtQuickImageMetaInfo, qtQuickRectangleMetaInfo);
return hasType("QtQuick.Image") || hasType("QtQuick.Rectangle");
} }
QJsonObject AssetNodeDumper::json(Component &component) const QJsonObject AssetNodeDumper::json(Component &component) const

View File

@@ -10,7 +10,7 @@ class Component;
class AssetNodeDumper : public ItemNodeDumper class AssetNodeDumper : public ItemNodeDumper
{ {
public: public:
AssetNodeDumper(const QByteArrayList &lineage, const ModelNode &node); AssetNodeDumper(const ModelNode &node);
~AssetNodeDumper() override = default; ~AssetNodeDumper() override = default;
bool isExportable() const override; bool isExportable() const override;

View File

@@ -22,16 +22,16 @@ static QString capitalize(const QString &str)
namespace QmlDesigner { namespace QmlDesigner {
using namespace Constants; using namespace Constants;
ItemNodeDumper::ItemNodeDumper(const QByteArrayList &lineage,
const ModelNode &node) : ItemNodeDumper::ItemNodeDumper(const ModelNode &node)
NodeDumper(lineage, node) : NodeDumper(node)
{ {
} }
bool QmlDesigner::ItemNodeDumper::isExportable() const bool QmlDesigner::ItemNodeDumper::isExportable() const
{ {
return lineage().contains("QtQuick.Item"); return metaInfo().isQtQuickItem();
} }
QJsonObject QmlDesigner::ItemNodeDumper::json([[maybe_unused]] QmlDesigner::Component &component) const QJsonObject QmlDesigner::ItemNodeDumper::json([[maybe_unused]] QmlDesigner::Component &component) const

View File

@@ -11,7 +11,7 @@ class Component;
class ItemNodeDumper : public NodeDumper class ItemNodeDumper : public NodeDumper
{ {
public: public:
ItemNodeDumper(const QByteArrayList &lineage, const ModelNode &node); ItemNodeDumper(const ModelNode &node);
~ItemNodeDumper() override = default; ~ItemNodeDumper() override = default;

View File

@@ -6,10 +6,11 @@
#include <auxiliarydataproperties.h> #include <auxiliarydataproperties.h>
namespace QmlDesigner { namespace QmlDesigner {
NodeDumper::NodeDumper(const QByteArrayList &lineage, const ModelNode &node) : NodeDumper::NodeDumper(const ModelNode &node)
m_node(node), : m_node(node)
m_objectNode(node), , m_objectNode(node)
m_lineage(lineage) , m_metaInfo(node.metaInfo())
, m_model{node.model()}
{ {
} }

View File

@@ -14,7 +14,7 @@ class ModelNode;
class NodeDumper class NodeDumper
{ {
public: public:
NodeDumper(const QByteArrayList &lineage, const ModelNode &node); NodeDumper(const ModelNode &node);
virtual ~NodeDumper() = default; virtual ~NodeDumper() = default;
@@ -22,16 +22,19 @@ public:
virtual bool isExportable() const = 0; virtual bool isExportable() const = 0;
virtual QJsonObject json(Component& component) const = 0; virtual QJsonObject json(Component& component) const = 0;
const QByteArrayList& lineage() const { return m_lineage; } const NodeMetaInfo &metaInfo() const { return m_metaInfo; }
const QmlObjectNode& objectNode() const { return m_objectNode; } const QmlObjectNode& objectNode() const { return m_objectNode; }
QVariant propertyValue(const PropertyName &name) const; QVariant propertyValue(const PropertyName &name) const;
QString uuid() const; QString uuid() const;
Model *model() const { return m_model; }
protected: protected:
const ModelNode &m_node; const ModelNode &m_node;
private: private:
QmlObjectNode m_objectNode; QmlObjectNode m_objectNode;
QByteArrayList m_lineage; NodeMetaInfo m_metaInfo;
Model *m_model = nullptr;
}; };
} }

View File

@@ -4,6 +4,8 @@
#include "textnodedumper.h" #include "textnodedumper.h"
#include "assetexportpluginconstants.h" #include "assetexportpluginconstants.h"
#include <model.h>
#include <QColor> #include <QColor>
#include <QFontInfo> #include <QFontInfo>
#include <QFontMetricsF> #include <QFontMetricsF>
@@ -35,18 +37,18 @@ QString toJsonAlignEnum(QString value) {
namespace QmlDesigner { namespace QmlDesigner {
using namespace Constants; using namespace Constants;
TextNodeDumper::TextNodeDumper(const QByteArrayList &lineage, const ModelNode &node) :
ItemNodeDumper(lineage, node) TextNodeDumper::TextNodeDumper(const ModelNode &node)
: ItemNodeDumper(node)
{ {
} }
bool TextNodeDumper::isExportable() const bool TextNodeDumper::isExportable() const
{ {
const QByteArrayList &baseClasses = lineage(); auto qtQuickTextMetaInfo = model()->qtQuickTextMetaInfo();
return std::any_of(baseClasses.cbegin(), baseClasses.cend(), [](const QByteArray &type) { auto qtQuickControlsLabelMetaInfo = model()->qtQuickControlsLabelMetaInfo();
return type == "QtQuick.Text" || type == "QtQuick.Controls.Label"; return metaInfo().isBasedOn(qtQuickTextMetaInfo, qtQuickControlsLabelMetaInfo);
});
} }
QJsonObject TextNodeDumper::json([[maybe_unused]] Component &component) const QJsonObject TextNodeDumper::json([[maybe_unused]] Component &component) const

View File

@@ -10,7 +10,7 @@ class Component;
class TextNodeDumper : public ItemNodeDumper class TextNodeDumper : public ItemNodeDumper
{ {
public: public:
TextNodeDumper(const QByteArrayList &lineage, const ModelNode &node); TextNodeDumper(const ModelNode &node);
~TextNodeDumper() override = default; ~TextNodeDumper() override = default;
bool isExportable() const override; bool isExportable() const override;

View File

@@ -41,7 +41,7 @@ void AssetsLibraryModel::createBackendModel()
QObject::connect(m_sourceFsModel, &QFileSystemModel::directoryLoaded, this, QObject::connect(m_sourceFsModel, &QFileSystemModel::directoryLoaded, this,
[this]([[maybe_unused]] const QString &dir) { [this]([[maybe_unused]] const QString &dir) {
syncHasFiles(); syncIsEmpty();
}); });
m_fileWatcher = new Utils::FileSystemWatcher(parent()); m_fileWatcher = new Utils::FileSystemWatcher(parent());
@@ -202,7 +202,7 @@ bool AssetsLibraryModel::isSameOrDescendantPath(const QUrl &source, const QStrin
Utils::FilePath srcPath = Utils::FilePath::fromUrl(source); Utils::FilePath srcPath = Utils::FilePath::fromUrl(source);
Utils::FilePath targetPath = Utils::FilePath::fromString(target); Utils::FilePath targetPath = Utils::FilePath::fromString(target);
return targetPath.isChildOf(srcPath); return srcPath == targetPath || targetPath.isChildOf(srcPath);
} }
bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
@@ -224,41 +224,20 @@ bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
} }
} }
bool AssetsLibraryModel::checkHasFiles(const QModelIndex &parentIdx) const void AssetsLibraryModel::setIsEmpty(bool value)
{ {
if (!parentIdx.isValid()) if (m_isEmpty != value) {
return false; m_isEmpty = value;
emit isEmptyChanged();
const int rowCount = this->rowCount(parentIdx);
for (int i = 0; i < rowCount; ++i) {
auto newIdx = this->index(i, 0, parentIdx);
if (!isDirectory(newIdx))
return true;
if (checkHasFiles(newIdx))
return true;
}
return false;
}
void AssetsLibraryModel::setHasFiles(bool value)
{
if (m_hasFiles != value) {
m_hasFiles = value;
emit hasFilesChanged();
} }
} }
bool AssetsLibraryModel::checkHasFiles() const void AssetsLibraryModel::syncIsEmpty()
{ {
auto rootIdx = indexForPath(m_rootPath); QModelIndex rootIdx = indexForPath(m_rootPath);
return checkHasFiles(rootIdx);
}
void AssetsLibraryModel::syncHasFiles() bool hasContent = rowCount(rootIdx);
{ setIsEmpty(!hasContent);
setHasFiles(checkHasFiles());
} }
void AssetsLibraryModel::setRootPath(const QString &newPath) void AssetsLibraryModel::setRootPath(const QString &newPath)

View File

@@ -23,7 +23,7 @@ public:
void setRootPath(const QString &newPath); void setRootPath(const QString &newPath);
void setSearchText(const QString &searchText); void setSearchText(const QString &searchText);
Q_PROPERTY(bool hasFiles READ hasFiles NOTIFY hasFilesChanged) Q_PROPERTY(bool isEmpty READ isEmpty NOTIFY isEmptyChanged)
Q_INVOKABLE QString rootPath() const; Q_INVOKABLE QString rootPath() const;
Q_INVOKABLE QString filePath(const QModelIndex &index) const; Q_INVOKABLE QString filePath(const QModelIndex &index) const;
@@ -36,7 +36,7 @@ public:
Q_INVOKABLE QModelIndex parentDirIndex(const QString &path) const; Q_INVOKABLE QModelIndex parentDirIndex(const QString &path) const;
Q_INVOKABLE QModelIndex parentDirIndex(const QModelIndex &index) const; Q_INVOKABLE QModelIndex parentDirIndex(const QModelIndex &index) const;
Q_INVOKABLE QString parentDirPath(const QString &path) const; Q_INVOKABLE QString parentDirPath(const QString &path) const;
Q_INVOKABLE void syncHasFiles(); Q_INVOKABLE void syncIsEmpty();
Q_INVOKABLE QList<QModelIndex> parentIndices(const QModelIndex &index) const; Q_INVOKABLE QList<QModelIndex> parentIndices(const QModelIndex &index) const;
Q_INVOKABLE bool indexIsValid(const QModelIndex &index) const; Q_INVOKABLE bool indexIsValid(const QModelIndex &index) const;
@@ -58,29 +58,27 @@ public:
return std::min(result, 1); return std::min(result, 1);
} }
bool hasFiles() const { return m_hasFiles; } bool isEmpty() const { return m_isEmpty; }
signals: signals:
void directoryLoaded(const QString &path); void directoryLoaded(const QString &path);
void rootPathChanged(); void rootPathChanged();
void hasFilesChanged(); void isEmptyChanged();
void fileChanged(const QString &path); void fileChanged(const QString &path);
void effectsDeleted(const QStringList &effectNames); void effectsDeleted(const QStringList &effectNames);
private: private:
void setHasFiles(bool value); void setIsEmpty(bool value);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
void resetModel(); void resetModel();
void createBackendModel(); void createBackendModel();
void destroyBackendModel(); void destroyBackendModel();
bool checkHasFiles(const QModelIndex &parentIdx) const;
bool checkHasFiles() const;
QString m_searchText; QString m_searchText;
QString m_rootPath; QString m_rootPath;
QFileSystemModel *m_sourceFsModel = nullptr; QFileSystemModel *m_sourceFsModel = nullptr;
bool m_hasFiles = false; bool m_isEmpty = true;
Utils::FileSystemWatcher *m_fileWatcher = nullptr; Utils::FileSystemWatcher *m_fileWatcher = nullptr;
}; };

View File

@@ -7,6 +7,7 @@
#include "assetslibrarymodel.h" #include "assetslibrarymodel.h"
#include "assetslibraryview.h" #include "assetslibraryview.h"
#include <createtexture.h>
#include <designeractionmanager.h> #include <designeractionmanager.h>
#include <designerpaths.h> #include <designerpaths.h>
#include <designmodewidget.h> #include <designmodewidget.h>
@@ -102,7 +103,6 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon
, m_assetsIconProvider{new AssetsLibraryIconProvider(synchronousFontImageCache)} , m_assetsIconProvider{new AssetsLibraryIconProvider(synchronousFontImageCache)}
, m_assetsModel{new AssetsLibraryModel(this)} , m_assetsModel{new AssetsLibraryModel(this)}
, m_assetsView{view} , m_assetsView{view}
, m_createTextures{view}
, m_assetsWidget{Utils::makeUniqueObjectPtr<StudioQuickWidget>(this)} , m_assetsWidget{Utils::makeUniqueObjectPtr<StudioQuickWidget>(this)}
{ {
setWindowTitle(tr("Assets Library", "Title of assets library widget")); setWindowTitle(tr("Assets Library", "Title of assets library widget"));
@@ -237,18 +237,20 @@ int AssetsLibraryWidget::qtVersion() const
void AssetsLibraryWidget::addTextures(const QStringList &filePaths) void AssetsLibraryWidget::addTextures(const QStringList &filePaths)
{ {
m_assetsView->executeInTransaction(__FUNCTION__, [&] { m_assetsView->executeInTransaction(__FUNCTION__, [&] {
m_createTextures.execute(filePaths, CreateTexture(m_assetsView)
AddTextureMode::Texture, .execute(filePaths,
Utils3D::active3DSceneId(m_assetsView->model())); AddTextureMode::Texture,
Utils3D::active3DSceneId(m_assetsView->model()));
}); });
} }
void AssetsLibraryWidget::addLightProbe(const QString &filePath) void AssetsLibraryWidget::addLightProbe(const QString &filePath)
{ {
m_assetsView->executeInTransaction(__FUNCTION__, [&] { m_assetsView->executeInTransaction(__FUNCTION__, [&] {
m_createTextures.execute({filePath}, CreateTexture(m_assetsView)
AddTextureMode::LightProbe, .execute(filePath,
Utils3D::active3DSceneId(m_assetsView->model())); AddTextureMode::LightProbe,
Utils3D::active3DSceneId(m_assetsView->model()));
}); });
} }
@@ -257,8 +259,9 @@ void AssetsLibraryWidget::updateContextMenuActionsEnableState()
setHasMaterialLibrary(Utils3D::materialLibraryNode(m_assetsView).isValid() setHasMaterialLibrary(Utils3D::materialLibraryNode(m_assetsView).isValid()
&& m_assetsView->model()->hasImport("QtQuick3D")); && m_assetsView->model()->hasImport("QtQuick3D"));
ModelNode activeSceneEnv = m_createTextures.resolveSceneEnv( ModelNode activeSceneEnv = Utils3D::resolveSceneEnv(m_assetsView,
Utils3D::active3DSceneId(m_assetsView->model())); Utils3D::active3DSceneId(
m_assetsView->model()));
setHasSceneEnv(activeSceneEnv.isValid()); setHasSceneEnv(activeSceneEnv.isValid());
} }
@@ -394,11 +397,11 @@ void AssetsLibraryWidget::handleAssetsDrop(const QList<QUrl> &urls, const QStrin
if (destDir.isFile()) if (destDir.isFile())
destDir = destDir.parentDir(); destDir = destDir.parentDir();
QMessageBox mb; QMessageBox msgBox;
mb.setInformativeText("What would you like to do with the existing asset?"); msgBox.setInformativeText("What would you like to do with the existing asset?");
mb.addButton("Keep Both", QMessageBox::AcceptRole); msgBox.addButton("Keep Both", QMessageBox::AcceptRole);
mb.addButton("Replace", QMessageBox::ResetRole); msgBox.addButton("Replace", QMessageBox::ResetRole);
mb.addButton("Cancel", QMessageBox::RejectRole); msgBox.addButton("Cancel", QMessageBox::RejectRole);
for (const QUrl &url : urls) { for (const QUrl &url : urls) {
Utils::FilePath src = Utils::FilePath::fromUrl(url); Utils::FilePath src = Utils::FilePath::fromUrl(url);
@@ -408,9 +411,9 @@ void AssetsLibraryWidget::handleAssetsDrop(const QList<QUrl> &urls, const QStrin
continue; continue;
if (dest.exists()) { if (dest.exists()) {
mb.setText("An asset named " + dest.fileName() + " already exists."); msgBox.setText("An asset named " + dest.fileName() + " already exists.");
mb.exec(); msgBox.exec();
int userAction = mb.buttonRole(mb.clickedButton()); int userAction = msgBox.buttonRole(msgBox.clickedButton());
if (userAction == QMessageBox::AcceptRole) { // "Keep Both" if (userAction == QMessageBox::AcceptRole) { // "Keep Both"
dest = Utils::FilePath::fromString(UniqueName::generatePath(dest.toString())); dest = Utils::FilePath::fromString(UniqueName::generatePath(dest.toString()));
@@ -424,8 +427,13 @@ void AssetsLibraryWidget::handleAssetsDrop(const QList<QUrl> &urls, const QStrin
} }
} }
if (!src.renameFile(dest)) if (!src.renameFile(dest) && src.isDir()) {
qWarning() << __FUNCTION__ << "Failed to move asset from" << src << "to" << dest; QMessageBox errBox;
QString message = QString("Failed to move folder \"%1\".\nThe folder might contain subfolders or one of its files is in use.")
.arg(src.fileName());
errBox.setInformativeText(message);
errBox.exec();
}
} }
if (m_assetsView->model()) if (m_assetsView->model())
@@ -439,7 +447,7 @@ QList<QToolButton *> AssetsLibraryWidget::createToolBarWidgets()
void AssetsLibraryWidget::handleSearchFilterChanged(const QString &filterText) void AssetsLibraryWidget::handleSearchFilterChanged(const QString &filterText)
{ {
if (filterText == m_filterText || (!m_assetsModel->hasFiles() if (filterText == m_filterText || (m_assetsModel->isEmpty()
&& filterText.contains(m_filterText, Qt::CaseInsensitive))) && filterText.contains(m_filterText, Qt::CaseInsensitive)))
return; return;

View File

@@ -3,7 +3,6 @@
#pragma once #pragma once
#include "createtexture.h"
#include "previewtooltipbackend.h" #include "previewtooltipbackend.h"
#include <coreplugin/icontext.h> #include <coreplugin/icontext.h>
@@ -135,7 +134,6 @@ private:
AssetsLibraryIconProvider *m_assetsIconProvider = nullptr; AssetsLibraryIconProvider *m_assetsIconProvider = nullptr;
AssetsLibraryModel *m_assetsModel = nullptr; AssetsLibraryModel *m_assetsModel = nullptr;
AssetsLibraryView *m_assetsView = nullptr; AssetsLibraryView *m_assetsView = nullptr;
CreateTextures m_createTextures = nullptr;
Utils::UniqueObjectPtr<StudioQuickWidget> m_assetsWidget; Utils::UniqueObjectPtr<StudioQuickWidget> m_assetsWidget;
std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend; std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend;

View File

@@ -20,7 +20,6 @@
#include <qmljstools/qmljsindenter.h> #include <qmljstools/qmljsindenter.h>
#include <utils/fancylineedit.h>
#include <utils/mimeconstants.h> #include <utils/mimeconstants.h>
#include <utils/transientscroll.h> #include <utils/transientscroll.h>

View File

@@ -77,6 +77,7 @@ void BundleHelper::createImporter()
newNode.simplifiedTypeName(), "node")); newNode.simplifiedTypeName(), "node"));
m_view->clearSelectedModelNodes(); m_view->clearSelectedModelNodes();
m_view->selectModelNode(newNode); m_view->selectModelNode(newNode);
m_view->resetPuppet();
}); });
} }
}); });
@@ -104,6 +105,7 @@ void BundleHelper::createImporter()
newNode.simplifiedTypeName(), "node")); newNode.simplifiedTypeName(), "node"));
m_view->clearSelectedModelNodes(); m_view->clearSelectedModelNodes();
m_view->selectModelNode(newNode); m_view->selectModelNode(newNode);
m_view->resetPuppet();
}); });
} }
}); });
@@ -135,8 +137,16 @@ void BundleHelper::importBundleToProject()
" of Qt Design Studio")); " of Qt Design Studio"));
return; return;
} }
QString bundleId = importedJsonObj.value("id").toString(); QString bundleId = importedJsonObj.value("id").toString();
bool hasQuick3DImport = m_view->model()->hasImport("QtQuick3D");
if (!hasQuick3DImport) {
Import import = Import::createLibraryImport("QtQuick3D");
m_view->model()->changeImports({import}, {});
}
QTemporaryDir tempDir; QTemporaryDir tempDir;
QTC_ASSERT(tempDir.isValid(), return); QTC_ASSERT(tempDir.isValid(), return);
auto bundlePath = Utils::FilePath::fromString(tempDir.path()); auto bundlePath = Utils::FilePath::fromString(tempDir.path());

View File

@@ -114,10 +114,10 @@ ModelNode CreateTexture::execute(const QString &filePath, AddTextureMode mode, i
return {}; return {};
if (mode == AddTextureMode::LightProbe && sceneId != -1) if (mode == AddTextureMode::LightProbe && sceneId != -1)
assignTextureAsLightProbe(texture, sceneId); Utils3D::assignTextureAsLightProbe(m_view, texture, sceneId);
QTimer::singleShot(0, m_view, [this, texture]() { QTimer::singleShot(0, m_view, [view = m_view, texture]() {
if (m_view->model() && texture.isValid()) { if (view && view->model() && texture.isValid()) {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialBrowser"); QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialBrowser");
Utils3D::selectTexture(texture); Utils3D::selectTexture(texture);
} }
@@ -210,6 +210,12 @@ ModelNode CreateTexture::execute(const ModelNode &texture)
return duplicateTextureNode; return duplicateTextureNode;
} }
void CreateTexture::execute(const QStringList &filePaths, AddTextureMode mode, int sceneId)
{
for (const QString &path : filePaths)
execute(path, mode, sceneId);
}
bool CreateTexture::addFileToProject(const QString &filePath) bool CreateTexture::addFileToProject(const QString &filePath)
{ {
AddFilesResult result = ModelNodeOperations::addImageToProject( AddFilesResult result = ModelNodeOperations::addImageToProject(
@@ -260,48 +266,4 @@ ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPat
return newTexNode; return newTexNode;
} }
void CreateTexture::assignTextureAsLightProbe(const ModelNode &texture, int sceneId)
{
ModelNode sceneEnvNode = resolveSceneEnv(sceneId);
QmlObjectNode sceneEnv = sceneEnvNode;
if (sceneEnv.isValid()) {
sceneEnv.setBindingProperty("lightProbe", texture.id());
sceneEnv.setVariantProperty("backgroundMode",
QVariant::fromValue(Enumeration("SceneEnvironment",
"SkyBox")));
}
}
ModelNode CreateTexture::resolveSceneEnv(int sceneId)
{
ModelNode activeSceneEnv;
ModelNode selectedNode = m_view->firstSelectedModelNode();
if (selectedNode.metaInfo().isQtQuick3DSceneEnvironment()) {
activeSceneEnv = selectedNode;
} else if (sceneId != -1) {
ModelNode activeScene = Utils3D::active3DSceneNode(m_view);
if (activeScene.isValid()) {
QmlObjectNode view3D;
if (activeScene.metaInfo().isQtQuick3DView3D()) {
view3D = activeScene;
} else {
ModelNode sceneParent = activeScene.parentProperty().parentModelNode();
if (sceneParent.metaInfo().isQtQuick3DView3D())
view3D = sceneParent;
}
if (view3D.isValid())
activeSceneEnv = m_view->modelNodeForId(view3D.expression("environment"));
}
}
return activeSceneEnv;
}
void CreateTextures::execute(const QStringList &filePaths, AddTextureMode mode, int sceneId)
{
for (const QString &path : filePaths)
CreateTexture::execute(path, mode, sceneId);
}
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -3,7 +3,7 @@
#pragma once #pragma once
#include <QObject> #include <QStringList>
namespace Utils { namespace Utils {
class FilePath; class FilePath;
@@ -16,10 +16,8 @@ class ModelNode;
enum class AddTextureMode { Image, Texture, LightProbe }; enum class AddTextureMode { Image, Texture, LightProbe };
class CreateTexture : public QObject class CreateTexture
{ {
Q_OBJECT
public: public:
CreateTexture(AbstractView *view); CreateTexture(AbstractView *view);
@@ -28,8 +26,7 @@ public:
AddTextureMode mode = AddTextureMode::Texture, AddTextureMode mode = AddTextureMode::Texture,
int sceneId = -1); int sceneId = -1);
ModelNode execute(const ModelNode &texture); ModelNode execute(const ModelNode &texture);
ModelNode resolveSceneEnv(int sceneId); void execute(const QStringList &filePaths, AddTextureMode mode, int sceneId = -1);
void assignTextureAsLightProbe(const ModelNode &texture, int sceneId);
private: private:
bool addFileToProject(const QString &filePath); bool addFileToProject(const QString &filePath);
@@ -38,11 +35,4 @@ private:
AbstractView *m_view = nullptr; AbstractView *m_view = nullptr;
}; };
class CreateTextures : public CreateTexture
{
public:
using CreateTexture::CreateTexture;
void execute(const QStringList &filePaths, AddTextureMode mode, int sceneId = -1);
};
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -80,6 +80,7 @@ public:
LightDirectionalIcon, LightDirectionalIcon,
LightPointIcon, LightPointIcon,
LightSpotIcon, LightSpotIcon,
LiveUpdateIcon,
LocalOrientIcon, LocalOrientIcon,
MakeComponentIcon, MakeComponentIcon,
MaterialIcon, MaterialIcon,
@@ -107,6 +108,7 @@ public:
SnappingIcon, SnappingIcon,
SnappingConfIcon, SnappingConfIcon,
SplitViewIcon, SplitViewIcon,
SyncIcon,
TimelineIcon, TimelineIcon,
ToggleGroupIcon, ToggleGroupIcon,
VisibilityIcon VisibilityIcon

View File

@@ -6,6 +6,7 @@
#include "addimagesdialog.h" #include "addimagesdialog.h"
#include "addsignalhandlerdialog.h" #include "addsignalhandlerdialog.h"
#include "componentcore_constants.h" #include "componentcore_constants.h"
#include "createtexture.h"
#include "findimplementation.h" #include "findimplementation.h"
#include "layoutingridlayout.h" #include "layoutingridlayout.h"
#include "modelnodecontextmenu_helper.h" #include "modelnodecontextmenu_helper.h"
@@ -87,11 +88,14 @@ Utils::SmallString auxPropertyString(Utils::SmallStringView name)
{ {
return auxDataString + name; return auxDataString + name;
} }
} // namespace
inline static void reparentTo(const ModelNode &node, const QmlItemNode &parent) QString relativePathToQmlFile(const QString &absolutePath)
{ {
return DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(absolutePath);
}
inline void reparentTo(const ModelNode &node, const QmlItemNode &parent)
{
if (parent.isValid() && node.isValid()) { if (parent.isValid() && node.isValid()) {
NodeAbstractProperty parentProperty; NodeAbstractProperty parentProperty;
@@ -104,7 +108,7 @@ inline static void reparentTo(const ModelNode &node, const QmlItemNode &parent)
} }
} }
inline static QPointF getUpperLeftPosition(const QList<ModelNode> &modelNodeList) inline QPointF getUpperLeftPosition(const QList<ModelNode> &modelNodeList)
{ {
QPointF postion(std::numeric_limits<qreal>::max(), std::numeric_limits<qreal>::max()); QPointF postion(std::numeric_limits<qreal>::max(), std::numeric_limits<qreal>::max());
for (const ModelNode &modelNode : modelNodeList) { for (const ModelNode &modelNode : modelNodeList) {
@@ -120,13 +124,15 @@ inline static QPointF getUpperLeftPosition(const QList<ModelNode> &modelNodeList
return postion; return postion;
} }
static void setUpperLeftPostionToNode(const ModelNode &layoutNode, const QList<ModelNode> &modelNodeList) void setUpperLeftPostionToNode(const ModelNode &layoutNode, const QList<ModelNode> &modelNodeList)
{ {
QPointF upperLeftPosition = getUpperLeftPosition(modelNodeList); QPointF upperLeftPosition = getUpperLeftPosition(modelNodeList);
layoutNode.variantProperty("x").setValue(qRound(upperLeftPosition.x())); layoutNode.variantProperty("x").setValue(qRound(upperLeftPosition.x()));
layoutNode.variantProperty("y") .setValue(qRound(upperLeftPosition.y())); layoutNode.variantProperty("y") .setValue(qRound(upperLeftPosition.y()));
} }
} // namespace
namespace ModelNodeOperations { namespace ModelNodeOperations {
bool goIntoComponent(const ModelNode &modelNode) bool goIntoComponent(const ModelNode &modelNode)
@@ -1753,13 +1759,10 @@ void editInEffectComposer(const SelectionContext &selectionContext)
bool isEffectComposerActivated() bool isEffectComposerActivated()
{ {
const ExtensionSystem::PluginSpecs specs = ExtensionSystem::PluginManager::plugins(); using namespace ExtensionSystem;
return std::ranges::find_if(specs, return Utils::anyOf(PluginManager::plugins(), [](PluginSpec *spec) {
[](ExtensionSystem::PluginSpec *spec) { return spec->name() == "EffectComposer" && spec->isEffectivelyEnabled();
return spec->name() == "EffectComposer" });
&& spec->isEffectivelyEnabled();
})
!= specs.end();
} }
void openEffectComposer(const QString &filePath) void openEffectComposer(const QString &filePath)
@@ -1905,41 +1908,15 @@ static bool moveNodeToParent(const NodeAbstractProperty &targetProperty, const M
return false; return false;
} }
ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath) ModelNode createTextureNode(AbstractView *view, const QString &imagePath)
{ {
AbstractView *view = targetProp.view();
QTC_ASSERT(view, return {}); QTC_ASSERT(view, return {});
if (targetProp.isValid()) { CreateTexture textureCreator(view);
// create a texture item lib return textureCreator.execute(imagePath, AddTextureMode::Texture);
ItemLibraryEntry itemLibraryEntry;
itemLibraryEntry.setName("Texture");
itemLibraryEntry.setType("QtQuick3D.Texture", 1, 0);
// set texture source
PropertyName prop = "source";
QString type = "QUrl";
QVariant val = imagePath;
itemLibraryEntry.addProperty(prop, type, val);
// create a texture
ModelNode newModelNode = QmlItemNode::createQmlObjectNode(view,
itemLibraryEntry,
{},
targetProp,
false);
// Rename the node based on source image
QFileInfo fi(imagePath);
newModelNode.setIdWithoutRefactoring(
view->model()->generateNewId(fi.baseName(), "textureImage"));
return newModelNode;
}
return {};
} }
bool dropAsImage3dTexture(const ModelNode &targetNode, bool dropAsImage3dTexture(const ModelNode &targetNode,
const NodeAbstractProperty &targetProp,
const QString &imagePath, const QString &imagePath,
ModelNode &newNode, ModelNode &newNode,
bool &outMoveNodesAfter) bool &outMoveNodesAfter)
@@ -1949,16 +1926,11 @@ bool dropAsImage3dTexture(const ModelNode &targetNode,
auto bindToProperty = [&](const PropertyName &propName) { auto bindToProperty = [&](const PropertyName &propName) {
view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] { view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] {
newNode = createTextureNode(targetProp, imagePath); newNode = createTextureNode(view, imagePath);
if (newNode.isValid()) { if (newNode.isValid()) {
BindingProperty bindProp = targetNode.bindingProperty(propName); BindingProperty bindProp = targetNode.bindingProperty(propName);
bindProp.setExpression(newNode.validId()); bindProp.setExpression(newNode.validId());
ModelNode matLib = Utils3D::materialLibraryNode(view); outMoveNodesAfter = false;
if (matLib.isValid()) {
NodeAbstractProperty matLibProp = matLib.defaultNodeAbstractProperty();
matLibProp.reparentHere(newNode);
outMoveNodesAfter = false;
}
} }
}); });
}; };
@@ -1979,7 +1951,7 @@ bool dropAsImage3dTexture(const ModelNode &targetNode,
if (dialog->result() == QDialog::Accepted) { if (dialog->result() == QDialog::Accepted) {
view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] { view->executeInTransaction("NavigatorTreeModel::dropAsImage3dTexture", [&] {
newNode = createTextureNode(targetProp, imagePath); newNode = createTextureNode(view, imagePath);
if (newNode.isValid()) // Automatically set the texture to selected property if (newNode.isValid()) // Automatically set the texture to selected property
targetNode.bindingProperty(dialog->selectedProperty()) targetNode.bindingProperty(dialog->selectedProperty())
.setExpression(newNode.validId()); .setExpression(newNode.validId());
@@ -1999,10 +1971,11 @@ bool dropAsImage3dTexture(const ModelNode &targetNode,
return newNode.isValid(); return newNode.isValid();
} else if (targetNode.metaInfo().isQtQuick3DTexture()) { } else if (targetNode.metaInfo().isQtQuick3DTexture()) {
// if dropping an image on an existing texture, set the source // if dropping an image on an existing texture, set the source
targetNode.variantProperty("source").setValue(imagePath); targetNode.variantProperty("source").setValue(relativePathToQmlFile(imagePath));
return true; return true;
} else if (targetNode.metaInfo().isQtQuick3DModel()) { } else if (targetNode.metaInfo().isQtQuick3DModel()) {
QTimer::singleShot(0, view, [targetNode, imagePath, view]() { const QString relImagePath = relativePathToQmlFile(imagePath);
QTimer::singleShot(0, view, [targetNode, relImagePath, view]() {
if (view && targetNode.isValid()) { if (view && targetNode.isValid()) {
// To MaterialBrowserView. Done async to avoid custom notification in transaction // To MaterialBrowserView. Done async to avoid custom notification in transaction
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialBrowser"); QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialBrowser");
@@ -2010,7 +1983,7 @@ bool dropAsImage3dTexture(const ModelNode &targetNode,
{targetNode}, {targetNode},
{DocumentManager::currentFilePath() {DocumentManager::currentFilePath()
.absolutePath() .absolutePath()
.pathAppended(imagePath) .pathAppended(relImagePath)
.cleanPath() .cleanPath()
.toString()}); .toString()});
} }
@@ -2102,20 +2075,12 @@ ModelNode handleItemLibraryImageDrop(const QString &imagePath,
AbstractView *view = targetNode.view(); AbstractView *view = targetNode.view();
QTC_ASSERT(view, return {}); QTC_ASSERT(view, return {});
const QString imagePathRelative
= DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(
imagePath); // relative to .ui.qml file
ModelNode newModelNode; ModelNode newModelNode;
if (!dropAsImage3dTexture(targetNode, if (!dropAsImage3dTexture(targetNode, imagePath, newModelNode, outMoveNodesAfter)) {
targetProperty,
imagePathRelative,
newModelNode,
outMoveNodesAfter)) {
if (targetNode.metaInfo().isQtQuickImage() || targetNode.metaInfo().isQtQuickBorderImage()) { if (targetNode.metaInfo().isQtQuickImage() || targetNode.metaInfo().isQtQuickBorderImage()) {
// if dropping an image on an existing image, set the source // if dropping an image on an existing image, set the source
targetNode.variantProperty("source").setValue(imagePathRelative); targetNode.variantProperty("source").setValue(relativePathToQmlFile(imagePath));
} else { } else {
// create an image // create an image
QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromImage(view, QmlItemNode newItemNode = QmlItemNode::createQmlItemNodeFromImage(view,
@@ -2176,8 +2141,7 @@ ModelNode handleItemLibraryShaderDrop(const QString &shaderPath,
ModelNode newModelNode; ModelNode newModelNode;
const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath( const QString relPath = relativePathToQmlFile(shaderPath);
shaderPath);
if (targetNode.metaInfo().isQtQuick3DShader()) { if (targetNode.metaInfo().isQtQuick3DShader()) {
// if dropping into an existing Shader, update // if dropping into an existing Shader, update
@@ -2233,8 +2197,7 @@ ModelNode handleItemLibrarySoundDrop(const QString &soundPath,
ModelNode newModelNode; ModelNode newModelNode;
const QString relPath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath( const QString relPath = relativePathToQmlFile(soundPath);
soundPath);
if (targetNode.metaInfo().isQtMultimediaSoundEffect()) { if (targetNode.metaInfo().isQtMultimediaSoundEffect()) {
// if dropping into on an existing SoundEffect, update // if dropping into on an existing SoundEffect, update
@@ -2268,7 +2231,6 @@ ModelNode handleItemLibrarySoundDrop(const QString &soundPath,
} }
ModelNode handleItemLibraryTexture3dDrop(const QString &tex3DPath, ModelNode handleItemLibraryTexture3dDrop(const QString &tex3DPath,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode, const ModelNode &targetNode,
bool &outMoveNodesAfter) bool &outMoveNodesAfter)
{ {
@@ -2279,24 +2241,9 @@ ModelNode handleItemLibraryTexture3dDrop(const QString &tex3DPath,
if (!view->model()->hasImport(import, true, true)) if (!view->model()->hasImport(import, true, true))
return {}; return {};
const QString imagePath = DocumentManager::currentFilePath().toFileInfo().dir().relativeFilePath(
tex3DPath); // relative to qml file
ModelNode newModelNode; ModelNode newModelNode;
if (!dropAsImage3dTexture(targetNode, dropAsImage3dTexture(targetNode, tex3DPath, newModelNode, outMoveNodesAfter);
targetProperty,
imagePath,
newModelNode,
outMoveNodesAfter)) {
view->executeInTransaction("NavigatorTreeModel::handleItemLibraryTexture3dDrop", [&] {
// create a standalone Texture3D at drop location
newModelNode = createTextureNode(targetProperty, imagePath);
if (!NodeHints::fromModelNode(targetProperty.parentModelNode())
.canBeContainerFor(newModelNode))
newModelNode.destroy();
});
}
return newModelNode; return newModelNode;
} }

View File

@@ -137,7 +137,7 @@ bool useLayerEffect();
bool validateEffect(const QString &effectPath); bool validateEffect(const QString &effectPath);
bool isEffectComposerActivated(); bool isEffectComposerActivated();
Utils::FilePath getImagesDefaultDirectory(); QMLDESIGNERCOMPONENTS_EXPORT Utils::FilePath getImagesDefaultDirectory();
//Item Library and Assets related drop operations //Item Library and Assets related drop operations
QMLDESIGNERCOMPONENTS_EXPORT ModelNode handleItemLibraryEffectDrop(const QString &effectPath, QMLDESIGNERCOMPONENTS_EXPORT ModelNode handleItemLibraryEffectDrop(const QString &effectPath,
@@ -160,7 +160,6 @@ ModelNode handleItemLibrarySoundDrop(const QString &soundPath,
NodeAbstractProperty targetProperty, NodeAbstractProperty targetProperty,
const ModelNode &targetNode); const ModelNode &targetNode);
ModelNode handleItemLibraryTexture3dDrop(const QString &tex3DPath, ModelNode handleItemLibraryTexture3dDrop(const QString &tex3DPath,
NodeAbstractProperty targetProperty,
const ModelNode &targetNode, const ModelNode &targetNode,
bool &outMoveNodesAfter); bool &outMoveNodesAfter);

View File

@@ -252,6 +252,43 @@ void applyMaterialToModels(AbstractView *view, const ModelNode &material,
}); });
} }
ModelNode resolveSceneEnv(AbstractView *view, int sceneId)
{
ModelNode activeSceneEnv;
ModelNode selectedNode = view->firstSelectedModelNode();
if (selectedNode.metaInfo().isQtQuick3DSceneEnvironment()) {
activeSceneEnv = selectedNode;
} else if (sceneId != -1) {
ModelNode activeScene = Utils3D::active3DSceneNode(view);
if (activeScene.isValid()) {
QmlObjectNode view3D;
if (activeScene.metaInfo().isQtQuick3DView3D()) {
view3D = activeScene;
} else {
ModelNode sceneParent = activeScene.parentProperty().parentModelNode();
if (sceneParent.metaInfo().isQtQuick3DView3D())
view3D = sceneParent;
}
if (view3D.isValid())
activeSceneEnv = view->modelNodeForId(view3D.expression("environment"));
}
}
return activeSceneEnv;
}
void assignTextureAsLightProbe(AbstractView *view, const ModelNode &texture, int sceneId)
{
ModelNode sceneEnvNode = resolveSceneEnv(view, sceneId);
QmlObjectNode sceneEnv = sceneEnvNode;
if (sceneEnv.isValid()) {
sceneEnv.setBindingProperty("lightProbe", texture.id());
sceneEnv.setVariantProperty("backgroundMode",
QVariant::fromValue(Enumeration("SceneEnvironment", "SkyBox")));
}
}
// This method should be executed within a transaction as it performs multiple modifications to the model // This method should be executed within a transaction as it performs multiple modifications to the model
#ifdef QDS_USE_PROJECTSTORAGE #ifdef QDS_USE_PROJECTSTORAGE
ModelNode createMaterial(AbstractView *view, const TypeName &typeName) ModelNode createMaterial(AbstractView *view, const TypeName &typeName)

View File

@@ -40,10 +40,14 @@ void selectTexture(const ModelNode &texture);
ModelNode selectedMaterial(AbstractView *view); ModelNode selectedMaterial(AbstractView *view);
ModelNode selectedTexture(AbstractView *view); ModelNode selectedTexture(AbstractView *view);
ModelNode resolveSceneEnv(AbstractView *view, int sceneId);
QList<ModelNode> getSelectedModels(AbstractView *view); QList<ModelNode> getSelectedModels(AbstractView *view);
void applyMaterialToModels(AbstractView *view, const ModelNode &material, void applyMaterialToModels(AbstractView *view, const ModelNode &material,
const QList<ModelNode> &models, bool add = false); const QList<ModelNode> &models, bool add = false);
void assignTextureAsLightProbe(AbstractView *view, const ModelNode &texture, int sceneId);
#ifdef QDS_USE_PROJECTSTORAGE #ifdef QDS_USE_PROJECTSTORAGE
ModelNode createMaterial(AbstractView *view, const TypeName &typeName); ModelNode createMaterial(AbstractView *view, const TypeName &typeName);
#else #else

View File

@@ -57,7 +57,6 @@ ContentLibraryView::ContentLibraryView(AsynchronousImageCache &imageCache,
ExternalDependenciesInterface &externalDependencies) ExternalDependenciesInterface &externalDependencies)
: AbstractView(externalDependencies) : AbstractView(externalDependencies)
, m_imageCache(imageCache) , m_imageCache(imageCache)
, m_createTexture(this)
{} {}
ContentLibraryView::~ContentLibraryView() ContentLibraryView::~ContentLibraryView()
@@ -88,15 +87,17 @@ WidgetInfo ContentLibraryView::widgetInfo()
m_draggedBundleItem = item; m_draggedBundleItem = item;
}); });
connect(m_widget, &ContentLibraryWidget::addTextureRequested, this, connect(m_widget,
[&] (const QString &texPath, AddTextureMode mode) { &ContentLibraryWidget::addTextureRequested,
executeInTransaction("ContentLibraryView::widgetInfo", [&]() { this,
m_createTexture.execute(texPath, mode, m_sceneId); [&](const QString &texPath, AddTextureMode mode) {
}); executeInTransaction("ContentLibraryView::widgetInfo", [&]() {
}); CreateTexture(this).execute(texPath, mode, m_sceneId);
});
});
connect(m_widget, &ContentLibraryWidget::updateSceneEnvStateRequested, this, [this] { connect(m_widget, &ContentLibraryWidget::updateSceneEnvStateRequested, this, [this] {
ModelNode activeSceneEnv = m_createTexture.resolveSceneEnv(m_sceneId); ModelNode activeSceneEnv = Utils3D::resolveSceneEnv(this, m_sceneId);
const bool sceneEnvExists = activeSceneEnv.isValid(); const bool sceneEnvExists = activeSceneEnv.isValid();
m_widget->texturesModel()->setHasSceneEnv(sceneEnvExists); m_widget->texturesModel()->setHasSceneEnv(sceneEnvExists);
m_widget->environmentsModel()->setHasSceneEnv(sceneEnvExists); m_widget->environmentsModel()->setHasSceneEnv(sceneEnvExists);

View File

@@ -90,7 +90,6 @@ private:
bool m_bundleMaterialAddToSelected = false; bool m_bundleMaterialAddToSelected = false;
bool m_hasQuick3DImport = false; bool m_hasQuick3DImport = false;
qint32 m_sceneId = -1; qint32 m_sceneId = -1;
CreateTexture m_createTexture;
Utils::FilePath m_iconSavePath; Utils::FilePath m_iconSavePath;
QString m_generatedFolderName; QString m_generatedFolderName;
QString m_bundleId; QString m_bundleId;

View File

@@ -101,8 +101,9 @@ bool MoveManipulator::itemsCanReparented() const
void MoveManipulator::setDirectUpdateInNodeInstances(bool directUpdate) void MoveManipulator::setDirectUpdateInNodeInstances(bool directUpdate)
{ {
for (FormEditorItem* item : std::as_const(m_itemList)) { const auto allFormEditorItems = m_view->scene()->allFormEditorItems();
if (item && item->qmlItemNode().isValid()) for (FormEditorItem *item : std::as_const(m_itemList)) {
if (item && allFormEditorItems.contains(item) && item->qmlItemNode().isValid())
item->qmlItemNode().nodeInstance().setDirectUpdate(directUpdate); item->qmlItemNode().nodeInstance().setDirectUpdate(directUpdate);
} }
} }

View File

@@ -376,10 +376,11 @@ void ItemLibraryModel::update(Model *model)
for (const ItemLibraryEntry &entry : itemLibEntries) { for (const ItemLibraryEntry &entry : itemLibEntries) {
NodeMetaInfo metaInfo; NodeMetaInfo metaInfo;
if constexpr (useProjectStorage()) #ifdef QDS_USE_PROJECTSTORAGE
metaInfo = NodeMetaInfo{entry.typeId(), model->projectStorage()}; metaInfo = NodeMetaInfo{entry.typeId(), model->projectStorage()};
else #else
metaInfo = model->metaInfo(entry.typeName()); metaInfo = model->metaInfo(entry.typeName());
#endif
#ifdef QDS_USE_PROJECTSTORAGE #ifdef QDS_USE_PROJECTSTORAGE
bool valid = metaInfo.isValid(); bool valid = metaInfo.isValid();

View File

@@ -209,7 +209,7 @@ WidgetInfo MaterialBrowserView::widgetInfo()
}); });
connect(texturesModel, &MaterialBrowserTexturesModel::updateSceneEnvStateRequested, this, [this] { connect(texturesModel, &MaterialBrowserTexturesModel::updateSceneEnvStateRequested, this, [this] {
ModelNode activeSceneEnv = CreateTexture(this).resolveSceneEnv(m_sceneId); ModelNode activeSceneEnv = Utils3D::resolveSceneEnv(this, m_sceneId);
const bool sceneEnvExists = activeSceneEnv.isValid(); const bool sceneEnvExists = activeSceneEnv.isValid();
m_widget->materialBrowserTexturesModel()->setHasSceneEnv(sceneEnvExists); m_widget->materialBrowserTexturesModel()->setHasSceneEnv(sceneEnvExists);
}); });
@@ -222,12 +222,14 @@ WidgetInfo MaterialBrowserView::widgetInfo()
m_widget->materialBrowserTexturesModel()->setHasSingleModelSelection(hasModel); m_widget->materialBrowserTexturesModel()->setHasSingleModelSelection(hasModel);
}); });
connect(texturesModel, &MaterialBrowserTexturesModel::applyAsLightProbeRequested, this, connect(texturesModel,
[&] (const ModelNode &texture) { &MaterialBrowserTexturesModel::applyAsLightProbeRequested,
executeInTransaction(__FUNCTION__, [&] { this,
CreateTexture(this).assignTextureAsLightProbe(texture, m_sceneId); [&](const ModelNode &texture) {
}); executeInTransaction(__FUNCTION__, [&] {
}); Utils3D::assignTextureAsLightProbe(this, texture, m_sceneId);
});
});
} }
return createWidgetInfo(m_widget.data(), return createWidgetInfo(m_widget.data(),
@@ -239,13 +241,9 @@ WidgetInfo MaterialBrowserView::widgetInfo()
void MaterialBrowserView::createTextures(const QStringList &assetPaths) void MaterialBrowserView::createTextures(const QStringList &assetPaths)
{ {
auto *create = new CreateTextures(this);
executeInTransaction("MaterialBrowserView::createTextures", [&]() { executeInTransaction("MaterialBrowserView::createTextures", [&]() {
create->execute(assetPaths, AddTextureMode::Texture, m_sceneId); CreateTexture(this).execute(assetPaths, AddTextureMode::Texture, m_sceneId);
}); });
create->deleteLater();
} }
void MaterialBrowserView::modelAttached(Model *model) void MaterialBrowserView::modelAttached(Model *model)
@@ -740,11 +738,10 @@ void MaterialBrowserView::applyTextureToProperty(const QString &matId, const QSt
{ {
executeInTransaction(__FUNCTION__, [&] { executeInTransaction(__FUNCTION__, [&] {
if (m_appliedTextureId.isEmpty() && !m_appliedTexturePath.isEmpty()) { if (m_appliedTextureId.isEmpty() && !m_appliedTexturePath.isEmpty()) {
auto texCreator = new CreateTexture(this); CreateTexture texCreator(this);
ModelNode tex = texCreator->execute(m_appliedTexturePath, AddTextureMode::Texture); ModelNode tex = texCreator.execute(m_appliedTexturePath, AddTextureMode::Texture);
m_appliedTextureId = tex.id(); m_appliedTextureId = tex.id();
m_appliedTexturePath.clear(); m_appliedTexturePath.clear();
texCreator->deleteLater();
} }
QTC_ASSERT(!m_appliedTextureId.isEmpty(), return); QTC_ASSERT(!m_appliedTextureId.isEmpty(), return);

View File

@@ -307,10 +307,8 @@ void MaterialBrowserWidget::acceptBundleTextureDropOnMaterial(int matIndex, cons
ModelNode mat = m_materialBrowserModel->materialAt(matIndex); ModelNode mat = m_materialBrowserModel->materialAt(matIndex);
QTC_ASSERT(mat.isValid(), return); QTC_ASSERT(mat.isValid(), return);
auto *creator = new CreateTexture(m_materialBrowserView);
m_materialBrowserView->executeInTransaction(__FUNCTION__, [&] { m_materialBrowserView->executeInTransaction(__FUNCTION__, [&] {
ModelNode tex = creator->execute(bundleTexPath.toLocalFile()); ModelNode tex = CreateTexture(m_materialBrowserView).execute(bundleTexPath.toLocalFile());
QTC_ASSERT(tex.isValid(), return); QTC_ASSERT(tex.isValid(), return);
m_materialBrowserModel->selectMaterial(matIndex); m_materialBrowserModel->selectMaterial(matIndex);
@@ -319,8 +317,6 @@ void MaterialBrowserWidget::acceptBundleTextureDropOnMaterial(int matIndex, cons
if (m_materialBrowserView->model()) if (m_materialBrowserView->model())
m_materialBrowserView->model()->endDrag(); m_materialBrowserView->model()->endDrag();
creator->deleteLater();
} }
void MaterialBrowserWidget::acceptAssetsDrop(const QList<QUrl> &urls) void MaterialBrowserWidget::acceptAssetsDrop(const QList<QUrl> &urls)
@@ -336,14 +332,12 @@ void MaterialBrowserWidget::acceptAssetsDropOnMaterial(int matIndex, const QList
ModelNode mat = m_materialBrowserModel->materialAt(matIndex); ModelNode mat = m_materialBrowserModel->materialAt(matIndex);
QTC_ASSERT(mat.isValid(), return); QTC_ASSERT(mat.isValid(), return);
auto *creator = new CreateTexture(m_materialBrowserView); QString imageSrc = Utils::findOrDefault(urls, [](const QUrl &url) {
return Asset(url.toLocalFile()).isValidTextureSource();
QString imageSrc = Utils::findOrDefault(urls, [] (const QUrl &url) { }).toLocalFile();
return Asset(url.toLocalFile()).isValidTextureSource();
}).toLocalFile();
m_materialBrowserView->executeInTransaction(__FUNCTION__, [&] { m_materialBrowserView->executeInTransaction(__FUNCTION__, [&] {
ModelNode tex = creator->execute(imageSrc); ModelNode tex = CreateTexture(m_materialBrowserView).execute(imageSrc);
QTC_ASSERT(tex.isValid(), return); QTC_ASSERT(tex.isValid(), return);
m_materialBrowserModel->selectMaterial(matIndex); m_materialBrowserModel->selectMaterial(matIndex);
@@ -352,8 +346,6 @@ void MaterialBrowserWidget::acceptAssetsDropOnMaterial(int matIndex, const QList
if (m_materialBrowserView->model()) if (m_materialBrowserView->model())
m_materialBrowserView->model()->endDrag(); m_materialBrowserView->model()->endDrag();
creator->deleteLater();
} }
void MaterialBrowserWidget::acceptTextureDropOnMaterial(int matIndex, const QString &texId) void MaterialBrowserWidget::acceptTextureDropOnMaterial(int matIndex, const QString &texId)

View File

@@ -177,8 +177,8 @@ static void reparentModelNodeToNodeProperty(NodeAbstractProperty &parentProperty
} }
} }
NavigatorTreeModel::NavigatorTreeModel(QObject *parent) : QAbstractItemModel(parent) NavigatorTreeModel::NavigatorTreeModel(QObject *parent)
, m_createTextures(Utils::makeUniqueObjectPtr<CreateTextures>(m_view)) : QAbstractItemModel(parent)
{ {
m_actionManager = &QmlDesignerPlugin::instance()->viewManager().designerActionManager(); m_actionManager = &QmlDesignerPlugin::instance()->viewManager().designerActionManager();
} }
@@ -324,7 +324,10 @@ QList<ModelNode> NavigatorTreeModel::filteredList(const NodeListProperty &proper
if (filter) { if (filter) {
list.append(::Utils::filtered(nameFilteredList, [](const ModelNode &arg) { list.append(::Utils::filtered(nameFilteredList, [](const ModelNode &arg) {
const bool value = (QmlItemNode::isValidQmlItemNode(arg) || NodeHints::fromModelNode(arg).visibleInNavigator()) const bool visibleInNavigator = NodeHints::fromModelNode(arg).visibleInNavigator();
const bool hideInNavigator = NodeHints::fromModelNode(arg).hideInNavigator();
const bool value = ((QmlItemNode::isValidQmlItemNode(arg) && !hideInNavigator)
|| visibleInNavigator)
&& arg.id() != Constants::MATERIAL_LIB_ID; && arg.id() != Constants::MATERIAL_LIB_ID;
return value; return value;
})); }));
@@ -583,17 +586,10 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
bool moveNodesAfter = false; bool moveNodesAfter = false;
m_view->executeInTransaction(__FUNCTION__, [&] { m_view->executeInTransaction(__FUNCTION__, [&] {
m_createTextures->execute(QStringList{texturePath}, ModelNodeOperations::handleItemLibraryTexture3dDrop(texturePath,
AddTextureMode::Image, modelNodeForIndex(
Utils3D::active3DSceneId(m_view->model())); rowModelIndex),
QString textureName = Utils::FilePath::fromString(texturePath).fileName(); moveNodesAfter);
QString textureAbsolutePath = DocumentManager::currentResourcePath()
.pathAppended("images/" + textureName).toString();
ModelNodeOperations::handleItemLibraryImageDrop(textureAbsolutePath,
targetProperty,
modelNodeForIndex(
rowModelIndex),
moveNodesAfter);
}); });
} }
} }
@@ -664,7 +660,6 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
} else if (assetType == Constants::MIME_TYPE_ASSET_TEXTURE3D) { } else if (assetType == Constants::MIME_TYPE_ASSET_TEXTURE3D) {
currNode = ModelNodeOperations::handleItemLibraryTexture3dDrop( currNode = ModelNodeOperations::handleItemLibraryTexture3dDrop(
assetPath, assetPath,
targetProperty,
modelNodeForIndex(rowModelIndex), modelNodeForIndex(rowModelIndex),
moveNodesAfter); moveNodesAfter);
} else if (assetType == Constants::MIME_TYPE_ASSET_EFFECT) { } else if (assetType == Constants::MIME_TYPE_ASSET_EFFECT) {
@@ -853,34 +848,6 @@ bool QmlDesigner::NavigatorTreeModel::moveNodeToParent(const NodeAbstractPropert
return false; return false;
} }
ModelNode NavigatorTreeModel::createTextureNode(const NodeAbstractProperty &targetProp,
const QString &imagePath)
{
if (targetProp.isValid()) {
// create a texture item lib
ItemLibraryEntry itemLibraryEntry;
itemLibraryEntry.setName("Texture");
itemLibraryEntry.setType("QtQuick3D.Texture", 1, 0);
// set texture source
PropertyName prop = "source";
QString type = "QUrl";
QVariant val = imagePath;
itemLibraryEntry.addProperty(prop, type, val);
// create a texture
ModelNode newModelNode = QmlItemNode::createQmlObjectNode(m_view, itemLibraryEntry, {},
targetProp, false);
// Rename the node based on source image
QFileInfo fi(imagePath);
newModelNode.setIdWithoutRefactoring(
m_view->model()->generateNewId(fi.baseName(), "textureImage"));
return newModelNode;
}
return {};
}
namespace { namespace {
NodeMetaInfo propertyType(const NodeAbstractProperty &property) NodeMetaInfo propertyType(const NodeAbstractProperty &property)
{ {

View File

@@ -17,7 +17,6 @@ QT_FORWARD_DECLARE_CLASS(QPixmap)
namespace QmlDesigner { namespace QmlDesigner {
class CreateTextures;
class DesignerActionManager; class DesignerActionManager;
class Model; class Model;
class ModelNode; class ModelNode;
@@ -88,7 +87,7 @@ public:
void updateToolTipPixmap(const ModelNode &node, const QPixmap &pixmap); void updateToolTipPixmap(const ModelNode &node, const QPixmap &pixmap);
signals: signals:
void toolTipPixmapUpdated(const QString &id, const QPixmap &pixmap) const; void toolTipPixmapUpdated(const QString &id, const QPixmap &pixmap);
private: private:
void moveNodesInteractive(NodeAbstractProperty &parentProperty, const QList<ModelNode> &modelNodes, void moveNodesInteractive(NodeAbstractProperty &parentProperty, const QList<ModelNode> &modelNodes,
@@ -96,16 +95,17 @@ private:
void handleInternalDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex); void handleInternalDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
void handleItemLibraryItemDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex); void handleItemLibraryItemDrop(const QMimeData *mimeData, int rowNumber, const QModelIndex &dropModelIndex);
bool dropAsImage3dTexture(const ModelNode &targetNode, const NodeAbstractProperty &targetProp, bool dropAsImage3dTexture(const ModelNode &targetNode,
const QString &imagePath, ModelNode &newNode, bool &outMoveNodesAfter); const NodeAbstractProperty &targetProp,
ModelNode createTextureNode(const NodeAbstractProperty &targetProp, const QString &imagePath); const QString &imagePath,
ModelNode &newNode,
bool &outMoveNodesAfter);
QList<QPersistentModelIndex> nodesToPersistentIndex(const QList<ModelNode> &modelNodes); QList<QPersistentModelIndex> nodesToPersistentIndex(const QList<ModelNode> &modelNodes);
void addImport(const QString &importName); void addImport(const QString &importName);
QList<ModelNode> filteredList(const NodeListProperty &property, bool filter, bool reverseOrder) const; QList<ModelNode> filteredList(const NodeListProperty &property, bool filter, bool reverseOrder) const;
bool moveNodeToParent(const NodeAbstractProperty &targetProperty, const ModelNode &newModelNode); bool moveNodeToParent(const NodeAbstractProperty &targetProperty, const ModelNode &newModelNode);
QPointer<NavigatorView> m_view; QPointer<NavigatorView> m_view;
Utils::UniqueObjectPtr<CreateTextures> m_createTextures;
mutable QHash<ModelNode, QModelIndex> m_nodeIndexHash; mutable QHash<ModelNode, QModelIndex> m_nodeIndexHash;
mutable QHash<ModelNode, QList<ModelNode> > m_rowCache; mutable QHash<ModelNode, QList<ModelNode> > m_rowCache;
bool m_showOnlyVisibleItems = true; bool m_showOnlyVisibleItems = true;

Some files were not shown because too many files have changed in this diff Show More