Compare commits

...

8 Commits

Author SHA1 Message Date
Mike Griese
f75166e87a chef kiss 2026-03-04 13:03:12 -06:00
Mike Griese
9e0ff8835f save when the workspace closes 2026-03-04 12:27:33 -06:00
Mike Griese
fd5ddfb4c0 some menu touch up 2026-03-04 08:29:11 -06:00
Mike Griese
d2fcfdc6b3 oh my fucking god 2026-03-03 16:51:34 -06:00
Mike Griese
34b3029fb1 tests 2026-03-03 12:48:50 -06:00
Mike Griese
45ddf94067 is that really everything? 2026-03-03 11:04:15 -06:00
Mike Griese
abb06e1918 todos 2026-03-03 06:10:34 -06:00
Mike Griese
eb2899e267 yea i guess 2026-03-02 14:23:16 -06:00
72 changed files with 2490 additions and 686 deletions

View File

@@ -1421,7 +1421,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"profile0", terminalArgs.Profile());
VERIFY_IS_NULL(terminalArgs.Elevate());
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs);
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, settings.WindowSettingsDefaults());
const auto termSettings = termSettingsResult.DefaultSettings();
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline());
VERIFY_ARE_EQUAL(false, termSettings->Elevate());
@@ -1444,7 +1444,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile());
VERIFY_IS_NULL(terminalArgs.Elevate());
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs);
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, settings.WindowSettingsDefaults());
const auto termSettings = termSettingsResult.DefaultSettings();
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings->Commandline());
VERIFY_ARE_EQUAL(true, termSettings->Elevate());
@@ -1467,7 +1467,7 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile());
VERIFY_IS_NULL(terminalArgs.Elevate());
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs);
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, settings.WindowSettingsDefaults());
const auto termSettings = termSettingsResult.DefaultSettings();
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline());
VERIFY_ARE_EQUAL(false, termSettings->Elevate());
@@ -1492,7 +1492,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(terminalArgs.Elevate());
VERIFY_IS_FALSE(terminalArgs.Elevate().Value());
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs);
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, settings.WindowSettingsDefaults());
const auto termSettings = termSettingsResult.DefaultSettings();
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline());
VERIFY_ARE_EQUAL(false, termSettings->Elevate());
@@ -1516,7 +1516,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(terminalArgs.Elevate());
VERIFY_IS_FALSE(terminalArgs.Elevate().Value());
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs);
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, settings.WindowSettingsDefaults());
const auto termSettings = termSettingsResult.DefaultSettings();
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings->Commandline());
VERIFY_ARE_EQUAL(false, termSettings->Elevate());
@@ -1540,7 +1540,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(terminalArgs.Elevate());
VERIFY_IS_FALSE(terminalArgs.Elevate().Value());
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs);
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, settings.WindowSettingsDefaults());
const auto termSettings = termSettingsResult.DefaultSettings();
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline());
VERIFY_ARE_EQUAL(false, termSettings->Elevate());
@@ -1565,7 +1565,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(terminalArgs.Elevate());
VERIFY_IS_TRUE(terminalArgs.Elevate().Value());
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs);
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, settings.WindowSettingsDefaults());
const auto termSettings = termSettingsResult.DefaultSettings();
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline());
VERIFY_ARE_EQUAL(true, termSettings->Elevate());
@@ -1588,7 +1588,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(terminalArgs.Elevate());
VERIFY_IS_TRUE(terminalArgs.Elevate().Value());
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs);
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, settings.WindowSettingsDefaults());
const auto termSettings = termSettingsResult.DefaultSettings();
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings->Commandline());
VERIFY_ARE_EQUAL(true, termSettings->Elevate());
@@ -1612,7 +1612,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(terminalArgs.Elevate());
VERIFY_IS_TRUE(terminalArgs.Elevate().Value());
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs);
const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, terminalArgs, settings.WindowSettingsDefaults());
const auto termSettings = termSettingsResult.DefaultSettings();
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline());
VERIFY_ARE_EQUAL(true, termSettings->Elevate());

View File

@@ -1099,7 +1099,7 @@ namespace TerminalAppLocalTests
Log::Comment(L"Change the tab switch order to MRU switching");
TestOnUIThread([&page]() {
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
page->_currentWindowSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
});
Log::Comment(L"Switch to the next MRU tab, which is the fourth tab");
@@ -1145,7 +1145,7 @@ namespace TerminalAppLocalTests
});
Log::Comment(L"Change the tab switch order to in-order switching");
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::InOrder);
page->_currentWindowSettings().TabSwitcherMode(TabSwitcherMode::InOrder);
Log::Comment(L"Switch to the next in-order tab, which is the third tab");
TestOnUIThread([&page]() {
@@ -1157,7 +1157,7 @@ namespace TerminalAppLocalTests
});
Log::Comment(L"Change the tab switch order to not use the tab switcher (which is in-order always)");
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::Disabled);
page->_currentWindowSettings().TabSwitcherMode(TabSwitcherMode::Disabled);
Log::Comment(L"Switch to the next in-order tab, which is the fourth tab");
TestOnUIThread([&page]() {
@@ -1219,7 +1219,7 @@ namespace TerminalAppLocalTests
Log::Comment(L"Change the tab switch order to MRU switching");
TestOnUIThread([&page]() {
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
page->_currentWindowSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
});
Log::Comment(L"Select the tabs from 0 to 3");

View File

@@ -550,7 +550,7 @@ namespace winrt::TerminalApp::implementation
if (const auto& realArgs = args.ActionArgs().try_as<CopyTextArgs>())
{
const auto copyFormatting = realArgs.CopyFormatting();
const auto format = copyFormatting ? copyFormatting.Value() : _settings.GlobalSettings().CopyFormatting();
const auto format = copyFormatting ? copyFormatting.Value() : _currentWindowSettings().CopyFormatting();
const auto handled = _CopyText(realArgs.DismissSelection(), realArgs.SingleLine(), realArgs.WithControlSequences(), format);
args.Handled(handled);
}
@@ -938,6 +938,27 @@ namespace winrt::TerminalApp::implementation
co_return;
}
// Launch `wt -w <name>` so the monarch can either summon an existing
// window with that name or restore a persisted workspace.
safe_void_coroutine TerminalPage::_OpenWorkspaceWindow(const winrt::hstring name)
{
co_await winrt::resume_background();
const auto exePath{ GetWtExePath() };
const auto cmdline = fmt::format(FMT_COMPILE(L"-w {}"), std::wstring_view{ name });
SHELLEXECUTEINFOW seInfo{ 0 };
seInfo.cbSize = sizeof(seInfo);
seInfo.fMask = SEE_MASK_NOASYNC;
seInfo.lpVerb = L"open";
seInfo.lpFile = exePath.c_str();
seInfo.lpParameters = cmdline.c_str();
seInfo.nShow = SW_SHOWNORMAL;
LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo));
co_return;
}
void TerminalPage::_HandleNewWindow(const IInspectable& /*sender*/,
const ActionEventArgs& actionArgs)
{
@@ -961,7 +982,7 @@ namespace winrt::TerminalApp::implementation
if (const auto& terminalArgs{ newContentArgs.try_as<NewTerminalArgs>() })
{
const auto profile{ _settings.GetProfileForArgs(terminalArgs) };
const auto profile{ _settings.GetProfileForArgs(terminalArgs, _currentWindowSettings()) };
terminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
}
@@ -1118,7 +1139,7 @@ namespace winrt::TerminalApp::implementation
// use global default if query URL is unspecified
if (queryUrl.empty())
{
queryUrl = std::wstring_view{ _settings.GlobalSettings().SearchWebDefaultQueryUrl() };
queryUrl = std::wstring_view{ _currentWindowSettings().SearchWebDefaultQueryUrl() };
}
constexpr std::wstring_view queryToken{ L"%s" };
@@ -1633,4 +1654,68 @@ namespace winrt::TerminalApp::implementation
args.Handled(handled);
}
}
void TerminalPage::_HandleOpenWorkspace(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
// Open (or summon) a named window. We launch a new `wt -w <name>`
// process which the monarch will route to the correct live window or
// restore from a persisted workspace.
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<OpenWorkspaceArgs>())
{
const auto name = realArgs.Name();
if (!name.empty())
{
_OpenWorkspaceWindow(name);
}
args.Handled(true);
}
}
}
void TerminalPage::_HandleSaveWorkspace(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<SaveWorkspaceArgs>())
{
// If a name is supplied, use it; otherwise fall back to the
// current window name.
auto name = realArgs.Name();
if (name.empty())
{
name = _WindowProperties.WindowName();
}
if (!name.empty())
{
if (const auto layout = GetWindowLayout())
{
ApplicationState::SharedInstance().SaveWorkspace(name, layout);
}
}
args.Handled(true);
}
}
}
void TerminalPage::_HandleDeleteWorkspace(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<DeleteWorkspaceArgs>())
{
const auto name = realArgs.Name();
if (!name.empty())
{
ApplicationState::SharedInstance().RemoveWorkspace(name);
}
args.Handled(true);
}
}
}
}

View File

@@ -194,7 +194,7 @@ namespace winrt::TerminalApp::implementation
g_hTerminalAppProvider,
"AppCreated",
TraceLoggingDescription("Event emitted when the application is started"),
TraceLoggingBool(_settings.GlobalSettings().ShowTabsInTitlebar(), "TabsInTitlebar"),
TraceLoggingBool(_settings.WindowSettingsDefaults().ShowTabsInTitlebar(), "TabsInTitlebar"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
@@ -229,12 +229,12 @@ namespace winrt::TerminalApp::implementation
}
_hasSettingsStartupActions = false;
const auto startupActions = newSettings.GlobalSettings().StartupActions();
const auto startupActions = newSettings.WindowSettingsDefaults().StartupActions();
if (!startupActions.empty())
{
_settingsAppArgs.FullResetState();
ExecuteCommandlineArgs args{ newSettings.GlobalSettings().StartupActions() };
ExecuteCommandlineArgs args{ newSettings.WindowSettingsDefaults().StartupActions() };
auto result = _settingsAppArgs.ParseArgs(args);
if (result == 0)
{

View File

@@ -85,6 +85,7 @@ namespace winrt::TerminalApp::implementation
WINRT_PROPERTY(TerminalApp::CommandlineArgs, Command, nullptr);
WINRT_PROPERTY(winrt::hstring, Content);
WINRT_PROPERTY(Windows::Foundation::IReference<Windows::Foundation::Rect>, InitialBounds);
WINRT_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::WindowLayout, PersistedLayout, nullptr);
};
}

View File

@@ -51,5 +51,6 @@ namespace TerminalApp
CommandlineArgs Command { get; };
String Content { get; };
Windows.Foundation.IReference<Windows.Foundation.Rect> InitialBounds { get; };
Microsoft.Terminal.Settings.Model.WindowLayout PersistedLayout;
};
}

View File

@@ -19,7 +19,7 @@ namespace winrt::TerminalApp::implementation
// Stash away the current requested theme of the app. We'll need that in
// _BackgroundBrush() to do a theme-aware resource lookup
_requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
_requestedTheme = settings.GlobalSettings().CurrentTheme(settings.WindowSettingsDefaults()).RequestedTheme();
}
void SettingsPaneContent::UpdateSettings(const CascadiaSettings& settings)
@@ -27,7 +27,7 @@ namespace winrt::TerminalApp::implementation
ASSERT_UI_THREAD();
_sui.UpdateSettings(settings);
_requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
_requestedTheme = settings.GlobalSettings().CurrentTheme(settings.WindowSettingsDefaults()).RequestedTheme();
}
winrt::Windows::UI::Xaml::FrameworkElement SettingsPaneContent::GetRoot()

View File

@@ -121,7 +121,7 @@ namespace winrt::TerminalApp::implementation
{
static const auto key = winrt::box_value(L"SettingsUiTabBrush");
return ThemeLookup(WUX::Application::Current().Resources(),
_settings.GlobalSettings().CurrentTheme().RequestedTheme(),
_settings.GlobalSettings().CurrentTheme(_settings.WindowSettingsDefaults()).RequestedTheme(),
key)
.try_as<winrt::WUX::Media::Brush>();
}

View File

@@ -173,7 +173,7 @@ namespace winrt::TerminalApp::implementation
// Make sure to try/catch this, because the LocalTests won't be
// able to use this helper.
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
if (settings.GlobalSettings().TabWidthMode() == winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::SizeToContent)
if (settings.WindowSettingsDefaults().TabWidthMode() == winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::SizeToContent)
{
_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthTitleLength);
}

View File

@@ -66,14 +66,14 @@ namespace winrt::TerminalApp::implementation
{
if (const auto& newTerminalArgs{ newContentArgs.try_as<NewTerminalArgs>() })
{
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs, _currentWindowSettings()) };
// GH#11114: GetProfileForArgs can return null if the index is higher
// than the number of available profiles.
if (!profile)
{
return S_FALSE;
}
const auto settings{ Settings::TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs) };
const auto settings{ Settings::TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, _currentWindowSettings()) };
// Try to handle auto-elevation
if (_maybeElevate(newTerminalArgs, settings, profile))
@@ -105,7 +105,7 @@ namespace winrt::TerminalApp::implementation
if (insertPosition == -1)
{
insertPosition = _tabs.Size();
if (_settings.GlobalSettings().NewTabPosition() == NewTabPosition::AfterCurrentTab)
if (_currentWindowSettings().NewTabPosition() == NewTabPosition::AfterCurrentTab)
{
auto currentTabIndex = _GetFocusedTabIndex();
if (currentTabIndex.has_value())
@@ -220,7 +220,7 @@ namespace winrt::TerminalApp::implementation
if (const auto content{ tab.GetActiveContent() })
{
const auto& icon{ content.Icon() };
const auto theme = _settings.GlobalSettings().CurrentTheme();
const auto theme = _settings.GlobalSettings().CurrentTheme(_currentWindowSettings());
const auto iconStyle = (theme && theme.Tab()) ? theme.Tab().IconStyle() : IconStyle::Default;
tab.UpdateIcon(icon, iconStyle);
@@ -231,7 +231,7 @@ namespace winrt::TerminalApp::implementation
// - Handle changes to the tab width set by the user
void TerminalPage::_UpdateTabWidthMode()
{
_tabView.TabWidthMode(_settings.GlobalSettings().TabWidthMode());
_tabView.TabWidthMode(_currentWindowSettings().TabWidthMode());
}
// Method Description:
@@ -244,9 +244,9 @@ namespace winrt::TerminalApp::implementation
// - there is more than one tab, or the user has chosen to always show tabs
const auto isVisible = !_isInFocusMode &&
(!_isFullscreen || _showTabsFullscreen) &&
(_settings.GlobalSettings().ShowTabsInTitlebar() ||
(_currentWindowSettings().ShowTabsInTitlebar() ||
(_tabs.Size() > 1) ||
_settings.GlobalSettings().AlwaysShowTabs());
_currentWindowSettings().AlwaysShowTabs());
if (_tabView)
{
@@ -286,7 +286,7 @@ namespace winrt::TerminalApp::implementation
// current control's live settings (which will include changes
// made through VT).
uint32_t insertPosition = _tabs.Size();
if (_settings.GlobalSettings().NewTabPosition() == NewTabPosition::AfterCurrentTab)
if (_currentWindowSettings().NewTabPosition() == NewTabPosition::AfterCurrentTab)
{
insertPosition = tab.TabViewIndex() + 1;
}
@@ -438,6 +438,21 @@ namespace winrt::TerminalApp::implementation
const auto focusedTabIndex{ _GetFocusedTabIndex() };
// If this is the last tab in a named window, persist the workspace
// layout now—before the tab is shut down—so GetWindowLayout() can
// still see its tab data.
if (_tabs.Size() == 1)
{
const auto& windowName = _WindowProperties.WindowName();
if (!windowName.empty())
{
if (const auto layout = GetWindowLayout())
{
ApplicationState::SharedInstance().SaveWorkspace(windowName, layout);
}
}
}
// Removing the tab from the collection should destroy its control and disconnect its connection,
// but it doesn't always do so. The UI tree may still be holding the control and preventing its destruction.
tab.Shutdown();
@@ -477,7 +492,7 @@ namespace winrt::TerminalApp::implementation
// 1. We want to customize this behavior (e.g., use MRU logic)
// 2. In fullscreen (GH#5799) and focus (GH#7916) modes the _OnTabItemsChanged is not fired
// 3. When rearranging tabs (GH#7916) _OnTabItemsChanged is suppressed
const auto tabSwitchMode = _settings.GlobalSettings().TabSwitcherMode();
const auto tabSwitchMode = _currentWindowSettings().TabSwitcherMode();
if (tabSwitchMode == TabSwitcherMode::MostRecentlyUsed)
{
@@ -528,7 +543,7 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_SelectNextTab(const bool bMoveRight, const Windows::Foundation::IReference<Microsoft::Terminal::Settings::Model::TabSwitcherMode>& customTabSwitcherMode)
{
const auto index{ _GetFocusedTabIndex().value_or(0) };
const auto tabSwitchMode = customTabSwitcherMode ? customTabSwitcherMode.Value() : _settings.GlobalSettings().TabSwitcherMode();
const auto tabSwitchMode = customTabSwitcherMode ? customTabSwitcherMode.Value() : _currentWindowSettings().TabSwitcherMode();
if (tabSwitchMode == TabSwitcherMode::Disabled)
{
auto tabCount = _tabs.Size();
@@ -1017,7 +1032,7 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_UpdateBackground(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile)
{
if (profile && _settings.GlobalSettings().UseBackgroundImageForWindow())
if (profile && _currentWindowSettings().UseBackgroundImageForWindow())
{
_SetBackgroundImage(profile.DefaultAppearance());
}

View File

@@ -25,6 +25,21 @@ namespace winrt::TerminalApp::implementation
InitializeComponent();
}
void TabRowControl::WorkspaceName(const winrt::hstring& value)
{
if (_WorkspaceName != value)
{
_WorkspaceName = value;
PropertyChanged.raise(*this, WUX::Data::PropertyChangedEventArgs{ L"WorkspaceName" });
// Collapse the name text when empty so the button shows only the icon.
if (const auto textBlock = WorkspaceNameText())
{
textBlock.Visibility(value.empty() ? WUX::Visibility::Collapsed : WUX::Visibility::Visible);
}
}
}
// Method Description:
// - Bound in the Xaml editor to the [+] button.
// Arguments:

View File

@@ -19,6 +19,14 @@ namespace winrt::TerminalApp::implementation
til::property_changed_event PropertyChanged;
WINRT_OBSERVABLE_PROPERTY(bool, ShowElevationShield, PropertyChanged.raise, false);
WINRT_OBSERVABLE_PROPERTY(bool, ShowWindowsButton, PropertyChanged.raise, true);
public:
winrt::hstring WorkspaceName() const noexcept { return _WorkspaceName; }
void WorkspaceName(const winrt::hstring& value);
private:
winrt::hstring _WorkspaceName{};
};
}

View File

@@ -9,5 +9,7 @@ namespace TerminalApp
TabRowControl();
Microsoft.UI.Xaml.Controls.TabView TabView { get; };
Boolean ShowElevationShield;
Boolean ShowWindowsButton;
String WorkspaceName;
}
}

View File

@@ -35,14 +35,43 @@
TabWidthMode="Equal">
<mux:TabView.TabStripHeader>
<!-- EA18 is the "Shield" glyph -->
<FontIcon x:Uid="ElevationShield"
Margin="9,4,0,4"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="16"
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}"
Glyph="&#xEA18;"
Visibility="{x:Bind ShowElevationShield, Mode=OneWay}" />
<StackPanel Orientation="Horizontal">
<!-- EA18 is the "Shield" glyph -->
<FontIcon x:Uid="ElevationShield"
Margin="9,4,0,4"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="16"
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}"
Glyph="&#xEA18;"
Visibility="{x:Bind ShowElevationShield, Mode=OneWay}" />
<!-- Workspace/windows button -->
<Button x:Name="WorkspaceDropdown"
Margin="4,4,0,4"
Padding="8,2,4,2"
VerticalAlignment="Center"
BorderThickness="0"
Background="Transparent"
Visibility="{x:Bind ShowWindowsButton, Mode=OneWay}">
<Button.Content>
<StackPanel Orientation="Horizontal"
Spacing="4">
<!-- EE40 is the "TaskViewSettings" glyph -->
<FontIcon FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Glyph="&#xEE40;" />
<TextBlock x:Name="WorkspaceNameText"
VerticalAlignment="Center"
FontSize="12"
Visibility="Collapsed"
Text="{x:Bind WorkspaceName, Mode=OneWay}" />
</StackPanel>
</Button.Content>
<Button.Flyout>
<MenuFlyout x:Name="WorkspaceFlyout" />
</Button.Flyout>
</Button>
</StackPanel>
</mux:TabView.TabStripHeader>
<mux:TabView.TabStripFooter>

View File

@@ -25,6 +25,7 @@
#include "TerminalSettingsCache.h"
#include "LaunchPositionRequest.g.cpp"
#include "WindowListRequest.g.cpp"
#include "RenameWindowRequestedArgs.g.cpp"
#include "RequestMoveContentArgs.g.cpp"
#include "TerminalPage.g.cpp"
@@ -226,6 +227,11 @@ namespace winrt::TerminalApp::implementation
_WindowProperties.PropertyChanged({ get_weak(), &TerminalPage::_windowPropertyChanged });
}
winrt::Microsoft::Terminal::Settings::Model::WindowSettings TerminalPage::_currentWindowSettings() const
{
return _settings.WindowSettings(_WindowProperties.WindowName());
}
// Method Description:
// - implements the IInitializeWithWindow interface from shobjidl_core.
// - We're going to use this HWND as the owner for the ConPTY windows, via
@@ -268,12 +274,19 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI)
{
assert(Dispatcher().HasThreadAccess());
if (_settings == nullptr)
// IMPORTANT: we must assign _settings FIRST, because many downstream
// helpers (e.g. _currentWindowSettings()) dereference it. On the very
// first call _settings is nullptr, so any access before this line would
// crash.
const auto firstLoad = _settings == nullptr;
_settings = settings;
if (firstLoad)
{
// Create this only on the first time we load the settings.
_terminalSettingsCache = std::make_shared<TerminalSettingsCache>(settings);
_terminalSettingsCache = std::make_shared<TerminalSettingsCache>(settings, _currentWindowSettings());
}
_settings = settings;
// Make sure to call SetCommands before _RefreshUIForSettingsReload.
// SetCommands will make sure the KeyChordText of Commands is updated, which needs
@@ -334,8 +347,22 @@ namespace winrt::TerminalApp::implementation
auto tabRowImpl = winrt::get_self<implementation::TabRowControl>(_tabRow);
_newTabButton = tabRowImpl->NewTabButton();
_workspaceFlyout = tabRowImpl->WorkspaceFlyout();
if (_settings.GlobalSettings().ShowTabsInTitlebar())
// Set the initial workspace name from the window name.
// Use raw WindowName() so unnamed windows show no text.
_tabRow.WorkspaceName(_WindowProperties.WindowName());
// Rebuild the workspace flyout each time it opens so it always
// reflects the latest set of persisted workspaces.
_workspaceFlyout.Opening([weakThis{ get_weak() }](auto&&, auto&&) {
if (auto page{ weakThis.get() })
{
page->_PopulateWorkspaceFlyout();
}
});
if (_currentWindowSettings().ShowTabsInTitlebar())
{
// Remove the TabView from the page. We'll hang on to it, we need to
// put it in the titlebar.
@@ -370,7 +397,7 @@ namespace winrt::TerminalApp::implementation
// Initialize the state of the CloseButtonOverlayMode property of
// our TabView, to match the tab.showCloseButton property in the theme.
if (const auto theme = _settings.GlobalSettings().CurrentTheme())
if (const auto theme = _settings.GlobalSettings().CurrentTheme(_currentWindowSettings()))
{
const auto visibility = theme.Tab() ? theme.Tab().ShowCloseButton() : Settings::Model::TabCloseButtonVisibility::Always;
@@ -425,7 +452,7 @@ namespace winrt::TerminalApp::implementation
// Settings AllowDependentAnimations will affect whether animations are
// enabled application-wide, so we don't need to check it each time we
// want to create an animation.
WUX::Media::Animation::Timeline::AllowDependentAnimations(!_settings.GlobalSettings().DisableAnimations());
WUX::Media::Animation::Timeline::AllowDependentAnimations(!_currentWindowSettings().DisableAnimations());
// Once the page is actually laid out on the screen, trigger all our
// startup actions. Things like Panes need to know at least how big the
@@ -434,14 +461,20 @@ namespace winrt::TerminalApp::implementation
// _OnFirstLayout will remove this handler so it doesn't get called more than once.
_layoutUpdatedRevoker = _tabContent.LayoutUpdated(winrt::auto_revoke, { this, &TerminalPage::_OnFirstLayout });
_isAlwaysOnTop = _settings.GlobalSettings().AlwaysOnTop();
_showTabsFullscreen = _settings.GlobalSettings().ShowTabsFullscreen();
_isAlwaysOnTop = _currentWindowSettings().AlwaysOnTop();
_showTabsFullscreen = _currentWindowSettings().ShowTabsFullscreen();
// DON'T set up Toasts/TeachingTips here. They should be loaded and
// initialized the first time they're opened, in whatever method opens
// them.
_tabRow.ShowElevationShield(IsRunningElevated() && _settings.GlobalSettings().ShowAdminShield());
_tabRow.ShowElevationShield(IsRunningElevated() && _currentWindowSettings().ShowAdminShield());
// Apply the ShowWindowsButton theme setting.
if (const auto theme = _settings.GlobalSettings().CurrentTheme(_currentWindowSettings()))
{
_tabRow.ShowWindowsButton(theme.Window() ? theme.Window().ShowWindowsButton() : true);
}
_adjustProcessPriorityThrottled = std::make_shared<ThrottledFunc<>>(
DispatcherQueue::GetForCurrentThread(),
@@ -520,7 +553,7 @@ namespace winrt::TerminalApp::implementation
// It's possible that newTerminalArgs is null here.
// GetProfileForArgs should be resilient to that.
const auto profile{ settings.GetProfileForArgs(newTerminalArgs) };
const auto profile{ settings.GetProfileForArgs(newTerminalArgs, settings.WindowSettingsDefaults()) };
if (profile.Elevate())
{
continue;
@@ -951,7 +984,7 @@ namespace winrt::TerminalApp::implementation
// Create profile entries from the NewTabMenu configuration using a
// recursive helper function. This returns a std::vector of FlyoutItemBases,
// that we then add to our Flyout.
auto entries = _settings.GlobalSettings().NewTabMenu();
auto entries = _currentWindowSettings().NewTabMenu();
auto items = _CreateNewTabFlyoutItems(entries);
for (const auto& item : items)
{
@@ -1233,7 +1266,7 @@ namespace winrt::TerminalApp::implementation
profileMenuItem.Icon(icon);
}
if (profile.Guid() == _settings.GlobalSettings().DefaultProfile())
if (profile.Guid() == _currentWindowSettings().DefaultProfile())
{
// Contrast the default profile with others in font weight.
profileMenuItem.FontWeight(FontWeights::Bold());
@@ -1399,7 +1432,7 @@ namespace winrt::TerminalApp::implementation
if (newTerminalArgs.ProfileIndex() != nullptr)
{
// We want to promote the index to a GUID because there is no "launch to profile index" command.
const auto profile = _settings.GetProfileForArgs(newTerminalArgs);
const auto profile = _settings.GetProfileForArgs(newTerminalArgs, _currentWindowSettings());
if (profile)
{
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
@@ -1470,7 +1503,7 @@ namespace winrt::TerminalApp::implementation
const bool inheritCursor)
{
static const auto textMeasurement = [&]() -> std::wstring_view {
switch (_settings.GlobalSettings().TextMeasurement())
switch (_currentWindowSettings().TextMeasurement())
{
case TextMeasurement::Graphemes:
return L"graphemes";
@@ -1589,7 +1622,7 @@ namespace winrt::TerminalApp::implementation
{
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
profile = GetClosestProfileForDuplicationOfProfile(profile);
controlSettings = Settings::TerminalSettings::CreateWithProfile(_settings, profile);
controlSettings = Settings::TerminalSettings::CreateWithProfile(_settings, profile, _currentWindowSettings());
// Replace the Starting directory with the CWD, if given
const auto workingDirectory = control.WorkingDirectory();
@@ -2226,14 +2259,14 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::PersistState()
WindowLayout TerminalPage::GetWindowLayout()
{
// This method may be called for a window even if it hasn't had a tab yet or lost all of them.
// We shouldn't persist such windows.
const auto tabCount = _tabs.Size();
if (_startupState != StartupState::Initialized || tabCount == 0)
{
return;
return nullptr;
}
std::vector<ActionAndArgs> actions;
@@ -2248,7 +2281,7 @@ namespace winrt::TerminalApp::implementation
// Avoid persisting a window with zero tabs, because `BuildStartupActions` happened to return an empty vector.
if (actions.empty())
{
return;
return nullptr;
}
// if the focused tab was not the last tab, restore that
@@ -2297,7 +2330,41 @@ namespace winrt::TerminalApp::implementation
RequestLaunchPosition.raise(*this, launchPosRequest);
layout.InitialPosition(launchPosRequest.Position());
ApplicationState::SharedInstance().AppendPersistedWindowLayout(layout);
return layout;
}
void TerminalPage::PersistState()
{
if (const auto layout = GetWindowLayout())
{
// For named windows, save the full state into the workspace collection
// and emit a lightweight openWorkspace action in the generic layout.
// This way, restoring generic layouts re-opens workspaces by name
// rather than duplicating their full tab state.
const auto& windowName = _WindowProperties.WindowName();
if (!windowName.empty())
{
// Persist the full layout into the workspace collection.
ApplicationState::SharedInstance().SaveWorkspace(windowName, layout);
// Build a minimal layout with just an openWorkspace action
// so the generic restore path re-opens this workspace by name.
std::vector<ActionAndArgs> actions;
ActionAndArgs action;
action.Action(ShortcutAction::OpenWorkspace);
OpenWorkspaceArgs args{ windowName };
action.Args(args);
actions.emplace_back(std::move(action));
WindowLayout stub;
stub.TabLayout(winrt::single_threaded_vector<ActionAndArgs>(std::move(actions)));
ApplicationState::SharedInstance().AppendPersistedWindowLayout(stub);
}
else
{
ApplicationState::SharedInstance().AppendPersistedWindowLayout(layout);
}
}
}
// Method Description:
@@ -2306,7 +2373,7 @@ namespace winrt::TerminalApp::implementation
safe_void_coroutine TerminalPage::CloseWindow()
{
if (_HasMultipleTabs() &&
_settings.GlobalSettings().ConfirmCloseAllTabs() &&
_currentWindowSettings().ConfirmCloseAllTabs() &&
!_displayingCloseDialog)
{
if (_newTabButton && _newTabButton.Flyout())
@@ -2831,7 +2898,7 @@ namespace winrt::TerminalApp::implementation
// - the title of the focused control if there is one, else "Terminal"
hstring TerminalPage::Title()
{
if (_settings.GlobalSettings().ShowTitleInTitlebar())
if (_currentWindowSettings().ShowTitleInTitlebar())
{
if (const auto tab{ _GetFocusedTab() })
{
@@ -2920,7 +2987,7 @@ namespace winrt::TerminalApp::implementation
// - See Pane::CalcSnappedDimension
float TerminalPage::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
{
if (_settings && _settings.GlobalSettings().SnapToGridOnResize())
if (_settings && _currentWindowSettings().SnapToGridOnResize())
{
if (const auto tabImpl{ _GetFocusedTabImpl() })
{
@@ -2948,7 +3015,7 @@ namespace winrt::TerminalApp::implementation
// the WinRT one on average, depending on CPU load. Don't use the WinRT clipboard API if you can.
const auto weakThis = get_weak();
const auto dispatcher = Dispatcher();
const auto globalSettings = _settings.GlobalSettings();
const auto windowSettings = _currentWindowSettings();
const auto bracketedPaste = eventArgs.BracketedPasteEnabled();
const auto sourceId = sender.try_as<ControlInteractivity>().Id();
@@ -2961,7 +3028,7 @@ namespace winrt::TerminalApp::implementation
text = clipboard::read();
}
if (!bracketedPaste && globalSettings.TrimPaste())
if (!bracketedPaste && windowSettings.TrimPaste())
{
text = winrt::hstring{ Utils::TrimPaste(text) };
}
@@ -2972,7 +3039,7 @@ namespace winrt::TerminalApp::implementation
}
bool warnMultiLine = false;
switch (globalSettings.WarnAboutMultiLinePaste())
switch (windowSettings.WarnAboutMultiLinePaste())
{
case WarnAboutMultiLinePaste::Automatic:
// NOTE that this is unsafe, because a shell that doesn't support bracketed paste
@@ -2995,7 +3062,7 @@ namespace winrt::TerminalApp::implementation
}
constexpr std::size_t minimumSizeForWarning = 1024 * 5; // 5 KiB
const auto warnLargeText = text.size() > minimumSizeForWarning && globalSettings.WarnAboutLargePaste();
const auto warnLargeText = text.size() > minimumSizeForWarning && windowSettings.WarnAboutLargePaste();
if (warnMultiLine || warnLargeText)
{
@@ -3340,13 +3407,13 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_WindowSizeChanged(const IInspectable sender, const Microsoft::Terminal::Control::WindowSizeChangedEventArgs args)
{
// Raise if:
// - Not in quake mode
// - Not in docked mode
// - Not in fullscreen
// - Only one tab exists
// - Only one pane exists
// else:
// - Reset conpty to its original size back
if (!WindowProperties().IsQuakeWindow() && !Fullscreen() &&
if (!_currentWindowSettings().DockWindow() && !Fullscreen() &&
NumberOfTabs() == 1 && _GetFocusedTabImpl()->GetLeafPaneCount() == 1)
{
WindowSizeChanged.raise(*this, args);
@@ -3530,7 +3597,7 @@ namespace winrt::TerminalApp::implementation
{
// Don't need to worry about duplicating or anything - we'll
// serialize the actual profile's GUID along with the content guid.
const auto& profile = _settings.GetProfileForArgs(newTerminalArgs);
const auto& profile = _settings.GetProfileForArgs(newTerminalArgs, _currentWindowSettings());
const auto control = _AttachControlToContent(newTerminalArgs.ContentId());
auto paneContent{ winrt::make<TerminalPaneContent>(profile, _terminalSettingsCache, control) };
return std::make_shared<Pane>(paneContent);
@@ -3546,7 +3613,7 @@ namespace winrt::TerminalApp::implementation
{
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
profile = GetClosestProfileForDuplicationOfProfile(profile);
controlSettings = Settings::TerminalSettings::CreateWithProfile(_settings, profile);
controlSettings = Settings::TerminalSettings::CreateWithProfile(_settings, profile, _currentWindowSettings());
const auto workingDirectory = tabImpl->GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
@@ -3557,8 +3624,8 @@ namespace winrt::TerminalApp::implementation
}
if (!profile)
{
profile = _settings.GetProfileForArgs(newTerminalArgs);
controlSettings = Settings::TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs);
profile = _settings.GetProfileForArgs(newTerminalArgs, _currentWindowSettings());
controlSettings = Settings::TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, _currentWindowSettings());
}
// Try to handle auto-elevation
@@ -3749,7 +3816,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalPage::_SetBackgroundImage(const winrt::Microsoft::Terminal::Settings::Model::IAppearanceConfig& newAppearance)
{
if (!_settings.GlobalSettings().UseBackgroundImageForWindow())
if (!_currentWindowSettings().UseBackgroundImageForWindow())
{
_tabContent.Background(nullptr);
return;
@@ -3820,7 +3887,7 @@ namespace winrt::TerminalApp::implementation
// updating terminal panes, so that we don't have to build a _new_
// TerminalSettings for every profile we update - we can just look them
// up the previous ones we built.
_terminalSettingsCache->Reset(_settings);
_terminalSettingsCache->Reset(_settings, _currentWindowSettings());
for (const auto& tab : _tabs)
{
@@ -3858,17 +3925,23 @@ namespace winrt::TerminalApp::implementation
// Reload the current value of alwaysOnTop from the settings file. This
// will let the user hot-reload this setting, but any runtime changes to
// the alwaysOnTop setting will be lost.
_isAlwaysOnTop = _settings.GlobalSettings().AlwaysOnTop();
_isAlwaysOnTop = _currentWindowSettings().AlwaysOnTop();
AlwaysOnTopChanged.raise(*this, nullptr);
_showTabsFullscreen = _settings.GlobalSettings().ShowTabsFullscreen();
_showTabsFullscreen = _currentWindowSettings().ShowTabsFullscreen();
// Settings AllowDependentAnimations will affect whether animations are
// enabled application-wide, so we don't need to check it each time we
// want to create an animation.
WUX::Media::Animation::Timeline::AllowDependentAnimations(!_settings.GlobalSettings().DisableAnimations());
WUX::Media::Animation::Timeline::AllowDependentAnimations(!_currentWindowSettings().DisableAnimations());
_tabRow.ShowElevationShield(IsRunningElevated() && _settings.GlobalSettings().ShowAdminShield());
_tabRow.ShowElevationShield(IsRunningElevated() && _currentWindowSettings().ShowAdminShield());
// Apply the ShowWindowsButton theme setting.
if (const auto theme = _settings.GlobalSettings().CurrentTheme(_currentWindowSettings()))
{
_tabRow.ShowWindowsButton(theme.Window() ? theme.Window().ShowWindowsButton() : true);
}
Media::SolidColorBrush transparent{ Windows::UI::Colors::Transparent() };
_tabView.Background(transparent);
@@ -3889,7 +3962,7 @@ namespace winrt::TerminalApp::implementation
// our TabView, to match the tab.showCloseButton property in the theme.
//
// Also update every tab's individual IsClosable to match the same property.
const auto theme = _settings.GlobalSettings().CurrentTheme();
const auto theme = _settings.GlobalSettings().CurrentTheme(_currentWindowSettings());
const auto visibility = (theme && theme.Tab()) ?
theme.Tab().ShowCloseButton() :
Settings::Model::TabCloseButtonVisibility::Always;
@@ -4503,7 +4576,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalPage::_UpdateTeachingTipTheme(winrt::Windows::UI::Xaml::FrameworkElement element)
{
auto theme{ _settings.GlobalSettings().CurrentTheme() };
auto theme{ _settings.GlobalSettings().CurrentTheme(_currentWindowSettings()) };
auto requestedTheme{ theme.RequestedTheme() };
while (element)
{
@@ -4656,7 +4729,7 @@ namespace winrt::TerminalApp::implementation
{
if (profile == _settings.ProfileDefaults())
{
return _settings.FindProfile(_settings.GlobalSettings().DefaultProfile());
return _settings.FindProfile(_currentWindowSettings().DefaultProfile());
}
return profile;
}
@@ -4877,7 +4950,7 @@ namespace winrt::TerminalApp::implementation
return;
}
const auto theme = _settings.GlobalSettings().CurrentTheme();
const auto theme = _settings.GlobalSettings().CurrentTheme(_currentWindowSettings());
auto requestedTheme{ theme.RequestedTheme() };
{
@@ -4921,7 +4994,7 @@ namespace winrt::TerminalApp::implementation
theme.TabRow().UnfocusedBackground()) :
ThemeColor{ nullptr } };
if (_settings.GlobalSettings().UseAcrylicInTabRow())
if (_currentWindowSettings().UseAcrylicInTabRow())
{
if (tabRowBg)
{
@@ -4951,7 +5024,7 @@ namespace winrt::TerminalApp::implementation
TitlebarBrush(backgroundSolidBrush);
}
if (!_settings.GlobalSettings().ShowTabsInTitlebar())
if (!_currentWindowSettings().ShowTabsInTitlebar())
{
_tabRow.Background(TitlebarBrush());
}
@@ -5187,7 +5260,7 @@ namespace winrt::TerminalApp::implementation
// Feature_ShellCompletions::IsEnabled back in _RegisterTerminalEvents
// User must explicitly opt-in on Preview builds
if (!_settings.GlobalSettings().EnableShellCompletionMenu())
if (!_currentWindowSettings().EnableShellCompletionMenu())
{
co_return;
}
@@ -5510,6 +5583,199 @@ namespace winrt::TerminalApp::implementation
}
}
// Rebuild the workspace flyout contents. Called every time the flyout opens
// so it reflects the current set of persisted workspaces.
void TerminalPage::_PopulateWorkspaceFlyout()
{
if (!_workspaceFlyout)
{
return;
}
_workspaceFlyout.Items().Clear();
// --- "Name / Rename this window" ---
{
MenuFlyoutItem item{};
item.Text(winrt::hstring{ _WindowProperties.WindowName().empty() ? L"Name this window\u2026" : L"Rename this window\u2026" });
auto iconElement = UI::IconPathConverter::IconWUX(L"\uE8AC"); // Rename glyph
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
item.Icon(iconElement);
item.Click([weakThis{ get_weak() }](auto&&, auto&&) {
if (auto page{ weakThis.get() })
{
// Re-use the existing openWindowRenamer action.
page->_actionDispatch->DoAction(ActionAndArgs{ ShortcutAction::OpenWindowRenamer, nullptr });
}
});
_workspaceFlyout.Items().Append(item);
}
// --- "Save workspace" ---
{
MenuFlyoutItem item{};
item.Text(winrt::hstring{ L"Save workspace" });
auto iconElement = UI::IconPathConverter::IconWUX(L"\uE74E"); // Save glyph
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
item.Icon(iconElement);
// Only enable if the window has a name.
item.IsEnabled(!_WindowProperties.WindowName().empty());
item.Click([weakThis{ get_weak() }](auto&&, auto&&) {
if (auto page{ weakThis.get() })
{
page->_actionDispatch->DoAction(ActionAndArgs{ ShortcutAction::SaveWorkspace, nullptr });
}
});
_workspaceFlyout.Items().Append(item);
}
// --- Gather open window info first so we can filter workspaces ---
// Ask the host (via AppHost → WindowEmperor) for all live windows.
const auto windowListReq{ winrt::make<WindowListRequest>() };
RequestWindowList.raise(*this, windowListReq);
const auto windowEntries = windowListReq.WindowEntries();
// Collect the names of all currently-open windows so we can hide
// workspaces that are already live from the saved-workspaces list.
std::set<winrt::hstring> openWindowNames;
if (windowEntries)
{
for (const auto& entry : windowEntries)
{
const std::wstring_view entryView{ entry };
const auto tabPos = entryView.find(L'\t');
if (tabPos != std::wstring_view::npos)
{
const auto namePart = entryView.substr(tabPos + 1);
if (!namePart.empty())
{
openWindowNames.emplace(namePart);
}
}
}
}
// --- Saved workspaces section (only those not currently open) ---
const auto workspaces = ApplicationState::SharedInstance().AllPersistedWorkspaces();
if (workspaces && workspaces.Size() > 0)
{
bool addedSeparator = false;
for (const auto& pair : workspaces)
{
const auto name = pair.Key();
// Skip workspaces that correspond to a currently-open window.
if (openWindowNames.count(name))
{
continue;
}
if (!addedSeparator)
{
_workspaceFlyout.Items().Append(MenuFlyoutSeparator{});
addedSeparator = true;
}
MenuFlyoutItem item{};
item.Text(name);
auto iconElement = UI::IconPathConverter::IconWUX(L"\uE8F1"); // SwitchApps glyph
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
item.Icon(iconElement);
item.Click([weakThis{ get_weak() }, name](auto&&, auto&&) {
if (auto page{ weakThis.get() })
{
page->_OpenWorkspaceWindow(name);
}
});
_workspaceFlyout.Items().Append(item);
}
}
// --- Open windows section ---
if (windowEntries && windowEntries.Size() > 0)
{
_workspaceFlyout.Items().Append(MenuFlyoutSeparator{});
const auto thisWindowId = _WindowProperties.WindowId();
for (const auto& entry : windowEntries)
{
// Each entry is formatted as "id\tname"
const std::wstring_view entryView{ entry };
const auto tabPos = entryView.find(L'\t');
if (tabPos == std::wstring_view::npos)
{
continue;
}
uint64_t id = 0;
const auto idPart = entryView.substr(0, tabPos);
for (const auto ch : idPart)
{
if (ch >= L'0' && ch <= L'9')
{
id = id * 10 + (ch - L'0');
}
}
const auto namePart = entryView.substr(tabPos + 1);
// Build display text like "#1: MyWindow" or "#2: <unnamed>"
winrt::hstring displayText;
if (namePart.empty())
{
displayText = winrt::hstring{ fmt::format(FMT_COMPILE(L"#{} (unnamed)"), id) };
}
else
{
displayText = winrt::hstring{ fmt::format(FMT_COMPILE(L"#{}: {}"), id, namePart) };
}
MenuFlyoutItem item{};
item.Text(displayText);
// Use a different glyph for the current window
if (id == thisWindowId)
{
auto iconElement = UI::IconPathConverter::IconWUX(L"\uE73E"); // CheckMark glyph
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
item.Icon(iconElement);
item.IsEnabled(false); // Can't summon yourself
}
else
{
auto iconElement = UI::IconPathConverter::IconWUX(L"\uE737"); // ChromeRestore glyph
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
item.Icon(iconElement);
// Click handler: summon the target window by ID.
// We raise SummonWindowByIdRequested which is handled by
// AppHost to directly summon the window without creating
// a new tab (unlike _OpenWorkspaceWindow which launches
// `wt -w <name>` and creates a tab).
item.Click([weakThis{ get_weak() }, id](auto&&, auto&&) {
if (auto page{ weakThis.get() })
{
page->SummonWindowByIdRequested.raise(*page, winrt::make<SummonWindowByIdRequestedArgs>(id));
}
});
}
_workspaceFlyout.Items().Append(item);
}
}
}
// Handler for our WindowProperties's PropertyChanged event. We'll use this
// to pop the "Identify Window" toast when the user renames our window.
void TerminalPage::_windowPropertyChanged(const IInspectable& /*sender*/, const WUX::Data::PropertyChangedEventArgs& args)
@@ -5519,6 +5785,10 @@ namespace winrt::TerminalApp::implementation
return;
}
// Keep the workspace dropdown label in sync with the window name.
// Use raw WindowName() so clearing the name hides the text.
_tabRow.WorkspaceName(_WindowProperties.WindowName());
// DON'T display the confirmation if this is the name we were
// given on startup!
if (_startupState == StartupState::Initialized)

View File

@@ -10,8 +10,10 @@
#include "AppKeyBindings.h"
#include "AppCommandlineArgs.h"
#include "RenameWindowRequestedArgs.g.h"
#include "SummonWindowByIdRequestedArgs.g.h"
#include "RequestMoveContentArgs.g.h"
#include "LaunchPositionRequest.g.h"
#include "WindowListRequest.g.h"
#include "Toast.h"
#include "WindowsPackageManagerFactory.h"
@@ -63,6 +65,15 @@ namespace winrt::TerminalApp::implementation
_ProposedName{ name } {};
};
struct SummonWindowByIdRequestedArgs : SummonWindowByIdRequestedArgsT<SummonWindowByIdRequestedArgs>
{
WINRT_PROPERTY(uint64_t, WindowId);
public:
SummonWindowByIdRequestedArgs(uint64_t id) :
_WindowId{ id } {};
};
struct RequestMoveContentArgs : RequestMoveContentArgsT<RequestMoveContentArgs>
{
WINRT_PROPERTY(winrt::hstring, Window);
@@ -84,6 +95,17 @@ namespace winrt::TerminalApp::implementation
til::property<winrt::Microsoft::Terminal::Settings::Model::LaunchPosition> Position;
};
struct WindowListRequest : WindowListRequestT<WindowListRequest>
{
WindowListRequest() :
_WindowEntries{ winrt::single_threaded_vector<winrt::hstring>() } {}
winrt::Windows::Foundation::Collections::IVector<winrt::hstring> WindowEntries() const { return _WindowEntries; }
private:
winrt::Windows::Foundation::Collections::IVector<winrt::hstring> _WindowEntries;
};
struct WinGetSearchParams
{
winrt::Microsoft::Management::Deployment::PackageMatchField Field;
@@ -122,6 +144,7 @@ namespace winrt::TerminalApp::implementation
safe_void_coroutine RequestQuit();
safe_void_coroutine CloseWindow();
winrt::Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout();
void PersistState();
std::vector<IPaneContent> Panes() const;
@@ -192,6 +215,7 @@ namespace winrt::TerminalApp::implementation
til::typed_event<IInspectable, IInspectable> IdentifyWindowsRequested;
til::typed_event<IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs> RenameWindowRequested;
til::typed_event<IInspectable, IInspectable> SummonWindowRequested;
til::typed_event<IInspectable, winrt::TerminalApp::SummonWindowByIdRequestedArgs> SummonWindowByIdRequested;
til::typed_event<IInspectable, winrt::Microsoft::Terminal::Control::WindowSizeChangedEventArgs> WindowSizeChanged;
til::typed_event<IInspectable, IInspectable> OpenSystemMenu;
@@ -203,6 +227,7 @@ namespace winrt::TerminalApp::implementation
til::typed_event<Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs> RequestReceiveContent;
til::typed_event<IInspectable, winrt::TerminalApp::LaunchPositionRequest> RequestLaunchPosition;
til::typed_event<IInspectable, winrt::TerminalApp::WindowListRequest> RequestWindowList;
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, PropertyChanged.raise, nullptr);
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, FrameBrush, PropertyChanged.raise, nullptr);
@@ -225,6 +250,7 @@ namespace winrt::TerminalApp::implementation
TerminalApp::TabRowControl _tabRow{ nullptr };
Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr };
Microsoft::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr };
Windows::UI::Xaml::Controls::MenuFlyout _workspaceFlyout{ nullptr };
winrt::TerminalApp::ColorPickupFlyout _tabColorPicker{ nullptr };
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
@@ -283,6 +309,8 @@ namespace winrt::TerminalApp::implementation
TerminalApp::ContentManager _manager{ nullptr };
Microsoft::Terminal::Settings::Model::WindowSettings _currentWindowSettings() const;
std::shared_ptr<TerminalSettingsCache> _terminalSettingsCache{};
struct StashedDragData
@@ -324,6 +352,7 @@ namespace winrt::TerminalApp::implementation
void _restartPaneConnection(const TerminalApp::TerminalPaneContent&, const winrt::Windows::Foundation::IInspectable&);
safe_void_coroutine _OpenNewWindow(const Microsoft::Terminal::Settings::Model::INewContentArgs newContentArgs);
safe_void_coroutine _OpenWorkspaceWindow(const winrt::hstring name);
void _OpenNewTerminalViaDropdown(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
@@ -562,6 +591,7 @@ namespace winrt::TerminalApp::implementation
void _PopulateContextMenu(const Microsoft::Terminal::Control::TermControl& control, const Microsoft::UI::Xaml::Controls::CommandBarFlyout& sender, const bool withSelection);
void _PopulateQuickFixMenu(const Microsoft::Terminal::Control::TermControl& control, const Windows::UI::Xaml::Controls::MenuFlyout& sender);
void _PopulateWorkspaceFlyout();
winrt::Windows::UI::Xaml::Controls::MenuFlyout _CreateRunAsAdminFlyout(int profileIndex);
winrt::Microsoft::Terminal::Control::TermControl _senderOrActiveControl(const winrt::Windows::Foundation::IInspectable& sender);

View File

@@ -19,6 +19,10 @@ namespace TerminalApp
{
String ProposedName { get; };
};
[default_interface] runtimeclass SummonWindowByIdRequestedArgs
{
UInt64 WindowId { get; };
};
[default_interface] runtimeclass RequestMoveContentArgs
{
String Window { get; };
@@ -41,8 +45,6 @@ namespace TerminalApp
String VirtualWorkingDirectory { get; set; };
String VirtualEnvVars { get; set; };
Boolean IsQuakeWindow();
};
runtimeclass LaunchPositionRequest
@@ -50,6 +52,14 @@ namespace TerminalApp
Microsoft.Terminal.Settings.Model.LaunchPosition Position;
}
// Raised by TerminalPage when it needs the list of open windows.
// The handler (AppHost) fills WindowNames synchronously.
[default_interface] runtimeclass WindowListRequest
{
// Each entry is "id\tname" (name may be empty for unnamed windows).
Windows.Foundation.Collections.IVector<String> WindowEntries { get; };
}
[default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged, Microsoft.Terminal.UI.IDirectKeyListener
{
TerminalPage(WindowProperties properties, ContentManager manager);
@@ -93,6 +103,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, SummonWindowByIdRequestedArgs> SummonWindowByIdRequested;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.WindowSizeChangedEventArgs> WindowSizeChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
@@ -103,5 +114,6 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> RequestReceiveContent;
event Windows.Foundation.TypedEventHandler<Object, LaunchPositionRequest> RequestLaunchPosition;
event Windows.Foundation.TypedEventHandler<Object, WindowListRequest> RequestWindowList;
}
}

View File

@@ -18,9 +18,9 @@ namespace winrt::TerminalApp::implementation
result.UnfocusedSettings().try_as(_unfocusedSettings);
}
TerminalSettingsCache::TerminalSettingsCache(const MTSM::CascadiaSettings& settings)
TerminalSettingsCache::TerminalSettingsCache(const MTSM::CascadiaSettings& settings, const MTSM::WindowSettings& windowSettings)
{
Reset(settings);
Reset(settings, windowSettings);
}
std::optional<TerminalSettingsPair> TerminalSettingsCache::TryLookup(const MTSM::Profile& profile)
@@ -35,7 +35,7 @@ namespace winrt::TerminalApp::implementation
auto& pair{ found->second };
if (!pair.second)
{
pair.second = winrt::Microsoft::Terminal::Settings::TerminalSettings::CreateWithProfile(_settings, pair.first);
pair.second = winrt::Microsoft::Terminal::Settings::TerminalSettings::CreateWithProfile(_settings, pair.first, _windowSettings);
}
return std::optional{ TerminalSettingsPair{ *pair.second } };
}
@@ -43,9 +43,10 @@ namespace winrt::TerminalApp::implementation
return std::nullopt;
}
void TerminalSettingsCache::Reset(const MTSM::CascadiaSettings& settings)
void TerminalSettingsCache::Reset(const MTSM::CascadiaSettings& settings, const MTSM::WindowSettings& windowSettings)
{
_settings = settings;
_windowSettings = windowSettings;
// Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however,
// when we stabilize its guid this will become fully safe.

View File

@@ -38,12 +38,13 @@ namespace winrt::TerminalApp::implementation
struct TerminalSettingsCache
{
TerminalSettingsCache(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
TerminalSettingsCache(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const Microsoft::Terminal::Settings::Model::WindowSettings& windowSettings);
std::optional<TerminalSettingsPair> TryLookup(const Microsoft::Terminal::Settings::Model::Profile& profile);
void Reset(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
void Reset(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const Microsoft::Terminal::Settings::Model::WindowSettings& windowSettings);
private:
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
Microsoft::Terminal::Settings::Model::WindowSettings _windowSettings{ nullptr };
std::unordered_map<winrt::guid, std::pair<Microsoft::Terminal::Settings::Model::Profile, std::optional<winrt::Microsoft::Terminal::Settings::TerminalSettingsCreateResult>>> profileGuidSettingsMap;
};
}

View File

@@ -232,7 +232,7 @@ namespace winrt::TerminalApp::implementation
// that the window size is _first_ set up as something sensible, so
// leaving fullscreen returns to a reasonable size.
const auto launchMode = this->GetLaunchMode();
if (_WindowProperties->IsQuakeWindow() || WI_IsFlagSet(launchMode, LaunchMode::FocusMode))
if (WI_IsFlagSet(launchMode, LaunchMode::FocusMode))
{
_root->SetFocusMode(true);
}
@@ -244,7 +244,7 @@ namespace winrt::TerminalApp::implementation
_root->Maximized(true);
}
if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !_WindowProperties->IsQuakeWindow())
if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode))
{
_root->SetFullscreen(true);
}
@@ -252,6 +252,15 @@ namespace winrt::TerminalApp::implementation
AppLogic::Current()->NotifyRootInitialized();
}
WindowLayout TerminalWindow::GetWindowLayout()
{
if (_root)
{
return _root->GetWindowLayout();
}
return nullptr;
}
void TerminalWindow::PersistState()
{
if (_root)
@@ -267,22 +276,22 @@ namespace winrt::TerminalApp::implementation
bool TerminalWindow::GetShowTabsInTitlebar()
{
return _settings.GlobalSettings().ShowTabsInTitlebar();
return _currentWindowSettings().ShowTabsInTitlebar();
}
bool TerminalWindow::GetInitialAlwaysOnTop()
{
return _settings.GlobalSettings().AlwaysOnTop();
return _currentWindowSettings().AlwaysOnTop();
}
bool TerminalWindow::GetInitialShowTabsFullscreen()
{
return _settings.GlobalSettings().ShowTabsFullscreen();
return _currentWindowSettings().ShowTabsFullscreen();
}
bool TerminalWindow::GetMinimizeToNotificationArea()
{
return _settings.GlobalSettings().MinimizeToNotificationArea();
return _currentWindowSettings().MinimizeToNotificationArea();
}
bool TerminalWindow::GetAlwaysShowNotificationIcon()
@@ -292,12 +301,12 @@ namespace winrt::TerminalApp::implementation
bool TerminalWindow::GetShowTitleInTitlebar()
{
return _settings.GlobalSettings().ShowTitleInTitlebar();
return _currentWindowSettings().ShowTitleInTitlebar();
}
Microsoft::Terminal::Settings::Model::Theme TerminalWindow::Theme()
{
return _settings.GlobalSettings().CurrentTheme();
return _settings.GlobalSettings().CurrentTheme(_currentWindowSettings());
}
// WinUI can't show 2 dialogs simultaneously. Yes, really. If you do, you get an exception.
@@ -374,7 +383,7 @@ namespace winrt::TerminalApp::implementation
auto themingLambda{ [weak](const Windows::Foundation::IInspectable& sender, const RoutedEventArgs&) {
if (const auto strong = weak.get())
{
auto theme{ strong->_settings.GlobalSettings().CurrentTheme() };
auto theme{ strong->_settings.GlobalSettings().CurrentTheme(strong->_currentWindowSettings()) };
auto requestedTheme{ theme.RequestedTheme() };
auto element{ sender.try_as<winrt::Windows::UI::Xaml::FrameworkElement>() };
while (element)
@@ -598,7 +607,7 @@ namespace winrt::TerminalApp::implementation
// --focusMode on the commandline here, and the mode in the settings.
// Below, we'll also account for if focus mode was persisted into the
// session for restoration.
bool focusMode = _appArgs && _appArgs->ParsedArgs().GetLaunchMode().value_or(_settings.GlobalSettings().LaunchMode()) == LaunchMode::FocusMode;
bool focusMode = _appArgs && _appArgs->ParsedArgs().GetLaunchMode().value_or(_currentWindowSettings().LaunchMode()) == LaunchMode::FocusMode;
const auto scale = static_cast<float>(dpi) / static_cast<float>(USER_DEFAULT_SCREEN_DPI);
if (const auto layout = LoadPersistedLayout())
@@ -621,7 +630,7 @@ namespace winrt::TerminalApp::implementation
if ((_appArgs && _appArgs->ParsedArgs().GetSize().has_value()) || (proposedSize.Width == 0 && proposedSize.Height == 0))
{
// Use the default profile to determine how big of a window we need.
const auto settings{ Settings::TerminalSettings::CreateWithNewTerminalArgs(_settings, nullptr) };
const auto settings{ Settings::TerminalSettings::CreateWithNewTerminalArgs(_settings, nullptr, _currentWindowSettings()) };
const til::size emptySize{};
const auto commandlineSize = _appArgs ? _appArgs->ParsedArgs().GetSize().value_or(emptySize) : til::size{};
@@ -645,7 +654,7 @@ namespace winrt::TerminalApp::implementation
// GH#2061 - If the global setting "Always show tab bar" is
// set or if "Show tabs in title bar" is set, then we'll need to add
// the height of the tab bar here.
if (_settings.GlobalSettings().ShowTabsInTitlebar() && !focusMode)
if (_currentWindowSettings().ShowTabsInTitlebar() && !focusMode)
{
// In the past, we used to actually instantiate a TitlebarControl
// and use Measure() to determine the DesiredSize of the control, to
@@ -663,7 +672,7 @@ namespace winrt::TerminalApp::implementation
static constexpr auto titlebarHeight = 40;
proposedSize.Height += (titlebarHeight)*scale;
}
else if (_settings.GlobalSettings().AlwaysShowTabs() && !focusMode)
else if (_currentWindowSettings().AlwaysShowTabs() && !focusMode)
{
// Same comment as above, but with a TabRowControl.
//
@@ -696,7 +705,7 @@ namespace winrt::TerminalApp::implementation
// GH#4620/#5801 - If the user passed --maximized or --fullscreen on the
// commandline, then use that to override the value from the settings.
const auto valueFromSettings = _settings.GlobalSettings().LaunchMode();
const auto valueFromSettings = _currentWindowSettings().LaunchMode();
const auto valueFromCommandlineArgs = _appArgs ? _appArgs->ParsedArgs().GetLaunchMode() : std::nullopt;
if (const auto layout = LoadPersistedLayout())
{
@@ -722,7 +731,7 @@ namespace winrt::TerminalApp::implementation
// - a point containing the requested initial position in pixels.
TerminalApp::InitialPosition TerminalWindow::GetInitialPosition(int64_t defaultInitialX, int64_t defaultInitialY)
{
auto initialPosition{ _settings.GlobalSettings().InitialPosition() };
auto initialPosition{ _currentWindowSettings().InitialPosition() };
if (const auto layout = LoadPersistedLayout())
{
@@ -770,10 +779,15 @@ namespace winrt::TerminalApp::implementation
return !_contentBounds &&
!hadPersistedPosition &&
_settings.GlobalSettings().CenterOnLaunch() &&
_currentWindowSettings().CenterOnLaunch() &&
(_appArgs && !_appArgs->ParsedArgs().GetPosition().has_value());
}
Microsoft::Terminal::Settings::Model::Docking TerminalWindow::Docking()
{
return _currentWindowSettings().DockWindow();
}
// Method Description:
// - See Pane::CalcSnappedDimension
float TerminalWindow::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
@@ -1097,6 +1111,11 @@ namespace winrt::TerminalApp::implementation
_initialContentArgs = wil::to_vector(args);
}
void TerminalWindow::SetPersistedLayout(const winrt::Microsoft::Terminal::Settings::Model::WindowLayout& layout)
{
_cachedLayout = layout;
}
// Method Description:
// - Parse the provided commandline arguments into actions, and try to
// perform them immediately.
@@ -1188,7 +1207,7 @@ namespace winrt::TerminalApp::implementation
bool TerminalWindow::AutoHideWindow()
{
return _settings.GlobalSettings().AutoHideWindow();
return _currentWindowSettings().AutoHideWindow();
}
void TerminalWindow::UpdateSettingsHandler(const winrt::IInspectable& /*sender*/,
@@ -1207,21 +1226,22 @@ namespace winrt::TerminalApp::implementation
void TerminalWindow::WindowName(const winrt::hstring& name)
{
const auto oldIsQuakeMode = _WindowProperties->IsQuakeWindow();
const auto oldName = _WindowProperties->WindowName();
_WindowProperties->WindowName(name);
if (!_root)
if (!_root || oldName == name)
{
return;
}
const auto newIsQuakeMode = _WindowProperties->IsQuakeWindow();
if (newIsQuakeMode != oldIsQuakeMode)
{
// If we're entering Quake Mode from ~Focus Mode, then this will enter Focus Mode
// If we're entering Quake Mode from Focus Mode, then this will do nothing
// If we're leaving Quake Mode (we're already in Focus Mode), then this will do nothing
_root->SetFocusMode(true);
IsQuakeWindowChanged.raise(*this, nullptr);
}
// The window name determines which WindowSettings we use
// (via _currentWindowSettings()). Since it changed, refresh
// all downstream consumers of per-window settings:
// - TerminalPage (settings cache, tab/pane settings)
// - Theme (may differ per window)
// - Docking & host-level properties (via IsQuakeWindowChanged -> AppHost)
_root->SetSettings(_settings, true);
_RefreshThemeRoutine();
IsQuakeWindowChanged.raise(*this, nullptr);
}
void TerminalWindow::WindowId(const uint64_t& id)
{
@@ -1332,13 +1352,13 @@ namespace winrt::TerminalApp::implementation
if (!FocusMode())
{
if (!_settings.GlobalSettings().AlwaysShowTabs())
if (!_currentWindowSettings().AlwaysShowTabs())
{
// Hide the title bar = off, Always show tabs = off.
static constexpr auto titlebarHeight = 10;
pixelSize.Height += (titlebarHeight)*scale;
}
else if (!_settings.GlobalSettings().ShowTabsInTitlebar())
else if (!_currentWindowSettings().ShowTabsInTitlebar())
{
// Hide the title bar = off, Always show tabs = on.
static constexpr auto titlebarAndTabBarHeight = 40;
@@ -1419,9 +1439,9 @@ namespace winrt::TerminalApp::implementation
return _WindowName.empty() ? til::hstring_format(FMT_COMPILE(L"<{}>"), RS_(L"UnnamedWindowName")) : _WindowName;
}
bool WindowProperties::IsQuakeWindow() const noexcept
WindowSettings TerminalWindow::_currentWindowSettings() const
{
return _WindowName == L"_quake";
return _settings.WindowSettings(_WindowProperties->WindowName());
}
};

View File

@@ -44,7 +44,6 @@ namespace winrt::TerminalApp::implementation
void WindowId(const uint64_t& value);
winrt::hstring WindowIdForDisplay() const noexcept;
winrt::hstring WindowNameForDisplay() const noexcept;
bool IsQuakeWindow() const noexcept;
til::property_changed_event PropertyChanged;
@@ -71,6 +70,7 @@ namespace winrt::TerminalApp::implementation
void Create();
winrt::Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout();
void PersistState();
void UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args);
@@ -79,6 +79,7 @@ namespace winrt::TerminalApp::implementation
int32_t SetStartupCommandline(TerminalApp::CommandlineArgs args);
void SetStartupContent(const winrt::hstring& content, const Windows::Foundation::IReference<Windows::Foundation::Rect>& contentBounds);
void SetPersistedLayout(const winrt::Microsoft::Terminal::Settings::Model::WindowLayout& layout);
int32_t ExecuteCommandline(TerminalApp::CommandlineArgs args);
void SetSettingsStartupArgs(const std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions);
@@ -134,12 +135,12 @@ namespace winrt::TerminalApp::implementation
void DismissDialog();
Microsoft::Terminal::Settings::Model::Theme Theme();
Microsoft::Terminal::Settings::Model::Docking Docking();
void UpdateSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SettingsLoadEventArgs& arg);
void WindowName(const winrt::hstring& value);
void WindowId(const uint64_t& value);
bool IsQuakeWindow() const noexcept { return _WindowProperties->IsQuakeWindow(); }
TerminalApp::WindowProperties WindowProperties() { return *_WindowProperties; }
void AttachContent(winrt::hstring content, uint32_t tabIndex);
@@ -187,6 +188,8 @@ namespace winrt::TerminalApp::implementation
TerminalApp::ContentManager _manager{ nullptr };
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> _initialContentArgs;
Microsoft::Terminal::Settings::Model::WindowSettings _currentWindowSettings() const;
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey,
const winrt::hstring& contentKey,
HRESULT settingsLoadedResult,
@@ -221,6 +224,7 @@ namespace winrt::TerminalApp::implementation
FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress);
FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested);
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
FORWARDED_TYPED_EVENT(SummonWindowByIdRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::SummonWindowByIdRequestedArgs, _root, SummonWindowByIdRequested);
FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu);
FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested);
FORWARDED_TYPED_EVENT(ShowWindowChanged, Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs, _root, ShowWindowChanged);
@@ -229,6 +233,7 @@ namespace winrt::TerminalApp::implementation
FORWARDED_TYPED_EVENT(RequestReceiveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs, _root, RequestReceiveContent);
FORWARDED_TYPED_EVENT(RequestLaunchPosition, Windows::Foundation::IInspectable, winrt::TerminalApp::LaunchPositionRequest, _root, RequestLaunchPosition);
FORWARDED_TYPED_EVENT(RequestWindowList, Windows::Foundation::IInspectable, winrt::TerminalApp::WindowListRequest, _root, RequestWindowList);
#ifdef UNIT_TESTING
friend class TerminalAppLocalTests::CommandlineTest;

View File

@@ -55,11 +55,13 @@ namespace TerminalApp
Int32 SetStartupCommandline(CommandlineArgs args);
void SetStartupContent(String json, Windows.Foundation.IReference<Windows.Foundation.Rect> bounds);
void SetPersistedLayout(Microsoft.Terminal.Settings.Model.WindowLayout layout);
Int32 ExecuteCommandline(CommandlineArgs args);
Boolean ShouldImmediatelyHandoffToElevated();
void HandoffToElevated();
Microsoft.Terminal.Settings.Model.WindowLayout GetWindowLayout();
void PersistState();
Windows.UI.Xaml.UIElement GetRoot();
@@ -72,6 +74,8 @@ namespace TerminalApp
Boolean AlwaysOnTop { get; };
Boolean AutoHideWindow { get; };
Boolean ShowTabsFullscreen { get; };
Microsoft.Terminal.Settings.Model.Theme Theme { get; };
Microsoft.Terminal.Settings.Model.Docking Docking { get; };
void IdentifyWindow();
void SetPersistedLayoutIdx(UInt32 idx);
@@ -105,7 +109,6 @@ namespace TerminalApp
WindowProperties WindowProperties { get; };
void WindowName(String name);
void WindowId(UInt64 id);
Boolean IsQuakeWindow();
// See IDialogPresenter and TerminalPage's DialogPresenter for more
// information.
@@ -126,6 +129,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, SummonWindowByIdRequestedArgs> SummonWindowByIdRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
event Windows.Foundation.TypedEventHandler<Object, TerminalApp.SystemMenuChangeArgs> SystemMenuChangeRequested;
@@ -137,6 +141,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, RequestMoveContentArgs> RequestMoveContent;
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> RequestReceiveContent;
event Windows.Foundation.TypedEventHandler<Object, LaunchPositionRequest> RequestLaunchPosition;
event Windows.Foundation.TypedEventHandler<Object, WindowListRequest> RequestWindowList;
void AttachContent(String content, UInt32 tabIndex);
void SendContentToOther(RequestReceiveContentArgs args);

View File

@@ -50,21 +50,21 @@ namespace winrt::Microsoft::Terminal::Settings
return { horizAlign, vertAlign };
}
winrt::com_ptr<TerminalSettings> TerminalSettings::_CreateWithProfileCommon(const Model::CascadiaSettings& appSettings, const Model::Profile& profile)
winrt::com_ptr<TerminalSettings> TerminalSettings::_CreateWithProfileCommon(const Model::CascadiaSettings& appSettings, const Model::Profile& profile, const Model::WindowSettings& windowSettings)
{
auto settings{ winrt::make_self<TerminalSettings>() };
const auto globals = appSettings.GlobalSettings();
settings->_ApplyProfileSettings(profile);
settings->_ApplyGlobalSettings(globals);
settings->_ApplyAppearanceSettings(profile.DefaultAppearance(), globals.ColorSchemes(), globals.CurrentTheme());
settings->_ApplyWindowSettings(windowSettings);
settings->_ApplyAppearanceSettings(profile.DefaultAppearance(), globals.ColorSchemes(), globals.CurrentTheme(windowSettings));
return settings;
}
winrt::com_ptr<TerminalSettings> TerminalSettings::CreateForPreview(const Model::CascadiaSettings& appSettings, const Model::Profile& profile)
winrt::com_ptr<TerminalSettings> TerminalSettings::CreateForPreview(const Model::CascadiaSettings& appSettings, const Model::Profile& profile, const Model::WindowSettings& windowSettings)
{
const auto settings = _CreateWithProfileCommon(appSettings, profile);
const auto settings = _CreateWithProfileCommon(appSettings, profile, windowSettings);
settings->_UseBackgroundImageForWindow = false;
return settings;
}
@@ -80,9 +80,9 @@ namespace winrt::Microsoft::Terminal::Settings
// Return Value:
// - A TerminalSettingsCreateResult, which contains a pair of TerminalSettings objects,
// one for when the terminal is focused and the other for when the terminal is unfocused
TerminalSettingsCreateResult TerminalSettings::CreateWithProfile(const Model::CascadiaSettings& appSettings, const Model::Profile& profile)
TerminalSettingsCreateResult TerminalSettings::CreateWithProfile(const Model::CascadiaSettings& appSettings, const Model::Profile& profile, const Model::WindowSettings& windowSettings)
{
const auto settings = _CreateWithProfileCommon(appSettings, profile);
const auto settings = _CreateWithProfileCommon(appSettings, profile, windowSettings);
winrt::com_ptr<TerminalSettings> child{ nullptr };
if (const auto& unfocusedAppearance{ profile.UnfocusedAppearance() })
@@ -90,7 +90,7 @@ namespace winrt::Microsoft::Terminal::Settings
const auto globals = appSettings.GlobalSettings();
child = winrt::make_self<TerminalSettings>();
child->_parent = settings->get_strong();
child->_ApplyAppearanceSettings(unfocusedAppearance, globals.ColorSchemes(), globals.CurrentTheme());
child->_ApplyAppearanceSettings(unfocusedAppearance, globals.ColorSchemes(), globals.CurrentTheme(windowSettings));
}
return TerminalSettingsCreateResult{ settings.get(), child.get() };
@@ -114,10 +114,11 @@ namespace winrt::Microsoft::Terminal::Settings
// - A TerminalSettingsCreateResult object, which contains a pair of TerminalSettings
// objects. One for when the terminal is focused and one for when the terminal is unfocused.
TerminalSettingsCreateResult TerminalSettings::CreateWithNewTerminalArgs(const Model::CascadiaSettings& appSettings,
const Model::NewTerminalArgs& newTerminalArgs)
const Model::NewTerminalArgs& newTerminalArgs,
const Model::WindowSettings& windowSettings)
{
const auto profile = appSettings.GetProfileForArgs(newTerminalArgs);
auto settingsPair{ CreateWithProfile(appSettings, profile) };
const auto profile = appSettings.GetProfileForArgs(newTerminalArgs, windowSettings);
auto settingsPair{ CreateWithProfile(appSettings, profile, windowSettings) };
auto defaultSettings = settingsPair.DefaultSettings();
if (newTerminalArgs)
@@ -356,31 +357,31 @@ namespace winrt::Microsoft::Terminal::Settings
}
// Method Description:
// - Applies appropriate settings from the globals into the TerminalSettings object.
// - Applies appropriate settings from the window settings into the TerminalSettings object.
// Arguments:
// - globalSettings: the global property values we're applying.
// - windowSettings: the per-window property values we're applying.
// Return Value:
// - <none>
void TerminalSettings::_ApplyGlobalSettings(const Model::GlobalAppSettings& globalSettings) noexcept
void TerminalSettings::_ApplyWindowSettings(const Model::WindowSettings& windowSettings) noexcept
{
_InitialRows = globalSettings.InitialRows();
_InitialCols = globalSettings.InitialCols();
_InitialRows = windowSettings.InitialRows();
_InitialCols = windowSettings.InitialCols();
_WordDelimiters = globalSettings.WordDelimiters();
_CopyOnSelect = globalSettings.CopyOnSelect();
_CopyFormatting = globalSettings.CopyFormatting();
_FocusFollowMouse = globalSettings.FocusFollowMouse();
_ScrollToZoom = globalSettings.ScrollToZoom();
_ScrollToChangeOpacity = globalSettings.ScrollToChangeOpacity();
_GraphicsAPI = globalSettings.GraphicsAPI();
_DisablePartialInvalidation = globalSettings.DisablePartialInvalidation();
_SoftwareRendering = globalSettings.SoftwareRendering();
_TextMeasurement = globalSettings.TextMeasurement();
_DefaultInputScope = globalSettings.DefaultInputScope();
_UseBackgroundImageForWindow = globalSettings.UseBackgroundImageForWindow();
_TrimBlockSelection = globalSettings.TrimBlockSelection();
_DetectURLs = globalSettings.DetectURLs();
_EnableUnfocusedAcrylic = globalSettings.EnableUnfocusedAcrylic();
_WordDelimiters = windowSettings.WordDelimiters();
_CopyOnSelect = windowSettings.CopyOnSelect();
_CopyFormatting = windowSettings.CopyFormatting();
_FocusFollowMouse = windowSettings.FocusFollowMouse();
_ScrollToZoom = windowSettings.ScrollToZoom();
_ScrollToChangeOpacity = windowSettings.ScrollToChangeOpacity();
_GraphicsAPI = windowSettings.GraphicsAPI();
_DisablePartialInvalidation = windowSettings.DisablePartialInvalidation();
_SoftwareRendering = windowSettings.SoftwareRendering();
_TextMeasurement = windowSettings.TextMeasurement();
_DefaultInputScope = windowSettings.DefaultInputScope();
_UseBackgroundImageForWindow = windowSettings.UseBackgroundImageForWindow();
_TrimBlockSelection = windowSettings.TrimBlockSelection();
_DetectURLs = windowSettings.DetectURLs();
_EnableUnfocusedAcrylic = windowSettings.EnableUnfocusedAcrylic();
}
// Method Description:

View File

@@ -57,13 +57,15 @@ namespace winrt::Microsoft::Terminal::Settings
{
TerminalSettings() = default;
static winrt::com_ptr<TerminalSettings> CreateForPreview(const Model::CascadiaSettings& appSettings, const Model::Profile& profile);
static winrt::com_ptr<TerminalSettings> CreateForPreview(const Model::CascadiaSettings& appSettings, const Model::Profile& profile, const Model::WindowSettings& windowSettings);
static TerminalSettingsCreateResult CreateWithProfile(const Model::CascadiaSettings& appSettings,
const Model::Profile& profile);
const Model::Profile& profile,
const Model::WindowSettings& windowSettings);
static TerminalSettingsCreateResult CreateWithNewTerminalArgs(const Model::CascadiaSettings& appSettings,
const Model::NewTerminalArgs& newTerminalArgs);
const Model::NewTerminalArgs& newTerminalArgs,
const Model::WindowSettings& windowSettings);
void ApplyColorScheme(const Model::ColorScheme& scheme);
@@ -102,10 +104,10 @@ namespace winrt::Microsoft::Terminal::Settings
std::optional<std::array<Microsoft::Terminal::Core::Color, COLOR_TABLE_SIZE>> _ColorTable;
std::span<Microsoft::Terminal::Core::Color> _getColorTableImpl();
static winrt::com_ptr<TerminalSettings> _CreateWithProfileCommon(const Model::CascadiaSettings& appSettings, const Model::Profile& profile);
static winrt::com_ptr<TerminalSettings> _CreateWithProfileCommon(const Model::CascadiaSettings& appSettings, const Model::Profile& profile, const Model::WindowSettings& windowSettings);
void _ApplyProfileSettings(const Model::Profile& profile);
void _ApplyGlobalSettings(const Model::GlobalAppSettings& globalSettings) noexcept;
void _ApplyWindowSettings(const Model::WindowSettings& windowSettings) noexcept;
void _ApplyAppearanceSettings(const Microsoft::Terminal::Settings::Model::IAppearanceConfig& appearance,
const Windows::Foundation::Collections::IMapView<hstring, Microsoft::Terminal::Settings::Model::ColorScheme>& schemes,
const winrt::Microsoft::Terminal::Settings::Model::Theme currentTheme);

View File

@@ -25,7 +25,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), AllowHeadless);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), DebugFeaturesEnabled);
GETSET_BINDABLE_ENUM_SETTING(TextMeasurement, winrt::Microsoft::Terminal::Control::TextMeasurement, _settings.GlobalSettings().TextMeasurement);
GETSET_BINDABLE_ENUM_SETTING(TextMeasurement, winrt::Microsoft::Terminal::Control::TextMeasurement, _settings.WindowSettingsDefaults().TextMeasurement);
private:
Model::CascadiaSettings _settings;

View File

@@ -22,7 +22,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
constexpr std::wstring_view legacyDarkThemeName{ L"legacyDark" };
constexpr std::wstring_view legacyLightThemeName{ L"legacyLight" };
GlobalAppearanceViewModel::GlobalAppearanceViewModel(Model::GlobalAppSettings globalSettings) :
GlobalAppearanceViewModel::GlobalAppearanceViewModel(Model::GlobalAppSettings globalSettings, Model::WindowSettings windowSettings) :
_windowSettings{ windowSettings },
_GlobalSettings{ globalSettings },
_ThemeList{ single_threaded_observable_vector<Model::Theme>() }
{
@@ -46,7 +47,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
winrt::Windows::Foundation::IInspectable GlobalAppearanceViewModel::CurrentTheme()
{
return _GlobalSettings.CurrentTheme();
return _GlobalSettings.CurrentTheme(_windowSettings);
}
// Get the name out of the newly selected item, stash that as the Theme name
@@ -56,7 +57,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
if (const auto& theme{ tag.try_as<Model::Theme>() })
{
_GlobalSettings.Theme(Model::ThemePair{ theme.Name() });
_windowSettings.Theme(Model::ThemePair{ theme.Name() });
}
}
@@ -103,12 +104,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
bool GlobalAppearanceViewModel::InvertedDisableAnimations()
{
return !_GlobalSettings.DisableAnimations();
return !_windowSettings.DisableAnimations();
}
void GlobalAppearanceViewModel::InvertedDisableAnimations(bool value)
{
_GlobalSettings.DisableAnimations(!value);
_windowSettings.DisableAnimations(!value);
}
void GlobalAppearanceViewModel::ShowTitlebarToggled(const winrt::Windows::Foundation::IInspectable& /* sender */, const RoutedEventArgs& /* args */)

View File

@@ -12,14 +12,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
struct GlobalAppearanceViewModel : GlobalAppearanceViewModelT<GlobalAppearanceViewModel>, ViewModelHelper<GlobalAppearanceViewModel>
{
public:
GlobalAppearanceViewModel(Model::GlobalAppSettings globalSettings);
GlobalAppearanceViewModel(Model::GlobalAppSettings globalSettings, Model::WindowSettings windowSettings);
// DON'T YOU DARE ADD A `WINRT_CALLBACK(PropertyChanged` TO A CLASS DERIVED FROM ViewModelHelper. Do this instead:
using ViewModelHelper<GlobalAppearanceViewModel>::PropertyChanged;
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Model::Theme>, ThemeList, nullptr);
GETSET_BINDABLE_ENUM_SETTING(NewTabPosition, Model::NewTabPosition, _GlobalSettings.NewTabPosition);
GETSET_BINDABLE_ENUM_SETTING(TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, _GlobalSettings.TabWidthMode);
GETSET_BINDABLE_ENUM_SETTING(NewTabPosition, Model::NewTabPosition, _windowSettings.NewTabPosition);
GETSET_BINDABLE_ENUM_SETTING(TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, _windowSettings.TabWidthMode);
public:
winrt::Windows::Foundation::IInspectable CurrentTheme();
@@ -31,19 +31,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void ShowTitlebarToggled(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, AlwaysShowTabs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ShowTabsFullscreen);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ShowTabsInTitlebar);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, UseAcrylicInTabRow);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ShowTitleInTitlebar);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, AlwaysOnTop);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, AutoHideWindow);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, AlwaysShowTabs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, ShowTabsFullscreen);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, ShowTabsInTitlebar);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, UseAcrylicInTabRow);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, ShowTitleInTitlebar);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, AlwaysOnTop);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, AutoHideWindow);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, AlwaysShowNotificationIcon);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, MinimizeToNotificationArea);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ShowAdminShield);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, EnableUnfocusedAcrylic);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, MinimizeToNotificationArea);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, ShowAdminShield);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, EnableUnfocusedAcrylic);
private:
Model::WindowSettings _windowSettings;
Model::GlobalAppSettings _GlobalSettings;
winrt::Windows::Foundation::IInspectable _currentTheme;

View File

@@ -9,7 +9,8 @@ namespace Microsoft.Terminal.Settings.Editor
{
runtimeclass GlobalAppearanceViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
GlobalAppearanceViewModel(Microsoft.Terminal.Settings.Model.GlobalAppSettings globalSettings);
GlobalAppearanceViewModel(Microsoft.Terminal.Settings.Model.GlobalAppSettings globalSettings,
Microsoft.Terminal.Settings.Model.WindowSettings windowSettings);
IInspectable CurrentTheme;
static String ThemeNameConverter(Microsoft.Terminal.Settings.Model.Theme theme);

View File

@@ -12,8 +12,9 @@ using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
InteractionViewModel::InteractionViewModel(Model::GlobalAppSettings globalSettings) :
_GlobalSettings{ globalSettings }
InteractionViewModel::InteractionViewModel(Model::GlobalAppSettings globalSettings, Model::WindowSettings windowSettings) :
_GlobalSettings{ globalSettings },
_windowSettings{ windowSettings }
{
INITIALIZE_BINDABLE_ENUM_SETTING(TabSwitcherMode, TabSwitcherMode, TabSwitcherMode, L"Globals_TabSwitcherMode", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(CopyFormat, CopyFormat, winrt::Microsoft::Terminal::Control::CopyFormat, L"Globals_CopyFormat", L"Content");

View File

@@ -12,32 +12,33 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
struct InteractionViewModel : InteractionViewModelT<InteractionViewModel>, ViewModelHelper<InteractionViewModel>
{
public:
InteractionViewModel(Model::GlobalAppSettings globalSettings);
InteractionViewModel(Model::GlobalAppSettings globalSettings, Model::WindowSettings windowSettings);
// DON'T YOU DARE ADD A `WINRT_CALLBACK(PropertyChanged` TO A CLASS DERIVED FROM ViewModelHelper. Do this instead:
using ViewModelHelper<InteractionViewModel>::PropertyChanged;
GETSET_BINDABLE_ENUM_SETTING(TabSwitcherMode, Model::TabSwitcherMode, _GlobalSettings.TabSwitcherMode);
GETSET_BINDABLE_ENUM_SETTING(CopyFormat, winrt::Microsoft::Terminal::Control::CopyFormat, _GlobalSettings.CopyFormatting);
GETSET_BINDABLE_ENUM_SETTING(TabSwitcherMode, Model::TabSwitcherMode, _windowSettings.TabSwitcherMode);
GETSET_BINDABLE_ENUM_SETTING(CopyFormat, winrt::Microsoft::Terminal::Control::CopyFormat, _windowSettings.CopyFormatting);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, CopyOnSelect);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, TrimBlockSelection);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, TrimPaste);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, SnapToGridOnResize);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, FocusFollowMouse);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ScrollToZoom);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ScrollToChangeOpacity);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, DetectURLs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, SearchWebDefaultQueryUrl);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WordDelimiters);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ConfirmCloseAllTabs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, CopyOnSelect);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, TrimBlockSelection);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, TrimPaste);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, SnapToGridOnResize);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, FocusFollowMouse);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, ScrollToZoom);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, ScrollToChangeOpacity);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, DetectURLs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, SearchWebDefaultQueryUrl);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, WordDelimiters);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, ConfirmCloseAllTabs);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, InputServiceWarning);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WarnAboutLargePaste);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, WarnAboutMultiLinePaste);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, WarnAboutLargePaste);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_windowSettings, WarnAboutMultiLinePaste);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, EnableColorSelection);
private:
Model::GlobalAppSettings _GlobalSettings;
Model::WindowSettings _windowSettings;
};
};

View File

@@ -9,7 +9,8 @@ namespace Microsoft.Terminal.Settings.Editor
{
runtimeclass InteractionViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
InteractionViewModel(Microsoft.Terminal.Settings.Model.GlobalAppSettings globalSettings);
InteractionViewModel(Microsoft.Terminal.Settings.Model.GlobalAppSettings globalSettings,
Microsoft.Terminal.Settings.Model.WindowSettings windowSettings);
IInspectable CurrentTabSwitcherMode;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TabSwitcherModeList { get; };

View File

@@ -231,7 +231,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
double LaunchViewModel::InitialPosX()
{
const auto x = _Settings.GlobalSettings().InitialPosition().X;
const auto x = _Settings.WindowSettingsDefaults().InitialPosition().X;
// If there's no value here, return NAN - XAML will ignore it and
// put the placeholder text in the box instead
const auto xCoord = x.try_as<int32_t>();
@@ -240,7 +240,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
double LaunchViewModel::InitialPosY()
{
const auto y = _Settings.GlobalSettings().InitialPosition().Y;
const auto y = _Settings.WindowSettingsDefaults().InitialPosition().Y;
// If there's no value here, return NAN - XAML will ignore it and
// put the placeholder text in the box instead
const auto yCoord = y.try_as<int32_t>();
@@ -255,8 +255,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
xCoordRef = gsl::narrow_cast<int32_t>(xCoord);
}
const LaunchPosition newPos{ xCoordRef, _Settings.GlobalSettings().InitialPosition().Y };
_Settings.GlobalSettings().InitialPosition(newPos);
const LaunchPosition newPos{ xCoordRef, _Settings.WindowSettingsDefaults().InitialPosition().Y };
_Settings.WindowSettingsDefaults().InitialPosition(newPos);
_NotifyChanges(L"LaunchParametersCurrentValue");
}
@@ -268,8 +268,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
yCoordRef = gsl::narrow_cast<int32_t>(yCoord);
}
const LaunchPosition newPos{ _Settings.GlobalSettings().InitialPosition().X, yCoordRef };
_Settings.GlobalSettings().InitialPosition(newPos);
const LaunchPosition newPos{ _Settings.WindowSettingsDefaults().InitialPosition().X, yCoordRef };
_Settings.WindowSettingsDefaults().InitialPosition(newPos);
_NotifyChanges(L"LaunchParametersCurrentValue");
}
@@ -291,7 +291,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
winrt::Windows::Foundation::IInspectable LaunchViewModel::CurrentLaunchMode()
{
return winrt::box_value<winrt::Microsoft::Terminal::Settings::Editor::EnumEntry>(_LaunchModeMap.Lookup(_Settings.GlobalSettings().LaunchMode()));
return winrt::box_value<winrt::Microsoft::Terminal::Settings::Editor::EnumEntry>(_LaunchModeMap.Lookup(_Settings.WindowSettingsDefaults().LaunchMode()));
}
void LaunchViewModel::CurrentLaunchMode(const winrt::Windows::Foundation::IInspectable& enumEntry)
@@ -299,7 +299,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (const auto ee = enumEntry.try_as<winrt::Microsoft::Terminal::Settings::Editor::EnumEntry>())
{
const auto setting = winrt::unbox_value<LaunchMode>(ee.EnumValue());
_Settings.GlobalSettings().LaunchMode(setting);
_Settings.WindowSettingsDefaults().LaunchMode(setting);
_NotifyChanges(L"LaunchParametersCurrentValue");
}
}
@@ -311,14 +311,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
winrt::Windows::Foundation::IInspectable LaunchViewModel::CurrentDefaultProfile()
{
const auto defaultProfileGuid{ _Settings.GlobalSettings().DefaultProfile() };
const auto defaultProfileGuid{ _Settings.WindowSettingsDefaults().DefaultProfile() };
return winrt::box_value(_Settings.FindProfile(defaultProfileGuid));
}
void LaunchViewModel::CurrentDefaultProfile(const IInspectable& value)
{
const auto profile{ winrt::unbox_value<Model::Profile>(value) };
_Settings.GlobalSettings().DefaultProfile(profile.Guid());
_Settings.WindowSettingsDefaults().DefaultProfile(profile.Guid());
}
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> LaunchViewModel::DefaultProfiles() const

View File

@@ -46,13 +46,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void CurrentLaunchMode(const winrt::Windows::Foundation::IInspectable& enumEntry);
winrt::Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Editor::EnumEntry> LaunchModeList();
GETSET_BINDABLE_ENUM_SETTING(DefaultInputScope, winrt::Microsoft::Terminal::Control::DefaultInputScope, _Settings.GlobalSettings().DefaultInputScope);
GETSET_BINDABLE_ENUM_SETTING(DefaultInputScope, winrt::Microsoft::Terminal::Control::DefaultInputScope, _Settings.WindowSettingsDefaults().DefaultInputScope);
GETSET_BINDABLE_ENUM_SETTING(FirstWindowPreference, Model::FirstWindowPreference, _Settings.GlobalSettings().FirstWindowPreference);
GETSET_BINDABLE_ENUM_SETTING(WindowingBehavior, Model::WindowingMode, _Settings.GlobalSettings().WindowingBehavior);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_Settings.GlobalSettings(), CenterOnLaunch);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_Settings.GlobalSettings(), InitialRows);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_Settings.GlobalSettings(), InitialCols);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_Settings.WindowSettingsDefaults(), CenterOnLaunch);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_Settings.WindowSettingsDefaults(), InitialRows);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_Settings.WindowSettingsDefaults(), InitialCols);
bool StartOnUserLoginAvailable();
safe_void_coroutine PrepareStartOnUserLoginSettings();

View File

@@ -540,7 +540,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else if (clickedItemTag == interactionTag)
{
contentFrame().Navigate(xaml_typename<Editor::Interaction>(), winrt::make<NavigateToPageArgs>(winrt::make<InteractionViewModel>(_settingsClone.GlobalSettings()), *this, elementToFocus));
contentFrame().Navigate(xaml_typename<Editor::Interaction>(), winrt::make<NavigateToPageArgs>(winrt::make<InteractionViewModel>(_settingsClone.GlobalSettings(), _settingsClone.WindowSettingsDefaults()), *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Interaction/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsNav().SelectedItem(InteractionNavItem());
@@ -656,7 +656,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else if (clickedItemTag == globalAppearanceTag)
{
contentFrame().Navigate(xaml_typename<Editor::GlobalAppearance>(), winrt::make<NavigateToPageArgs>(winrt::make<GlobalAppearanceViewModel>(_settingsClone.GlobalSettings()), *this, elementToFocus));
contentFrame().Navigate(xaml_typename<Editor::GlobalAppearance>(), winrt::make<NavigateToPageArgs>(winrt::make<GlobalAppearanceViewModel>(_settingsClone.GlobalSettings(), _settingsClone.WindowSettingsDefaults()), *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Appearance/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsNav().SelectedItem(AppearanceNavItem());
@@ -1189,10 +1189,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
const auto& theme = _settingsSource.GlobalSettings().CurrentTheme();
const bool hasThemeForSettings{ theme.Settings() != nullptr };
const auto& appTheme = theme.RequestedTheme();
const auto& requestedTheme = (hasThemeForSettings) ? theme.Settings().RequestedTheme() : appTheme;
const auto& theme = _settingsSource.GlobalSettings().CurrentTheme(_settingsSource.WindowSettingsDefaults());
const auto& requestedTheme = theme.RequestedTheme();
RequestedTheme(requestedTheme);
@@ -1203,7 +1201,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
//
// To mitigate this, don't set the transparent background in the case
// that our theme is different than the app's.
const bool actuallyUseMica = isMicaAvailable && (appTheme == requestedTheme);
const bool actuallyUseMica = isMicaAvailable;
const auto bgKey = (theme.Window() != nullptr && theme.Window().UseMica()) && actuallyUseMica ?
L"SettingsPageMicaBackground" :

View File

@@ -325,7 +325,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
SelectedProfile(AvailableProfiles().GetAt(0));
_rootEntries = _ConvertToViewModelEntries(_Settings.GlobalSettings().NewTabMenu(), _Settings);
_rootEntries = _ConvertToViewModelEntries(_Settings.WindowSettingsDefaults().NewTabMenu(), _Settings);
_rootEntriesChangedRevoker = _rootEntries.VectorChanged(winrt::auto_revoke, [this](auto&&, const IVectorChangedEventArgs& args) {
switch (args.CollectionChange())
{
@@ -337,25 +337,25 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
modelEntries.push_back(NewTabMenuEntryViewModel::GetModel(entry));
}
_Settings.GlobalSettings().NewTabMenu(single_threaded_vector<Model::NewTabMenuEntry>(std::move(modelEntries)));
_Settings.WindowSettingsDefaults().NewTabMenu(single_threaded_vector<Model::NewTabMenuEntry>(std::move(modelEntries)));
return;
}
case CollectionChange::ItemInserted:
{
const auto& insertedEntryVM = _rootEntries.GetAt(args.Index());
const auto& insertedEntry = NewTabMenuEntryViewModel::GetModel(insertedEntryVM);
_Settings.GlobalSettings().NewTabMenu().InsertAt(args.Index(), insertedEntry);
_Settings.WindowSettingsDefaults().NewTabMenu().InsertAt(args.Index(), insertedEntry);
return;
}
case CollectionChange::ItemRemoved:
{
_Settings.GlobalSettings().NewTabMenu().RemoveAt(args.Index());
_Settings.WindowSettingsDefaults().NewTabMenu().RemoveAt(args.Index());
return;
}
case CollectionChange::ItemChanged:
{
const auto& modifiedEntry = _rootEntries.GetAt(args.Index());
_Settings.GlobalSettings().NewTabMenu().SetAt(args.Index(), NewTabMenuEntryViewModel::GetModel(modifiedEntry));
_Settings.WindowSettingsDefaults().NewTabMenu().SetAt(args.Index(), NewTabMenuEntryViewModel::GetModel(modifiedEntry));
return;
}
}

View File

@@ -218,7 +218,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// This may look pricey, but it only resolves resources that have not been visited
// and the preview update is debounced.
_appSettings.ResolveMediaResources();
return *Settings::TerminalSettings::CreateForPreview(_appSettings, _profile);
return *Settings::TerminalSettings::CreateForPreview(_appSettings, _profile, _appSettings.WindowSettingsDefaults());
}
// Method Description:
@@ -385,7 +385,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::UI::Color ProfileViewModel::TabThemeColorPreview() const
{
const auto currentTheme = _appSettings.GlobalSettings().CurrentTheme();
const auto currentTheme = _appSettings.GlobalSettings().CurrentTheme(_appSettings.WindowSettingsDefaults());
if (const auto tabTheme = currentTheme.Tab())
{
// theme.tab.background: theme color must be evaluated

View File

@@ -13,9 +13,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
explicit RenderingViewModel(Model::CascadiaSettings settings) noexcept;
GETSET_BINDABLE_ENUM_SETTING(GraphicsAPI, winrt::Microsoft::Terminal::Control::GraphicsAPI, _settings.GlobalSettings().GraphicsAPI);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), DisablePartialInvalidation);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), SoftwareRendering);
GETSET_BINDABLE_ENUM_SETTING(GraphicsAPI, winrt::Microsoft::Terminal::Control::GraphicsAPI, _settings.WindowSettingsDefaults().GraphicsAPI);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.WindowSettingsDefaults(), DisablePartialInvalidation);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.WindowSettingsDefaults(), SoftwareRendering);
private:
Model::CascadiaSettings _settings{ nullptr };

View File

@@ -73,6 +73,9 @@ static constexpr std::string_view NewWindowKey{ "newWindow" };
static constexpr std::string_view IdentifyWindowKey{ "identifyWindow" };
static constexpr std::string_view IdentifyWindowsKey{ "identifyWindows" };
static constexpr std::string_view RenameWindowKey{ "renameWindow" };
static constexpr std::string_view OpenWorkspaceKey{ "openWorkspace" };
static constexpr std::string_view SaveWorkspaceKey{ "saveWorkspace" };
static constexpr std::string_view DeleteWorkspaceKey{ "deleteWorkspace" };
static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" };
static constexpr std::string_view DisplayWorkingDirectoryKey{ "debugTerminalCwd" };
static constexpr std::string_view SearchForTextKey{ "searchWeb" };

View File

@@ -40,6 +40,9 @@
#include "PrevTabArgs.g.cpp"
#include "NextTabArgs.g.cpp"
#include "RenameWindowArgs.g.cpp"
#include "OpenWorkspaceArgs.g.cpp"
#include "SaveWorkspaceArgs.g.cpp"
#include "DeleteWorkspaceArgs.g.cpp"
#include "SearchForTextArgs.g.cpp"
#include "GlobalSummonArgs.g.cpp"
#include "FocusPaneArgs.g.cpp"
@@ -795,6 +798,34 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return RS_switchable_(L"ResetWindowNameCommandKey");
}
winrt::hstring OpenWorkspaceArgs::GenerateName(const winrt::WARC::ResourceContext& /*context*/) const
{
// "Open workspace \"{_Name}\""
if (!Name().empty())
{
return winrt::hstring{ fmt::format(L"Open workspace \"{}\"", std::wstring_view{ Name() }) };
}
return winrt::hstring{ L"Open workspace" };
}
winrt::hstring SaveWorkspaceArgs::GenerateName(const winrt::WARC::ResourceContext& /*context*/) const
{
if (!Name().empty())
{
return winrt::hstring{ fmt::format(L"Save workspace \"{}\"", std::wstring_view{ Name() }) };
}
return winrt::hstring{ L"Save workspace" };
}
winrt::hstring DeleteWorkspaceArgs::GenerateName(const winrt::WARC::ResourceContext& /*context*/) const
{
if (!Name().empty())
{
return winrt::hstring{ fmt::format(L"Delete workspace \"{}\"", std::wstring_view{ Name() }) };
}
return winrt::hstring{ L"Delete workspace" };
}
winrt::hstring SearchForTextArgs::GenerateName(const winrt::WARC::ResourceContext& context) const
{
if (QueryUrl().empty())

View File

@@ -42,6 +42,9 @@
#include "PrevTabArgs.g.h"
#include "NextTabArgs.g.h"
#include "RenameWindowArgs.g.h"
#include "OpenWorkspaceArgs.g.h"
#include "SaveWorkspaceArgs.g.h"
#include "DeleteWorkspaceArgs.g.h"
#include "SearchForTextArgs.g.h"
#include "GlobalSummonArgs.g.h"
#include "FocusPaneArgs.g.h"
@@ -246,6 +249,18 @@ protected: \
#define RENAME_WINDOW_ARGS(X) \
X(winrt::hstring, Name, "name", false, ArgTypeHint::None, L"")
////////////////////////////////////////////////////////////////////////////////
#define OPEN_WORKSPACE_ARGS(X) \
X(winrt::hstring, Name, "name", false, ArgTypeHint::None, L"")
////////////////////////////////////////////////////////////////////////////////
#define SAVE_WORKSPACE_ARGS(X) \
X(winrt::hstring, Name, "name", false, ArgTypeHint::None, L"")
////////////////////////////////////////////////////////////////////////////////
#define DELETE_WORKSPACE_ARGS(X) \
X(winrt::hstring, Name, "name", false, ArgTypeHint::None, L"")
////////////////////////////////////////////////////////////////////////////////
#define SEARCH_FOR_TEXT_ARGS(X) \
X(winrt::hstring, QueryUrl, "queryUrl", false, ArgTypeHint::None, L"")
@@ -940,6 +955,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARGS_STRUCT(RenameWindowArgs, RENAME_WINDOW_ARGS);
ACTION_ARGS_STRUCT(OpenWorkspaceArgs, OPEN_WORKSPACE_ARGS);
ACTION_ARGS_STRUCT(SaveWorkspaceArgs, SAVE_WORKSPACE_ARGS);
ACTION_ARGS_STRUCT(DeleteWorkspaceArgs, DELETE_WORKSPACE_ARGS);
ACTION_ARGS_STRUCT(SearchForTextArgs, SEARCH_FOR_TEXT_ARGS);
struct GlobalSummonArgs : public GlobalSummonArgsT<GlobalSummonArgs>
@@ -1059,6 +1080,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(SetMaximizedArgs);
BASIC_FACTORY(SetColorSchemeArgs);
BASIC_FACTORY(RenameWindowArgs);
BASIC_FACTORY(OpenWorkspaceArgs);
BASIC_FACTORY(SaveWorkspaceArgs);
BASIC_FACTORY(DeleteWorkspaceArgs);
BASIC_FACTORY(ExecuteCommandlineArgs);
BASIC_FACTORY(CloseOtherTabsArgs);
BASIC_FACTORY(CloseTabsAfterArgs);

View File

@@ -420,6 +420,24 @@ namespace Microsoft.Terminal.Settings.Model
String Name { get; };
};
[default_interface] runtimeclass OpenWorkspaceArgs : IActionArgs, IActionArgsDescriptorAccess
{
OpenWorkspaceArgs(String name);
String Name { get; };
};
[default_interface] runtimeclass SaveWorkspaceArgs : IActionArgs, IActionArgsDescriptorAccess
{
SaveWorkspaceArgs(String name);
String Name { get; };
};
[default_interface] runtimeclass DeleteWorkspaceArgs : IActionArgs, IActionArgsDescriptorAccess
{
DeleteWorkspaceArgs(String name);
String Name { get; };
};
[default_interface] runtimeclass SearchForTextArgs : IActionArgs, IActionArgsDescriptorAccess
{
String QueryUrl { get; };

View File

@@ -113,7 +113,10 @@
ON_ALL_ACTIONS(OpenScratchpad) \
ON_ALL_ACTIONS(OpenAbout) \
ON_ALL_ACTIONS(QuickFix) \
ON_ALL_ACTIONS(OpenCWD)
ON_ALL_ACTIONS(OpenCWD) \
ON_ALL_ACTIONS(OpenWorkspace) \
ON_ALL_ACTIONS(SaveWorkspace) \
ON_ALL_ACTIONS(DeleteWorkspace)
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \
@@ -158,7 +161,10 @@
ON_ALL_ACTIONS_WITH_ARGS(Suggestions) \
ON_ALL_ACTIONS_WITH_ARGS(SelectCommand) \
ON_ALL_ACTIONS_WITH_ARGS(SelectOutput) \
ON_ALL_ACTIONS_WITH_ARGS(ColorSelection)
ON_ALL_ACTIONS_WITH_ARGS(ColorSelection) \
ON_ALL_ACTIONS_WITH_ARGS(OpenWorkspace) \
ON_ALL_ACTIONS_WITH_ARGS(SaveWorkspace) \
ON_ALL_ACTIONS_WITH_ARGS(DeleteWorkspace)
// These two macros here are for actions that we only use as internal currency.
// They don't need to be parsed by the settings model, or saved as actions to

View File

@@ -20,6 +20,7 @@ static constexpr std::string_view TabLayoutKey{ "tabLayout" };
static constexpr std::string_view InitialPositionKey{ "initialPosition" };
static constexpr std::string_view InitialSizeKey{ "initialSize" };
static constexpr std::string_view LaunchModeKey{ "launchMode" };
static constexpr std::string_view PersistedWorkspacesKey{ "persistedWorkspaces" };
namespace Microsoft::Terminal::Settings::Model::JsonUtils
{
@@ -276,6 +277,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
#undef MTSM_APPLICATION_STATE_GEN
// Manually handled because IMap<K,V> has a comma that breaks the X-macro.
if (WI_IsFlagSet(parseSource, FileSource::Shared))
state->PersistedWorkspaces = JsonUtils::GetValueForKey<std::optional<Windows::Foundation::Collections::IMap<hstring, Model::WindowLayout>>>(root, PersistedWorkspacesKey);
}
Json::Value ApplicationState::ToJson(FileSource parseSource) const noexcept
@@ -298,6 +303,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
#undef MTSM_APPLICATION_STATE_GEN
// Manually handled because IMap<K,V> has a comma that breaks the X-macro.
if (WI_IsFlagSet(parseSource, FileSource::Shared))
JsonUtils::SetValueForKey(root, PersistedWorkspacesKey, state->PersistedWorkspaces);
}
return root;
}
@@ -341,6 +350,51 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return false;
}
void ApplicationState::SaveWorkspace(const hstring& name, const Model::WindowLayout& layout)
{
{
const auto state = _state.lock();
if (!state->PersistedWorkspaces || !*state->PersistedWorkspaces)
{
state->PersistedWorkspaces = winrt::single_threaded_map<hstring, Model::WindowLayout>();
}
(*state->PersistedWorkspaces).Insert(name, layout);
}
_throttler();
}
bool ApplicationState::RemoveWorkspace(const hstring& name)
{
bool removed{ false };
{
const auto state = _state.lock();
if (state->PersistedWorkspaces && *state->PersistedWorkspaces)
{
auto map = *state->PersistedWorkspaces;
if (map.HasKey(name))
{
map.Remove(name);
removed = true;
}
}
}
if (removed)
{
_throttler();
}
return removed;
}
Windows::Foundation::Collections::IMapView<hstring, Model::WindowLayout> ApplicationState::AllPersistedWorkspaces()
{
const auto state = _state.lock_shared();
if (state->PersistedWorkspaces && *state->PersistedWorkspaces)
{
return (*state->PersistedWorkspaces).GetView();
}
return nullptr;
}
// Generate all getter/setters
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
type ApplicationState::name() const noexcept \

View File

@@ -75,6 +75,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
bool DismissBadge(const hstring& badgeId);
bool BadgeDismissed(const hstring& badgeId) const;
void SaveWorkspace(const hstring& name, const Model::WindowLayout& layout);
bool RemoveWorkspace(const hstring& name);
Windows::Foundation::Collections::IMapView<hstring, Model::WindowLayout> AllPersistedWorkspaces();
// State getters/setters
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
type name() const noexcept; \
@@ -88,6 +92,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) std::optional<type> name{ __VA_ARGS__ };
MTSM_APPLICATION_STATE_FIELDS(MTSM_APPLICATION_STATE_GEN)
#undef MTSM_APPLICATION_STATE_GEN
// Manually declared because IMap<K,V> has a comma that breaks the macro.
std::optional<Windows::Foundation::Collections::IMap<hstring, Model::WindowLayout>> PersistedWorkspaces;
};
til::shared_mutex<state_t> _state;
std::filesystem::path _sharedPath;

View File

@@ -36,6 +36,10 @@ namespace Microsoft.Terminal.Settings.Model
Boolean DismissBadge(String badgeId);
Boolean BadgeDismissed(String badgeId);
void SaveWorkspace(String name, WindowLayout layout);
Boolean RemoveWorkspace(String name);
Windows.Foundation.Collections.IMapView<String, WindowLayout> AllPersistedWorkspaces();
String SettingsHash;
Windows.Foundation.Collections.IVector<WindowLayout> PersistedWindowLayouts;
Windows.Foundation.Collections.IVector<String> RecentCommands;

View File

@@ -113,6 +113,7 @@ Model::CascadiaSettings CascadiaSettings::Copy() const
settings->_globals = _globals->Copy();
settings->_allProfiles = winrt::single_threaded_observable_vector(std::move(allProfiles));
settings->_activeProfiles = winrt::single_threaded_observable_vector(std::move(activeProfiles));
settings->_baseWindowSettings = _baseWindowSettings->Copy();
// extension packages don't need a deep clone
// because they're fully immutable. We can just copy the reference over instead.
@@ -220,6 +221,37 @@ Model::Profile CascadiaSettings::ProfileDefaults() const
return *_baseLayerProfile;
}
Model::WindowSettings CascadiaSettings::WindowSettingsDefaults() const
{
return *_baseWindowSettings;
}
Model::WindowSettings CascadiaSettings::WindowSettings(const winrt::hstring& windowName) const
{
if (const auto& forName = _windows.TryLookup(windowName))
{
return forName;
}
else if (windowName == L"_quake")
{
const auto& quakeSettings{ winrt::make_self<implementation::WindowSettings>() };
quakeSettings->AddLeastImportantParent(_baseWindowSettings);
_resolveDefaultProfileForWindow(*quakeSettings, _allProfiles.GetAt(0).Guid());
quakeSettings->InitializeForQuakeMode();
return *quakeSettings;
}
else
{
return *_baseWindowSettings;
}
}
winrt::Windows::Foundation::Collections::IMap<winrt::hstring, Model::WindowSettings> CascadiaSettings::AllWindowSettings() const noexcept
{
return _windows;
}
// Method Description:
// - Create a new profile based off the default profile settings.
// Arguments:
@@ -706,7 +738,7 @@ static bool _validateNTMEntries(const IVector<Model::NewTabMenuEntry>& entries)
void CascadiaSettings::_validateRegexes()
{
if (!_validateNTMEntries(_globals->NewTabMenu()))
if (!_validateNTMEntries(_baseWindowSettings->NewTabMenu()))
{
_warnings.Append(SettingsLoadWarnings::InvalidRegex);
}
@@ -728,7 +760,9 @@ void CascadiaSettings::_validateRegexes()
// and attempt to look the profile up by name instead.
// Return Value:
// - the GUID of the profile corresponding to this combination of index and NewTerminalArgs
Model::Profile CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs& newTerminalArgs) const
Model::Profile CascadiaSettings::GetProfileForArgs(
const Model::NewTerminalArgs& newTerminalArgs,
const Model::WindowSettings& currentWindowSettings) const
{
if (newTerminalArgs)
{
@@ -772,7 +806,7 @@ Model::Profile CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs&
// Case 2 above could be the result of a "nt" or "sp" invocation that doesn't specify anything.
// TODO GH#10952: Detect the profile based on the commandline (add matching support)
return (!newTerminalArgs || newTerminalArgs.Commandline().empty()) ?
FindProfile(GlobalSettings().DefaultProfile()) :
FindProfile(currentWindowSettings.DefaultProfile()) :
ProfileDefaults();
}
@@ -1232,10 +1266,10 @@ void CascadiaSettings::_validateThemeExists()
auto newTheme = winrt::make_self<Theme>();
newTheme->Name(L"system");
_globals->AddTheme(*newTheme);
_globals->Theme(Model::ThemePair{ L"system" });
_baseWindowSettings->Theme(Model::ThemePair{ L"system" });
}
const auto& theme{ _globals->Theme() };
const auto& theme{ _baseWindowSettings->Theme() };
if (theme.DarkName() == theme.LightName())
{
// Only one theme. We'll treat it as such.
@@ -1243,7 +1277,7 @@ void CascadiaSettings::_validateThemeExists()
{
_warnings.Append(SettingsLoadWarnings::UnknownTheme);
// safely fall back to system as the theme.
_globals->Theme(*winrt::make_self<ThemePair>(L"system"));
_baseWindowSettings->Theme(*winrt::make_self<ThemePair>(L"system"));
}
}
else

View File

@@ -69,8 +69,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
winrt::com_ptr<implementation::GlobalAppSettings> globals;
winrt::com_ptr<implementation::Profile> baseLayerProfile;
winrt::com_ptr<implementation::WindowSettings> baseWindowSettings;
std::vector<winrt::com_ptr<implementation::Profile>> profiles;
std::unordered_map<winrt::guid, winrt::com_ptr<implementation::Profile>> profilesByGuid;
std::unordered_map<winrt::hstring, winrt::com_ptr<implementation::WindowSettings>> windowsByName;
std::unordered_map<winrt::hstring, winrt::com_ptr<implementation::ColorScheme>> colorSchemes;
std::unordered_map<winrt::hstring, winrt::hstring> colorSchemeRemappings;
bool fixupsAppliedDuringLoad{ false };
@@ -111,6 +113,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
const Json::Value& profileDefaults;
const Json::Value& profilesList;
const Json::Value& themes;
const Json::Value& windowDefaults;
const Json::Value& windowsList;
};
struct ParseFragmentMetadata
{
@@ -163,6 +167,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
winrt::hstring Hash() const noexcept;
Model::CascadiaSettings Copy() const;
Model::GlobalAppSettings GlobalSettings() const;
Model::WindowSettings WindowSettingsDefaults() const;
Model::WindowSettings WindowSettings(const winrt::hstring& windowName) const;
Windows::Foundation::Collections::IMap<winrt::hstring, Model::WindowSettings> AllWindowSettings() const noexcept;
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> AllProfiles() const noexcept;
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> ActiveProfiles() const noexcept;
Model::ActionMap ActionMap() const noexcept;
@@ -175,7 +184,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Model::Profile CreateNewProfile();
Model::Profile FindProfile(const winrt::guid& guid) const noexcept;
void UpdateColorSchemeReferences(const winrt::hstring& oldName, const winrt::hstring& newName);
Model::Profile GetProfileForArgs(const Model::NewTerminalArgs& newTerminalArgs) const;
Model::Profile GetProfileForArgs(const Model::NewTerminalArgs& newTerminalArgs, const Model::WindowSettings& currentWindowSettings) const;
Model::Profile GetProfileByName(const winrt::hstring& name) const;
Model::Profile GetProfileByIndex(uint32_t index) const;
Model::Profile DuplicateProfile(const Model::Profile& source);
@@ -209,7 +218,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void _writeSettingsToDisk(std::string_view contents);
void _resolveDefaultProfile() const;
bool _resolveDefaultProfileForWindow(const Model::WindowSettings& window,
const winrt::guid firstProfileGuid) const;
void _resolveNewTabMenuProfiles() const;
void _resolveNewTabMenuProfilesForWindow(const Model::WindowSettings& window) const;
void _resolveNewTabMenuProfilesSet(const winrt::Windows::Foundation::Collections::IVector<Model::NewTabMenuEntry> entries, winrt::Windows::Foundation::Collections::IMap<int, Model::Profile>& remainingProfiles, Model::RemainingProfilesEntry& remainingProfilesEntry) const;
void _validateSettings();
@@ -228,6 +240,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// user settings
winrt::hstring _hash;
winrt::com_ptr<implementation::GlobalAppSettings> _globals = winrt::make_self<implementation::GlobalAppSettings>();
winrt::com_ptr<implementation::WindowSettings> _baseWindowSettings = winrt::make_self<implementation::WindowSettings>();
Windows::Foundation::Collections::IMap<winrt::hstring, Model::WindowSettings> _windows{ winrt::single_threaded_map<winrt::hstring, Model::WindowSettings>() };
winrt::com_ptr<implementation::Profile> _baseLayerProfile = winrt::make_self<implementation::Profile>();
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _allProfiles = winrt::single_threaded_observable_vector<Model::Profile>();
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _activeProfiles = winrt::single_threaded_observable_vector<Model::Profile>();

View File

@@ -36,7 +36,9 @@ namespace Microsoft.Terminal.Settings.Model
String Hash { get; };
GlobalAppSettings GlobalSettings { get; };
GlobalAppSettings GlobalSettings();
WindowSettings WindowSettings(String windowName);
WindowSettings WindowSettingsDefaults();
Profile ProfileDefaults { get; };
@@ -56,7 +58,7 @@ namespace Microsoft.Terminal.Settings.Model
Profile FindProfile(Guid profileGuid);
void UpdateColorSchemeReferences(String oldName, String newName);
Profile GetProfileForArgs(NewTerminalArgs newTerminalArgs);
Profile GetProfileForArgs(NewTerminalArgs newTerminalArgs, WindowSettings currentWindowSettings);
static Boolean IsDefaultTerminalAvailable { get; };
static Boolean IsDefaultTerminalSet { get; };

View File

@@ -39,6 +39,7 @@ static constexpr std::string_view DefaultSettingsKey{ "defaults" };
static constexpr std::string_view ProfilesListKey{ "list" };
static constexpr std::string_view SchemesKey{ "schemes" };
static constexpr std::string_view ThemesKey{ "themes" };
static constexpr std::string_view WindowsListKey{ "windows" };
constexpr std::wstring_view systemThemeName{ L"system" };
constexpr std::wstring_view darkThemeName{ L"dark" };
@@ -103,9 +104,11 @@ void ParsedSettings::clear()
{
globals = {};
baseLayerProfile = {};
baseWindowSettings = {};
profiles.clear();
profilesByGuid.clear();
colorSchemes.clear();
windowsByName.clear();
fixupsAppliedDuringLoad = false;
themesChangeLog.clear();
}
@@ -291,7 +294,7 @@ void SettingsLoader::ApplyRuntimeInitialSettings()
}
}
userSettings.globals->DefaultProfile(guid);
userSettings.baseWindowSettings->DefaultProfile(guid);
}
// 2.
@@ -470,6 +473,7 @@ void SettingsLoader::FinalizeLayering()
// Layer default globals -> user globals
userSettings.globals->AddLeastImportantParent(inboxSettings.globals);
userSettings.baseWindowSettings->AddLeastImportantParent(inboxSettings.baseWindowSettings);
// Actions are currently global, so if we want to conditionally light up a bunch of
// actions, this is the time to do it.
@@ -500,6 +504,13 @@ void SettingsLoader::FinalizeLayering()
profile->Hidden(profile->Hidden());
}
}
// Layer user window settings defaults -> user named windows
for (const auto& [_, window] : userSettings.windowsByName)
{
window->AddMostImportantParent(userSettings.baseWindowSettings);
window->_FinalizeInheritance();
}
}
// Let's say a user doesn't know that they need to write `"hidden": true` in
@@ -559,7 +570,7 @@ bool SettingsLoader::AddDynamicProfileFolders()
folderEntry->RawEntries(winrt::single_threaded_vector<Model::NewTabMenuEntry>({ *matchProfilesEntry }));
// NewTabMenu is guaranteed to exist by FixupUserSettings, which runs before this fixup.
userSettings.globals->NewTabMenu().Append(folderEntry.as<Model::NewTabMenuEntry>());
userSettings.baseWindowSettings->NewTabMenu().Append(folderEntry.as<Model::NewTabMenuEntry>());
state->SSHFolderGenerated(true);
return true;
}
@@ -693,9 +704,9 @@ bool SettingsLoader::FixupUserSettings()
// Ensure that the user always has a newTabMenu. We used to do this last, after
// resolving all of the new tab menu entries, but there was no conceivable reason
// that it should happen so late.
if (!userSettings.globals->HasNewTabMenu())
if (!userSettings.baseWindowSettings->HasNewTabMenu())
{
userSettings.globals->NewTabMenu(winrt::single_threaded_vector<Model::NewTabMenuEntry>({ Model::RemainingProfilesEntry{} }));
userSettings.baseWindowSettings->NewTabMenu(winrt::single_threaded_vector<Model::NewTabMenuEntry>({ Model::RemainingProfilesEntry{} }));
// This one does not need to be written back to the settings file immediately, it can wait until we write one for another reason.
}
@@ -818,6 +829,18 @@ void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source
}
}
{
settings.baseWindowSettings = WindowSettings::FromJson(json.root);
for (const auto& windowJson : json.windowsList)
{
if (auto window = WindowSettings::FromJson(windowJson))
{
settings.windowsByName.insert({ window->Name(), window });
}
}
}
{
for (const auto& themeJson : json.themes)
{
@@ -1014,10 +1037,15 @@ SettingsLoader::JsonSettings SettingsLoader::_parseJson(const std::string_view&
auto root = content.empty() ? Json::Value{ Json::ValueType::objectValue } : _parseJSON(content);
const auto& colorSchemes = _getJSONValue(root, SchemesKey);
const auto& themes = _getJSONValue(root, ThemesKey);
const auto& profilesObject = _getJSONValue(root, ProfilesKey);
const auto& profileDefaults = _getJSONValue(profilesObject, DefaultSettingsKey);
const auto& profilesList = profilesObject.isArray() ? profilesObject : _getJSONValue(profilesObject, ProfilesListKey);
return JsonSettings{ std::move(root), colorSchemes, profileDefaults, profilesList, themes };
const auto& windowDefaults = content.empty() ? Json::Value{ Json::ValueType::objectValue } : _parseJSON(content);
const auto& windowsList = _getJSONValue(root, WindowsListKey);
return JsonSettings{ std::move(root), colorSchemes, profileDefaults, profilesList, themes, windowDefaults, windowsList };
}
// Just a common helper function between _parse and _parseFragment.
@@ -1316,8 +1344,8 @@ void CascadiaSettings::_researchOnLoad()
{
// ----------------------------- RE: Themes ----------------------------
const auto numThemes = GlobalSettings().Themes().Size();
const auto themeInUse = GlobalSettings().CurrentTheme().Name();
const auto changedTheme = GlobalSettings().HasTheme();
const auto themeInUse = GlobalSettings().CurrentTheme(*_baseWindowSettings).Name();
const auto changedTheme = _baseWindowSettings->HasTheme();
// system: 0
// light: 1
@@ -1478,6 +1506,12 @@ CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) :
_warnings = winrt::single_threaded_vector(std::move(warnings));
_themesChangeLog = std::move(loader.userSettings.themesChangeLog);
_baseWindowSettings = loader.userSettings.baseWindowSettings;
for (const auto& [name, window] : loader.userSettings.windowsByName)
{
_windows.Insert(name, *window);
}
_resolveDefaultProfile();
_resolveNewTabMenuProfiles();
_validateSettings();
@@ -1643,6 +1677,15 @@ Json::Value CascadiaSettings::ToJson() const
{
// top-level json object
auto json{ _globals->ToJson() };
// Merge the base window settings into the root JSON.
// (Window-level settings are written to the top level, just like globals.)
const auto windowJson = _baseWindowSettings->ToJson();
for (const auto& key : windowJson.getMemberNames())
{
json[key] = windowJson[key];
}
json["$help"] = "https://aka.ms/terminal-documentation";
json["$schema"] =
#if defined(WT_BRANDING_RELEASE)
@@ -1697,27 +1740,63 @@ Json::Value CascadiaSettings::ToJson() const
}
json[JsonKey(ThemesKey)] = themes;
// Serialize named windows (if any)
if (_windows.Size() > 0)
{
Json::Value windowsList{ Json::ValueType::arrayValue };
for (const auto& [name, window] : _windows)
{
const auto windowImpl = winrt::get_self<implementation::WindowSettings>(window);
auto windowJson = windowImpl->ToJson();
windowJson["name"] = til::u16u8(name);
windowsList.append(std::move(windowJson));
}
json[JsonKey(WindowsListKey)] = windowsList;
}
return json;
}
// Method Description:
// - Resolves the "defaultProfile", which can be a profile name, to a GUID
// and stores it back to the globals.
// and stores it back to the window settings.
void CascadiaSettings::_resolveDefaultProfile() const
{
if (const auto unparsedDefaultProfile = _globals->UnparsedDefaultProfile(); !unparsedDefaultProfile.empty())
const auto firstProfileGuid = _allProfiles.GetAt(0).Guid();
// Resolve for the base window settings first.
_resolveDefaultProfileForWindow(*_baseWindowSettings, firstProfileGuid);
// Then resolve for each named window.
for (const auto& [_, window] : _windows)
{
_resolveDefaultProfileForWindow(window, firstProfileGuid);
}
}
// Method Description:
// - Resolves the "defaultProfile" for a single WindowSettings instance.
// Returns:
// - true if a warning was produced (missing default profile)
bool CascadiaSettings::_resolveDefaultProfileForWindow(const Model::WindowSettings& window,
const winrt::guid firstProfileGuid) const
{
const auto windowImpl = winrt::get_self<implementation::WindowSettings>(window);
if (const auto unparsedDefaultProfile = windowImpl->UnparsedDefaultProfile(); !unparsedDefaultProfile.empty())
{
if (const auto profile = GetProfileByName(unparsedDefaultProfile))
{
_globals->DefaultProfile(profile.Guid());
return;
windowImpl->DefaultProfile(profile.Guid());
return false;
}
_warnings.Append(SettingsLoadWarnings::MissingDefaultProfile);
return true;
}
// Use the first profile as the new default.
GlobalSettings().DefaultProfile(_allProfiles.GetAt(0).Guid());
windowImpl->DefaultProfile(firstProfileGuid);
return false;
}
// Method Description:
@@ -1729,6 +1808,22 @@ void CascadiaSettings::_resolveDefaultProfile() const
// multiple of these entries are found.
void CascadiaSettings::_resolveNewTabMenuProfiles() const
{
// Resolve for the base window settings first.
_resolveNewTabMenuProfilesForWindow(*_baseWindowSettings);
// Then resolve for each named window.
for (const auto& [_, window] : _windows)
{
_resolveNewTabMenuProfilesForWindow(window);
}
}
// Method Description:
// - Resolves the new tab menu profile entries for a single WindowSettings instance.
void CascadiaSettings::_resolveNewTabMenuProfilesForWindow(const Model::WindowSettings& window) const
{
const auto windowImpl = winrt::get_self<implementation::WindowSettings>(window);
Model::RemainingProfilesEntry remainingProfilesEntry = nullptr;
// The TerminalPage needs to know which profile has which profile ID. To prevent
@@ -1741,16 +1836,11 @@ void CascadiaSettings::_resolveNewTabMenuProfiles() const
remainingProfilesMap.emplace(profileIndex, _activeProfiles.GetAt(profileIndex));
}
// We keep track of the "remaining profiles" - those that have not yet been resolved
// in either a "profile" or "source" entry. They will possibly be assigned to a
// "remainingProfiles" entry
auto remainingProfiles = single_threaded_map(std::move(remainingProfilesMap));
// We call a recursive helper function to process the entries
auto entries = _globals->NewTabMenu();
auto entries = windowImpl->NewTabMenu();
_resolveNewTabMenuProfilesSet(entries, remainingProfiles, remainingProfilesEntry);
// If a "remainingProfiles" entry has been found, assign to it the remaining profiles
if (remainingProfilesEntry != nullptr)
{
remainingProfilesEntry.Profiles(remainingProfiles);

View File

@@ -59,9 +59,6 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
{
auto globals{ winrt::make_self<GlobalAppSettings>() };
globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile;
globals->_defaultProfile = _defaultProfile;
globals->_actionMap = _actionMap->Copy();
globals->_keybindingsWarnings = _keybindingsWarnings;
@@ -86,14 +83,6 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_themes.Insert(kv.Key(), *themeImpl->Copy());
}
}
if (_NewTabMenu)
{
globals->_NewTabMenu = winrt::single_threaded_vector<Model::NewTabMenuEntry>();
for (const auto& entry : *_NewTabMenu)
{
globals->_NewTabMenu->Append(get_self<NewTabMenuEntry>(entry)->Copy());
}
}
if (_DisabledProfileSources)
{
globals->_DisabledProfileSources = winrt::single_threaded_vector<hstring>();
@@ -117,17 +106,6 @@ winrt::Windows::Foundation::Collections::IMapView<winrt::hstring, winrt::Microso
#pragma region DefaultProfile
void GlobalAppSettings::DefaultProfile(const winrt::guid& defaultProfile) noexcept
{
_defaultProfile = defaultProfile;
_UnparsedDefaultProfile = Utils::GuidToString(defaultProfile);
}
winrt::guid GlobalAppSettings::DefaultProfile() const
{
return _defaultProfile;
}
#pragma endregion
winrt::Microsoft::Terminal::Settings::Model::ActionMap GlobalAppSettings::ActionMap() const noexcept
@@ -150,17 +128,7 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::FromJson(const Json::Value&
void GlobalAppSettings::LayerJson(const Json::Value& json, const OriginTag origin)
{
JsonUtils::GetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile);
// GH#8076 - when adding enum values to this key, we also changed it from
// "useTabSwitcher" to "tabSwitcherMode". Continue supporting
// "useTabSwitcher", but prefer "tabSwitcherMode"
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyUseTabSwitcherModeKey, _TabSwitcherMode) || _fixupsAppliedDuringLoad;
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyInputServiceWarningKey, _InputServiceWarning) || _fixupsAppliedDuringLoad;
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyWarnAboutLargePasteKey, _WarnAboutLargePaste) || _fixupsAppliedDuringLoad;
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyWarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste) || _fixupsAppliedDuringLoad;
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyConfirmCloseAllTabsKey, _ConfirmCloseAllTabs) || _fixupsAppliedDuringLoad;
#define GLOBAL_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \
JsonUtils::GetValueForKey(json, jsonKey, _##name); \
@@ -169,17 +137,6 @@ void GlobalAppSettings::LayerJson(const Json::Value& json, const OriginTag origi
MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_LAYER_JSON)
#undef GLOBAL_SETTINGS_LAYER_JSON
// GH#11975 We only want to allow sensible values and prevent crashes, so we are clamping those values
// We only want to assign if the value did change through clamping,
// otherwise we could end up setting defaults that get persisted
if (this->HasInitialCols())
{
this->InitialCols(std::clamp(this->InitialCols(), 1, 999));
}
if (this->HasInitialRows())
{
this->InitialRows(std::clamp(this->InitialRows(), 1, 999));
}
LayerActionsFrom(json, origin, true);
// No need to update _fixupsAppliedDuringLoad here.
@@ -203,20 +160,11 @@ void GlobalAppSettings::LayerJson(const Json::Value& json, const OriginTag origi
_fixupsAppliedDuringLoad |= firstWindowPreferenceValue == LegacyPersistedWindowLayout.data();
}
// Remove settings included in userDefaults
static constexpr std::array<std::pair<std::string_view, std::string_view>, 2> userDefaultSettings{ { { "copyOnSelect", "false" },
{ "copyFormatting", "false" } } };
for (const auto& [setting, val] : userDefaultSettings)
{
if (const auto settingJson{ json.find(&*setting.cbegin(), (&*setting.cbegin()) + setting.size()) })
{
if (settingJson->asString() == val)
{
// false positive!
_changeLog.erase(std::string{ setting });
}
}
}
// NOTE: "copyOnSelect" and "copyFormatting" used to be global settings.
// They were moved to WindowSettings and are now handled by
// WindowSettings::LayerJson. No cleanup is needed here because
// MTSM_GLOBAL_SETTINGS no longer includes them, so they never enter
// the changeLog in the first place.
}
void GlobalAppSettings::LayerActionsFrom(const Json::Value& json, const OriginTag origin, const bool withKeybindings)
@@ -301,35 +249,9 @@ const std::vector<winrt::Microsoft::Terminal::Settings::Model::SettingsLoadWarni
// - the JsonObject representing this instance
Json::Value GlobalAppSettings::ToJson()
{
// These experimental options should be removed from the settings file if they're at their default value.
// This prevents them from sticking around forever, even if the user was just experimenting with them.
// One could consider this a workaround for the settings UI right now not having a "reset to default" button for these.
if (_GraphicsAPI == Control::GraphicsAPI::Automatic)
{
_GraphicsAPI.reset();
}
if (_TextMeasurement == Control::TextMeasurement::Graphemes)
{
_TextMeasurement.reset();
}
if (_DefaultInputScope == Control::DefaultInputScope::Default)
{
_DefaultInputScope.reset();
}
if (_DisablePartialInvalidation == false)
{
_DisablePartialInvalidation.reset();
}
if (_SoftwareRendering == false)
{
_SoftwareRendering.reset();
}
Json::Value json{ Json::ValueType::objectValue };
JsonUtils::SetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile);
#define GLOBAL_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
JsonUtils::SetValueForKey(json, jsonKey, _##name);
MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_TO_JSON)
@@ -346,19 +268,20 @@ bool GlobalAppSettings::FixupsAppliedDuringLoad()
return _fixupsAppliedDuringLoad || _actionMap->FixupsAppliedDuringLoad();
}
winrt::Microsoft::Terminal::Settings::Model::Theme GlobalAppSettings::CurrentTheme() noexcept
winrt::Microsoft::Terminal::Settings::Model::Theme GlobalAppSettings::CurrentTheme(const Model::WindowSettings& window) noexcept
{
auto requestedTheme = Model::Theme::IsSystemInDarkTheme() ?
winrt::Windows::UI::Xaml::ElementTheme::Dark :
winrt::Windows::UI::Xaml::ElementTheme::Light;
const auto& themePair{ window.Theme() };
switch (requestedTheme)
{
case winrt::Windows::UI::Xaml::ElementTheme::Light:
return _themes.TryLookup(Theme().LightName());
return _themes.TryLookup(themePair.LightName());
case winrt::Windows::UI::Xaml::ElementTheme::Dark:
return _themes.TryLookup(Theme().DarkName());
return _themes.TryLookup(themePair.DarkName());
case winrt::Windows::UI::Xaml::ElementTheme::Default:
default:
@@ -390,16 +313,6 @@ bool GlobalAppSettings::ShouldUsePersistedLayout() const
void GlobalAppSettings::ResolveMediaResources(const Model::MediaResourceResolver& resolver)
{
_actionMap->ResolveMediaResourcesWithBasePath(SourceBasePath, resolver);
if (_NewTabMenu)
{
for (const auto& entry : *_NewTabMenu)
{
if (const auto resolvable{ entry.try_as<IPathlessMediaResourceContainer>() })
{
resolvable->ResolveMediaResourcesWithBasePath(SourceBasePath, resolver);
}
}
}
for (auto& parent : _parents)
{
parent->ResolveMediaResources(resolver);
@@ -408,115 +321,20 @@ void GlobalAppSettings::ResolveMediaResources(const Model::MediaResourceResolver
void GlobalAppSettings::_logSettingSet(const std::string_view& setting)
{
if (setting == "theme")
{
if (_Theme.has_value())
{
// ThemePair always has a Dark/Light value,
// so we need to check if they were explicitly set
if (_Theme->DarkName() == _Theme->LightName())
{
_changeLog.emplace(setting);
}
else
{
_changeLog.emplace(fmt::format(FMT_COMPILE("{}.{}"), setting, "dark"));
_changeLog.emplace(fmt::format(FMT_COMPILE("{}.{}"), setting, "light"));
}
}
}
else if (setting == "newTabMenu")
{
if (_NewTabMenu.has_value())
{
for (const auto& entry : *_NewTabMenu)
{
std::string entryType;
switch (entry.Type())
{
case NewTabMenuEntryType::Profile:
entryType = "profile";
break;
case NewTabMenuEntryType::Separator:
entryType = "separator";
break;
case NewTabMenuEntryType::Folder:
entryType = "folder";
break;
case NewTabMenuEntryType::RemainingProfiles:
entryType = "remainingProfiles";
break;
case NewTabMenuEntryType::MatchProfiles:
entryType = "matchProfiles";
break;
case NewTabMenuEntryType::Action:
entryType = "action";
break;
case NewTabMenuEntryType::Invalid:
// ignore invalid
continue;
}
_changeLog.emplace(fmt::format(FMT_COMPILE("{}.{}"), setting, entryType));
}
}
}
else
{
_changeLog.emplace(setting);
}
_changeLog.emplace(setting);
}
void GlobalAppSettings::UpdateCommandID(const Model::Command& cmd, winrt::hstring newID)
{
const auto oldID = cmd.ID();
_actionMap->UpdateCommandID(cmd, newID);
// newID might have been empty when this function was called, if so actionMap would have generated a new ID, use that
newID = cmd.ID();
if (_NewTabMenu)
{
// Recursive lambda function to look through all the new tab menu entries and update IDs accordingly
std::function<void(const Model::NewTabMenuEntry&)> recursiveEntryIdUpdate;
recursiveEntryIdUpdate = [&](const Model::NewTabMenuEntry& entry) {
if (entry.Type() == NewTabMenuEntryType::Action)
{
if (const auto actionEntry{ entry.try_as<ActionEntry>() })
{
if (actionEntry.ActionId() == oldID)
{
actionEntry.ActionId(newID);
}
}
}
else if (entry.Type() == NewTabMenuEntryType::Folder)
{
if (const auto folderEntry{ entry.try_as<FolderEntry>() })
{
for (const auto& nestedEntry : folderEntry.RawEntries())
{
recursiveEntryIdUpdate(nestedEntry);
}
}
}
};
for (const auto& entry : *_NewTabMenu)
{
recursiveEntryIdUpdate(entry);
}
}
}
void GlobalAppSettings::_logSettingIfSet(const std::string_view& setting, const bool isSet)
{
if (isSet)
{
// Exclude some false positives from userDefaults.json
const bool settingCopyFormattingToDefault = til::equals_insensitive_ascii(setting, "copyFormatting") && _CopyFormatting.has_value() && _CopyFormatting.value() == static_cast<Control::CopyFormat>(0);
const bool settingNTMToDefault = til::equals_insensitive_ascii(setting, "newTabMenu") && _NewTabMenu.has_value() && _NewTabMenu->Size() == 1 && _NewTabMenu->GetAt(0).Type() == NewTabMenuEntryType::RemainingProfiles;
if (!settingCopyFormattingToDefault && !settingNTMToDefault)
{
_logSettingSet(setting);
}
_logSettingSet(setting);
}
}

View File

@@ -25,6 +25,7 @@ Author(s):
#include "Theme.h"
#include "NewTabMenuEntry.h"
#include "RemainingProfilesEntry.h"
#include "WindowSettings.h"
// fwdecl unittest classes
namespace SettingsModelUnitTests
@@ -58,14 +59,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
const std::vector<SettingsLoadWarnings>& KeybindingsWarnings() const;
// This DefaultProfile() setter is called by CascadiaSettings,
// when it parses UnparsedDefaultProfile in _finalizeSettings().
void DefaultProfile(const guid& defaultProfile) noexcept;
guid DefaultProfile() const;
Windows::Foundation::Collections::IMapView<hstring, Model::Theme> Themes() noexcept;
void AddTheme(const Model::Theme& theme);
Model::Theme CurrentTheme() noexcept;
Model::Theme CurrentTheme(const Model::WindowSettings& window) noexcept;
bool ShouldUsePersistedLayout() const;
void ExpandCommands(const Windows::Foundation::Collections::IVectorView<Model::Profile>& profiles,
@@ -80,8 +78,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
winrt::hstring SourceBasePath;
INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, UnparsedDefaultProfile, L"");
#define GLOBAL_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \
INHERITABLE_SETTING_WITH_LOGGING(Model::GlobalAppSettings, type, name, jsonKey, ##__VA_ARGS__)
MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_INITIALIZE)
@@ -94,7 +90,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static constexpr bool debugFeaturesDefault{ true };
#endif
winrt::guid _defaultProfile{};
bool _fixupsAppliedDuringLoad{ false };
bool _legacyReloadEnvironmentVariables{ true };
bool _legacyForceVTInput{ false };

View File

@@ -50,62 +50,34 @@ namespace Microsoft.Terminal.Settings.Model
AfterCurrentTab,
};
enum DockPosition
{
None,
Top,
Bottom,
Left,
Right
};
[default_interface] runtimeclass Docking
{
Docking();
DockPosition Side { get; };
Double Width { get; };
Double Height { get; };
};
[default_interface] runtimeclass GlobalAppSettings {
Guid DefaultProfile;
INHERITABLE_SETTING(String, UnparsedDefaultProfile);
INHERITABLE_SETTING(Int32, InitialRows);
INHERITABLE_SETTING(Int32, InitialCols);
INHERITABLE_SETTING(Boolean, AlwaysShowTabs);
INHERITABLE_SETTING(Boolean, ShowTabsFullscreen);
INHERITABLE_SETTING(NewTabPosition, NewTabPosition);
INHERITABLE_SETTING(Boolean, ShowTitleInTitlebar);
INHERITABLE_SETTING(Boolean, ConfirmCloseAllTabs);
INHERITABLE_SETTING(String, Language);
INHERITABLE_SETTING(Microsoft.UI.Xaml.Controls.TabViewWidthMode, TabWidthMode);
INHERITABLE_SETTING(Boolean, UseAcrylicInTabRow);
INHERITABLE_SETTING(Boolean, ShowTabsInTitlebar);
INHERITABLE_SETTING(String, WordDelimiters);
INHERITABLE_SETTING(Boolean, CopyOnSelect);
INHERITABLE_SETTING(Boolean, InputServiceWarning);
INHERITABLE_SETTING(Microsoft.Terminal.Control.CopyFormat, CopyFormatting);
INHERITABLE_SETTING(Boolean, WarnAboutLargePaste);
INHERITABLE_SETTING(Microsoft.Terminal.Control.WarnAboutMultiLinePaste, WarnAboutMultiLinePaste);
INHERITABLE_SETTING(Boolean, TrimPaste);
INHERITABLE_SETTING(LaunchPosition, InitialPosition);
INHERITABLE_SETTING(Boolean, CenterOnLaunch);
INHERITABLE_SETTING(Microsoft.Terminal.Control.DefaultInputScope, DefaultInputScope);
INHERITABLE_SETTING(FirstWindowPreference, FirstWindowPreference);
INHERITABLE_SETTING(LaunchMode, LaunchMode);
INHERITABLE_SETTING(Boolean, SnapToGridOnResize);
INHERITABLE_SETTING(Microsoft.Terminal.Control.GraphicsAPI, GraphicsAPI);
INHERITABLE_SETTING(Boolean, DisablePartialInvalidation);
INHERITABLE_SETTING(Boolean, SoftwareRendering);
INHERITABLE_SETTING(Microsoft.Terminal.Control.TextMeasurement, TextMeasurement);
INHERITABLE_SETTING(Boolean, UseBackgroundImageForWindow);
INHERITABLE_SETTING(Boolean, DebugFeaturesEnabled);
INHERITABLE_SETTING(Boolean, AlwaysOnTop);
INHERITABLE_SETTING(Boolean, AutoHideWindow);
INHERITABLE_SETTING(TabSwitcherMode, TabSwitcherMode);
INHERITABLE_SETTING(Boolean, DisableAnimations);
INHERITABLE_SETTING(String, StartupActions);
INHERITABLE_SETTING(Boolean, FocusFollowMouse);
INHERITABLE_SETTING(Boolean, ScrollToZoom);
INHERITABLE_SETTING(Boolean, ScrollToChangeOpacity);
INHERITABLE_SETTING(WindowingMode, WindowingBehavior);
INHERITABLE_SETTING(Boolean, TrimBlockSelection);
INHERITABLE_SETTING(Boolean, DetectURLs);
INHERITABLE_SETTING(Boolean, MinimizeToNotificationArea);
INHERITABLE_SETTING(Boolean, AlwaysShowNotificationIcon);
INHERITABLE_SETTING(IVector<String>, DisabledProfileSources);
INHERITABLE_SETTING(Boolean, ShowAdminShield);
INHERITABLE_SETTING(IVector<NewTabMenuEntry>, NewTabMenu);
INHERITABLE_SETTING(Boolean, EnableColorSelection);
INHERITABLE_SETTING(Boolean, EnableShellCompletionMenu);
INHERITABLE_SETTING(Boolean, EnableUnfocusedAcrylic);
INHERITABLE_SETTING(Boolean, AllowHeadless);
INHERITABLE_SETTING(String, SearchWebDefaultQueryUrl);
INHERITABLE_SETTING(Boolean, EnableColorSelection);
Windows.Foundation.Collections.IMapView<String, ColorScheme> ColorSchemes();
void AddColorScheme(ColorScheme scheme);
@@ -116,9 +88,61 @@ namespace Microsoft.Terminal.Settings.Model
Windows.Foundation.Collections.IMapView<String, Theme> Themes();
void AddTheme(Theme theme);
INHERITABLE_SETTING(ThemePair, Theme);
Theme CurrentTheme { get; };
Theme CurrentTheme(WindowSettings window);
Boolean ShouldUsePersistedLayout();
}
[default_interface] runtimeclass WindowSettings {
String Name { get; };
Guid DefaultProfile;
INHERITABLE_SETTING(String, UnparsedDefaultProfile);
INHERITABLE_SETTING(Int32, InitialRows);
INHERITABLE_SETTING(Int32, InitialCols);
INHERITABLE_SETTING(Boolean, AlwaysShowTabs);
INHERITABLE_SETTING(Boolean, ShowTabsFullscreen);
INHERITABLE_SETTING(NewTabPosition, NewTabPosition);
INHERITABLE_SETTING(Boolean, ShowTitleInTitlebar);
INHERITABLE_SETTING(Boolean, ConfirmCloseAllTabs);
INHERITABLE_SETTING(Microsoft.UI.Xaml.Controls.TabViewWidthMode, TabWidthMode);
INHERITABLE_SETTING(Boolean, UseAcrylicInTabRow);
INHERITABLE_SETTING(Boolean, ShowTabsInTitlebar);
INHERITABLE_SETTING(String, WordDelimiters);
INHERITABLE_SETTING(Boolean, CopyOnSelect);
INHERITABLE_SETTING(Microsoft.Terminal.Control.CopyFormat, CopyFormatting);
INHERITABLE_SETTING(Boolean, WarnAboutLargePaste);
INHERITABLE_SETTING(Microsoft.Terminal.Control.WarnAboutMultiLinePaste, WarnAboutMultiLinePaste);
INHERITABLE_SETTING(Boolean, TrimPaste);
INHERITABLE_SETTING(LaunchPosition, InitialPosition);
INHERITABLE_SETTING(Boolean, CenterOnLaunch);
INHERITABLE_SETTING(Microsoft.Terminal.Control.DefaultInputScope, DefaultInputScope);
INHERITABLE_SETTING(LaunchMode, LaunchMode);
INHERITABLE_SETTING(Boolean, SnapToGridOnResize);
INHERITABLE_SETTING(Microsoft.Terminal.Control.GraphicsAPI, GraphicsAPI);
INHERITABLE_SETTING(Boolean, DisablePartialInvalidation);
INHERITABLE_SETTING(Boolean, SoftwareRendering);
INHERITABLE_SETTING(Microsoft.Terminal.Control.TextMeasurement, TextMeasurement);
INHERITABLE_SETTING(Boolean, UseBackgroundImageForWindow);
INHERITABLE_SETTING(Boolean, AlwaysOnTop);
INHERITABLE_SETTING(Boolean, AutoHideWindow);
INHERITABLE_SETTING(TabSwitcherMode, TabSwitcherMode);
INHERITABLE_SETTING(Boolean, DisableAnimations);
INHERITABLE_SETTING(String, StartupActions);
INHERITABLE_SETTING(Boolean, FocusFollowMouse);
INHERITABLE_SETTING(Boolean, ScrollToZoom);
INHERITABLE_SETTING(Boolean, ScrollToChangeOpacity);
INHERITABLE_SETTING(Boolean, TrimBlockSelection);
INHERITABLE_SETTING(Boolean, DetectURLs);
INHERITABLE_SETTING(Boolean, MinimizeToNotificationArea);
INHERITABLE_SETTING(Boolean, ShowAdminShield);
INHERITABLE_SETTING(IVector<NewTabMenuEntry>, NewTabMenu);
INHERITABLE_SETTING(Boolean, EnableShellCompletionMenu);
INHERITABLE_SETTING(Boolean, EnableUnfocusedAcrylic);
INHERITABLE_SETTING(String, SearchWebDefaultQueryUrl);
INHERITABLE_SETTING(ThemePair, Theme);
INHERITABLE_SETTING(Docking, DockWindow);
}
}

View File

@@ -17,6 +17,7 @@ Author(s):
#include <json/json.h>
#include "../types/inc/utils.hpp"
#include <til/winrt.h>
namespace winrt
{
@@ -59,6 +60,35 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
std::string TypeDescription() const { return "<unknown>"; }
};
// Specialization for til::property<T> so we can deserialize directly into properties
template<typename T>
struct ConversionTrait<til::property<T>>
{
til::property<T> FromJson(const Json::Value& json)
{
ConversionTrait<T> trait;
return til::property<T>{ trait.FromJson(json) };
}
bool CanConvert(const Json::Value& json)
{
ConversionTrait<T> trait;
return trait.CanConvert(json);
}
Json::Value ToJson(const til::property<T>& val)
{
ConversionTrait<T> trait;
return trait.ToJson(val());
}
std::string TypeDescription() const
{
ConversionTrait<T> trait;
return trait.TypeDescription();
}
};
namespace Detail
{
// Function Description:

View File

@@ -18,7 +18,22 @@ Author(s):
// Macro format (defaultArgs are optional):
// (type, name, jsonKey, defaultArgs)
#define MTSM_GLOBAL_SETTINGS(X) \
// These are truly global settings - they apply to the entire application,
// not to any specific window.
#define MTSM_GLOBAL_SETTINGS(X) \
X(hstring, Language, "language") \
X(bool, InputServiceWarning, "warning.inputService", true) \
X(Model::FirstWindowPreference, FirstWindowPreference, "firstWindowPreference", FirstWindowPreference::DefaultProfile) \
X(bool, DebugFeaturesEnabled, "debugFeatures", debugFeaturesDefault) \
X(Model::WindowingMode, WindowingBehavior, "windowingBehavior", Model::WindowingMode::UseNew) \
X(bool, AlwaysShowNotificationIcon, "alwaysShowNotificationIcon", false) \
X(winrt::Windows::Foundation::Collections::IVector<winrt::hstring>, DisabledProfileSources, "disabledProfileSources", nullptr) \
X(bool, AllowHeadless, "compatibility.allowHeadless", false) \
X(bool, EnableColorSelection, "experimental.enableColorSelection", false)
// These are per-window settings - different windows can have different values.
// The "base" window settings act as the defaults for all windows.
#define MTSM_WINDOW_SETTINGS(X) \
X(int32_t, InitialRows, "initialRows", 30) \
X(int32_t, InitialCols, "initialCols", 80) \
X(hstring, WordDelimiters, "wordDelimiters", DEFAULT_WORD_DELIMITERS) \
@@ -39,36 +54,28 @@ Author(s):
X(bool, ShowTitleInTitlebar, "showTerminalTitleInTitlebar", true) \
X(bool, ConfirmCloseAllTabs, "warning.confirmCloseAllTabs", true) \
X(Model::ThemePair, Theme, "theme") \
X(hstring, Language, "language") \
X(Model::Docking, DockWindow, "dockWindow", nullptr) \
X(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, "tabWidthMode", winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal) \
X(bool, UseAcrylicInTabRow, "useAcrylicInTabRow", false) \
X(bool, ShowTabsInTitlebar, "showTabsInTitlebar", true) \
X(bool, InputServiceWarning, "warning.inputService", true) \
X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, "copyFormatting", 0) \
X(bool, WarnAboutLargePaste, "warning.largePaste", true) \
X(winrt::Microsoft::Terminal::Control::WarnAboutMultiLinePaste, WarnAboutMultiLinePaste, "warning.multiLinePaste", winrt::Microsoft::Terminal::Control::WarnAboutMultiLinePaste::Automatic) \
X(Model::LaunchPosition, InitialPosition, "initialPosition", nullptr, nullptr) \
X(bool, CenterOnLaunch, "centerOnLaunch", false) \
X(Model::FirstWindowPreference, FirstWindowPreference, "firstWindowPreference", FirstWindowPreference::DefaultProfile) \
X(Model::LaunchMode, LaunchMode, "launchMode", LaunchMode::DefaultMode) \
X(bool, SnapToGridOnResize, "snapToGridOnResize", true) \
X(bool, DebugFeaturesEnabled, "debugFeatures", debugFeaturesDefault) \
X(bool, AlwaysOnTop, "alwaysOnTop", false) \
X(bool, AutoHideWindow, "autoHideWindow", false) \
X(bool, MinimizeToNotificationArea, "minimizeToNotificationArea", false) \
X(Model::TabSwitcherMode, TabSwitcherMode, "tabSwitcherMode", Model::TabSwitcherMode::InOrder) \
X(bool, DisableAnimations, "disableAnimations", false) \
X(hstring, StartupActions, "startupActions", L"") \
X(Model::WindowingMode, WindowingBehavior, "windowingBehavior", Model::WindowingMode::UseNew) \
X(bool, MinimizeToNotificationArea, "minimizeToNotificationArea", false) \
X(bool, AlwaysShowNotificationIcon, "alwaysShowNotificationIcon", false) \
X(winrt::Windows::Foundation::Collections::IVector<winrt::hstring>, DisabledProfileSources, "disabledProfileSources", nullptr) \
X(bool, ShowAdminShield, "showAdminShield", true) \
X(bool, TrimPaste, "trimPaste", true) \
X(bool, EnableColorSelection, "experimental.enableColorSelection", false) \
X(bool, EnableShellCompletionMenu, "experimental.enableShellCompletionMenu", false) \
X(bool, EnableUnfocusedAcrylic, "compatibility.enableUnfocusedAcrylic", true) \
X(winrt::Windows::Foundation::Collections::IVector<Model::NewTabMenuEntry>, NewTabMenu, "newTabMenu", winrt::single_threaded_vector<Model::NewTabMenuEntry>({ Model::RemainingProfilesEntry{} })) \
X(bool, AllowHeadless, "compatibility.allowHeadless", false) \
X(hstring, SearchWebDefaultQueryUrl, "searchWebDefaultQueryUrl", L"https://www.bing.com/search?q=%22%s%22") \
X(bool, ShowTabsFullscreen, "showTabsFullscreen", false)
@@ -158,7 +165,8 @@ Author(s):
X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Frame, "frame", nullptr) \
X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, UnfocusedFrame, "unfocusedFrame", nullptr) \
X(bool, RainbowFrame, "experimental.rainbowFrame", false) \
X(bool, UseMica, "useMica", false)
X(bool, UseMica, "useMica", false) \
X(bool, ShowWindowsButton, "showWindowsButton", true)
#define MTSM_THEME_SETTINGS_SETTINGS(X) \
X(winrt::Windows::UI::Xaml::ElementTheme, RequestedTheme, "theme", winrt::Windows::UI::Xaml::ElementTheme::Default)

View File

@@ -82,6 +82,9 @@
<ClInclude Include="GlobalAppSettings.h">
<DependentUpon>GlobalAppSettings.idl</DependentUpon>
</ClInclude>
<ClInclude Include="WindowSettings.h">
<DependentUpon>GlobalAppSettings.idl</DependentUpon>
</ClInclude>
<ClInclude Include="IInheritable.h" />
<ClInclude Include="MTSMSettings.h" />
<ClInclude Include="IDynamicProfileGenerator.h" />
@@ -161,6 +164,9 @@
<ClCompile Include="GlobalAppSettings.cpp">
<DependentUpon>GlobalAppSettings.idl</DependentUpon>
</ClCompile>
<ClCompile Include="WindowSettings.cpp">
<DependentUpon>GlobalAppSettings.idl</DependentUpon>
</ClCompile>
<ClCompile Include="KeyChordSerialization.cpp">
<DependentUpon>KeyChordSerialization.idl</DependentUpon>
</ClCompile>

View File

@@ -528,6 +528,17 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::WindowingMode)
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::DockPosition)
{
JSON_MAPPINGS(5) = {
pair_type{ "none", ValueType::None },
pair_type{ "top", ValueType::Top },
pair_type{ "bottom", ValueType::Bottom },
pair_type{ "left", ValueType::Left },
pair_type{ "right", ValueType::Right },
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::DesktopBehavior)
{
JSON_MAPPINGS(3) = {

View File

@@ -62,6 +62,7 @@ namespace Microsoft.Terminal.Settings.Model
Windows.UI.Xaml.ElementTheme RequestedTheme { get; };
Boolean UseMica { get; };
Boolean RainbowFrame { get; };
Boolean ShowWindowsButton { get; };
ThemeColor Frame { get; };
ThemeColor UnfocusedFrame { get; };
}

View File

@@ -0,0 +1,203 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "WindowSettings.h"
#include "../../types/inc/Utils.hpp"
#include "JsonUtils.h"
#include "KeyChordSerialization.h"
#include "WindowSettings.g.cpp"
#include "Docking.g.cpp"
#include "ProfileEntry.h"
#include "FolderEntry.h"
#include "MatchProfilesEntry.h"
using namespace ::Microsoft::Console;
using namespace ::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::Foundation::Collections;
static constexpr std::string_view NameKey{ "name" };
static constexpr std::string_view ThemeKey{ "theme" };
static constexpr std::string_view DefaultProfileKey{ "defaultProfile" };
static constexpr std::string_view LegacyUseTabSwitcherModeKey{ "useTabSwitcher" };
// Method Description:
// - Copies any extraneous data from the parent before completing a CreateChild call
void WindowSettings::_FinalizeInheritance()
{
for (const auto& parent : _parents)
{
parent;
}
}
winrt::com_ptr<WindowSettings> WindowSettings::Copy() const
{
auto globals{ winrt::make_self<WindowSettings>() };
globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile;
globals->_defaultProfile = _defaultProfile;
#define WINDOW_SETTINGS_COPY(type, name, jsonKey, ...) \
globals->_##name = _##name;
MTSM_WINDOW_SETTINGS(WINDOW_SETTINGS_COPY)
#undef WINDOW_SETTINGS_COPY
if (_NewTabMenu)
{
globals->_NewTabMenu = winrt::single_threaded_vector<Model::NewTabMenuEntry>();
for (const auto& entry : *_NewTabMenu)
{
globals->_NewTabMenu->Append(get_self<NewTabMenuEntry>(entry)->Copy());
}
}
for (const auto& parent : _parents)
{
globals->AddLeastImportantParent(parent->Copy());
}
return globals;
}
#pragma region DefaultProfile
void WindowSettings::DefaultProfile(const winrt::guid& defaultProfile) noexcept
{
_defaultProfile = defaultProfile;
_UnparsedDefaultProfile = Utils::GuidToString(defaultProfile);
}
winrt::guid WindowSettings::DefaultProfile() const
{
return _defaultProfile;
}
#pragma endregion
// Method Description:
// - Create a new instance of this class from a serialized JsonObject.
winrt::com_ptr<WindowSettings> WindowSettings::FromJson(const Json::Value& json)
{
auto result = winrt::make_self<WindowSettings>();
result->LayerJson(json);
return result;
}
void WindowSettings::LayerJson(const Json::Value& json)
{
JsonUtils::GetValueForKey(json, NameKey, Name);
// If the name is _quake, set up some default window settings:
if (Name() == L"_quake")
{
InitializeForQuakeMode();
}
JsonUtils::GetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile);
// GH#8076 - when adding enum values to this key, we also changed it from
// "useTabSwitcher" to "tabSwitcherMode". Continue supporting
// "useTabSwitcher", but prefer "tabSwitcherMode"
JsonUtils::GetValueForKey(json, LegacyUseTabSwitcherModeKey, _TabSwitcherMode);
#define WINDOW_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \
JsonUtils::GetValueForKey(json, jsonKey, _##name);
MTSM_WINDOW_SETTINGS(WINDOW_SETTINGS_LAYER_JSON)
#undef WINDOW_SETTINGS_LAYER_JSON
}
// Method Description:
// - Create a new serialized JsonObject from an instance of this class
Json::Value WindowSettings::ToJson()
{
// These experimental options should be removed from the settings file if they're at their default value.
// This prevents them from sticking around forever, even if the user was just experimenting with them.
// One could consider this a workaround for the settings UI right now not having a "reset to default" button for these.
if (_GraphicsAPI == winrt::Microsoft::Terminal::Control::GraphicsAPI::Automatic)
{
_GraphicsAPI.reset();
}
if (_TextMeasurement == winrt::Microsoft::Terminal::Control::TextMeasurement::Graphemes)
{
_TextMeasurement.reset();
}
if (_DefaultInputScope == winrt::Microsoft::Terminal::Control::DefaultInputScope::Default)
{
_DefaultInputScope.reset();
}
if (_DisablePartialInvalidation == false)
{
_DisablePartialInvalidation.reset();
}
if (_SoftwareRendering == false)
{
_SoftwareRendering.reset();
}
Json::Value json{ Json::ValueType::objectValue };
JsonUtils::SetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile);
#define WINDOW_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
JsonUtils::SetValueForKey(json, jsonKey, _##name);
MTSM_WINDOW_SETTINGS(WINDOW_SETTINGS_TO_JSON)
#undef WINDOW_SETTINGS_TO_JSON
return json;
}
// Set up anything that we need that's quake-mode specific.
void WindowSettings::InitializeForQuakeMode()
{
LaunchMode(LaunchMode::FocusMode);
auto dockSettings{ winrt::make_self<Docking>() };
dockSettings->Side(Model::DockPosition::Top);
dockSettings->Width(1.0);
dockSettings->Height(0.5);
_DockWindow = *dockSettings;
_MinimizeToNotificationArea = true;
}
static constexpr std::string_view SideKey{ "side" };
static constexpr std::string_view WidthKey{ "width" };
static constexpr std::string_view HeightKey{ "height" };
winrt::com_ptr<Docking> Docking::FromJson(const Json::Value& json)
{
auto result = winrt::make_self<Docking>();
if (json.isObject())
{
JsonUtils::GetValueForKey(json, SideKey, result->Side);
JsonUtils::GetValueForKey(json, WidthKey, result->Width);
JsonUtils::GetValueForKey(json, HeightKey, result->Height);
}
return result;
}
Json::Value Docking::ToJson() const
{
Json::Value json{ Json::ValueType::objectValue };
JsonUtils::SetValueForKey(json, SideKey, Side);
JsonUtils::SetValueForKey(json, WidthKey, Width);
JsonUtils::SetValueForKey(json, HeightKey, Height);
return json;
}
winrt::com_ptr<Docking> Docking::Copy() const
{
auto pair{ winrt::make_self<Docking>() };
pair->Side = Side;
pair->Width = Width;
pair->Height = Height;
return pair;
}

View File

@@ -0,0 +1,123 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- WindowSettings.h
Abstract:
- This class encapsulates all of the settings that are specific to a single
window. Broader than Profile settings (which are more like, per-pane
settings). Different windows can have different settings for things like
Theme, default profile, launch mode, etc.
Author(s):
- Mike Griese - Sept 2023
--*/
#pragma once
#include "WindowSettings.g.h"
#include "Docking.g.h"
#include "IInheritable.h"
#include "MTSMSettings.h"
#include "TerminalSettingsSerializationHelpers.h"
#include "ColorScheme.h"
#include "Theme.h"
#include "NewTabMenuEntry.h"
#include "RemainingProfilesEntry.h"
// fwdecl unittest classes
namespace SettingsModelUnitTests
{
class DeserializationTests;
class ColorSchemeTests;
};
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct WindowSettings : WindowSettingsT<WindowSettings>, IInheritable<WindowSettings>
{
public:
void _FinalizeInheritance() override;
com_ptr<WindowSettings> Copy() const;
static com_ptr<WindowSettings> FromJson(const Json::Value& json);
void LayerJson(const Json::Value& json);
Json::Value ToJson();
void InitializeForQuakeMode();
// This DefaultProfile() setter is called by CascadiaSettings,
// when it parses UnparsedDefaultProfile in _finalizeSettings().
void DefaultProfile(const guid& defaultProfile) noexcept;
guid DefaultProfile() const;
til::property<winrt::hstring> Name;
INHERITABLE_SETTING(Model::WindowSettings, hstring, UnparsedDefaultProfile, L"");
#define WINDOW_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \
INHERITABLE_SETTING(Model::WindowSettings, type, name, ##__VA_ARGS__)
MTSM_WINDOW_SETTINGS(WINDOW_SETTINGS_INITIALIZE)
#undef WINDOW_SETTINGS_INITIALIZE
private:
winrt::guid _defaultProfile;
};
struct Docking : DockingT<Docking>
{
Docking() = default;
til::property<Model::DockPosition> Side{ Model::DockPosition::None };
til::property<double> Width{ 1.0 };
til::property<double> Height{ 1.0 };
static com_ptr<Docking> FromJson(const Json::Value& json);
Json::Value ToJson() const;
com_ptr<Docking> Copy() const;
};
}
namespace Microsoft::Terminal::Settings::Model::JsonUtils
{
using namespace winrt::Microsoft::Terminal::Settings::Model;
template<>
struct ConversionTrait<Docking>
{
Docking FromJson(const Json::Value& json)
{
const auto entry = implementation::Docking::FromJson(json);
if (entry == nullptr)
{
return nullptr;
}
return *entry;
}
bool CanConvert(const Json::Value& json) const
{
return json.isObject();
}
Json::Value ToJson(const Docking& val)
{
return winrt::get_self<implementation::Docking>(val)->ToJson();
}
std::string TypeDescription() const
{
return "Docking";
}
};
}
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
{
BASIC_FACTORY(Docking);
}

View File

@@ -235,7 +235,7 @@ namespace SettingsModelUnitTests
const auto settings = createSettings(goodProfiles);
VERIFY_ARE_EQUAL(static_cast<size_t>(0), settings->Warnings().Size());
VERIFY_ARE_EQUAL(static_cast<size_t>(2), settings->AllProfiles().Size());
VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->AllProfiles().GetAt(0).Guid());
VERIFY_ARE_EQUAL(settings->WindowSettingsDefaults().DefaultProfile(), settings->AllProfiles().GetAt(0).Guid());
}
{
// Case 2: Bad settings
@@ -246,7 +246,7 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingDefaultProfile, settings->Warnings().GetAt(0));
VERIFY_ARE_EQUAL(static_cast<size_t>(2), settings->AllProfiles().Size());
VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->AllProfiles().GetAt(0).Guid());
VERIFY_ARE_EQUAL(settings->WindowSettingsDefaults().DefaultProfile(), settings->AllProfiles().GetAt(0).Guid());
}
{
// Case 2: Bad settings
@@ -257,7 +257,7 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingDefaultProfile, settings->Warnings().GetAt(0));
VERIFY_ARE_EQUAL(static_cast<size_t>(2), settings->AllProfiles().Size());
VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->AllProfiles().GetAt(0).Guid());
VERIFY_ARE_EQUAL(settings->WindowSettingsDefaults().DefaultProfile(), settings->AllProfiles().GetAt(0).Guid());
}
{
// Case 4: Good settings, default profile is a string
@@ -266,7 +266,7 @@ namespace SettingsModelUnitTests
const auto settings = createSettings(goodProfilesSpecifiedByName);
VERIFY_ARE_EQUAL(static_cast<size_t>(0), settings->Warnings().Size());
VERIFY_ARE_EQUAL(static_cast<size_t>(2), settings->AllProfiles().Size());
VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->AllProfiles().GetAt(1).Guid());
VERIFY_ARE_EQUAL(settings->WindowSettingsDefaults().DefaultProfile(), settings->AllProfiles().GetAt(1).Guid());
}
}
@@ -354,7 +354,7 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(SettingsLoadWarnings::MissingDefaultProfile, settings->Warnings().GetAt(1));
VERIFY_ARE_EQUAL(3u, settings->AllProfiles().Size());
VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(0).Guid(), settings->GlobalSettings().DefaultProfile());
VERIFY_ARE_EQUAL(settings->AllProfiles().GetAt(0).Guid(), settings->WindowSettingsDefaults().DefaultProfile());
}
void DeserializationTests::LayerGlobalProperties()
@@ -376,10 +376,10 @@ namespace SettingsModelUnitTests
})" };
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userSettings, inboxSettings);
VERIFY_ARE_EQUAL(true, settings->GlobalSettings().AlwaysShowTabs());
VERIFY_ARE_EQUAL(240, settings->GlobalSettings().InitialCols());
VERIFY_ARE_EQUAL(60, settings->GlobalSettings().InitialRows());
VERIFY_ARE_EQUAL(false, settings->GlobalSettings().ShowTabsInTitlebar());
VERIFY_ARE_EQUAL(true, settings->WindowSettingsDefaults().AlwaysShowTabs());
VERIFY_ARE_EQUAL(240, settings->WindowSettingsDefaults().InitialCols());
VERIFY_ARE_EQUAL(60, settings->WindowSettingsDefaults().InitialRows());
VERIFY_ARE_EQUAL(false, settings->WindowSettingsDefaults().ShowTabsInTitlebar());
}
void DeserializationTests::ValidateProfileOrdering()
@@ -989,7 +989,7 @@ namespace SettingsModelUnitTests
VERIFY_IS_NOT_NULL(settings->ProfileDefaults());
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", settings->GlobalSettings().UnparsedDefaultProfile());
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", settings->WindowSettingsDefaults().UnparsedDefaultProfile());
VERIFY_ARE_EQUAL(2u, settings->AllProfiles().Size());
VERIFY_ARE_EQUAL(2345, settings->AllProfiles().GetAt(0).HistorySize());
@@ -1027,7 +1027,7 @@ namespace SettingsModelUnitTests
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settings0String, implementation::LoadStringResource(IDR_DEFAULTS));
VERIFY_ARE_EQUAL(guid1String, settings->GlobalSettings().UnparsedDefaultProfile());
VERIFY_ARE_EQUAL(guid1String, settings->WindowSettingsDefaults().UnparsedDefaultProfile());
VERIFY_ARE_EQUAL(4u, settings->AllProfiles().Size());
VERIFY_ARE_EQUAL(guid1, settings->AllProfiles().GetAt(0).Guid());
VERIFY_ARE_NOT_EQUAL(guid1, settings->AllProfiles().GetAt(1).Guid());
@@ -1273,8 +1273,8 @@ namespace SettingsModelUnitTests
const auto settings = createSettings(inputSettings);
VERIFY_ARE_EQUAL(999, settings->GlobalSettings().InitialCols());
VERIFY_ARE_EQUAL(1, settings->GlobalSettings().InitialRows());
VERIFY_ARE_EQUAL(999, settings->WindowSettingsDefaults().InitialCols());
VERIFY_ARE_EQUAL(1, settings->WindowSettingsDefaults().InitialRows());
}
void DeserializationTests::TestTrailingCommas()
@@ -1757,7 +1757,7 @@ namespace SettingsModelUnitTests
const auto copyImpl{ winrt::get_self<implementation::CascadiaSettings>(copy) };
// test globals
VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), copyImpl->GlobalSettings().DefaultProfile());
VERIFY_ARE_EQUAL(settings->WindowSettingsDefaults().DefaultProfile(), copyImpl->WindowSettingsDefaults().DefaultProfile());
// test profiles
VERIFY_ARE_EQUAL(settings->AllProfiles().Size(), copyImpl->AllProfiles().Size());
@@ -1775,9 +1775,9 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(nameMapOriginal.Size(), nameMapCopy.Size());
// Test that changing the copy should not change the original
VERIFY_ARE_EQUAL(settings->GlobalSettings().WordDelimiters(), copyImpl->GlobalSettings().WordDelimiters());
copyImpl->GlobalSettings().WordDelimiters(L"changed value");
VERIFY_ARE_NOT_EQUAL(settings->GlobalSettings().WordDelimiters(), copyImpl->GlobalSettings().WordDelimiters());
VERIFY_ARE_EQUAL(settings->WindowSettingsDefaults().WordDelimiters(), copyImpl->WindowSettingsDefaults().WordDelimiters());
copyImpl->WindowSettingsDefaults().WordDelimiters(L"changed value");
VERIFY_ARE_NOT_EQUAL(settings->WindowSettingsDefaults().WordDelimiters(), copyImpl->WindowSettingsDefaults().WordDelimiters());
}
void DeserializationTests::TestCloneInheritanceTree()
@@ -1813,7 +1813,7 @@ namespace SettingsModelUnitTests
const auto copyImpl{ winrt::get_self<implementation::CascadiaSettings>(copy) };
// test globals
VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), copyImpl->GlobalSettings().DefaultProfile());
VERIFY_ARE_EQUAL(settings->WindowSettingsDefaults().DefaultProfile(), copyImpl->WindowSettingsDefaults().DefaultProfile());
// test profiles
VERIFY_ARE_EQUAL(settings->AllProfiles().Size(), copyImpl->AllProfiles().Size());

View File

@@ -40,7 +40,7 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(0u, settings->Warnings().Size());
const auto& entries = settings->GlobalSettings().NewTabMenu();
const auto& entries = settings->WindowSettingsDefaults().NewTabMenu();
VERIFY_ARE_EQUAL(1u, entries.Size());
VERIFY_ARE_EQUAL(winrt::Microsoft::Terminal::Settings::Model::NewTabMenuEntryType::RemainingProfiles, entries.GetAt(0).Type());
}
@@ -74,7 +74,7 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(0u, settings->Warnings().Size());
const auto& entries = settings->GlobalSettings().NewTabMenu();
const auto& entries = settings->WindowSettingsDefaults().NewTabMenu();
VERIFY_ARE_EQUAL(1u, entries.Size());
}
catch (const SettingsException& ex)

View File

@@ -45,6 +45,7 @@
<ClCompile Include="TerminalSettingsTests.cpp" />
<ClCompile Include="ThemeTests.cpp" />
<ClCompile Include="MediaResourceTests.cpp" />
<ClCompile Include="WindowSettingsTests.cpp" />
<ClCompile Include="../TerminalSettingsAppAdapterLib/TerminalSettings.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>

View File

@@ -244,7 +244,7 @@ namespace SettingsModelUnitTests
NewTerminalArgs args;
args.Commandline(testCase.input);
const auto profile = settings->GetProfileForArgs(args);
const auto profile = settings->GetProfileForArgs(args, settings->WindowSettingsDefaults());
VERIFY_IS_NOT_NULL(profile);
if (testCase.expected < 0)
@@ -332,8 +332,8 @@ namespace SettingsModelUnitTests
VERIFY_IS_TRUE(terminalArgs.TabTitle().empty());
VERIFY_IS_TRUE(terminalArgs.Profile().empty());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline());
@@ -356,8 +356,8 @@ namespace SettingsModelUnitTests
VERIFY_IS_FALSE(terminalArgs.Profile().empty());
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", terminalArgs.Profile());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings->Commandline());
@@ -380,8 +380,8 @@ namespace SettingsModelUnitTests
VERIFY_IS_FALSE(terminalArgs.Profile().empty());
VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings->Commandline());
@@ -404,8 +404,8 @@ namespace SettingsModelUnitTests
VERIFY_IS_FALSE(terminalArgs.Profile().empty());
VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline());
@@ -428,8 +428,8 @@ namespace SettingsModelUnitTests
VERIFY_IS_TRUE(terminalArgs.Profile().empty());
VERIFY_ARE_EQUAL(L"foo.exe", terminalArgs.Commandline());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
// This action specified a command but no profile; it gets reassigned to the base profile
VERIFY_ARE_EQUAL(settings->ProfileDefaults(), profile);
@@ -454,8 +454,8 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile());
VERIFY_ARE_EQUAL(L"foo.exe", terminalArgs.Commandline());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"foo.exe", termSettings->Commandline());
@@ -476,8 +476,8 @@ namespace SettingsModelUnitTests
VERIFY_IS_TRUE(terminalArgs.TabTitle().empty());
VERIFY_IS_TRUE(terminalArgs.Profile().empty());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline());
@@ -499,8 +499,8 @@ namespace SettingsModelUnitTests
VERIFY_IS_TRUE(terminalArgs.Profile().empty());
VERIFY_ARE_EQUAL(L"c:\\foo", terminalArgs.StartingDirectory());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline());
@@ -524,8 +524,8 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(L"c:\\foo", terminalArgs.StartingDirectory());
VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline());
@@ -548,8 +548,8 @@ namespace SettingsModelUnitTests
VERIFY_IS_TRUE(terminalArgs.Profile().empty());
VERIFY_ARE_EQUAL(L"bar", terminalArgs.TabTitle());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings->Commandline());
@@ -573,8 +573,8 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(L"bar", terminalArgs.TabTitle());
VERIFY_ARE_EQUAL(L"profile2", terminalArgs.Profile());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings->Commandline());
@@ -600,8 +600,8 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(L"bar", terminalArgs.TabTitle());
VERIFY_ARE_EQUAL(L"profile1", terminalArgs.Profile());
const auto profile{ settings->GetProfileForArgs(terminalArgs) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs) };
const auto profile{ settings->GetProfileForArgs(terminalArgs, settings->WindowSettingsDefaults()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, terminalArgs, settings->WindowSettingsDefaults()) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"foo.exe", termSettings->Commandline());
@@ -640,7 +640,7 @@ namespace SettingsModelUnitTests
try
{
auto settingsStruct{ TerminalSettings::CreateWithProfile(*settings, profile1) };
auto settingsStruct{ TerminalSettings::CreateWithProfile(*settings, profile1, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(1, settingsStruct.DefaultSettings()->HistorySize());
}
catch (...)
@@ -650,7 +650,7 @@ namespace SettingsModelUnitTests
try
{
auto settingsStruct{ TerminalSettings::CreateWithProfile(*settings, profile2) };
auto settingsStruct{ TerminalSettings::CreateWithProfile(*settings, profile2, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(2, settingsStruct.DefaultSettings()->HistorySize());
}
catch (...)
@@ -660,7 +660,7 @@ namespace SettingsModelUnitTests
try
{
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(1, settingsStruct.DefaultSettings()->HistorySize());
}
catch (...)
@@ -695,10 +695,10 @@ namespace SettingsModelUnitTests
VERIFY_ARE_EQUAL(2u, settings->Warnings().Size());
VERIFY_ARE_EQUAL(2u, settings->ActiveProfiles().Size());
VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->ActiveProfiles().GetAt(0).Guid());
VERIFY_ARE_EQUAL(settings->WindowSettingsDefaults().DefaultProfile(), settings->ActiveProfiles().GetAt(0).Guid());
try
{
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(1, settingsStruct.DefaultSettings()->HistorySize());
}
catch (...)
@@ -799,7 +799,7 @@ namespace SettingsModelUnitTests
const auto activeProfiles = settings->ActiveProfiles();
const auto colorSchemes = settings->GlobalSettings().ColorSchemes();
const auto currentTheme = settings->GlobalSettings().CurrentTheme();
const auto currentTheme = settings->GlobalSettings().CurrentTheme(settings->WindowSettingsDefaults());
const auto terminalSettings0 = createTerminalSettings(activeProfiles.GetAt(0), colorSchemes, currentTheme);
const auto terminalSettings1 = createTerminalSettings(activeProfiles.GetAt(1), colorSchemes, currentTheme);
const auto terminalSettings2 = createTerminalSettings(activeProfiles.GetAt(2), colorSchemes, currentTheme);
@@ -838,58 +838,58 @@ namespace SettingsModelUnitTests
{ // just a profile (profile wins)
NewTerminalArgs args{};
args.Profile(L"profile0");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings()->StartingTitle());
}
{ // profile and command line -> no promotion (profile wins)
NewTerminalArgs args{};
args.Profile(L"profile0");
args.Commandline(L"foo.exe");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings()->StartingTitle());
}
{ // just a title -> it is propagated
NewTerminalArgs args{};
args.TabTitle(L"Analog Kid");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(L"Analog Kid", settingsStruct.DefaultSettings()->StartingTitle());
}
{ // title and command line -> no promotion
NewTerminalArgs args{};
args.TabTitle(L"Digital Man");
args.Commandline(L"foo.exe");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(L"Digital Man", settingsStruct.DefaultSettings()->StartingTitle());
}
{ // just a commandline -> promotion
NewTerminalArgs args{};
args.Commandline(L"foo.exe");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings()->StartingTitle());
}
// various typesof commandline follow
{
NewTerminalArgs args{};
args.Commandline(L"foo.exe bar");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings()->StartingTitle());
}
{
NewTerminalArgs args{};
args.Commandline(L"\"foo exe.exe\" bar");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(L"foo exe.exe", settingsStruct.DefaultSettings()->StartingTitle());
}
{
NewTerminalArgs args{};
args.Commandline(L"\"\" grand designs");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings()->StartingTitle());
}
{
NewTerminalArgs args{};
args.Commandline(L" imagine a man");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, settings->WindowSettingsDefaults()) };
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings()->StartingTitle());
}
}

View File

@@ -248,7 +248,7 @@ namespace SettingsModelUnitTests
VERIFY_IS_NULL(bar.TabRow().Background());
}
const auto currentTheme{ settings->GlobalSettings().CurrentTheme() };
const auto currentTheme{ settings->GlobalSettings().CurrentTheme(settings->WindowSettingsDefaults()) };
VERIFY_IS_NOT_NULL(currentTheme);
VERIFY_ARE_EQUAL(L"system", currentTheme.Name());
}

View File

@@ -0,0 +1,541 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "../TerminalSettingsModel/CascadiaSettings.h"
#include "../TerminalSettingsModel/WindowSettings.h"
#include "../TerminalSettingsModel/resource.h"
#include "JsonTestClass.h"
#include "TestUtils.h"
using namespace Microsoft::Console;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace WEX::Common;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Control;
namespace SettingsModelUnitTests
{
class WindowSettingsTests : public JsonTestClass
{
TEST_CLASS(WindowSettingsTests);
// === Deserialization tests ===
TEST_METHOD(DefaultQuakeWindowSettings);
TEST_METHOD(LayeredWindowSettings);
TEST_METHOD(LayeredOnDefaultWindowSettings);
TEST_METHOD(LayeredQuakeWindowSettings);
TEST_METHOD(TestGeneratedQuakeWindowSettings);
// === Edge-case tests ===
TEST_METHOD(NoWindowsDefined);
TEST_METHOD(LookupUnknownWindowName);
TEST_METHOD(InheritedSettingsFromDefaults);
TEST_METHOD(NamedWindowOverridesDefaults);
TEST_METHOD(PartialNamedWindowInheritsRest);
TEST_METHOD(MultipleNamedWindows);
// === Serialization roundtrip ===
TEST_METHOD(RoundtripWindowSettings);
TEST_METHOD(RoundtripNamedWindows);
private:
// Helper: build a CascadiaSettings from a user-settings JSON string,
// with a minimal inbox that provides at least one profile and the
// Campbell colour scheme.
static winrt::com_ptr<implementation::CascadiaSettings> createSettings(const std::string_view& userJSON)
{
static constexpr std::string_view inboxJSON{ R"({
"profiles": [
{
"name": "Default",
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"commandline": "cmd.exe"
}
],
"schemes": [
{
"name": "Campbell",
"foreground": "#CCCCCC",
"background": "#0C0C0C",
"cursorColor": "#FFFFFF",
"black": "#0C0C0C",
"red": "#C50F1F",
"green": "#13A10E",
"yellow": "#C19C00",
"blue": "#0037DA",
"purple": "#881798",
"cyan": "#3A96DD",
"white": "#CCCCCC",
"brightBlack": "#767676",
"brightRed": "#E74856",
"brightGreen": "#16C60C",
"brightYellow": "#F9F1A5",
"brightBlue": "#3B78FF",
"brightPurple": "#B4009E",
"brightCyan": "#61D6D6",
"brightWhite": "#F2F2F2"
}
]
})" };
return winrt::make_self<implementation::CascadiaSettings>(userJSON, inboxJSON);
}
};
// ===================================================================
// Deserialization tests
// ===================================================================
// Verify that asking for the _quake window when no explicit quake
// settings are defined still produces reasonable quake defaults.
void WindowSettingsTests::DefaultQuakeWindowSettings()
{
Log::Comment(L"When no windows are defined, requesting '_quake' "
L"should return quake-mode defaults (focus mode, dock top, etc.).");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
]
})" };
const auto settings = createSettings(settingsJson);
// The _quake window is synthesised on-the-fly when not explicitly
// defined in the "windows" array.
const auto quake = settings->WindowSettings(L"_quake");
VERIFY_ARE_EQUAL(LaunchMode::FocusMode, quake.LaunchMode());
VERIFY_IS_NOT_NULL(quake.DockWindow());
VERIFY_ARE_EQUAL(DockPosition::Top, quake.DockWindow().Side());
VERIFY_ARE_EQUAL(1.0, quake.DockWindow().Width());
VERIFY_ARE_EQUAL(0.5, quake.DockWindow().Height());
VERIFY_IS_TRUE(quake.MinimizeToNotificationArea());
}
// Verify that a named window declared in the "windows" array gets its
// explicit settings, and inherits everything else from the defaults.
void WindowSettingsTests::LayeredWindowSettings()
{
Log::Comment(L"A named window should get its own explicit values and "
L"inherit everything else from the base window settings.");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"initialRows": 50,
"initialCols": 100,
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
],
"windows": [
{
"name": "work",
"initialRows": 80,
"copyOnSelect": true
}
]
})" };
const auto settings = createSettings(settingsJson);
const auto work = settings->WindowSettings(L"work");
// Explicit overrides
VERIFY_ARE_EQUAL(80, work.InitialRows());
VERIFY_IS_TRUE(work.CopyOnSelect());
// Inherited from base (which was explicitly set to 100)
VERIFY_ARE_EQUAL(100, work.InitialCols());
}
// Verify that a named window that only specifies a name still inherits
// every setting from the base window defaults.
void WindowSettingsTests::LayeredOnDefaultWindowSettings()
{
Log::Comment(L"A named window with no explicit settings should be "
L"identical to the base window defaults.");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"initialRows": 42,
"copyOnSelect": true,
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
],
"windows": [
{
"name": "empty"
}
]
})" };
const auto settings = createSettings(settingsJson);
const auto empty = settings->WindowSettings(L"empty");
const auto defaults = settings->WindowSettingsDefaults();
VERIFY_ARE_EQUAL(defaults.InitialRows(), empty.InitialRows());
VERIFY_ARE_EQUAL(defaults.InitialCols(), empty.InitialCols());
VERIFY_ARE_EQUAL(defaults.CopyOnSelect(), empty.CopyOnSelect());
VERIFY_ARE_EQUAL(defaults.WordDelimiters(), empty.WordDelimiters());
}
// Verify that an explicitly defined _quake window in the "windows" array
// gets its custom values while still starting from quake defaults.
void WindowSettingsTests::LayeredQuakeWindowSettings()
{
Log::Comment(L"An explicitly-defined _quake window should honour its "
L"custom values but still start from quake defaults.");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
],
"windows": [
{
"name": "_quake",
"initialRows": 20
}
]
})" };
const auto settings = createSettings(settingsJson);
const auto quake = settings->WindowSettings(L"_quake");
// Custom override
VERIFY_ARE_EQUAL(20, quake.InitialRows());
// Quake defaults should still apply (InitializeForQuakeMode is called
// by LayerJson when name == "_quake").
VERIFY_ARE_EQUAL(LaunchMode::FocusMode, quake.LaunchMode());
VERIFY_IS_NOT_NULL(quake.DockWindow());
VERIFY_ARE_EQUAL(DockPosition::Top, quake.DockWindow().Side());
VERIFY_IS_TRUE(quake.MinimizeToNotificationArea());
}
// Verify that asking for _quake when no windows are defined at all still
// generates a valid quake window with inheritance from the base.
void WindowSettingsTests::TestGeneratedQuakeWindowSettings()
{
Log::Comment(L"Generated _quake settings should still inherit from "
L"the base window defaults.");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"initialRows": 99,
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
]
})" };
const auto settings = createSettings(settingsJson);
const auto quake = settings->WindowSettings(L"_quake");
// The generated quake window should inherit the base window's rows
// BEFORE InitializeForQuakeMode overwrites launch mode / docking.
// Actually, InitializeForQuakeMode does not touch InitialRows, so
// the inherited value should come through.
VERIFY_ARE_EQUAL(99, quake.InitialRows());
// Quake-specific defaults
VERIFY_ARE_EQUAL(LaunchMode::FocusMode, quake.LaunchMode());
VERIFY_IS_NOT_NULL(quake.DockWindow());
VERIFY_ARE_EQUAL(DockPosition::Top, quake.DockWindow().Side());
}
// ===================================================================
// Edge-case tests
// ===================================================================
// When no "windows" array is present, WindowSettingsDefaults() should
// still return a valid object and WindowSettings(name) should fall back
// to the base defaults.
void WindowSettingsTests::NoWindowsDefined()
{
Log::Comment(L"Without a 'windows' array, WindowSettingsDefaults() "
L"must still be valid with standard defaults.");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
]
})" };
const auto settings = createSettings(settingsJson);
const auto defaults = settings->WindowSettingsDefaults();
// The defaults should have the standard built-in values.
VERIFY_ARE_EQUAL(30, defaults.InitialRows());
VERIFY_ARE_EQUAL(80, defaults.InitialCols());
VERIFY_IS_FALSE(defaults.CopyOnSelect());
VERIFY_IS_NULL(defaults.DockWindow());
VERIFY_IS_FALSE(defaults.MinimizeToNotificationArea());
// AllWindowSettings should be empty — no named windows exist.
VERIFY_ARE_EQUAL(0u, settings->AllWindowSettings().Size());
}
// Looking up a window name that doesn't exist in the "windows" array
// should fall back to the base window defaults rather than crashing.
void WindowSettingsTests::LookupUnknownWindowName()
{
Log::Comment(L"Requesting a window name that doesn't exist should "
L"return the base window defaults.");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"initialRows": 45,
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
],
"windows": [
{
"name": "known",
"initialRows": 60
}
]
})" };
const auto settings = createSettings(settingsJson);
// "unknown" is NOT in the windows list.
const auto unknown = settings->WindowSettings(L"unknown");
const auto defaults = settings->WindowSettingsDefaults();
// Should be identical to the base defaults (initialRows 45).
VERIFY_ARE_EQUAL(defaults.InitialRows(), unknown.InitialRows());
VERIFY_ARE_EQUAL(45, unknown.InitialRows());
// Meanwhile the "known" window should have its override.
const auto known = settings->WindowSettings(L"known");
VERIFY_ARE_EQUAL(60, known.InitialRows());
}
// When the base window settings override a default and a named window
// does NOT explicitly set that property, the named window should inherit
// the base value (not the hard-coded MTSM default).
void WindowSettingsTests::InheritedSettingsFromDefaults()
{
Log::Comment(L"A named window should inherit the user's root-level "
L"overrides, not the hard-coded defaults.");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"initialRows": 70,
"initialCols": 200,
"copyOnSelect": true,
"wordDelimiters": "abc",
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
],
"windows": [
{
"name": "child"
}
]
})" };
const auto settings = createSettings(settingsJson);
const auto child = settings->WindowSettings(L"child");
// All values should come from the base window settings (user root).
VERIFY_ARE_EQUAL(70, child.InitialRows());
VERIFY_ARE_EQUAL(200, child.InitialCols());
VERIFY_IS_TRUE(child.CopyOnSelect());
VERIFY_ARE_EQUAL(L"abc", child.WordDelimiters());
}
// When a named window explicitly sets a property, it should override the
// base defaults.
void WindowSettingsTests::NamedWindowOverridesDefaults()
{
Log::Comment(L"Named-window explicit values must override the base defaults.");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"initialRows": 30,
"copyOnSelect": false,
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
],
"windows": [
{
"name": "override",
"initialRows": 100,
"copyOnSelect": true
}
]
})" };
const auto settings = createSettings(settingsJson);
const auto win = settings->WindowSettings(L"override");
VERIFY_ARE_EQUAL(100, win.InitialRows());
VERIFY_IS_TRUE(win.CopyOnSelect());
// Base defaults should be unchanged.
const auto defaults = settings->WindowSettingsDefaults();
VERIFY_ARE_EQUAL(30, defaults.InitialRows());
VERIFY_IS_FALSE(defaults.CopyOnSelect());
}
// A named window that specifies only some properties should inherit the
// rest from the base defaults.
void WindowSettingsTests::PartialNamedWindowInheritsRest()
{
Log::Comment(L"A partially-specified named window inherits the "
L"remaining properties from base window defaults.");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"initialRows": 35,
"initialCols": 120,
"wordDelimiters": "xyz",
"alwaysShowTabs": false,
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
],
"windows": [
{
"name": "partial",
"initialRows": 50
}
]
})" };
const auto settings = createSettings(settingsJson);
const auto partial = settings->WindowSettings(L"partial");
// Overridden
VERIFY_ARE_EQUAL(50, partial.InitialRows());
// Inherited from base
VERIFY_ARE_EQUAL(120, partial.InitialCols());
VERIFY_ARE_EQUAL(L"xyz", partial.WordDelimiters());
VERIFY_IS_FALSE(partial.AlwaysShowTabs());
}
// Verify that multiple named windows each get their own values and don't
// bleed into each other.
void WindowSettingsTests::MultipleNamedWindows()
{
Log::Comment(L"Multiple named windows must each get their own overrides "
L"without affecting each other.");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"initialRows": 30,
"initialCols": 80,
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
],
"windows": [
{
"name": "alpha",
"initialRows": 40,
"copyOnSelect": true
},
{
"name": "beta",
"initialCols": 200,
"alwaysOnTop": true
}
]
})" };
const auto settings = createSettings(settingsJson);
const auto alpha = settings->WindowSettings(L"alpha");
const auto beta = settings->WindowSettings(L"beta");
const auto defaults = settings->WindowSettingsDefaults();
// Alpha overrides
VERIFY_ARE_EQUAL(40, alpha.InitialRows());
VERIFY_IS_TRUE(alpha.CopyOnSelect());
// Alpha inherits cols from base
VERIFY_ARE_EQUAL(80, alpha.InitialCols());
// Alpha should NOT have beta's alwaysOnTop
VERIFY_IS_FALSE(alpha.AlwaysOnTop());
// Beta overrides
VERIFY_ARE_EQUAL(200, beta.InitialCols());
VERIFY_IS_TRUE(beta.AlwaysOnTop());
// Beta inherits rows from base
VERIFY_ARE_EQUAL(30, beta.InitialRows());
// Beta should NOT have alpha's copyOnSelect
VERIFY_IS_FALSE(beta.CopyOnSelect());
// We defined two named windows.
VERIFY_ARE_EQUAL(2u, settings->AllWindowSettings().Size());
}
// ===================================================================
// Serialization roundtrip tests
// ===================================================================
// Verify that WindowSettings survive a FromJson -> ToJson roundtrip.
void WindowSettingsTests::RoundtripWindowSettings()
{
Log::Comment(L"WindowSettings should roundtrip through JSON correctly.");
static constexpr std::string_view windowJson{ R"({
"copyOnSelect": true,
"initialCols": 200,
"initialRows": 50,
"wordDelimiters": "test"
})" };
const auto json = VerifyParseSucceeded(windowJson);
const auto windowSettings = implementation::WindowSettings::FromJson(json);
const auto result = windowSettings->ToJson();
// The roundtrip should produce the same keys and values.
VERIFY_IS_TRUE(result.isMember("copyOnSelect"));
VERIFY_IS_TRUE(result["copyOnSelect"].asBool());
VERIFY_ARE_EQUAL(200, result["initialCols"].asInt());
VERIFY_ARE_EQUAL(50, result["initialRows"].asInt());
VERIFY_ARE_EQUAL(std::string("test"), result["wordDelimiters"].asString());
}
// Verify that a full CascadiaSettings with named windows roundtrips
// correctly through ToJson.
void WindowSettingsTests::RoundtripNamedWindows()
{
Log::Comment(L"Named windows in 'windows' array should survive a "
L"settings roundtrip.");
static constexpr std::string_view settingsJson{ R"({
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"initialRows": 42,
"profiles": [
{ "name": "profile0", "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" }
],
"windows": [
{
"name": "myWindow",
"initialRows": 60,
"copyOnSelect": true
}
]
})" };
const auto settings = createSettings(settingsJson);
const auto json = settings->ToJson();
// The root should have our base initialRows.
VERIFY_ARE_EQUAL(42, json["initialRows"].asInt());
// The "windows" array should exist and contain our named window.
VERIFY_IS_TRUE(json.isMember("windows"));
const auto& windows = json["windows"];
VERIFY_ARE_EQUAL(1u, windows.size());
const auto& win = windows[0u];
VERIFY_ARE_EQUAL(std::string("myWindow"), win["name"].asString());
VERIFY_ARE_EQUAL(60, win["initialRows"].asInt());
VERIFY_IS_TRUE(win["copyOnSelect"].asBool());
}
}

View File

@@ -132,7 +132,12 @@ void AppHost::_HandleCommandlineArgs(const winrt::TerminalApp::WindowRequestedAr
// We don't have XAML yet, but we do have other stuff.
_windowLogic = _appLogic.CreateNewWindow();
if (const auto content = windowArgs.Content(); !content.empty())
if (const auto layout = windowArgs.PersistedLayout())
{
_windowLogic.SetPersistedLayout(layout);
_launchShowWindowCommand = SW_NORMAL;
}
else if (const auto content = windowArgs.Content(); !content.empty())
{
_windowLogic.SetStartupContent(content, windowArgs.InitialBounds());
_launchShowWindowCommand = SW_NORMAL;
@@ -265,11 +270,13 @@ void AppHost::Initialize()
_revokers.IsQuakeWindowChanged = _windowLogic.IsQuakeWindowChanged(winrt::auto_revoke, { this, &AppHost::_IsQuakeWindowChanged });
_revokers.SummonWindowRequested = _windowLogic.SummonWindowRequested(winrt::auto_revoke, { this, &AppHost::_SummonWindowRequested });
_revokers.SummonWindowByIdRequested = _windowLogic.SummonWindowByIdRequested(winrt::auto_revoke, { this, &AppHost::_SummonWindowByIdRequested });
_revokers.OpenSystemMenu = _windowLogic.OpenSystemMenu(winrt::auto_revoke, { this, &AppHost::_OpenSystemMenu });
_revokers.QuitRequested = _windowLogic.QuitRequested(winrt::auto_revoke, { this, &AppHost::_RequestQuitAll });
_revokers.ShowWindowChanged = _windowLogic.ShowWindowChanged(winrt::auto_revoke, { this, &AppHost::_ShowWindowChanged });
_revokers.RequestMoveContent = _windowLogic.RequestMoveContent(winrt::auto_revoke, { this, &AppHost::_handleMoveContent });
_revokers.RequestReceiveContent = _windowLogic.RequestReceiveContent(winrt::auto_revoke, { this, &AppHost::_handleReceiveContent });
_revokers.RequestWindowList = _windowLogic.RequestWindowList(winrt::auto_revoke, { this, &AppHost::_HandleRequestWindowList });
// BODGY
// On certain builds of Windows, when Terminal is set as the default
@@ -409,6 +416,26 @@ void AppHost::_HandleRequestLaunchPosition(const winrt::Windows::Foundation::IIn
args.Position(_GetWindowLaunchPosition());
}
void AppHost::_HandleRequestWindowList(const winrt::Windows::Foundation::IInspectable& /*sender*/,
winrt::TerminalApp::WindowListRequest args)
{
// Ask the Emperor (on the main thread) for the current window list.
// SendMessage blocks until the message is processed, so this is
// synchronous and the results vector is filled in-place.
std::vector<WindowEmperor::WindowListEntry> entries;
SendMessage(_windowManager->GetMainWindow(),
WindowEmperor::WM_GET_WINDOW_LIST,
0,
reinterpret_cast<LPARAM>(&entries));
auto windowEntries = args.WindowEntries();
for (const auto& entry : entries)
{
// Format: "id\tname" (name may be empty for unnamed windows)
windowEntries.Append(winrt::hstring{ fmt::format(FMT_COMPILE(L"{}\t{}"), entry.Id, entry.Name) });
}
}
LaunchPosition AppHost::_GetWindowLaunchPosition()
{
LaunchPosition pos{};
@@ -556,21 +583,66 @@ void AppHost::_initialResizeAndRepositionWindow(const HWND hwnd, til::rect propo
til::point origin{ (proposedRect.left + nonClientFrame.left),
(proposedRect.top) };
if (_windowLogic.IsQuakeWindow())
if (const auto dockingSettings{ _windowLogic.Docking() })
{
// If we just use rcWork by itself, we'll fail to account for the invisible
// space reserved for the resize handles. So retrieve that size here.
const auto availableSpace = desktopDimensions + nonClientSize;
const auto singleBorderWidth = nonClientSize.width / 2;
const auto singleBorderHeight = nonClientSize.height / 2;
origin = {
(nearestMonitorInfo.rcWork.left - (nonClientSize.width / 2)),
(nearestMonitorInfo.rcWork.top)
};
dimensions = {
availableSpace.width,
availableSpace.height / 2
};
launchMode = LaunchMode::FocusMode;
// If it's >1, then use that as a number of px.
// If it's <= 1, then use that as a multiplier on the available space
const auto settingsWidth = dockingSettings.Width();
const auto width{ settingsWidth > 1.0 ? settingsWidth : (availableSpace.width * settingsWidth) };
const auto settingsHeight = dockingSettings.Height();
const auto height{ settingsHeight > 1.0 ? settingsHeight : (availableSpace.height * settingsHeight) };
dimensions = { til::math::rounding,
width,
height };
// Account for centerOnLaunch too
const til::size fromSide = centerOnLaunch ? (til::size{ til::math::rounding,
(availableSpace.width - width) / 2.0,
(availableSpace.height - height) / 2.0 }) :
(til::size{ 0, 0 });
switch (dockingSettings.Side())
{
case winrt::Microsoft::Terminal::Settings::Model::DockPosition::Top:
{
origin = {
(nearestMonitorInfo.rcWork.left - (singleBorderWidth) + fromSide.width),
(nearestMonitorInfo.rcWork.top)
};
break;
}
case winrt::Microsoft::Terminal::Settings::Model::DockPosition::Bottom:
{
origin = {
(nearestMonitorInfo.rcWork.left - (singleBorderWidth) + fromSide.width),
(nearestMonitorInfo.rcWork.bottom - singleBorderHeight - (dimensions.height))
};
break;
}
case winrt::Microsoft::Terminal::Settings::Model::DockPosition::Left:
{
origin = {
(nearestMonitorInfo.rcWork.left - (singleBorderWidth)),
(nearestMonitorInfo.rcWork.top) + fromSide.height
};
break;
}
case winrt::Microsoft::Terminal::Settings::Model::DockPosition::Right:
{
origin = {
(nearestMonitorInfo.rcWork.right - (singleBorderWidth) - (dimensions.width)),
(nearestMonitorInfo.rcWork.top) + fromSide.height
};
break;
}
}
}
else if (centerOnLaunch)
{
@@ -930,7 +1002,7 @@ void _frameColorHelper(const HWND h, const COLORREF color)
void AppHost::_updateTheme()
{
auto theme = _appLogic.Settings().GlobalSettings().CurrentTheme();
auto theme = _windowLogic.Theme();
_window->OnApplicationThemeChanged(theme.RequestedTheme());
@@ -1027,7 +1099,13 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta
void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
_window->IsQuakeWindow(_windowLogic.IsQuakeWindow());
// The window's per-window settings identity changed (e.g. window
// name changed, or settings reloaded). Re-push every host-level
// per-window property so the IslandWindow stays in sync.
_window->DockSettings(_windowLogic.Docking(), _windowLogic.CenterOnLaunch());
_window->SetMinimizeToNotificationAreaBehavior(_windowLogic.GetMinimizeToNotificationArea());
_window->SetAutoHideWindow(_windowLogic.AutoHideWindow());
_window->SetShowTabsFullscreen(_windowLogic.ShowTabsFullscreen());
}
// Raised from TerminalWindow. We handle by bubbling the request to the window manager.
@@ -1064,6 +1142,23 @@ void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspecta
HandleSummon(std::move(summonArgs));
}
void AppHost::_SummonWindowByIdRequested(const winrt::Windows::Foundation::IInspectable&,
const winrt::TerminalApp::SummonWindowByIdRequestedArgs& args)
{
// Summon the window by its ID without creating a new tab.
// We look up the target window in WindowEmperor and call HandleSummon directly.
const auto targetId = args.WindowId();
if (auto* targetWindow = _windowManager->GetWindowById(targetId))
{
winrt::TerminalApp::SummonWindowBehavior summonBehavior;
summonBehavior.MoveToCurrentDesktop(false);
summonBehavior.DropdownDuration(0);
summonBehavior.ToMonitor(winrt::TerminalApp::MonitorBehavior::InPlace);
summonBehavior.ToggleVisibility(false); // Do not toggle, just make visible.
targetWindow->HandleSummon(std::move(summonBehavior));
}
}
void AppHost::_OpenSystemMenu(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{

View File

@@ -91,6 +91,9 @@ private:
void _SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _SummonWindowByIdRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::TerminalApp::SummonWindowByIdRequestedArgs& args);
void _OpenSystemMenu(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
@@ -130,6 +133,8 @@ private:
void _AppTitleChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&);
void _HandleRequestLaunchPosition(const winrt::Windows::Foundation::IInspectable& sender,
winrt::TerminalApp::LaunchPositionRequest args);
void _HandleRequestWindowList(const winrt::Windows::Foundation::IInspectable& sender,
winrt::TerminalApp::WindowListRequest args);
// Helper struct. By putting these all into one struct, we can revoke them
// all at once, by assigning _revokers to a fresh Revokers instance. That'll
@@ -151,12 +156,14 @@ private:
winrt::TerminalApp::TerminalWindow::IdentifyWindowsRequested_revoker IdentifyWindowsRequested;
winrt::TerminalApp::TerminalWindow::IsQuakeWindowChanged_revoker IsQuakeWindowChanged;
winrt::TerminalApp::TerminalWindow::SummonWindowRequested_revoker SummonWindowRequested;
winrt::TerminalApp::TerminalWindow::SummonWindowByIdRequested_revoker SummonWindowByIdRequested;
winrt::TerminalApp::TerminalWindow::OpenSystemMenu_revoker OpenSystemMenu;
winrt::TerminalApp::TerminalWindow::QuitRequested_revoker QuitRequested;
winrt::TerminalApp::TerminalWindow::ShowWindowChanged_revoker ShowWindowChanged;
winrt::TerminalApp::TerminalWindow::RequestMoveContent_revoker RequestMoveContent;
winrt::TerminalApp::TerminalWindow::RequestReceiveContent_revoker RequestReceiveContent;
winrt::TerminalApp::TerminalWindow::RequestLaunchPosition_revoker RequestLaunchPosition;
winrt::TerminalApp::TerminalWindow::RequestWindowList_revoker RequestWindowList;
winrt::TerminalApp::TerminalWindow::PropertyChanged_revoker PropertyChanged;
winrt::TerminalApp::TerminalWindow::SettingsChanged_revoker SettingsChanged;
winrt::TerminalApp::TerminalWindow::WindowSizeChanged_revoker WindowSizeChanged;

View File

@@ -17,6 +17,7 @@ using namespace winrt::Windows::UI::Composition;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Hosting;
using namespace winrt::Windows::Foundation::Numerics;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal;
@@ -230,6 +231,14 @@ void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexce
UpdateWindowIconForActiveMetrics(_window.get());
}
bool _preventSizingForDocked(const Model::DockPosition side, const WPARAM sizingSide)
{
return (side == Model::DockPosition::Top && sizingSide == WMSZ_TOP) ||
(side == Model::DockPosition::Bottom && sizingSide == WMSZ_BOTTOM) ||
(side == Model::DockPosition::Left && sizingSide == WMSZ_LEFT) ||
(side == Model::DockPosition::Right && sizingSide == WMSZ_RIGHT);
}
// Method Description:
// - Handles a WM_SIZING message, which occurs when user drags a window border
// or corner. It intercepts this resize action and applies 'snapping' i.e.
@@ -253,14 +262,16 @@ LRESULT IslandWindow::_OnSizing(const WPARAM wParam, const LPARAM lParam)
auto winRect = reinterpret_cast<LPRECT>(lParam);
// If we're the quake window, prevent resizing on all sides except the
// bottom. This also applies to resizing with the Alt+Space menu
if (IsQuakeWindow() && wParam != WMSZ_BOTTOM)
// I think it's okay to resize a docked window on any side that's not the side we're docked to.
if (_docked())
{
// Stuff our current window size into the lParam, and return true. This
// will tell User32 to use our current dimensions to resize to.
::GetWindowRect(_window.get(), winRect);
return true;
if (_preventSizingForDocked(_dockingSettings.Side(), wParam))
{
// Stuff our current window size into the lParam, and return true. This
// will tell User32 to use our current dimensions to resize to.
::GetWindowRect(_window.get(), winRect);
return true;
}
}
// Find nearest monitor.
@@ -355,7 +366,7 @@ LRESULT IslandWindow::_OnMoving(const WPARAM /*wParam*/, const LPARAM lParam)
// If we're the quake window, prevent moving the window. If we don't do
// this, then Alt+Space...Move will still be able to move the window.
if (IsQuakeWindow())
if (_docked())
{
// Stuff our current window into the lParam, and return true. This
// will tell User32 to use our current position to move to.
@@ -520,7 +531,7 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam
if (_autoHideWindow && !activated)
{
if (_isQuakeWindow || _minimizeToNotificationArea)
if (_minimizeToNotificationArea)
{
HideWindow();
}
@@ -569,7 +580,8 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam
if (wparam == SIZE_MINIMIZED)
{
WindowVisibilityChanged.raise(false);
if (_isQuakeWindow)
if (_minimizeToNotificationArea)
{
ShowWindow(GetHandle(), SW_HIDE);
return 0;
@@ -643,7 +655,7 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam
{
// GH#10274 - if the quake window gets moved to another monitor via aero
// snap (win+shift+arrows), then re-adjust the size for the new monitor.
if (IsQuakeWindow())
if (_docked())
{
// Retrieve the suggested dimensions and make a rect and size.
auto lpwpos = (LPWINDOWPOS)lparam;
@@ -679,10 +691,10 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam
if (til::rect{ proposedInfo.rcMonitor } !=
til::rect{ currentInfo.rcMonitor })
{
const auto newWindowRect{ _getQuakeModeSize(proposed) };
const auto newWindowRect{ _getDockedSize(proposed) };
// Inform User32 that we want to be placed at the position
// and dimensions that _getQuakeModeSize returned. When we
// and dimensions that _getDockedSize returned. When we
// snap across monitor boundaries, this will re-evaluate our
// size for the new monitor.
lpwpos->x = newWindowRect.left;
@@ -1309,7 +1321,10 @@ void IslandWindow::SummonWindow(winrt::TerminalApp::SummonWindowBehavior args)
// Method Description:
// - Helper for performing a sliding animation. This will animate our _Xaml
// Island_, either growing down or shrinking up, using SetWindowRgn.
// Island_ using SetWindowRgn, revealing or hiding the window from the edge
// it is docked to. For Top-docked windows the region grows/shrinks
// vertically from the top; for Bottom from the bottom; for Left horizontally
// from the left; for Right from the right.
// - This function does the entire animation on the main thread (the UI thread),
// and **DOES NOT YIELD IT**. The window will be animating for the entire
// duration of dropdownDuration.
@@ -1318,15 +1333,22 @@ void IslandWindow::SummonWindow(winrt::TerminalApp::SummonWindowBehavior args)
// Arguments:
// - dropdownDuration: The duration to play the animation, in milliseconds. If
// 0, we won't perform a dropdown animation.
// - down: if true, increase the height from top to bottom. otherwise, decrease
// the height, from bottom to top.
// - appearing: if true, the window is sliding into view (the visible region
// grows). If false, it is sliding out of view (the visible region shrinks).
// Return Value:
// - <none>
void IslandWindow::_doSlideAnimation(const uint32_t dropdownDuration, const bool down)
void IslandWindow::_doSlideAnimation(const uint32_t dropdownDuration, const bool appearing)
{
til::rect fullWindowSize{ GetWindowRect() };
const auto fullWidth = fullWindowSize.width();
const auto fullHeight = fullWindowSize.height();
// Determine which axis and direction to animate based on the dock side.
// Default to Top for the (unlikely) case where there is no docking config.
const auto side = _dockingSettings ? _dockingSettings.Side() : Model::DockPosition::Top;
const bool horizontal = (side == Model::DockPosition::Left || side == Model::DockPosition::Right);
const auto fullExtent = horizontal ? fullWidth : fullHeight;
const double animationDuration = dropdownDuration; // use floating-point math throughout
const auto start = std::chrono::system_clock::now();
@@ -1343,12 +1365,31 @@ void IslandWindow::_doSlideAnimation(const uint32_t dropdownDuration, const bool
break;
}
// If going down, increase the height over time. If going up, decrease the height.
const auto currentHeight = ::base::saturated_cast<int>(
down ? ((dt / animationDuration) * fullHeight) :
((1.0 - (dt / animationDuration)) * fullHeight));
// Progress goes 0→1 when appearing and 1→0 when disappearing.
const auto progress = appearing ? (dt / animationDuration) : (1.0 - (dt / animationDuration));
const auto currentExtent = ::base::saturated_cast<int>(progress * fullExtent);
wil::unique_hrgn rgn{ CreateRectRgn(0, 0, fullWindowSize.width(), currentHeight) };
wil::unique_hrgn rgn;
switch (side)
{
case Model::DockPosition::Top:
default:
// Reveal/hide from the top edge downward.
rgn.reset(CreateRectRgn(0, 0, fullWidth, currentExtent));
break;
case Model::DockPosition::Bottom:
// Reveal/hide from the bottom edge upward.
rgn.reset(CreateRectRgn(0, fullHeight - currentExtent, fullWidth, fullHeight));
break;
case Model::DockPosition::Left:
// Reveal/hide from the left edge rightward.
rgn.reset(CreateRectRgn(0, 0, currentExtent, fullHeight));
break;
case Model::DockPosition::Right:
// Reveal/hide from the right edge leftward.
rgn.reset(CreateRectRgn(fullWidth - currentExtent, 0, fullWidth, fullHeight));
break;
}
SetWindowRgn(_interopWindowHandle, rgn.get(), true);
// Go immediately into another frame. This prevents the window from
@@ -1388,13 +1429,13 @@ void IslandWindow::_dropdownWindow(const uint32_t dropdownDuration,
// Possibly go to the monitor of the mouse / old foreground window.
_moveToMonitor(oldForegroundWindow, toMonitor);
// Now that we're visible, animate the dropdown.
// Now that we're visible, animate the window sliding into view.
_doSlideAnimation(dropdownDuration, true);
}
void IslandWindow::_slideUpWindow(const uint32_t dropdownDuration)
{
// First, animate the window sliding up.
// First, animate the window sliding out of view.
_doSlideAnimation(dropdownDuration, false);
// Then, use SetWindowPlacement to minimize without the animation.
@@ -1616,46 +1657,56 @@ void IslandWindow::_moveToMonitor(const MONITORINFO activeMonitor)
// GH#10274, GH#10182: Re-evaluate the size of the quake window when we
// move to another monitor.
if (IsQuakeWindow())
{
_enterQuakeMode();
}
_applyDocking();
}
}
bool IslandWindow::IsQuakeWindow() const noexcept
Docking IslandWindow::DockSettings() const noexcept
{
return _isQuakeWindow;
return _dockingSettings;
}
void IslandWindow::IsQuakeWindow(bool isQuakeWindow) noexcept
void IslandWindow::DockSettings(Docking settings, const bool centered) noexcept
{
if (_isQuakeWindow != isQuakeWindow)
_centered = centered;
if (_dockingSettings != settings)
{
_isQuakeWindow = isQuakeWindow;
// Don't enter quake mode if we don't have an HWND yet
if (IsQuakeWindow() && _window)
_dockingSettings = settings;
// If the window has already been created, immediately apply the
// new docking configuration so it snaps into place. When called
// before window creation (e.g. during startup) the docking will
// be applied later by _initialResizeAndRepositionWindow.
if (settings && _window)
{
_enterQuakeMode();
_applyDocking();
}
}
}
bool IslandWindow::_docked() const noexcept
{
return _dockingSettings != nullptr && _dockingSettings.Side() != Model::DockPosition::None;
}
void IslandWindow::SetAutoHideWindow(bool autoHideWindow) noexcept
{
_autoHideWindow = autoHideWindow;
}
// Method Description:
// - Enter quake mode for the monitor this window is currently on. This involves
// resizing it to the top half of the monitor.
// - Apply the current docking configuration. The window is moved and
// resized to the appropriate edge of the nearest monitor according
// to _dockingSettings (side, width, height). If _centered is set,
// the window is offset towards the centre of the monitor along the
// non-docked axis.
// Arguments:
// - <none>
// Return Value:
// - <none>
void IslandWindow::_enterQuakeMode()
void IslandWindow::_applyDocking()
{
if (!_window)
if (!_window || !_dockingSettings || _dockingSettings.Side() == Model::DockPosition::None)
{
return;
}
@@ -1664,7 +1715,7 @@ void IslandWindow::_enterQuakeMode()
auto hmon = MonitorFromRect(&windowRect, MONITOR_DEFAULTTONEAREST);
// Get the size and position of the window that we should occupy
const auto newRect{ _getQuakeModeSize(hmon) };
const auto newRect{ _getDockedSize(hmon) };
SetWindowPos(GetHandle(),
HWND_TOP,
@@ -1676,14 +1727,14 @@ void IslandWindow::_enterQuakeMode()
}
// Method Description:
// - Get the size and position of the window that a "quake mode" should occupy
// on the given monitor.
// - The window will occupy the top half of the monitor.
// - Calculate the position and size of the docked window on the given
// monitor. The result depends on _dockingSettings (side, width,
// height) and _centered.
// Arguments:
// - <none>
// - hmon: the monitor to dock on.
// Return Value:
// - <none>
til::rect IslandWindow::_getQuakeModeSize(HMONITOR hmon)
// - A til::rect describing the window rectangle in screen coordinates.
til::rect IslandWindow::_getDockedSize(HMONITOR hmon)
{
MONITORINFO nearestMonitorInfo;
@@ -1704,17 +1755,61 @@ til::rect IslandWindow::_getQuakeModeSize(HMONITOR hmon)
const til::size ncSize{ GetTotalNonClientExclusiveSize(dpix) };
const auto availableSpace = desktopDimensions + ncSize;
// GH#10201 - The borders are still visible in quake mode, so make us 1px
// smaller on either side to account for that, so they don't hang onto
// adjacent monitors.
const til::point origin{
::base::ClampSub(nearestMonitorInfo.rcWork.left, (ncSize.width / 2)) + 1,
(nearestMonitorInfo.rcWork.top)
};
const til::size dimensions{
availableSpace.width - 2,
availableSpace.height / 2
};
const auto singleBorderWidth = ncSize.width / 2;
const auto singleBorderHeight = ncSize.height / 2;
// If it's >1, then use that as a number of px.
// If it's <= 1, then use that as a multiplier on the available space
const auto settingsWidth = _dockingSettings.Width();
const auto width{ settingsWidth > 1.0 ? settingsWidth : (availableSpace.width * settingsWidth) };
const auto settingsHeight = _dockingSettings.Height();
const auto height{ settingsHeight > 1.0 ? settingsHeight : (availableSpace.height * settingsHeight) };
const til::size dimensions = { til::math::rounding,
width,
height };
// Account for centerOnLaunch too
const til::size fromSide = _centered ? (til::size{ til::math::rounding,
(availableSpace.width - width) / 2.0,
(availableSpace.height - height) / 2.0 }) :
(til::size{ 0, 0 });
til::point origin;
switch (_dockingSettings.Side())
{
case winrt::Microsoft::Terminal::Settings::Model::DockPosition::Top:
{
origin = {
(nearestMonitorInfo.rcWork.left - (singleBorderWidth) + fromSide.width),
(nearestMonitorInfo.rcWork.top)
};
break;
}
case winrt::Microsoft::Terminal::Settings::Model::DockPosition::Bottom:
{
origin = {
(nearestMonitorInfo.rcWork.left - (singleBorderWidth) + fromSide.width),
(nearestMonitorInfo.rcWork.bottom - singleBorderHeight - (dimensions.height))
};
break;
}
case winrt::Microsoft::Terminal::Settings::Model::DockPosition::Left:
{
origin = {
(nearestMonitorInfo.rcWork.left - (singleBorderWidth)),
(nearestMonitorInfo.rcWork.top) + fromSide.height
};
break;
}
case winrt::Microsoft::Terminal::Settings::Model::DockPosition::Right:
{
origin = {
(nearestMonitorInfo.rcWork.right - (singleBorderWidth) - (dimensions.width)),
(nearestMonitorInfo.rcWork.top) + fromSide.height
};
break;
}
}
return { origin, dimensions };
}

View File

@@ -57,8 +57,8 @@ public:
void SummonWindow(winrt::TerminalApp::SummonWindowBehavior args);
bool IsQuakeWindow() const noexcept;
void IsQuakeWindow(bool isQuakeWindow) noexcept;
winrt::Microsoft::Terminal::Settings::Model::Docking DockSettings() const noexcept;
void DockSettings(winrt::Microsoft::Terminal::Settings::Model::Docking settings, const bool centered) noexcept;
void SetAutoHideWindow(bool autoHideWindow) noexcept;
void HideWindow();
@@ -131,7 +131,7 @@ protected:
void _dropdownWindow(const uint32_t dropdownDuration,
const winrt::TerminalApp::MonitorBehavior toMonitor);
void _slideUpWindow(const uint32_t dropdownDuration);
void _doSlideAnimation(const uint32_t dropdownDuration, const bool down);
void _doSlideAnimation(const uint32_t dropdownDuration, const bool appearing);
void _globalDismissWindow(const uint32_t dropdownDuration);
static MONITORINFO _getMonitorForCursor();
@@ -141,11 +141,13 @@ protected:
void _moveToMonitorOf(HWND foregroundWindow);
void _moveToMonitor(const MONITORINFO activeMonitor);
bool _isQuakeWindow{ false };
winrt::Microsoft::Terminal::Settings::Model::Docking _dockingSettings{ nullptr };
bool _centered{ false };
bool _autoHideWindow{ false };
void _enterQuakeMode();
til::rect _getQuakeModeSize(HMONITOR hmon);
void _applyDocking();
til::rect _getDockedSize(HMONITOR hmon);
bool _docked() const noexcept;
bool _minimizeToNotificationArea{ false };

View File

@@ -743,23 +743,6 @@ int NonClientIslandWindow::_GetResizeHandleHeight() const noexcept
if (originalRet != HTCLIENT)
{
// If we're the quake window, suppress resizing on any side except the
// bottom. I don't believe that this actually works on the top. That's
// handled below.
if (IsQuakeWindow())
{
switch (originalRet)
{
case HTBOTTOMRIGHT:
case HTRIGHT:
case HTTOPRIGHT:
case HTTOP:
case HTTOPLEFT:
case HTLEFT:
case HTBOTTOMLEFT:
return HTCLIENT;
}
}
return originalRet;
}
@@ -779,9 +762,15 @@ int NonClientIslandWindow::_GetResizeHandleHeight() const noexcept
// the top of the drag bar is used to resize the window
if (!_isMaximized && isOnResizeBorder)
{
// However, if we're the quake window, then just return HTCAPTION so we
// don't get a resize handle on the top.
return IsQuakeWindow() ? HTCAPTION : HTTOP;
// If we're docked to the top, return HTCAPTION so we don't get a
// resize handle on the side we're docked to. The user can't resize
// the window away from the docked edge.
if (_docked() && _dockingSettings.Side() == winrt::Microsoft::Terminal::Settings::Model::DockPosition::Top)
{
return HTCAPTION;
}
return HTTOP;
}
return HTCAPTION;

View File

@@ -654,6 +654,25 @@ void WindowEmperor::_dispatchCommandline(winrt::TerminalApp::CommandlineArgs arg
{
winrt::TerminalApp::WindowRequestedArgs request{ windowId, std::move(args) };
request.WindowName(std::move(windowName));
// If we're opening a named window that doesn't exist yet, check
// if there's a persisted workspace with that name to restore.
const auto& reqName = request.WindowName();
if (!reqName.empty())
{
const auto state = ApplicationState::SharedInstance();
if (const auto workspaces = state.AllPersistedWorkspaces())
{
if (workspaces.HasKey(reqName))
{
const auto layout = workspaces.Lookup(reqName);
request.PersistedLayout(layout);
// Remove the workspace entry now that we're restoring it.
state.RemoveWorkspace(reqName);
}
}
}
CreateNewWindow(std::move(request));
}
}
@@ -919,6 +938,22 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c
// anyway (since we threw and exited this message handler) so this at least gives back our
// deterministic window count management.
const auto strong = *it;
// Before destroying a named window, persist its full
// tab/buffer state as a workspace so it can be restored later.
try
{
const auto windowName = strong->Logic().WindowProperties().WindowName();
if (!windowName.empty())
{
if (const auto layout = strong->Logic().GetWindowLayout())
{
ApplicationState::SharedInstance().SaveWorkspace(windowName, layout);
}
}
}
CATCH_LOG();
_windows.erase(it);
try
{
@@ -946,6 +981,19 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c
host->Logic().IdentifyWindow();
}
return 0;
case WM_GET_WINDOW_LIST:
{
auto* result = reinterpret_cast<std::vector<WindowListEntry>*>(lParam);
if (result)
{
for (const auto& host : _windows)
{
const auto props = host->Logic().WindowProperties();
result->emplace_back(WindowListEntry{ props.WindowId(), std::wstring{ props.WindowName() } });
}
}
return 0;
}
case WM_NOTIFY_FROM_NOTIFICATION_AREA:
switch (LOWORD(lParam))
{
@@ -1419,12 +1467,14 @@ void WindowEmperor::_checkWindowsForNotificationIcon()
// RequestsTrayIcon setting value, and combine that with the result of each
// window (which won't change during a settings reload).
const auto globals = _app.Logic().Settings().GlobalSettings();
auto needsIcon = globals.AlwaysShowNotificationIcon() || globals.MinimizeToNotificationArea();
const auto windowDefaults = _app.Logic().Settings().WindowSettingsDefaults();
auto needsIcon = globals.AlwaysShowNotificationIcon() || windowDefaults.MinimizeToNotificationArea();
if (!needsIcon)
{
for (const auto& host : _windows)
{
needsIcon |= host->Logic().IsQuakeWindow();
const auto logic = host->Logic();
needsIcon |= logic.GetMinimizeToNotificationArea();
}
}

View File

@@ -28,6 +28,16 @@ public:
WM_MESSAGE_BOX_CLOSED,
WM_IDENTIFY_ALL_WINDOWS,
WM_NOTIFY_FROM_NOTIFICATION_AREA,
WM_GET_WINDOW_LIST,
};
// Used by WM_GET_WINDOW_LIST. Callers allocate a vector on their
// stack and pass a pointer through LPARAM; the emperor fills it in
// synchronously via SendMessage.
struct WindowListEntry
{
uint64_t Id;
std::wstring Name;
};
HWND GetMainWindow() const noexcept;