[ui] Improve separation between main and Application

All the actions, file dialogs and message dialogs that are specific to
the application are removed from `main.qml` and placed instead in
`Application.qml`.

Only elements and functions related to the main window remain in
`main.qml`. As such, the `ensureSaved` and `ensureNotComputing` functions,
used when closing the main window, remain in `main.qml`.

Message dialogs that may be opened by these functions however are moved
to `Application.qml` as they are application-specific and can't be opened
on the homepage.

Application dialogs that may need to be called when triggering the closure
of the main window are aliased from `Application.qml` to remain callable
through the `StackView`.
This commit is contained in:
Candice Bentéjac 2024-09-03 10:36:26 +01:00
parent e0ec0aef55
commit e0120fd287
2 changed files with 505 additions and 502 deletions

View file

@ -16,6 +16,10 @@ import Controls 1.0
Page {
id: root
property alias computingAtExitDialog: computingAtExitDialog
property alias unsavedDialog: unsavedDialog
Settings {
id: settings_UILayout
category: 'UILayout'
@ -26,7 +30,6 @@ Page {
}
// Utility functions for elements in the menubar
function initFileDialogFolder(dialog, importImages = false) {
let folder = "";
@ -50,6 +53,496 @@ Page {
dialog.folder = folder
}
function getSelectedNodesName() {
if (!_reconstruction)
return ""
var nodesName = ""
for (var i = 0; i < _reconstruction.selectedNodes.count; i++) {
if (nodesName !== "")
nodesName += ", "
var node = _reconstruction.selectedNodes.at(i)
nodesName += node.name
}
return nodesName
}
property url imagesFolder: {
var recentImportedImagesFolders = MeshroomApp.recentImportedImagesFolders
if (recentImportedImagesFolders.length > 0) {
for (var i = 0; i < recentImportedImagesFolders.length; i++) {
if (Filepath.exists(recentImportedImagesFolders[i]))
return Filepath.stringToUrl(recentImportedImagesFolders[i])
else
MeshroomApp.removeRecentImportedImagesFolder(Filepath.stringToUrl(recentImportedImagesFolders[i]))
}
}
return ""
}
// File dialogs
Platform.FileDialog {
id: saveFileDialog
options: Platform.FileDialog.DontUseNativeDialog
signal closed(var result)
title: "Save File"
nameFilters: ["Meshroom Graphs (*.mg)"]
defaultSuffix: ".mg"
fileMode: Platform.FileDialog.SaveFile
onAccepted: {
_reconstruction.saveAs(currentFile)
closed(Platform.Dialog.Accepted)
MeshroomApp.addRecentProjectFile(currentFile.toString())
}
onRejected: closed(Platform.Dialog.Rejected)
}
Platform.FileDialog {
id: saveTemplateDialog
options: Platform.FileDialog.DontUseNativeDialog
signal closed(var result)
title: "Save Template"
nameFilters: ["Meshroom Graphs (*.mg)"]
defaultSuffix: ".mg"
fileMode: Platform.FileDialog.SaveFile
onAccepted: {
_reconstruction.saveAsTemplate(currentFile)
closed(Platform.Dialog.Accepted)
MeshroomApp.reloadTemplateList()
}
onRejected: closed(Platform.Dialog.Rejected)
}
Platform.FileDialog {
id: openFileDialog
options: Platform.FileDialog.DontUseNativeDialog
title: "Open File"
nameFilters: ["Meshroom Graphs (*.mg)"]
onAccepted: {
if (_reconstruction.loadUrl(currentFile)) {
MeshroomApp.addRecentProjectFile(currentFile.toString())
}
}
}
Platform.FileDialog {
id: loadTemplateDialog
options: Platform.FileDialog.DontUseNativeDialog
title: "Load Template"
nameFilters: ["Meshroom Graphs (*.mg)"]
onAccepted: {
// Open the template as a regular file
if (_reconstruction.loadUrl(currentFile, true, true)) {
MeshroomApp.addRecentProjectFile(currentFile.toString())
}
}
}
Platform.FileDialog {
id: importImagesDialog
options: Platform.FileDialog.DontUseNativeDialog
title: "Import Images"
fileMode: Platform.FileDialog.OpenFiles
nameFilters: []
onAccepted: {
_reconstruction.importImagesUrls(currentFiles)
imagesFolder = Filepath.dirname(currentFiles[0])
MeshroomApp.addRecentImportedImagesFolder(imagesFolder)
}
}
Platform.FileDialog {
id: importProjectDialog
options: Platform.FileDialog.DontUseNativeDialog
title: "Import Project"
fileMode: Platform.FileDialog.OpenFile
nameFilters: ["Meshroom Graphs (*.mg)"]
onAccepted: {
graphEditor.uigraph.importProject(currentFile)
}
}
Item {
id: computeManager
property bool warnIfUnsaved: true
// Evaluate if graph computation can be submitted externally
property bool canSubmit: _reconstruction ?
_reconstruction.canSubmit // current setup allows to compute externally
&& _reconstruction.graph.filepath : // graph is saved on disk
false
function compute(nodes, force) {
if (!force && warnIfUnsaved && !_reconstruction.graph.filepath) {
unsavedComputeDialog.selectedNodes = nodes;
unsavedComputeDialog.open();
}
else {
try {
_reconstruction.execute(nodes)
}
catch (error) {
const data = ErrorHandler.analyseError(error)
if (data.context === "COMPUTATION")
computeSubmitErrorDialog.openError(data.type, data.msg, nodes)
}
}
}
function submit(nodes) {
if (!canSubmit) {
unsavedSubmitDialog.open()
} else {
try {
_reconstruction.submit(nodes)
}
catch (error) {
const data = ErrorHandler.analyseError(error)
if (data.context === "SUBMITTING")
computeSubmitErrorDialog.openError(data.type, data.msg, nodes)
}
}
}
MessageDialog {
id: computeSubmitErrorDialog
property string errorType // Used to specify signals' behavior
property var currentNode: null
function openError(type, msg, node) {
errorType = type
switch (type) {
case "Already Submitted": {
this.setupPendingStatusError(msg, node)
break
}
case "Compatibility Issue": {
this.setupCompatibilityIssue(msg)
break
}
default: {
this.onlyDisplayError(msg)
}
}
this.open()
}
function onlyDisplayError(msg) {
text = msg
standardButtons = Dialog.Ok
}
function setupPendingStatusError(msg, node) {
currentNode = node
text = msg + "\n\nDo you want to Clear Pending Status and Start Computing?"
standardButtons = (Dialog.Ok | Dialog.Cancel)
}
function setupCompatibilityIssue(msg) {
text = msg + "\n\nDo you want to open the Compatibility Manager?"
standardButtons = (Dialog.Ok | Dialog.Cancel)
}
canCopy: false
icon.text: MaterialIcons.warning
parent: Overlay.overlay
preset: "Warning"
title: "Computation/Submitting"
text: ""
onAccepted: {
switch (errorType) {
case "Already Submitted": {
close()
_reconstruction.graph.clearSubmittedNodes()
_reconstruction.execute(currentNode)
break
}
case "Compatibility Issue": {
close()
compatibilityManager.open()
break
}
default: close()
}
}
onRejected: close()
}
MessageDialog {
id: unsavedComputeDialog
property var selectedNodes: null
canCopy: false
icon.text: MaterialIcons.warning
parent: Overlay.overlay
preset: "Warning"
title: "Unsaved Project"
text: "Data will be computed in the default cache folder if project remains unsaved."
detailedText: "Default cache folder: " + (_reconstruction ? _reconstruction.graph.cacheDir : "unknown")
helperText: "Save project first?"
standardButtons: Dialog.Discard | Dialog.Cancel | Dialog.Save
CheckBox {
Layout.alignment: Qt.AlignRight
text: "Don't ask again for this session"
padding: 0
onToggled: computeManager.warnIfUnsaved = !checked
}
Component.onCompleted: {
// Set up discard button text
standardButton(Dialog.Discard).text = "Continue without Saving"
}
onDiscarded: {
close()
computeManager.compute(selectedNodes, true)
}
onAccepted: saveAsAction.trigger()
}
MessageDialog {
id: unsavedSubmitDialog
canCopy: false
icon.text: MaterialIcons.warning
parent: Overlay.overlay
preset: "Warning"
title: "Unsaved Project"
text: "The project cannot be submitted if it remains unsaved."
helperText: "Save the project to be able to submit it?"
standardButtons: Dialog.Cancel | Dialog.Save
onDiscarded: close()
onAccepted: saveAsAction.trigger()
}
MessageDialog {
id: fileModifiedDialog
canCopy: false
icon.text: MaterialIcons.warning
parent: Overlay.overlay
preset: "Warning"
title: "File Modified"
text: "The file has been modified by another instance."
detailedText: "Do you want to overwrite the file?"
// Add a reload file button next to the save button
footer: DialogButtonBox {
position: DialogButtonBox.Footer
standardButtons: Dialog.Save | Dialog.Cancel
Button {
text: "Reload File"
onClicked: {
_reconstruction.loadUrl(_reconstruction.graph.filepath)
fileModifiedDialog.close()
}
}
}
onAccepted: _reconstruction.save()
onDiscarded: close()
}
}
// Message dialogs
MessageDialog {
id: unsavedDialog
property var _callback: undefined
title: (_reconstruction ? Filepath.basename(_reconstruction.graph.filepath) : "") || "Unsaved Project"
preset: "Info"
canCopy: false
text: _reconstruction && _reconstruction.graph.filepath ? "Current project has unsaved modifications."
: "Current project has not been saved."
helperText: _reconstruction && _reconstruction.graph.filepath ? "Would you like to save those changes?"
: "Would you like to save this project?"
standardButtons: Dialog.Save | Dialog.Cancel | Dialog.Discard
onDiscarded: {
close() // BUG ? discard does not close window
fireCallback()
}
onAccepted: {
// Save current file
if (saveAction.enabled)
{
saveAction.trigger()
fireCallback()
}
// Open "Save As" dialog
else
{
saveFileDialog.open()
function _callbackWrapper(rc) {
if (rc === Platform.Dialog.Accepted)
fireCallback()
saveFileDialog.closed.disconnect(_callbackWrapper)
}
saveFileDialog.closed.disconnect(_callbackWrapper)
}
}
function fireCallback()
{
// Call the callback and reset it
if (_callback)
_callback()
_callback = undefined
}
/// Open the unsaved dialog warning with an optional
/// callback to fire when the dialog is accepted/discarded
function prompt(callback)
{
_callback = callback
open()
}
}
MessageDialog {
id: computingAtExitDialog
title: "Operation in progress"
modal: true
canCopy: false
Label {
text: "Please stop any local computation before exiting Meshroom"
}
}
MessageDialog {
// Popup displayed while the application
// is busy building intrinsics while importing images
id: buildingIntrinsicsDialog
modal: true
visible: _reconstruction ? _reconstruction.buildingIntrinsics : false
closePolicy: Popup.NoAutoClose
title: "Initializing Cameras"
icon.text: MaterialIcons.camera
icon.font.pointSize: 10
canCopy: false
standardButtons: Dialog.NoButton
detailedText: "Extracting images metadata and creating Camera intrinsics..."
ProgressBar {
indeterminate: true
Layout.fillWidth: true
}
}
AboutDialog {
id: aboutDialog
}
DialogsFactory {
id: dialogsFactory
}
CompatibilityManager {
id: compatibilityManager
uigraph: _reconstruction
}
// Actions
Action {
id: removeAllImagesAction
property string tooltip: "Remove all the images from the current CameraInit group"
text: "Remove All Images"
onTriggered: {
_reconstruction.removeAllImages()
_reconstruction.selectedViewId = "-1"
}
}
Action {
id: removeImagesFromAllGroupsAction
property string tooltip: "Remove all the images from all the CameraInit groups"
text: "Remove Images From All CameraInit Nodes"
onTriggered: {
_reconstruction.removeImagesFromAllGroups()
_reconstruction.selectedViewId = "-1"
}
}
Action {
id: undoAction
property string tooltip: 'Undo "' + (_reconstruction ? _reconstruction.undoStack.undoText : "Unknown") + '"'
text: "Undo"
shortcut: "Ctrl+Z"
enabled: _reconstruction ? _reconstruction.undoStack.canUndo && _reconstruction.undoStack.isUndoableIndex : false
onTriggered: _reconstruction.undoStack.undo()
}
Action {
id: redoAction
property string tooltip: 'Redo "' + (_reconstruction ? _reconstruction.undoStack.redoText : "Unknown") + '"'
text: "Redo"
shortcut: "Ctrl+Shift+Z"
enabled: _reconstruction ? _reconstruction.undoStack.canRedo && !_reconstruction.undoStack.lockedRedo : false
onTriggered: _reconstruction.undoStack.redo()
}
Action {
id: cutAction
property string tooltip: {
var s = "Copy selected node"
s += (_reconstruction && _reconstruction.selectedNodes.count > 1 ? "s (" : " (") + getSelectedNodesName()
s += ") to the clipboard and remove them from the graph"
return s
}
text: "Cut Node" + (_reconstruction && _reconstruction.selectedNodes.count > 1 ? "s " : " ")
enabled: _reconstruction ? _reconstruction.selectedNodes.count > 0 : false
onTriggered: {
graphEditor.copyNodes()
graphEditor.uigraph.removeNodes(graphEditor.uigraph.selectedNodes)
}
}
Action {
id: copyAction
property string tooltip: {
var s = "Copy selected node"
s += (_reconstruction && _reconstruction.selectedNodes.count > 1 ? "s (" : " (") + getSelectedNodesName()
s += ") to the clipboard"
return s
}
text: "Copy Node" + (_reconstruction && _reconstruction.selectedNodes.count > 1 ? "s " : " ")
enabled: _reconstruction ? _reconstruction.selectedNodes.count > 0 : false
onTriggered: graphEditor.copyNodes()
}
Action {
id: pasteAction
property string tooltip: "Paste the clipboard content to the project if it contains valid nodes"
text: "Paste Node(s)"
onTriggered: graphEditor.pasteNodes()
}
Action {
id: loadTemplateAction
@ -185,10 +678,10 @@ Page {
enabled: _reconstruction ? (_reconstruction.graph && !_reconstruction.graph.filepath) || !_reconstruction.undoStack.clean : false
onTriggered: {
if (_reconstruction.graph.filepath) {
// get current time date
// Get current time date
var date = _reconstruction.graph.getFileDateVersionFromPath(_reconstruction.graph.filepath)
// check if the file has been modified by another instance
// Check if the file has been modified by another instance
if (_reconstruction.graph.fileDateVersion !== date) {
fileModifiedDialog.open()
} else
@ -368,7 +861,7 @@ Page {
Action {
text: "About Meshroom"
onTriggered: aboutDialog.open()
// should be StandardKey.HelpContents, but for some reason it's not stable
// Should be StandardKey.HelpContents, but for some reason it's not stable
// (may cause crash, requires pressing F1 twice after closing the popup)
shortcut: "F1"
}
@ -477,8 +970,7 @@ Page {
target: _reconstruction
// Bind messages to DialogsFactory
function createDialog(func, message)
{
function createDialog(func, message) {
var dialog = func(_window)
// Set text afterwards to avoid dialog sizing issues
dialog.title = message.title
@ -487,10 +979,10 @@ Page {
}
function onGraphChanged() {
// open CompatibilityManager after file loading if any issue is detected
// Open CompatibilityManager after file loading if any issue is detected
if (compatibilityManager.issueCount)
compatibilityManager.open()
// trigger fit to visualize all nodes
// Trigger fit to visualize all nodes
graphEditor.fit()
}

View file

@ -32,7 +32,7 @@ ApplicationWindow {
}
onClosing: {
// make sure document is saved before exiting application
// Make sure document is saved before exiting application
close.accepted = false
if (!ensureNotComputing())
return
@ -70,19 +70,6 @@ ApplicationWindow {
SystemPalette { id: activePalette }
SystemPalette { id: disabledPalette; colorGroup: SystemPalette.Disabled }
property url imagesFolder: {
var recentImportedImagesFolders = MeshroomApp.recentImportedImagesFolders
if (recentImportedImagesFolders.length > 0) {
for (var i = 0; i < recentImportedImagesFolders.length; i++) {
if (Filepath.exists(recentImportedImagesFolders[i]))
return Filepath.stringToUrl(recentImportedImagesFolders[i])
else
MeshroomApp.removeRecentImportedImagesFolder(Filepath.stringToUrl(recentImportedImagesFolders[i]))
}
}
return ""
}
Settings {
id: settings_General
category: 'General'
@ -91,511 +78,35 @@ ApplicationWindow {
}
Component.onDestruction: {
// store main window dimensions in persisting Settings
// Store main window dimensions in persisting Settings
settings_General.windowWidth = _window.width
settings_General.windowHeight = _window.height
}
MessageDialog {
id: unsavedDialog
property var _callback: undefined
title: (_reconstruction ? Filepath.basename(_reconstruction.graph.filepath) : "") || "Unsaved Project"
preset: "Info"
canCopy: false
text: _reconstruction && _reconstruction.graph.filepath ? "Current project has unsaved modifications."
: "Current project has not been saved."
helperText: _reconstruction && _reconstruction.graph.filepath ? "Would you like to save those changes?"
: "Would you like to save this project?"
standardButtons: Dialog.Save | Dialog.Cancel | Dialog.Discard
onDiscarded: {
close() // BUG ? discard does not close window
fireCallback()
}
onAccepted: {
// save current file
if (saveAction.enabled)
{
saveAction.trigger()
fireCallback()
}
// open "save as" dialog
else
{
saveFileDialog.open()
function _callbackWrapper(rc) {
if (rc === Platform.Dialog.Accepted)
fireCallback()
saveFileDialog.closed.disconnect(_callbackWrapper)
}
saveFileDialog.closed.connect(_callbackWrapper)
}
}
function fireCallback()
{
// call the callback and reset it
if (_callback)
_callback()
_callback = undefined
}
/// Open the unsaved dialog warning with an optional
/// callback to fire when the dialog is accepted/discarded
function prompt(callback)
{
_callback = callback
open()
}
}
Platform.FileDialog {
id: saveFileDialog
options: Platform.FileDialog.DontUseNativeDialog
signal closed(var result)
title: "Save File"
nameFilters: ["Meshroom Graphs (*.mg)"]
defaultSuffix: ".mg"
fileMode: Platform.FileDialog.SaveFile
onAccepted: {
_reconstruction.saveAs(currentFile)
closed(Platform.Dialog.Accepted)
MeshroomApp.addRecentProjectFile(currentFile.toString())
}
onRejected: closed(Platform.Dialog.Rejected)
}
Platform.FileDialog {
id: saveTemplateDialog
options: Platform.FileDialog.DontUseNativeDialog
signal closed(var result)
title: "Save Template"
nameFilters: ["Meshroom Graphs (*.mg)"]
defaultSuffix: ".mg"
fileMode: Platform.FileDialog.SaveFile
onAccepted: {
_reconstruction.saveAsTemplate(currentFile)
closed(Platform.Dialog.Accepted)
MeshroomApp.reloadTemplateList()
}
onRejected: closed(Platform.Dialog.Rejected)
}
Item {
id: computeManager
property bool warnIfUnsaved: true
// evaluate if graph computation can be submitted externally
property bool canSubmit: _reconstruction ?
_reconstruction.canSubmit // current setup allows to compute externally
&& _reconstruction.graph.filepath : // graph is saved on disk
false
function compute(nodes, force) {
if (!force && warnIfUnsaved && !_reconstruction.graph.filepath)
{
unsavedComputeDialog.selectedNodes = nodes;
unsavedComputeDialog.open();
}
else {
try {
_reconstruction.execute(nodes)
}
catch (error) {
const data = ErrorHandler.analyseError(error)
if (data.context === "COMPUTATION")
computeSubmitErrorDialog.openError(data.type, data.msg, nodes)
}
}
}
function submit(nodes) {
if (!canSubmit) {
unsavedSubmitDialog.open()
} else {
try {
_reconstruction.submit(nodes)
}
catch (error) {
const data = ErrorHandler.analyseError(error)
if (data.context === "SUBMITTING")
computeSubmitErrorDialog.openError(data.type, data.msg, nodes)
}
}
}
MessageDialog {
id: computeSubmitErrorDialog
property string errorType // Used to specify signals' behavior
property var currentNode: null
function openError(type, msg, node) {
errorType = type
switch (type) {
case "Already Submitted": {
this.setupPendingStatusError(msg, node)
break
}
case "Compatibility Issue": {
this.setupCompatibilityIssue(msg)
break
}
default: {
this.onlyDisplayError(msg)
}
}
this.open()
}
function onlyDisplayError(msg) {
text = msg
standardButtons = Dialog.Ok
}
function setupPendingStatusError(msg, node) {
currentNode = node
text = msg + "\n\nDo you want to Clear Pending Status and Start Computing?"
standardButtons = (Dialog.Ok | Dialog.Cancel)
}
function setupCompatibilityIssue(msg) {
text = msg + "\n\nDo you want to open the Compatibility Manager?"
standardButtons = (Dialog.Ok | Dialog.Cancel)
}
canCopy: false
icon.text: MaterialIcons.warning
parent: Overlay.overlay
preset: "Warning"
title: "Computation/Submitting"
text: ""
onAccepted: {
switch (errorType) {
case "Already Submitted": {
close()
_reconstruction.graph.clearSubmittedNodes()
_reconstruction.execute(currentNode)
break
}
case "Compatibility Issue": {
close()
compatibilityManager.open()
break
}
default: close()
}
}
onRejected: close()
}
MessageDialog {
id: unsavedComputeDialog
property var selectedNodes: null
canCopy: false
icon.text: MaterialIcons.warning
parent: Overlay.overlay
preset: "Warning"
title: "Unsaved Project"
text: "Data will be computed in the default cache folder if project remains unsaved."
detailedText: "Default cache folder: " + (_reconstruction ? _reconstruction.graph.cacheDir : "unknown")
helperText: "Save project first?"
standardButtons: Dialog.Discard | Dialog.Cancel | Dialog.Save
CheckBox {
Layout.alignment: Qt.AlignRight
text: "Don't ask again for this session"
padding: 0
onToggled: computeManager.warnIfUnsaved = !checked
}
Component.onCompleted: {
// set up discard button text
standardButton(Dialog.Discard).text = "Continue without Saving"
}
onDiscarded: {
close()
computeManager.compute(selectedNodes, true)
}
onAccepted: saveAsAction.trigger()
}
MessageDialog {
id: unsavedSubmitDialog
canCopy: false
icon.text: MaterialIcons.warning
parent: Overlay.overlay
preset: "Warning"
title: "Unsaved Project"
text: "The project cannot be submitted if it remains unsaved."
helperText: "Save the project to be able to submit it?"
standardButtons: Dialog.Cancel | Dialog.Save
onDiscarded: close()
onAccepted: saveAsAction.trigger()
}
MessageDialog {
id: fileModifiedDialog
canCopy: false
icon.text: MaterialIcons.warning
parent: Overlay.overlay
preset: "Warning"
title: "File Modified"
text: "The file has been modified by another instance."
detailedText: "Do you want to overwrite the file?"
// Add a reload file button next to the save button
footer: DialogButtonBox {
position: DialogButtonBox.Footer
standardButtons: Dialog.Save | Dialog.Cancel
Button {
text: "Reload File"
onClicked: {
_reconstruction.loadUrl(_reconstruction.graph.filepath)
fileModifiedDialog.close()
}
}
}
onAccepted: _reconstruction.save()
onDiscarded: close()
}
}
Platform.FileDialog {
id: openFileDialog
options: Platform.FileDialog.DontUseNativeDialog
title: "Open File"
nameFilters: ["Meshroom Graphs (*.mg)"]
onAccepted: {
if (_reconstruction.loadUrl(currentFile)) {
MeshroomApp.addRecentProjectFile(currentFile.toString())
}
}
}
Platform.FileDialog {
id: loadTemplateDialog
options: Platform.FileDialog.DontUseNativeDialog
title: "Load Template"
nameFilters: ["Meshroom Graphs (*.mg)"]
onAccepted: {
// Open the template as a regular file
if (_reconstruction.loadUrl(currentFile, true, true)) {
MeshroomApp.addRecentProjectFile(currentFile.toString())
}
}
}
Platform.FileDialog {
id: importImagesDialog
options: Platform.FileDialog.DontUseNativeDialog
title: "Import Images"
fileMode: Platform.FileDialog.OpenFiles
nameFilters: []
onAccepted: {
_reconstruction.importImagesUrls(currentFiles)
imagesFolder = Filepath.dirname(currentFiles[0])
MeshroomApp.addRecentImportedImagesFolder(imagesFolder)
}
}
Platform.FileDialog {
id: importProjectDialog
options: Platform.FileDialog.DontUseNativeDialog
title: "Import Project"
fileMode: Platform.FileDialog.OpenFile
nameFilters: ["Meshroom Graphs (*.mg)"]
onAccepted: {
graphEditor.uigraph.importProject(currentFile)
}
}
AboutDialog {
id: aboutDialog
}
// Check if document has been saved
function ensureSaved(callback)
{
var saved = _reconstruction.undoStack.clean
if (!saved) { // If current document is modified, open "unsaved dialog"
unsavedDialog.prompt(callback)
mainStack.currentItem.unsavedDialog.prompt(callback)
} else { // Otherwise, directly call the callback
callback()
}
return saved
}
MessageDialog {
id: computingAtExitDialog
title: "Operation in progress"
modal: true
canCopy: false
Label {
text: "Please stop any local computation before exiting Meshroom"
}
}
// Check and return whether no local computation is in progress
function ensureNotComputing()
{
if (_reconstruction.computingLocally)
{
// Open a warning dialog to ask for computation to be stopped
computingAtExitDialog.open()
mainStack.currentItem.computingAtExitDialog.open()
return false
}
return true
}
MessageDialog {
// Popup displayed while the application
// is busy building intrinsics while importing images
id: buildingIntrinsicsDialog
modal: true
visible: _reconstruction ? _reconstruction.buildingIntrinsics : false
closePolicy: Popup.NoAutoClose
title: "Initializing Cameras"
icon.text: MaterialIcons.camera
icon.font.pointSize: 10
canCopy: false
standardButtons: Dialog.NoButton
detailedText: "Extracting images metadata and creating Camera intrinsics..."
ProgressBar {
indeterminate: true
Layout.fillWidth: true
}
}
DialogsFactory {
id: dialogsFactory
}
CompatibilityManager {
id: compatibilityManager
uigraph: _reconstruction
}
Action {
id: removeAllImagesAction
property string tooltip: "Remove all the images from the current CameraInit group"
text: "Remove All Images"
onTriggered: {
_reconstruction.removeAllImages()
_reconstruction.selectedViewId = "-1"
}
}
Action {
id: removeImagesFromAllGroupsAction
property string tooltip: "Remove all the images from all the CameraInit groups"
text: "Remove Images From All CameraInit Nodes"
onTriggered: {
_reconstruction.removeImagesFromAllGroups()
_reconstruction.selectedViewId = "-1"
}
}
Action {
id: undoAction
property string tooltip: 'Undo "' + (_reconstruction ? _reconstruction.undoStack.undoText : "Unknown") + '"'
text: "Undo"
shortcut: "Ctrl+Z"
enabled: _reconstruction ? _reconstruction.undoStack.canUndo && _reconstruction.undoStack.isUndoableIndex : false
onTriggered: _reconstruction.undoStack.undo()
}
Action {
id: redoAction
property string tooltip: 'Redo "' + (_reconstruction ? _reconstruction.undoStack.redoText : "Unknown") + '"'
text: "Redo"
shortcut: "Ctrl+Shift+Z"
enabled: _reconstruction ? _reconstruction.undoStack.canRedo && !_reconstruction.undoStack.lockedRedo : false
onTriggered: _reconstruction.undoStack.redo()
}
function getSelectedNodesName()
{
if (!_reconstruction)
return ""
var nodesName = ""
for (var i = 0; i < _reconstruction.selectedNodes.count; i++)
{
if (nodesName !== "")
nodesName += ", "
var node = _reconstruction.selectedNodes.at(i)
nodesName += node.name
}
return nodesName
}
Action {
id: cutAction
property string tooltip: {
var s = "Copy selected node"
s += (_reconstruction && _reconstruction.selectedNodes.count > 1 ? "s (" : " (") + getSelectedNodesName()
s += ") to the clipboard and remove them from the graph"
return s
}
text: "Cut Node" + (_reconstruction && _reconstruction.selectedNodes.count > 1 ? "s " : " ")
enabled: _reconstruction ? _reconstruction.selectedNodes.count > 0 : false
onTriggered: {
graphEditor.copyNodes()
graphEditor.uigraph.removeNodes(graphEditor.uigraph.selectedNodes)
}
}
Action {
id: copyAction
property string tooltip: {
var s = "Copy selected node"
s += (_reconstruction && _reconstruction.selectedNodes.count > 1 ? "s (" : " (") + getSelectedNodesName()
s += ") to the clipboard"
return s
}
text: "Copy Node" + (_reconstruction && _reconstruction.selectedNodes.count > 1 ? "s " : " ")
enabled: _reconstruction ? _reconstruction.selectedNodes.count > 0 : false
onTriggered: graphEditor.copyNodes()
}
Action {
id: pasteAction
property string tooltip: "Paste the clipboard content to the project if it contains valid nodes"
text: "Paste Node(s)"
onTriggered: graphEditor.pasteNodes()
}
// TODO: uncomment for Qt6 to re-enable the alternative palette (the alternative palette and the disabled items currently cannot both be supported)
/* Action {
@ -629,4 +140,4 @@ ApplicationWindow {
forceActiveFocus();
}
}
}
}