diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 24f42ed5791..a20d757a1ba 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -6,6 +6,7 @@ #include #include "App.g.cpp" +#include "TerminalPage.h" using namespace winrt::Windows::ApplicationModel::DataTransfer; using namespace winrt::Windows::UI::Xaml; @@ -76,110 +77,43 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Create all of the initial UI elements of the Terminal app. - // * Creates the tab bar, initially hidden. - // * Creates the tab content area, which is where we'll display the tabs/panes. // * Initializes the first terminal control, using the default profile, // and adds it to our list of tabs. void App::_Create(uint64_t parentHwnd) { - _tabView = MUX::Controls::TabView{}; - - _tabView.SelectionChanged({ this, &App::_OnTabSelectionChanged }); - _tabView.TabClosing({ this, &App::_OnTabClosing }); - _tabView.Items().VectorChanged({ this, &App::_OnTabItemsChanged }); - - _root = Controls::Grid{}; - - _tabRow = Controls::Grid{}; - _tabRow.Name(L"Tab Row"); - _tabContent = Controls::Grid{}; - _tabContent.Name(L"Tab Content"); - - // Set up two columns in the tabs row - one for the tabs themselves, and - // another for the settings button. - auto tabsColDef = Controls::ColumnDefinition(); - auto newTabBtnColDef = Controls::ColumnDefinition(); - newTabBtnColDef.Width(GridLengthHelper::Auto()); - - _tabRow.ColumnDefinitions().Append(tabsColDef); - _tabRow.ColumnDefinitions().Append(newTabBtnColDef); - - // Set up two rows - one for the tabs, the other for the tab content, - // the terminal panes. - auto tabBarRowDef = Controls::RowDefinition(); - tabBarRowDef.Height(GridLengthHelper::Auto()); - _root.RowDefinitions().Append(tabBarRowDef); - _root.RowDefinitions().Append(Controls::RowDefinition{}); - - _root.Children().Append(_tabRow); - - Controls::Grid::SetRow(_tabRow, 0); - - _root.Children().Append(_tabContent); - Controls::Grid::SetRow(_tabContent, 1); - Controls::Grid::SetColumn(_tabView, 0); - - // Create the new tab button. - _newTabButton = Controls::SplitButton{}; - Controls::SymbolIcon newTabIco{}; - newTabIco.Symbol(Controls::Symbol::Add); - _newTabButton.Content(newTabIco); - Controls::Grid::SetRow(_newTabButton, 0); - Controls::Grid::SetColumn(_newTabButton, 1); - _newTabButton.VerticalAlignment(VerticalAlignment::Stretch); - _newTabButton.HorizontalAlignment(HorizontalAlignment::Left); + /* !!! TODO + This is not the correct way to host a XAML page. This exists today because we valued + getting a .xaml over tearing out all of the terminal logic and splitting it across App + and Page. + The work to clarify the boundary between app global state and "terminal page" state + is tracked in GH#1878. + */ + auto terminalPage = winrt::make_self(); + _root = terminalPage.as(); + _tabContent = terminalPage->TabContent(); + _tabRow = terminalPage->TabRow(); + _tabView = terminalPage->TabView(); + _newTabButton = terminalPage->NewTabButton(); + + _minMaxCloseControl = terminalPage->MinMaxCloseControl(); + _minMaxCloseControl.ParentWindowHandle(parentHwnd); + + if (!_settings->GlobalSettings().GetShowTabsInTitlebar()) + { + _minMaxCloseControl.Visibility(Visibility::Collapsed); + } - // When the new tab button is clicked, open the default profile + // Event Bindings (Early) _newTabButton.Click([this](auto&&, auto&&) { this->_OpenNewTab(std::nullopt); }); + _tabView.SelectionChanged({ this, &App::_OnTabSelectionChanged }); + _tabView.TabClosing({ this, &App::_OnTabClosing }); + _tabView.Items().VectorChanged({ this, &App::_OnTabItemsChanged }); + _root.Loaded({ this, &App::_OnLoaded }); - // Populate the new tab button's flyout with entries for each profile _CreateNewTabFlyout(); - - _tabRow.Children().Append(_tabView); - - if (_settings->GlobalSettings().GetShowTabsInTitlebar()) - { - _minMaxCloseControl = winrt::TerminalApp::MinMaxCloseControl(parentHwnd); - Controls::Grid::SetRow(_minMaxCloseControl, 0); - Controls::Grid::SetColumn(_minMaxCloseControl, 1); - _minMaxCloseControl.Content().Children().Append(_newTabButton); - - _tabRow.Children().Append(_minMaxCloseControl); - } - else - { - _tabRow.Children().Append(_newTabButton); - } - - _tabContent.VerticalAlignment(VerticalAlignment::Stretch); - _tabContent.HorizontalAlignment(HorizontalAlignment::Stretch); - - // Here, we're doing the equivalent of defining the _tabRow as the - // following: We need to set the Background - // to that ThemeResource, so it'll be colored appropriately regardless - // of what theme the user has selected. - // We're looking up the Style we've defined in App.xaml, and applying it - // here. A ResourceDictionary is a Map, so - // you'll need to try_as to get the type we actually want. - auto res = Resources(); - IInspectable key = winrt::box_value(L"BackgroundGridThemeStyle"); - if (res.HasKey(key)) - { - IInspectable g = res.Lookup(key); - winrt::Windows::UI::Xaml::Style style = g.try_as(); - _root.Style(style); - _tabRow.Style(style); - } - - // Apply the UI theme from our settings to our UI elements - _ApplyTheme(_settings->GlobalSettings().GetRequestedTheme()); - _OpenNewTab(std::nullopt); - - _root.Loaded({ this, &App::_OnLoaded }); } // Method Description: @@ -210,9 +144,10 @@ namespace winrt::TerminalApp::implementation dialog.Content(contentElement); dialog.CloseButtonText(closeButtonText); - // IMPORTANT: Add the dialog to the _root UIElement before you show it, - // so it knows how to attach to the XAML content. - _root.Children().Append(dialog); + // IMPORTANT: This is necessary as documented in the ContentDialog MSDN docs. + // Since we're hosting the dialog in a Xaml island, we need to connect it to the + // xaml tree somehow. + dialog.XamlRoot(_root.XamlRoot()); // Display the dialog. Controls::ContentDialogResult result = co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); @@ -766,7 +701,6 @@ namespace winrt::TerminalApp::implementation void App::_ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme) { _root.RequestedTheme(newTheme); - _tabRow.RequestedTheme(newTheme); } UIElement App::GetRoot() noexcept @@ -774,11 +708,6 @@ namespace winrt::TerminalApp::implementation return _root; } - UIElement App::GetTabs() noexcept - { - return _tabRow; - } - void App::_SetFocusedTabIndex(int tabIndex) { // GH#1117: This is a workaround because _tabView.SelectedIndex(tabIndex) diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 1b1cc31bb62..c1e944da1c0 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -26,7 +26,6 @@ namespace winrt::TerminalApp::implementation App(); Windows::UI::Xaml::UIElement GetRoot() noexcept; - Windows::UI::Xaml::UIElement GetTabs() noexcept; // Gets the current dragglable area in the non client region of the top level window Windows::UI::Xaml::Controls::Border GetDragBar() noexcept; @@ -52,7 +51,7 @@ namespace winrt::TerminalApp::implementation // ALSO: If you add any UIElements as roots here, make sure they're // updated in _ApplyTheme. The two roots currently are _root and _tabRow // (which is a root when the tabs are in the titlebar.) - Windows::UI::Xaml::Controls::Grid _root{ nullptr }; + Windows::UI::Xaml::Controls::Control _root{ nullptr }; Microsoft::UI::Xaml::Controls::TabView _tabView{ nullptr }; Windows::UI::Xaml::Controls::Grid _tabRow{ nullptr }; Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr }; diff --git a/src/cascadia/TerminalApp/App.idl b/src/cascadia/TerminalApp/App.idl index bd9ec0fdad5..20a14edff00 100644 --- a/src/cascadia/TerminalApp/App.idl +++ b/src/cascadia/TerminalApp/App.idl @@ -21,7 +21,6 @@ namespace TerminalApp void LoadSettings(); Windows.UI.Xaml.UIElement GetRoot(); - Windows.UI.Xaml.UIElement GetTabs(); Windows.UI.Xaml.Controls.Border GetDragBar{ get; }; Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi); diff --git a/src/cascadia/TerminalApp/MinMaxCloseControl.cpp b/src/cascadia/TerminalApp/MinMaxCloseControl.cpp index e10bb185037..43616abf45c 100644 --- a/src/cascadia/TerminalApp/MinMaxCloseControl.cpp +++ b/src/cascadia/TerminalApp/MinMaxCloseControl.cpp @@ -11,29 +11,41 @@ namespace winrt::TerminalApp::implementation { - MinMaxCloseControl::MinMaxCloseControl(uint64_t hWnd) : - _window(reinterpret_cast(hWnd)) + MinMaxCloseControl::MinMaxCloseControl() { const winrt::Windows::Foundation::Uri resourceLocator{ L"ms-appx:///MinMaxCloseControl.xaml" }; winrt::Windows::UI::Xaml::Application::LoadComponent(*this, resourceLocator, winrt::Windows::UI::Xaml::Controls::Primitives::ComponentResourceLocation::Nested); } + uint64_t MinMaxCloseControl::ParentWindowHandle() const + { + return reinterpret_cast(_window); + } + + void MinMaxCloseControl::ParentWindowHandle(uint64_t handle) + { + _window = reinterpret_cast(handle); + } + void MinMaxCloseControl::_OnMaximize(byte flag) { - POINT point1 = {}; - ::GetCursorPos(&point1); - const LPARAM lParam = MAKELPARAM(point1.x, point1.y); - WINDOWPLACEMENT placement = { sizeof(placement) }; - ::GetWindowPlacement(_window, &placement); - if (placement.showCmd == SW_SHOWNORMAL) - { - winrt::Windows::UI::Xaml::VisualStateManager::GoToState(this->Maximize(), L"WindowStateMaximized", false); - ::PostMessage(_window, WM_SYSCOMMAND, SC_MAXIMIZE | flag, lParam); - } - else if (placement.showCmd == SW_SHOWMAXIMIZED) + if (_window) { - winrt::Windows::UI::Xaml::VisualStateManager::GoToState(this->Maximize(), L"WindowStateNormal", false); - ::PostMessage(_window, WM_SYSCOMMAND, SC_RESTORE | flag, lParam); + POINT point1 = {}; + ::GetCursorPos(&point1); + const LPARAM lParam = MAKELPARAM(point1.x, point1.y); + WINDOWPLACEMENT placement = { sizeof(placement) }; + ::GetWindowPlacement(_window, &placement); + if (placement.showCmd == SW_SHOWNORMAL) + { + winrt::Windows::UI::Xaml::VisualStateManager::GoToState(this->Maximize(), L"WindowStateMaximized", false); + ::PostMessage(_window, WM_SYSCOMMAND, SC_MAXIMIZE | flag, lParam); + } + else if (placement.showCmd == SW_SHOWMAXIMIZED) + { + winrt::Windows::UI::Xaml::VisualStateManager::GoToState(this->Maximize(), L"WindowStateNormal", false); + ::PostMessage(_window, WM_SYSCOMMAND, SC_RESTORE | flag, lParam); + } } } @@ -49,7 +61,10 @@ namespace winrt::TerminalApp::implementation void MinMaxCloseControl::Minimize_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e) { - ::PostMessage(_window, WM_SYSCOMMAND, SC_MINIMIZE | HTMINBUTTON, 0); + if (_window) + { + ::PostMessage(_window, WM_SYSCOMMAND, SC_MINIMIZE | HTMINBUTTON, 0); + } } void MinMaxCloseControl::Close_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e) diff --git a/src/cascadia/TerminalApp/MinMaxCloseControl.h b/src/cascadia/TerminalApp/MinMaxCloseControl.h index e819f540632..917736c1e76 100644 --- a/src/cascadia/TerminalApp/MinMaxCloseControl.h +++ b/src/cascadia/TerminalApp/MinMaxCloseControl.h @@ -13,16 +13,19 @@ namespace winrt::TerminalApp::implementation { struct MinMaxCloseControl : MinMaxCloseControlT { - MinMaxCloseControl(uint64_t hWnd); + MinMaxCloseControl(); void Minimize_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); void Maximize_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); void Close_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); void DragBar_DoubleTapped(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Input::DoubleTappedRoutedEventArgs const& e); + uint64_t ParentWindowHandle() const; + void ParentWindowHandle(uint64_t handle); + private: void _OnMaximize(byte flag); - HWND _window = nullptr; + HWND _window{ nullptr }; // non-owning handle; should not be freed in the dtor. }; } diff --git a/src/cascadia/TerminalApp/MinMaxCloseControl.idl b/src/cascadia/TerminalApp/MinMaxCloseControl.idl index 5021204c1df..26f0295485c 100644 --- a/src/cascadia/TerminalApp/MinMaxCloseControl.idl +++ b/src/cascadia/TerminalApp/MinMaxCloseControl.idl @@ -3,9 +3,11 @@ [default_interface] runtimeclass MinMaxCloseControl : Windows.UI.Xaml.Controls.StackPanel { - MinMaxCloseControl(UInt64 hParentWnd); + MinMaxCloseControl(); Windows.UI.Xaml.Controls.Grid Content{ get; }; Windows.UI.Xaml.Controls.Border DragBar{ get; }; + + UInt64 ParentWindowHandle; } } diff --git a/src/cascadia/TerminalApp/TerminalApp.vcxproj b/src/cascadia/TerminalApp/TerminalApp.vcxproj index f36a9b09c4d..85d16aa4365 100644 --- a/src/cascadia/TerminalApp/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/TerminalApp.vcxproj @@ -36,6 +36,10 @@ + + TerminalPage.xaml + Code + MinMaxCloseControl.xaml @@ -58,9 +62,17 @@ + + TerminalPage.xaml + Code + MinMaxCloseControl.xaml + + TerminalPage.xaml + Code + MinMaxCloseControl.xaml Code @@ -119,6 +131,9 @@ + + Designer + Designer diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp new file mode 100644 index 00000000000..315d18f09ac --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "TerminalPage.h" + +#include "TerminalPage.g.cpp" + +using namespace winrt; +using namespace Windows::UI::Xaml; + +namespace winrt::TerminalApp::implementation +{ + TerminalPage::TerminalPage() + { + // The generated code will by default attempt to load from ms-appx://TerminalApp/TerminalPage.xaml. + // We'll force it to load from the root of the appx instead. + const winrt::Windows::Foundation::Uri resourceLocator{ L"ms-appx:///TerminalPage.xaml" }; + winrt::Windows::UI::Xaml::Application::LoadComponent(*this, resourceLocator, winrt::Windows::UI::Xaml::Controls::Primitives::ComponentResourceLocation::Nested); + } + + // Method Description: + // - Bound in the Xaml editor to the [+] button. + // Arguments: + // - sender + // - event arguments + void TerminalPage::OnNewTabButtonClick(IInspectable const&, Controls::SplitButtonClickEventArgs const&) + { + } +} diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h new file mode 100644 index 00000000000..c061a407f7f --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "winrt/Microsoft.UI.Xaml.Controls.h" + +#include "TerminalPage.g.h" + +namespace winrt::TerminalApp::implementation +{ + struct TerminalPage : TerminalPageT + { + TerminalPage(); + + void OnNewTabButtonClick(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Controls::SplitButtonClickEventArgs const& args); + }; +} + +namespace winrt::TerminalApp::factory_implementation +{ + struct TerminalPage : TerminalPageT + { + }; +} diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl new file mode 100644 index 00000000000..8ecf8e18bac --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace TerminalApp +{ + [default_interface] + runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page + { + TerminalPage(); + } +} diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml new file mode 100644 index 00000000000..4fad7af787a --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalApp/pch.h b/src/cascadia/TerminalApp/pch.h index ef9a19717c8..11f5c33b608 100644 --- a/src/cascadia/TerminalApp/pch.h +++ b/src/cascadia/TerminalApp/pch.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include