mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
Display a warning if SUI is unable to write to the settings file (#19027)
Adds logic to display a warning popup if the settings.json is marked as read-only and we try to write to the settings.json file. Previously, this scenario would crash, which definitely isn't right. However, a simple fix of "not-crashing" wouldn't feel right either. This leverages the existing infrastructure to display a warning dialog when we failed to write to the settings file. The main annoyance here is that that popup dialog is located in `TerminalWindow` and is normally triggered from a failed `SettingsLoadEventArgs`. To get around this, `CascadiaSettings::WriteSettingsToDisk()` now returns a boolean to signal if the write was successful; whereas if it fails, a warning is added to the settings object. If we fail to write to disk, the function will return false and we'll raise an event with the settings' warnings to `TerminalPage` which passes it along to `TerminalWindow`. Additionally, this uses `IVectorView<SettingsLoadWarnings>` as opposed to `IVector<SettingsLoadWarnings>` throughout the relevant code. It's more correct as the list of warnings shouldn't be mutable and the warnings from the `CascadiaSettings` object are retrieved in that format. ## Validation Steps Performed - ✅ Using SUI, save settings when the settings.json is set to read-only Closes #18913
This commit is contained in:
parent
3680e13bc0
commit
218c9fbe3e
@ -392,7 +392,7 @@ namespace winrt::TerminalApp::implementation
|
||||
auto ev = winrt::make_self<SettingsLoadEventArgs>(true,
|
||||
static_cast<uint64_t>(_settingsLoadedResult),
|
||||
_settingsLoadExceptionText,
|
||||
warnings,
|
||||
warnings.GetView(),
|
||||
_settings);
|
||||
SettingsChanged.raise(*this, *ev);
|
||||
return;
|
||||
@ -424,7 +424,7 @@ namespace winrt::TerminalApp::implementation
|
||||
auto ev = winrt::make_self<SettingsLoadEventArgs>(!initialLoad,
|
||||
_settingsLoadedResult,
|
||||
_settingsLoadExceptionText,
|
||||
warnings,
|
||||
warnings.GetView(),
|
||||
_settings);
|
||||
SettingsChanged.raise(*this, *ev);
|
||||
}
|
||||
@ -491,7 +491,7 @@ namespace winrt::TerminalApp::implementation
|
||||
auto ev = winrt::make_self<SettingsLoadEventArgs>(false,
|
||||
_settingsLoadedResult,
|
||||
_settingsLoadExceptionText,
|
||||
warnings,
|
||||
warnings.GetView(),
|
||||
_settings);
|
||||
|
||||
auto window = winrt::make_self<implementation::TerminalWindow>(*ev, _contentManager);
|
||||
|
||||
@ -12,14 +12,14 @@ namespace winrt::TerminalApp::implementation
|
||||
WINRT_PROPERTY(bool, Reload, false);
|
||||
WINRT_PROPERTY(uint64_t, Result, S_OK);
|
||||
WINRT_PROPERTY(winrt::hstring, ExceptionText, L"");
|
||||
WINRT_PROPERTY(winrt::Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>, Warnings, nullptr);
|
||||
WINRT_PROPERTY(winrt::Windows::Foundation::Collections::IVectorView<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>, Warnings, nullptr);
|
||||
WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::CascadiaSettings, NewSettings, nullptr);
|
||||
|
||||
public:
|
||||
SettingsLoadEventArgs(bool reload,
|
||||
uint64_t result,
|
||||
winrt::hstring exceptionText,
|
||||
winrt::Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> warnings,
|
||||
winrt::Windows::Foundation::Collections::IVectorView<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> warnings,
|
||||
Microsoft::Terminal::Settings::Model::CascadiaSettings newSettings) :
|
||||
_Reload{ reload },
|
||||
_Result{ result },
|
||||
|
||||
@ -4165,6 +4165,13 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
sui.ShowLoadWarningsDialog([weakThis{ get_weak() }](auto&& /*s*/, const Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>& warnings) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
page->ShowLoadWarningsDialog.raise(*page, warnings);
|
||||
}
|
||||
});
|
||||
|
||||
return *settingsContent;
|
||||
}
|
||||
|
||||
|
||||
@ -187,6 +187,7 @@ namespace winrt::TerminalApp::implementation
|
||||
til::typed_event<IInspectable, IInspectable> OpenSystemMenu;
|
||||
til::typed_event<IInspectable, IInspectable> QuitRequested;
|
||||
til::typed_event<IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs> ShowWindowChanged;
|
||||
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>> ShowLoadWarningsDialog;
|
||||
|
||||
til::typed_event<Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs> RequestMoveContent;
|
||||
til::typed_event<Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs> RequestReceiveContent;
|
||||
|
||||
@ -97,6 +97,7 @@ namespace TerminalApp
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.Foundation.Collections.IVectorView<Microsoft.Terminal.Settings.Model.SettingsLoadWarnings> > ShowLoadWarningsDialog;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestMoveContentArgs> RequestMoveContent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> RequestReceiveContent;
|
||||
|
||||
@ -220,6 +220,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_root->Initialized({ get_weak(), &TerminalWindow::_pageInitialized });
|
||||
_root->WindowSizeChanged({ get_weak(), &TerminalWindow::_WindowSizeChanged });
|
||||
_root->RenameWindowRequested({ get_weak(), &TerminalWindow::_RenameWindowRequested });
|
||||
_root->ShowLoadWarningsDialog({ get_weak(), &TerminalWindow::_ShowLoadWarningsDialog });
|
||||
_root->Create();
|
||||
|
||||
AppLogic::Current()->SettingsChanged({ get_weak(), &TerminalWindow::UpdateSettingsHandler });
|
||||
@ -478,7 +479,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// validating the settings.
|
||||
// - Only one dialog can be visible at a time. If another dialog is visible
|
||||
// when this is called, nothing happens. See ShowDialog for details
|
||||
void TerminalWindow::_ShowLoadWarningsDialog(const Windows::Foundation::Collections::IVector<SettingsLoadWarnings>& warnings)
|
||||
void TerminalWindow::_ShowLoadWarningsDialog(const IInspectable&, const Windows::Foundation::Collections::IVectorView<SettingsLoadWarnings>& warnings)
|
||||
{
|
||||
auto title = RS_(L"SettingsValidateErrorTitle");
|
||||
auto buttonText = RS_(L"Ok");
|
||||
@ -539,7 +540,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else if (settingsLoadedResult == S_FALSE)
|
||||
{
|
||||
_ShowLoadWarningsDialog(_initialLoadResult.Warnings());
|
||||
_ShowLoadWarningsDialog(nullptr, _initialLoadResult.Warnings());
|
||||
}
|
||||
}
|
||||
|
||||
@ -825,7 +826,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else if (args.Result() == S_FALSE)
|
||||
{
|
||||
_ShowLoadWarningsDialog(args.Warnings());
|
||||
_ShowLoadWarningsDialog(nullptr, args.Warnings());
|
||||
}
|
||||
else if (args.Result() == S_OK)
|
||||
{
|
||||
|
||||
@ -189,7 +189,7 @@ namespace winrt::TerminalApp::implementation
|
||||
const winrt::hstring& contentKey,
|
||||
HRESULT settingsLoadedResult,
|
||||
const winrt::hstring& exceptionText);
|
||||
void _ShowLoadWarningsDialog(const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>& warnings);
|
||||
void _ShowLoadWarningsDialog(const IInspectable& sender, const Windows::Foundation::Collections::IVectorView<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings>& warnings);
|
||||
|
||||
bool _IsKeyboardServiceEnabled();
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ namespace TerminalApp
|
||||
{
|
||||
Boolean Reload { get; };
|
||||
UInt64 Result { get; };
|
||||
IVector<Microsoft.Terminal.Settings.Model.SettingsLoadWarnings> Warnings { get; };
|
||||
IVectorView<Microsoft.Terminal.Settings.Model.SettingsLoadWarnings> Warnings { get; };
|
||||
String ExceptionText { get; };
|
||||
|
||||
Microsoft.Terminal.Settings.Model.CascadiaSettings NewSettings { get; };
|
||||
|
||||
@ -680,7 +680,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
void MainPage::SaveButton_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
|
||||
{
|
||||
_settingsClone.LogSettingChanges(false);
|
||||
_settingsClone.WriteSettingsToDisk();
|
||||
if (!_settingsClone.WriteSettingsToDisk())
|
||||
{
|
||||
ShowLoadWarningsDialog.raise(*this, _settingsClone.Warnings());
|
||||
}
|
||||
}
|
||||
|
||||
void MainPage::ResetButton_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
|
||||
|
||||
@ -46,6 +46,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
Editor::ExtensionsViewModel ExtensionsVM() const noexcept { return _extensionsVM; }
|
||||
|
||||
til::typed_event<Windows::Foundation::IInspectable, Model::SettingsTarget> OpenJson;
|
||||
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::Collections::IVectorView<Model::SettingsLoadWarnings>> ShowLoadWarningsDialog;
|
||||
|
||||
private:
|
||||
Windows::Foundation::Collections::IObservableVector<IInspectable> _breadcrumbs;
|
||||
|
||||
@ -39,6 +39,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
|
||||
void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.SettingsTarget> OpenJson;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.Foundation.Collections.IVectorView<Microsoft.Terminal.Settings.Model.SettingsLoadWarnings> > ShowLoadWarningsDialog;
|
||||
|
||||
// Due to the aforementioned bug, we can't use IInitializeWithWindow _here_ either.
|
||||
// Let's just smuggle the HWND in as a UInt64 :|
|
||||
|
||||
@ -167,7 +167,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
winrt::Windows::Foundation::Collections::IVectorView<Model::ExtensionPackage> Extensions();
|
||||
void ResetApplicationState() const;
|
||||
void ResetToDefaultSettings();
|
||||
void WriteSettingsToDisk();
|
||||
bool WriteSettingsToDisk();
|
||||
Json::Value ToJson() const;
|
||||
Model::Profile ProfileDefaults() const;
|
||||
Model::Profile CreateNewProfile();
|
||||
|
||||
@ -31,7 +31,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
CascadiaSettings Copy();
|
||||
void ResetApplicationState();
|
||||
void ResetToDefaultSettings();
|
||||
void WriteSettingsToDisk();
|
||||
Boolean WriteSettingsToDisk();
|
||||
void LogSettingChanges(Boolean isJsonLoad);
|
||||
|
||||
String Hash { get; };
|
||||
|
||||
@ -1226,15 +1226,7 @@ try
|
||||
// settings string back to the file.
|
||||
if (mustWriteToDisk)
|
||||
{
|
||||
try
|
||||
{
|
||||
settings->WriteSettingsToDisk();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
settings->_warnings.Append(SettingsLoadWarnings::FailedToWriteToSettings);
|
||||
}
|
||||
settings->WriteSettingsToDisk();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1539,7 +1531,7 @@ void CascadiaSettings::ResetToDefaultSettings()
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CascadiaSettings::WriteSettingsToDisk()
|
||||
bool CascadiaSettings::WriteSettingsToDisk()
|
||||
{
|
||||
// write current settings to current settings file
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
@ -1547,7 +1539,17 @@ void CascadiaSettings::WriteSettingsToDisk()
|
||||
wbuilder.settings_["indentation"] = " ";
|
||||
wbuilder.settings_["precision"] = 6; // prevent values like 1.1000000000000001
|
||||
|
||||
_writeSettingsToDisk(Json::writeString(wbuilder, ToJson()));
|
||||
try
|
||||
{
|
||||
_writeSettingsToDisk(Json::writeString(wbuilder, ToJson()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
_warnings.Append(SettingsLoadWarnings::FailedToWriteToSettings);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CascadiaSettings::_writeSettingsToDisk(std::string_view contents)
|
||||
|
||||
@ -256,7 +256,11 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
// renaming one is (supposed to be) atomic.
|
||||
// Wait... "supposed to be"!? Well it's technically not always atomic,
|
||||
// but it's pretty darn close to it, so... better than nothing.
|
||||
std::filesystem::rename(tmpPath, resolvedPath);
|
||||
std::filesystem::rename(tmpPath, resolvedPath, ec);
|
||||
if (ec)
|
||||
{
|
||||
THROW_WIN32_MSG(ec.value(), "failed to write to file");
|
||||
}
|
||||
}
|
||||
} // io
|
||||
} // til
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user