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 a HighlightCursor keybinding #10821

Draft
wants to merge 18 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 10 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
10 changes: 10 additions & 0 deletions src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -861,4 +861,14 @@ namespace winrt::TerminalApp::implementation
}
}
}

void TerminalPage::_HandleHighlightCursor(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto termControl{ _GetActiveControl() })
{
termControl.HighlightCursor();
args.Handled(true);
}
}
}
12 changes: 12 additions & 0 deletions src/cascadia/TerminalControl/ControlCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _terminal != nullptr && _terminal->IsTrackingMouseInput();
}

bool ControlCore::IsCursorOffScreen() const
{
// If we haven't been initialized yet, then just return true
if (!_initializedTerminal)
{
return true;
}

auto lock = _terminal->LockForReading();
return _terminal->IsCursorOffScreen();
}

til::point ControlCore::CursorPosition() const
{
// If we haven't been initialized yet, then fake it.
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalControl/ControlCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void CursorOn(const bool isCursorOn);

bool IsVtMouseModeEnabled() const;
bool IsCursorOffScreen() const;
til::point CursorPosition() const;

bool HasSelection() const;
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalControl/ControlCore.idl
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ namespace Microsoft.Terminal.Control
Boolean IsInReadOnlyMode { get; };
Boolean CursorOn;
void EnablePainting();
Boolean IsCursorOffScreen();

event FontSizeChangedEventArgs FontSizeChanged;

Expand Down
28 changes: 28 additions & 0 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2381,6 +2381,34 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core.TaskbarProgress();
}

// Method Description:
// - Toggles the spotlight on the cursor
// - If the cursor is currently off the screen, does nothing
void TermControl::HighlightCursor()
{
if (CursorLight::GetIsTarget(RootGrid()))
{
CursorLight::SetIsTarget(RootGrid(), false);
}
else if (!_core.IsCursorOffScreen())
{
// Compute the location of where to place the light
const auto charSizeInPixels = CharacterDimensions();
const auto htInDips = charSizeInPixels.Height / SwapChainPanel().CompositionScaleY();
const auto wtInDips = charSizeInPixels.Width / SwapChainPanel().CompositionScaleX();
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
const til::size fontSize{ til::math::rounding, _core.FontSize() };
const til::point cursorPos = _core.CursorPosition();
const til::point cursorPosInPixels{ cursorPos * fontSize };
const til::point cursorPosInDIPs{ cursorPosInPixels / SwapChainPanel().CompositionScaleX() };
const til::point cursorLocationInDIPs{ cursorPosInDIPs + marginsInDips };

CursorLight().ChangeLocation(gsl::narrow_cast<float>(cursorLocationInDIPs.x()) + wtInDips / 2,
gsl::narrow_cast<float>(cursorLocationInDIPs.y()) + htInDips / 2);
CursorLight::SetIsTarget(RootGrid(), true);
}
}

void TermControl::BellLightOn()
{
// Initialize the animation if it does not exist
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalControl/TermControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation

void BellLightOn();

void HighlightCursor();

bool ReadOnly() const noexcept;
void ToggleReadOnly();

Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalControl/TermControl.idl
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ namespace Microsoft.Terminal.Control

void BellLightOn();

void HighlightCursor();

Boolean ReadOnly { get; };
void ToggleReadOnly();
}
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalControl/TermControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<Grid x:Name="RootGrid">
<Grid.Lights>
<local:VisualBellLight x:Name="BellLight" />
<local:CursorLight x:Name="CursorLight" />
</Grid.Lights>
<Image x:Name="BackgroundImage"
AutomationProperties.AccessibilityView="Raw" />
Expand Down
94 changes: 49 additions & 45 deletions src/cascadia/TerminalControl/XamlLights.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,15 @@
#include "TermControl.h"
#include "XamlLights.h"
#include "VisualBellLight.g.cpp"
#include "CursorLight.g.cpp"

using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Media;

namespace winrt::Microsoft::Terminal::Control::implementation
{
DependencyProperty VisualBellLight::_IsTargetProperty{ nullptr };

VisualBellLight::VisualBellLight()
{
_InitializeProperties();
}
DependencyProperty CursorLight::_IsTargetProperty{ nullptr };

void VisualBellLight::_InitializeProperties()
{
Expand Down Expand Up @@ -49,59 +46,66 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}

// Method Description:
// - This function is called when there are no more target UIElements on the screen
// - Disposes of composition resources when no longer in use
// Arguments:
// - oldElement: unused
void VisualBellLight::OnDisconnected(UIElement const& /* oldElement */)
void CursorLight::_InitializeProperties()
PankajBhojwani marked this conversation as resolved.
Show resolved Hide resolved
{
if (CompositionLight())
// Initialize any dependency properties here.
// This performs a lazy load on these properties, instead of
// initializing them when the DLL loads.
if (!_IsTargetProperty)
{
CompositionLight(nullptr);
_IsTargetProperty =
DependencyProperty::RegisterAttached(
L"IsTarget",
winrt::xaml_typename<bool>(),
winrt::xaml_typename<Control::CursorLight>(),
PropertyMetadata{ winrt::box_value(false), PropertyChangedCallback{ &CursorLight::OnIsTargetChanged } });
}
}

winrt::hstring VisualBellLight::GetId()
void CursorLight::ChangeLocation(float xCoord, float yCoord)
{
return VisualBellLight::GetIdStatic();
if (const auto light = CompositionLight())
{
if (const auto cursorLight = light.try_as<Windows::UI::Composition::SpotLight>())
{
cursorLight.Offset({ xCoord, yCoord, 100 });
}
}
else
{
_InitializeHelper(xCoord, yCoord);
}
}

void VisualBellLight::OnIsTargetChanged(DependencyObject const& d, DependencyPropertyChangedEventArgs const& e)
// Method Description:
// - This function is called when the first target UIElement is shown on the screen,
// this enables delaying composition object creation until it's actually necessary.
// Arguments:
// - newElement: unused
void CursorLight::OnConnected(UIElement const& /* newElement */)
{
const auto uielem{ d.try_as<UIElement>() };
const auto brush{ d.try_as<Brush>() };

if (!uielem && !brush)
if (!CompositionLight())
{
// terminate early
return;
_InitializeHelper(0, 0);
}
}

const auto isAdding = winrt::unbox_value<bool>(e.NewValue());
const auto id = GetIdStatic();

if (isAdding)
{
if (uielem)
{
XamlLight::AddTargetElement(id, uielem);
}
else
{
XamlLight::AddTargetBrush(id, brush);
}
}
else
// Method Description:
// - Helper to initialize the propoerties of the spotlight such as the location and
// the angles of the inner and outer cones
// Arguments:
// - xCoord: the x-coordinate of where to put the light
// - yCoord: the y-coordinate of where to put the light
void CursorLight::_InitializeHelper(float xCoord, float yCoord)
{
if (!CompositionLight())
{
if (uielem)
{
XamlLight::RemoveTargetElement(id, uielem);
}
else
{
XamlLight::RemoveTargetBrush(id, brush);
}
auto spotLight{ Window::Current().Compositor().CreateSpotLight() };
spotLight.InnerConeColor(Windows::UI::Colors::White());
spotLight.InnerConeAngleInDegrees(10);
spotLight.OuterConeAngleInDegrees(25);
spotLight.Offset({ xCoord, yCoord, 100 });
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
CompositionLight(spotLight);
}
}
}
Loading