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

implement drag&drop path in '+' button (#10073) #10160

Merged
2 commits merged into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions src/cascadia/TerminalApp/Resources/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -649,4 +649,7 @@
<value>Open in Windows Terminal</value>
<comment>{Locked="Windows"} This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal</comment>
</data>
<data name="DropPathTabRun.Text" xml:space="preserve">
<value>Open a new tab in given starting directory</value>
</data>
</root>
48 changes: 48 additions & 0 deletions src/cascadia/TerminalApp/TabRowControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,19 @@
#include "TabRowControl.h"

#include "TabRowControl.g.cpp"
#include <LibraryResources.h>

using namespace winrt::Windows::ApplicationModel::DataTransfer;

using namespace winrt;
using namespace winrt::Microsoft::UI::Xaml;
using namespace winrt::Windows::UI::Text;

namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
namespace WUX = Windows::UI::Xaml;
}

namespace winrt::TerminalApp::implementation
{
Expand All @@ -23,4 +33,42 @@ namespace winrt::TerminalApp::implementation
void TabRowControl::OnNewTabButtonClick(IInspectable const&, Controls::SplitButtonClickEventArgs const&)
{
}

// Method Description:
// - Bound in Drag&Drop of the Xaml editor to the [+] button.
// Arguments:
// <unused>
void TabRowControl::OnNewTabButtonDrop(IInspectable const&, winrt::Windows::UI::Xaml::DragEventArgs const&)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have to make and bind this if nothing is going to happen? Is this some XAML trickery?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, I did copy-cat of the other Drag&Drop stuff.
I`ll try to remove it and see what happens

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whichever way it works, I'm fine with it. It was just an interesting curiosity to me.

{
}

// Method Description:
// - Bound in Drag-over of the Xaml editor to the [+] button.
// Allows drop of 'StorageItems' which will be used as StartingDirectory
// Arguments:
// - <unused>
// - e: DragEventArgs which hold the items
void TabRowControl::OnNewTabButtonDragOver(IInspectable const&, winrt::Windows::UI::Xaml::DragEventArgs const& e)
{
// We can only handle drag/dropping StorageItems (files).
// If the format on the clipboard is anything else, returning
// early here will prevent the drag/drop from doing anything.
if (!e.DataView().Contains(StandardDataFormats::StorageItems()))
{
return;
}

// Make sure to set the AcceptedOperation, so that we can later receive the path in the Drop event
e.AcceptedOperation(DataPackageOperation::Copy);

// Sets custom UI text
e.DragUIOverride().Caption(RS_(L"DropPathTabRun/Text"));

// Sets if the caption is visible
e.DragUIOverride().IsCaptionVisible(true);
// Sets if the dragged content is visible
e.DragUIOverride().IsContentVisible(false);
// Sets if the glyph is visible
e.DragUIOverride().IsGlyphVisible(false);
}
}
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/TabRowControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace winrt::TerminalApp::implementation
TabRowControl();

void OnNewTabButtonClick(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::Controls::SplitButtonClickEventArgs const& args);
void OnNewTabButtonDrop(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::DragEventArgs const& e);
void OnNewTabButtonDragOver(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::DragEventArgs const& e);
};
}

Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalApp/TabRowControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@
x:Uid="NewTabSplitButton"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
AllowDrop="True"
AutomationProperties.AccessibilityView="Control"
BorderThickness="0"
Click="OnNewTabButtonClick"
Content="&#xE710;"
CornerRadius="{Binding Source={ThemeResource OverlayCornerRadius}, Converter={StaticResource TopCornerRadiusFilterConverter}}"
DragOver="OnNewTabButtonDragOver"
Drop="OnNewTabButtonDrop"
FontFamily="Segoe MDL2 Assets"
FontSize="12"
FontWeight="SemiLight"
Expand Down
154 changes: 83 additions & 71 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "Utils.h"
#include "../../types/inc/utils.hpp"

#include <filesystem>
#include <LibraryResources.h>

#include "TerminalPage.g.cpp"
Expand Down Expand Up @@ -172,40 +173,13 @@ namespace winrt::TerminalApp::implementation
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
if (auto page{ weakThis.get() })
{
// if alt is pressed, open a pane
const CoreWindow window = CoreWindow::GetForCurrentThread();
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);

const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
const auto rShiftState = window.GetKeyState(VirtualKey::RightShift);
const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift);
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };

// Check for DebugTap
bool debugTap = page->_settings.GlobalSettings().DebugFeaturesEnabled() &&
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);

if (altPressed && !debugTap)
{
page->_SplitPane(SplitState::Automatic,
SplitType::Manual,
0.5f,
nullptr);
}
else if (shiftPressed && !debugTap)
{
page->_OpenNewWindow(false, NewTerminalArgs());
}
else
{
page->_OpenNewTab(nullptr);
}
page->_OpenNewTerminal(NewTerminalArgs());
}
});
_newTabButton.Drop([weakThis{ get_weak() }](Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::DragEventArgs e) {
if (auto page{ weakThis.get() })
{
page->NewTerminalByDrop(e);
}
});
_tabView.SelectionChanged({ this, &TerminalPage::_OnTabSelectionChanged });
Expand Down Expand Up @@ -262,6 +236,37 @@ namespace winrt::TerminalApp::implementation
CATCH_LOG();
}

winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e)
{
Windows::Foundation::Collections::IVectorView<Windows::Storage::IStorageItem> items;
try
{
items = co_await e.DataView().GetStorageItemsAsync();
}
CATCH_LOG();

if (items.Size() == 1)
{
std::filesystem::path path(items.GetAt(0).Path().c_str());
if (!std::filesystem::is_directory(path))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder how well std::filesystem::is_directory handles UNC paths.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested it with \\HOST\folder and \\HOST\folder\file.txt, std::filesystem::is_directory handles it just fine.
Sadly darg & drop this UNC path only works with windows powershell.
Command Prompt gives the following error:

'\\HOST\folder'
CMD.EXE was started with the above path as the current directory.
UNC paths are not supported.  Defaulting to Windows directory.
Microsoft Windows [Version 10.0.19042.985]
(c) Microsoft Corporation. All rights reserved.

C:\Windows>

while ubuntu just default to the home folder.

{
path = path.parent_path();
}

std::wstring pathText = path.wstring();

// Handle edge case of "C:\\", seems like the "StartingDirectory" doesn't like path which ends with '\'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait what? My experience with StartingDirectory tells me this can't be true ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This edge case happens when you press shift and drop the C: drive on it, you get the following error:

[error 0x8007010b when launching `cmd.exe']
Could not access starting directory "C:\WINDOWS\SysWOW64""

D: drive

[error 0x8007010b when launching `cmd.exe']
Could not access starting directory "D:""

While trying to debug the value of pathText I notice normal folders don't end with \ while C & D drives do.
removing that \ solved the problem...maybe it solved it by avoiding it?
can you please help me understand why it happens?
note that pressing shift is critical since normal drop works just fine.
If you think it should work without this if will trying to debug it further.

if (pathText.back() == L'\\')
{
pathText.erase(std::prev(pathText.end()));
}

NewTerminalArgs args;
args.StartingDirectory(winrt::hstring{ pathText });
this->_OpenNewTerminal(args);
}
}

// Method Description:
// - This method is called once command palette action was chosen for dispatching
// We'll use this event to dispatch this command.
Expand Down Expand Up @@ -622,43 +627,7 @@ namespace winrt::TerminalApp::implementation
if (auto page{ weakThis.get() })
{
NewTerminalArgs newTerminalArgs{ profileIndex };

// if alt is pressed, open a pane
const CoreWindow window = CoreWindow::GetForCurrentThread();
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);

const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
const auto rShiftState = window.GetKeyState(VirtualKey::RightShift);
const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift);
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };

// Check for DebugTap
bool debugTap = page->_settings.GlobalSettings().DebugFeaturesEnabled() &&
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);

if (altPressed && !debugTap)
{
page->_SplitPane(SplitState::Automatic,
SplitType::Manual,
0.5f,
newTerminalArgs);
}
else if (shiftPressed && !debugTap)
{
// Manually fill in the evaluated profile.
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(page->_settings.GetProfileForArgs(newTerminalArgs)));
page->_OpenNewWindow(false, newTerminalArgs);
}
else
{
page->_OpenNewTab(newTerminalArgs);
}
page->_OpenNewTerminal(newTerminalArgs);
}
});
newTabFlyout.Items().Append(profileMenuItem);
Expand Down Expand Up @@ -752,6 +721,49 @@ namespace winrt::TerminalApp::implementation
_newTabButton.Flyout().ShowAt(_newTabButton);
}

void TerminalPage::_OpenNewTerminal(const NewTerminalArgs newTerminalArgs)
{
// if alt is pressed, open a pane
const CoreWindow window = CoreWindow::GetForCurrentThread();
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);

const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
const auto rShiftState = window.GetKeyState(VirtualKey::RightShift);
const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift);
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };

// Check for DebugTap
bool debugTap = this->_settings.GlobalSettings().DebugFeaturesEnabled() &&
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);

if (altPressed && !debugTap)
{
this->_SplitPane(SplitState::Automatic,
SplitType::Manual,
0.5f,
newTerminalArgs);
}
else if (shiftPressed && !debugTap)
{
// Manually fill in the evaluated profile.
if (newTerminalArgs.ProfileIndex() != nullptr)
{
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(this->_settings.GetProfileForArgs(newTerminalArgs)));
}
this->_OpenNewWindow(false, newTerminalArgs);
}
else
{
this->_OpenNewTab(newTerminalArgs);
}
}

winrt::fire_and_forget TerminalPage::_RemoveOnCloseRoutine(Microsoft::UI::Xaml::Controls::TabViewItem tabViewItem, winrt::com_ptr<TerminalPage> page)
{
co_await winrt::resume_foreground(page->_tabView.Dispatcher());
Expand Down
4 changes: 4 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ namespace winrt::TerminalApp::implementation

void Create();

winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e);

hstring Title();

void TitlebarClicked();
Expand Down Expand Up @@ -192,6 +194,8 @@ namespace winrt::TerminalApp::implementation

winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);

void _OpenNewTerminal(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);

bool _displayingCloseDialog{ false };
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _CommandPaletteButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
Expand Down