Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for multiple panes in the same window #825

Merged
merged 41 commits into from
Jun 7, 2019
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
606dd57
Start working on adding support for panes
zadjii-msft May 8, 2019
91dc273
Add basic pane splitting
zadjii-msft May 9, 2019
a600b20
This works for nesting panes
zadjii-msft May 9, 2019
faaba47
Switching tabs keeps focus on the last active pane now
zadjii-msft May 10, 2019
9b792d3
Reload settings for the panes.
zadjii-msft May 14, 2019
7ca3e8d
Get titles working again
zadjii-msft May 14, 2019
db7523a
Update the icon too
zadjii-msft May 14, 2019
7258a71
Pull these two guys into helpers
zadjii-msft May 14, 2019
29464ea
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes
zadjii-msft May 14, 2019
377aa4f
Hook up the terminal's closing to the pane's to the tab's
zadjii-msft May 14, 2019
23930b5
This is me trying to mess with the tab closing, but I think it's prob…
zadjii-msft May 14, 2019
a4fbc53
Revert "This is me trying to mess with the tab closing, but I think i…
zadjii-msft May 14, 2019
b353e89
TONS of polish. Doc comments, etc.
zadjii-msft May 14, 2019
dc3f522
Correctly close a pane when one of it's children has children
zadjii-msft May 15, 2019
d45a9aa
Correctly move focus to a child with children when the focused pane i…
zadjii-msft May 15, 2019
0a96ed4
doc comments for days
zadjii-msft May 15, 2019
a754474
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes
zadjii-msft May 17, 2019
ff21bd2
This is most all of the PR feedback
zadjii-msft May 17, 2019
ba805e8
Update doc/cascadia/Panes.md
zadjii-msft May 17, 2019
3d1c4ca
Mostly just PR nits
zadjii-msft May 20, 2019
013cefa
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes
zadjii-msft May 20, 2019
7c5c222
Fix some bugs with closing a leaf _after_ it was a parent.
zadjii-msft May 21, 2019
cd75b1d
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes
zadjii-msft May 21, 2019
d5060b3
Merge branch 'master' into dev/migrie/f/panes
zadjii-msft May 23, 2019
6d847dc
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes
zadjii-msft May 23, 2019
0f848e7
Remove the default keybinding.
zadjii-msft May 24, 2019
bbed9ed
switch to in-class initializers
zadjii-msft May 24, 2019
704f6cc
Update doc/cascadia/Panes.md
zadjii-msft May 30, 2019
d016d5c
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes
zadjii-msft May 30, 2019
40c5600
Apply suggestions from code review
zadjii-msft May 31, 2019
890fb3a
Add a NOTICES file.
DHowett May 31, 2019
0ea8b85
move the NOTICE to the right branch...
DHowett May 31, 2019
80036e2
Much of the easier PR feedback
zadjii-msft Jun 3, 2019
9165d02
Lock up the panes when they're getting opened/closed
zadjii-msft Jun 3, 2019
7f50a0f
A little bit of cleanup on the comments here
zadjii-msft Jun 3, 2019
b5d954e
Refactor the code for actually doing a split
zadjii-msft Jun 4, 2019
c6311f7
Clean up some typos
zadjii-msft Jun 7, 2019
a76db18
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes
zadjii-msft Jun 7, 2019
c265317
Fixed build error
carlos-zamora Jun 7, 2019
14f3ffa
Apply suggestions from code review
zadjii-msft Jun 7, 2019
f945c18
Apply suggestions from code review
zadjii-msft Jun 7, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 163 additions & 56 deletions src/cascadia/TerminalApp/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,8 @@ namespace winrt::TerminalApp::implementation
bindings.ScrollDown([this]() { _DoScroll(1); });
bindings.NextTab([this]() { _SelectNextTab(true); });
bindings.PrevTab([this]() { _SelectNextTab(false); });
bindings.SplitVertical([this]() { _SplitVertical(std::nullopt); });
bindings.SplitHorizontal([this]() { _SplitHorizontal(std::nullopt); });
bindings.SwitchToTab([this](const auto index) { _SelectTab({ index }); });
bindings.OpenSettings([this]() { _OpenSettings(); });
}
Expand Down Expand Up @@ -419,23 +421,16 @@ namespace winrt::TerminalApp::implementation

for (auto &tab : _tabs)
{
const auto term = tab->GetTerminalControl();
const GUID tabProfile = tab->GetProfile();

if (profileGuid == tabProfile)
{
term.UpdateSettings(settings);

// Update the icons of the tabs with this profile open.
auto tabViewItem = tab->GetTabViewItem();
tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [profile, tabViewItem]() {
// _GetIconFromProfile has to run on the main thread
tabViewItem.Icon(App::_GetIconFromProfile(profile));
});
}
// Attempt to reload the settings of any panes with this profile
tab->CheckUpdateSettings(settings, profileGuid);
}
}

// Update the icon of the tab for the currently focused profile in that tab.
for (auto& tab : _tabs)
{
_UpdateTabIcon(tab);
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
}

_root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() {
// Refresh the UI theme
Expand All @@ -448,6 +443,51 @@ namespace winrt::TerminalApp::implementation

}

// Method Description:
// - Get the icon of the currently focused terminal control, and set it's
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
// tab's icon to that icon.
// Arguments:
// - tab: the Tab to update the title for.
void App::_UpdateTabIcon(std::shared_ptr<Tab> tab)
{
const auto lastFocusedProfileOpt = tab->GetLastFocusedProfile();
if (lastFocusedProfileOpt.has_value())
{
const auto lastFocusedProfile = lastFocusedProfileOpt.value();

auto tabViewItem = tab->GetTabViewItem();
tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this, lastFocusedProfile, tabViewItem]() {
// _GetIconFromProfile has to run on the main thread
const auto* const matchingProfile = _settings->FindProfile(lastFocusedProfile);
if (matchingProfile)
{
tabViewItem.Icon(App::_GetIconFromProfile(*matchingProfile));
}
});
}
}

// Method Description:
// - Get the title of the currently focused terminal control, and set it's
// tab's text to that text. If this tab is the focused tab, then also
// bubble this title to any listeners of our TitleChanged event.
// Arguments:
// - tab: the Tab to update the title for.
void App::_CheckTitleUpdate(std::shared_ptr<Tab> tab)
{
auto newTabTitle = tab->GetLastFocusedTitle();

// TODO #608: If the settings don't want the terminal's text in the
// tab, then display something else.
tab->SetTabText(newTabTitle);
if (_settings->GlobalSettings().GetShowTitleInTitlebar() &&
tab->IsFocused())
{
_titleChangeHandlers(newTabTitle);
}

zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
}

// Method Description:
// - Update the current theme of the application. This will manually update
// all of the elements in our UI to match the given theme.
Expand Down Expand Up @@ -566,18 +606,21 @@ namespace winrt::TerminalApp::implementation
}

// Method Description:
// - Creates a new tab with the given settings. If the tab bar is not being
// currently displayed, it will be shown.
// - Connects event handlers to the TermControl for events that we want to
// handle. This includes:
// * the Copy and Paste events, for setting and retrieving clipboard data
// on the right thread
// * the TitleChanged event, for changing the text of the tab
// * the GotFocus event, for chnging the title/icon in the tab when a new
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
// control is focused
// Arguments:
// - settings: the TerminalSettings object to use to create the TerminalControl with.
void App::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings)
// - term: The newly created TermControl to connect the events for
// - hostingTab: The Tab that's hosting this TermControl instance
void App::_RegisterTerminalEvents(TermControl term, std::shared_ptr<Tab> hostingTab)
{
// Initialize the new tab
TermControl term{ settings };

// Add an event handler when the terminal's selection wants to be copied.
// When the text buffer data is retrieved, we'll copy the data into the Clipboard
term.CopyToClipboard([=](auto copiedData) {
term.CopyToClipboard([this](auto copiedData) {
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
_root.Dispatcher().RunAsync(CoreDispatcherPriority::High, [copiedData]() {
DataPackage dataPack = DataPackage();
dataPack.RequestedOperation(DataPackageOperation::Copy);
Expand All @@ -590,26 +633,50 @@ namespace winrt::TerminalApp::implementation
});

// Add an event handler when the terminal wants to paste data from the Clipboard.
term.PasteFromClipboard([=](auto /*sender*/, auto eventArgs) {
term.PasteFromClipboard([this](auto /*sender*/, auto eventArgs) {
_root.Dispatcher().RunAsync(CoreDispatcherPriority::High, [eventArgs]() {
PasteFromClipboard(eventArgs);
});
});

// TODO: hostingTab feels like it should be a weak ref, not a strong ref.
term.TitleChanged([this, hostingTab](auto newTitle){
// The title of the control changed, but not necessarily the title
// of the tab. Get the title of the focused pane of the tab, and set
// the tab's text to the focused panes' text.
_CheckTitleUpdate(hostingTab);
});

// TODO: hostingTab feels like it should be a weak ref, not a strong ref.
term.GetControl().GotFocus([this, hostingTab](auto&&, auto&&)
{
// Update the focus of the tab's panes
hostingTab->CheckFocus();

// Possibly update the title of the tab, window to match the newly
// focused pane.
_CheckTitleUpdate(hostingTab);

// Possibly update the icon of the tab.
_UpdateTabIcon(hostingTab);
});
}

// Method Description:
// - Creates a new tab with the given settings. If the tab bar is not being
// currently displayed, it will be shown.
// Arguments:
// - settings: the TerminalSettings object to use to create the TerminalControl with.
void App::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings)
{
// Initialize the new tab
TermControl term{ settings };

// Add the new tab to the list of our tabs.
auto newTab = _tabs.emplace_back(std::make_shared<Tab>(profileGuid, term));

// Add an event handler when the terminal's title changes. When the
// title changes, we'll bubble it up to listeners of our own title
// changed event, so they can handle it.
newTab->GetTerminalControl().TitleChanged([=](auto newTitle){
// Only bubble the change if this tab is the focused tab.
if (_settings->GlobalSettings().GetShowTitleInTitlebar() &&
newTab->IsFocused())
{
_titleChangeHandlers(newTitle);
}
});
// Hookp our event handlers to the new terminal
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
_RegisterTerminalEvents(term, newTab);

auto tabViewItem = newTab->GetTabViewItem();
_tabView.Items().Append(tabViewItem);
Expand All @@ -622,24 +689,15 @@ namespace winrt::TerminalApp::implementation
tabViewItem.Icon(_GetIconFromProfile(*profile));
}

// Add an event handler when the terminal's connection is closed.
newTab->GetTerminalControl().ConnectionClosed([=]() {
_tabView.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [newTab, tabViewItem, this]() {
const GUID tabProfile = newTab->GetProfile();
// Don't just capture this pointer, because the profile might
// get destroyed before this is called (case in point -
// reloading settings)
const auto* const p = _settings->FindProfile(tabProfile);
tabViewItem.PointerPressed({ this, &App::_OnTabClick });

if (p != nullptr && p->GetCloseOnExit())
{
_RemoveTabViewItem(tabViewItem);
}
// When the tab is closed, remove it from our list of tabs.
newTab->Closed([tabViewItem, this](){
_tabView.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [tabViewItem, this]() {
_RemoveTabViewItem(tabViewItem);
});
});

tabViewItem.PointerPressed({ this, &App::_OnTabClick });

// This is one way to set the tab's selected background color.
// tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), a Brush?);

Expand Down Expand Up @@ -691,10 +749,7 @@ namespace winrt::TerminalApp::implementation
// and get text to appear on separate lines.
void App::_CopyText(const bool trimTrailingWhitespace)
{
const int focusedTabIndex = _GetFocusedTabIndex();
std::shared_ptr<Tab> focusedTab{ _tabs[focusedTabIndex] };

const auto control = focusedTab->GetTerminalControl();
const auto control = _GetFocusedControl();
control.CopySelectionToClipboard(trimTrailingWhitespace);
}

Expand Down Expand Up @@ -744,10 +799,9 @@ namespace winrt::TerminalApp::implementation
try
{
auto tab = _tabs.at(selectedIndex);
auto control = tab->GetTerminalControl().GetControl();

_tabContent.Children().Clear();
_tabContent.Children().Append(control);
_tabContent.Children().Append(tab->GetRootElement());

tab->SetFocused(true);
_titleChangeHandlers(GetTitle());
Expand Down Expand Up @@ -800,8 +854,7 @@ namespace winrt::TerminalApp::implementation
{
try
{
auto tab = _tabs.at(selectedIndex);
return tab->GetTerminalControl().Title();
return _GetFocusedControl().Title();
}
CATCH_LOG();
}
Expand Down Expand Up @@ -883,6 +936,60 @@ namespace winrt::TerminalApp::implementation
}
}

winrt::Microsoft::Terminal::TerminalControl::TermControl App::_GetFocusedControl()
{
int focusedTabIndex = _GetFocusedTabIndex();
auto focusedTab = _tabs[focusedTabIndex];
return focusedTab->GetLastFocusedTerminalControl();
}

// Method Description:
// - Vertically split the focused pane, and place the given TermControl into
// the newly created pane.
// Arguments:
// - profile: The profile GUID to associate with the newly created pane. If
// this is nullopt, use the default profile.
void App::_SplitVertical(std::optional<GUID> profileGuid)
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
{
_SplitPane(false, profileGuid);
}

// Method Description:
// - Horizontally split the focused pane and place the given TermControl
// into the newly created pane.
// Arguments:
// - profile: The profile GUID to associate with the newly created pane. If
// this is nullopt, use the default profile.
void App::_SplitHorizontal(std::optional<GUID> profileGuid)
{
_SplitPane(true, profileGuid);
}

// Method Description:
// - Split the focused pane either horizontally or vertically, and place the
// given TermControl into the newly created pane.
// Arguments:
// - splitHorizontal: if true, split the pane horizontally. Else split
// vertically.
// - profile: The profile GUID to associate with the newly created pane. If
// this is nullopt, use the default profile.
void App::_SplitPane(const bool splitHorizontal, std::optional<GUID> profileGuid)
{
const GUID realGuid = profileGuid ? profileGuid.value() :
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
_settings->GlobalSettings().GetDefaultProfile();
auto controlSettings = _settings->MakeSettings(realGuid);
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
TermControl newControl{ controlSettings };

int focusedTabIndex = _GetFocusedTabIndex();
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
auto focusedTab = _tabs[focusedTabIndex];

// Hookp our event handlers to the new terminal
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
_RegisterTerminalEvents(newControl, focusedTab);

return splitHorizontal ? focusedTab->SplitHorizontal(realGuid, newControl) :
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
focusedTab->SplitVertical(realGuid, newControl);
}

// -------------------------------- WinRT Events ---------------------------------
// Winrt events need a method for adding a callback to the event and removing the callback.
// These macros will define them both for you.
Expand Down
10 changes: 10 additions & 0 deletions src/cascadia/TerminalApp/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ namespace winrt::TerminalApp::implementation
void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);

void _UpdateTabView();
void _UpdateTabIcon(std::shared_ptr<Tab> tab);
void _CheckTitleUpdate(std::shared_ptr<Tab> tab);
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved


void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab);

void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);

Expand All @@ -96,6 +101,9 @@ namespace winrt::TerminalApp::implementation

void _DoScroll(int delta);
void _CopyText(const bool trimTrailingWhitespace);
void _SplitVertical(std::optional<GUID> profileGuid);
void _SplitHorizontal(std::optional<GUID> profileGuid);
void _SplitPane(const bool splitHorizontal, std::optional<GUID> profileGuid);
// Todo: add more event implementations here
// MSFT:20641986: Add keybindings for New Window

Expand All @@ -109,6 +117,8 @@ namespace winrt::TerminalApp::implementation
void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme);

static Windows::UI::Xaml::Controls::IconElement _GetIconFromProfile(const ::TerminalApp::Profile& profile);

winrt::Microsoft::Terminal::TerminalControl::TermControl _GetFocusedControl();
};
}

Expand Down
9 changes: 9 additions & 0 deletions src/cascadia/TerminalApp/AppKeyBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ namespace winrt::TerminalApp::implementation
_PrevTabHandlers();
return true;

case ShortcutAction::SplitVertical:
_SplitVerticalHandlers();
return true;
case ShortcutAction::SplitHorizontal:
_SplitHorizontalHandlers();
return true;

case ShortcutAction::SwitchToTab0:
_SwitchToTabHandlers(0);
return true;
Expand Down Expand Up @@ -142,6 +149,8 @@ namespace winrt::TerminalApp::implementation
DEFINE_EVENT(AppKeyBindings, SwitchToTab, _SwitchToTabHandlers, TerminalApp::SwitchToTabEventArgs);
DEFINE_EVENT(AppKeyBindings, NextTab, _NextTabHandlers, TerminalApp::NextTabEventArgs);
DEFINE_EVENT(AppKeyBindings, PrevTab, _PrevTabHandlers, TerminalApp::PrevTabEventArgs);
DEFINE_EVENT(AppKeyBindings, SplitVertical, _SplitVerticalHandlers, TerminalApp::SplitVerticalEventArgs);
DEFINE_EVENT(AppKeyBindings, SplitHorizontal, _SplitHorizontalHandlers, TerminalApp::SplitHorizontalEventArgs);
DEFINE_EVENT(AppKeyBindings, IncreaseFontSize, _IncreaseFontSizeHandlers, TerminalApp::IncreaseFontSizeEventArgs);
DEFINE_EVENT(AppKeyBindings, DecreaseFontSize, _DecreaseFontSizeHandlers, TerminalApp::DecreaseFontSizeEventArgs);
DEFINE_EVENT(AppKeyBindings, ScrollUp, _ScrollUpHandlers, TerminalApp::ScrollUpEventArgs);
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/AppKeyBindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ namespace winrt::TerminalApp::implementation
DECLARE_EVENT(SwitchToTab, _SwitchToTabHandlers, TerminalApp::SwitchToTabEventArgs);
DECLARE_EVENT(NextTab, _NextTabHandlers, TerminalApp::NextTabEventArgs);
DECLARE_EVENT(PrevTab, _PrevTabHandlers, TerminalApp::PrevTabEventArgs);
DECLARE_EVENT(SplitVertical, _SplitVerticalHandlers, TerminalApp::SplitVerticalEventArgs);
DECLARE_EVENT(SplitHorizontal, _SplitHorizontalHandlers, TerminalApp::SplitHorizontalEventArgs);
DECLARE_EVENT(IncreaseFontSize, _IncreaseFontSizeHandlers, TerminalApp::IncreaseFontSizeEventArgs);
DECLARE_EVENT(DecreaseFontSize, _DecreaseFontSizeHandlers, TerminalApp::DecreaseFontSizeEventArgs);
DECLARE_EVENT(ScrollUp, _ScrollUpHandlers, TerminalApp::ScrollUpEventArgs);
Expand Down
6 changes: 6 additions & 0 deletions src/cascadia/TerminalApp/AppKeyBindings.idl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ namespace TerminalApp
CloseTab,
NextTab,
PrevTab,
SplitVertical,
SplitHorizontal,
SwitchToTab0,
SwitchToTab1,
SwitchToTab2,
Expand All @@ -49,6 +51,8 @@ namespace TerminalApp
delegate void CloseTabEventArgs();
delegate void NextTabEventArgs();
delegate void PrevTabEventArgs();
delegate void SplitVerticalEventArgs();
delegate void SplitHorizontalEventArgs();
delegate void SwitchToTabEventArgs(Int32 profileIndex);
delegate void IncreaseFontSizeEventArgs();
delegate void DecreaseFontSizeEventArgs();
Expand All @@ -73,6 +77,8 @@ namespace TerminalApp
event SwitchToTabEventArgs SwitchToTab;
event NextTabEventArgs NextTab;
event PrevTabEventArgs PrevTab;
event SplitVerticalEventArgs SplitVertical;
event SplitHorizontalEventArgs SplitHorizontal;
event IncreaseFontSizeEventArgs IncreaseFontSize;
event DecreaseFontSizeEventArgs DecreaseFontSize;
event ScrollUpEventArgs ScrollUp;
Expand Down
Loading