Add support for dragging and dropping paths onto the Terminal (#4323)
## Summary of the Pull Request
I took the code from conhost that handles this and just copy-pasted it into the terminal codebase.
## References
Original conhost code:
027f1228cb/src/interactivity/win32/windowproc.cpp (L854-L889)
## PR Checklist
* [x] Closes #904
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
Okay it was a little more complicated than that. I had `IslandWindow` handle the drop, which then raises a generic event for `AppHost` to handle. `AppHost` handles this by writing the path as input to the terminal, traversing `AppLogic`, `TerminalPage` and finally landing in `TermControl`
## Validation Steps Performed
Tested manually with both paths with and without spaces.
This commit is contained in:
parent
32ea419c3d
commit
4ad77d9ff7
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
@ -137,4 +137,7 @@
|
|||
<value>Find...</value>
|
||||
<comment>The placeholder text in the search dialog TextBox</comment>
|
||||
</data>
|
||||
</root>
|
||||
<data name="DragFileCaption" xml:space="preserve">
|
||||
<value>Copy path to file</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <Utf16Parser.hpp>
|
||||
#include <Utils.h>
|
||||
#include <WinUser.h>
|
||||
#include <LibraryResources.h>
|
||||
#include "..\..\types\inc\GlyphWidth.hpp"
|
||||
|
||||
#include "TermControl.g.cpp"
|
||||
|
@ -21,6 +22,7 @@ using namespace winrt::Windows::UI::Xaml::Automation::Peers;
|
|||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
|
@ -162,6 +164,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
_connectionStateChangedRevoker = _connection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) {
|
||||
_ConnectionStateChangedHandlers(*this, nullptr);
|
||||
});
|
||||
|
||||
_root.AllowDrop(true);
|
||||
_root.Drop({ get_weak(), &TermControl::_DragDropHandler });
|
||||
_root.DragOver({ get_weak(), &TermControl::_DragOverHandler });
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1390,6 +1396,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Writes the given sequence as input to the active terminal connection,
|
||||
// Arguments:
|
||||
// - wstr: the string of characters to write to the terminal connection.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TermControl::_SendInputToConnection(const std::wstring& wstr)
|
||||
{
|
||||
_connection.WriteInput(wstr);
|
||||
|
@ -2150,6 +2162,95 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
return std::pow(cursorDistanceFromBorder, 2.0) / 25.0 + 2.0;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Async handler for the "Drop" event. If a file was dropped onto our
|
||||
// root, we'll try to get the path of the file dropped onto us, and write
|
||||
// the full path of the file to our terminal connection. Like conhost, if
|
||||
// the path contains a space, we'll wrap the path in quotes.
|
||||
// - Unlike conhost, if multiple files are dropped onto the terminal, we'll
|
||||
// write all the paths to the terminal, separated by spaces.
|
||||
// Arguments:
|
||||
// - e: The DragEventArgs from the Drop event
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget TermControl::_DoDragDrop(DragEventArgs const e)
|
||||
{
|
||||
if (e.DataView().Contains(StandardDataFormats::StorageItems()))
|
||||
{
|
||||
auto items = co_await e.DataView().GetStorageItemsAsync();
|
||||
if (items.Size() > 0)
|
||||
{
|
||||
std::wstring allPaths;
|
||||
for (auto item : items)
|
||||
{
|
||||
// Join the paths with spaces
|
||||
if (!allPaths.empty())
|
||||
{
|
||||
allPaths += L" ";
|
||||
}
|
||||
|
||||
std::wstring fullPath{ item.Path() };
|
||||
const auto containsSpaces = std::find(fullPath.begin(),
|
||||
fullPath.end(),
|
||||
L' ') != fullPath.end();
|
||||
|
||||
auto lock = _terminal->LockForWriting();
|
||||
|
||||
if (containsSpaces)
|
||||
{
|
||||
fullPath.insert(0, L"\"");
|
||||
fullPath += L"\"";
|
||||
}
|
||||
|
||||
allPaths += fullPath;
|
||||
}
|
||||
_SendInputToConnection(allPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Synchronous handler for the "Drop" event. We'll dispatch the async
|
||||
// _DoDragDrop method to handle this, because getting information about
|
||||
// the file that was potentially dropped onto us must be done off the UI
|
||||
// thread.
|
||||
// Arguments:
|
||||
// - e: The DragEventArgs from the Drop event
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TermControl::_DragDropHandler(Windows::Foundation::IInspectable const& /*sender*/,
|
||||
DragEventArgs const& e)
|
||||
{
|
||||
// Dispatch an async method to handle the drop event.
|
||||
_DoDragDrop(e);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handle the DragOver event. We'll signal that the drag operation we
|
||||
// support is the "copy" operation, and we'll also customize the
|
||||
// appearance of the drag-drop UI, by removing the preview and setting a
|
||||
// custom caption. For more information, see
|
||||
// https://docs.microsoft.com/en-us/windows/uwp/design/input/drag-and-drop#customize-the-ui
|
||||
// Arguments:
|
||||
// - e: The DragEventArgs from the DragOver event
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TermControl::_DragOverHandler(Windows::Foundation::IInspectable const& /*sender*/,
|
||||
DragEventArgs const& e)
|
||||
{
|
||||
// Make sure to set the AcceptedOperation, so that we can later recieve the path in the Drop event
|
||||
e.AcceptedOperation(winrt::Windows::ApplicationModel::DataTransfer::DataPackageOperation::Copy);
|
||||
|
||||
// Sets custom UI text
|
||||
e.DragUIOverride().Caption(RS_(L"DragFileCaption"));
|
||||
// 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 visibile
|
||||
e.DragUIOverride().IsGlyphVisible(false);
|
||||
}
|
||||
|
||||
// -------------------------------- 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.
|
||||
|
|
|
@ -187,6 +187,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
|||
void _ScrollbarChangeHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e);
|
||||
void _GotFocusHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void _LostFocusHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void _DragDropHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::DragEventArgs const& e);
|
||||
void _DragOverHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::DragEventArgs const& e);
|
||||
winrt::fire_and_forget _DoDragDrop(Windows::UI::Xaml::DragEventArgs const e);
|
||||
|
||||
void _BlinkCursor(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
|
||||
void _SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition);
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
#include <winrt/Windows.UI.Xaml.Input.h>
|
||||
#include <winrt/Windows.UI.Xaml.Interop.h>
|
||||
#include <winrt/Windows.ui.xaml.markup.h>
|
||||
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
|
||||
#include <windows.ui.xaml.media.dxinterop.h>
|
||||
|
||||
|
|
Loading…
Reference in a new issue