Skip to content

Conversation

@Alxiice
Copy link
Contributor

@Alxiice Alxiice commented Dec 9, 2025

Why this PR ?

  • Having a way to save as a new version without overriding the current file
  • Having a more modern save UI standardized through different platforms

I was mainly inspired by Blender save UI :
image

Description of the content

Create a new file manager :

  • Left part with standard directories and favourites
  • Add right part with
    • search toolbar
    • file list view
    • open/save toolbar
  • the bottom toolbar is the main new addition :
    • there's a "+" sign that changes the name of the file to add an index so that we can save a new version
    • if the file exists the rectangle is colored in red to have a visual feedback

Peek 2025-12-09 09-54

Next steps

If reviewers are okay with this proposal there's a few changes to add still :

  • Replace all the file dialogs with this UI by default, test all
  • Add buttons to go to previous location, create folder
  • Add button to filter files and filter to ".mg" by default
  • tests
  • ...

@Alxiice Alxiice self-assigned this Dec 9, 2025
@Alxiice Alxiice requested a review from cbentejac December 9, 2025 09:17
@codecov
Copy link

codecov bot commented Dec 9, 2025

Codecov Report

❌ Patch coverage is 40.00000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.75%. Comparing base (cca5cc5) to head (44b26e4).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
meshroom/core/graph.py 40.00% 6 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #2951      +/-   ##
===========================================
- Coverage    79.80%   79.75%   -0.05%     
===========================================
  Files           64       64              
  Lines         8674     8684      +10     
===========================================
+ Hits          6922     6926       +4     
- Misses        1752     1758       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Alxiice Alxiice requested a review from fabiencastan December 9, 2025 09:18
@Alxiice Alxiice force-pushed the dev/save_as_updates branch 2 times, most recently from 4ebff46 to d30cf11 Compare December 12, 2025 14:25
@Alxiice Alxiice added the feature new feature (proposed as PR or issue planned by dev) label Dec 12, 2025
@Alxiice Alxiice added this to the Meshroom 2026.1.0 milestone Dec 12, 2025
@cbentejac cbentejac requested a review from Copilot January 14, 2026 10:03
@Alxiice Alxiice force-pushed the dev/save_as_updates branch from d30cf11 to 5aba40d Compare January 14, 2026 10:06
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.


// Custom title bar
// Could be used to add options and menus
// For now only the name of the mode (Opn/Save and a close UI button)
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a typo in this comment. "Opn" should be "Open".

Suggested change
// For now only the name of the mode (Opn/Save and a close UI button)
// For now only the name of the mode (Open/Save and a close UI button)

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +157
// onRejected: {
// console.log("File not saved")
// }
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commented-out code should be removed if it's not needed. Keeping commented-out code reduces maintainability.

Suggested change
// onRejected: {
// console.log("File not saved")
// }

Copilot uses AI. Check for mistakes.
Comment on lines +677 to +687
// Split filename and extension
var lastDotIndex = filename.lastIndexOf(".")
var baseName = ""
var extension = ""

if (lastDotIndex > 0) {
baseName = filename.substring(0, lastDotIndex)
extension = filename.substring(lastDotIndex)
} else {
baseName = filename
}
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a file doesn't have an extension (lastDotIndex <= 0), the extension variable remains empty but is still concatenated. While this works correctly, the condition should be lastDotIndex > 0 (which it is), but the comment or logic could be clearer about this edge case. The current implementation is correct but could benefit from a comment explaining the handling of files without extensions.

Copilot uses AI. Check for mistakes.
}

Label {
text: model.fileIsDir ? "" : (model.fileSize / 1024).toFixed(1) + " KB"
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file size is always displayed in KB (kilobytes), which may not be appropriate for very large files. Consider implementing dynamic units (KB, MB, GB) based on file size for better readability. For example, files larger than 1024 KB could be shown in MB.

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +62
@Slot(result=bool)
def isDarkPalette(self):
return QApplication.instance().palette() == self.darkPalette
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The isDarkPalette method uses equality comparison (==) to check if the current palette is the dark palette. This comparison may not work as expected since QPalette objects may not be comparable with ==. Consider using a dedicated boolean flag to track which palette is active, or compare specific palette properties instead.

Copilot uses AI. Check for mistakes.
color: Qt.lighter(root.activePalette.base, 1.4)

function getSelectedPath() {
return folderModel.folder.toString().replace("file://", "") + "/" + filenameField.text
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path concatenation using + "/" + could result in double slashes or incorrect paths if folderModel.folder already ends with a slash or if filenameField.text is empty. Consider using proper path joining utilities or handling edge cases explicitly to ensure correct path construction.

Suggested change
return folderModel.folder.toString().replace("file://", "") + "/" + filenameField.text
var basePath = folderModel.folder.toString().replace("file://", "");
// Remove trailing slash from basePath, if any
if (basePath.length > 1 && basePath[basePath.length - 1] === "/") {
basePath = basePath.slice(0, -1);
}
var fileName = filenameField.text;
if (!fileName) {
return basePath;
}
return basePath + "/" + fileName;

Copilot uses AI. Check for mistakes.
Comment on lines 182 to 152
property string fileToSave: ""

onFileSelected: (path) => {
fileToSave = path.toString().replace("file://", "")
// Do something with the file path
}

onAccepted: {
if (!validateFilepathForSave(fileToSave, newSaveFileDialog))
{
return;
}

// Only save a valid file
_reconstruction.saveAs("file://" + fileToSave)
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable 'fileToSave' is declared as a property but its value assignment in the onFileSelected handler appears redundant since the same path is already captured in the 'path' parameter and used in onAccepted. The fileToSave property could be removed and the path parameter from fileSelected could be used directly, or if it serves a specific purpose, that should be documented.

Suggested change
property string fileToSave: ""
onFileSelected: (path) => {
fileToSave = path.toString().replace("file://", "")
// Do something with the file path
}
onAccepted: {
if (!validateFilepathForSave(fileToSave, newSaveFileDialog))
{
return;
}
// Only save a valid file
_reconstruction.saveAs("file://" + fileToSave)
onAccepted: (path) => {
let fileToSave = path.toString().replace("file://", "")
if (!validateFilepathForSave(fileToSave, newSaveFileDialog))
{
return;
}
// Only save a valid file
_reconstruction.saveAs(path.toString())

Copilot uses AI. Check for mistakes.
Comment on lines +627 to +628
function getSelectedPath() {
return folderModel.folder.toString().replace("file://", "") + "/" + filenameField.text
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple places in this file use .replace("file://", "") to convert URLs to paths. This pattern is repeated throughout (lines 279, 303, 433, 522, 628). Consider creating a utility function to handle this conversion consistently and correctly, which would improve maintainability and reduce code duplication.

Suggested change
function getSelectedPath() {
return folderModel.folder.toString().replace("file://", "") + "/" + filenameField.text
function fileUrlToPath(url) {
return url.toString().replace("file://", "")
}
function getSelectedPath() {
return fileUrlToPath(folderModel.folder) + "/" + filenameField.text

Copilot uses AI. Check for mistakes.
}
}
Action {
id: newSaveAsAction
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The action name "newSaveAsAction" is somewhat ambiguous. The "new" prefix suggests creating something new, but this is actually a "Save As" action that allows saving with version incrementing. Consider renaming to something more descriptive like "saveAsWithVersionAction" or "saveAsNewVersionAction" to better reflect its purpose.

Suggested change
id: newSaveAsAction
id: saveAsNewVersionAction

Copilot uses AI. Check for mistakes.
Comment on lines +422 to +427
onClicked: {
var url = folderModel.folder.toString()
var parentPath = url.substring(0, url.lastIndexOf("/"))
if (parentPath.length > 7) // more than "file://"
folderModel.customFolder = parentPath
}
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the "Go up one level" button is clicked and the parent path length check fails (line 425), the operation silently does nothing. Consider providing user feedback (e.g., disabling the button or showing a tooltip) when at the root directory to indicate why navigation up is not possible.

Copilot uses AI. Check for mistakes.
@Alxiice Alxiice force-pushed the dev/save_as_updates branch 2 times, most recently from a1d8fd0 to 7cf7035 Compare January 19, 2026 09:15
@Alxiice Alxiice force-pushed the dev/save_as_updates branch from 7cf7035 to 44b26e4 Compare January 21, 2026 11:09
@Alxiice Alxiice marked this pull request as draft January 21, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature new feature (proposed as PR or issue planned by dev)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants