Enable resizing the panes with the keyboard. (#1207)

Adds the ability to resize panes with the keyboard. 

This is accomplished by making the Column/RowDefinitions for a Pane use `GridLengthHelper::FromPixels` to set their size. We store a pair of floats that represents the relative amount that each pane takes out of the parent pane. When the window is resized, we use that percentage to figure out the new size of each child in pixels, and manually size each column. 

Then, when the user presses the keybindings for resizePane{Left/Right/Up/Down}, we'll adjust those percentages, and resize the rows/cols as appropriate.

Currently, each pane adjusts the width/height by 5% of the total size at a time. I am not in love with this, but it works for now. I think when we get support for keybindings with arbitrary arg blobs, then we could do either a percent movement, or a number of characters at a time. The number of characters one would be trickier, because we'd have to get the focused control, and get the number of pixels per character, as adjacent panes might not have the same font sizes.
This commit is contained in:
Mike Griese 2019-07-10 08:27:12 -05:00 committed by GitHub
parent 122f0de382
commit 2de2f445c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 466 additions and 21 deletions

View file

@ -114,6 +114,8 @@ namespace winrt::TerminalApp::implementation
_CreateNewTabFlyout();
_OpenNewTab(std::nullopt);
_tabContent.SizeChanged({ this, &App::_OnContentSizeChanged });
}
// Method Description:
@ -458,6 +460,7 @@ namespace winrt::TerminalApp::implementation
bindings.ScrollDownPage([this]() { _ScrollPage(1); });
bindings.SwitchToTab([this](const auto index) { _SelectTab({ index }); });
bindings.OpenSettings([this]() { _OpenSettings(); });
bindings.ResizePane([this](const auto direction) { _ResizePane(direction); });
bindings.CopyText([this](const auto trimWhitespace) { _CopyText(trimWhitespace); });
bindings.PasteText([this]() { _PasteText(); });
}
@ -967,6 +970,20 @@ namespace winrt::TerminalApp::implementation
_tabs[focusedTabIndex]->Scroll(termHeight * delta);
}
// Method Description:
// - Attempt to move a separator between panes, as to resize each child on
// either size of the separator. See Pane::ResizePane for details.
// - Moves a separator on the currently focused tab.
// Arguments:
// - direction: The direction to move the separator in.
// Return Value:
// - <none>
void App::_ResizePane(const Direction& direction)
{
const auto focusedTabIndex = _GetFocusedTabIndex();
_tabs[focusedTabIndex]->ResizePane(direction);
}
// Method Description:
// - Copy text from the focused terminal to the Windows Clipboard
// Arguments:
@ -1256,6 +1273,23 @@ namespace winrt::TerminalApp::implementation
focusedTab->AddVerticalSplit(realGuid, newControl);
}
// Method Description:
// - Called when our tab content size changes. This updates each tab with
// the new size, so they have a chance to update each of their panes with
// the new size.
// Arguments:
// - e: the SizeChangedEventArgs with the new size of the tab content area.
// Return Value:
// - <none>
void App::_OnContentSizeChanged(const IInspectable& /*sender*/, Windows::UI::Xaml::SizeChangedEventArgs const& e)
{
const auto newSize = e.NewSize();
for (auto& tab : _tabs)
{
tab->ResizeContent(newSize);
}
}
// Method Description:
// - Place `copiedData` into the clipboard as text. Triggered when a
// terminal control raises it's CopyToClipboard event.

View file

@ -121,12 +121,14 @@ namespace winrt::TerminalApp::implementation
// Todo: add more event implementations here
// MSFT:20641986: Add keybindings for New Window
void _ScrollPage(int delta);
void _ResizePane(const Direction& direction);
void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _OnTabSelectionChanged(const IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& eventArgs);
void _OnTabClosing(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabClosingEventArgs& eventArgs);
void _OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs);
void _OnTabClick(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
void _OnContentSizeChanged(const IInspectable& sender, Windows::UI::Xaml::SizeChangedEventArgs const& e);
void _RemoveTabViewItem(const IInspectable& tabViewItem);

View file

@ -157,6 +157,18 @@ namespace winrt::TerminalApp::implementation
case ShortcutAction::SwitchToTab8:
_SwitchToTabHandlers(8);
return true;
case ShortcutAction::ResizePaneLeft:
_ResizePaneHandlers(Direction::Left);
return true;
case ShortcutAction::ResizePaneRight:
_ResizePaneHandlers(Direction::Right);
return true;
case ShortcutAction::ResizePaneUp:
_ResizePaneHandlers(Direction::Up);
return true;
case ShortcutAction::ResizePaneDown:
_ResizePaneHandlers(Direction::Down);
return true;
default:
return false;
@ -238,5 +250,6 @@ namespace winrt::TerminalApp::implementation
DEFINE_EVENT(AppKeyBindings, ScrollUpPage, _ScrollUpPageHandlers, TerminalApp::ScrollUpPageEventArgs);
DEFINE_EVENT(AppKeyBindings, ScrollDownPage, _ScrollDownPageHandlers, TerminalApp::ScrollDownPageEventArgs);
DEFINE_EVENT(AppKeyBindings, OpenSettings, _OpenSettingsHandlers, TerminalApp::OpenSettingsEventArgs);
DEFINE_EVENT(AppKeyBindings, ResizePane, _ResizePaneHandlers, TerminalApp::ResizePaneEventArgs);
// clang-format on
}

View file

@ -60,6 +60,7 @@ namespace winrt::TerminalApp::implementation
DECLARE_EVENT(ScrollUpPage, _ScrollUpPageHandlers, TerminalApp::ScrollUpPageEventArgs);
DECLARE_EVENT(ScrollDownPage, _ScrollDownPageHandlers, TerminalApp::ScrollDownPageEventArgs);
DECLARE_EVENT(OpenSettings, _OpenSettingsHandlers, TerminalApp::OpenSettingsEventArgs);
DECLARE_EVENT(ResizePane, _ResizePaneHandlers, TerminalApp::ResizePaneEventArgs);
// clang-format on
private:

View file

@ -3,6 +3,14 @@
namespace TerminalApp
{
enum Direction
{
Left = 0,
Right,
Up,
Down
};
enum ShortcutAction
{
CopyText = 0,
@ -41,6 +49,10 @@ namespace TerminalApp
ScrollDown,
ScrollUpPage,
ScrollDownPage,
ResizePaneLeft,
ResizePaneRight,
ResizePaneUp,
ResizePaneDown,
OpenSettings
};
@ -64,6 +76,7 @@ namespace TerminalApp
delegate void ScrollUpPageEventArgs();
delegate void ScrollDownPageEventArgs();
delegate void OpenSettingsEventArgs();
delegate void ResizePaneEventArgs(Direction direction);
[default_interface]
runtimeclass AppKeyBindings : Microsoft.Terminal.Settings.IKeyBindings
@ -93,5 +106,6 @@ namespace TerminalApp
event ScrollUpPageEventArgs ScrollUpPage;
event ScrollDownPageEventArgs ScrollDownPage;
event OpenSettingsEventArgs OpenSettings;
event ResizePaneEventArgs ResizePane;
}
}

View file

@ -51,6 +51,10 @@ static constexpr std::string_view SwitchToTab8Key{ "switchToTab8" };
static constexpr std::string_view OpenSettingsKey{ "openSettings" };
static constexpr std::string_view SplitHorizontalKey{ "splitHorizontal" };
static constexpr std::string_view SplitVerticalKey{ "splitVertical" };
static constexpr std::string_view ResizePaneLeftKey{ "resizePaneLeft" };
static constexpr std::string_view ResizePaneRightKey{ "resizePaneRight" };
static constexpr std::string_view ResizePaneUpKey{ "resizePaneUp" };
static constexpr std::string_view ResizePaneDownKey{ "resizePaneDown" };
// Specifically use a map here over an unordered_map. We want to be able to
// iterate over these entries in-order when we're serializing the keybindings.
@ -97,6 +101,10 @@ static const std::map<std::string_view, ShortcutAction, std::less<>> commandName
{ SwitchToTab8Key, ShortcutAction::SwitchToTab8 },
{ SplitHorizontalKey, ShortcutAction::SplitHorizontal },
{ SplitVerticalKey, ShortcutAction::SplitVertical },
{ ResizePaneLeftKey, ShortcutAction::ResizePaneLeft },
{ ResizePaneRightKey, ShortcutAction::ResizePaneRight },
{ ResizePaneUpKey, ShortcutAction::ResizePaneUp },
{ ResizePaneDownKey, ShortcutAction::ResizePaneDown },
{ OpenSettingsKey, ShortcutAction::OpenSettings },
};

View file

@ -4,12 +4,15 @@
#include "pch.h"
#include "Pane.h"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::TerminalApp;
static const int PaneSeparatorSize = 4;
static const float Half = 0.50f;
Pane::Pane(const GUID& profile, const TermControl& control, const bool lastFocused) :
_control{ control },
@ -36,6 +39,154 @@ Pane::Pane(const GUID& profile, const TermControl& control, const bool lastFocus
}
}
// Method Description:
// - Update the size of this pane. Resizes each of our columns so they have the
// same relative sizes, given the newSize.
// - Because we're just manually setting the row/column sizes in pixels, we have
// to be told our new size, we can't just use our own OnSized event, because
// that _won't fire when we get smaller_.
// Arguments:
// - newSize: the amount of space that this pane has to fill now.
// Return Value:
// - <none>
void Pane::ResizeContent(const Size& newSize)
{
const auto width = newSize.Width;
const auto height = newSize.Height;
_CreateRowColDefinitions(newSize);
if (_splitState == SplitState::Vertical)
{
const auto paneSizes = _GetPaneSizes(width);
const Size firstSize{ paneSizes.first, height };
const Size secondSize{ paneSizes.second, height };
_firstChild->ResizeContent(firstSize);
_secondChild->ResizeContent(secondSize);
}
else if (_splitState == SplitState::Horizontal)
{
const auto paneSizes = _GetPaneSizes(height);
const Size firstSize{ width, paneSizes.first };
const Size secondSize{ width, paneSizes.second };
_firstChild->ResizeContent(firstSize);
_secondChild->ResizeContent(secondSize);
}
}
// Method Description:
// - Adjust our child percentages to increase the size of one of our children
// and decrease the size of the other.
// - Adjusts the separation amount by 5%
// - Does nothing if the direction doesn't match our current split direction
// Arguments:
// - direction: the direction to move our separator. If it's down or right,
// we'll be increasing the size of the first of our children. Else, we'll be
// decreasing the size of our first child.
// Return Value:
// - false if we couldn't resize this pane in the given direction, else true.
bool Pane::_Resize(const Direction& direction)
{
if (!DirectionMatchesSplit(direction, _splitState))
{
return false;
}
float amount = .05f;
if (direction == Direction::Right || direction == Direction::Down)
{
amount = -amount;
}
// Make sure we're not making a pane explode here by resizing it to 0 characters.
const bool changeWidth = _splitState == SplitState::Vertical;
const Size actualSize{ gsl::narrow_cast<float>(_root.ActualWidth()),
gsl::narrow_cast<float>(_root.ActualHeight()) };
// actualDimension is the size in DIPs of this pane in the direction we're
// resizing.
auto actualDimension = changeWidth ? actualSize.Width : actualSize.Height;
actualDimension -= PaneSeparatorSize;
const auto firstMinSize = _firstChild->_GetMinSize();
const auto secondMinSize = _secondChild->_GetMinSize();
// These are the minimum amount of space we need for each of our children
const auto firstMinDimension = changeWidth ? firstMinSize.Width : firstMinSize.Height;
const auto secondMinDimension = changeWidth ? secondMinSize.Width : secondMinSize.Height;
const auto firstMinPercent = firstMinDimension / actualDimension;
const auto secondMinPercent = secondMinDimension / actualDimension;
// Make sure that the first pane doesn't get bigger than the space we need
// to reserve for the second.
const auto firstMaxPercent = 1.0f - secondMinPercent;
_firstPercent = std::clamp(_firstPercent.value() - amount, firstMinPercent, firstMaxPercent);
// Update the other child to fill the remaining percent
_secondPercent = 1.0f - _firstPercent.value();
// Resize our columns to match the new percentages.
ResizeContent(actualSize);
return true;
}
// Method Description:
// - Moves the separator between panes, as to resize each child on either size
// of the separator. Tries to move a separator in the given direction. The
// separator moved is the separator that's closest depth-wise to the
// currently focused pane, that's also in the correct direction to be moved.
// If there isn't such a separator, then this method returns false, as we
// couldn't handle the resize.
// Arguments:
// - direction: The direction to move the separator in.
// Return Value:
// - true if we or a child handled this resize request.
bool Pane::ResizePane(const Direction& direction)
{
// If we're a leaf, do nothing. We can't possibly have a descendant with a
// separator the correct direction.
if (_IsLeaf())
{
return false;
}
// Check if either our first or second child is the currently focused leaf.
// If it is, and the requested resize direction matches our separator, then
// we're the pane that needs to adjust its separator.
// If our separator is the wrong direction, then we can't handle it.
const bool firstIsFocused = _firstChild->_IsLeaf() && _firstChild->_lastFocused;
const bool secondIsFocused = _secondChild->_IsLeaf() && _secondChild->_lastFocused;
if (firstIsFocused || secondIsFocused)
{
return _Resize(direction);
}
else
{
// If neither of our children were the focused leaf, then recurse into
// our children and see if they can handle the resize.
// For each child, if it has a focused descendant, try having that child
// handle the resize.
// If the child wasn't able to handle the resize, it's possible that
// there were no descendants with a separator the correct direction. If
// our separator _is_ the correct direction, then we should be the pane
// to resize. Otherwise, just return false, as we couldn't handle it
// either.
if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild())
{
return _firstChild->ResizePane(direction) || _Resize(direction);
}
else if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild())
{
return _secondChild->ResizePane(direction) || _Resize(direction);
}
}
return false;
}
// Method Description:
// - Called when our attached control is closed. Triggers listeners to our close
// event, if we're a leaf pane.
@ -415,6 +566,61 @@ void Pane::_SetupChildCloseHandlers()
});
}
// Method Description:
// - Sets up row/column definitions for this pane. There are three total
// row/cols. The middle one is for the separator. The first and third are for
// each of the child panes, and are given a size in pixels, based off the
// availiable space, and the percent of the space they respectively consume,
// which is stored in _firstPercent and _secondPercent.
// - Does nothing if our split state is currently set to SplitState::None
// Arguments:
// - rootSize: The dimensions in pixels that this pane (and its children should consume.)
// Return Value:
// - <none>
void Pane::_CreateRowColDefinitions(const Size& rootSize)
{
if (_splitState == SplitState::Vertical)
{
_root.ColumnDefinitions().Clear();
// Create three columns in this grid: one for each pane, and one for the separator.
auto separatorColDef = Controls::ColumnDefinition();
separatorColDef.Width(GridLengthHelper::Auto());
const auto paneSizes = _GetPaneSizes(rootSize.Width);
auto firstColDef = Controls::ColumnDefinition();
firstColDef.Width(GridLengthHelper::FromPixels(paneSizes.first));
auto secondColDef = Controls::ColumnDefinition();
secondColDef.Width(GridLengthHelper::FromPixels(paneSizes.second));
_root.ColumnDefinitions().Append(firstColDef);
_root.ColumnDefinitions().Append(separatorColDef);
_root.ColumnDefinitions().Append(secondColDef);
}
else if (_splitState == SplitState::Horizontal)
{
_root.RowDefinitions().Clear();
// Create three rows in this grid: one for each pane, and one for the separator.
auto separatorRowDef = Controls::RowDefinition();
separatorRowDef.Height(GridLengthHelper::Auto());
const auto paneSizes = _GetPaneSizes(rootSize.Height);
auto firstRowDef = Controls::RowDefinition();
firstRowDef.Height(GridLengthHelper::FromPixels(paneSizes.first));
auto secondRowDef = Controls::RowDefinition();
secondRowDef.Height(GridLengthHelper::FromPixels(paneSizes.second));
_root.RowDefinitions().Append(firstRowDef);
_root.RowDefinitions().Append(separatorRowDef);
_root.RowDefinitions().Append(secondRowDef);
}
}
// Method Description:
// - Initializes our UI for a new split in this pane. Sets up row/column
// definitions, and initializes the separator grid. Does nothing if our split
@ -425,16 +631,13 @@ void Pane::_SetupChildCloseHandlers()
// - <none>
void Pane::_CreateSplitContent()
{
Size actualSize{ gsl::narrow_cast<float>(_root.ActualWidth()),
gsl::narrow_cast<float>(_root.ActualHeight()) };
_CreateRowColDefinitions(actualSize);
if (_splitState == SplitState::Vertical)
{
// Create three columns in this grid: one for each pane, and one for the separator.
auto separatorColDef = Controls::ColumnDefinition();
separatorColDef.Width(GridLengthHelper::Auto());
_root.ColumnDefinitions().Append(Controls::ColumnDefinition{});
_root.ColumnDefinitions().Append(separatorColDef);
_root.ColumnDefinitions().Append(Controls::ColumnDefinition{});
// Create the pane separator
_separatorRoot = Controls::Grid{};
_separatorRoot.Width(PaneSeparatorSize);
@ -443,14 +646,6 @@ void Pane::_CreateSplitContent()
}
else if (_splitState == SplitState::Horizontal)
{
// Create three rows in this grid: one for each pane, and one for the separator.
auto separatorRowDef = Controls::RowDefinition();
separatorRowDef.Height(GridLengthHelper::Auto());
_root.RowDefinitions().Append(Controls::RowDefinition{});
_root.RowDefinitions().Append(separatorRowDef);
_root.RowDefinitions().Append(Controls::RowDefinition{});
// Create the pane separator
_separatorRoot = Controls::Grid{};
_separatorRoot.Height(PaneSeparatorSize);
@ -507,7 +702,7 @@ void Pane::SplitVertical(const GUID& profile, const TermControl& control)
return;
}
_DoSplit(SplitState::Vertical, profile, control);
_Split(SplitState::Vertical, profile, control);
}
// Method Description:
@ -535,7 +730,7 @@ void Pane::SplitHorizontal(const GUID& profile, const TermControl& control)
return;
}
_DoSplit(SplitState::Horizontal, profile, control);
_Split(SplitState::Horizontal, profile, control);
}
// Method Description:
@ -547,7 +742,7 @@ void Pane::SplitHorizontal(const GUID& profile, const TermControl& control)
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void Pane::_DoSplit(SplitState splitType, const GUID& profile, const TermControl& control)
void Pane::_Split(SplitState splitType, const GUID& profile, const TermControl& control)
{
// Lock the create/close lock so that another operation won't concurrently
// modify our tree
@ -559,6 +754,9 @@ void Pane::_DoSplit(SplitState splitType, const GUID& profile, const TermControl
_splitState = splitType;
_firstPercent = { Half };
_secondPercent = { Half };
_CreateSplitContent();
// Remove any children we currently have. We can't add the existing
@ -585,4 +783,52 @@ void Pane::_DoSplit(SplitState splitType, const GUID& profile, const TermControl
_lastFocused = false;
}
// Method Description:
// - Gets the size in pixels of each of our children, given the full size they
// should fill. Accounts for the size of the separator that should be between
// them as well.
// Arguments:
// - fullSize: the amount of space in pixels that should be filled by our
// children and their separator
// Return Value:
// - a pair with the size of our first child and the size of our second child,
// respectively.
std::pair<float, float> Pane::_GetPaneSizes(const float& fullSize)
{
if (_IsLeaf())
{
THROW_HR(E_FAIL);
}
const auto sizeMinusSeparator = fullSize - PaneSeparatorSize;
const auto firstSize = sizeMinusSeparator * _firstPercent.value();
const auto secondSize = sizeMinusSeparator * _secondPercent.value();
return { firstSize, secondSize };
}
// Method Description:
// - Get the absolute minimum size that this pane can be resized to and still
// have 1x1 character visible, in each of its children. This includes the
// space needed for the separator.
// Arguments:
// - <none>
// Return Value:
// - The minimum size that this pane can be resized to and still have a visible
// character.
Size Pane::_GetMinSize() const
{
if (_IsLeaf())
{
return _control.MinimumSize();
}
else
{
const auto firstSize = _firstChild->_GetMinSize();
const auto secondSize = _secondChild->_GetMinSize();
const auto newWidth = firstSize.Width + secondSize.Width + (_splitState == SplitState::Vertical ? PaneSeparatorSize : 0);
const auto newHeight = firstSize.Height + secondSize.Height + (_splitState == SplitState::Horizontal ? PaneSeparatorSize : 0);
return { newWidth, newHeight };
}
}
DEFINE_EVENT(Pane, Closed, _closedHandlers, ConnectionClosedEventArgs);

View file

@ -20,6 +20,7 @@
#pragma once
#include <winrt/Microsoft.Terminal.TerminalControl.h>
#include <winrt/TerminalApp.h>
#include "../../cascadia/inc/cppwinrt_utils.h"
class Pane : public std::enable_shared_from_this<Pane>
@ -44,6 +45,8 @@ public:
void UpdateFocus();
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::TerminalSettings& settings, const GUID& profile);
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
bool ResizePane(const winrt::TerminalApp::Direction& direction);
void SplitHorizontal(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void SplitVertical(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
@ -58,6 +61,8 @@ private:
std::shared_ptr<Pane> _firstChild{ nullptr };
std::shared_ptr<Pane> _secondChild{ nullptr };
SplitState _splitState{ SplitState::None };
std::optional<float> _firstPercent{ std::nullopt };
std::optional<float> _secondPercent{ std::nullopt };
bool _lastFocused{ false };
std::optional<GUID> _profile{ std::nullopt };
@ -71,12 +76,52 @@ private:
bool _HasFocusedChild() const noexcept;
void _SetupChildCloseHandlers();
void _DoSplit(SplitState splitType, const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void _Split(SplitState splitType, const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void _CreateRowColDefinitions(const winrt::Windows::Foundation::Size& rootSize);
void _CreateSplitContent();
void _ApplySplitDefinitions();
bool _Resize(const winrt::TerminalApp::Direction& direction);
void _CloseChild(const bool closeFirst);
void _FocusFirstChild();
void _ControlClosedHandler();
std::pair<float, float> _GetPaneSizes(const float& fullSize);
winrt::Windows::Foundation::Size _GetMinSize() const;
// Function Description:
// - Returns true if the given direction can be used with the given split
// type.
// - This is used for pane resizing (which will need a pane separator
// that's perpendicular to the direction to be able to move the separator
// in that direction).
// - Additionally, it will be used for moving focus between panes, which
// again happens _across_ a separator.
// Arguments:
// - direction: The Direction to compare
// - splitType: The SplitState to compare
// Return Value:
// - true iff the direction is perpendicular to the splitType. False for
// SplitState::None.
static constexpr bool DirectionMatchesSplit(const winrt::TerminalApp::Direction& direction,
const SplitState& splitType)
{
if (splitType == SplitState::None)
{
return false;
}
else if (splitType == SplitState::Horizontal)
{
return direction == winrt::TerminalApp::Direction::Up ||
direction == winrt::TerminalApp::Direction::Down;
}
else if (splitType == SplitState::Vertical)
{
return direction == winrt::TerminalApp::Direction::Left ||
direction == winrt::TerminalApp::Direction::Right;
}
return false;
}
};

View file

@ -213,4 +213,28 @@ void Tab::AddHorizontalSplit(const GUID& profile, TermControl& control)
_rootPane->SplitHorizontal(profile, control);
}
// Method Description:
// - Update the size of our panes to fill the new given size. This happens when
// the window is resized.
// Arguments:
// - newSize: the amount of space that the panes have to fill now.
// Return Value:
// - <none>
void Tab::ResizeContent(const winrt::Windows::Foundation::Size& newSize)
{
_rootPane->ResizeContent(newSize);
}
// Method Description:
// - Attempt to move a separator between panes, as to resize each child on
// either size of the separator. See Pane::ResizePane for details.
// Arguments:
// - direction: The direction to move the separator in.
// Return Value:
// - <none>
void Tab::ResizePane(const winrt::TerminalApp::Direction& direction)
{
_rootPane->ResizePane(direction);
}
DEFINE_EVENT(Tab, Closed, _closedHandlers, ConnectionClosedEventArgs);

View file

@ -23,6 +23,8 @@ public:
void AddHorizontalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
void UpdateFocus();
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
void ResizePane(const winrt::TerminalApp::Direction& direction);
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::TerminalSettings& settings, const GUID& profile);
winrt::hstring GetFocusedTitle() const;

View file

@ -1080,7 +1080,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
// Method Description:
// - Process a resize event that was initiated by the user. This can either be due to the user resizing the window (causing the swapchain to resize) or due to the DPI changing (causing us to need to resize the buffer to match)
// - Process a resize event that was initiated by the user. This can either
// be due to the user resizing the window (causing the swapchain to
// resize) or due to the DPI changing (causing us to need to resize the
// buffer to match)
// Arguments:
// - newWidth: the new width of the swapchain, in pixels.
// - newHeight: the new height of the swapchain, in pixels.
@ -1090,6 +1093,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
size.cx = static_cast<long>(newWidth);
size.cy = static_cast<long>(newHeight);
// Don't actually resize so small that a single character wouldn't fit
// in either dimension. The buffer really doesn't like being size 0.
if (size.cx < _actualFont.GetSize().X || size.cy < _actualFont.GetSize().Y)
{
return;
}
// Tell the dx engine that our window is now the new size.
THROW_IF_FAILED(_renderEngine->SetWindowSize(size));
@ -1351,6 +1361,48 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return { gsl::narrow_cast<float>(width), gsl::narrow_cast<float>(height) };
}
// Method Description:
// - Get the size of a single character of this control. The size is in
// DIPs. If you need it in _pixels_, you'll need to multiply by the
// current display scaling.
// Arguments:
// - <none>
// Return Value:
// - The dimensions of a single character of this control, in DIPs
winrt::Windows::Foundation::Size TermControl::CharacterDimensions() const
{
const auto fontSize = _actualFont.GetSize();
return { gsl::narrow_cast<float>(fontSize.X), gsl::narrow_cast<float>(fontSize.Y) };
}
// Method Description:
// - Get the absolute minimum size that this control can be resized to and
// still have 1x1 character visible. This includes the space needed for
// the scrollbar and the padding.
// Arguments:
// - <none>
// Return Value:
// - The minimum size that this terminal control can be resized to and still
// have a visible character.
winrt::Windows::Foundation::Size TermControl::MinimumSize() const
{
const auto fontSize = _actualFont.GetSize();
double width = fontSize.X;
double height = fontSize.Y;
// Reserve additional space if scrollbar is intended to be visible
if (_settings.ScrollState() == ScrollbarState::Visible)
{
width += _scrollBar.ActualWidth();
}
// Account for the size of any padding
auto thickness = _ParseThicknessFromPadding(_settings.Padding());
width += thickness.Left + thickness.Right;
height += thickness.Top + thickness.Bottom;
return { gsl::narrow_cast<float>(width), gsl::narrow_cast<float>(height) };
}
// Method Description:
// - Create XAML Thickness object based on padding props provided.
// Used for controlling the TermControl XAML Grid container's Padding prop.

View file

@ -44,6 +44,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void PasteTextFromClipboard();
void Close();
bool ShouldCloseOnExit() const noexcept;
Windows::Foundation::Size CharacterDimensions() const;
Windows::Foundation::Size MinimumSize() const;
void ScrollViewport(int viewTop);
void KeyboardScrollViewport(int viewTop);

View file

@ -35,6 +35,8 @@ namespace Microsoft.Terminal.TerminalControl
void PasteTextFromClipboard();
void Close();
Boolean ShouldCloseOnExit { get; };
Windows.Foundation.Size CharacterDimensions { get; };
Windows.Foundation.Size MinimumSize { get; };
void ScrollViewport(Int32 viewTop);
void KeyboardScrollViewport(Int32 viewTop);