mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-17 15:36:35 +00:00
Compare commits
2 Commits
dev/cazamo
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c09178d7a | ||
|
|
056925eb42 |
@@ -85,7 +85,7 @@
|
||||
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
|
||||
</uap3:AppExtensionHost>
|
||||
</uap3:Extension>
|
||||
<uap3:Extension Category="windows.appExtension">
|
||||
<!-- <uap3:Extension Category="windows.appExtension">
|
||||
<uap3:AppExtension Name="com.microsoft.windows.console.host"
|
||||
Id="OpenConsole-Dev"
|
||||
DisplayName="OpenConsole Dev"
|
||||
@@ -111,7 +111,7 @@
|
||||
<com:ComInterface>
|
||||
<com:ProxyStub Id="DEC4804D-56D1-4F73-9FBE-6828E7C85C56" DisplayName="OpenConsoleHandoffProxy" Path="OpenConsoleProxy.dll"/>
|
||||
<com:Interface Id="E686C757-9A35-4A1C-B3CE-0BCC8B5C69F4" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
|
||||
<com:Interface Id="6F23DA90-15C5-4203-9DB0-64E73F1B1B00" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/> <!-- ITerminalHandoff3 -->
|
||||
<com:Interface Id="6F23DA90-15C5-4203-9DB0-64E73F1B1B00" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
|
||||
<com:Interface Id="746E6BC0-AB05-4E38-AB14-71E86763141F" ProxyStubClsid="DEC4804D-56D1-4F73-9FBE-6828E7C85C56"/>
|
||||
</com:ComInterface>
|
||||
</com:Extension>
|
||||
@@ -137,7 +137,7 @@
|
||||
<desktop5:Verb Id="OpenTerminalDev" Clsid="52065414-e077-47ec-a3ac-1cc5455e1b54" />
|
||||
</desktop5:ItemType>
|
||||
</desktop4:FileExplorerContextMenus>
|
||||
</desktop4:Extension>
|
||||
</desktop4:Extension> -->
|
||||
|
||||
</Extensions>
|
||||
|
||||
|
||||
@@ -245,6 +245,7 @@
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary Source="ms-resource:///Files/TerminalApp/HighlightedTextControlStyle.xaml" />
|
||||
<ResourceDictionary Source="ms-resource:///Files/TerminalApp/VerticalTabViewStyle.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
||||
|
||||
@@ -442,6 +442,10 @@
|
||||
<data name="NewTabRun.Text" xml:space="preserve">
|
||||
<value>Open a new tab</value>
|
||||
</data>
|
||||
<data name="TabNewButtonText" xml:space="preserve">
|
||||
<value>New tab</value>
|
||||
<comment>Label for the new tab button in vertical tab strip mode.</comment>
|
||||
</data>
|
||||
<data name="NewPaneRun.Text" xml:space="preserve">
|
||||
<value>Alt+Click to split the current window</value>
|
||||
</data>
|
||||
|
||||
@@ -255,9 +255,38 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
if (_tabRow)
|
||||
{
|
||||
// collapse/show the row that the tabs are in.
|
||||
// NaN is the special value XAML uses for "Auto" sizing.
|
||||
_tabRow.Height(isVisible ? NAN : 0);
|
||||
if (_tabPosition == Settings::Model::TabPosition::Left ||
|
||||
_tabPosition == Settings::Model::TabPosition::Right)
|
||||
{
|
||||
// For left/right positions, collapse the column width instead of row height.
|
||||
// Also hide the splitter.
|
||||
_tabRow.Width(isVisible ? std::numeric_limits<double>::quiet_NaN() : 0);
|
||||
if (_tabStripSplitter)
|
||||
{
|
||||
_tabStripSplitter.Visibility(isVisible ? Visibility::Visible : Visibility::Collapsed);
|
||||
}
|
||||
// Collapse or restore the tab strip column
|
||||
auto tabStripColIdx = (_tabPosition == Settings::Model::TabPosition::Left) ? 0u : 2u;
|
||||
auto root = this->Root();
|
||||
if (root.ColumnDefinitions().Size() > tabStripColIdx)
|
||||
{
|
||||
auto col = root.ColumnDefinitions().GetAt(tabStripColIdx);
|
||||
if (isVisible)
|
||||
{
|
||||
col.Width(WUX::GridLengthHelper::FromPixels(200));
|
||||
}
|
||||
else
|
||||
{
|
||||
col.Width(WUX::GridLengthHelper::FromPixels(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Top/Bottom: collapse/show the row that the tabs are in.
|
||||
// NaN is the special value XAML uses for "Auto" sizing.
|
||||
_tabRow.Height(isVisible ? NAN : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,10 @@
|
||||
<Type>DefaultStyle</Type>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="VerticalTabViewStyle.xaml">
|
||||
<Type>DefaultStyle</Type>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="ColorPickupFlyout.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
|
||||
@@ -315,6 +315,307 @@ namespace winrt::TerminalApp::implementation
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Rearranges the TerminalPage's root grid to place the tab strip at the
|
||||
// position indicated by the current theme's TabPosition property. For Top,
|
||||
// the existing behavior is preserved (tabs in titlebar or Row 0). For
|
||||
// Bottom, the tab row goes in the last row. For Left/Right, an outer
|
||||
// 3-column grid is created with a resizable splitter between the tab strip
|
||||
// and the content area.
|
||||
void TerminalPage::_ApplyTabPosition()
|
||||
{
|
||||
// Read the tab position from the theme
|
||||
if (const auto theme = _settings.GlobalSettings().CurrentTheme())
|
||||
{
|
||||
if (const auto window = theme.Window())
|
||||
{
|
||||
_tabPosition = window.TabPosition();
|
||||
}
|
||||
}
|
||||
|
||||
auto root = this->Root();
|
||||
auto infoBarPanel = this->InfoBarPanel();
|
||||
|
||||
switch (_tabPosition)
|
||||
{
|
||||
case TabPosition::Top:
|
||||
{
|
||||
// Default XAML layout: Row 0=TabRow(Auto), Row 1=InfoBars(Auto), Row 2=TabContent(*)
|
||||
// If ShowTabsInTitlebar, remove the tab row from the grid and raise SetTitleBarContent
|
||||
if (_settings.GlobalSettings().ShowTabsInTitlebar())
|
||||
{
|
||||
uint32_t index = 0;
|
||||
if (root.Children().IndexOf(_tabRow, index))
|
||||
{
|
||||
root.Children().RemoveAt(index);
|
||||
}
|
||||
SetTitleBarContent.raise(*this, _tabRow);
|
||||
|
||||
const auto transparent = Media::SolidColorBrush();
|
||||
transparent.Color(Windows::UI::Colors::Transparent());
|
||||
_tabRow.Background(transparent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TabPosition::Bottom:
|
||||
{
|
||||
// Rearrange to: Row 0=InfoBars(Auto), Row 1=TabContent(*), Row 2=TabRow(Auto)
|
||||
// Remove only the three layout elements; leave deferred-load stubs
|
||||
// (CommandPalette, SuggestionsControl, dialogs, etc.) in the tree
|
||||
// so that FindName() can still locate them later.
|
||||
uint32_t idx;
|
||||
if (root.Children().IndexOf(_tabRow, idx))
|
||||
root.Children().RemoveAt(idx);
|
||||
if (root.Children().IndexOf(infoBarPanel, idx))
|
||||
root.Children().RemoveAt(idx);
|
||||
if (root.Children().IndexOf(_tabContent, idx))
|
||||
root.Children().RemoveAt(idx);
|
||||
|
||||
root.RowDefinitions().Clear();
|
||||
|
||||
WUX::Controls::RowDefinition row0;
|
||||
row0.Height(WUX::GridLengthHelper::FromValueAndType(1, WUX::GridUnitType::Auto));
|
||||
WUX::Controls::RowDefinition row1;
|
||||
row1.Height(WUX::GridLengthHelper::FromValueAndType(1, WUX::GridUnitType::Star));
|
||||
WUX::Controls::RowDefinition row2;
|
||||
row2.Height(WUX::GridLengthHelper::FromValueAndType(1, WUX::GridUnitType::Auto));
|
||||
root.RowDefinitions().Append(row0);
|
||||
root.RowDefinitions().Append(row1);
|
||||
root.RowDefinitions().Append(row2);
|
||||
|
||||
WUX::Controls::Grid::SetRow(infoBarPanel, 0);
|
||||
WUX::Controls::Grid::SetRow(_tabContent, 1);
|
||||
WUX::Controls::Grid::SetRow(_tabRow, 2);
|
||||
|
||||
// Insert layout elements at the front so overlay elements
|
||||
// (command palette, dialogs) remain on top in z-order.
|
||||
root.Children().InsertAt(0, infoBarPanel);
|
||||
root.Children().InsertAt(1, _tabContent);
|
||||
root.Children().InsertAt(2, _tabRow);
|
||||
|
||||
// Update overlay elements: the ones that had Grid.Row="2" in XAML
|
||||
// should now target Row 1 (the content area) instead of Row 2
|
||||
// (which is the tab row in this layout). Iterate the remaining
|
||||
// children (index 3+) and reassign any that were on row 2.
|
||||
for (uint32_t i = 3; i < root.Children().Size(); ++i)
|
||||
{
|
||||
auto child = root.Children().GetAt(i);
|
||||
if (const auto& fwe { child.try_as<WUX::FrameworkElement>() })
|
||||
{
|
||||
if (WUX::Controls::Grid::GetRow(fwe) == 2)
|
||||
{
|
||||
WUX::Controls::Grid::SetRow(fwe, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TabPosition::Left:
|
||||
case TabPosition::Right:
|
||||
{
|
||||
// Build a 3-column layout: [tabstrip | splitter | content] or reversed.
|
||||
// Remove only the three layout elements; leave deferred-load stubs
|
||||
// (CommandPalette, SuggestionsControl, dialogs, etc.) in the tree
|
||||
// so that FindName() can still locate them later.
|
||||
uint32_t removeIdx;
|
||||
if (root.Children().IndexOf(_tabRow, removeIdx))
|
||||
root.Children().RemoveAt(removeIdx);
|
||||
if (root.Children().IndexOf(infoBarPanel, removeIdx))
|
||||
root.Children().RemoveAt(removeIdx);
|
||||
if (root.Children().IndexOf(_tabContent, removeIdx))
|
||||
root.Children().RemoveAt(removeIdx);
|
||||
|
||||
root.RowDefinitions().Clear();
|
||||
|
||||
// Create column definitions
|
||||
WUX::Controls::ColumnDefinition tabStripCol;
|
||||
tabStripCol.Width(WUX::GridLengthHelper::FromPixels(200));
|
||||
tabStripCol.MinWidth(100);
|
||||
tabStripCol.MaxWidth(400);
|
||||
|
||||
WUX::Controls::ColumnDefinition splitterCol;
|
||||
splitterCol.Width(WUX::GridLengthHelper::FromValueAndType(1, WUX::GridUnitType::Auto));
|
||||
|
||||
WUX::Controls::ColumnDefinition contentCol;
|
||||
contentCol.Width(WUX::GridLengthHelper::FromValueAndType(1, WUX::GridUnitType::Star));
|
||||
|
||||
// Create internal content grid (infobars + tab content stacked vertically)
|
||||
WUX::Controls::Grid contentGrid;
|
||||
WUX::Controls::RowDefinition infoRow;
|
||||
infoRow.Height(WUX::GridLengthHelper::FromValueAndType(1, WUX::GridUnitType::Auto));
|
||||
WUX::Controls::RowDefinition mainRow;
|
||||
mainRow.Height(WUX::GridLengthHelper::FromValueAndType(1, WUX::GridUnitType::Star));
|
||||
contentGrid.RowDefinitions().Append(infoRow);
|
||||
contentGrid.RowDefinitions().Append(mainRow);
|
||||
|
||||
WUX::Controls::Grid::SetRow(infoBarPanel, 0);
|
||||
WUX::Controls::Grid::SetRow(_tabContent, 1);
|
||||
contentGrid.Children().Append(infoBarPanel);
|
||||
contentGrid.Children().Append(_tabContent);
|
||||
|
||||
// Create the splitter border
|
||||
_tabStripSplitter = WUX::Controls::Border();
|
||||
_tabStripSplitter.Width(4);
|
||||
// the BG color will get set in _updatePaneResources
|
||||
|
||||
// Use a custom cursor via InputSystemCursorShape
|
||||
_tabStripSplitter.ManipulationMode(WUX::Input::ManipulationModes::None);
|
||||
|
||||
// Wire up pointer events for the splitter
|
||||
_tabStripSplitter.PointerEntered([](const auto& /*sender*/, const auto&) {
|
||||
if (const auto coreWindow = Windows::UI::Core::CoreWindow::GetForCurrentThread())
|
||||
{
|
||||
coreWindow.PointerCursor(
|
||||
Windows::UI::Core::CoreCursor{ Windows::UI::Core::CoreCursorType::SizeWestEast, 0 });
|
||||
}
|
||||
});
|
||||
_tabStripSplitter.PointerExited([](const auto& /*sender*/, const auto&) {
|
||||
if (const auto coreWindow = Windows::UI::Core::CoreWindow::GetForCurrentThread())
|
||||
{
|
||||
coreWindow.PointerCursor(
|
||||
Windows::UI::Core::CoreCursor{ Windows::UI::Core::CoreCursorType::Arrow, 0 });
|
||||
}
|
||||
});
|
||||
_tabStripSplitter.PointerPressed([weakThis{ get_weak() }](const auto& sender, const WUX::Input::PointerRoutedEventArgs& args) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
auto border = sender.template as<WUX::Controls::Border>();
|
||||
border.CapturePointer(args.Pointer());
|
||||
page->_splitterDragging = true;
|
||||
page->_splitterDragStartX = args.GetCurrentPoint(page->Root()).Position().X;
|
||||
// Find the tab strip column width
|
||||
auto tabStripColIdx = (page->_tabPosition == TabPosition::Left) ? 0u : 2u;
|
||||
page->_splitterDragStartWidth = page->Root().ColumnDefinitions().GetAt(tabStripColIdx).ActualWidth();
|
||||
args.Handled(true);
|
||||
}
|
||||
});
|
||||
_tabStripSplitter.PointerMoved([weakThis{ get_weak() }](const auto& /*sender*/, const WUX::Input::PointerRoutedEventArgs& args) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
if (!page->_splitterDragging)
|
||||
return;
|
||||
auto currentX = args.GetCurrentPoint(page->Root()).Position().X;
|
||||
auto delta = currentX - page->_splitterDragStartX;
|
||||
auto tabStripColIdx = (page->_tabPosition == TabPosition::Left) ? 0u : 2u;
|
||||
auto newWidth = (page->_tabPosition == TabPosition::Left) ? (page->_splitterDragStartWidth + delta) : (page->_splitterDragStartWidth - delta);
|
||||
newWidth = std::clamp(newWidth, 100.0, 400.0);
|
||||
page->Root().ColumnDefinitions().GetAt(tabStripColIdx).Width(WUX::GridLengthHelper::FromPixels(newWidth));
|
||||
args.Handled(true);
|
||||
}
|
||||
});
|
||||
_tabStripSplitter.PointerReleased([weakThis{ get_weak() }](const auto& sender, const WUX::Input::PointerRoutedEventArgs& args) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
if (page->_splitterDragging)
|
||||
{
|
||||
page->_splitterDragging = false;
|
||||
auto border = sender.template as<WUX::Controls::Border>();
|
||||
border.ReleasePointerCapture(args.Pointer());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (_tabPosition == TabPosition::Left)
|
||||
{
|
||||
root.ColumnDefinitions().Append(tabStripCol);
|
||||
root.ColumnDefinitions().Append(splitterCol);
|
||||
root.ColumnDefinitions().Append(contentCol);
|
||||
|
||||
WUX::Controls::Grid::SetColumn(_tabRow, 0);
|
||||
WUX::Controls::Grid::SetColumn(_tabStripSplitter, 1);
|
||||
WUX::Controls::Grid::SetColumn(contentGrid, 2);
|
||||
}
|
||||
else // Right
|
||||
{
|
||||
root.ColumnDefinitions().Append(contentCol);
|
||||
root.ColumnDefinitions().Append(splitterCol);
|
||||
root.ColumnDefinitions().Append(tabStripCol);
|
||||
|
||||
WUX::Controls::Grid::SetColumn(contentGrid, 0);
|
||||
WUX::Controls::Grid::SetColumn(_tabStripSplitter, 1);
|
||||
WUX::Controls::Grid::SetColumn(_tabRow, 2);
|
||||
}
|
||||
|
||||
// Clear any row assignments from XAML
|
||||
WUX::Controls::Grid::SetRow(_tabRow, 0);
|
||||
|
||||
// Insert layout elements at the front so overlay elements
|
||||
// (command palette, dialogs) remain on top in z-order.
|
||||
root.Children().InsertAt(0, _tabRow);
|
||||
root.Children().InsertAt(1, _tabStripSplitter);
|
||||
root.Children().InsertAt(2, contentGrid);
|
||||
|
||||
// Update remaining overlay children (deferred-load stubs for
|
||||
// CommandPalette, SuggestionsControl, dialogs, InfoBars,
|
||||
// TeachingTips, etc.) so they span all 3 columns and cover the
|
||||
// full width of the page, not just the tab strip column.
|
||||
for (uint32_t i = 3; i < root.Children().Size(); ++i)
|
||||
{
|
||||
auto child = root.Children().GetAt(i);
|
||||
if (const auto& fwe { child.try_as<WUX::FrameworkElement>() })
|
||||
{
|
||||
WUX::Controls::Grid::SetColumn(fwe, 0);
|
||||
WUX::Controls::Grid::SetColumnSpan(fwe, 3);
|
||||
WUX::Controls::Grid::SetRow(fwe, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply vertical styles to the TabView from our resource dictionary
|
||||
if (const auto res = Application::Current().Resources())
|
||||
{
|
||||
if (const auto verticalTabViewStyle = res.Lookup(winrt::box_value(L"VerticalTabViewStyle")))
|
||||
{
|
||||
_tabView.Style(verticalTabViewStyle.as<WUX::Style>());
|
||||
}
|
||||
// Apply the vertical TabViewItem style as an implicit style
|
||||
// on the TabRow's resources so new tab items pick it up.
|
||||
if (const auto verticalItemStyle = res.Lookup(winrt::box_value(L"VerticalTabViewItemStyle")))
|
||||
{
|
||||
auto tabRowResources = _tabRow.Resources();
|
||||
if (!tabRowResources)
|
||||
{
|
||||
tabRowResources = WUX::ResourceDictionary{};
|
||||
_tabRow.Resources(tabRowResources);
|
||||
}
|
||||
auto itemStyleType = winrt::xaml_typename<MUX::Controls::TabViewItem>();
|
||||
tabRowResources.Insert(winrt::box_value(itemStyleType), verticalItemStyle);
|
||||
}
|
||||
}
|
||||
|
||||
// Bug fix: TabRowControl.xaml sets VerticalAlignment="Bottom" on
|
||||
// the TabView for the default horizontal mode. Override to Top so
|
||||
// tabs start at the top of the vertical strip.
|
||||
_tabView.VerticalAlignment(WUX::VerticalAlignment::Top);
|
||||
|
||||
// Bug fix: In vertical mode, give the "new tab" button a text
|
||||
// label ("New tab") next to its "+" icon so it reads naturally
|
||||
// in the wider sidebar.
|
||||
if (_newTabButton)
|
||||
{
|
||||
auto panel = WUX::Controls::StackPanel();
|
||||
panel.Orientation(WUX::Controls::Orientation::Horizontal);
|
||||
panel.Spacing(8);
|
||||
|
||||
WUX::Controls::FontIcon plusIcon{};
|
||||
plusIcon.Glyph(L"\uE710");
|
||||
plusIcon.FontFamily(WUX::Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
|
||||
plusIcon.FontSize(12);
|
||||
panel.Children().Append(plusIcon);
|
||||
|
||||
WUX::Controls::TextBlock label{};
|
||||
label.Text(RS_(L"TabNewButtonText"));
|
||||
label.VerticalAlignment(WUX::VerticalAlignment::Center);
|
||||
label.FontFamily(WUX::Media::FontFamily{ L"Segoe UI" });
|
||||
panel.Children().Append(label);
|
||||
|
||||
_newTabButton.Content(panel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::Create()
|
||||
{
|
||||
// Hookup the key bindings
|
||||
@@ -335,37 +636,10 @@ namespace winrt::TerminalApp::implementation
|
||||
auto tabRowImpl = winrt::get_self<implementation::TabRowControl>(_tabRow);
|
||||
_newTabButton = tabRowImpl->NewTabButton();
|
||||
|
||||
if (_settings.GlobalSettings().ShowTabsInTitlebar())
|
||||
{
|
||||
// Remove the TabView from the page. We'll hang on to it, we need to
|
||||
// put it in the titlebar.
|
||||
uint32_t index = 0;
|
||||
if (this->Root().Children().IndexOf(_tabRow, index))
|
||||
{
|
||||
this->Root().Children().RemoveAt(index);
|
||||
}
|
||||
// Apply the tab position from the theme. This rearranges the grid
|
||||
// layout and potentially moves the tab row into the titlebar (for Top).
|
||||
_ApplyTabPosition();
|
||||
|
||||
// Inform the host that our titlebar content has changed.
|
||||
SetTitleBarContent.raise(*this, _tabRow);
|
||||
|
||||
// GH#13143 Manually set the tab row's background to transparent here.
|
||||
//
|
||||
// We're doing it this way because ThemeResources are tricky. We
|
||||
// default in XAML to using the appropriate ThemeResource background
|
||||
// color for our TabRow. When tabs in the titlebar are _disabled_,
|
||||
// this will ensure that the tab row has the correct theme-dependent
|
||||
// value. When tabs in the titlebar are _enabled_ (the default),
|
||||
// we'll switch the BG to Transparent, to let the Titlebar Control's
|
||||
// background be used as the BG for the tab row.
|
||||
//
|
||||
// We can't do it the other way around (default to Transparent, only
|
||||
// switch to a color when disabling tabs in the titlebar), because
|
||||
// looking up the correct ThemeResource from and App dictionary is a
|
||||
// capital-H Hard problem.
|
||||
const auto transparent = Media::SolidColorBrush();
|
||||
transparent.Color(Windows::UI::Colors::Transparent());
|
||||
_tabRow.Background(transparent);
|
||||
}
|
||||
_updateThemeColors();
|
||||
|
||||
// Initialize the state of the CloseButtonOverlayMode property of
|
||||
@@ -4951,7 +5225,7 @@ namespace winrt::TerminalApp::implementation
|
||||
TitlebarBrush(backgroundSolidBrush);
|
||||
}
|
||||
|
||||
if (!_settings.GlobalSettings().ShowTabsInTitlebar())
|
||||
if (_tabPosition != TabPosition::Top || !_settings.GlobalSettings().ShowTabsInTitlebar())
|
||||
{
|
||||
_tabRow.Background(TitlebarBrush());
|
||||
}
|
||||
@@ -5022,6 +5296,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// will eat focus.
|
||||
_paneResources.focusedBorderBrush = SolidColorBrush{ Colors::Black() };
|
||||
}
|
||||
_tabStripSplitter.Background(_paneResources.focusedBorderBrush);
|
||||
|
||||
const auto unfocusedBorderBrushKey = winrt::box_value(L"UnfocusedBorderBrush");
|
||||
if (res.HasKey(unfocusedBorderBrushKey))
|
||||
|
||||
@@ -227,6 +227,9 @@ namespace winrt::TerminalApp::implementation
|
||||
Microsoft::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr };
|
||||
winrt::TerminalApp::ColorPickupFlyout _tabColorPicker{ nullptr };
|
||||
|
||||
Microsoft::Terminal::Settings::Model::TabPosition _tabPosition{ Microsoft::Terminal::Settings::Model::TabPosition::Top };
|
||||
Windows::UI::Xaml::Controls::Border _tabStripSplitter{ nullptr };
|
||||
|
||||
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<TerminalApp::Tab> _tabs;
|
||||
@@ -243,6 +246,11 @@ namespace winrt::TerminalApp::implementation
|
||||
bool _isAlwaysOnTop{ false };
|
||||
bool _showTabsFullscreen{ false };
|
||||
|
||||
// Splitter drag state for left/right tab positions
|
||||
bool _splitterDragging{ false };
|
||||
double _splitterDragStartX{ 0.0 };
|
||||
double _splitterDragStartWidth{ 0.0 };
|
||||
|
||||
std::optional<uint32_t> _loadFromPersistedLayoutIdx{};
|
||||
|
||||
bool _rearranging{ false };
|
||||
@@ -342,6 +350,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void _UpdateTabIcon(Tab& tab);
|
||||
void _UpdateTabView();
|
||||
void _UpdateTabWidthMode();
|
||||
void _ApplyTabPosition();
|
||||
void _SetBackgroundImage(const winrt::Microsoft::Terminal::Settings::Model::IAppearanceConfig& newAppearance);
|
||||
|
||||
void _DuplicateFocusedTab();
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
Grid.Row="0"
|
||||
KeyUp="_KeyDownHandler" />
|
||||
|
||||
<StackPanel Grid.Row="1"
|
||||
<StackPanel x:Name="InfoBarPanel"
|
||||
Grid.Row="1"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<mux:InfoBar x:Name="KeyboardServiceWarningInfoBar"
|
||||
x:Load="False"
|
||||
@@ -84,29 +85,34 @@
|
||||
|
||||
<local:AboutDialog x:Name="AboutDialog"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
x:Load="False" />
|
||||
|
||||
<ContentDialog x:Name="QuitDialog"
|
||||
x:Uid="QuitDialog"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
x:Load="False"
|
||||
DefaultButton="Primary" />
|
||||
|
||||
<ContentDialog x:Name="CloseAllDialog"
|
||||
x:Uid="CloseAllDialog"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
x:Load="False"
|
||||
DefaultButton="Primary" />
|
||||
|
||||
<ContentDialog x:Name="CloseReadOnlyDialog"
|
||||
x:Uid="CloseReadOnlyDialog"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
x:Load="False"
|
||||
DefaultButton="Close" />
|
||||
|
||||
<ContentDialog x:Name="MultiLinePasteDialog"
|
||||
x:Uid="MultiLinePasteDialog"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
x:Load="False"
|
||||
DefaultButton="Primary">
|
||||
<StackPanel>
|
||||
@@ -127,12 +133,14 @@
|
||||
<ContentDialog x:Name="LargePasteDialog"
|
||||
x:Uid="LargePasteDialog"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
x:Load="False"
|
||||
DefaultButton="Primary" />
|
||||
|
||||
<ContentDialog x:Name="ControlNoticeDialog"
|
||||
x:Uid="ControlNoticeDialog"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
x:Load="False"
|
||||
DefaultButton="Primary">
|
||||
<TextBlock IsTextSelectionEnabled="True"
|
||||
@@ -147,6 +155,7 @@
|
||||
<ContentDialog x:Name="CouldNotOpenUriDialog"
|
||||
x:Uid="CouldNotOpenUriDialog"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
x:Load="False"
|
||||
DefaultButton="Primary">
|
||||
<TextBlock IsTextSelectionEnabled="True"
|
||||
@@ -162,6 +171,7 @@
|
||||
|
||||
<local:CommandPalette x:Name="CommandPaletteElement"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
VerticalAlignment="Stretch"
|
||||
x:Load="False"
|
||||
PreviewKeyDown="_KeyDownHandler"
|
||||
@@ -169,6 +179,7 @@
|
||||
|
||||
<local:SuggestionsControl x:Name="SuggestionsElement"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
x:Load="False"
|
||||
|
||||
@@ -270,6 +270,16 @@ namespace winrt::TerminalApp::implementation
|
||||
return _settings.GlobalSettings().ShowTabsInTitlebar();
|
||||
}
|
||||
|
||||
Microsoft::Terminal::Settings::Model::TabPosition TerminalWindow::GetTabPosition()
|
||||
{
|
||||
auto theme = Theme();
|
||||
if (auto window = theme.Window())
|
||||
{
|
||||
return window.TabPosition();
|
||||
}
|
||||
return Microsoft::Terminal::Settings::Model::TabPosition::Top;
|
||||
}
|
||||
|
||||
bool TerminalWindow::GetInitialAlwaysOnTop()
|
||||
{
|
||||
return _settings.GlobalSettings().AlwaysOnTop();
|
||||
|
||||
@@ -106,6 +106,7 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme();
|
||||
Microsoft::Terminal::Settings::Model::LaunchMode GetLaunchMode();
|
||||
bool GetShowTabsInTitlebar();
|
||||
Microsoft::Terminal::Settings::Model::TabPosition GetTabPosition();
|
||||
bool GetInitialAlwaysOnTop();
|
||||
bool GetInitialShowTabsFullscreen();
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
|
||||
@@ -84,6 +84,7 @@ namespace TerminalApp
|
||||
Windows.UI.Xaml.ElementTheme GetRequestedTheme();
|
||||
Microsoft.Terminal.Settings.Model.LaunchMode GetLaunchMode();
|
||||
Boolean GetShowTabsInTitlebar();
|
||||
Microsoft.Terminal.Settings.Model.TabPosition GetTabPosition();
|
||||
Boolean GetInitialAlwaysOnTop();
|
||||
Boolean GetInitialShowTabsFullscreen();
|
||||
Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension);
|
||||
|
||||
268
src/cascadia/TerminalApp/VerticalTabViewStyle.xaml
Normal file
268
src/cascadia/TerminalApp/VerticalTabViewStyle.xaml
Normal file
@@ -0,0 +1,268 @@
|
||||
<!--
|
||||
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
Retemplated styles for vertical (Left/Right) tab strip orientation.
|
||||
These styles override the horizontal TabView layout to stack tabs vertically
|
||||
in a sidebar configuration.
|
||||
-->
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:primitives="using:Microsoft.UI.Xaml.Controls.Primitives">
|
||||
|
||||
<!--
|
||||
VerticalTabViewListViewStyle:
|
||||
Override the ItemsPanel to use vertical orientation and switch scrolling
|
||||
from horizontal to vertical.
|
||||
-->
|
||||
<Style x:Key="VerticalTabViewListViewStyle"
|
||||
TargetType="primitives:TabViewListView">
|
||||
<Setter Property="TabNavigation" Value="Once" />
|
||||
<Setter Property="IsItemClickEnabled" Value="True" />
|
||||
<Setter Property="IsSwipeEnabled" Value="False" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
|
||||
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Enabled" />
|
||||
<Setter Property="ScrollViewer.IsHorizontalRailEnabled" Value="False" />
|
||||
<Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="True" />
|
||||
<Setter Property="SingleSelectionFollowsFocus" Value="False" />
|
||||
<Setter Property="ItemContainerTransitions">
|
||||
<Setter.Value>
|
||||
<TransitionCollection>
|
||||
<AddDeleteThemeTransition />
|
||||
<ContentThemeTransition />
|
||||
<ReorderThemeTransition />
|
||||
</TransitionCollection>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!--
|
||||
VerticalTabViewStyle:
|
||||
Retemplate the TabView control for a vertical sidebar layout.
|
||||
Instead of 4 columns in TabContainerGrid, we use 3 rows:
|
||||
Row 0 (Auto): Header (elevation shield)
|
||||
Row 1 (*): Tab list (fills vertical space)
|
||||
Row 2 (Auto): Footer (new tab button)
|
||||
The TabContentPresenter is removed — WT doesn't use it.
|
||||
-->
|
||||
<Style x:Key="VerticalTabViewStyle"
|
||||
TargetType="mux:TabView">
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="TabNavigation" Value="Once" />
|
||||
<Setter Property="IsAddTabButtonVisible" Value="False" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="mux:TabView">
|
||||
<Grid x:Name="TabContainerGrid">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header (elevation shield, etc.) -->
|
||||
<ContentPresenter x:Name="LeftContentPresenter"
|
||||
Grid.Row="0"
|
||||
Content="{TemplateBinding TabStripHeader}"
|
||||
ContentTemplate="{TemplateBinding TabStripHeaderTemplate}" />
|
||||
|
||||
<!-- Tab list -->
|
||||
<primitives:TabViewListView x:Name="TabListView"
|
||||
Grid.Row="1"
|
||||
AllowDrop="{TemplateBinding AllowDropTabs}"
|
||||
CanDragItems="{TemplateBinding CanDragTabs}"
|
||||
CanReorderItems="{TemplateBinding CanReorderTabs}"
|
||||
ItemTemplate="{TemplateBinding TabItemTemplate}"
|
||||
ItemTemplateSelector="{TemplateBinding TabItemTemplateSelector}"
|
||||
ItemsSource="{TemplateBinding TabItemsSource}"
|
||||
Style="{StaticResource VerticalTabViewListViewStyle}" />
|
||||
|
||||
<!-- Footer (new tab button) -->
|
||||
<ContentPresenter x:Name="RightContentPresenter"
|
||||
Grid.Row="2"
|
||||
Content="{TemplateBinding TabStripFooter}"
|
||||
ContentTemplate="{TemplateBinding TabStripFooterTemplate}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!--
|
||||
VerticalTabViewItemStyle:
|
||||
Simplified TabViewItem template for vertical tabs.
|
||||
Removes curved corner arcs, replaces with rounded Border and a
|
||||
left-edge accent bar for the selected tab. Tabs fill strip width.
|
||||
-->
|
||||
<Style x:Key="VerticalTabViewItemStyle"
|
||||
TargetType="mux:TabViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="Padding" Value="8,6,8,6" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="mux:TabViewItem">
|
||||
<Grid x:Name="LayoutRoot"
|
||||
Margin="2,1,2,1">
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TabContainer.Background" Value="Transparent" />
|
||||
<Setter Target="SelectedIndicator.Opacity" Value="0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TabContainer.Background" Value="{ThemeResource TabViewItemHeaderBackgroundPointerOver}" />
|
||||
<Setter Target="SelectedIndicator.Opacity" Value="0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TabContainer.Background" Value="{ThemeResource TabViewItemHeaderBackgroundPressed}" />
|
||||
<Setter Target="SelectedIndicator.Opacity" Value="0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Selected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TabContainer.Background" Value="{ThemeResource TabViewItemHeaderBackgroundSelected}" />
|
||||
<Setter Target="SelectedIndicator.Opacity" Value="1" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="PointerOverSelected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TabContainer.Background" Value="{ThemeResource TabViewItemHeaderBackgroundSelected}" />
|
||||
<Setter Target="SelectedIndicator.Opacity" Value="1" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="PressedSelected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TabContainer.Background" Value="{ThemeResource TabViewItemHeaderBackgroundPressed}" />
|
||||
<Setter Target="SelectedIndicator.Opacity" Value="1" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="CloseButtonOverlayModeStates">
|
||||
<VisualState x:Name="CloseButtonVisible" />
|
||||
<VisualState x:Name="CloseButtonCollapsed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="CloseButton.Visibility" Value="Collapsed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="TabViewWidthModeStates">
|
||||
<!-- In vertical mode, tabs always stretch to full width -->
|
||||
<VisualState x:Name="StandardWidth" />
|
||||
<VisualState x:Name="Compact" />
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="IconStates">
|
||||
<VisualState x:Name="Icon" />
|
||||
<VisualState x:Name="NoIcon">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="IconBox.Visibility" Value="Collapsed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="DisabledStates">
|
||||
<VisualState x:Name="Enabled" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="LayoutRoot.Opacity" Value="{ThemeResource ListViewItemDisabledThemeOpacity}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<!-- Selected tab background -->
|
||||
<Border x:Name="TabContainer"
|
||||
Background="Transparent"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="IconColumn"
|
||||
Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Tab icon -->
|
||||
<Viewbox x:Name="IconBox"
|
||||
Grid.Column="0"
|
||||
MaxWidth="16"
|
||||
MaxHeight="16"
|
||||
Margin="0,0,8,0">
|
||||
<ContentControl x:Name="IconControl"
|
||||
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TabViewTemplateSettings.IconElement}"
|
||||
IsTabStop="False" />
|
||||
</Viewbox>
|
||||
|
||||
<!-- Tab title -->
|
||||
<ContentPresenter x:Name="ContentPresenter"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
FontWeight="{TemplateBinding FontWeight}"
|
||||
TextWrapping="NoWrap" />
|
||||
|
||||
<!-- Close button -->
|
||||
<Button x:Name="CloseButton"
|
||||
Grid.Column="2"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Margin="4,0,0,0"
|
||||
Padding="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Content=""
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="10"
|
||||
IsTextScaleFactorEnabled="False" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Left-edge accent bar for selected tab -->
|
||||
<Border x:Name="SelectedIndicator"
|
||||
Width="3"
|
||||
Height="16"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||
CornerRadius="2"
|
||||
Opacity="0" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -158,7 +158,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(winrt::Microsoft::Terminal::Settings::Model::TabPosition, TabPosition, "tabPosition", winrt::Microsoft::Terminal::Settings::Model::TabPosition::Top)
|
||||
|
||||
#define MTSM_THEME_SETTINGS_SETTINGS(X) \
|
||||
X(winrt::Windows::UI::Xaml::ElementTheme, RequestedTheme, "theme", winrt::Windows::UI::Xaml::ElementTheme::Default)
|
||||
|
||||
@@ -673,6 +673,16 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::IconStyle)
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::TabPosition)
|
||||
{
|
||||
JSON_MAPPINGS(4) = {
|
||||
pair_type{ "top", ValueType::Top },
|
||||
pair_type{ "bottom", ValueType::Bottom },
|
||||
pair_type{ "left", ValueType::Left },
|
||||
pair_type{ "right", ValueType::Right },
|
||||
};
|
||||
};
|
||||
|
||||
// Possible ScrollToMarkDirection values
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::ScrollToMarkDirection)
|
||||
{
|
||||
|
||||
@@ -11,6 +11,14 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Monochrome
|
||||
};
|
||||
|
||||
enum TabPosition
|
||||
{
|
||||
Top,
|
||||
Bottom,
|
||||
Left,
|
||||
Right
|
||||
};
|
||||
|
||||
enum ThemeColorType
|
||||
{
|
||||
Accent,
|
||||
@@ -64,6 +72,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Boolean RainbowFrame { get; };
|
||||
ThemeColor Frame { get; };
|
||||
ThemeColor UnfocusedFrame { get; };
|
||||
TabPosition TabPosition { get; };
|
||||
}
|
||||
|
||||
runtimeclass TabRowTheme {
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace SettingsModelUnitTests
|
||||
TEST_METHOD(ParseNullWindowTheme);
|
||||
TEST_METHOD(ParseThemeWithNullThemeColor);
|
||||
TEST_METHOD(InvalidCurrentTheme);
|
||||
TEST_METHOD(ParseTabPosition);
|
||||
|
||||
static Core::Color rgb(uint8_t r, uint8_t g, uint8_t b) noexcept
|
||||
{
|
||||
@@ -265,4 +266,103 @@ namespace SettingsModelUnitTests
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
void ThemeTests::ParseTabPosition()
|
||||
{
|
||||
Log::Comment(L"Verify tabPosition round-trips through JSON serialization.");
|
||||
|
||||
// Test each of the four tab positions
|
||||
static constexpr std::string_view leftTheme{ R"({
|
||||
"name": "leftTabs",
|
||||
"window":
|
||||
{
|
||||
"tabPosition": "left"
|
||||
}
|
||||
})" };
|
||||
|
||||
static constexpr std::string_view rightTheme{ R"({
|
||||
"name": "rightTabs",
|
||||
"window":
|
||||
{
|
||||
"tabPosition": "right"
|
||||
}
|
||||
})" };
|
||||
|
||||
static constexpr std::string_view bottomTheme{ R"({
|
||||
"name": "bottomTabs",
|
||||
"window":
|
||||
{
|
||||
"tabPosition": "bottom"
|
||||
}
|
||||
})" };
|
||||
|
||||
static constexpr std::string_view topTheme{ R"({
|
||||
"name": "topTabs",
|
||||
"window":
|
||||
{
|
||||
"tabPosition": "top"
|
||||
}
|
||||
})" };
|
||||
|
||||
// Test default (no tabPosition specified)
|
||||
static constexpr std::string_view defaultTheme{ R"({
|
||||
"name": "defaultTabs",
|
||||
"window":
|
||||
{
|
||||
"useMica": false
|
||||
}
|
||||
})" };
|
||||
|
||||
// Parse and verify "left"
|
||||
{
|
||||
const auto schemeObject = VerifyParseSucceeded(leftTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(L"leftTabs", theme->Name());
|
||||
VERIFY_IS_NOT_NULL(theme->Window());
|
||||
VERIFY_ARE_EQUAL(Settings::Model::TabPosition::Left, theme->Window().TabPosition());
|
||||
|
||||
// Re-serialize and verify the key is present
|
||||
const auto reJson = theme->ToJson();
|
||||
VERIFY_IS_TRUE(reJson.isMember("window"));
|
||||
VERIFY_IS_TRUE(reJson["window"].isMember("tabPosition"));
|
||||
VERIFY_ARE_EQUAL("left", reJson["window"]["tabPosition"].asString());
|
||||
}
|
||||
|
||||
// Parse and verify "right"
|
||||
{
|
||||
const auto schemeObject = VerifyParseSucceeded(rightTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(Settings::Model::TabPosition::Right, theme->Window().TabPosition());
|
||||
|
||||
const auto reJson = theme->ToJson();
|
||||
VERIFY_ARE_EQUAL("right", reJson["window"]["tabPosition"].asString());
|
||||
}
|
||||
|
||||
// Parse and verify "bottom"
|
||||
{
|
||||
const auto schemeObject = VerifyParseSucceeded(bottomTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(Settings::Model::TabPosition::Bottom, theme->Window().TabPosition());
|
||||
|
||||
const auto reJson = theme->ToJson();
|
||||
VERIFY_ARE_EQUAL("bottom", reJson["window"]["tabPosition"].asString());
|
||||
}
|
||||
|
||||
// Parse and verify "top"
|
||||
{
|
||||
const auto schemeObject = VerifyParseSucceeded(topTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(Settings::Model::TabPosition::Top, theme->Window().TabPosition());
|
||||
|
||||
const auto reJson = theme->ToJson();
|
||||
VERIFY_ARE_EQUAL("top", reJson["window"]["tabPosition"].asString());
|
||||
}
|
||||
|
||||
// Parse default — should be Top
|
||||
{
|
||||
const auto schemeObject = VerifyParseSucceeded(defaultTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(Settings::Model::TabPosition::Top, theme->Window().TabPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,9 @@ AppHost::AppHost(WindowEmperor* manager, const winrt::TerminalApp::AppLogic& log
|
||||
_HandleCommandlineArgs(args);
|
||||
|
||||
// _HandleCommandlineArgs will create a _windowLogic
|
||||
_useNonClientArea = _windowLogic.GetShowTabsInTitlebar();
|
||||
const auto tabPos = _windowLogic.GetTabPosition();
|
||||
_useNonClientArea = (tabPos == winrt::Microsoft::Terminal::Settings::Model::TabPosition::Top) &&
|
||||
_windowLogic.GetShowTabsInTitlebar();
|
||||
|
||||
if (_useNonClientArea)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user