Add selection marker overlays for keyboard selection

This commit is contained in:
Carlos Zamora 2021-07-30 17:46:42 -07:00
parent b7842624a0
commit ad2678051f
11 changed files with 163 additions and 8 deletions

View file

@ -378,6 +378,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto lock = _terminal->LockForWriting();
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second);
_renderer->TriggerSelection();
_UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(false));
return true;
}
@ -386,6 +387,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_terminal->ClearSelection();
_renderer->TriggerSelection();
_UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(true));
}
// When there is a selection active, escape should clear it and NOT flow through
@ -909,6 +911,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_terminal->SetSelectionAnchor(position);
}
Core::Point ControlCore::SelectionAnchor() const
{
auto lock = _terminal->LockForReading();
return til::point{ _terminal->SelectionStartForRendering() };
}
Core::Point ControlCore::SelectionEnd() const
{
auto lock = _terminal->LockForReading();
return til::point{ _terminal->SelectionEndForRendering() };
}
bool ControlCore::MovingStart() const
{
auto lock = _terminal->LockForReading();
return _terminal->MovingStart();
}
// Method Description:
// - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging.
// Arguments:
@ -935,6 +955,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// save location (for rendering) + render
_terminal->SetSelectionEnd(terminalPosition);
_renderer->TriggerSelection();
_UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(true));
}
// Called when the Terminal wants to set something to the clipboard, i.e.
@ -995,6 +1016,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_terminal->ClearSelection();
_renderer->TriggerSelection();
_UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(true));
}
// send data up for clipboard
@ -1286,6 +1308,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_terminal->SetBlockSelection(false);
search.Select();
_renderer->TriggerSelection();
_UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(true));
}
}
@ -1478,6 +1501,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
_renderer->TriggerSelection();
_UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(true));
}
void ControlCore::AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine)

View file

@ -128,6 +128,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool HasSelection() const;
bool CopyOnSelect() const;
Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
Core::Point SelectionAnchor() const;
Core::Point SelectionEnd() const;
bool MovingStart() const;
void SetSelectionAnchor(til::point const& position);
void SetEndSelectionPoint(til::point const& position);
@ -169,6 +172,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs);
TYPED_EVENT(TransparencyChanged, IInspectable, Control::TransparencyChangedEventArgs);
TYPED_EVENT(ReceivedOutput, IInspectable, IInspectable);
TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs);
// clang-format on
private:

View file

@ -80,6 +80,9 @@ namespace Microsoft.Terminal.Control
Boolean HasSelection { get; };
IVector<String> SelectedText(Boolean trimTrailingWhitespace);
Microsoft.Terminal.Core.Point SelectionAnchor { get; };
Microsoft.Terminal.Core.Point SelectionEnd { get; };
Boolean MovingStart { get; };
String HoveredUriText { get; };
Windows.Foundation.IReference<Microsoft.Terminal.Core.Point> HoveredCell { get; };
@ -110,6 +113,7 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, NoticeEventArgs> RaiseNotice;
event Windows.Foundation.TypedEventHandler<Object, TransparencyChangedEventArgs> TransparencyChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> ReceivedOutput;
event Windows.Foundation.TypedEventHandler<Object, UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
};
}

View file

@ -11,3 +11,4 @@
#include "ScrollPositionChangedArgs.g.cpp"
#include "RendererWarningArgs.g.cpp"
#include "TransparencyChangedEventArgs.g.cpp"
#include "UpdateSelectionMarkersEventArgs.g.cpp"

View file

@ -11,6 +11,7 @@
#include "ScrollPositionChangedArgs.g.h"
#include "RendererWarningArgs.g.h"
#include "TransparencyChangedEventArgs.g.h"
#include "UpdateSelectionMarkersEventArgs.g.h"
#include "cppwinrt_utils.h"
namespace winrt::Microsoft::Terminal::Control::implementation
@ -131,4 +132,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WINRT_PROPERTY(double, Opacity);
};
struct UpdateSelectionMarkersEventArgs : public UpdateSelectionMarkersEventArgsT<UpdateSelectionMarkersEventArgs>
{
public:
UpdateSelectionMarkersEventArgs(const bool clearMarkers) :
_ClearMarkers(clearMarkers)
{
}
WINRT_PROPERTY(bool, ClearMarkers, false);
};
}

View file

@ -67,4 +67,9 @@ namespace Microsoft.Terminal.Control
{
Double Opacity { get; };
}
runtimeclass UpdateSelectionMarkersEventArgs
{
Boolean ClearMarkers { get; };
}
}

View file

@ -83,6 +83,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core.TransparencyChanged({ this, &TermControl::_coreTransparencyChanged });
_core.RaiseNotice({ this, &TermControl::_coreRaisedNotice });
_core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged });
_core.UpdateSelectionMarkers({ this, &TermControl::_updateSelectionMarkers });
_interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler });
_interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
@ -333,6 +334,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto bg = newAppearance.DefaultBackground();
_changeBackgroundColor(bg);
// Update selection markers
Windows::UI::Xaml::Media::SolidColorBrush selectionBackgroundBrush{ til::color{ newAppearance.SelectionBackground() } };
SelectionStartIcon().Foreground(selectionBackgroundBrush);
SelectionEndIcon().Foreground(selectionBackgroundBrush);
// Set TSF Foreground
Media::SolidColorBrush foregroundBrush{};
foregroundBrush.Color(static_cast<til::color>(newAppearance.DefaultForeground()));
@ -1659,6 +1665,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
update.newValue = args.ViewTop();
_updateScrollBar->Run(update);
_updatePatternLocations->Run();
_updateSelectionMarkers(nullptr, winrt::make<UpdateSelectionMarkersEventArgs>(false));
}
// Method Description:
@ -2464,8 +2472,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_core.ClearHoveredCell();
}
winrt::fire_and_forget TermControl::_hoveredHyperlinkChanged(IInspectable sender,
IInspectable args)
winrt::fire_and_forget TermControl::_hoveredHyperlinkChanged(IInspectable /*sender*/,
IInspectable /*args*/)
{
auto weakThis{ get_weak() };
co_await resume_foreground(Dispatcher());
@ -2488,12 +2496,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
HyperlinkTooltipBorder().BorderThickness(newThickness);
// Compute the location of the top left corner of the cell in DIPS
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
const til::point startPos{ lastHoveredCell.Value() };
const til::size fontSize{ til::math::rounding, _core.FontSize() };
const til::point posInPixels{ startPos * fontSize };
const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() };
const til::point locationInDIPs{ posInDIPs + marginsInDips };
const til::point locationInDIPs{ _toPosInDips(lastHoveredCell.Value()) };
// Move the border to the top left corner of the cell
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(),
@ -2505,10 +2508,71 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
winrt::fire_and_forget TermControl::_updateSelectionMarkers(IInspectable /*sender*/, Control::UpdateSelectionMarkersEventArgs args)
{
auto weakThis{ get_weak() };
co_await resume_foreground(Dispatcher());
if (weakThis.get() && args)
{
if (_core.HasSelection() && !args.ClearMarkers())
{
// show/update selection markers
// figure out which endpoint to move, get it and the relevant icon (hide the other icon)
const auto movingStart{ _core.MovingStart() };
const auto selectionAnchor{ movingStart ? _core.SelectionAnchor() : _core.SelectionEnd() };
const auto& icon{ movingStart ? SelectionStartIcon() : SelectionEndIcon() };
const auto& otherIcon{ movingStart ? SelectionEndIcon() : SelectionStartIcon() };
icon.Opacity(1);
otherIcon.Opacity(0);
// Compute the location of the top left corner of the cell in DIPS
const til::point locationInDIPs{ _toPosInDips(selectionAnchor) };
// Move the icon to the top left corner of the cell
SelectionCanvas().SetLeft(icon,
(locationInDIPs.x() - SwapChainPanel().ActualOffset().x));
SelectionCanvas().SetTop(icon,
(locationInDIPs.y() - SwapChainPanel().ActualOffset().y));
}
else
{
// hide selection markers
SelectionStartIcon().Opacity(0);
SelectionEndIcon().Opacity(0);
}
}
}
til::point TermControl::_toPosInDips(const til::point terminalCellPos)
{
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
const til::size fontSize{ til::math::rounding, _core.FontSize() };
const til::point posInPixels{ terminalCellPos * fontSize };
const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() };
return posInDIPs + marginsInDips;
}
void TermControl::_coreFontSizeChanged(const int fontWidth,
const int fontHeight,
const bool isInitialChange)
{
// scale the selection markers to be the size of a cell
auto scaleIconMarker = [fontWidth, fontHeight](Windows::UI::Xaml::Controls::FontIcon icon) {
const auto size{ icon.DesiredSize() };
const auto scaleX = fontWidth / size.Width;
const auto scaleY = fontHeight / size.Height;
Windows::UI::Xaml::Media::ScaleTransform transform{};
transform.ScaleX(transform.ScaleX() * scaleX);
transform.ScaleY(transform.ScaleY() * scaleY);
icon.RenderTransform(transform);
// now hide the icon
icon.Opacity(0);
};
scaleIconMarker(SelectionStartIcon());
scaleIconMarker(SelectionEndIcon());
// Don't try to inspect the core here. The Core is raising this while
// it's holding its write lock. If the handlers calls back to some
// method on the TermControl on the same thread, and that _method_ calls

View file

@ -267,6 +267,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _FontInfoHandler(const IInspectable& sender, const FontInfoEventArgs& eventArgs);
winrt::fire_and_forget _hoveredHyperlinkChanged(IInspectable sender, IInspectable args);
winrt::fire_and_forget _updateSelectionMarkers(IInspectable sender, Control::UpdateSelectionMarkersEventArgs args);
void _coreFontSizeChanged(const int fontWidth,
const int fontHeight,
@ -274,6 +275,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args);
void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args);
void _coreWarningBell(const IInspectable& sender, const IInspectable& args);
til::point _toPosInDips(const til::point terminalCellPos);
};
}

View file

@ -73,6 +73,23 @@
</ToolTipService.ToolTip>
</Border>
</Canvas>
<Canvas x:Name="SelectionCanvas"
Visibility="Visible">
<!-- LOAD BEARING Use Opacity instead of Visibility for these two FontIcons.
Visibility::Collapsed prevents us from aquiring the desired size of the icons,
resulting in an inability to scale them upon a FontSize change event -->
<FontIcon x:Name="SelectionStartIcon"
FontFamily="Segoe MDL2 Assets"
Glyph="&#xE893;"
Opacity="0">
</FontIcon>
<FontIcon x:Name="SelectionEndIcon"
FontFamily="Segoe MDL2 Assets"
Glyph="&#xE892;"
Opacity="0">
</FontIcon>
</Canvas>
</SwapChainPanel>
<!--

View file

@ -251,6 +251,9 @@ public:
using UpdateSelectionParams = std::optional<std::pair<SelectionDirection, SelectionExpansion>>;
static UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey);
bool MovingStart() const noexcept;
til::point SelectionStartForRendering() const;
til::point SelectionEndForRendering() const;
const TextBuffer::TextAndColor RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace);
#pragma endregion

View file

@ -82,6 +82,24 @@ const COORD Terminal::GetSelectionEnd() const noexcept
return _selection->end;
}
til::point Terminal::SelectionStartForRendering() const
{
auto pos{ _selection->start };
const auto bufferSize{ _buffer->GetSize() };
bufferSize.DecrementInBounds(pos);
pos.Y = std::clamp<short>(base::ClampSub(pos.Y, _VisibleStartIndex()), bufferSize.Top(), bufferSize.BottomInclusive());
return pos;
}
til::point Terminal::SelectionEndForRendering() const
{
auto pos{ _selection->end };
const auto bufferSize{ _buffer->GetSize() };
bufferSize.IncrementInBounds(pos);
pos.Y = std::clamp<short>(base::ClampSub(pos.Y, _VisibleStartIndex()), bufferSize.Top(), bufferSize.BottomInclusive());
return pos;
}
// Method Description:
// - Checks if selection is active
// Return Value: