diff --git a/source/MRCommonPlugins/ViewerButtons/MRSceneControlMenuItems.cpp b/source/MRCommonPlugins/ViewerButtons/MRSceneControlMenuItems.cpp index 359ebc67992a..ff0d2c05e5e1 100644 --- a/source/MRCommonPlugins/ViewerButtons/MRSceneControlMenuItems.cpp +++ b/source/MRCommonPlugins/ViewerButtons/MRSceneControlMenuItems.cpp @@ -21,6 +21,7 @@ #include "MRViewer/MRRibbonConstants.h" #include "MRViewer/MRUIStyle.h" #include "MRViewer/MRSceneCache.h" +#include "MRViewer/MRUISaveChangesPopup.h" #include namespace @@ -97,85 +98,15 @@ void ResetSceneMenuItem::preDraw_() ImGui::SetNextWindowSize( windowSize, ImGuiCond_Always ); popupId_ = ImGui::GetID( "New scene##new scene" ); - - ImGui::PushStyleVar( ImGuiStyleVar_ItemSpacing, { 2.0f * cDefaultItemSpacing * scaling, 3.0f * cDefaultItemSpacing * scaling } ); - ImGui::PushStyleVar( ImGuiStyleVar_WindowPadding, { cModalWindowPaddingX * scaling, cModalWindowPaddingY * scaling } ); - if ( ImGui::BeginModalNoAnimation( "New scene##new scene", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar ) ) - { - auto headerFont = RibbonFontManager::getFontByTypeStatic( RibbonFontManager::FontType::Headline ); - if ( headerFont ) - ImGui::PushFont( headerFont ); - - const auto headerWidth = ImGui::CalcTextSize( "New Scene" ).x; - ImGui::SetCursorPosX( ( windowSize.x - headerWidth ) * 0.5f ); - ImGui::Text( "New Scene" ); - - if ( headerFont ) - ImGui::PopFont(); - - // do not suggest saving empty scene - const bool showSave = !SceneCache::getAllObjects().empty(); - if ( showSave ) - { - const char* text = "Save your changes?"; - ImGui::SetCursorPosX( ( windowSize.x - ImGui::CalcTextSize( text ).x ) * 0.5f ); - ImGui::Text( "%s", text ); - } - - const auto style = ImGui::GetStyle(); - ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, { style.FramePadding.x, cButtonPadding * scaling } ); - - const float p = ImGui::GetStyle().ItemSpacing.x; - const Vector2f btnSize{ showSave ? ( ImGui::GetContentRegionAvail().x - p * 2 ) / 3.f : ( ImGui::GetContentRegionAvail().x - p ) / 2.f, 0 }; - - if ( showSave ) - { - if ( UI::button( "Save", btnSize, ImGuiKey_Enter ) ) - { - auto savePath = SceneRoot::getScenePath(); - if ( savePath.empty() ) - savePath = saveFileDialog( { .filters = SceneSave::getFilters() } ); - - ImGui::CloseCurrentPopup(); - if ( !savePath.empty() ) - ProgressBar::orderWithMainThreadPostProcessing( "Saving scene", [this, savePath, &root = SceneRoot::get()]()->std::function - { - auto res = ObjectSave::toAnySupportedSceneFormat( root, savePath, ProgressBar::callBackSetProgress ); - - return[this, savePath, res]() - { - if ( res ) - { - getViewerInstance().onSceneSaved( savePath ); - resetScene_(); - } - else - showError( "Error saving scene: " + res.error() ); - }; - } ); - } - UI::setTooltipIfHovered( "Save current scene and then remove all objects", scaling ); - ImGui::SameLine(); - } - - if ( UI::buttonCommonSize( showSave ? "Don't Save" : "New", btnSize, ImGuiKey_N ) ) - { - ImGui::CloseCurrentPopup(); - resetScene_(); - } - UI::setTooltipIfHovered( "Remove all objects without saving and ability to restore them", scaling ); - ImGui::SameLine(); - if ( UI::buttonCommonSize( "Cancel", btnSize, ImGuiKey_Escape ) ) - ImGui::CloseCurrentPopup(); - - UI::setTooltipIfHovered( "Do not remove any objects, return back", scaling ); - - if ( ImGui::IsMouseClicked( 0 ) && !( ImGui::IsAnyItemHovered() || ImGui::IsWindowHovered( ImGuiHoveredFlags_AnyWindow ) ) ) - ImGui::CloseCurrentPopup(); - ImGui::PopStyleVar(); - ImGui::EndPopup(); - } - ImGui::PopStyleVar( 2 ); + UI::SaveChangesPopupSettings settings; + settings.scaling = scaling; + settings.header = "New Scene"; + settings.shortCloseText = "New"; + settings.saveTooltip = "Save current scene and then remove all objects"; + settings.dontSaveTooltip = "Remove all objects without saving and ability to restore them"; + settings.cancelTooltip = "Do not remove any objects, return back"; + settings.onOk = [this] () { resetScene_(); }; + UI::saveChangesPopup( "New scene##new scene", settings ); } void ResetSceneMenuItem::resetScene_() diff --git a/source/MRViewer/MRSaveOnClose.cpp b/source/MRViewer/MRSaveOnClose.cpp index d489d8482d82..8b967f63d303 100644 --- a/source/MRViewer/MRSaveOnClose.cpp +++ b/source/MRViewer/MRSaveOnClose.cpp @@ -17,6 +17,7 @@ #include #include "ImGuiHelpers.h" #include "MRPch/MRSpdlog.h" +#include "MRUISaveChangesPopup.h" #include #include @@ -57,7 +58,7 @@ void SaveOnClosePlugin::preDraw_() } else if ( noModalWasPresent ) { - ImGui::OpenPopup( "Application close##modal" ); + ImGui::OpenPopup( "Application Close##modal" ); showCloseModal_ = false; } else @@ -65,94 +66,21 @@ void SaveOnClosePlugin::preDraw_() showCloseModal_ = false; } } - const ImVec2 windowSize{ MR::cModalWindowWidth * scaling, -1 }; - ImGui::SetNextWindowSize( windowSize, ImGuiCond_Always ); - ImGui::PushStyleVar( ImGuiStyleVar_WindowPadding, { cModalWindowPaddingX * scaling, cModalWindowPaddingY * scaling } ); - ImGui::PushStyleVar( ImGuiStyleVar_ItemSpacing, { 2.0f * cDefaultItemSpacing * scaling, 3.0f * cDefaultItemSpacing * scaling } ); - if ( ImGui::BeginModalNoAnimation( "Application close##modal", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar ) ) - { - auto headerFont = RibbonFontManager::getFontByTypeStatic( RibbonFontManager::FontType::Headline ); - if ( headerFont ) - ImGui::PushFont( headerFont ); - - const auto headerWidth = ImGui::CalcTextSize( "Application Close" ).x; - - ImGui::SetCursorPosX( ( windowSize.x - headerWidth ) * 0.5f ); - ImGui::Text( "Application Close" ); - - if ( headerFont ) - ImGui::PopFont(); - - // do not suggest saving empty scene - const bool showSave = !SceneCache::getAllObjects().empty(); - if ( showSave ) - { - const char* text = "Save your changes?"; - ImGui::SetCursorPosX( ( windowSize.x - ImGui::CalcTextSize( text ).x ) * 0.5f ); - ImGui::Text( "%s", text ); - } - - const auto style = ImGui::GetStyle(); - ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, { style.FramePadding.x, cButtonPadding * scaling } ); - - const float p = ImGui::GetStyle().ItemSpacing.x; - const Vector2f btnSize{ showSave ? ( ImGui::GetContentRegionAvail().x - p * 2 ) / 3.f : ( ImGui::GetContentRegionAvail().x - p ) / 2.f, 0 }; - - if ( showSave ) - { - if ( UI::button( "Save", btnSize, ImGuiKey_Enter ) ) - { - auto savePath = SceneRoot::getScenePath(); - if ( savePath.empty() ) - savePath = saveFileDialog( { .filters = SceneSave::getFilters() } ); - - ImGui::CloseCurrentPopup(); - if ( !savePath.empty() ) - ProgressBar::orderWithMainThreadPostProcessing( "Saving scene", [&shouldClose = shouldClose_, savePath, &root = SceneRoot::get()]()->std::function - { - auto res = ObjectSave::toAnySupportedSceneFormat( root, savePath, ProgressBar::callBackSetProgress ); - - return[&shouldClose = shouldClose, savePath, res]() - { - if ( res ) - { - getViewerInstance().onSceneSaved( savePath ); - glfwSetWindowShouldClose( Viewer::instance()->window, true ); - shouldClose = true; - } - else - showError( "Error saving scene: " + res.error() ); - }; - } ); - } - UI::setTooltipIfHovered( "Save the current scene and close the application", scaling ); - ImGui::SameLine( 0, p ); - } - - if ( UI::button( showSave ? "Don't Save" : "Close", btnSize, ImGuiKey_N ) ) - { - glfwSetWindowShouldClose( Viewer::instance()->window, true ); - shouldClose_ = true; - ImGui::CloseCurrentPopup(); - } - UI::setTooltipIfHovered( "Close the application without saving", scaling ); - - ImGui::SameLine( 0, p ); - if ( UI::button( "Cancel", btnSize, ImGuiKey_Escape ) ) - { - ImGui::CloseCurrentPopup(); - } - UI::setTooltipIfHovered( "Do not close the application", scaling ); - - if ( ImGui::IsMouseClicked( 0 ) && !( ImGui::IsAnyItemHovered() || ImGui::IsWindowHovered( ImGuiHoveredFlags_AnyWindow ) ) ) - ImGui::CloseCurrentPopup(); - - ImGui::PopStyleVar(); - ImGui::EndPopup(); - } - - ImGui::PopStyleVar( 2 ); + UI::SaveChangesPopupSettings settings; + settings.scaling = scaling; + settings.header = "Application Close"; + settings.saveTooltip = "Save the current scene and close the application"; + settings.dontSaveTooltip = "Close the application without saving"; + settings.cancelTooltip = "Do not close the application"; + settings.onOk = [this] () + { + glfwSetWindowShouldClose( Viewer::instance()->window, true ); + shouldClose_ = true; + }; + UI::saveChangesPopup( + "Application Close##modal", + settings ); } void SaveOnClosePlugin::init( Viewer* _viewer ) diff --git a/source/MRViewer/MRUISaveChangesPopup.cpp b/source/MRViewer/MRUISaveChangesPopup.cpp new file mode 100644 index 000000000000..f9789b1e089f --- /dev/null +++ b/source/MRViewer/MRUISaveChangesPopup.cpp @@ -0,0 +1,116 @@ +#include "MRUISaveChangesPopup.h" + +#include "MRRibbonConstants.h" +#include "MRUIStyle.h" +#include "ImGuiHelpers.h" +#include "MRRibbonFontManager.h" +#include "MRFileDialog.h" +#include "MRProgressBar.h" +#include "MRShowModal.h" +#include "MRViewer.h" +#include "ImGuiMenu.h" +#include "MRSceneCache.h" +#include "MRMesh/MRSceneRoot.h" +#include "MRMesh/MRIOFormatsRegistry.h" +#include "MRMesh/MRObjectSave.h" +#include "MRMesh/MRVisualObject.h" + +namespace MR +{ + +namespace UI +{ + +void saveChangesPopup( const char* str_id, const SaveChangesPopupSettings& settings ) +{ + const ImVec2 windowSize{ cModalWindowWidth * settings.scaling, -1 }; + ImGui::SetNextWindowSize( windowSize, ImGuiCond_Always ); + + ImGui::PushStyleVar( ImGuiStyleVar_ItemSpacing, { 2.0f * cDefaultItemSpacing * settings.scaling, 3.0f * cDefaultItemSpacing * settings.scaling } ); + ImGui::PushStyleVar( ImGuiStyleVar_WindowPadding, { cModalWindowPaddingX * settings.scaling, cModalWindowPaddingY * settings.scaling } ); + if ( ImGui::BeginModalNoAnimation( str_id, nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar ) ) + { + auto headerFont = RibbonFontManager::getFontByTypeStatic( RibbonFontManager::FontType::Headline ); + if ( headerFont ) + ImGui::PushFont( headerFont ); + + const auto headerWidth = ImGui::CalcTextSize( settings.header.c_str() ).x; + ImGui::SetCursorPosX( ( windowSize.x - headerWidth ) * 0.5f ); + ImGui::Text( "%s", settings.header.c_str() ); + + if ( headerFont ) + ImGui::PopFont(); + + // do not suggest saving empty scene + const bool showSave = !SceneCache::getAllObjects().empty(); + if ( showSave ) + { + const char* text = "Save your changes?"; + ImGui::SetCursorPosX( ( windowSize.x - ImGui::CalcTextSize( text ).x ) * 0.5f ); + ImGui::Text( "%s", text ); + } + + const auto style = ImGui::GetStyle(); + ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, { style.FramePadding.x, cButtonPadding * settings.scaling } ); + + const float p = ImGui::GetStyle().ItemSpacing.x; + const Vector2f btnSize{ showSave ? ( ImGui::GetContentRegionAvail().x - p * 2 ) / 3.f : ( ImGui::GetContentRegionAvail().x - p ) / 2.f, 0 }; + + if ( showSave ) + { + if ( UI::button( "Save", btnSize, ImGuiKey_Enter ) ) + { + auto savePath = SceneRoot::getScenePath(); + if ( savePath.empty() ) + savePath = saveFileDialog( { .filters = SceneSave::getFilters() } ); + + ImGui::CloseCurrentPopup(); + if ( !savePath.empty() ) + ProgressBar::orderWithMainThreadPostProcessing( "Saving scene", [customFunction = settings.onOk, savePath, &root = SceneRoot::get()] ()->std::function + { + auto res = ObjectSave::toAnySupportedSceneFormat( root, savePath, ProgressBar::callBackSetProgress ); + + return[customFunction = customFunction, savePath, res] () + { + if ( res ) + { + getViewerInstance().onSceneSaved( savePath ); + if ( customFunction ) + customFunction(); + } + else + showError( "Error saving scene: " + res.error() ); + }; + } ); + } + if( !settings.saveTooltip.empty() ) + UI::setTooltipIfHovered( settings.saveTooltip.c_str(), settings.scaling ); + ImGui::SameLine(); + } + + if ( UI::buttonCommonSize( showSave ? settings.dontSaveText.c_str() : settings.shortCloseText.c_str(), btnSize, ImGuiKey_N) ) + { + ImGui::CloseCurrentPopup(); + if ( settings.onOk ) + settings.onOk(); + } + if ( !settings.dontSaveTooltip.empty() ) + UI::setTooltipIfHovered( settings.dontSaveTooltip.c_str(), settings.scaling ); + ImGui::SameLine(); + if ( UI::buttonCommonSize( "Cancel", btnSize, ImGuiKey_Escape ) ) + ImGui::CloseCurrentPopup(); + + if ( !settings.cancelTooltip.empty() ) + UI::setTooltipIfHovered( settings.cancelTooltip.c_str(), settings.scaling); + + if ( ImGui::IsMouseClicked( 0 ) && !( ImGui::IsAnyItemHovered() || ImGui::IsWindowHovered( ImGuiHoveredFlags_AnyWindow ) ) ) + ImGui::CloseCurrentPopup(); + ImGui::PopStyleVar(); + ImGui::EndPopup(); + } + ImGui::PopStyleVar( 2 ); +} + +} + +} diff --git a/source/MRViewer/MRUISaveChangesPopup.h b/source/MRViewer/MRUISaveChangesPopup.h new file mode 100644 index 000000000000..8b39dadd0741 --- /dev/null +++ b/source/MRViewer/MRUISaveChangesPopup.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include "exports.h" + +namespace MR +{ + +namespace UI +{ + +struct SaveChangesPopupSettings +{ + // menu scaling + float scaling = 1.0f; + // text that is shown if we have nothing to save + std::string shortCloseText = "Close"; + // text that is shown if we have changes but don't want to save them + std::string dontSaveText = "Don't Save"; + + std::string saveTooltip = "Save current scene"; + std::string dontSaveTooltip = "Donh't save current scene"; + std::string cancelTooltip = "Cansel"; + // header that is used in dialog + std::string header; + // if not empty this function is called on "save" and "not save" options( if succeed ) + std::function onOk = {}; +}; +// Shows ImGui popup that suggests user to save changes, +// user need to call ImGui::OpenPopup( str_id ) to open this popup. +// It has 3 options: save, don't save, cancel +// str_id - ImGui string id for the popup window +// settings - settings for dialog +MRVIEWER_API void saveChangesPopup( const char* str_id, const SaveChangesPopupSettings& settings = {} ); +} + +} diff --git a/source/MRViewer/MRViewer.vcxproj b/source/MRViewer/MRViewer.vcxproj index ce0d6139d8d7..6ea040e93d58 100644 --- a/source/MRViewer/MRViewer.vcxproj +++ b/source/MRViewer/MRViewer.vcxproj @@ -42,6 +42,7 @@ + @@ -171,6 +172,7 @@ + diff --git a/source/MRViewer/MRViewer.vcxproj.filters b/source/MRViewer/MRViewer.vcxproj.filters index fc32d4a55881..addf2e0521cb 100644 --- a/source/MRViewer/MRViewer.vcxproj.filters +++ b/source/MRViewer/MRViewer.vcxproj.filters @@ -439,6 +439,9 @@ Source Files + + Helpers + @@ -860,6 +863,9 @@ Source Files + + Helpers +