Skip to content

Commit

Permalink
Add support for moving panes and tabs between windows (#14866)
Browse files Browse the repository at this point in the history
_Lo! Harken to me, for I shall divulge the heart of the tab tear-out
saga. Verily, this PR shall bestow upon thee the power to move tabs and
panes between windows by means of pre-defined actions. Though be warned,
it does not yet grant thee the power to drag and drop them as thou
mayest desire. Yet, the same plumbing that underpins this work shall
remain steadfast. Behold, the majority of this undertaking concerns the
elevation of the RequestMoveContent event from the TerminalPage to the
very summit of the Monarch. From thence, a great AttachContent method
shall descend back to the lowest depths. Furthermore, there are minor
revisions to TermControl that shall enable thee to better detach the
content and attach it to a new one._

This is the most important part of the tab tear-out saga. This PR
enables the user to move tabs and panes between windows using
pre-defined actions. It does _not_ enable the user to drag/drop them
yet, but the same fundamental plumbing will still apply. Most of the PR
is plumbing the `RequestMoveContent` event up from the `TerminalPage` up
to the `Monarch`, and then plumbing an `AttachContent` method back down.
There are also small changes to `TermControl` to better support
detaching the content and attaching to a new one.

For testing, I recommend:

```json
        { "keys": "f1", "command": { "action": "moveTab", "window": "1" } },
        { "keys": "f2", "command": { "action": "moveTab", "window": "2" } },

        { "keys": "f3", "command": { "action": "movePane", "window": "1" } },
        { "keys": "f4", "command": { "action": "movePane", "window": "2" } },

        { "keys": "shift+f3", "command": { "action": "movePane", "window": "1", "index": 3 } },
        { "keys": "shift+f4", "command": { "action": "movePane", "window": "2", "index": 3 } },
```

* Related to #1256
* Related to #5000

---------

Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com>
  • Loading branch information
zadjii-msft and carlos-zamora authored Mar 30, 2023
1 parent 34aa6aa commit 17a5b77
Show file tree
Hide file tree
Showing 52 changed files with 944 additions and 169 deletions.
54 changes: 54 additions & 0 deletions src/cascadia/Remoting/Monarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1055,4 +1055,58 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation

return winrt::single_threaded_vector(std::move(vec));
}

void Monarch::RequestMoveContent(winrt::hstring window,
winrt::hstring content,
uint32_t tabIndex)
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_MoveContent_Requested",
TraceLoggingWideString(window.c_str(), "window", "The name of the window we tried to move to"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));

uint64_t windowId = _lookupPeasantIdForName(window);
if (windowId == 0)
{
// Try the name as an integer ID
uint32_t temp;
if (!Utils::StringToUint(window.c_str(), temp))
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_MoveContent_FailedToParseId",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
else
{
windowId = temp;
}
}

if (auto targetPeasant{ _getPeasant(windowId) })
{
auto request = winrt::make_self<implementation::AttachRequest>(content, tabIndex);
targetPeasant.AttachContentToWindow(*request);
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_MoveContent_Completed",
TraceLoggingInt64(windowId, "windowId", "The ID of the peasant which we sent the content to"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
else
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_MoveContent_NoWindow",
TraceLoggingInt64(windowId, "windowId", "We could not find a peasant with this ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));

// TODO GH#5000
//
// In the case where window couldn't be found, then create a window
// for that name / ID. Do this as a part of tear-out (different than
// drag/drop)
}
}
}
2 changes: 2 additions & 0 deletions src/cascadia/Remoting/Monarch.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();

void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex);

TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/Remoting/Monarch.idl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ namespace Microsoft.Terminal.Remoting
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; };
Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();

void RequestMoveContent(String window, String content, UInt32 tabIndex);

event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
Expand Down
17 changes: 17 additions & 0 deletions src/cascadia/Remoting/Peasant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "GetWindowLayoutArgs.h"
#include "Peasant.g.cpp"
#include "../../types/inc/utils.hpp"
#include "AttachRequest.g.cpp"

using namespace winrt;
using namespace winrt::Microsoft::Terminal;
Expand Down Expand Up @@ -275,6 +276,22 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}

void Peasant::AttachContentToWindow(Remoting::AttachRequest request)
{
try
{
_AttachRequestedHandlers(*this, request);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_AttachContentToWindow",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}

void Peasant::Quit()
{
try
Expand Down
18 changes: 18 additions & 0 deletions src/cascadia/Remoting/Peasant.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,26 @@

#include "Peasant.g.h"
#include "RenameRequestArgs.h"
#include "AttachRequest.g.h"

namespace RemotingUnitTests
{
class RemotingTests;
};
namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct AttachRequest : public AttachRequestT<AttachRequest>
{
WINRT_PROPERTY(winrt::hstring, Content);
WINRT_PROPERTY(uint32_t, TabIndex);

public:
AttachRequest(winrt::hstring content,
uint32_t tabIndex) :
_Content{ content },
_TabIndex{ tabIndex } {};
};

struct Peasant : public PeasantT<Peasant>
{
Peasant();
Expand All @@ -32,6 +45,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void RequestQuitAll();
void Quit();

void AttachContentToWindow(Remoting::AttachRequest request);

winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();

winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
Expand All @@ -47,12 +62,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);

TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs);

TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest);

private:
Peasant(const uint64_t testPID);
uint64_t _ourPID;
Expand Down
12 changes: 12 additions & 0 deletions src/cascadia/Remoting/Peasant.idl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ namespace Microsoft.Terminal.Remoting
MonitorBehavior ToMonitor;
}

[default_interface] runtimeclass AttachRequest {
String Content { get; };
UInt32 TabIndex { get; };
}

interface IPeasant
{
CommandlineArgs InitialArgs { get; };
Expand All @@ -69,23 +74,30 @@ namespace Microsoft.Terminal.Remoting
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
void Summon(SummonWindowBehavior behavior);

void RequestShowNotificationIcon();
void RequestHideNotificationIcon();
void RequestQuitAll();
void Quit();
String GetWindowLayout();

void AttachContentToWindow(AttachRequest request);


event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;

event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, GetWindowLayoutArgs> GetWindowLayoutRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;

event Windows.Foundation.TypedEventHandler<Object, AttachRequest> AttachRequested;
};

[default_interface] runtimeclass Peasant : IPeasant
Expand Down
9 changes: 9 additions & 0 deletions src/cascadia/Remoting/WindowManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,4 +413,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
return nullptr;
}

winrt::fire_and_forget WindowManager::RequestMoveContent(winrt::hstring window,
winrt::hstring content,
uint32_t tabIndex)
{
co_await winrt::resume_background();
_monarch.RequestMoveContent(window, content, tabIndex);
}

}
4 changes: 4 additions & 0 deletions src/cascadia/Remoting/WindowManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
void SummonAllWindows();
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();

uint64_t GetNumberOfPeasants();

static winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant);
void UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant);

Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
bool DoesQuakeWindowExist();

winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex);

TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);

TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/Remoting/WindowManager.idl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace Microsoft.Terminal.Remoting

Boolean DoesQuakeWindowExist();

void RequestMoveContent(String window, String content, UInt32 tabIndex);

event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;

event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
Expand Down
22 changes: 0 additions & 22 deletions src/cascadia/TerminalApp/ActionPaletteItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,5 @@ namespace winrt::TerminalApp::implementation
Name(command.Name());
KeyChordText(command.KeyChordText());
Icon(command.IconPath());

_commandChangedRevoker = command.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& sender, auto& e) {
auto item{ weakThis.get() };
auto senderCommand{ sender.try_as<Microsoft::Terminal::Settings::Model::Command>() };

if (item && senderCommand)
{
auto changedProperty = e.PropertyName();
if (changedProperty == L"Name")
{
item->Name(senderCommand.Name());
}
else if (changedProperty == L"KeyChordText")
{
item->KeyChordText(senderCommand.KeyChordText());
}
else if (changedProperty == L"IconPath")
{
item->Icon(senderCommand.IconPath());
}
}
});
}
}
15 changes: 3 additions & 12 deletions src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ namespace winrt::TerminalApp::implementation
}
else if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
{
auto moved = _MovePane(realArgs.TabIndex());
auto moved = _MovePane(realArgs);
args.Handled(moved);
}
}
Expand Down Expand Up @@ -811,17 +811,8 @@ namespace winrt::TerminalApp::implementation
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<MoveTabArgs>())
{
auto direction = realArgs.Direction();
if (direction != MoveTabDirection::None)
{
if (auto focusedTabIndex = _GetFocusedTabIndex())
{
auto currentTabIndex = focusedTabIndex.value();
auto delta = direction == MoveTabDirection::Forward ? 1 : -1;
_TryMoveTab(currentTabIndex, currentTabIndex + delta);
}
}
actionArgs.Handled(true);
auto moved = _MoveTab(realArgs);
actionArgs.Handled(moved);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalApp/AppCommandlineArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ void AppCommandlineArgs::_buildMovePaneParser()
if (_movePaneTabIndex >= 0)
{
movePaneAction.Action(ShortcutAction::MovePane);
MovePaneArgs args{ static_cast<unsigned int>(_movePaneTabIndex) };
MovePaneArgs args{ static_cast<unsigned int>(_movePaneTabIndex), L"" };
movePaneAction.Args(args);
_startupActions.push_back(movePaneAction);
}
Expand Down
13 changes: 11 additions & 2 deletions src/cascadia/TerminalApp/ContentManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,17 @@ namespace winrt::TerminalApp::implementation
return it != _content.end() ? it->second : ControlInteractivity{ nullptr };
}

void ContentManager::_closedHandler(winrt::Windows::Foundation::IInspectable sender,
winrt::Windows::Foundation::IInspectable e)
void ContentManager::Detach(const Microsoft::Terminal::Control::TermControl& control)
{
const auto contentId{ control.ContentId() };
if (const auto& content{ TryLookupCore(contentId) })
{
control.Detach();
}
}

void ContentManager::_closedHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable&)
{
if (const auto& content{ sender.try_as<winrt::Microsoft::Terminal::Control::ControlInteractivity>() })
{
Expand Down
9 changes: 7 additions & 2 deletions src/cascadia/TerminalApp/ContentManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ Class Name:
other threads.
- When you want to create a new TermControl, call CreateCore to instantiate a
new content with a GUID for later reparenting.
- Detach can be used to temporarily remove a content from its hosted
TermControl. After detaching, you can still use LookupCore &
TermControl::AttachContent to re-attach to the content.
--*/
#pragma once

Expand All @@ -35,10 +38,12 @@ namespace winrt::TerminalApp::implementation
const Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection);
Microsoft::Terminal::Control::ControlInteractivity TryLookupCore(uint64_t id);

void Detach(const Microsoft::Terminal::Control::TermControl& control);

private:
std::unordered_map<uint64_t, Microsoft::Terminal::Control::ControlInteractivity> _content;

void _closedHandler(winrt::Windows::Foundation::IInspectable sender,
winrt::Windows::Foundation::IInspectable e);
void _closedHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& e);
};
}
Loading

0 comments on commit 17a5b77

Please sign in to comment.