Add "portable mode", where settings load from the install path (#15051)

This pull request implements portable mode for Windows Terminal, which
will make side by side deployment of different versions generally more
feasible.

Portable mode was specified in #15032.

There are three broad categories of changes in this PR:

1. Changes to settings loading.
2. A new indicator in the settings UI plus a link to the portable mode
   docs.
3. A new application display name, `Terminal (Portable)`, which users
   will hopefully include in their bug reports.

It's surprisingly small for how big a deal it is!

Related to #15034
Closes #1386
This commit is contained in:
Dustin L. Howett
2023-03-31 17:12:00 -05:00
committed by GitHub
parent bbd4d1b1e4
commit e6a3fa8d4e
10 changed files with 72 additions and 10 deletions

View File

@@ -1946,6 +1946,7 @@ trx
tsattrs
tsf
tsgr
tsm
TStr
TSTRFORMAT
TSub

View File

@@ -9,6 +9,7 @@
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:tsm="using:Microsoft.Terminal.Settings.Model"
mc:Ignorable="d">
<Page.Resources>
@@ -189,13 +190,29 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock x:Uid="Settings_UnsavedSettingsWarning"
Margin="30,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Foreground="Goldenrod"
TextAlignment="Left"
Visibility="Collapsed" />
<StackPanel Margin="30,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Orientation="Vertical">
<TextBlock x:Uid="Settings_UnsavedSettingsWarning"
Foreground="Goldenrod"
TextAlignment="Left"
Visibility="Collapsed" />
<StackPanel VerticalAlignment="Center"
Orientation="Horizontal"
Spacing="4"
Visibility="{x:Bind tsm:CascadiaSettings.IsPortableMode, Mode=OneTime}">
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}"
Foreground="SlateBlue"
Glyph="&#xE946;" />
<TextBlock Foreground="SlateBlue">
<Run x:Uid="Settings_PortableModeNote" />
<Hyperlink x:Uid="Settings_PortableModeInfoLink">
<Run x:Uid="Settings_PortableModeInfoLinkTextRun" />
</Hyperlink>
</TextBlock>
</StackPanel>
</StackPanel>
<StackPanel Margin="0,0,30,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"

View File

@@ -1585,4 +1585,16 @@
<value>Warn when closing more than one tab</value>
<comment>Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open.</comment>
</data>
</root>
<data name="Settings_PortableModeNote.Text" xml:space="preserve">
<value>Windows Terminal is running in portable mode.</value>
<comment>A disclaimer that indicates that Terminal is running in a mode that saves settings to a different folder.</comment>
</data>
<data name="Settings_PortableModeInfoLink.NavigateUri" xml:space="preserve">
<value>https://go.microsoft.com/fwlink/?linkid=2229086</value>
<comment>{Locked}</comment>
</data>
<data name="Settings_PortableModeInfoLinkTextRun.Text" xml:space="preserve">
<value>Learn more.</value>
<comment>A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information.</comment>
</data>
</root>

View File

@@ -1006,7 +1006,7 @@ winrt::hstring CascadiaSettings::ApplicationDisplayName()
}
CATCH_LOG();
return RS_(L"ApplicationDisplayNameUnpackaged");
return IsPortableMode() ? RS_(L"ApplicationDisplayNamePortable") : RS_(L"ApplicationDisplayNameUnpackaged");
}
winrt::hstring CascadiaSettings::ApplicationVersion()

View File

@@ -105,6 +105,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static winrt::hstring DefaultSettingsPath();
static winrt::hstring ApplicationDisplayName();
static winrt::hstring ApplicationVersion();
static bool IsPortableMode();
static void ExportFile(winrt::hstring path, winrt::hstring content);
CascadiaSettings() noexcept = default;

View File

@@ -15,6 +15,7 @@ namespace Microsoft.Terminal.Settings.Model
static String SettingsPath { get; };
static String DefaultSettingsPath { get; };
static Boolean IsPortableMode { get; };
static String ApplicationDisplayName { get; };
static String ApplicationVersion { get; };

View File

@@ -822,7 +822,7 @@ try
// read settings.json from the Release stable file path if it exists.
// Otherwise use default settings file provided from original settings file
bool releaseSettingExists = false;
if (firstTimeSetup)
if (firstTimeSetup && !IsPortableMode())
{
#if defined(WT_BRANDING_PREVIEW)
{
@@ -1162,6 +1162,11 @@ winrt::hstring CascadiaSettings::SettingsPath()
return winrt::hstring{ _settingsPath().native() };
}
bool CascadiaSettings::IsPortableMode()
{
return Model::IsPortableMode();
}
winrt::hstring CascadiaSettings::DefaultSettingsPath()
{
// Both of these posts suggest getting the path to the exe, then removing

View File

@@ -15,14 +15,34 @@
static constexpr std::string_view Utf8Bom{ "\xEF\xBB\xBF", 3 };
static constexpr std::wstring_view UnpackagedSettingsFolderName{ L"Microsoft\\Windows Terminal\\" };
static constexpr std::wstring_view ReleaseSettingsFolder{ L"Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\" };
static constexpr std::wstring_view PortableModeMarkerFile{ L".portable" };
static constexpr std::wstring_view PortableModeSettingsFolder{ L"settings" };
namespace winrt::Microsoft::Terminal::Settings::Model
{
// Returns a path like C:\Users\<username>\AppData\Local\Packages\<packagename>\LocalState
// You can put your settings.json or state.json in this directory.
bool IsPortableMode()
{
static auto portableMode = []() {
std::filesystem::path modulePath{ wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()) };
modulePath.replace_filename(PortableModeMarkerFile);
return std::filesystem::exists(modulePath);
}();
return portableMode;
}
std::filesystem::path GetBaseSettingsPath()
{
static auto baseSettingsPath = []() {
if (!IsPackaged() && IsPortableMode())
{
std::filesystem::path modulePath{ wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()) };
modulePath.replace_filename(PortableModeSettingsFolder);
std::filesystem::create_directories(modulePath);
return modulePath;
}
wil::unique_cotaskmem_string localAppDataFolder;
// KF_FLAG_FORCE_APP_DATA_REDIRECTION, when engaged, causes SHGet... to return
// the new AppModel paths (Packages/xxx/RoamingState, etc.) for standard path requests.

View File

@@ -3,6 +3,7 @@
namespace winrt::Microsoft::Terminal::Settings::Model
{
bool IsPortableMode();
std::filesystem::path GetBaseSettingsPath();
std::filesystem::path GetReleaseSettingsPath();
std::string ReadUTF8File(const std::filesystem::path& path, const bool elevatedOnly = false, FILETIME* lastWriteTime = nullptr);

View File

@@ -126,6 +126,10 @@
<data name="SplitPaneParentCommandName" xml:space="preserve">
<value>Split Pane...</value>
</data>
<data name="ApplicationDisplayNamePortable" xml:space="preserve">
<value>Terminal (Portable)</value>
<comment>This display name is used when the Terminal application is running in a "portable" mode, where settings are not stored in a shared location.</comment>
</data>
<data name="ApplicationDisplayNameUnpackaged" xml:space="preserve">
<value>Terminal (Unpackaged)</value>
<comment>This display name is used when the application's name cannot be determined</comment>