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,
|
auto ev = winrt::make_self<SettingsLoadEventArgs>(true,
|
||||||
static_cast<uint64_t>(_settingsLoadedResult),
|
static_cast<uint64_t>(_settingsLoadedResult),
|
||||||
_settingsLoadExceptionText,
|
_settingsLoadExceptionText,
|
||||||
warnings,
|
warnings.GetView(),
|
||||||
_settings);
|
_settings);
|
||||||
SettingsChanged.raise(*this, *ev);
|
SettingsChanged.raise(*this, *ev);
|
||||||
return;
|
return;
|
||||||
@ -424,7 +424,7 @@ namespace winrt::TerminalApp::implementation
|
|||||||
auto ev = winrt::make_self<SettingsLoadEventArgs>(!initialLoad,
|
auto ev = winrt::make_self<SettingsLoadEventArgs>(!initialLoad,
|
||||||
_settingsLoadedResult,
|
_settingsLoadedResult,
|
||||||
_settingsLoadExceptionText,
|
_settingsLoadExceptionText,
|
||||||
warnings,
|
warnings.GetView(),
|
||||||
_settings);
|
_settings);
|
||||||
SettingsChanged.raise(*this, *ev);
|
SettingsChanged.raise(*this, *ev);
|
||||||
}
|
}
|
||||||
@ -491,7 +491,7 @@ namespace winrt::TerminalApp::implementation
|
|||||||
auto ev = winrt::make_self<SettingsLoadEventArgs>(false,
|
auto ev = winrt::make_self<SettingsLoadEventArgs>(false,
|
||||||
_settingsLoadedResult,
|
_settingsLoadedResult,
|
||||||
_settingsLoadExceptionText,
|
_settingsLoadExceptionText,
|
||||||
warnings,
|
warnings.GetView(),
|
||||||
_settings);
|
_settings);
|
||||||
|
|
||||||
auto window = winrt::make_self<implementation::TerminalWindow>(*ev, _contentManager);
|
auto window = winrt::make_self<implementation::TerminalWindow>(*ev, _contentManager);
|
||||||
|
|||||||
@ -12,14 +12,14 @@ namespace winrt::TerminalApp::implementation
|
|||||||
WINRT_PROPERTY(bool, Reload, false);
|
WINRT_PROPERTY(bool, Reload, false);
|
||||||
WINRT_PROPERTY(uint64_t, Result, S_OK);
|
WINRT_PROPERTY(uint64_t, Result, S_OK);
|
||||||
WINRT_PROPERTY(winrt::hstring, ExceptionText, L"");
|
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);
|
WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::CascadiaSettings, NewSettings, nullptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SettingsLoadEventArgs(bool reload,
|
SettingsLoadEventArgs(bool reload,
|
||||||
uint64_t result,
|
uint64_t result,
|
||||||
winrt::hstring exceptionText,
|
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) :
|
Microsoft::Terminal::Settings::Model::CascadiaSettings newSettings) :
|
||||||
_Reload{ reload },
|
_Reload{ reload },
|
||||||
_Result{ result },
|
_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;
|
return *settingsContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -187,6 +187,7 @@ namespace winrt::TerminalApp::implementation
|
|||||||
til::typed_event<IInspectable, IInspectable> OpenSystemMenu;
|
til::typed_event<IInspectable, IInspectable> OpenSystemMenu;
|
||||||
til::typed_event<IInspectable, IInspectable> QuitRequested;
|
til::typed_event<IInspectable, IInspectable> QuitRequested;
|
||||||
til::typed_event<IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs> ShowWindowChanged;
|
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::RequestMoveContentArgs> RequestMoveContent;
|
||||||
til::typed_event<Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs> RequestReceiveContent;
|
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, Object> OpenSystemMenu;
|
||||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
|
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, RequestMoveContentArgs> RequestMoveContent;
|
||||||
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> RequestReceiveContent;
|
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> RequestReceiveContent;
|
||||||
|
|||||||
@ -220,6 +220,7 @@ namespace winrt::TerminalApp::implementation
|
|||||||
_root->Initialized({ get_weak(), &TerminalWindow::_pageInitialized });
|
_root->Initialized({ get_weak(), &TerminalWindow::_pageInitialized });
|
||||||
_root->WindowSizeChanged({ get_weak(), &TerminalWindow::_WindowSizeChanged });
|
_root->WindowSizeChanged({ get_weak(), &TerminalWindow::_WindowSizeChanged });
|
||||||
_root->RenameWindowRequested({ get_weak(), &TerminalWindow::_RenameWindowRequested });
|
_root->RenameWindowRequested({ get_weak(), &TerminalWindow::_RenameWindowRequested });
|
||||||
|
_root->ShowLoadWarningsDialog({ get_weak(), &TerminalWindow::_ShowLoadWarningsDialog });
|
||||||
_root->Create();
|
_root->Create();
|
||||||
|
|
||||||
AppLogic::Current()->SettingsChanged({ get_weak(), &TerminalWindow::UpdateSettingsHandler });
|
AppLogic::Current()->SettingsChanged({ get_weak(), &TerminalWindow::UpdateSettingsHandler });
|
||||||
@ -478,7 +479,7 @@ namespace winrt::TerminalApp::implementation
|
|||||||
// validating the settings.
|
// validating the settings.
|
||||||
// - Only one dialog can be visible at a time. If another dialog is visible
|
// - Only one dialog can be visible at a time. If another dialog is visible
|
||||||
// when this is called, nothing happens. See ShowDialog for details
|
// 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 title = RS_(L"SettingsValidateErrorTitle");
|
||||||
auto buttonText = RS_(L"Ok");
|
auto buttonText = RS_(L"Ok");
|
||||||
@ -539,7 +540,7 @@ namespace winrt::TerminalApp::implementation
|
|||||||
}
|
}
|
||||||
else if (settingsLoadedResult == S_FALSE)
|
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)
|
else if (args.Result() == S_FALSE)
|
||||||
{
|
{
|
||||||
_ShowLoadWarningsDialog(args.Warnings());
|
_ShowLoadWarningsDialog(nullptr, args.Warnings());
|
||||||
}
|
}
|
||||||
else if (args.Result() == S_OK)
|
else if (args.Result() == S_OK)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -189,7 +189,7 @@ namespace winrt::TerminalApp::implementation
|
|||||||
const winrt::hstring& contentKey,
|
const winrt::hstring& contentKey,
|
||||||
HRESULT settingsLoadedResult,
|
HRESULT settingsLoadedResult,
|
||||||
const winrt::hstring& exceptionText);
|
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();
|
bool _IsKeyboardServiceEnabled();
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ namespace TerminalApp
|
|||||||
{
|
{
|
||||||
Boolean Reload { get; };
|
Boolean Reload { get; };
|
||||||
UInt64 Result { get; };
|
UInt64 Result { get; };
|
||||||
IVector<Microsoft.Terminal.Settings.Model.SettingsLoadWarnings> Warnings { get; };
|
IVectorView<Microsoft.Terminal.Settings.Model.SettingsLoadWarnings> Warnings { get; };
|
||||||
String ExceptionText { get; };
|
String ExceptionText { get; };
|
||||||
|
|
||||||
Microsoft.Terminal.Settings.Model.CascadiaSettings NewSettings { 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*/)
|
void MainPage::SaveButton_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
|
||||||
{
|
{
|
||||||
_settingsClone.LogSettingChanges(false);
|
_settingsClone.LogSettingChanges(false);
|
||||||
_settingsClone.WriteSettingsToDisk();
|
if (!_settingsClone.WriteSettingsToDisk())
|
||||||
|
{
|
||||||
|
ShowLoadWarningsDialog.raise(*this, _settingsClone.Warnings());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainPage::ResetButton_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
|
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; }
|
Editor::ExtensionsViewModel ExtensionsVM() const noexcept { return _extensionsVM; }
|
||||||
|
|
||||||
til::typed_event<Windows::Foundation::IInspectable, Model::SettingsTarget> OpenJson;
|
til::typed_event<Windows::Foundation::IInspectable, Model::SettingsTarget> OpenJson;
|
||||||
|
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::Collections::IVectorView<Model::SettingsLoadWarnings>> ShowLoadWarningsDialog;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Windows::Foundation::Collections::IObservableVector<IInspectable> _breadcrumbs;
|
Windows::Foundation::Collections::IObservableVector<IInspectable> _breadcrumbs;
|
||||||
|
|||||||
@ -39,6 +39,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
|||||||
|
|
||||||
void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
||||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.SettingsTarget> OpenJson;
|
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.
|
// Due to the aforementioned bug, we can't use IInitializeWithWindow _here_ either.
|
||||||
// Let's just smuggle the HWND in as a UInt64 :|
|
// 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();
|
winrt::Windows::Foundation::Collections::IVectorView<Model::ExtensionPackage> Extensions();
|
||||||
void ResetApplicationState() const;
|
void ResetApplicationState() const;
|
||||||
void ResetToDefaultSettings();
|
void ResetToDefaultSettings();
|
||||||
void WriteSettingsToDisk();
|
bool WriteSettingsToDisk();
|
||||||
Json::Value ToJson() const;
|
Json::Value ToJson() const;
|
||||||
Model::Profile ProfileDefaults() const;
|
Model::Profile ProfileDefaults() const;
|
||||||
Model::Profile CreateNewProfile();
|
Model::Profile CreateNewProfile();
|
||||||
|
|||||||
@ -31,7 +31,7 @@ namespace Microsoft.Terminal.Settings.Model
|
|||||||
CascadiaSettings Copy();
|
CascadiaSettings Copy();
|
||||||
void ResetApplicationState();
|
void ResetApplicationState();
|
||||||
void ResetToDefaultSettings();
|
void ResetToDefaultSettings();
|
||||||
void WriteSettingsToDisk();
|
Boolean WriteSettingsToDisk();
|
||||||
void LogSettingChanges(Boolean isJsonLoad);
|
void LogSettingChanges(Boolean isJsonLoad);
|
||||||
|
|
||||||
String Hash { get; };
|
String Hash { get; };
|
||||||
|
|||||||
@ -1226,15 +1226,7 @@ try
|
|||||||
// settings string back to the file.
|
// settings string back to the file.
|
||||||
if (mustWriteToDisk)
|
if (mustWriteToDisk)
|
||||||
{
|
{
|
||||||
try
|
settings->WriteSettingsToDisk();
|
||||||
{
|
|
||||||
settings->WriteSettingsToDisk();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
LOG_CAUGHT_EXCEPTION();
|
|
||||||
settings->_warnings.Append(SettingsLoadWarnings::FailedToWriteToSettings);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1539,7 +1531,7 @@ void CascadiaSettings::ResetToDefaultSettings()
|
|||||||
// - <none>
|
// - <none>
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - <none>
|
// - <none>
|
||||||
void CascadiaSettings::WriteSettingsToDisk()
|
bool CascadiaSettings::WriteSettingsToDisk()
|
||||||
{
|
{
|
||||||
// write current settings to current settings file
|
// write current settings to current settings file
|
||||||
Json::StreamWriterBuilder wbuilder;
|
Json::StreamWriterBuilder wbuilder;
|
||||||
@ -1547,7 +1539,17 @@ void CascadiaSettings::WriteSettingsToDisk()
|
|||||||
wbuilder.settings_["indentation"] = " ";
|
wbuilder.settings_["indentation"] = " ";
|
||||||
wbuilder.settings_["precision"] = 6; // prevent values like 1.1000000000000001
|
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)
|
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.
|
// renaming one is (supposed to be) atomic.
|
||||||
// Wait... "supposed to be"!? Well it's technically not always atomic,
|
// Wait... "supposed to be"!? Well it's technically not always atomic,
|
||||||
// but it's pretty darn close to it, so... better than nothing.
|
// 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
|
} // io
|
||||||
} // til
|
} // til
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user