diff --git a/src/easyshapes_app.qmlproject b/src/easyshapes_app.qmlproject index ff45ed3..1c9f27c 100644 --- a/src/easyshapes_app.qmlproject +++ b/src/easyshapes_app.qmlproject @@ -1,12 +1,13 @@ import QmlProject 1.1 + Project { mainFile: "easyshapes_app/main.qml" // List of module and plugin directories passed to QML runtime importPaths: [ "easyshapes_app", - "../../EasyApp/src", // EasyApp + "../../EasyApp/src", // EasyApplication ] // Include .qml files from specified directory and its subdirectories @@ -14,7 +15,7 @@ Project { directory: "easyshapes_app" } QmlFiles { - directory: "../../src/EasyApp" + directory: "../../EasyApp/src/EasyApplication" } // Include .js files from specified directory and its subdirectories @@ -22,7 +23,7 @@ Project { directory: "easyshapes_app" } JavaScriptFiles { - directory: "../../src/EasyApp" + directory: "../../EasyApp/src/EasyApplication" } // Include Module Definition Files (qmldir), as well as .ts and .qrc @@ -33,9 +34,11 @@ Project { recursive: true } Files { - directory: "../../src/EasyApp" + directory: "../../EasyApp/src/EasyApplication" filter: "qmldir;*.ts;*.qrc;*.html" recursive: true } + + mainUiFile: "easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/SampleModel.qml" } diff --git a/src/easyshapes_app.qrc b/src/easyshapes_app.qrc new file mode 100644 index 0000000..a69b8b4 --- /dev/null +++ b/src/easyshapes_app.qrc @@ -0,0 +1,205 @@ + + + ../../EasyApp/src/EasyApplication/Gui/Animations/ColorReset.qml + ../../EasyApp/src/EasyApplication/Gui/Animations/qmldir + ../../EasyApp/src/EasyApplication/Gui/Animations/ThemeChange.qml + ../../EasyApp/src/EasyApplication/Gui/Animations/TranslationChange.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/ChartViewHeatmap2dPlotly.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/ChartViewSimple1dPlotly.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/Plotly1dBarPlot.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/Plotly1dLine.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/Plotly1dMeasVsCalc.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/Plotly2dHeatmap.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/Plotly2dPolarHeatmap.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/Plotly3dScatter.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/Plotly3dSurface.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/qmldir + ../../EasyApp/src/EasyApplication/Gui/Charts/QtCharts1dBase 2.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/QtCharts1dBase.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/QtCharts1dMeasVsCalc.qml + ../../EasyApp/src/EasyApplication/Gui/Charts/QtCharts1dValueAxis.qml + ../../EasyApp/src/EasyApplication/Gui/Components/AboutDialog.qml + ../../EasyApp/src/EasyApplication/Gui/Components/AppBarCentralTabs.qml + ../../EasyApp/src/EasyApplication/Gui/Components/AppBarLeftButtons.qml + ../../EasyApp/src/EasyApplication/Gui/Components/AppBarRightButtons.qml + ../../EasyApp/src/EasyApplication/Gui/Components/ApplicationWindow.qml + ../../EasyApp/src/EasyApplication/Gui/Components/BasicReport.qml + ../../EasyApp/src/EasyApplication/Gui/Components/ContentArea.qml + ../../EasyApp/src/EasyApplication/Gui/Components/ContentPage.qml + ../../EasyApp/src/EasyApplication/Gui/Components/GuideWindow.qml + ../../EasyApp/src/EasyApplication/Gui/Components/GuideWindowContainer.qml + ../../EasyApp/src/EasyApplication/Gui/Components/JsonListModel.qml + ../../EasyApp/src/EasyApplication/Gui/Components/ListView.qml + ../../EasyApp/src/EasyApplication/Gui/Components/ListViewDelegate.qml + ../../EasyApp/src/EasyApplication/Gui/Components/ListViewHeader.qml + ../../EasyApp/src/EasyApplication/Gui/Components/ListViewTextInput.qml + ../../EasyApp/src/EasyApplication/Gui/Components/MainContent.qml + ../../EasyApp/src/EasyApplication/Gui/Components/PreferencesDialog.qml + ../../EasyApp/src/EasyApplication/Gui/Components/ProjectDescriptionDialog.qml + ../../EasyApp/src/EasyApplication/Gui/Components/qmldir + ../../EasyApp/src/EasyApplication/Gui/Components/SideBar.qml + ../../EasyApp/src/EasyApplication/Gui/Components/SideBarColumn.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableView.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableViewAdvancedLabel.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableViewButton.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableViewCheckBox.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableViewComboBox.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableViewDelegate.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableViewHeader.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableViewLabel.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableViewLabelControl.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableViewParameter.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableViewTextInput.qml + ../../EasyApp/src/EasyApplication/Gui/Components/TableViewTwoRowsAdvancedLabel.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/AppBarTabButton.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/ApplicationWindow.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/Button.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/CheckBox.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/CheckIndicator.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/ComboBox.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/CursorDelegate.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/Dialog.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/DialogButtonBox.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/GroupBox.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/GroupButton.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/GroupColumn.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/GroupRow.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/Label.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/LinkedImage.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/Menu.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/MenuItem.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/ParamComboBox.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/Parameter.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/ParamTextField.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/qmldir + ../../EasyApp/src/EasyApplication/Gui/Elements/RadioButton.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/RadioIndicator.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/RemoteController.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/RemotePointer.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/RunningLabel.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/ScrollBar.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/ScrollIndicator.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/SideBarButton.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/Slider.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/SliderHandle.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/SpinBox.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/SplashScreen.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/StatusBar.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/StatusBarItem.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/TabBar.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/TabButton.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/TextArea.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/TextField.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/TextInput.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/ToolButton.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/ToolTip.qml + ../../EasyApp/src/EasyApplication/Gui/Elements/ToolTipShadow.qml + ../../EasyApp/src/EasyApplication/Gui/Globals/qmldir + ../../EasyApp/src/EasyApplication/Gui/Globals/Vars.qml + ../../EasyApp/src/EasyApplication/Gui/Html/plotly-2.18.0.min.js + ../../EasyApp/src/EasyApplication/Gui/Logic/Plotting.js + ../../EasyApp/src/EasyApplication/Gui/Logic/ProjectConfig.js + ../../EasyApp/src/EasyApplication/Gui/Logic/qmldir + ../../EasyApp/src/EasyApplication/Gui/Logic/Translate.js + ../../EasyApp/src/EasyApplication/Gui/Logic/Utils.js + ../../EasyApp/src/EasyApplication/Gui/Style/Colors.qml + ../../EasyApp/src/EasyApplication/Gui/Style/Fonts.qml + ../../EasyApp/src/EasyApplication/Gui/Style/qmldir + ../../EasyApp/src/EasyApplication/Gui/Style/Sizes.qml + ../../EasyApp/src/EasyApplication/Gui/Style/Times.qml + ../../EasyApp/src/EasyApplication/Logic/Maintenance/qmldir + ../../EasyApp/src/EasyApplication/Logic/Maintenance/Updater.qml + easyshapes_app.qmlproject + easyshapes_app/Backends/MockBackend.qml + easyshapes_app/Backends/MockQml/Analysis.qml + easyshapes_app/Backends/MockQml/AnalysisConfig.qml + easyshapes_app/Backends/MockQml/BallStructure.qml + easyshapes_app/Backends/MockQml/BilayerStructure.qml + easyshapes_app/Backends/MockQml/Components.qml + easyshapes_app/Backends/MockQml/ComponentsFiles.qml + easyshapes_app/Backends/MockQml/Fractions.qml + easyshapes_app/Backends/MockQml/Ions.qml + easyshapes_app/Backends/MockQml/Lamellae.qml + easyshapes_app/Backends/MockQml/LatticeStructure.qml + easyshapes_app/Backends/MockQml/Layers.qml + easyshapes_app/Backends/MockQml/MonolayerStructure.qml + easyshapes_app/Backends/MockQml/PositionRestraints.qml + easyshapes_app/Backends/MockQml/Project.qml + easyshapes_app/Backends/MockQml/qmldir + easyshapes_app/Backends/MockQml/Report.qml + easyshapes_app/Backends/MockQml/RingStructure.qml + easyshapes_app/Backends/MockQml/RodStructure.qml + easyshapes_app/Backends/MockQml/SampleModel.qml + easyshapes_app/Backends/MockQml/Solution.qml + easyshapes_app/Backends/MockQml/Status.qml + easyshapes_app/Backends/MockQml/StructureFiles.qml + easyshapes_app/Backends/MockQml/VesicleStructure.qml + easyshapes_app/Backends/qmldir + easyshapes_app/Gui/ApplicationWindow.qml + easyshapes_app/Gui/Globals/ApplicationInfo.qml + easyshapes_app/Gui/Globals/BackendWrapper.qml + easyshapes_app/Gui/Globals/qmldir + easyshapes_app/Gui/Globals/References.qml + easyshapes_app/Gui/Pages/Analysis/Layout.qml + easyshapes_app/Gui/Pages/Analysis/MainArea/Chart.qml + easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Groups/AnalysisConfig.qml + easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Layout.qml + easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Popups/AddAnalysisConfigFiles.qml + easyshapes_app/Gui/Pages/Home/Content.qml + easyshapes_app/Gui/Pages/Home/Popups/About.qml + easyshapes_app/Gui/Pages/Project/Layout.qml + easyshapes_app/Gui/Pages/Project/MainArea/Description.qml + easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/Examples.qml + easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/GetStarted.qml + easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/Recent.qml + easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Layout.qml + easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Popups/OpenCifFile.qml + easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Popups/ProjectDescription.qml + easyshapes_app/Gui/Pages/Project/Sidebar/Extra/Groups/Scrolling.qml + easyshapes_app/Gui/Pages/Project/Sidebar/Extra/Layout.qml + easyshapes_app/Gui/Pages/Project/Sidebar/Text/Layout.qml + easyshapes_app/Gui/Pages/Report/Layout.qml + easyshapes_app/Gui/Pages/Report/MainArea/Summary.qml + easyshapes_app/Gui/Pages/Report/Sidebar/Basic/Groups/Export.qml + easyshapes_app/Gui/Pages/Report/Sidebar/Basic/Layout.qml + easyshapes_app/Gui/Pages/Report/Sidebar/Extra/Groups/Empty.qml + easyshapes_app/Gui/Pages/Report/Sidebar/Extra/Layout.qml + easyshapes_app/Gui/Pages/SampleModel/Layout.qml + easyshapes_app/Gui/Pages/SampleModel/MainArea/GraphsView.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/ComponentsFiles.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/PositionRestraints.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/StructureFiles.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Layout.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Popups/AddComponentFiles.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Popups/ReplaceStructure.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Components/Fractions.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/BallStructure.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/BilayerStructure.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Components.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Lamellae.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/LatticeStructure.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Layers.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/MonolayerStructure.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/RingStructure.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/RodStructure.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/SampleModel.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Solution.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/VesicleStructure.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Layout.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewComponent.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewModel.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewSolvent.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewSubstructure.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingComponent.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingIon.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingModel.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingSolvent.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingSubstructure.qml + easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/OpenAssetFile.qml + easyshapes_app/Gui/qmldir + easyshapes_app/Gui/StatusBar.qml + easyshapes_app/main.qml + easyshapes_app/test.qml + easyshapes_app/test2.qml + + \ No newline at end of file diff --git a/src/easyshapes_app/Backends/MockBackend.qml b/src/easyshapes_app/Backends/MockBackend.qml index f911028..81ea03a 100644 --- a/src/easyshapes_app/Backends/MockBackend.qml +++ b/src/easyshapes_app/Backends/MockBackend.qml @@ -12,8 +12,51 @@ import Backends.MockQml as MockLogic QtObject { property var project: MockLogic.Project + property var sampleModel: MockLogic.SampleModel + property var components: MockLogic.Components + // Advanced sidebar (Sample Model page) domain singletons. + property var componentsFiles: MockLogic.ComponentsFiles + property var positionRestraints: MockLogic.PositionRestraints + property var structureFiles: MockLogic.StructureFiles + // Default fractions set tied to the shared component list. + // Used as the fallback for the Fractions sidebar component when no + // per-row override is set. + property var fractions: MockLogic.Fractions { source: MockLogic.Components.loaded } + // Layers owns one Fractions instance per layer (per-row state) and + // shares the same component source so names stay in sync with the + // global components list. + property var layers: MockLogic.Layers + // Lamellae owns distinct inner/outer Fractions instances per lamella. + property var lamellae: MockLogic.Lamellae + // Ring structure parameters (single record, edited inline). + property var ringStructure: MockLogic.RingStructure + // Ball structure parameters (single record, edited inline). + property var ballStructure: MockLogic.BallStructure + // Vesicle structure parameters (single record, edited inline). + property var vesicleStructure: MockLogic.VesicleStructure + // Rod structure parameters (single record, edited inline). + property var rodStructure: MockLogic.RodStructure + // Bilayer structure parameters (single record, edited inline). + property var bilayerStructure: MockLogic.BilayerStructure + // Monolayer structure parameters (single record, edited inline). + property var monolayerStructure: MockLogic.MonolayerStructure + // Lattice structure parameters (single record, edited inline) plus a + // single-record substructure with its own asset library. + property var latticeStructure: MockLogic.LatticeStructure + // Solution group: solution (single-record, TIP3 default) + ions (max 2 rows). + property var solution: MockLogic.Solution + property var ions: MockLogic.Ions property var analysis: MockLogic.Analysis + // Analysis page — equilibration configuration (config files + force + // field + step range), driving the Equilibrate action. + property var analysisConfig: MockLogic.AnalysisConfig property var status: MockLogic.Status property var report: MockLogic.Report + Component.onCompleted: { + layers.fractionsSource = MockLogic.Components.loaded + lamellae.fractionsSource = MockLogic.Components.loaded + positionRestraints.source = MockLogic.Components.loaded + } + } diff --git a/src/easyshapes_app/Backends/MockQml/AnalysisConfig.qml b/src/easyshapes_app/Backends/MockQml/AnalysisConfig.qml new file mode 100644 index 0000000..eabbd5b --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/AnalysisConfig.qml @@ -0,0 +1,88 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +// Analysis page — equilibration configuration. +// +// Holds: +// * configFiles — Pattern D ListModel of .mdp paths (delete + edit per row). +// * forceFields — JS array of selectable force-field labels (no edits). +// * forceField — currently chosen entry from forceFields. +// * startStep / stopStep — inclusive integer bounds driving which mdp +// files participate in equilibration (defaults 0..6, matching the +// seeded equil0.mdp..equil6.mdp set). +QtObject { + + readonly property var configFiles: ListModel { + id: configFilesModel + ListElement { path: 'analysis/equil0.mdp' } + ListElement { path: 'analysis/equil1.mdp' } + ListElement { path: 'analysis/equil2.mdp' } + ListElement { path: 'analysis/equil3.mdp' } + ListElement { path: 'analysis/equil4.mdp' } + ListElement { path: 'analysis/equil5.mdp' } + ListElement { path: 'analysis/equil6.mdp' } + } + + readonly property var forceFields: [ + 'CHARMM36', + 'CHARMM27', + 'AMBER99SB-ILDN', + 'AMBER14SB', + 'OPLS-AA/L', + 'GROMOS54a7', + 'MARTINI 3', + 'MARTINI 2.2', + 'GAFF' + ] + + property string forceField: 'CHARMM36' + + property int startStep: 0 + property int stopStep: 6 + + function appendFile(item) { + configFilesModel.append({ path: item.path || '' }) + } + + function appendPath(path) { + configFilesModel.append({ path: path }) + } + + function removeFile(index) { + if (index < 0 || index >= configFilesModel.count) return + configFilesModel.remove(index) + } + + function clearFiles() { configFilesModel.clear() } + + // Mock placeholder for opening the file in an editor. + function editFile(index) { + if (index < 0 || index >= configFilesModel.count) return + console.debug('AnalysisConfig.editFile:', configFilesModel.get(index).path) + } + + function setForceField(value) { + if (forceField === value) return + forceField = value + } + + function setStartStep(value) { + const v = Math.max(0, parseInt(value)) + if (isNaN(v) || startStep === v) return + startStep = v + if (stopStep < startStep) stopStep = startStep + } + + function setStopStep(value) { + const v = Math.max(0, parseInt(value)) + if (isNaN(v) || stopStep === v) return + stopStep = v + if (startStep > stopStep) startStep = stopStep + } +} diff --git a/src/easyshapes_app/Backends/MockQml/BallStructure.qml b/src/easyshapes_app/Backends/MockQml/BallStructure.qml new file mode 100644 index 0000000..8b89830 --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/BallStructure.qml @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +QtObject { + property bool fxz: false + property bool rev: false + property string fill: 'FIBO' +} diff --git a/src/easyshapes_app/Backends/MockQml/BilayerStructure.qml b/src/easyshapes_app/Backends/MockQml/BilayerStructure.qml new file mode 100644 index 0000000..c4dd28c --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/BilayerStructure.qml @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +QtObject { + property double zsep: 0.0 + property int nside: 1 + property double dmin: 0.5 +} diff --git a/src/easyshapes_app/Backends/MockQml/Components.qml b/src/easyshapes_app/Backends/MockQml/Components.qml new file mode 100644 index 0000000..d75cfdb --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/Components.qml @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + +QtObject { + + // Working set of components currently loaded into the sample model. + // ListModel (not JS array) so EaComponents.ListView's ItemSelectionModel + // can drive Ctrl/Shift multi-selection, and so role-based delegate + // properties (`required property string name` etc.) bind correctly. + // Roles: name, component_type, atoms, mint, mext. + readonly property var loaded: ListModel { + id: loadedComponentsModel + } + + // File paths staged while creating a new component. + // Roles: path. + readonly property var pendingFilePaths: ListModel { + id: pendingFilePathsModel + } + + function appendItem(item) { + loadedComponentsModel.append({ + name: item.name, + component_type: item.component_type, + atoms: item.atoms, + mint: item.mint, + mext: item.mext + }) + } + + function removeItem(index) { + if (index < 0 || index >= loadedComponentsModel.count) return + loadedComponentsModel.remove(index) + } + + function clear() { + loadedComponentsModel.clear() + } + + function appendPendingFilePath(path) { + pendingFilePathsModel.append({ path: path }) + } + + function removePendingFilePath(index) { + if (index < 0 || index >= pendingFilePathsModel.count) return + pendingFilePathsModel.remove(index) + } + + function clearPendingFilePaths() { + pendingFilePathsModel.clear() + } + +} diff --git a/src/easyshapes_app/Backends/MockQml/ComponentsFiles.qml b/src/easyshapes_app/Backends/MockQml/ComponentsFiles.qml new file mode 100644 index 0000000..65f3b06 --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/ComponentsFiles.qml @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +// Pattern D backend (multi-row list with selection) for the per-component +// file list shown in the Advanced sidebar of the Sample Model page. +// +// The full state is a map of {componentName -> [paths]}. The UI only ever +// sees one component's file list at a time, so we keep that as a single +// ListModel (`files`) and repopulate it whenever the user picks a different +// component through `selectComponent(name)`. Storing the active list as a +// ListModel (rather than a JS array) keeps role-based delegate properties +// and ItemSelectionModel-driven selection working — see QML_MOCKUP_BACKEND +// Pattern D for the rationale. +QtObject { + id: root + + // Name of the component whose files are currently being shown. + // Empty string means no selection — `files` is then empty. + property string selectedComponent: '' + + // Internal map: componentName -> JS array of file paths. Mutations go + // through __syncBack() so the map stays consistent with the ListModel. + property var filesByComponent: ({ + 'DPPC': [ + 'assets/components/DPPC.itp', + 'assets/components/DPPC.gro', + 'assets/components/DPPC.pdb' + ], + 'DOPC': [ + 'assets/components/DOPC.itp', + 'assets/components/DOPC.gro' + ], + 'POPC': [ + 'assets/components/POPC.itp' + ] + }) + + // Active file list for `selectedComponent`. Roles: path. + readonly property var files: ListModel { + id: filesModel + } + + function selectComponent(name) { + if (selectedComponent === name) return + selectedComponent = name + filesModel.clear() + const list = filesByComponent[name] || [] + for (let i = 0; i < list.length; ++i) { + filesModel.append({ path: list[i] }) + } + } + + function appendFile(path) { + if (selectedComponent === '') return + filesModel.append({ path: path }) + __syncBack() + } + + function removeFile(index) { + if (index < 0 || index >= filesModel.count) return + filesModel.remove(index) + __syncBack() + } + + // Export the whole selected component (all its files). Mock backend + // just logs — the real backend will write to disk. + function exportComponent() { + if (selectedComponent === '') return + console.debug('ComponentsFiles.exportComponent:', selectedComponent, + JSON.stringify(filesByComponent[selectedComponent] || [])) + } + + // Persist the current file list for the selected component. Mock just + // mirrors filesModel back into filesByComponent — already kept in sync, + // so this is a no-op for the mock other than the debug print. + function save() { + if (selectedComponent === '') return + __syncBack() + console.debug('ComponentsFiles.save:', selectedComponent, + JSON.stringify(filesByComponent[selectedComponent])) + } + + function __syncBack() { + if (selectedComponent === '') return + const arr = [] + for (let i = 0; i < filesModel.count; ++i) { + arr.push(filesModel.get(i).path) + } + // Reassign the map so the `filesByComponent` property emits its + // Changed signal — keeps any future bindings on the map honest. + const copy = Object.assign({}, filesByComponent) + copy[selectedComponent] = arr + filesByComponent = copy + } +} diff --git a/src/easyshapes_app/Backends/MockQml/Fractions.qml b/src/easyshapes_app/Backends/MockQml/Fractions.qml new file mode 100644 index 0000000..92599eb --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/Fractions.qml @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick + + +// Fractions backend object — instantiable, NOT a singleton. +// One instance per layer/lamella. Tracks the shared component list via +// `source` and adds per-instance `fracs` / `present` state. Sync between +// `source` and the exposed `model` is owned here so GUI consumers only +// bind `model` and read/write roles via DelegateModel.ReadWrite. +QtObject { + id: root + + // Source list of components this fractions set tracks. + // Typically `MockLogic.Components.loaded`. May be reassigned. + property var source: null + + // ListModel exposed to the GUI ListView. + // Roles: name (mirrored from source), fracs (per-instance), + // present (per-instance). + readonly property var model: ListModel { id: backing } + + Component.onCompleted: rebuild() + onSourceChanged: rebuild() + + // Internal sync. Surgical inserts/removes preserve the per-instance + // fracs/present state of unaffected rows; full rebuild only on reset + // or source reassignment. + property var __sync: Connections { + target: root.source + + function onRowsInserted(parent, first, last) { + for (let i = first; i <= last; ++i) { + const c = root.source.get(i) + backing.insert(i, { name: c.name, fracs: 1, present: true }) + } + } + function onRowsRemoved(parent, first, last) { + for (let i = last; i >= first; --i) backing.remove(i) + } + function onDataChanged(topLeft, bottomRight, roles) { + for (let i = topLeft.row; i <= bottomRight.row; ++i) { + backing.setProperty(i, 'name', root.source.get(i).name) + } + } + function onModelReset() { root.rebuild() } + } + + function rebuild() { + backing.clear() + if (!source) return + for (let i = 0; i < source.count; ++i) { + const c = source.get(i) + backing.append({ name: c.name, fracs: 1, present: true }) + } + } + + function setFracs(index, value) { + if (index < 0 || index >= backing.count) return + backing.setProperty(index, 'fracs', value) + } + + function setPresent(index, value) { + if (index < 0 || index >= backing.count) return + backing.setProperty(index, 'present', value) + } +} diff --git a/src/easyshapes_app/Backends/MockQml/Ions.qml b/src/easyshapes_app/Backends/MockQml/Ions.qml new file mode 100644 index 0000000..73ed1c3 --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/Ions.qml @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +QtObject { + + // Loaded ions — at most 2 rows. ListModel so the chip Repeater rebinds + // on append/remove without manual reassignment. Roles: name. + readonly property var loaded: ListModel { + id: loadedIonsModel + } + + readonly property var available: ListModel { + ListElement { name: 'NA+' } + ListElement { name: 'CL-' } + ListElement { name: 'K+' } + ListElement { name: 'CA2+' } + ListElement { name: 'MG2+' } + ListElement { name: 'ZN2+' } + } + + // Hard cap at the backend layer. The button row also disables itself, + // but mocks must not be the only place enforcing the rule. + readonly property int maxIons: 2 + + function appendItem(item) { + if (loadedIonsModel.count >= maxIons) return + loadedIonsModel.append({ name: item.name }) + } + + function removeItem(index) { + if (index < 0 || index >= loadedIonsModel.count) return + loadedIonsModel.remove(index) + } + + function clear() { + loadedIonsModel.clear() + } + + function saveToCatalog() { + for (let i = 0; i < loadedIonsModel.count; ++i) { + const row = loadedIonsModel.get(i) + if (!row.name) continue + available.append({ name: row.name }) + } + } + + function removeFromCatalog(index) { + if (index < 0 || index >= available.count) return + available.remove(index) + } +} diff --git a/src/easyshapes_app/Backends/MockQml/Lamellae.qml b/src/easyshapes_app/Backends/MockQml/Lamellae.qml new file mode 100644 index 0000000..df7012e --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/Lamellae.qml @@ -0,0 +1,136 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +// Pattern D backend (multi-row list with selection) for lamellae, plus +// parallel arrays of per-lamella Fractions instances. Each lamella owns +// distinct inner and outer leaflet fractions so asymmetric lamellae can +// diverge without leaking state between leaflets. +QtObject { + id: root + + // Source of components shared with every leaflet's Fractions instance. + // Each leaflet keeps its own per-instance fracs/present state but + // mirrors the same component name list. + property var fractionsSource: null + + // Table of lamellae. Roles: rmin, innerDmin, outerDmin, shell, symmetric. + readonly property var items: ListModel { + id: lamellaeModel + ListElement { rmin: 0.5; innerDmin: 0.25; outerDmin: 0.3; shell: 0.5; symmetric: true } + } + + // Parallel arrays of Fractions QtObjects, one inner and one outer + // leaflet instance per lamella. + property var innerFractionsInstances: [] + property var outerFractionsInstances: [] + property int itemsRevision: 0 + property int fractionsRevision: 0 + + property var __fractionsComponent: null + + Component.onCompleted: { + __fractionsComponent = Qt.createComponent(Qt.resolvedUrl("Fractions.qml")) + if (__fractionsComponent.status !== Component.Ready) { + console.warn("Lamellae: failed to load Fractions.qml:", __fractionsComponent.errorString()) + return + } + const inner = [] + const outer = [] + for (let i = 0; i < lamellaeModel.count; ++i) { + inner.push(__createFractions()) + outer.push(__createFractions()) + } + innerFractionsInstances = inner + outerFractionsInstances = outer + itemsRevision++ + fractionsRevision++ + } + + function __createFractions() { + // Bind (don't snapshot) so per-instance Fractions follow later + // assignments to root.fractionsSource — singleton init order is + // not deterministic, so the source may be set after this runs. + const f = __fractionsComponent.createObject(root) + f.source = Qt.binding(function() { return root.fractionsSource }) + return f + } + + function appendItem(item) { + lamellaeModel.append({ + rmin: item.rmin, + innerDmin: item.innerDmin, + outerDmin: item.outerDmin, + shell: item.shell, + symmetric: item.symmetric + }) + + const inner = innerFractionsInstances.slice() + const outer = outerFractionsInstances.slice() + inner.push(__createFractions()) + outer.push(__createFractions()) + innerFractionsInstances = inner + outerFractionsInstances = outer + itemsRevision++ + fractionsRevision++ + } + + function removeItem(index) { + if (index < 0 || index >= lamellaeModel.count) return + lamellaeModel.remove(index) + + const inner = innerFractionsInstances.slice() + const outer = outerFractionsInstances.slice() + const removedInner = inner.splice(index, 1)[0] + const removedOuter = outer.splice(index, 1)[0] + if (removedInner) removedInner.destroy() + if (removedOuter) removedOuter.destroy() + innerFractionsInstances = inner + outerFractionsInstances = outer + itemsRevision++ + fractionsRevision++ + } + + function setRmin(index, value) { + if (index < 0 || index >= lamellaeModel.count) return + lamellaeModel.setProperty(index, 'rmin', value) + } + + function setInnerDmin(index, value) { + if (index < 0 || index >= lamellaeModel.count) return + lamellaeModel.setProperty(index, 'innerDmin', value) + } + + function setOuterDmin(index, value) { + if (index < 0 || index >= lamellaeModel.count) return + lamellaeModel.setProperty(index, 'outerDmin', value) + } + + function setShell(index, value) { + if (index < 0 || index >= lamellaeModel.count) return + lamellaeModel.setProperty(index, 'shell', value) + } + + function setSymmetric(index, value) { + if (index < 0 || index >= lamellaeModel.count) return + lamellaeModel.setProperty(index, 'symmetric', value) + itemsRevision++ + } + + function innerFractionsModelAt(index) { + if (index < 0 || index >= innerFractionsInstances.length) return null + const f = innerFractionsInstances[index] + return f ? f.model : null + } + + function outerFractionsModelAt(index) { + if (index < 0 || index >= outerFractionsInstances.length) return null + const f = outerFractionsInstances[index] + return f ? f.model : null + } +} diff --git a/src/easyshapes_app/Backends/MockQml/LatticeStructure.qml b/src/easyshapes_app/Backends/MockQml/LatticeStructure.qml new file mode 100644 index 0000000..c274952 --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/LatticeStructure.qml @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +QtObject { + property double alpha: 0.0 + property double theta: 0.0 + property double sbuff: 1.0 + property string latticeType: 'LAT1' + property int nlatx: 1 + property int nlaty: 1 + property int nlatz: 1 + + readonly property var latticeTypes: ['LAT1', 'LAT2', 'LAT3'] + + readonly property var substructureTypes: [ + 'Ring', 'Ball', 'Vesicle', 'Rod', 'Bilayer', 'Monolayer' + ] + + // Loaded substructure — JS array of length 0 or 1, mirroring the + // SampleModel `loaded` shape so EaComponents.ListView can consume it. + // Element shape: { name, structure_type, description, file_path }. + property var substructureLoaded: [] + + readonly property var availableSubstructures: ListModel { + ListElement { name: 'CubicSeed'; structure_type: 'Ball'; description: 'Spherical seed for cubic packing' } + ListElement { name: 'HexRing'; structure_type: 'Ring'; description: 'Ring substructure for hexagonal lattice' } + ListElement { name: 'BilayerTile'; structure_type: 'Bilayer'; description: 'Planar tile for stacked lattices' } + ListElement { name: 'RodSeed'; structure_type: 'Rod'; description: 'Rod-shaped seed' } + ListElement { name: 'VesicleSeed'; structure_type: 'Vesicle'; description: 'Vesicle seed for hollow lattices' } + } + + function setSubstructureLoaded(item) { + substructureLoaded = [{ + name: item.name, + structure_type: item.structure_type, + description: item.description, + file_path: item.file_path !== undefined ? item.file_path : '' + }] + } + + function updateSubstructureField(field, value) { + if (substructureLoaded.length === 0) return + substructureLoaded[0][field] = value + } + + function clearSubstructure() { + substructureLoaded = [] + } + + function saveSubstructureToCatalog() { + if (substructureLoaded.length === 0 || !substructureLoaded[0].name) return + availableSubstructures.append({ + name: substructureLoaded[0].name, + structure_type: substructureLoaded[0].structure_type, + description: substructureLoaded[0].description + }) + } + + function removeSubstructureFromCatalog(index) { + if (index < 0 || index >= availableSubstructures.count) return + availableSubstructures.remove(index) + } +} diff --git a/src/easyshapes_app/Backends/MockQml/Layers.qml b/src/easyshapes_app/Backends/MockQml/Layers.qml new file mode 100644 index 0000000..19cbbb2 --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/Layers.qml @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +// Pattern D backend (multi-row list with selection) for layers, plus a +// parallel JS array of per-layer Fractions instances. ListElement only +// accepts literal scalars, so the Fractions QtObjects can't live inside +// the ListModel rows — they're maintained in lockstep here. +QtObject { + id: root + + // Source of components shared with every layer's Fractions instance. + // Each layer keeps its own per-instance fracs/present state but + // mirrors the same name list. + property var fractionsSource: null + + // Table of layers. Roles: dmin, rmin. + readonly property var items: ListModel { + id: layersModel + ListElement { dmin: 0.25; rmin: 0.5 } + } + + // Parallel array of Fractions QtObjects, one per layer. + // Bumping the revision token triggers rebinding for `var` consumers, + // since rebuilding the array reference (not its contents) is what + // QML actually signals on. + property var fractionsInstances: [] + property int fractionsRevision: 0 + + property var __fractionsComponent: null + + Component.onCompleted: { + __fractionsComponent = Qt.createComponent(Qt.resolvedUrl("Fractions.qml")) + if (__fractionsComponent.status !== Component.Ready) { + console.warn("Layers: failed to load Fractions.qml:", __fractionsComponent.errorString()) + return + } + const arr = [] + for (let i = 0; i < layersModel.count; ++i) arr.push(__createFractions()) + fractionsInstances = arr + fractionsRevision++ + } + + function __createFractions() { + // Bind (don't snapshot) so per-instance Fractions follow later + // assignments to root.fractionsSource — singleton init order is + // not deterministic, so the source may be set after this runs. + const f = __fractionsComponent.createObject(root) + f.source = Qt.binding(function() { return root.fractionsSource }) + return f + } + + function appendItem(item) { + layersModel.append({ dmin: item.dmin, rmin: item.rmin }) + const arr = fractionsInstances.slice() + arr.push(__createFractions()) + fractionsInstances = arr + fractionsRevision++ + } + + function removeItem(index) { + if (index < 0 || index >= layersModel.count) return + layersModel.remove(index) + const arr = fractionsInstances.slice() + const removed = arr.splice(index, 1)[0] + if (removed) removed.destroy() + fractionsInstances = arr + fractionsRevision++ + } + + function setDmin(index, value) { + if (index < 0 || index >= layersModel.count) return + layersModel.setProperty(index, 'dmin', value) + } + + function setRmin(index, value) { + if (index < 0 || index >= layersModel.count) return + layersModel.setProperty(index, 'rmin', value) + } + + function fractionsModelAt(index) { + if (index < 0 || index >= fractionsInstances.length) return null + const f = fractionsInstances[index] + return f ? f.model : null + } +} diff --git a/src/easyshapes_app/Backends/MockQml/MonolayerStructure.qml b/src/easyshapes_app/Backends/MockQml/MonolayerStructure.qml new file mode 100644 index 0000000..c4dd28c --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/MonolayerStructure.qml @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +QtObject { + property double zsep: 0.0 + property int nside: 1 + property double dmin: 0.5 +} diff --git a/src/easyshapes_app/Backends/MockQml/PositionRestraints.qml b/src/easyshapes_app/Backends/MockQml/PositionRestraints.qml new file mode 100644 index 0000000..0ea67aa --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/PositionRestraints.qml @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +// Pattern D backend (multi-row list with selection) for position restraints +// shown in the Advanced sidebar of the Sample Model page. +// +// Rows are derived from the shared Components list: one restraint row per +// loaded component, with the component name mirrored read-only and the +// atom / p_form / geom / radius / f_constant fields owned here. Sync is +// surgical so editing one component doesn't reset the others' state. +// +// Roles: component_name, atom, p_form, geom, radius, f_constant. +QtObject { + id: root + + // Shared component list this mirrors. Wired by MockBackend at startup; + // kept as a property (rather than a hard import) to side-step singleton + // init-order quirks — same approach as Layers/Lamellae fractionsSource. + property var source: null + + readonly property var items: ListModel { id: itemsModel } + + onSourceChanged: rebuild() + + property var __sync: Connections { + target: root.source + + function onRowsInserted(parent, first, last) { + for (let i = first; i <= last; ++i) { + const c = root.source.get(i) + itemsModel.insert(i, { + component_name: c.name, + atom: 1, + p_form: 1, + geom: 1, + radius: 0.5, + f_constant: 'POSRES_FC_LIP' + }) + } + } + function onRowsRemoved(parent, first, last) { + for (let i = last; i >= first; --i) itemsModel.remove(i) + } + function onDataChanged(topLeft, bottomRight, roles) { + for (let i = topLeft.row; i <= bottomRight.row; ++i) { + itemsModel.setProperty(i, 'component_name', root.source.get(i).name) + } + } + function onModelReset() { root.rebuild() } + } + + function rebuild() { + itemsModel.clear() + if (!source) return + for (let i = 0; i < source.count; ++i) { + const c = source.get(i) + itemsModel.append({ + component_name: c.name, + atom: 1, + p_form: 1, + geom: 1, + radius: 0.5, + f_constant: 'POSRES_FC_LIP' + }) + } + } + + function setAtom(index, value) { + if (index < 0 || index >= itemsModel.count) return + itemsModel.setProperty(index, 'atom', value) + } + + function setPForm(index, value) { + if (index < 0 || index >= itemsModel.count) return + itemsModel.setProperty(index, 'p_form', value) + } + + function setGeom(index, value) { + if (index < 0 || index >= itemsModel.count) return + itemsModel.setProperty(index, 'geom', value) + } + + function setRadius(index, value) { + if (index < 0 || index >= itemsModel.count) return + itemsModel.setProperty(index, 'radius', value) + } +} diff --git a/src/easyshapes_app/Backends/MockQml/RingStructure.qml b/src/easyshapes_app/Backends/MockQml/RingStructure.qml new file mode 100644 index 0000000..1ec3975 --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/RingStructure.qml @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +QtObject { + property double dmin: 0.5 + property double rmin: 0.25 + property double alpha: 0.0 + property double theta: 0.0 + property double fxz: 0.0 + property double rev: 0.0 +} diff --git a/src/easyshapes_app/Backends/MockQml/RodStructure.qml b/src/easyshapes_app/Backends/MockQml/RodStructure.qml new file mode 100644 index 0000000..5c0770f --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/RodStructure.qml @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +QtObject { + property double dmin: 0.5 + property double rmin: 0.25 + property int turns: 1 + property bool fxz: false + property bool rev: false +} diff --git a/src/easyshapes_app/Backends/MockQml/SampleModel.qml b/src/easyshapes_app/Backends/MockQml/SampleModel.qml new file mode 100644 index 0000000..7fef0d2 --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/SampleModel.qml @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + +QtObject { + + property bool created: false + + // Loaded sample model — JS array of length 0 or 1. + // Capacity is one by convention (single record), but the value is + // exposed as an array so QML ListView can consume it directly + // (Qt 6 ListView only supports int / ListModel / QAbstractItemModel / JS array). + // Element shape: { name, structure_type, description } + property var loaded: [] + + readonly property var structureTypes: [ + 'Ring', 'Ball', 'Vesicle', 'Rod', 'Bilayer', 'Monolayer', 'Lattice' + ] + + // Externally-observed structure type. Decoupled from `loaded` because + // updateField mutates `loaded[0]` in place (preserving the row delegate + // and any focused TextInput) and that mutation does NOT fire loadedChanged. + // The wrapper writes this property whenever structure_type is updated; + // setLoaded/clear keep it in sync with the active record. + property string currentStructureType: '' + + // Catalog of saveable/loadable models (asset library). + // ListModel mirrors the Python QAbstractListModel with roles + // (name, structure_type, description) so delegate code is identical + // for both real and mock backends, and ItemSelectionModel works. + readonly property var availableModels: ListModel { + ListElement { name: 'Samle1_aluv'; structure_type: 'Vesicle'; description: 'In order to avoid a prolonged pro-inflammatory neutrophil response, signaling downstream of an agonist-activated G protein-coupled receptor (GPCR) has to be rapidly terminated. Among the family of GPCR kinases (GRKs) that regulate receptor phosphorylation and signaling termination, GRK2, which is highly expressed by immune cells, plays an important role.' } + ListElement { name: 'Sample2_nanodisc'; structure_type: 'Ring'; description: 'The medium chain fatty acid receptor GPR84 as well as formyl peptide receptor 2 (FPR2)' } + ListElement { name: 'Sample3_cubosome'; structure_type: 'Lattice'; description: 'receptors expressed in neutrophils, play a key role in regulating inflammation. In this study, we investigated the effects of GRK2 inhibitors on neutrophil functions induced by GPR84 and FPR2 agonists.' } + ListElement { name: 'Sample4'; structure_type: 'Ring'; description: 'GRK2 was shown to be expressed in human neutrophils and analysis of subcellular fractions' } + ListElement { name: 'Sample5'; structure_type: 'Ball'; description: 'revealed a cytosolic localization. The GRK2 inhibitors enhanced and prolonged neutrophil production ' } + ListElement { name: 'Sample6'; structure_type: 'Vesicle'; description: 'production of reactive oxygen species (ROS) induced by GPR84- but not FPR2-agonists' } + ListElement { name: 'Sample7'; structure_type: 'Rod'; description: 'suggesting a receptor selective function of GRK2. This suggestion was supported by β-arrestin recruitment data. The ROS production induced by a non β-arrestin recruiting GPR84' } + ListElement { name: 'Sample8'; structure_type: 'Bilayer'; description: 'This suggestion was supported by β-arrestin recruitment data. The ROS production induced by a non β-arrestin recruiting GPR84 agonist was not affected by the GRK2 inhibitor.' } + ListElement { name: 'Sample9'; structure_type: 'Monolayer'; description: 'Termination of this β-arrestin independent response relied, similar to the response induced by FPR2 agonists, primarily on the actin cytoskeleton.' } + ListElement { name: 'Samplewithareallylongname'; structure_type: 'Lattice'; description: 'In summary, we show that GPR84 utilizes GRK2 in concert with β-arrestin and actin cytoskeleton dependent processes to fine-tune the activity of the ROS generating NADPH-oxidase in neutrophils.' } + } + + function setLoaded(model) { + console.debug(`Loading sample model '${model.name}'`) + loaded = [{ + name: model.name, + structure_type: model.structure_type, + description: model.description + }] + currentStructureType = model.structure_type + created = true + } + + function updateField(field, value) { + if (loaded.length === 0) return + loaded[0][field] = value + } + + function clear() { + loaded = [] + currentStructureType = '' + created = false + } + + function saveToCatalog() { + if (loaded.length === 0 || !loaded[0].name) return + availableModels.append({ + name: loaded[0].name, + structure_type: loaded[0].structure_type, + description: loaded[0].description + }) + console.debug(`Saved sample model '${loaded[0].name}' to catalog`) + } + + function removeFromCatalog(index) { + if (index < 0 || index >= availableModels.count) return + availableModels.remove(index) + } + +} diff --git a/src/easyshapes_app/Backends/MockQml/Solution.qml b/src/easyshapes_app/Backends/MockQml/Solution.qml new file mode 100644 index 0000000..940f661 --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/Solution.qml @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +QtObject { + + readonly property var solutionTypes: ['other', 'water_3site', 'water_4site', 'water_5site', 'protic_polar', 'halogenated', 'denaturant'] + + property var loaded: [{ + name: 'TIP3P', + solvent_type: 'water_3site', + description: '3-site model standard with AMBER and CHARMM. Uses spc216.gro coordinates.', + file_path: '' + }] + + readonly property var available: ListModel { + id: solventModel + + ListElement { + name: "SPC" + solvent_type: "water_3site" + description: "Simple Point Charge water. Use spc216.gro with -ci. Standard for GROMOS force fields." + } + ListElement { + name: "SPC/E" + solvent_type: "water_3site" + description: "Extended SPC with self-polarization correction. Uses spc216.gro coordinates." + } + ListElement { + name: "TIP3P" + solvent_type: "water_3site" + description: "3-site model standard with AMBER and CHARMM. Uses spc216.gro coordinates." + } + ListElement { + name: "TIP4P" + solvent_type: "water_4site" + description: "4-site water with virtual site. Use tip4p.gro." + } + ListElement { + name: "TIP4P-Ew" + solvent_type: "water_4site" + description: "TIP4P optimized for Ewald summation. Available in AMBER ports." + } + ListElement { + name: "TIP4P/2005" + solvent_type: "water_4site" + description: "Reparameterized TIP4P with excellent bulk properties. Shipped with recent GROMACS." + } + ListElement { + name: "TIP5P" + solvent_type: "water_5site" + description: "5-site model with lone-pair virtual sites. Use tip5p.gro." + } + ListElement { + name: "TIP5PE" + solvent_type: "water_5site" + description: "TIP5P variant for Ewald electrostatics." + } + + ListElement { + name: "Methanol" + solvent_type: "protic_polar" + description: "CH3OH. Equilibrated box methanol.gro shipped with GROMACS (OPLS-AA)." + } + ListElement { + name: "1,2-Dichloroethane" + solvent_type: "halogenated" + description: "ClCH2CH2Ch. Equilibrated box ClCH2CH2Ch.gro included for OPLS-AA." + } + + ListElement { + name: "Urea" + solvent_type: "denaturant" + description: "Equilibrated urea box urea.gro shipped with GROMACS for cosolvent simulations." + } + } + + function setLoaded(item) { + loaded = [{ + name: item.name, + solvent_type: item.solvent_type, + description: item.description, + file_path: item.file_path !== undefined ? item.file_path : '' + }] + } + + // Mutate loaded[0] in place — same trick as SampleModel.updateField — so + // the ListView delegate and any focused TextInput survive the edit. + function updateField(field, value) { + if (loaded.length === 0) return + loaded[0][field] = value + } + + function clear() { + loaded = [] + } + + function saveToCatalog() { + if (loaded.length === 0 || !loaded[0].name) return + available.append({ + name: loaded[0].name, + solvent_type: loaded[0].solvent_type, + description: loaded[0].description + }) + } + + function removeFromCatalog(index) { + if (index < 0 || index >= available.count) return + available.remove(index) + } +} diff --git a/src/easyshapes_app/Backends/MockQml/StructureFiles.qml b/src/easyshapes_app/Backends/MockQml/StructureFiles.qml new file mode 100644 index 0000000..61838eb --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/StructureFiles.qml @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +// Pattern D backend (multi-row list with selection) for structure files +// shown in the Advanced sidebar of the Sample Model page. +// Roles: path. +QtObject { + + readonly property var files: ListModel { + id: filesModel + ListElement { path: 'structure/topology.top' } + ListElement { path: 'structure/coordinates.gro' } + ListElement { path: 'structure/serialized.dat' } + } + + function appendItem(item) { + filesModel.append({ path: item.path || '' }) + } + + function appendPath(path) { + filesModel.append({ path: path }) + } + + function removeItem(index) { + if (index < 0 || index >= filesModel.count) return + filesModel.remove(index) + } + + function clear() { + filesModel.clear() + } + + // Mock placeholder — real backend will persist to the asset library. + function saveToLib() { + console.debug('StructureFiles.saveToLib: paths=' + filesModel.count) + } +} diff --git a/src/easyshapes_app/Backends/MockQml/VesicleStructure.qml b/src/easyshapes_app/Backends/MockQml/VesicleStructure.qml new file mode 100644 index 0000000..8b89830 --- /dev/null +++ b/src/easyshapes_app/Backends/MockQml/VesicleStructure.qml @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +pragma Singleton + +import QtQuick + + +QtObject { + property bool fxz: false + property bool rev: false + property string fill: 'FIBO' +} diff --git a/src/easyshapes_app/Backends/MockQml/qmldir b/src/easyshapes_app/Backends/MockQml/qmldir index 7ee148e..580dcd6 100644 --- a/src/easyshapes_app/Backends/MockQml/qmldir +++ b/src/easyshapes_app/Backends/MockQml/qmldir @@ -1,6 +1,24 @@ module MockQml singleton Project Project.qml +singleton SampleModel SampleModel.qml +singleton Components Components.qml +singleton ComponentsFiles ComponentsFiles.qml +singleton PositionRestraints PositionRestraints.qml +singleton StructureFiles StructureFiles.qml +Fractions Fractions.qml +singleton Layers Layers.qml +singleton Lamellae Lamellae.qml +singleton RingStructure RingStructure.qml +singleton BallStructure BallStructure.qml +singleton VesicleStructure VesicleStructure.qml +singleton RodStructure RodStructure.qml +singleton BilayerStructure BilayerStructure.qml +singleton MonolayerStructure MonolayerStructure.qml +singleton LatticeStructure LatticeStructure.qml +singleton Solution Solution.qml +singleton Ions Ions.qml singleton Analysis Analysis.qml +singleton AnalysisConfig AnalysisConfig.qml singleton Report Report.qml singleton Status Status.qml diff --git a/src/easyshapes_app/Gui/ApplicationWindow.qml b/src/easyshapes_app/Gui/ApplicationWindow.qml index 3996326..149e8f9 100644 --- a/src/easyshapes_app/Gui/ApplicationWindow.qml +++ b/src/easyshapes_app/Gui/ApplicationWindow.qml @@ -5,9 +5,9 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui as Gui import Gui.Globals as Globals @@ -75,9 +75,9 @@ EaComponents.ApplicationWindow { EaElements.AppBarTabButton { id: sampleModelButton enabled: false - fontIcon: 'puzzle-piece' - text: qsTr('Sample model') - ToolTip.text: qsTr('Sample model definition page') + fontIcon: 'vial' // 'layer-group' 'vials' 'vial' + text: qsTr('Sample Model') // qsTr('Model') + ToolTip.text: qsTr('Sample Model definition page') Component.onCompleted: { Globals.References.applicationWindow.appBarCentralTabs.sampleModelButton = sampleModelButton } @@ -108,16 +108,8 @@ EaComponents.ApplicationWindow { Component.onCompleted: { Globals.References.applicationWindow.appBarCentralTabs.summaryButton = summaryButton } - }, - // Summary page - - // Toolbox page - EaElements.AppBarTabButton { - fontIcon: 'toolbox' - text: qsTr('Toolbox') - ToolTip.text: qsTr('Toolbox with common widgets') } - // Toolbox page + // Summary page ] @@ -131,8 +123,7 @@ EaComponents.ApplicationWindow { Loader { source: 'Pages/Project/Layout.qml' }, Loader { source: 'Pages/SampleModel/Layout.qml' }, Loader { source: 'Pages/Analysis/Layout.qml' }, - Loader { source: 'Pages/Report/Layout.qml' }, - Loader { source: 'Pages/Toolbox/Layout.qml' } + Loader { source: 'Pages/Report/Layout.qml' } ] ///////////// diff --git a/src/easyshapes_app/Gui/Globals/BackendWrapper.qml b/src/easyshapes_app/Gui/Globals/BackendWrapper.qml index e8529b3..bdcfba1 100644 --- a/src/easyshapes_app/Gui/Globals/BackendWrapper.qml +++ b/src/easyshapes_app/Gui/Globals/BackendWrapper.qml @@ -56,6 +56,189 @@ QtObject { function projectSave() { activeBackend.project.save() } function projectEditInfo(path, new_value) { activeBackend.project.editInfo(path, new_value) } + //////////////////// + // Sample Model page + //////////////////// + + readonly property var sampleModelLoaded: activeBackend.sampleModel.loaded + readonly property var sampleModelAvailable: activeBackend.sampleModel.availableModels + readonly property var sampleModelStructureTypes: activeBackend.sampleModel.structureTypes + readonly property string sampleModelCurrentStructureType: activeBackend.sampleModel.currentStructureType + + property bool sampleModelCreated: activeBackend.sampleModel.created + onSampleModelCreatedChanged: activeBackend.sampleModel.created = sampleModelCreated + + function sampleModelSetLoaded(model) { activeBackend.sampleModel.setLoaded(model) } + function sampleModelUpdateField(field, value) { + activeBackend.sampleModel.updateField(field, value) + // updateField mutates `loaded[0]` in place to avoid recreating the + // ListView delegate (which would steal focus from any field being + // edited). The trade-off is that `loadedChanged` doesn't fire, so + // bindings derived from `loaded` won't re-evaluate. Mirror the + // structure_type into its own property here so visibility bindings + // (Layers/Lamellae GroupBoxes) update. + if (field === 'structure_type') activeBackend.sampleModel.currentStructureType = value + } + function sampleModelClear() { activeBackend.sampleModel.clear() } + function sampleModelSaveToCatalog() { activeBackend.sampleModel.saveToCatalog() } + function sampleModelRemoveFromCatalog(index) { activeBackend.sampleModel.removeFromCatalog(index) } + + // Components group (Sample Model sidebar) + readonly property var componentsLoaded: activeBackend.components.loaded + readonly property var componentsPendingFilePaths: activeBackend.components.pendingFilePaths + + function componentsAppend(item) { activeBackend.components.appendItem(item) } + function componentsRemove(index) { activeBackend.components.removeItem(index) } + function componentsClear() { activeBackend.components.clear() } + function componentsAppendPendingFilePath(path) { activeBackend.components.appendPendingFilePath(path) } + function componentsRemovePendingFilePath(index) { activeBackend.components.removePendingFilePath(index) } + function componentsClearPendingFilePaths() { activeBackend.components.clearPendingFilePaths() } + + // Global default Fractions set (fallback for the Fractions sidebar + // component). Layers/Lamellae do NOT use this — see the per-row + // fraction accessors below. + readonly property var fractionsModel: activeBackend.fractions.model + + function fractionsSetFracs(index, value) { activeBackend.fractions.setFracs(index, value) } + function fractionsSetPresent(index, value) { activeBackend.fractions.setPresent(index, value) } + + // Layers (Sample Model sidebar). One Fractions instance per layer is + // owned by the backend; the GUI binds the fractions list of the + // currently-selected layer via layersFractionsModelAt(row). + readonly property var layersItems: activeBackend.layers.items + readonly property int layersFractionsRevision: activeBackend.layers.fractionsRevision + + function layersAppend(item) { activeBackend.layers.appendItem(item) } + function layersRemove(index) { activeBackend.layers.removeItem(index) } + function layersSetDmin(index, value) { activeBackend.layers.setDmin(index, value) } + function layersSetRmin(index, value) { activeBackend.layers.setRmin(index, value) } + // Callers binding through this must also reference layersFractionsRevision + // in their binding body to re-evaluate when rows are inserted/removed. + function layersFractionsModelAt(index) { return activeBackend.layers.fractionsModelAt(index) } + + // Lamellae (Sample Model sidebar). One inner and one outer Fractions + // instance are owned per lamella; asymmetric lamellae bind both models. + readonly property var lamellaeItems: activeBackend.lamellae.items + readonly property int lamellaeItemsRevision: activeBackend.lamellae.itemsRevision + readonly property int lamellaeFractionsRevision: activeBackend.lamellae.fractionsRevision + + function lamellaeAppend(item) { activeBackend.lamellae.appendItem(item) } + function lamellaeRemove(index) { activeBackend.lamellae.removeItem(index) } + function lamellaeSetRmin(index, value) { activeBackend.lamellae.setRmin(index, value) } + function lamellaeSetInnerDmin(index, value) { activeBackend.lamellae.setInnerDmin(index, value) } + function lamellaeSetOuterDmin(index, value) { activeBackend.lamellae.setOuterDmin(index, value) } + function lamellaeSetShell(index, value) { activeBackend.lamellae.setShell(index, value) } + function lamellaeSetSymmetric(index, value) { activeBackend.lamellae.setSymmetric(index, value) } + // Callers binding through these must also reference lamellaeFractionsRevision + // in their binding body to re-evaluate when rows are inserted/removed. + function lamellaeInnerFractionsModelAt(index) { return activeBackend.lamellae.innerFractionsModelAt(index) } + function lamellaeOuterFractionsModelAt(index) { return activeBackend.lamellae.outerFractionsModelAt(index) } + + // Ring structure (Sample Model sidebar). Single-record fielded form + // shown only when sampleModelCurrentStructureType === 'Ring'. + readonly property var ringStructure: activeBackend.ringStructure + + // Ball structure (Sample Model sidebar). Single-record fielded form + // shown only when sampleModelCurrentStructureType === 'Ball'. + readonly property var ballStructure: activeBackend.ballStructure + + // Vesicle structure (Sample Model sidebar). Single-record fielded form + // shown only when sampleModelCurrentStructureType === 'Vesicle'. + readonly property var vesicleStructure: activeBackend.vesicleStructure + + // Rod structure (Sample Model sidebar). Single-record fielded form + // shown only when sampleModelCurrentStructureType === 'Rod'. + readonly property var rodStructure: activeBackend.rodStructure + + // Bilayer structure (Sample Model sidebar). Single-record fielded form + // shown only when sampleModelCurrentStructureType === 'Bilayer'. + readonly property var bilayerStructure: activeBackend.bilayerStructure + + // Monolayer structure (Sample Model sidebar). Single-record fielded form + // shown only when sampleModelCurrentStructureType === 'Monolayer'. + readonly property var monolayerStructure: activeBackend.monolayerStructure + + // Lattice structure (Sample Model sidebar). Single-record fielded form + // shown only when sampleModelCurrentStructureType === 'Lattice'. The + // substructure slot mirrors the SampleModel loaded/available shape, so + // the embedded list/popups follow the same recipe. + readonly property var latticeStructure: activeBackend.latticeStructure + readonly property var latticeSubstructureLoaded: activeBackend.latticeStructure.substructureLoaded + readonly property var latticeSubstructureAvailable: activeBackend.latticeStructure.availableSubstructures + readonly property var latticeSubstructureTypes: activeBackend.latticeStructure.substructureTypes + + function latticeSubstructureSetLoaded(item) { activeBackend.latticeStructure.setSubstructureLoaded(item) } + function latticeSubstructureClear() { activeBackend.latticeStructure.clearSubstructure() } + function latticeSubstructureSaveToCatalog() { activeBackend.latticeStructure.saveSubstructureToCatalog() } + function latticeSubstructureRemoveFromCatalog(index) { activeBackend.latticeStructure.removeSubstructureFromCatalog(index) } + + // Solution group (Sample Model sidebar). Two backend-owned slots: + // * solution — single-record JS array (capacity 0 or 1), TIP3 by default. + // * ions — ListModel with at most 2 rows, name only. + + readonly property var solutionLoaded: activeBackend.solution.loaded + readonly property var solutionAvailable: activeBackend.solution.available + readonly property var solutionTypes: activeBackend.solution.solutionTypes + + function solutionSetLoaded(item) { activeBackend.solution.setLoaded(item) } + function solutionUpdateField(field, value) { activeBackend.solution.updateField(field, value) } + function solutionClear() { activeBackend.solution.clear() } + function solutionSaveToCatalog() { activeBackend.solution.saveToCatalog() } + function solutionRemoveFromCatalog(index) { activeBackend.solution.removeFromCatalog(index) } + + // Solvent aliases — same backend object, used by the solvent table popup files. + readonly property var solventLoaded: activeBackend.solution.loaded + readonly property var solventAvailable: activeBackend.solution.available + readonly property var solventTypes: activeBackend.solution.solutionTypes + + function solventSetLoaded(item) { activeBackend.solution.setLoaded(item) } + function solventUpdateField(field, value) { activeBackend.solution.updateField(field, value) } + function solventClear() { activeBackend.solution.clear() } + function solventSaveToCatalog() { activeBackend.solution.saveToCatalog() } + function solventRemoveFromCatalog(index) { activeBackend.solution.removeFromCatalog(index) } + + readonly property var ionsLoaded: activeBackend.ions.loaded + readonly property var ionsAvailable: activeBackend.ions.available + + function ionsAppend(item) { activeBackend.ions.appendItem(item) } + function ionsRemove(index) { activeBackend.ions.removeItem(index) } + function ionsClear() { activeBackend.ions.clear() } + function ionsSaveToCatalog() { activeBackend.ions.saveToCatalog() } + function ionsRemoveFromCatalog(index) { activeBackend.ions.removeFromCatalog(index) } + + // Components Files (Sample Model Advanced sidebar). Per-component file + // list driven by `componentsFilesSelectedComponent`; switching the + // selection repopulates `componentsFilesFiles`. + readonly property var componentsFilesFiles: activeBackend.componentsFiles.files + readonly property string componentsFilesSelectedComponent: activeBackend.componentsFiles.selectedComponent + + function componentsFilesSelect(name) { activeBackend.componentsFiles.selectComponent(name) } + function componentsFilesAppend(path) { activeBackend.componentsFiles.appendFile(path) } + function componentsFilesRemove(index) { activeBackend.componentsFiles.removeFile(index) } + function componentsFilesExportComponent() { activeBackend.componentsFiles.exportComponent() } + function componentsFilesSave() { activeBackend.componentsFiles.save() } + + // Position Restraints (Sample Model Advanced sidebar). One row per + // loaded component (mirrored from componentsLoaded); component_name is + // read-only here. f_constant is fixed to POSRES_FC_LIP for the mock. + readonly property var positionRestraintsItems: activeBackend.positionRestraints.items + + function positionRestraintsSetAtom(index, value) { activeBackend.positionRestraints.setAtom(index, value) } + function positionRestraintsSetPForm(index, value) { activeBackend.positionRestraints.setPForm(index, value) } + function positionRestraintsSetGeom(index, value) { activeBackend.positionRestraints.setGeom(index, value) } + function positionRestraintsSetRadius(index, value) { activeBackend.positionRestraints.setRadius(index, value) } + + // Structure Files (Sample Model Advanced sidebar). List of structure- + // related files plus actions for saving the set to the asset library + // and re-seeding structure parameters from a serialized data file. + readonly property var structureFilesFiles: activeBackend.structureFiles.files + + function structureFilesAppend(item) { activeBackend.structureFiles.appendItem(item) } + function structureFilesAppendPath(path) { activeBackend.structureFiles.appendPath(path) } + function structureFilesRemove(index) { activeBackend.structureFiles.removeItem(index) } + function structureFilesClear() { activeBackend.structureFiles.clear() } + function structureFilesSaveToLib() { activeBackend.structureFiles.saveToLib() } + //////////////// // Analysis page //////////////// @@ -63,6 +246,21 @@ QtObject { // All the properties and methods related to the analysis page // are defined directly in the Backends/MockQml/Analysis.qml !!! + // Analysis configuration group — equilibration .mdp files, the chosen + // force field, and the inclusive [start, stop] step range. + readonly property var analysisConfigFiles: activeBackend.analysisConfig.configFiles + readonly property var analysisConfigForceFields: activeBackend.analysisConfig.forceFields + readonly property string analysisConfigForceField: activeBackend.analysisConfig.forceField + readonly property int analysisConfigStartStep: activeBackend.analysisConfig.startStep + readonly property int analysisConfigStopStep: activeBackend.analysisConfig.stopStep + + function analysisConfigAppendPath(path) { activeBackend.analysisConfig.appendPath(path) } + function analysisConfigRemoveFile(index) { activeBackend.analysisConfig.removeFile(index) } + function analysisConfigEditFile(index) { activeBackend.analysisConfig.editFile(index) } + function analysisConfigSetForceField(value) { activeBackend.analysisConfig.setForceField(value) } + function analysisConfigSetStartStep(value) { activeBackend.analysisConfig.setStartStep(value) } + function analysisConfigSetStopStep(value) { activeBackend.analysisConfig.setStopStep(value) } + /////////////// // Summary page /////////////// diff --git a/src/easyshapes_app/Gui/Globals/References.qml b/src/easyshapes_app/Gui/Globals/References.qml index 53be81c..9189a76 100644 --- a/src/easyshapes_app/Gui/Globals/References.qml +++ b/src/easyshapes_app/Gui/Globals/References.qml @@ -34,6 +34,22 @@ QtObject { } } }, + 'samplemodel': { + 'sidebar': { + 'basic': { + 'popups': { + 'LoadExistingModel': null, + 'CreateNewModel': null, + 'LoadExistingComponent': null, + 'CreateNewComponent': null, + 'OpenAssetFile': null + }, + 'groups': { + 'sampleModel': null + } + } + } + }, 'analysis': { 'sidebar': { 'basic': { diff --git a/src/easyshapes_app/Gui/Pages/Analysis/Layout.qml b/src/easyshapes_app/Gui/Pages/Analysis/Layout.qml index 4c9d546..3315766 100644 --- a/src/easyshapes_app/Gui/Pages/Analysis/Layout.qml +++ b/src/easyshapes_app/Gui/Pages/Analysis/Layout.qml @@ -5,10 +5,10 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Analysis/MainArea/Chart.qml b/src/easyshapes_app/Gui/Pages/Analysis/MainArea/Chart.qml index 4f8ce10..1d07269 100644 --- a/src/easyshapes_app/Gui/Pages/Analysis/MainArea/Chart.qml +++ b/src/easyshapes_app/Gui/Pages/Analysis/MainArea/Chart.qml @@ -5,8 +5,8 @@ import QtQuick import QtGraphs -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Groups/AnalysisConfig.qml b/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Groups/AnalysisConfig.qml new file mode 100644 index 0000000..03eb8f4 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Groups/AnalysisConfig.qml @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents + +import Gui.Globals as Globals + + +EaElements.GroupColumn { + + EaComponents.ListView { + id: configFilesList + defaultInfoText: qsTr('No configuration files added') + multiSelection: false + + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 2.5, + -1, + EaStyle.Sizes.tableRowHeight, + EaStyle.Sizes.tableRowHeight + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr('№') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('Path') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: Globals.BackendWrapper.analysisConfigFiles + + delegateModelAccess: DelegateModel.ReadWrite + + delegate: EaComponents.ListViewDelegate { + required property int index + required property string path + + EaComponents.TableViewLabel { + text: index + 1 + enabled: false + } + EaComponents.TableViewLabel { + text: path + elide: Text.ElideLeft + } + EaComponents.TableViewButton { + fontIcon: 'edit' + ToolTip.text: qsTr('Edit this file') + onClicked: Globals.BackendWrapper.analysisConfigEditFile(index) + } + EaComponents.TableViewButton { + fontIcon: 'minus-circle' + ToolTip.text: qsTr('Remove this file') + onClicked: Globals.BackendWrapper.analysisConfigRemoveFile(index) + } + } + } + + EaElements.SideBarButton { + fontIcon: 'plus-circle' + text: qsTr('Add file(s)') + width: EaStyle.Sizes.sideBarContentWidth * 0.48 + ToolTip.text: qsTr('Pick one or more .mdp files from disk') + onClicked: addFilesLoader.item.open() + } + + // ForceField selector on the left, step-range pair on the right. + Row { + spacing: EaStyle.Sizes.fontPixelSize + property real halfWidth: (EaStyle.Sizes.sideBarContentWidth - spacing) / 2 + property real fieldWidth: (halfWidth - EaStyle.Sizes.fontPixelSize * 0.5) / 2 + + EaElements.ComboBox { + id: forceFieldSelector + width: parent.halfWidth + topInset: forceFieldLabel.height + topPadding: topInset + padding + model: Globals.BackendWrapper.analysisConfigForceFields + currentIndex: Math.max( + 0, + Globals.BackendWrapper.analysisConfigForceFields.indexOf( + Globals.BackendWrapper.analysisConfigForceField)) + onActivated: (i) => Globals.BackendWrapper.analysisConfigSetForceField( + Globals.BackendWrapper.analysisConfigForceFields[i]) + + EaElements.Label { + id: forceFieldLabel + text: qsTr('ForceField') + } + } + + Row { + spacing: EaStyle.Sizes.fontPixelSize * 0.5 + + EaElements.TextField { + id: startStepField + width: parent.parent.fieldWidth + topInset: startStepLabel.height + topPadding: topInset + padding + horizontalAlignment: TextInput.AlignLeft + inputMethodHints: Qt.ImhDigitsOnly + validator: IntValidator { bottom: 0 } + text: Globals.BackendWrapper.analysisConfigStartStep + onEditingFinished: Globals.BackendWrapper.analysisConfigSetStartStep(text) + + EaElements.Label { + id: startStepLabel + text: qsTr('Start step') + } + } + + EaElements.TextField { + id: stopStepField + width: parent.parent.fieldWidth + topInset: stopStepLabel.height + topPadding: topInset + padding + horizontalAlignment: TextInput.AlignLeft + inputMethodHints: Qt.ImhDigitsOnly + validator: IntValidator { bottom: 0 } + text: Globals.BackendWrapper.analysisConfigStopStep + onEditingFinished: Globals.BackendWrapper.analysisConfigSetStopStep(text) + + EaElements.Label { + id: stopStepLabel + text: qsTr('Stop step') + } + } + } + } + + Loader { + id: addFilesLoader + source: '../Popups/AddAnalysisConfigFiles.qml' + } +} diff --git a/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Groups/GetStarted.qml b/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Groups/GetStarted.qml deleted file mode 100644 index 7b73f23..0000000 --- a/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Groups/GetStarted.qml +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic - -import Gui.Globals as Globals - -EaElements.GroupColumn { - - // 1st row - EaElements.GroupRow { - spacing: EaStyle.Sizes.fontPixelSize - - // button - EaElements.SideBarButton { - id: generateDataButton - - fontIcon: 'plus-circle' - text: qsTr('Generate new data') - - onClicked: { - console.debug(`Clicking '${text}' button ::: ${this}`) - Globals.BackendWrapper.activeBackend.analysis.generateData() - } - } - // button - - // text input - EaElements.ParamTextField { - height: generateDataButton.height - - inputMethodHints: Qt.ImhDigitsOnly - - value: Globals.BackendWrapper.activeBackend.analysis.dataSize - units: 'points' - - onTextChanged: { - Globals.BackendWrapper.activeBackend.analysis.dataSize = text - } - } - // text input - } - // 1st row -} diff --git a/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Layout.qml b/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Layout.qml index e59ca3c..d647b5b 100644 --- a/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Layout.qml +++ b/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Layout.qml @@ -5,8 +5,9 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals @@ -14,11 +15,29 @@ import Gui.Globals as Globals EaComponents.SideBarColumn { EaElements.GroupBox { - title: qsTr('Get started') - icon: 'rocket' + title: qsTr('Equilibration setup') + icon: 'sliders-h' collapsed: false - Loader { source: 'Groups/GetStarted.qml' } + Loader { source: 'Groups/AnalysisConfig.qml' } + } + + // Centered "Equilibrate" call-to-action below the groups. + Item { + width: parent.width + height: equilibrateButton.height + EaStyle.Sizes.fontPixelSize + + EaElements.SideBarButton { + id: equilibrateButton + anchors.centerIn: parent + text: qsTr('Equilibrate') + fontIcon: 'magic' + width: EaStyle.Sizes.sideBarContentWidth + enabled: Globals.BackendWrapper.analysisConfigFiles + ? Globals.BackendWrapper.analysisConfigFiles.count > 0 + : false + onClicked: console.debug('Equilibrate clicked') + } } } diff --git a/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Popups/AddAnalysisConfigFiles.qml b/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Popups/AddAnalysisConfigFiles.qml new file mode 100644 index 0000000..34cc2ce --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/Analysis/Sidebar/Basic/Popups/AddAnalysisConfigFiles.qml @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Dialogs + +import Gui.Globals as Globals + + +FileDialog { + fileMode: FileDialog.OpenFiles + nameFilters: [ + 'GROMACS run parameter files (*.mdp)', + 'Any (*)' + ] + + onAccepted: { + for (let i = 0; i < selectedFiles.length; ++i) { + Globals.BackendWrapper.analysisConfigAppendPath(selectedFiles[i].toString()) + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/Home/Content.qml b/src/easyshapes_app/Gui/Pages/Home/Content.qml index e0b7a19..420851d 100644 --- a/src/easyshapes_app/Gui/Pages/Home/Content.qml +++ b/src/easyshapes_app/Gui/Pages/Home/Content.qml @@ -4,9 +4,9 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Home/Popups/About.qml b/src/easyshapes_app/Gui/Pages/Home/Popups/About.qml index 11815fd..c1af059 100644 --- a/src/easyshapes_app/Gui/Pages/Home/Popups/About.qml +++ b/src/easyshapes_app/Gui/Pages/Home/Popups/About.qml @@ -4,8 +4,8 @@ import QtQuick -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Project/Layout.qml b/src/easyshapes_app/Gui/Pages/Project/Layout.qml index 6ebbb95..f7c6116 100644 --- a/src/easyshapes_app/Gui/Pages/Project/Layout.qml +++ b/src/easyshapes_app/Gui/Pages/Project/Layout.qml @@ -5,10 +5,10 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Project/MainArea/Description.qml b/src/easyshapes_app/Gui/Pages/Project/MainArea/Description.qml index 8ce72b2..eab134f 100644 --- a/src/easyshapes_app/Gui/Pages/Project/MainArea/Description.qml +++ b/src/easyshapes_app/Gui/Pages/Project/MainArea/Description.qml @@ -4,8 +4,8 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/Examples.qml b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/Examples.qml index 065031c..bfd3695 100644 --- a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/Examples.qml +++ b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/Examples.qml @@ -5,11 +5,11 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Logic as EaLogic import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/GetStarted.qml b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/GetStarted.qml index cb1ab18..83e643d 100644 --- a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/GetStarted.qml +++ b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/GetStarted.qml @@ -6,11 +6,11 @@ import QtQuick import QtQuick.Controls //import QtQuick.Dialogs -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Logic as EaLogic import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/Recent.qml b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/Recent.qml index 8357f95..e9158c1 100644 --- a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/Recent.qml +++ b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Groups/Recent.qml @@ -6,11 +6,11 @@ import QtQuick import QtQuick.Controls import QtCore -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Logic as EaLogic import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Layout.qml b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Layout.qml index 23952b4..0cdbc68 100644 --- a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Layout.qml +++ b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Layout.qml @@ -5,8 +5,8 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Popups/OpenCifFile.qml b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Popups/OpenCifFile.qml index 649bb8a..29580d6 100644 --- a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Popups/OpenCifFile.qml +++ b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Popups/OpenCifFile.qml @@ -6,8 +6,8 @@ import QtQuick import QtQuick.Controls import QtQuick.Dialogs -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Popups/ProjectDescription.qml b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Popups/ProjectDescription.qml index dcc2277..68d21c2 100644 --- a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Popups/ProjectDescription.qml +++ b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Basic/Popups/ProjectDescription.qml @@ -5,8 +5,8 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Extra/Groups/Scrolling.qml b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Extra/Groups/Scrolling.qml index 4bc8920..5521228 100644 --- a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Extra/Groups/Scrolling.qml +++ b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Extra/Groups/Scrolling.qml @@ -4,8 +4,8 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements Column { diff --git a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Extra/Layout.qml b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Extra/Layout.qml index 273d95a..fd00806 100644 --- a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Extra/Layout.qml +++ b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Extra/Layout.qml @@ -5,8 +5,8 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Text/Layout.qml b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Text/Layout.qml index 3c9c9d6..9c4629b 100644 --- a/src/easyshapes_app/Gui/Pages/Project/Sidebar/Text/Layout.qml +++ b/src/easyshapes_app/Gui/Pages/Project/Sidebar/Text/Layout.qml @@ -4,8 +4,8 @@ import QtQuick -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents EaComponents.SideBarColumn {} diff --git a/src/easyshapes_app/Gui/Pages/Report/Layout.qml b/src/easyshapes_app/Gui/Pages/Report/Layout.qml index 3beddbd..6604f52 100644 --- a/src/easyshapes_app/Gui/Pages/Report/Layout.qml +++ b/src/easyshapes_app/Gui/Pages/Report/Layout.qml @@ -5,10 +5,10 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Report/MainArea/Summary.qml b/src/easyshapes_app/Gui/Pages/Report/MainArea/Summary.qml index 0168ba3..8c1bcd4 100644 --- a/src/easyshapes_app/Gui/Pages/Report/MainArea/Summary.qml +++ b/src/easyshapes_app/Gui/Pages/Report/MainArea/Summary.qml @@ -5,9 +5,9 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Report/Sidebar/Basic/Groups/Export.qml b/src/easyshapes_app/Gui/Pages/Report/Sidebar/Basic/Groups/Export.qml index e80a6b4..7cc971b 100644 --- a/src/easyshapes_app/Gui/Pages/Report/Sidebar/Basic/Groups/Export.qml +++ b/src/easyshapes_app/Gui/Pages/Report/Sidebar/Basic/Groups/Export.qml @@ -7,10 +7,10 @@ import QtQuick.Controls import QtQuick.Dialogs import QtCore -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Logic as EaLogic +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Logic as EaLogic import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Report/Sidebar/Basic/Layout.qml b/src/easyshapes_app/Gui/Pages/Report/Sidebar/Basic/Layout.qml index 472d957..059d6de 100644 --- a/src/easyshapes_app/Gui/Pages/Report/Sidebar/Basic/Layout.qml +++ b/src/easyshapes_app/Gui/Pages/Report/Sidebar/Basic/Layout.qml @@ -4,8 +4,8 @@ import QtQuick -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/Report/Sidebar/Extra/Groups/Empty.qml b/src/easyshapes_app/Gui/Pages/Report/Sidebar/Extra/Groups/Empty.qml index 9f47804..4401fe7 100644 --- a/src/easyshapes_app/Gui/Pages/Report/Sidebar/Extra/Groups/Empty.qml +++ b/src/easyshapes_app/Gui/Pages/Report/Sidebar/Extra/Groups/Empty.qml @@ -4,8 +4,8 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements Column {} diff --git a/src/easyshapes_app/Gui/Pages/Report/Sidebar/Extra/Layout.qml b/src/easyshapes_app/Gui/Pages/Report/Sidebar/Extra/Layout.qml index ef2a2ed..7219c46 100644 --- a/src/easyshapes_app/Gui/Pages/Report/Sidebar/Extra/Layout.qml +++ b/src/easyshapes_app/Gui/Pages/Report/Sidebar/Extra/Layout.qml @@ -5,8 +5,8 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Layout.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Layout.qml index ac5e6e4..0450c67 100644 --- a/src/easyshapes_app/Gui/Pages/SampleModel/Layout.qml +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Layout.qml @@ -5,10 +5,10 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals @@ -27,11 +27,13 @@ EaComponents.ContentPage { sideBar: EaComponents.SideBar { tabs: [ - EaElements.TabButton { text: qsTr('Basic controls') } + EaElements.TabButton { text: qsTr('Basic controls') }, + EaElements.TabButton { text: qsTr('Advanced controls') } ] items: [ - Loader { source: 'Sidebar/Basic/Layout.qml' } + Loader { source: 'Sidebar/Basic/Layout.qml' }, + Loader { source: 'Sidebar/Advanced/Layout.qml' } ] continueButton.text: qsTr('Continue') diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/MainArea/GraphsView.qml b/src/easyshapes_app/Gui/Pages/SampleModel/MainArea/GraphsView.qml index 9f2fd2d..276dfff 100644 --- a/src/easyshapes_app/Gui/Pages/SampleModel/MainArea/GraphsView.qml +++ b/src/easyshapes_app/Gui/Pages/SampleModel/MainArea/GraphsView.qml @@ -5,8 +5,8 @@ import QtQuick import QtGraphs -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements import Gui.Globals as Globals diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/ComponentsFiles.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/ComponentsFiles.qml new file mode 100644 index 0000000..53681f7 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/ComponentsFiles.qml @@ -0,0 +1,169 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents + +import Gui.Globals as Globals + + +EaElements.GroupColumn { + property double buttonWidth: EaStyle.Sizes.sideBarContentWidth * 0.3164 + + // ComboBox + empty-state placeholder share a single slot so the layout + // doesn't reflow between the two states. The ComboBox is hidden (not + // just disabled) when there are no components because its popup keeps + // an ElevationEffect that leaks a soft shadow when the model is empty. + readonly property int componentsCount: + Globals.BackendWrapper.componentsLoaded + ? Globals.BackendWrapper.componentsLoaded.count + : 0 + + EaElements.ComboBox { + id: componentSelector + visible: componentsCount > 0 + width: EaStyle.Sizes.sideBarContentWidth + topInset: componentLabel.height + topPadding: topInset + padding + textRole: 'name' + model: Globals.BackendWrapper.componentsLoaded + + EaElements.Label { + id: componentLabel + text: qsTr('Component') + } + + onActivated: (i) => { + const row = Globals.BackendWrapper.componentsLoaded.get(i) + if (row) Globals.BackendWrapper.componentsFilesSelect(row.name) + } + + // Seed the selection from the currently-selected component, or the + // first component once the model populates. + function syncFromBackend() { + const total = componentsCount + if (total === 0) { + currentIndex = -1 + return + } + const sel = Globals.BackendWrapper.componentsFilesSelectedComponent + if (sel !== '') { + for (let i = 0; i < total; ++i) { + if (Globals.BackendWrapper.componentsLoaded.get(i).name === sel) { + currentIndex = i + return + } + } + } + currentIndex = 0 + const row = Globals.BackendWrapper.componentsLoaded.get(0) + if (row) Globals.BackendWrapper.componentsFilesSelect(row.name) + } + + Component.onCompleted: syncFromBackend() + // Re-seed when the user adds the first component on the Basic tab. + Connections { + target: Globals.BackendWrapper.componentsLoaded + function onCountChanged() { componentSelector.syncFromBackend() } + } + } + + EaElements.Label { + visible: componentsCount === 0 + enabled: false + width: EaStyle.Sizes.sideBarContentWidth + text: qsTr('Load a component on the Basic controls tab to manage its files here.') + wrapMode: Text.WordWrap + } + + EaComponents.ListView { + id: componentFilesList + defaultInfoText: qsTr('No files associated with this component') + multiSelection: false + + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 2.5, + -1, + EaStyle.Sizes.tableRowHeight + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr('№') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('Path') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: Globals.BackendWrapper.componentsFilesFiles + + delegateModelAccess: DelegateModel.ReadWrite + + delegate: EaComponents.ListViewDelegate { + required property int index + required property string path + + EaComponents.TableViewLabel { + text: index + 1 + enabled: false + } + EaComponents.TableViewLabel { + text: path + elide: Text.ElideLeft + } + EaComponents.TableViewButton { + fontIcon: 'minus-circle' + ToolTip.text: qsTr('Remove this file') + onClicked: Globals.BackendWrapper.componentsFilesRemove(index) + } + } + } + + Grid { + columns: 3 + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.SideBarButton { + fontIcon: 'plus-circle' + text: qsTr('Add new file(s)') + width: buttonWidth + ToolTip.text: qsTr('Pick one or more files from disk and add them to this component') + enabled: Globals.BackendWrapper.componentsFilesSelectedComponent !== '' + onClicked: addFilesLoader.item.open() + } + + EaElements.SideBarButton { + fontIcon: 'file-export' + text: qsTr('Export component') + width: buttonWidth + ToolTip.text: qsTr('Export the selected component to disk') + enabled: Globals.BackendWrapper.componentsFilesSelectedComponent !== '' + onClicked: Globals.BackendWrapper.componentsFilesExportComponent() + } + + EaElements.SideBarButton { + fontIcon: 'save' + text: qsTr('Save') + width: buttonWidth + ToolTip.text: qsTr('Save the file list for this component') + enabled: Globals.BackendWrapper.componentsFilesSelectedComponent !== '' + onClicked: Globals.BackendWrapper.componentsFilesSave() + } + } + + Loader { + id: addFilesLoader + source: '../Popups/AddComponentFiles.qml' + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/PositionRestraints.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/PositionRestraints.qml new file mode 100644 index 0000000..c30d7be --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/PositionRestraints.qml @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents + +import Gui.Globals as Globals + + +EaElements.GroupColumn { + + EaComponents.ListView { + id: positionRestraintsList + defaultInfoText: qsTr('No position restraints defined') + multiSelection: false + + columnWidths: [ + -1, + EaStyle.Sizes.fontPixelSize * 4, + EaStyle.Sizes.fontPixelSize * 4, + EaStyle.Sizes.fontPixelSize * 4, + EaStyle.Sizes.fontPixelSize * 5, + EaStyle.Sizes.fontPixelSize * 8 + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr('Component') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('Atom') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('P. form') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('Geom.') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('Radius') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('F. constant') + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: Globals.BackendWrapper.positionRestraintsItems + + delegateModelAccess: DelegateModel.ReadWrite + + delegate: EaComponents.ListViewDelegate { + required property int index + required property string component_name + required property int atom + required property int p_form + required property int geom + required property double radius + required property string f_constant + + EaComponents.TableViewLabel { + text: component_name + } + EaComponents.ListViewTextInput { + text: atom + validator: IntValidator { bottom: 0 } + onEditingFinished: Globals.BackendWrapper.positionRestraintsSetAtom(index, parseInt(text)) + } + EaComponents.ListViewTextInput { + text: p_form + validator: IntValidator { bottom: 0 } + onEditingFinished: Globals.BackendWrapper.positionRestraintsSetPForm(index, parseInt(text)) + } + EaComponents.ListViewTextInput { + text: geom + validator: IntValidator { bottom: 0 } + onEditingFinished: Globals.BackendWrapper.positionRestraintsSetGeom(index, parseInt(text)) + } + EaComponents.ListViewTextInput { + text: radius + validator: DoubleValidator { bottom: 0 } + onEditingFinished: Globals.BackendWrapper.positionRestraintsSetRadius(index, parseFloat(text)) + } + EaComponents.TableViewLabel { + text: f_constant + enabled: false + } + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/StructureFiles.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/StructureFiles.qml new file mode 100644 index 0000000..41820e5 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Groups/StructureFiles.qml @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents + +import Gui.Globals as Globals + + +EaElements.GroupColumn { + property double buttonWidth: EaStyle.Sizes.sideBarContentWidth * 0.48 + + EaComponents.ListView { + id: structureFilesList + defaultInfoText: qsTr('No structure files available') + multiSelection: false + + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 2.5, + -1, + EaStyle.Sizes.tableRowHeight + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr('№') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('File') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: Globals.BackendWrapper.structureFilesFiles + + delegateModelAccess: DelegateModel.ReadWrite + + delegate: EaComponents.ListViewDelegate { + required property int index + required property string path + + EaComponents.TableViewLabel { + text: index + 1 + enabled: false + } + EaComponents.TableViewLabel { + text: path + elide: Text.ElideLeft + } + EaComponents.TableViewButton { + fontIcon: 'minus-circle' + ToolTip.text: qsTr('Remove this file') + onClicked: Globals.BackendWrapper.structureFilesRemove(index) + } + } + } + + Grid { + columns: 2 + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.SideBarButton { + fontIcon: 'save' + text: qsTr('Save to lib') + width: buttonWidth + ToolTip.text: qsTr('Save the current structure files to the asset library') + enabled: Globals.BackendWrapper.structureFilesFiles + ? Globals.BackendWrapper.structureFilesFiles.count > 0 + : false + onClicked: Globals.BackendWrapper.structureFilesSaveToLib() + } + + EaElements.SideBarButton { + fontIcon: 'folder-open' + text: qsTr('Replace structure') + width: buttonWidth + ToolTip.text: qsTr('Pick a directory to replace the current structure with') + onClicked: replaceStructureLoader.item.open() + } + } + + Loader { + id: replaceStructureLoader + source: '../Popups/ReplaceStructure.qml' + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Layout.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Layout.qml new file mode 100644 index 0000000..a3341e0 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Layout.qml @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents + + +EaComponents.SideBarColumn { + + EaElements.GroupBox { + title: qsTr('Components Files') + icon: 'file-alt' + collapsed: false + + Loader { source: 'Groups/ComponentsFiles.qml' } + } + + EaElements.GroupBox { + title: qsTr('Position Restraints') + icon: 'thumbtack' + + Loader { source: 'Groups/PositionRestraints.qml' } + } + + EaElements.GroupBox { + title: qsTr('Structure Files') + icon: 'folder-open' + + Loader { source: 'Groups/StructureFiles.qml' } + } + +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Popups/AddComponentFiles.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Popups/AddComponentFiles.qml new file mode 100644 index 0000000..9c641da --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Popups/AddComponentFiles.qml @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Dialogs + +import Gui.Globals as Globals + + +FileDialog { + fileMode: FileDialog.OpenFiles + nameFilters: [ + 'Any (*)', + 'Structure files (*.gro *.pdb *.xyz)', + 'Topology files (*.itp)', + 'Smiles files (*.sml)' + ] + + onAccepted: { + for (let i = 0; i < selectedFiles.length; ++i) { + Globals.BackendWrapper.componentsFilesAppend(selectedFiles[i].toString()) + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Popups/ReplaceStructure.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Popups/ReplaceStructure.qml new file mode 100644 index 0000000..66fe84e --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Advanced/Popups/ReplaceStructure.qml @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Dialogs + + +FolderDialog { + title: qsTr('Select a directory to replace the structure with') +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Components/Fractions.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Components/Fractions.qml new file mode 100644 index 0000000..f4924ab --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Components/Fractions.qml @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Components as EaComponents + +import Gui.Globals as Globals + + +EaComponents.ListView { + // Override to bind a per-row Fractions backend (e.g. layer/lamella). + // Defaults to the global Fractions set on the wrapper. + property var fractionsModel: Globals.BackendWrapper.fractionsModel + + defaultInfoText: qsTr("Missing components") + selectionActive: false + + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 6, + -1, + EaStyle.Sizes.fontPixelSize * 8, + EaStyle.Sizes.fontPixelSize * 8, + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr("Present") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel {} // filler + EaComponents.TableViewLabel { + text: qsTr("Component name") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Mole ratio") + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: fractionsModel + + delegateModelAccess: DelegateModel.ReadWrite + + delegate: EaComponents.ListViewDelegate { + required property int index + required property string name + required property double fracs + required property bool present + + EaComponents.TableViewCheckBox { + checked: present + onToggled: present = checked + } + EaComponents.TableViewLabel {} // filler + EaComponents.TableViewLabel { + text: name + enabled: false + } + EaComponents.ListViewTextInput { + text: present ? fracs : 0 + enabled: present + onEditingFinished: fracs = parseFloat(text) + validator: DoubleValidator { bottom: 0 } + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/BallStructure.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/BallStructure.qml new file mode 100644 index 0000000..829e7df --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/BallStructure.qml @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + + +EaElements.GroupColumn { + + Row { + property real itemWidth: EaStyle.Sizes.sideBarContentWidth * 0.2 + spacing: EaStyle.Sizes.fontPixelSize * 2 + + Column { + width: parent.itemWidth + + EaElements.Label { + enabled: false + text: qsTr('Fill') + } + EaElements.ComboBox { + width: parent.width + model: ['FIBO', 'RINGS', 'RINGS0'] + Component.onCompleted: { + currentIndex = model.indexOf(Globals.BackendWrapper.ballStructure.fill) + } + onActivated: (i) => { + Globals.BackendWrapper.ballStructure.fill = model[i] + } + } + } + EaElements.CheckBox { + height: EaStyle.Sizes.fontPixelSize * 5 + text: qsTr('Fxz') + checked: Globals.BackendWrapper.ballStructure.fxz + onToggled: Globals.BackendWrapper.ballStructure.fxz = checked + } + EaElements.CheckBox { + height: EaStyle.Sizes.fontPixelSize * 5 + text: qsTr('Rev') + checked: Globals.BackendWrapper.ballStructure.rev + onToggled: Globals.BackendWrapper.ballStructure.rev = checked + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/BilayerStructure.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/BilayerStructure.qml new file mode 100644 index 0000000..fd53d7a --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/BilayerStructure.qml @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + +import "../Components" as Local + + +EaElements.GroupColumn { + + Row { + property real itemWidth: EaStyle.Sizes.sideBarContentWidth * 0.27 + spacing: (EaStyle.Sizes.sideBarContentWidth - (itemWidth * 3)) / 2 + + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Zsep') + units: 'nm' + validator: DoubleValidator { bottom: 0 } + text: Globals.BackendWrapper.bilayerStructure.zsep + onEditingFinished: Globals.BackendWrapper.bilayerStructure.zsep = parseFloat(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Nside') + text: Globals.BackendWrapper.bilayerStructure.nside + validator: IntValidator { bottom: 1 } + onEditingFinished: Globals.BackendWrapper.bilayerStructure.nside = parseInt(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Dmin') + units: 'nm' + validator: DoubleValidator { bottom: 0.5 } + text: Globals.BackendWrapper.bilayerStructure.dmin + onEditingFinished: Globals.BackendWrapper.bilayerStructure.dmin = parseFloat(text) + } + } + + Column { + width: parent.width + + EaElements.Label { + enabled: false + text: qsTr('Bilayer Fractions') + } + + Local.Fractions { + id: bilayerFractions + } + } + +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Components.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Components.qml new file mode 100644 index 0000000..9bd2a0e --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Components.qml @@ -0,0 +1,165 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +//import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Logic as EaLogic + +import Gui.Globals as Globals + + +EaElements.GroupColumn { + property double buttonWidth: EaStyle.Sizes.sideBarContentWidth * 0.3164 + + EaComponents.ListView { + id: loadedComponents + defaultInfoText: qsTr("Load or create components") + multiSelection: true + + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 2.5, + -1, + EaStyle.Sizes.fontPixelSize * 6, + EaStyle.Sizes.fontPixelSize * 4, + EaStyle.Sizes.fontPixelSize * 4, + EaStyle.Sizes.fontPixelSize * 4, + EaStyle.Sizes.tableRowHeight + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr("№") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Name") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Type") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Atoms") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Mint") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Mext") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: Globals.BackendWrapper.componentsLoaded + + delegateModelAccess: DelegateModel.ReadWrite + + delegate: EaComponents.ListViewDelegate { + required property int index + required property string name + required property string component_type + required property int mint + required property int mext + required property int atoms + + EaComponents.TableViewLabel { + text: index + 1 + enabled: false + } + + EaComponents.ListViewTextInput { + text: name + onEditingFinished: name = text + } + + EaComponents.TableViewLabel { + text: component_type + enabled: false + } + + EaComponents.ListViewTextInput { + text: atoms + onEditingFinished: atoms = parseInt(text) + validator: IntValidator { bottom: 0 } + } + + EaComponents.ListViewTextInput { + text: mint + onEditingFinished: mint = parseInt(text) + validator: IntValidator { bottom: 0 } + } + + EaComponents.ListViewTextInput { + text: mext + onEditingFinished: mext = parseInt(text) + validator: IntValidator { bottom: 0 } + } + + EaComponents.TableViewButton { + fontIcon: "minus-circle" + ToolTip.text: qsTr("Remove this component") + onClicked: Globals.BackendWrapper.componentsRemove(index) + } + } + } + + Grid { + columns: 3 + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.SideBarButton { + fontIcon: 'upload' + text: qsTr('Load component(s)') + width: buttonWidth + onClicked: loadExistingComponentLoader.item.open() + } + + EaElements.SideBarButton { + fontIcon: 'plus-circle' + text: qsTr('Import component') + width: buttonWidth + onClicked: createNewComponentLoader.item.open() + } + + EaElements.SideBarButton { + id: saveModelButton + fontIcon: 'download' + text: qsTr('Save component(s)') + width: buttonWidth + enabled: Globals.BackendWrapper.componentsLoaded + ? Globals.BackendWrapper.componentsLoaded.count > 0 + : false + onClicked: { + const indexes = loadedComponents.selectedIndexes + for (let i = 0; i < indexes.length; ++i) + loadExistingComponentLoader.item.availableComponentsModel.append( + Globals.BackendWrapper.componentsLoaded.get(indexes[i].row) + ) + } + } + } + + Loader { + id: loadExistingComponentLoader + source: '../Popups/LoadExistingComponent.qml' + } + + Loader { + id: createNewComponentLoader + source: '../Popups/CreateNewComponent.qml' + } + +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group1.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group1.qml deleted file mode 100644 index f2e5c14..0000000 --- a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group1.qml +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic - -import Gui.Globals as Globals - -EaElements.GroupColumn { - -} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group2.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group2.qml deleted file mode 100644 index f2e5c14..0000000 --- a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group2.qml +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic - -import Gui.Globals as Globals - -EaElements.GroupColumn { - -} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group3.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group3.qml deleted file mode 100644 index f2e5c14..0000000 --- a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group3.qml +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic - -import Gui.Globals as Globals - -EaElements.GroupColumn { - -} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group4.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group4.qml deleted file mode 100644 index f2e5c14..0000000 --- a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Group4.qml +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic - -import Gui.Globals as Globals - -EaElements.GroupColumn { - -} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Lamellae.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Lamellae.qml new file mode 100644 index 0000000..4f4432c --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Lamellae.qml @@ -0,0 +1,183 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Logic as EaLogic + +import Gui.Globals as Globals + +import "../Components" as Local + +EaElements.GroupColumn { + property double buttonWidth: EaStyle.Sizes.sideBarContentWidth * 0.3164 + + EaComponents.ListView { + id: lamellae + defaultInfoText: qsTr("Add at least one lamella") + multiSelection: false + + columnWidths: [ + EaStyle.Sizes.tableColumnAuto, // № + EaStyle.Sizes.tableColumnAuto, // Rmin, nm + EaStyle.Sizes.tableColumnAuto, // Inner Dmin, nm + EaStyle.Sizes.tableColumnAuto, // Outer Dmin, nm + EaStyle.Sizes.tableColumnAuto, // Shell thickness, nm + EaStyle.Sizes.tableColumnAuto, // Symmetric (checkbox) + EaStyle.Sizes.tableRowHeight // delete button + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr("№") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Rmin, nm") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Inner dmin, nm") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Outer dmin, nm") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Shell thickness, nm") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Symmetric") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel {} + } + + model: Globals.BackendWrapper.lamellaeItems + + delegateModelAccess: DelegateModel.ReadWrite + + delegate: EaComponents.ListViewDelegate { + required property int index + required property double rmin + required property double innerDmin + required property double outerDmin + required property double shell + required property bool symmetric + + EaComponents.TableViewLabel { + text: index + 1 + enabled: false + } + EaComponents.ListViewTextInput { + text: rmin + onEditingFinished: Globals.BackendWrapper.lamellaeSetRmin(index, parseFloat(text)) + validator: DoubleValidator { bottom: 0.25 } + } + EaComponents.ListViewTextInput { + text: innerDmin + onEditingFinished: Globals.BackendWrapper.lamellaeSetInnerDmin(index, parseFloat(text)) + validator: DoubleValidator { bottom: 0.25 } + } + EaComponents.ListViewTextInput { + text: outerDmin + onEditingFinished: Globals.BackendWrapper.lamellaeSetOuterDmin(index, parseFloat(text)) + validator: DoubleValidator { bottom: 0.25 } + } + EaComponents.ListViewTextInput { + text: shell + onEditingFinished: Globals.BackendWrapper.lamellaeSetShell(index, parseFloat(text)) + validator: DoubleValidator { bottom: 0 } + } + EaComponents.TableViewCheckBox { + checked: symmetric + onToggled: Globals.BackendWrapper.lamellaeSetSymmetric(index, checked) + } + EaComponents.TableViewButton { + id: deleteRowColumn + fontIcon: "minus-circle" + ToolTip.text: qsTr("Remove this lamella") + onClicked: Globals.BackendWrapper.lamellaeRemove(index) + } + } + } + + Item { + width: lamellae.width + height: addLamellaButton.height + + EaElements.SideBarButton { + id: addLamellaButton + anchors.right: parent.right + anchors.rightMargin: EaStyle.Sizes.tableColumnSpacing + fontIcon: "plus-circle" + text: qsTr("Add lamella") + width: buttonWidth + onClicked: Globals.BackendWrapper.lamellaeAppend({ rmin: 0.5, innerDmin: 0.25, outerDmin: 0.3, shell: 0.5, symmetric: true }) + } + } + + Column { + id: fractionsSection + visible: lamellae.selectedIndexes.length > 0 + width: parent.width + spacing: EaStyle.Sizes.groupBoxSpacing + + readonly property int selectedRow: lamellae.selectedIndexes.length > 0 ? lamellae.selectedIndexes[0].row : -1 + readonly property bool selectedSymmetric: { + // Touch the revision token so this binding re-evaluates when + // the selected row's symmetric flag changes. + void Globals.BackendWrapper.lamellaeItemsRevision + return selectedRow >= 0 && selectedRow < Globals.BackendWrapper.lamellaeItems.count + ? Globals.BackendWrapper.lamellaeItems.get(selectedRow).symmetric + : true + } + readonly property var selectedInnerFractionsModel: { + // Touch the revision token so this binding re-evaluates when + // the per-lamella Fractions arrays are rebuilt. + void Globals.BackendWrapper.lamellaeFractionsRevision + return selectedRow >= 0 ? Globals.BackendWrapper.lamellaeInnerFractionsModelAt(selectedRow) : null + } + readonly property var selectedOuterFractionsModel: { + void Globals.BackendWrapper.lamellaeFractionsRevision + return selectedRow >= 0 ? Globals.BackendWrapper.lamellaeOuterFractionsModelAt(selectedRow) : null + } + + Column { + width: parent.width + + EaElements.Label { + enabled: false + text: fractionsSection.selectedSymmetric + ? qsTr("Lamella %1 Fractions").arg(fractionsSection.selectedRow + 1) + : qsTr("Lamella %1 Inner Leaflet Fractions").arg(fractionsSection.selectedRow + 1) + } + Local.Fractions { + id: innerFractions + fractionsModel: fractionsSection.selectedInnerFractionsModel + } + } + + Column { + width: parent.width + visible: !fractionsSection.selectedSymmetric + + EaElements.Label { + enabled: false + text: qsTr("Lamella %1 Outer Leaflet Fractions").arg(fractionsSection.selectedRow + 1) + } + Local.Fractions { + id: outerFractions + fractionsModel: fractionsSection.selectedOuterFractionsModel + } + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/LatticeStructure.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/LatticeStructure.qml new file mode 100644 index 0000000..a546a26 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/LatticeStructure.qml @@ -0,0 +1,179 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents + +import Gui.Globals as Globals + + +EaElements.GroupColumn { + + Row { + property real itemWidth: EaStyle.Sizes.sideBarContentWidth * 0.3 + spacing: (EaStyle.Sizes.sideBarContentWidth - (itemWidth * 3)) / 2 + + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Alpha') + units: '⚬' + validator: DoubleValidator { bottom: 0; top: 360 } + text: Globals.BackendWrapper.latticeStructure.alpha + onEditingFinished: Globals.BackendWrapper.latticeStructure.alpha = parseFloat(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Theta') + units: '⚬' + validator: DoubleValidator { bottom: 0; top: 180 } + text: Globals.BackendWrapper.latticeStructure.theta + onEditingFinished: Globals.BackendWrapper.latticeStructure.theta = parseFloat(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Sbuff') + units: 'nm' + validator: DoubleValidator { bottom: 0 } + text: Globals.BackendWrapper.latticeStructure.sbuff + onEditingFinished: Globals.BackendWrapper.latticeStructure.sbuff = parseFloat(text) + } + } + + Row { + property real itemWidth: EaStyle.Sizes.sideBarContentWidth * 0.2 + spacing: (EaStyle.Sizes.sideBarContentWidth - (itemWidth * 4)) / 3 + + + Column { + width: parent.itemWidth + + EaElements.Label { + enabled: false + text: qsTr('Type') + } + EaElements.ComboBox { + width: parent.width + model: Globals.BackendWrapper.latticeStructure.latticeTypes + Component.onCompleted: { + currentIndex = model.indexOf(Globals.BackendWrapper.latticeStructure.latticeType) + } + onActivated: (i) => { + Globals.BackendWrapper.latticeStructure.latticeType = model[i] + } + } + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Nlatx') + validator: IntValidator { bottom: 1 } + text: Globals.BackendWrapper.latticeStructure.nlatx + onEditingFinished: Globals.BackendWrapper.latticeStructure.nlatx = parseInt(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Nlaty') + validator: IntValidator { bottom: 1 } + text: Globals.BackendWrapper.latticeStructure.nlaty + onEditingFinished: Globals.BackendWrapper.latticeStructure.nlaty = parseInt(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Nlatz') + validator: IntValidator { bottom: 1 } + text: Globals.BackendWrapper.latticeStructure.nlatz + onEditingFinished: Globals.BackendWrapper.latticeStructure.nlatz = parseInt(text) + } + + } + + Column { + width: parent.width + + EaElements.Label { + enabled: false + text: qsTr('Substructure') + } + + EaComponents.ListView { + id: loadedSubstructure + defaultInfoText: qsTr('Load or create a substructure') + multiSelection: false + + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 12, + EaStyle.Sizes.fontPixelSize * 7, + -1 + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr('Name') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('Type') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('Description') + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: Globals.BackendWrapper.latticeSubstructureLoaded + + delegate: EaComponents.ListViewDelegate { + required property var modelData + required property int index + + EaComponents.TableViewLabel { + text: modelData ? modelData.name : '' + enabled: false + } + EaComponents.TableViewLabel { + text: modelData ? modelData.structure_type : '' + enabled: false + } + EaComponents.TableViewLabel { + text: modelData ? modelData.description : '' + enabled: false + } + } + } + } + + Row { + id: buttonRow + property double buttonWidth: (EaStyle.Sizes.sideBarContentWidth - EaStyle.Sizes.fontPixelSize) / 2 + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.SideBarButton { + fontIcon: 'upload' + text: qsTr('Load structure') + width: buttonRow.buttonWidth + onClicked: loadExistingSubstructureLoader.item.open() + } + + EaElements.SideBarButton { + fontIcon: 'plus-circle' + text: qsTr('Import structure') + width: buttonRow.buttonWidth + onClicked: createNewSubstructureLoader.item.open() + } + } + + Loader { + id: loadExistingSubstructureLoader + source: '../Popups/LoadExistingSubstructure.qml' + } + + Loader { + id: createNewSubstructureLoader + source: '../Popups/CreateNewSubstructure.qml' + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Layers.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Layers.qml new file mode 100644 index 0000000..59e3643 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Layers.qml @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Logic as EaLogic + +import Gui.Globals as Globals + +import "../Components" as Local + +EaElements.GroupColumn { + property double buttonWidth: EaStyle.Sizes.sideBarContentWidth * 0.3164 + + EaComponents.ListView { + id: layers + defaultInfoText: qsTr("Add at least one layer") + multiSelection: false + + columnWidths: [ + EaStyle.Sizes.tableColumnAuto, // № + EaStyle.Sizes.tableColumnFlex, // filler + EaStyle.Sizes.tableColumnAuto, // Dmin, nm + EaStyle.Sizes.tableColumnAuto, // Rmin, nm + EaStyle.Sizes.tableRowHeight // delete button + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr("№") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel {} // filler + EaComponents.TableViewLabel { + text: qsTr("Dmin, nm") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Rmin, nm") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel {} + } + + model: Globals.BackendWrapper.layersItems + + delegateModelAccess: DelegateModel.ReadWrite + + delegate: EaComponents.ListViewDelegate { + required property int index + required property double dmin + required property double rmin + + EaComponents.TableViewLabel { + text: index + 1 + enabled: false + } + EaComponents.TableViewLabel {} // filler + EaComponents.ListViewTextInput { + text: dmin + onEditingFinished: Globals.BackendWrapper.layersSetDmin(index, parseFloat(text)) + validator: DoubleValidator { bottom: 0.25 } + } + EaComponents.ListViewTextInput { + text: rmin + onEditingFinished: Globals.BackendWrapper.layersSetRmin(index, parseFloat(text)) + validator: DoubleValidator { bottom: 0.25 } + } + EaComponents.TableViewButton { + id: deleteRowColumn + fontIcon: "minus-circle" + ToolTip.text: qsTr("Remove this layer") + onClicked: Globals.BackendWrapper.layersRemove(index) + } + } + } + Item { + width: layers.width + height: addLayerButton.height + + EaElements.SideBarButton { + id: addLayerButton + anchors.right: parent.right + anchors.rightMargin: EaStyle.Sizes.tableColumnSpacing + fontIcon: "plus-circle" + text: qsTr("Add layer") + width: buttonWidth + onClicked: Globals.BackendWrapper.layersAppend({ dmin: 0.25, rmin: 0.5 }) + } + } + Column { + id: fractionsSection + visible: layers.selectedIndexes.length > 0 + width: parent.width + + readonly property int selectedRow: layers.selectedIndexes.length > 0 ? layers.selectedIndexes[0].row : -1 + readonly property var selectedFractionsModel: { + // Touch the revision token so this binding re-evaluates when + // the per-layer Fractions array is rebuilt. + void Globals.BackendWrapper.layersFractionsRevision + return selectedRow >= 0 ? Globals.BackendWrapper.layersFractionsModelAt(selectedRow) : null + } + + EaElements.Label { + id: layerFractionsLabel + enabled: false + text: qsTr("Layer %1 Fractions").arg(fractionsSection.selectedRow >= 0 ? fractionsSection.selectedRow + 1 : 1) + } + Local.Fractions { + id: fractions + fractionsModel: fractionsSection.selectedFractionsModel + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/MonolayerStructure.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/MonolayerStructure.qml new file mode 100644 index 0000000..45c5b40 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/MonolayerStructure.qml @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + +import "../Components" as Local + + +EaElements.GroupColumn { + + Row { + property real itemWidth: EaStyle.Sizes.sideBarContentWidth * 0.27 + spacing: (EaStyle.Sizes.sideBarContentWidth - (itemWidth * 3)) / 2 + + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Zsep') + units: 'nm' + validator: DoubleValidator { bottom: 0 } + text: Globals.BackendWrapper.monolayerStructure.zsep + onEditingFinished: Globals.BackendWrapper.monolayerStructure.zsep = parseFloat(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Nside') + text: Globals.BackendWrapper.monolayerStructure.nside + validator: IntValidator { bottom: 1 } + onEditingFinished: Globals.BackendWrapper.monolayerStructure.nside = parseInt(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Dmin') + units: 'nm' + validator: DoubleValidator { bottom: 0.5 } + text: Globals.BackendWrapper.monolayerStructure.dmin + onEditingFinished: Globals.BackendWrapper.monolayerStructure.dmin = parseFloat(text) + } + } + + Column { + width: parent.width + + EaElements.Label { + enabled: false + text: qsTr('Monolayer Fractions') + } + + Local.Fractions { + id: monolayerFractions + } + } + +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/RingStructure.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/RingStructure.qml new file mode 100644 index 0000000..8c44e0a --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/RingStructure.qml @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + +import "../Components" as Local + + +EaElements.GroupColumn { + + Row { + property real itemWidth: EaStyle.Sizes.sideBarContentWidth * 0.2 + spacing: (EaStyle.Sizes.sideBarContentWidth - (itemWidth * 4)) / 3 + + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Dmin') + units: 'nm' + validator: DoubleValidator { bottom: 0.5 } + text: Globals.BackendWrapper.ringStructure.dmin + onEditingFinished: Globals.BackendWrapper.ringStructure.dmin = parseFloat(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Rmin') + units: 'nm' + validator: DoubleValidator { bottom: 0.25 } + text: Globals.BackendWrapper.ringStructure.rmin + onEditingFinished: Globals.BackendWrapper.ringStructure.rmin = parseFloat(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Alpha') + text: Globals.BackendWrapper.ringStructure.alpha + validator: DoubleValidator { bottom: 0; top: 360 } + units: '⚬' + onEditingFinished: Globals.BackendWrapper.ringStructure.alpha = parseFloat(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Theta') + text: Globals.BackendWrapper.ringStructure.theta + validator: DoubleValidator { bottom: 0; top: 180 } + units: '⚬' + onEditingFinished: Globals.BackendWrapper.ringStructure.theta = parseFloat(text) + } + } + Row { + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.CheckBox { + width: EaStyle.Sizes.sideBarContentWidth * 0.15 + height: EaStyle.Sizes.fontPixelSize * 3 + text: qsTr('Fxz') + checked: Globals.BackendWrapper.ringStructure.fxz + onToggled: Globals.BackendWrapper.ringStructure.fxz = checked + } + EaElements.CheckBox { + width: EaStyle.Sizes.sideBarContentWidth * 0.15 + height: EaStyle.Sizes.fontPixelSize * 3 + text: qsTr('Rev') + checked: Globals.BackendWrapper.ringStructure.rev + onToggled: Globals.BackendWrapper.ringStructure.rev = checked + } + } + + Column { + width: parent.width + + EaElements.Label { + enabled: false + text: qsTr('Ring Fractions') + } + + Local.Fractions { + id: ringFractions + } + } + + + +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/RodStructure.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/RodStructure.qml new file mode 100644 index 0000000..9974c71 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/RodStructure.qml @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + +import "../Components" as Local + + +EaElements.GroupColumn { + + Row { + property real itemWidth: EaStyle.Sizes.sideBarContentWidth * 0.27 + spacing: (EaStyle.Sizes.sideBarContentWidth - (itemWidth * 3)) / 2 + + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Dmin') + units: 'nm' + validator: DoubleValidator { bottom: 0.5 } + text: Globals.BackendWrapper.rodStructure.dmin + onEditingFinished: Globals.BackendWrapper.rodStructure.dmin = parseFloat(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Rmin') + units: 'nm' + validator: DoubleValidator { bottom: 0.25 } + text: Globals.BackendWrapper.rodStructure.rmin + onEditingFinished: Globals.BackendWrapper.rodStructure.rmin = parseFloat(text) + } + EaElements.Parameter { + width: parent.itemWidth + title: qsTr('Turns') + text: Globals.BackendWrapper.rodStructure.turns + validator: IntValidator { bottom: 1 } + onEditingFinished: Globals.BackendWrapper.rodStructure.turns = parseInt(text) + } + } + + Row { + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.CheckBox { + width: EaStyle.Sizes.sideBarContentWidth * 0.15 + height: EaStyle.Sizes.fontPixelSize * 3 + text: qsTr('Fxz') + checked: Globals.BackendWrapper.rodStructure.fxz + onToggled: Globals.BackendWrapper.rodStructure.fxz = checked + } + EaElements.CheckBox { + width: EaStyle.Sizes.sideBarContentWidth * 0.15 + height: EaStyle.Sizes.fontPixelSize * 3 + text: qsTr('Rev') + checked: Globals.BackendWrapper.rodStructure.rev + onToggled: Globals.BackendWrapper.rodStructure.rev = checked + } + } + + Column { + width: parent.width + + EaElements.Label { + enabled: false + text: qsTr('Rod Fractions') + } + + Local.Fractions { + id: rodFractions + } + } + +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/SampleModel.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/SampleModel.qml new file mode 100644 index 0000000..f8cf33e --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/SampleModel.qml @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Logic as EaLogic + +import Gui.Globals as Globals + +EaElements.GroupColumn { + id: root + property double buttonWidth: EaStyle.Sizes.sideBarContentWidth * 0.3164 + + Component.onCompleted: Globals.References.pages.samplemodel.sidebar.basic.groups.sampleModel = root + + EaComponents.ListView { + id: loadedSampleModel + defaultInfoText: qsTr("Get started by loading or creating a model") + multiSelection: false + + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 12, + EaStyle.Sizes.fontPixelSize * 7, + -1 + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr("Name") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Type") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Description") + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: Globals.BackendWrapper.sampleModelLoaded + + delegate: EaComponents.ListViewDelegate { + required property var modelData + required property int index + + EaComponents.ListViewTextInput { + text: modelData ? modelData.name : '' + onEditingFinished: Globals.BackendWrapper.sampleModelUpdateField('name', text) + } + EaComponents.TableViewComboBox { + model: Globals.BackendWrapper.sampleModelStructureTypes + Component.onCompleted: { + currentIndex = model.indexOf(modelData.structure_type) + } + onActivated: (i) => { + Globals.BackendWrapper.sampleModelUpdateField('structure_type', model[i]) + } + } + EaComponents.ListViewTextInput { + text: modelData ? modelData.description : '' + onEditingFinished: Globals.BackendWrapper.sampleModelUpdateField('description', text) + } + } + } + + Grid { + columns: 3 + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.SideBarButton { + fontIcon: "upload" + text: qsTr("Load model") + width: buttonWidth + onClicked: loadExistingModelLoader.item.open() + } + + EaElements.SideBarButton { + fontIcon: "plus-circle" + text: qsTr("Create model") + width: buttonWidth + onClicked: createNewModelLoader.item.open() + } + + EaElements.SideBarButton { + fontIcon: "download" + text: qsTr("Save model") + width: buttonWidth + enabled: Globals.BackendWrapper.sampleModelLoaded.length > 0 + && Globals.BackendWrapper.sampleModelLoaded[0].name !== "" + onClicked: Globals.BackendWrapper.sampleModelSaveToCatalog() + } + } + + Loader { + id: loadExistingModelLoader + source: '../Popups/LoadExistingModel.qml' + } + + Loader { + id: createNewModelLoader + source: '../Popups/CreateNewModel.qml' + } + +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Solution.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Solution.qml new file mode 100644 index 0000000..5f3bb0f --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/Solution.qml @@ -0,0 +1,255 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents + +import Gui.Globals as Globals + + +EaElements.GroupColumn { + id: root + property double buttonWidth: (EaStyle.Sizes.sideBarContentWidth - (EaStyle.Sizes.fontPixelSize * 2) ) / 3 + + Column { + width: EaStyle.Sizes.sideBarContentWidth * 0.5 + + EaElements.Label { + enabled: false + text: qsTr('Solvent') + } + + EaComponents.ListView { + id: loadedSolvent + defaultInfoText: qsTr('Load or import a solvent') + multiSelection: false + + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 7, + EaStyle.Sizes.fontPixelSize * 8, + -1 + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr('Name') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('Type') + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr('Description') + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: Globals.BackendWrapper.solutionLoaded + + delegate: EaComponents.ListViewDelegate { + required property var modelData + required property int index + + EaComponents.ListViewTextInput { + text: modelData ? modelData.name : '' + onEditingFinished: Globals.BackendWrapper.solventUpdateField('name', text) + } + EaComponents.TableViewComboBox { + model: Globals.BackendWrapper.solventTypes + Component.onCompleted: { + currentIndex = model.indexOf(modelData.solvent_type) + } + onActivated: (i) => { + Globals.BackendWrapper.solventUpdateField('solvent_type', model[i]) + } + } + EaComponents.ListViewTextInput { + text: modelData ? modelData.description : '' + onEditingFinished: Globals.BackendWrapper.solventUpdateField('description', text) + } + } + } + } + + Grid { + columns: 3 + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.SideBarButton { + fontIcon: 'upload' + text: qsTr('Load solvent') + width: buttonWidth + onClicked: loadExistingSolvent.item.open() + } + + EaElements.SideBarButton { + fontIcon: 'plus-circle' + text: qsTr('Import solvent') + width: buttonWidth + onClicked: createNewSolvent.item.open() + } + + EaElements.SideBarButton { + fontIcon: 'download' + text: qsTr('Save solvent') + width: buttonWidth + enabled: Globals.BackendWrapper.solventLoaded.length > 0 + && Globals.BackendWrapper.solventLoaded[0].name !== "" + onClicked: Globals.BackendWrapper.solventSaveToCatalog() + } + } + + Row { + spacing: EaStyle.Sizes.fontPixelSize + + Column { + width: EaStyle.Sizes.sideBarContentWidth / 3 + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.Label { + enabled: false + text: qsTr('Ions') + } + + Row { + id: ionChipRow + spacing: EaStyle.Sizes.fontPixelSize * 0.5 + height: EaStyle.Sizes.fontPixelSize * 2 + width: buttonWidth + + EaElements.Label { + visible: ionChipRow.ionCount === 0 + enabled: false + text: qsTr('No ions loaded') + anchors.verticalCenter: parent.verticalCenter + } + + readonly property int ionCount: + Globals.BackendWrapper.ionsLoaded + ? Globals.BackendWrapper.ionsLoaded.count + : 0 + + Repeater { + model: Globals.BackendWrapper.ionsLoaded + + delegate: Rectangle { + required property int index + required property string name + + height: ionChipRow.height + width: EaStyle.Sizes.fontPixelSize + + chipLabel.implicitWidth + + EaStyle.Sizes.fontPixelSize + + chipRemove.width + + EaStyle.Sizes.fontPixelSize * 0.6 + radius: height / 2 + color: EaStyle.Colors.appBarBackground + border.color: EaStyle.Colors.appBarComboBoxBorder + border.width: 1 + + Row { + anchors.fill: parent + anchors.leftMargin: EaStyle.Sizes.fontPixelSize + anchors.rightMargin: chipRemove.width + EaStyle.Sizes.fontPixelSize * 0.25 + spacing: EaStyle.Sizes.fontPixelSize * 0.25 + + EaElements.Label { + id: chipLabel + text: name + anchors.verticalCenter: parent.verticalCenter + } + } + + EaElements.Label { + id: chipRemove + text: '×' + font.pixelSize: EaStyle.Sizes.fontPixelSize * 1.4 + color: chipRemoveArea.containsMouse + ? EaStyle.Colors.themeForegroundHovered + : EaStyle.Colors.themeForegroundMinor + anchors.right: parent.right + anchors.rightMargin: EaStyle.Sizes.fontPixelSize * 0.6 + anchors.verticalCenter: parent.verticalCenter + + MouseArea { + id: chipRemoveArea + anchors.fill: parent + anchors.margins: -EaStyle.Sizes.fontPixelSize * 0.3 + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: Globals.BackendWrapper.ionsRemove(index) + } + + EaElements.ToolTip { + text: qsTr('Remove this ion') + visible: chipRemoveArea.containsMouse + && EaGlobals.Vars.showToolTips + } + } + } + } + } + + EaElements.SideBarButton { + fontIcon: 'upload' + text: qsTr('Load ion') + width: buttonWidth + enabled: ionChipRow.ionCount < 2 + onClicked: loadExistingIonLoader.item.open() + } + } + + Column { + width: EaStyle.Sizes.sideBarContentWidth / 3 + spacing: EaStyle.Sizes.fontPixelSize + + Column { + width: parent.width + + EaElements.Label { + enabled: false + text: qsTr('Amount') + } + EaElements.TextField { + id: ionAmountField + width: parent.width + enabled: !matchStructureCheck.checked + placeholderText: qsTr('Set the amount of selected ions') + validator: IntValidator { bottom: 0 } + } + } + EaElements.CheckBox { + id: matchStructureCheck + text: qsTr('Match components') + checked: false + onCheckedChanged: { + if (checked) { + ionAmountField.text = '' + ionAmountField.placeholderText = 'Will match the structure components' + } + else ionAmountField.placeholderText = 'Set the amount of ions' + } + } + } + } + + Loader { + id: loadExistingSolvent + source: '../Popups/LoadExistingSolvent.qml' + } + Loader { + id: createNewSolvent + source: '../Popups/CreateNewSolvent.qml' + } + Loader { + id: loadExistingIonLoader + source: '../Popups/LoadExistingIon.qml' + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/VesicleStructure.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/VesicleStructure.qml new file mode 100644 index 0000000..829e7df --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Groups/VesicleStructure.qml @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls + +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + + +EaElements.GroupColumn { + + Row { + property real itemWidth: EaStyle.Sizes.sideBarContentWidth * 0.2 + spacing: EaStyle.Sizes.fontPixelSize * 2 + + Column { + width: parent.itemWidth + + EaElements.Label { + enabled: false + text: qsTr('Fill') + } + EaElements.ComboBox { + width: parent.width + model: ['FIBO', 'RINGS', 'RINGS0'] + Component.onCompleted: { + currentIndex = model.indexOf(Globals.BackendWrapper.ballStructure.fill) + } + onActivated: (i) => { + Globals.BackendWrapper.ballStructure.fill = model[i] + } + } + } + EaElements.CheckBox { + height: EaStyle.Sizes.fontPixelSize * 5 + text: qsTr('Fxz') + checked: Globals.BackendWrapper.ballStructure.fxz + onToggled: Globals.BackendWrapper.ballStructure.fxz = checked + } + EaElements.CheckBox { + height: EaStyle.Sizes.fontPixelSize * 5 + text: qsTr('Rev') + checked: Globals.BackendWrapper.ballStructure.rev + onToggled: Globals.BackendWrapper.ballStructure.rev = checked + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Layout.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Layout.qml index d13c364..400bc4c 100644 --- a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Layout.qml +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Layout.qml @@ -5,8 +5,9 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals @@ -14,31 +15,113 @@ import Gui.Globals as Globals EaComponents.SideBarColumn { EaElements.GroupBox { - title: qsTr('Group 1') - icon: 'rocket' + title: qsTr('Model Definition') + icon: 'tag' + collapsed: false - Loader { source: 'Groups/Group1.qml' } + Loader { id: sampleModelLoader; source: 'Groups/SampleModel.qml' } } EaElements.GroupBox { - title: qsTr('Group 2') - icon: 'rocket' + title: qsTr('Components') + icon: 'puzzle-piece' - Loader { source: 'Groups/Group2.qml' } + Loader { source: 'Groups/Components.qml' } } EaElements.GroupBox { - title: qsTr('Group 3') - icon: 'rocket' + title: qsTr('Ring Structure Definition') + icon: 'vector-square' + visible: Globals.BackendWrapper.sampleModelCurrentStructureType === 'Ring' - Loader { source: 'Groups/Group3.qml' } + Loader { source: 'Groups/RingStructure.qml' } } EaElements.GroupBox { - title: qsTr('Group 4') - icon: 'rocket' + title: qsTr('Ball Structure Definition') + icon: 'vector-square' + visible: Globals.BackendWrapper.sampleModelCurrentStructureType === 'Ball' - Loader { source: 'Groups/Group4.qml' } + Loader { source: 'Groups/BallStructure.qml' } + } + + EaElements.GroupBox { + title: qsTr('Vesicle Structure Definition') + icon: 'vector-square' + visible: Globals.BackendWrapper.sampleModelCurrentStructureType === 'Vesicle' + + Loader { source: 'Groups/VesicleStructure.qml' } + } + + EaElements.GroupBox { + title: qsTr('Rod Structure Definition') + icon: 'vector-square' + visible: Globals.BackendWrapper.sampleModelCurrentStructureType === 'Rod' + + Loader { source: 'Groups/RodStructure.qml' } + } + + EaElements.GroupBox { + title: qsTr('Bilayer Structure Definition') + icon: 'vector-square' + visible: Globals.BackendWrapper.sampleModelCurrentStructureType === 'Bilayer' + + Loader { source: 'Groups/BilayerStructure.qml' } + } + + EaElements.GroupBox { + title: qsTr('Monolayer Structure Definition') + icon: 'vector-square' + visible: Globals.BackendWrapper.sampleModelCurrentStructureType === 'Monolayer' + + Loader { source: 'Groups/MonolayerStructure.qml' } + } + + EaElements.GroupBox { + title: qsTr('Lattice Structure Definition') + icon: 'vector-square' + visible: Globals.BackendWrapper.sampleModelCurrentStructureType === 'Lattice' + + Loader { source: 'Groups/LatticeStructure.qml' } + } + + EaElements.GroupBox { + title: qsTr('Layers') + icon: 'layer-group' // 'grip-lines' + visible: Globals.BackendWrapper.sampleModelCurrentStructureType === 'Ball' + + Loader { source: 'Groups/Layers.qml' } + } + + EaElements.GroupBox { + title: qsTr('Lamellae') + icon: 'layer-group' + visible: Globals.BackendWrapper.sampleModelCurrentStructureType === 'Vesicle' + + Loader { source: 'Groups/Lamellae.qml' } + } + + EaElements.GroupBox { + title: qsTr('Solution') + icon: 'flask' + + Loader { source: 'Groups/Solution.qml' } + } + + // Centered "Assemble" call-to-action below the groups. + Item { + width: parent.width + height: assembleButton.height + EaStyle.Sizes.fontPixelSize + + EaElements.SideBarButton { + id: assembleButton + anchors.centerIn: parent + text: qsTr('Assemble') + fontIcon: 'cubes' + width: EaStyle.Sizes.sideBarContentWidth + enabled: Globals.BackendWrapper.sampleModelLoaded.length > 0 + onClicked: console.debug('Assemble clicked') + } } } diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewComponent.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewComponent.qml new file mode 100644 index 0000000..cf82267 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewComponent.qml @@ -0,0 +1,143 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + + +EaElements.Dialog{ + id: componentCreationDialog + + property int inputFieldWidth: EaStyle.Sizes.fontPixelSize * 35 + + title: qsTr("Create a new Component") + standardButtons: Dialog.Ok | Dialog.Cancel + + onAccepted: { + Globals.BackendWrapper.componentsAppend({ + name: componentNameField.text, + component_type: sampleModelTypeField.currentText, + atoms: 42, + mint: 0, + mext: 0 + }) + } + + Column { + spacing: EaStyle.Sizes.fontPixelSize + + Row { + property int halfFieldWidth: (componentCreationDialog.inputFieldWidth - spacing) / 2 + spacing: EaStyle.Sizes.fontPixelSize + + Column { + EaElements.Label { + enabled: false + text: qsTr("Name") + } + EaElements.TextField { + id: componentNameField + implicitWidth: parent.parent.halfFieldWidth + horizontalAlignment: TextInput.AlignLeft + validator: RegularExpressionValidator { regularExpression: /^[a-zA-Z][a-zA-Z0-9_\-\.]{1,30}$/ } + placeholderText: qsTr("(optional) Enter Component name here") + } + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Component type") + } + EaElements.ComboBox { + id: sampleModelTypeField + implicitWidth: parent.parent.halfFieldWidth + model: [qsTr("Other"), qsTr("Lipid"), qsTr("Surfactant")] + } + } + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("SMILES string") + } + + EaElements.TextField { + implicitWidth: componentCreationDialog.inputFieldWidth + horizontalAlignment: TextInput.AlignLeft + placeholderText: qsTr("(optional) Define component using SMILES") + } + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Paths") + } + + EaComponents.ListView { + id: filePaths + defaultInfoText: qsTr("No files added") + enabled: true + maxRowCountShow: 2 + width: componentCreationDialog.inputFieldWidth + scrollBarInteractive: false + + columnWidths: [ + -1, + EaStyle.Sizes.tableRowHeight + ] + + model: Globals.BackendWrapper.componentsPendingFilePaths + + delegate: EaComponents.ListViewDelegate { + required property int index + required property url path + + EaComponents.TableViewLabel { + id: pathColumn + text: path + elide: Text.ElideLeft + horizontalAlignment: Text.Alignleft + + leftPadding: EaStyle.Sizes.fontPixelSize * 0.5 + } + + EaComponents.TableViewButton { + id: deleteRowColumn + fontIcon: "minus-circle" + ToolTip.text: qsTr("Remove this file") + onClicked: Globals.BackendWrapper.componentsRemovePendingFilePath(index) + } + } + } + } + Column { + EaElements.SideBarButton { + fontIcon: 'upload' + text: qsTr('Add files') + width: componentCreationDialog.width * 0.3164 + + onClicked: { + console.debug(`Clicking '${text}' button ::: ${this}`) + Globals.References.pages.samplemodel.sidebar.basic.popups.openAssetFile.open() + } + + Loader { + source: '../Popups/OpenAssetFile.qml' + } + } + } + } +} + diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewModel.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewModel.qml new file mode 100644 index 0000000..39a6fe4 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewModel.qml @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + + +EaElements.Dialog{ + id: sampleModelCreationDialog + + property int inputFieldWidth: EaStyle.Sizes.fontPixelSize * 35 + + title: qsTr("Create a new Sample Model") + standardButtons: Dialog.Ok | Dialog.Cancel + + onAccepted: { + Globals.BackendWrapper.sampleModelSetLoaded({ + name: sampleModelNameField.text, + structure_type: sampleModelTypeField.currentText, + description: sampleModelDescrField.text + }) + } + + Column { + spacing: EaStyle.Sizes.fontPixelSize + + Row { + id: nameTypeRow + spacing: EaStyle.Sizes.fontPixelSize + + Column { + EaElements.Label { + enabled: false + text: qsTr("Name") + } + EaElements.TextField { + id: sampleModelNameField + implicitWidth: (sampleModelCreationDialog.inputFieldWidth - nameTypeRow.spacing) / 2 + horizontalAlignment: TextInput.AlignLeft + validator: RegularExpressionValidator { regularExpression: /^[a-zA-Z][a-zA-Z0-9_\-\.]{1,30}$/ } + placeholderText: qsTr("(optional) Enter Sample Model name here") + } + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Structure type") + } + EaElements.ComboBox { + id: sampleModelTypeField + implicitWidth: (sampleModelCreationDialog.inputFieldWidth - nameTypeRow.spacing) / 2 + model: Globals.BackendWrapper.sampleModelStructureTypes + } + } + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Description") + } + EaElements.TextField { + id: sampleModelDescrField + implicitWidth: sampleModelCreationDialog.inputFieldWidth + horizontalAlignment: TextInput.AlignLeft + validator: RegularExpressionValidator { regularExpression: /^[a-zA-Z][a-zA-Z0-9_\-\.]{1,30}$/ } + placeholderText: qsTr("(optional) Enter Sample Model description here") + } + } + } +} + diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewSolvent.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewSolvent.qml new file mode 100644 index 0000000..0855279 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewSolvent.qml @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + + +EaElements.Dialog { + id: solventCreationDialog + + property int inputFieldWidth: EaStyle.Sizes.fontPixelSize * 35 + + title: qsTr("Import a new Solvent") + standardButtons: Dialog.Ok | Dialog.Cancel + + onAccepted: { + Globals.BackendWrapper.solventSetLoaded({ + name: solventNameField.text, + solvent_type: solventTypeField.currentText, + description: solventDescrField.text, + file_path: solventFileField.text + }) + } + + Column { + spacing: EaStyle.Sizes.fontPixelSize + + Row { + id: nameTypeRow + spacing: EaStyle.Sizes.fontPixelSize + + Column { + EaElements.Label { + enabled: false + text: qsTr("Name") + } + EaElements.TextField { + id: solventNameField + implicitWidth: (solventCreationDialog.inputFieldWidth - nameTypeRow.spacing) / 2 + horizontalAlignment: TextInput.AlignLeft + validator: RegularExpressionValidator { regularExpression: /^[a-zA-Z][a-zA-Z0-9_\-\.\/]{1,30}$/ } + placeholderText: qsTr("(optional) Enter Solvent name here") + } + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Solvent type") + } + EaElements.ComboBox { + id: solventTypeField + implicitWidth: (solventCreationDialog.inputFieldWidth - nameTypeRow.spacing) / 2 + model: Globals.BackendWrapper.solventTypes + } + } + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Description") + } + EaElements.TextField { + id: solventDescrField + implicitWidth: solventCreationDialog.inputFieldWidth + horizontalAlignment: TextInput.AlignLeft + placeholderText: qsTr("(optional) Enter Solvent description here") + } + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("File") + } + + Row { + id: fileRow + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.TextField { + id: solventFileField + implicitWidth: solventCreationDialog.inputFieldWidth + - browseFileButton.width + - fileRow.spacing + horizontalAlignment: TextInput.AlignLeft + placeholderText: qsTr("(optional) Choose a single solvent file") + readOnly: true + } + + EaElements.SideBarButton { + id: browseFileButton + fontIcon: 'upload' + text: qsTr('Browse…') + width: solventCreationDialog.inputFieldWidth * 0.2 + onClicked: solventFilePicker.open() + } + } + + FileDialog { + id: solventFilePicker + fileMode: FileDialog.OpenFile + nameFilters: ['Any (*)', 'Structure files (*.gro *.pdb *.xyz)', 'Topology files (*.itp)'] + onAccepted: solventFileField.text = selectedFile + } + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewSubstructure.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewSubstructure.qml new file mode 100644 index 0000000..1d7c7e4 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/CreateNewSubstructure.qml @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + + +EaElements.Dialog{ + id: substructureCreationDialog + + property int inputFieldWidth: EaStyle.Sizes.fontPixelSize * 35 + + title: qsTr("Create a new Substructure") + standardButtons: Dialog.Ok | Dialog.Cancel + + onAccepted: { + Globals.BackendWrapper.latticeSubstructureSetLoaded({ + name: substructureNameField.text, + structure_type: substructureTypeField.currentText, + description: substructureDescrField.text, + file_path: substructureFileField.text + }) + } + + Column { + spacing: EaStyle.Sizes.fontPixelSize + + Row { + id: nameTypeRow + spacing: EaStyle.Sizes.fontPixelSize + + Column { + EaElements.Label { + enabled: false + text: qsTr("Name") + } + EaElements.TextField { + id: substructureNameField + implicitWidth: (substructureCreationDialog.inputFieldWidth - nameTypeRow.spacing) / 2 + horizontalAlignment: TextInput.AlignLeft + validator: RegularExpressionValidator { regularExpression: /^[a-zA-Z][a-zA-Z0-9_\-\.]{1,30}$/ } + placeholderText: qsTr("(optional) Enter Substructure name here") + } + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Structure type") + } + EaElements.ComboBox { + id: substructureTypeField + implicitWidth: (substructureCreationDialog.inputFieldWidth - nameTypeRow.spacing) / 2 + model: Globals.BackendWrapper.latticeSubstructureTypes + } + } + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Description") + } + EaElements.TextField { + id: substructureDescrField + implicitWidth: substructureCreationDialog.inputFieldWidth + horizontalAlignment: TextInput.AlignLeft + validator: RegularExpressionValidator { regularExpression: /^[a-zA-Z][a-zA-Z0-9_\-\.]{1,30}$/ } + placeholderText: qsTr("(optional) Enter Substructure description here") + } + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("File") + } + + Row { + id: fileRow + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.TextField { + id: substructureFileField + implicitWidth: substructureCreationDialog.inputFieldWidth + - browseFileButton.width + - fileRow.spacing + horizontalAlignment: TextInput.AlignLeft + placeholderText: qsTr("(optional) Choose a single structure file") + readOnly: true + } + + EaElements.SideBarButton { + id: browseFileButton + fontIcon: 'upload' + text: qsTr('Browse…') + width: substructureCreationDialog.inputFieldWidth * 0.2 + onClicked: substructureFilePicker.open() + } + } + + FileDialog { + id: substructureFilePicker + fileMode: FileDialog.OpenFile + nameFilters: ['Any (*)', 'Structure files (*.gro *.pdb *.xyz)', 'Topology files (*.itp)'] + onAccepted: substructureFileField.text = selectedFile + } + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingComponent.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingComponent.qml new file mode 100644 index 0000000..0209f82 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingComponent.qml @@ -0,0 +1,153 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle + +import Gui.Globals as Globals + + +EaElements.Dialog{ + id: sampleModelLoadDialog + + property int inputFieldWidth: EaStyle.Sizes.fontPixelSize * 35 + property alias availableComponentsModel: availableComponentsModel + + title: qsTr("Load Components from the Asset Library") + standardButtons: Dialog.Ok | Dialog.Cancel + + onAccepted: { + let selected = loadComponentListView.selectedIndexes + for (let i = 0; i < selected.length; ++i) { + var row = selected[i].row + var item = availableComponentsModel.get(row) + + Globals.BackendWrapper.componentsAppend({ + name: item.name, + component_type: item.component_type, + mint: item.mint, + mext: item.mext, + atoms: item.atoms + }) + } + loadComponentListView.clearSelection() + } + onRejected: { + loadComponentListView.clearSelection() + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Available in the Asset Library") + } + + EaComponents.ListView { + id: loadComponentListView + defaultInfoText: qsTr("No models found") + multiSelection: true + + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 2.5, + -1, + EaStyle.Sizes.fontPixelSize * 8, + EaStyle.Sizes.fontPixelSize * 6, + EaStyle.Sizes.fontPixelSize * 6, + EaStyle.Sizes.fontPixelSize * 6, + EaStyle.Sizes.tableRowHeight, + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr("№") + color: EaStyle.Colors.themeForegroundMinor + horizontalAlignment: Text.AlignHCenter + } + EaComponents.TableViewLabel { + text: qsTr("Name") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Type") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Atoms") + color: EaStyle.Colors.themeForegroundMinor + horizontalAlignment: Text.AlignHCenter + } + EaComponents.TableViewLabel { + text: qsTr("Mint") + color: EaStyle.Colors.themeForegroundMinor + horizontalAlignment: Text.AlignHCenter + } + EaComponents.TableViewLabel { + text: qsTr("Mext") + color: EaStyle.Colors.themeForegroundMinor + horizontalAlignment: Text.AlignHCenter + } + } + + model: ListModel { + id: availableComponentsModel + ListElement { name: "DPPC"; component_type: "Lipid"; atoms: 130; mint: 0; mext: 130 } + ListElement { name: "DOPC"; component_type: "Lipid"; atoms: 138; mint: 0; mext: 138 } + ListElement { name: "POPC"; component_type: "Lipid"; atoms: 134; mint: 0; mext: 134 } + ListElement { name: "DMPC"; component_type: "Lipid"; atoms: 118; mint: 0; mext: 118 } + ListElement { name: "Cholesterol"; component_type: "Lipid"; atoms: 74; mint: 0; mext: 74 } + ListElement { name: "SDS"; component_type: "Surfactant"; atoms: 42; mint: 0; mext: 42 } + ListElement { name: "CTAB"; component_type: "Surfactant"; atoms: 62; mint: 0; mext: 62 } + ListElement { name: "Triton-X100"; component_type: "Surfactant"; atoms: 85; mint: 0; mext: 85 } + ListElement { name: "Tween-20"; component_type: "Surfactant"; atoms: 98; mint: 0; mext: 98 } + ListElement { name: "D2O-buffer"; component_type: "Other"; atoms: 3; mint: 0; mext: 3 } + } + + delegateModelAccess: DelegateModel.ReadOnly + + delegate: EaComponents.ListViewDelegate { + required property int index + required property string name + required property string component_type + required property int atoms + required property int mint + required property int mext + + EaComponents.TableViewLabel { + text: index + 1 + horizontalAlignment: Text.AlignHCenter + enabled: false + } + EaComponents.TableViewLabel { + text: name + } + EaComponents.TableViewLabel { + text: component_type + } + EaComponents.TableViewLabel { + text: atoms + horizontalAlignment: Text.AlignHCenter + } + EaComponents.TableViewLabel { + text: mint + horizontalAlignment: Text.AlignHCenter + } + EaComponents.TableViewLabel { + text: mext + horizontalAlignment: Text.AlignHCenter + } + EaComponents.TableViewButton { + fontIcon: "minus-circle" + ToolTip.text: qsTr("Remove this component") + onClicked: availableComponentsModel.remove(index) + } + } + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingIon.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingIon.qml new file mode 100644 index 0000000..de267fb --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingIon.qml @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + + +EaElements.Dialog { + id: ionLoadDialog + + property int inputFieldWidth: EaStyle.Sizes.fontPixelSize * 25 + + title: qsTr("Load an Ion from the Asset Library") + standardButtons: Dialog.Ok | Dialog.Cancel + + // Block confirmation when the slot is full or no row is selected. + Component.onCompleted: { + var ok = standardButton(Dialog.Ok) + if (ok) { + ok.enabled = Qt.binding(function () { + if (!Globals.BackendWrapper.ionsLoaded + || Globals.BackendWrapper.ionsLoaded.count >= 2) return false + return loadIonListView.selectedIndexes.length > 0 + }) + } + } + + onAccepted: { + var indexes = loadIonListView.selectedIndexes + + if (indexes.length > 0) { + var row = indexes[0].row + var item = Globals.BackendWrapper.ionsAvailable.get(row) + Globals.BackendWrapper.ionsAppend({ name: item.name }) + loadIonListView.clearSelection() + } + } + onRejected: { + loadIonListView.clearSelection() + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Available in the Asset Library") + } + EaComponents.ListView { + id: loadIonListView + defaultInfoText: qsTr("No ions found") + multiSelection: false + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 2.5, + -1, + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr("№") + color: EaStyle.Colors.themeForegroundMinor + horizontalAlignment: Text.AlignHCenter + } + EaComponents.TableViewLabel { + text: qsTr("Name") + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: Globals.BackendWrapper.ionsAvailable + + delegate: EaComponents.ListViewDelegate { + required property int index + required property string name + + EaComponents.TableViewLabel { + text: index + 1 + horizontalAlignment: Text.AlignHCenter + enabled: false + } + EaComponents.TableViewLabel { + text: name + } + } + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingModel.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingModel.qml new file mode 100644 index 0000000..73e8542 --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingModel.qml @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle + +import Gui.Globals as Globals + + + +EaElements.Dialog{ + id: sampleModelLoadDialog + + property int inputFieldWidth: EaStyle.Sizes.fontPixelSize * 35 + + title: qsTr("Load a Sample Model from the Asset Library") + standardButtons: Dialog.Ok | Dialog.Cancel + + onAccepted: { + var indexes = loadModelListView.selectedIndexes + + if (indexes.length > 0) { + var row = indexes[0].row + var item = Globals.BackendWrapper.sampleModelAvailable.get(row) + Globals.BackendWrapper.sampleModelSetLoaded(item) + loadModelListView.clearSelection() + } + } + onRejected: { + loadModelListView.clearSelection() + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Available in the Asset Library") + } + EaComponents.ListView { + id: loadModelListView + defaultInfoText: qsTr("No models found") + multiSelection: false + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 2.5, + EaStyle.Sizes.fontPixelSize * 10, + EaStyle.Sizes.fontPixelSize * 6, + -1, + EaStyle.Sizes.tableRowHeight, + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr("№") + color: EaStyle.Colors.themeForegroundMinor + horizontalAlignment: Text.AlignHCenter + } + EaComponents.TableViewLabel { + text: qsTr("Name") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Type") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Description") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel {} // filler + } + + model: Globals.BackendWrapper.sampleModelAvailable + + delegate: EaComponents.ListViewDelegate { + required property int index + required property string name + required property string structure_type + required property string description + + EaComponents.TableViewLabel { + text: index + 1 + horizontalAlignment: Text.AlignHCenter + enabled: false + } + EaComponents.TableViewLabel { + text: name + } + EaComponents.TableViewLabel { + text: structure_type + } + EaComponents.TableViewLabel { + text: description + } + EaComponents.TableViewButton { + fontIcon: "minus-circle" + ToolTip.text: qsTr("Remove this component") + onClicked: Globals.BackendWrapper.sampleModelRemoveFromCatalog(index) + } + } + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingSolvent.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingSolvent.qml new file mode 100644 index 0000000..0e0c3ff --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingSolvent.qml @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements + +import Gui.Globals as Globals + + +EaElements.Dialog { + id: solventLoadDialog + + property int inputFieldWidth: EaStyle.Sizes.fontPixelSize * 35 + + title: qsTr("Load a Solvent from the Asset Library") + standardButtons: Dialog.Ok | Dialog.Cancel + + onAccepted: { + var indexes = loadSolventListView.selectedIndexes + + if (indexes.length > 0) { + var row = indexes[0].row + var item = Globals.BackendWrapper.solventAvailable.get(row) + Globals.BackendWrapper.solventSetLoaded(item) + loadSolventListView.clearSelection() + } + } + onRejected: { + loadSolventListView.clearSelection() + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Available in the Asset Library") + } + EaComponents.ListView { + id: loadSolventListView + defaultInfoText: qsTr("No solvents found") + multiSelection: false + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 2.5, + EaStyle.Sizes.fontPixelSize * 10, + EaStyle.Sizes.fontPixelSize * 6, + -1, + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr("№") + color: EaStyle.Colors.themeForegroundMinor + horizontalAlignment: Text.AlignHCenter + } + EaComponents.TableViewLabel { + text: qsTr("Name") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Type") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Description") + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: Globals.BackendWrapper.solventAvailable + + delegate: EaComponents.ListViewDelegate { + required property int index + required property string name + required property string solvent_type + required property string description + + EaComponents.TableViewLabel { + text: index + 1 + horizontalAlignment: Text.AlignHCenter + enabled: false + } + EaComponents.TableViewLabel { + text: name + } + EaComponents.TableViewLabel { + text: solvent_type + } + EaComponents.TableViewLabel { + text: description + ToolTip.text: description + } + } + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingSubstructure.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingSubstructure.qml new file mode 100644 index 0000000..61b896d --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/LoadExistingSubstructure.qml @@ -0,0 +1,107 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle + +import Gui.Globals as Globals + + +EaElements.Dialog{ + id: substructureLoadDialog + + property int inputFieldWidth: EaStyle.Sizes.fontPixelSize * 35 + + title: qsTr("Load a Substructure from the Asset Library") + standardButtons: Dialog.Ok | Dialog.Cancel + + onAccepted: { + var indexes = loadSubstructureListView.selectedIndexes + + if (indexes.length > 0) { + var row = indexes[0].row + var item = Globals.BackendWrapper.latticeSubstructureAvailable.get(row) + Globals.BackendWrapper.latticeSubstructureSetLoaded(item) + loadSubstructureListView.clearSelection() + } + } + onRejected: { + loadSubstructureListView.clearSelection() + } + + Column { + EaElements.Label { + enabled: false + text: qsTr("Available in the Asset Library") + } + EaComponents.ListView { + id: loadSubstructureListView + defaultInfoText: qsTr("No substructures found") + multiSelection: false + columnWidths: [ + EaStyle.Sizes.fontPixelSize * 2.5, + EaStyle.Sizes.fontPixelSize * 10, + EaStyle.Sizes.fontPixelSize * 6, + -1, + EaStyle.Sizes.tableRowHeight, + ] + + header: EaComponents.ListViewHeader { + EaComponents.TableViewLabel { + text: qsTr("№") + color: EaStyle.Colors.themeForegroundMinor + horizontalAlignment: Text.AlignHCenter + } + EaComponents.TableViewLabel { + text: qsTr("Name") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Type") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel { + text: qsTr("Description") + color: EaStyle.Colors.themeForegroundMinor + } + EaComponents.TableViewLabel {} // filler + } + + model: Globals.BackendWrapper.latticeSubstructureAvailable + + delegate: EaComponents.ListViewDelegate { + required property int index + required property string name + required property string structure_type + required property string description + + EaComponents.TableViewLabel { + text: index + 1 + horizontalAlignment: Text.AlignHCenter + enabled: false + } + EaComponents.TableViewLabel { + text: name + } + EaComponents.TableViewLabel { + text: structure_type + } + EaComponents.TableViewLabel { + text: description + } + EaComponents.TableViewButton { + fontIcon: "minus-circle" + ToolTip.text: qsTr("Remove this substructure") + onClicked: Globals.BackendWrapper.latticeSubstructureRemoveFromCatalog(index) + } + } + } + } +} diff --git a/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/OpenAssetFile.qml b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/OpenAssetFile.qml new file mode 100644 index 0000000..d22023d --- /dev/null +++ b/src/easyshapes_app/Gui/Pages/SampleModel/Sidebar/Basic/Popups/OpenAssetFile.qml @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Components as EaComponents + +import Gui.Globals as Globals + + +FileDialog{ + fileMode: FileDialog.OpenFiles + nameFilters: ['Any (*)', 'Structure files (*.gro *.pdb .*xyz)', 'Topology files (*.itp)', 'Smiles files (*.sml)'] + + onAccepted: { + for (let i = 0; i < selectedFiles.length; ++i) + Globals.BackendWrapper.componentsAppendPendingFilePath(selectedFiles[i]) + } + + Component.onCompleted: { + Globals.References.pages.samplemodel.sidebar.basic.popups.openAssetFile = this + } + +} diff --git a/src/easyshapes_app/Gui/Pages/Toolbox/Layout.qml b/src/easyshapes_app/Gui/Pages/Toolbox/Layout.qml deleted file mode 100644 index 360aa6f..0000000 --- a/src/easyshapes_app/Gui/Pages/Toolbox/Layout.qml +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents - -import Gui.Globals as Globals - - -EaComponents.ContentPage { - - mainView: EaComponents.MainContent { - tabs: [ - EaElements.TabButton { text: qsTr('Image') }, - EaElements.TabButton { text: qsTr('EaElements.GraphsView') }, - EaElements.TabButton { text: qsTr('EaElements.TextArea (Plain)') }, - EaElements.TabButton { text: qsTr('EaElements.TextArea (Rich)') } - ] - - items: [ - Loader { source: 'MainArea/Image.qml' }, - Loader { source: 'MainArea/GraphsView.qml' }, - Loader { source: 'MainArea/TextAreaPlain.qml' }, - Loader { source: 'MainArea/TextAreaRich.qml' } - ] - } - - sideBar: EaComponents.SideBar { - tabs: [ - EaElements.TabButton { text: qsTr('Basic controls') } - ] - - items: [ - Loader { source: 'Sidebar/Basic/Layout.qml' } - ] - - continueButton.visible: false - - } - - Component.onCompleted: console.debug(`Toolbox page loaded ::: ${this}`) - Component.onDestruction: console.debug(`Toolbox page destroyed ::: ${this}`) - -} diff --git a/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/GraphsView.qml b/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/GraphsView.qml deleted file mode 100644 index 9f2fd2d..0000000 --- a/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/GraphsView.qml +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtGraphs - -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements - -import Gui.Globals as Globals - - -GraphsView { - anchors.fill: parent - - marginTop: EaStyle.Sizes.fontPixelSize * 2 - marginBottom: EaStyle.Sizes.fontPixelSize * 2 - marginLeft: EaStyle.Sizes.fontPixelSize - marginRight: EaStyle.Sizes.fontPixelSize * 2 - - zoomAreaEnabled: true - - // theme - theme: GraphsTheme { - backgroundColor: EaStyle.Colors.chartBackground - plotAreaBackgroundColor: EaStyle.Colors.chartBackground - - axisX.mainColor: EaStyle.Colors.chartGridLine - axisX.mainWidth: 0 - - axisY.mainColor: EaStyle.Colors.chartGridLine - axisY.mainWidth: 0 - - gridVisible: true - grid.mainWidth: 1 - grid.subWidth: 0 - grid.mainColor: EaStyle.Colors.chartGridLine - grid.subColor: EaStyle.Colors.chartMinorGridLine - - labelFont.family: EaStyle.Fonts.fontFamily - labelFont.pixelSize: EaStyle.Sizes.fontPixelSize - labelTextColor: EaStyle.Colors.chartLabels - } - // theme - - // axisX - axisX: ValueAxis { - labelDelegate: TextEdit { - horizontalAlignment: TextInput.AlignHCenter - verticalAlignment: Text.AlignVCenter - bottomPadding: EaStyle.Sizes.fontPixelSize - color: EaStyle.Colors.chartLabels - } - - titleText: 'x' - min: 0 - max: 100 - } - // axisX - - // axisY - axisY: ValueAxis { - labelDelegate: TextEdit { - horizontalAlignment: TextInput.AlignRight - verticalAlignment: Text.AlignVCenter - rightPadding: -EaStyle.Sizes.fontPixelSize - color: EaStyle.Colors.chartLabels - } - - titleText: 'y' - min: -2 - max: 2 - } - // axisY - - // lineSeries - LineSeries { - color: 'red' - - XYPoint { x: 0; y: -1 } - XYPoint { x: 50; y: 1.5 } - XYPoint { x: 100; y: -0.5 } - } - // lineSeries - -} diff --git a/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/Image.qml b/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/Image.qml deleted file mode 100644 index fa89bc8..0000000 --- a/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/Image.qml +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtGraphs - -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements - -import Gui.Globals as Globals - -Image { - - fillMode: Image.PreserveAspectFit - source: "../../../Resources/Images/structure.png" - -} diff --git a/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/TextAreaPlain.qml b/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/TextAreaPlain.qml deleted file mode 100644 index 9d1b2e8..0000000 --- a/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/TextAreaPlain.qml +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtGraphs - -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements - -import Gui.Globals as Globals - - -EaElements.TextArea { - text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, -sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. -Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris -nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in -reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla -pariatur. Excepteur sint occaecat cupidatat non proident, sunt in -culpa qui officia deserunt mollit anim id est laborum.' -} diff --git a/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/TextAreaRich.qml b/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/TextAreaRich.qml deleted file mode 100644 index 7a04f57..0000000 --- a/src/easyshapes_app/Gui/Pages/Toolbox/MainArea/TextAreaRich.qml +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtGraphs - -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements - -import Gui.Globals as Globals - - -EaElements.TextArea { - textFormat: TextEdit.RichText - - text: '

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, - sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. - Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris - nisi ut aliquip ex ea
commodo consequat. -

- -

- Duis aute irure dolor in - reprehenderit in voluptate velit esse cillum dolore eu fugiat - nulla pariatur.
- Excepteur sint occaecat cupidatat non proident, sunt in - culpa qui officia deserunt mollit anim id est laborum. -

' -} diff --git a/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group1.qml b/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group1.qml deleted file mode 100644 index f177a50..0000000 --- a/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group1.qml +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic - -import Gui.Globals as Globals - -EaElements.GroupColumn {} diff --git a/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group2.qml b/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group2.qml deleted file mode 100644 index 585ee24..0000000 --- a/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group2.qml +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic - -import Gui.Globals as Globals - -EaElements.GroupColumn { - - EaElements.Label { - text: "EaElements.Label" - } - - EaElements.TextInput { - text: 'EaElements.TextInput' - } - - EaElements.TextField { - text: 'EaElements.TextField' - } - -} diff --git a/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group3.qml b/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group3.qml deleted file mode 100644 index f57f027..0000000 --- a/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group3.qml +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic - -import Gui.Globals as Globals - -EaElements.GroupColumn { - - EaElements.ComboBox { - model: ["EaElements.ComboBox 1", "EaElements.ComboBox 2", "EaElements.ComboBox 3"] - } - - Column { - EaElements.RadioButton { - checked: true - text: qsTr("EaElements.RadioButton 1") - } - EaElements.RadioButton { - text: qsTr("EaElements.RadioButton 2") - } - } - - Column { - spacing: 10 - EaElements.CheckBox { - checked: true - text: qsTr("EaElements.CheckBox 1") - } - EaElements.CheckBox { - text: qsTr("EaElements.CheckBox 2") - } - } - -} diff --git a/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group4.qml b/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group4.qml deleted file mode 100644 index a3d5b92..0000000 --- a/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Groups/Group4.qml +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents -import EasyApp.Gui.Logic as EaLogic - -import Gui.Globals as Globals - -EaElements.GroupColumn { - - - EaElements.SideBarButton { - fontIcon: 'plus-circle' - text: 'EaElements.SideBarButton' - } - - EaElements.Slider { - from: 1 - value: 25 - to: 100 - } - -} diff --git a/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Layout.qml b/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Layout.qml deleted file mode 100644 index 5e16781..0000000 --- a/src/easyshapes_app/Gui/Pages/Toolbox/Sidebar/Basic/Layout.qml +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: 2024 EasyApp contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2024 Contributors to the EasyApp project - -import QtQuick -import QtQuick.Controls - -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents - -import Gui.Globals as Globals - - -EaComponents.SideBarColumn { - - EaElements.GroupBox { - title: qsTr('Group 1: Empty') - icon: 'rocket' - - Loader { source: 'Groups/Group1.qml' } - } - - EaElements.GroupBox { - title: qsTr('Group 2: Label, TextInput, TextField') - icon: 'rocket' - collapsed: false - - Loader { source: 'Groups/Group2.qml' } - } - - EaElements.GroupBox { - title: qsTr('Group 3: ComboBox, RadioButton, CheckBox') - icon: 'rocket' - - Loader { source: 'Groups/Group3.qml' } - } - - EaElements.GroupBox { - title: qsTr('Group 4: SidebarButton, Slider') - icon: 'rocket' - - Loader { source: 'Groups/Group4.qml' } - } - -} diff --git a/src/easyshapes_app/Gui/StatusBar.qml b/src/easyshapes_app/Gui/StatusBar.qml index 2bd91f9..8f1b73a 100644 --- a/src/easyshapes_app/Gui/StatusBar.qml +++ b/src/easyshapes_app/Gui/StatusBar.qml @@ -5,9 +5,9 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents import Gui.Globals as Globals diff --git a/src/easyshapes_app/test.qml b/src/easyshapes_app/test.qml new file mode 100644 index 0000000..cb4d953 --- /dev/null +++ b/src/easyshapes_app/test.qml @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements +import Qt.labs.qmlmodels + +import Gui.Globals as Globals + +ApplicationWindow{ + + width: 650 + height: 680 + color: EaStyle.Colors.contentBackground + visible: true + + Grid { + spacing: EaStyle.Sizes.fontPixelSize * 4 + leftPadding: 12 + + Column { + width: 600 + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.ComboBox { + model: [qsTr("Light"), qsTr("Dark"), qsTr("System")] + onActivated: { + if (currentIndex === 0) + EaStyle.Colors.theme = EaStyle.Colors.LightTheme + else if (currentIndex === 1) + EaStyle.Colors.theme = EaStyle.Colors.DarkTheme + else if (currentIndex === 2) + EaStyle.Colors.theme = EaStyle.Colors.SystemTheme + } + Component.onCompleted: { + if (EaStyle.Colors.theme === EaStyle.Colors.LightTheme) + currentIndex = 0 + else if (EaStyle.Colors.theme === EaStyle.Colors.DarkTheme) + currentIndex = 1 + else if (EaStyle.Colors.theme === EaStyle.Colors.SystemTheme) + currentIndex = 2 + } + } + + // groubox to test the element + // EaElements.GroupBox { + // title: qsTr('Test a group widget') + // icon: 'wrench' + // collapsed: false + + // Loader { source: 'Gui/Pages/SampleModel/Sidebar/Basic/Groups/Solution.qml'} + // } + + EaElements.Pill { + text: "text" + } + + EaElements.Pill { + text: "fontIcon" + fontIcon: "atom" + } + + EaElements.Pill { + text: "superlongtext why can't I hold all this text in my hands" + } + } + } + + + + //Component.onCompleted: Globals.References.pages.samplemodel.sidebar.basic.popups.LoadExistingModel.open() + +} diff --git a/src/easyshapes_app/test2.qml b/src/easyshapes_app/test2.qml new file mode 100644 index 0000000..4468ba5 --- /dev/null +++ b/src/easyshapes_app/test2.qml @@ -0,0 +1,166 @@ +// SPDX-FileCopyrightText: 2024 EasyApp contributors +// SPDX-License-Identifier: BSD-3-Clause +// © 2024 Contributors to the EasyApp project + +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs + +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Components as EaComponents +import EasyApplication.Gui.Elements as EaElements +import Qt.labs.qmlmodels + +import Gui.Globals as Globals + +ApplicationWindow{ + + width: 650 + height: 580 + color: EaStyle.Colors.contentBackground + visible: true + + Grid { + spacing: EaStyle.Sizes.fontPixelSize * 4 + leftPadding: 12 + + + Column { + width: 600 + spacing: EaStyle.Sizes.fontPixelSize + + EaElements.ComboBox { + model: [qsTr("Light"), qsTr("Dark"), qsTr("System")] + onActivated: { + if (currentIndex === 0) + EaStyle.Colors.theme = EaStyle.Colors.LightTheme + else if (currentIndex === 1) + EaStyle.Colors.theme = EaStyle.Colors.DarkTheme + else if (currentIndex === 2) + EaStyle.Colors.theme = EaStyle.Colors.SystemTheme + } + Component.onCompleted: { + if (EaStyle.Colors.theme === EaStyle.Colors.LightTheme) + currentIndex = 0 + else if (EaStyle.Colors.theme === EaStyle.Colors.DarkTheme) + currentIndex = 1 + else if (EaStyle.Colors.theme === EaStyle.Colors.SystemTheme) + currentIndex = 2 + } + } + + EaComponents.TableView { + id: whatverTable + defaultInfoText: qsTr("No models found") + enabled: true + + header: EaComponents.TableViewHeader { + EaComponents.TableViewLabel { + id: modelNameColumnName + width: whatverTable.width * 0.25 + text: qsTr("Name") + color: EaStyle.Colors.themeForegroundMinor + leftPadding: EaStyle.Sizes.fontPixelSize * 0.7 + } + + EaComponents.TableViewLabel { + id: modelTypeColumnName + width: whatverTable.width * 0.15 + text: qsTr("Type") + color: EaStyle.Colors.themeForegroundMinor + } + + EaComponents.TableViewLabel { + id: modelDescrColumnName + width: whatverTable.width * 0.6 + text: qsTr("Description") + color: EaStyle.Colors.themeForegroundMinor + } + } + + model: ListModel { + id: availableSambleModelsModel + ListElement { name: "Samle1_aluv"; structure_type: "Vesicle"; description: "In order to avoid a prolonged pro-inflammatory neutrophil response, signaling downstream of an agonist-activated G protein-coupled receptor (GPCR) has to be rapidly terminated. Among the family of GPCR kinases (GRKs) that regulate receptor phosphorylation and signaling termination, GRK2, which is highly expressed by immune cells, plays an important role." } + ListElement { name: "Sample2_nanodisc"; structure_type: "Ring"; description: "The medium chain fatty acid receptor GPR84 as well as formyl peptide receptor 2 (FPR2)" } + ListElement { name: "Sample3_cubosome"; structure_type: "Lattice"; description: "receptors expressed in neutrophils, play a key role in regulating inflammation. In this study, we investigated the effects of GRK2 inhibitors on neutrophil functions induced by GPR84 and FPR2 agonists." } + ListElement { name: "Sample4"; structure_type: "Ring"; description: "GRK2 was shown to be expressed in human neutrophils and analysis of subcellular fractions" } + ListElement { name: "Sample5"; structure_type: "Ball"; description: "revealed a cytosolic localization. The GRK2 inhibitors enhanced and prolonged neutrophil production " } + ListElement { name: "Sample6"; structure_type: "Vesicle"; description: "production of reactive oxygen species (ROS) induced by GPR84- but not FPR2-agonists" } + ListElement { name: "Sample7"; structure_type: "Rod"; description: "suggesting a receptor selective function of GRK2. This suggestion was supported by β-arrestin recruitment data. The ROS production induced by a non β-arrestin recruiting GPR84" } + ListElement { name: "Sample8"; structure_type: "Bilayer"; description: "This suggestion was supported by β-arrestin recruitment data. The ROS production induced by a non β-arrestin recruiting GPR84 agonist was not affected by the GRK2 inhibitor." } + ListElement { name: "Sample9"; structure_type: "Monolayer"; description: "Termination of this β-arrestin independent response relied, similar to the response induced by FPR2 agonists, primarily on the actin cytoskeleton." } + ListElement { name: "Samplewithareallylongname"; structure_type: "Lattice"; description: "In summary, we show that GPR84 utilizes GRK2 in concert with β-arrestin and actin cytoskeleton dependent processes to fine-tune the activity of the ROS generating NADPH-oxidase in neutrophils." } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "1"; structure_type: "2"; description: "3" } + ListElement { name: "end1"; structure_type: "end2"; description: "end3" } + } + + delegate: EaComponents.TableViewDelegate { + required property int index + required property string name + required property string structure_type + required property string description + + EaComponents.TableViewLabel { + id: modelNameColumn + //width: whatverTable.width * 0.25 + text: name + } + + EaComponents.TableViewLabel { + id: typeColumn + //width: whatverTable.width * 0.15 + text: structure_type + } + + EaComponents.TableViewTextInput { + id: descrColumn + //width: whatverTable.width * 0.58 + text: description + } + } + + // ScrollBar.vertical: EaElements.ScrollBar { + // policy: ScrollBar.AlwaysOn // ScrollBar.AsNeeded // AlwaysOn + // } + + + } + } + } + + + + //Component.onCompleted: Globals.References.pages.samplemodel.sidebar.basic.popups.LoadExistingModel.open() + +}