Move most of TerminalApp's runtime Xaml to a .xaml file and class (#1885)

This commit is contained in:
Dustin L. Howett (MSFT) 2019-07-09 14:47:30 -07:00 committed by GitHub
parent cfbc9e8f9f
commit 122f0de382
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 198 additions and 124 deletions

View file

@ -6,6 +6,7 @@
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
#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{};
/* !!! 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<TerminalPage>();
_root = terminalPage.as<winrt::Windows::UI::Xaml::Controls::Control>();
_tabContent = terminalPage->TabContent();
_tabRow = terminalPage->TabRow();
_tabView = terminalPage->TabView();
_newTabButton = terminalPage->NewTabButton();
_tabView.SelectionChanged({ this, &App::_OnTabSelectionChanged });
_tabView.TabClosing({ this, &App::_OnTabClosing });
_tabView.Items().VectorChanged({ this, &App::_OnTabItemsChanged });
_minMaxCloseControl = terminalPage->MinMaxCloseControl();
_minMaxCloseControl.ParentWindowHandle(parentHwnd);
_root = Controls::Grid{};
if (!_settings->GlobalSettings().GetShowTabsInTitlebar())
{
_minMaxCloseControl.Visibility(Visibility::Collapsed);
}
_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);
// When the new tab button is clicked, open the default profile
// Event Bindings (Early)
_newTabButton.Click([this](auto&&, auto&&) {
this->_OpenNewTab(std::nullopt);
});
// 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: <Grid Background="{ThemeResource
// ApplicationPageBackgroundThemeBrush}"> 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<IInspectable, IInspectable>, 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<winrt::Windows::UI::Xaml::Style>();
_root.Style(style);
_tabRow.Style(style);
}
// Apply the UI theme from our settings to our UI elements
_ApplyTheme(_settings->GlobalSettings().GetRequestedTheme());
_OpenNewTab(std::nullopt);
_tabView.SelectionChanged({ this, &App::_OnTabSelectionChanged });
_tabView.TabClosing({ this, &App::_OnTabClosing });
_tabView.Items().VectorChanged({ this, &App::_OnTabItemsChanged });
_root.Loaded({ this, &App::_OnLoaded });
_CreateNewTabFlyout();
_OpenNewTab(std::nullopt);
}
// 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)

View file

@ -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 };

View file

@ -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);

View file

@ -11,29 +11,41 @@
namespace winrt::TerminalApp::implementation
{
MinMaxCloseControl::MinMaxCloseControl(uint64_t hWnd) :
_window(reinterpret_cast<HWND>(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<uint64_t>(_window);
}
void MinMaxCloseControl::ParentWindowHandle(uint64_t handle)
{
_window = reinterpret_cast<HWND>(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)
if (_window)
{
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);
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)

View file

@ -13,16 +13,19 @@ namespace winrt::TerminalApp::implementation
{
struct MinMaxCloseControl : MinMaxCloseControlT<MinMaxCloseControl>
{
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.
};
}

View file

@ -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;
}
}

View file

@ -36,6 +36,10 @@
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="App.base.h" />
<ClInclude Include="TerminalPage.h">
<DependentUpon>TerminalPage.xaml</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="MinMaxCloseControl.h">
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
</ClInclude>
@ -58,9 +62,17 @@
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
<ClCompile Include="TerminalPage.cpp">
<DependentUpon>TerminalPage.xaml</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="MinMaxCloseControl.cpp">
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
</ClCompile>
<Midl Include="TerminalPage.idl">
<DependentUpon>TerminalPage.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="MinMaxCloseControl.idl">
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
<SubType>Code</SubType>
@ -119,6 +131,9 @@
<!-- This is needed to be able to reference the XamlApplication type. -->
</ItemGroup>
<ItemGroup>
<Page Include="TerminalPage.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="MinMaxCloseControl.xaml">
<SubType>Designer</SubType>
</Page>

View file

@ -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&)
{
}
}

View file

@ -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>
{
TerminalPage();
void OnNewTabButtonClick(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Controls::SplitButtonClickEventArgs const& args);
};
}
namespace winrt::TerminalApp::factory_implementation
{
struct TerminalPage : TerminalPageT<TerminalPage, implementation::TerminalPage>
{
};
}

View file

@ -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();
}
}

View file

@ -0,0 +1,45 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information. -->
<Page
x:Class="TerminalApp.TerminalPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TerminalApp"
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="TabRow" Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<mux:TabView x:Name="TabView" Grid.Column="0" />
<SplitButton
x:Name="NewTabButton"
Grid.Column="1"
Click="OnNewTabButtonClick"
VerticalAlignment="Stretch"
HorizontalAlignment="Left">
<SymbolIcon Symbol="Add" />
</SplitButton>
<local:MinMaxCloseControl
x:Name="MinMaxCloseControl"
Grid.Column="3"
HorizontalAlignment="Right" />
</Grid>
<Grid x:Name="TabContent" Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
</Grid>
</Page>

View file

@ -32,6 +32,7 @@
#include <winrt/Windows.UI.Text.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.UI.Xaml.Markup.h>
#include <winrt/Windows.ui.xaml.media.h>
#include <winrt/Windows.ui.xaml.input.h>
#include <winrt/Windows.UI.Xaml.Hosting.h>