Skip to content

Commit 90a82d4

Browse files
committed
Add support for horizontal mouse wheel events
This adds support for horizontal mouse wheel events (WM_MOUSEHWHEEL). With this change, applications running in the terminal can now receive and respond to horizontal scroll inputs from the mouse/trackpad. Fixes: #19245
1 parent 1b2aad6 commit 90a82d4

18 files changed

+100
-44
lines changed

src/cascadia/TerminalControl/ControlInteractivity.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
485485
// - modifiers: The modifiers pressed during this event, in the form of a VirtualKeyModifiers
486486
// - delta: the mouse wheel delta that triggered this event.
487487
bool ControlInteractivity::MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
488+
const bool horizontal,
488489
const int32_t delta,
489490
const Core::Point pixelPosition,
490491
const Control::MouseButtonState buttonState)
@@ -506,7 +507,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
506507
// PointerPoint to work with. So, we're just going to do a
507508
// mousewheel event manually
508509
return _sendMouseEventHelper(terminalPosition,
509-
WM_MOUSEWHEEL,
510+
horizontal ? WM_MOUSEHWHEEL : WM_MOUSEWHEEL,
510511
modifiers,
511512
::base::saturated_cast<short>(delta),
512513
buttonState);
@@ -667,7 +668,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
667668
{
668669
return false;
669670
}
670-
return _core->ShouldSendAlternateScroll(WM_MOUSEWHEEL, delta);
671+
return _core->ShouldSendAlternateScroll(WM_MOUSEWHEEL, delta) || _core->ShouldSendAlternateScroll(WM_MOUSEHWHEEL, delta);
671672
}
672673

673674
// Method Description:

src/cascadia/TerminalControl/ControlInteractivity.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
7474
void TouchReleased();
7575

7676
bool MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
77+
const bool horizontal,
7778
const int32_t delta,
7879
const Core::Point pixelPosition,
7980
const Control::MouseButtonState state);

src/cascadia/TerminalControl/ControlInteractivity.idl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ namespace Microsoft.Terminal.Control
6060
void TouchReleased();
6161

6262
Boolean MouseWheel(Microsoft.Terminal.Core.ControlKeyStates modifiers,
63+
Boolean horizontal,
6364
Int32 delta,
6465
Microsoft.Terminal.Core.Point pixelPosition,
6566
MouseButtonState state);

src/cascadia/TerminalControl/IMouseWheelListener.idl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ namespace Microsoft.Terminal.Control
1111
[uuid("65b8b8c5-988f-43ff-aba9-e89368da1598")]
1212
interface IMouseWheelListener
1313
{
14-
Boolean OnMouseWheel(Windows.Foundation.Point coord, Int32 delta, Boolean leftButtonDown, Boolean midButtonDown, Boolean rightButtonDown);
14+
Boolean OnMouseWheel(Windows.Foundation.Point coord, Boolean horizontal, Int32 delta, Boolean leftButtonDown, Boolean midButtonDown, Boolean rightButtonDown);
1515
}
1616
}

src/cascadia/TerminalControl/TermControl.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2164,14 +2164,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
21642164
RestorePointerCursor.raise(*this, nullptr);
21652165

21662166
const auto point = args.GetCurrentPoint(*this);
2167-
// GH#10329 - we don't need to handle horizontal scrolls. Only vertical ones.
2168-
// So filter out the horizontal ones.
2169-
if (point.Properties().IsHorizontalMouseWheel())
2170-
{
2171-
return;
2172-
}
2173-
21742167
auto result = _interactivity.MouseWheel(ControlKeyStates{ args.KeyModifiers() },
2168+
point.Properties().IsHorizontalMouseWheel(),
21752169
point.Properties().MouseWheelDelta(),
21762170
_toTerminalOrigin(point.Position()),
21772171
TermControl::GetPressedMouseButtons(point));
@@ -2192,6 +2186,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
21922186
// - delta: the mouse wheel delta that triggered this event.
21932187
// - state: the state for each of the mouse buttons individually (pressed/unpressed)
21942188
bool TermControl::OnMouseWheel(const Windows::Foundation::Point location,
2189+
const bool horizontal,
21952190
const int32_t delta,
21962191
const bool leftButtonDown,
21972192
const bool midButtonDown,
@@ -2204,7 +2199,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
22042199
WI_SetFlagIf(state, Control::MouseButtonState::IsMiddleButtonDown, midButtonDown);
22052200
WI_SetFlagIf(state, Control::MouseButtonState::IsRightButtonDown, rightButtonDown);
22062201

2207-
return _interactivity.MouseWheel(modifiers, delta, _toTerminalOrigin(location), state);
2202+
return _interactivity.MouseWheel(modifiers, horizontal, delta, _toTerminalOrigin(location), state);
22082203
}
22092204

22102205
// Method Description:

src/cascadia/TerminalControl/TermControl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
147147

148148
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
149149

150-
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown);
150+
bool OnMouseWheel(const Windows::Foundation::Point location, const bool horizontal, const int32_t delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown);
151151

152152
~TermControl();
153153

src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ namespace ControlUnitTests
170170

171171
// The mouse location and buttons don't matter here.
172172
interactivity->MouseWheel(modifiers,
173+
false,
173174
30,
174175
Core::Point{ 0, 0 },
175176
buttonState);
@@ -188,6 +189,7 @@ namespace ControlUnitTests
188189

189190
// The mouse location and buttons don't matter here.
190191
interactivity->MouseWheel(modifiers,
192+
false,
191193
-30,
192194
Core::Point{ 0, 0 },
193195
buttonState);
@@ -245,6 +247,7 @@ namespace ControlUnitTests
245247
expectedTop = 20;
246248

247249
interactivity->MouseWheel(modifiers,
250+
false,
248251
WHEEL_DELTA,
249252
Core::Point{ 0, 0 },
250253
buttonState);
@@ -254,17 +257,20 @@ namespace ControlUnitTests
254257
{
255258
expectedTop--;
256259
interactivity->MouseWheel(modifiers,
260+
false,
257261
WHEEL_DELTA,
258262
Core::Point{ 0, 0 },
259263
buttonState);
260264
}
261265
Log::Comment(L"Scrolling up more should do nothing");
262266
expectedTop = 0;
263267
interactivity->MouseWheel(modifiers,
268+
false,
264269
WHEEL_DELTA,
265270
Core::Point{ 0, 0 },
266271
buttonState);
267272
interactivity->MouseWheel(modifiers,
273+
false,
268274
WHEEL_DELTA,
269275
Core::Point{ 0, 0 },
270276
buttonState);
@@ -275,6 +281,7 @@ namespace ControlUnitTests
275281
Log::Comment(NoThrowString().Format(L"---scroll down #%d---", i));
276282
expectedTop++;
277283
interactivity->MouseWheel(modifiers,
284+
false,
278285
-WHEEL_DELTA,
279286
Core::Point{ 0, 0 },
280287
buttonState);
@@ -283,10 +290,12 @@ namespace ControlUnitTests
283290
Log::Comment(L"Scrolling down more should do nothing");
284291
expectedTop = 21;
285292
interactivity->MouseWheel(modifiers,
293+
false,
286294
-WHEEL_DELTA,
287295
Core::Point{ 0, 0 },
288296
buttonState);
289297
interactivity->MouseWheel(modifiers,
298+
false,
290299
-WHEEL_DELTA,
291300
Core::Point{ 0, 0 },
292301
buttonState);
@@ -444,6 +453,7 @@ namespace ControlUnitTests
444453

445454
Log::Comment(L"Scroll up a line, with the left mouse button selected");
446455
interactivity->MouseWheel(modifiers,
456+
false,
447457
WHEEL_DELTA,
448458
cursorPosition1.to_core_point(),
449459
leftMouseDown);
@@ -492,55 +502,55 @@ namespace ControlUnitTests
492502
const Core::Point mousePos{ 0, 0 };
493503
Control::MouseButtonState state{};
494504

495-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5
505+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 1/5
496506
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
497507

498508
Log::Comment(L"Scroll up 4 more times. Once we're at 3/5 scrolls, "
499509
L"we'll round the internal scrollbar position to scrolling to the next row.");
500-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 2/5
510+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 2/5
501511
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
502-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 3/5
512+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 3/5
503513
VERIFY_ARE_EQUAL(20, core->ScrollOffset());
504-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 4/5
514+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 4/5
505515
VERIFY_ARE_EQUAL(20, core->ScrollOffset());
506-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 5/5
516+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 5/5
507517
VERIFY_ARE_EQUAL(20, core->ScrollOffset());
508518

509519
Log::Comment(L"Jump to line 5, so we can scroll down from there.");
510520
interactivity->UpdateScrollbar(5);
511521
VERIFY_ARE_EQUAL(5, core->ScrollOffset());
512522
Log::Comment(L"Scroll down 5 times, at which point we should accumulate a whole row of delta.");
513-
interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 1/5
523+
interactivity->MouseWheel(modifiers, false, -delta, mousePos, state); // 1/5
514524
VERIFY_ARE_EQUAL(5, core->ScrollOffset());
515-
interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 2/5
525+
interactivity->MouseWheel(modifiers, false, -delta, mousePos, state); // 2/5
516526
VERIFY_ARE_EQUAL(5, core->ScrollOffset());
517-
interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 3/5
527+
interactivity->MouseWheel(modifiers, false, -delta, mousePos, state); // 3/5
518528
VERIFY_ARE_EQUAL(6, core->ScrollOffset());
519-
interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 4/5
529+
interactivity->MouseWheel(modifiers, false, -delta, mousePos, state); // 4/5
520530
VERIFY_ARE_EQUAL(6, core->ScrollOffset());
521-
interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 5/5
531+
interactivity->MouseWheel(modifiers, false, -delta, mousePos, state); // 5/5
522532
VERIFY_ARE_EQUAL(6, core->ScrollOffset());
523533

524534
Log::Comment(L"Jump to the bottom.");
525535
interactivity->UpdateScrollbar(21);
526536
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
527537
Log::Comment(L"Scroll a bit, then emit a line of text. We should reset our internal scroll position.");
528-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5
538+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 1/5
529539
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
530-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 2/5
540+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 2/5
531541
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
532542

533543
conn->WriteInput(winrt_wstring_to_array_view(L"Foo\r\n"));
534544
VERIFY_ARE_EQUAL(22, core->ScrollOffset());
535-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5
545+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 1/5
536546
VERIFY_ARE_EQUAL(22, core->ScrollOffset());
537-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 2/5
547+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 2/5
538548
VERIFY_ARE_EQUAL(22, core->ScrollOffset());
539-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 3/5
549+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 3/5
540550
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
541-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 4/5
551+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 4/5
542552
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
543-
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 5/5
553+
interactivity->MouseWheel(modifiers, false, delta, mousePos, state); // 5/5
544554
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
545555
}
546556

@@ -709,6 +719,7 @@ namespace ControlUnitTests
709719
{
710720
expectedTop--;
711721
interactivity->MouseWheel(modifiers,
722+
false,
712723
WHEEL_DELTA,
713724
Core::Point{ 0, 0 },
714725
noMouseDown);

src/cascadia/WindowsTerminal/AppHost.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ void AppHost::_RaiseVisualBell(const winrt::Windows::Foundation::IInspectable&,
745745
// - delta: the wheel delta that triggered this event.
746746
// Return Value:
747747
// - <none>
748-
void AppHost::_WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const int32_t delta)
748+
void AppHost::_WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const int32_t delta, const bool horizontal)
749749
{
750750
if (_windowLogic)
751751
{
@@ -772,7 +772,7 @@ void AppHost::_WindowMouseWheeled(const winrt::Windows::Foundation::Point coord,
772772
const auto mButtonDown = WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed);
773773
const auto rButtonDown = WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed);
774774

775-
if (control.OnMouseWheel(offsetPoint, delta, lButtonDown, mButtonDown, rButtonDown))
775+
if (control.OnMouseWheel(offsetPoint, horizontal, delta, lButtonDown, mButtonDown, rButtonDown))
776776
{
777777
// If the element handled the mouse wheel event, don't
778778
// continue to iterate over the remaining controls.

src/cascadia/WindowsTerminal/AppHost.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class AppHost : public std::enable_shared_from_this<AppHost>
7373

7474
void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender,
7575
const winrt::Windows::Foundation::IInspectable& arg);
76-
void _WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const int32_t delta);
76+
void _WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const int32_t delta, const bool horizontal);
7777
void _WindowActivated(bool activated);
7878
void _WindowMoved();
7979

src/cascadia/WindowsTerminal/IslandWindow.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam
595595
WindowCloseButtonClicked.raise();
596596
return 0;
597597
}
598+
case WM_MOUSEHWHEEL:
598599
case WM_MOUSEWHEEL:
599600
try
600601
{
@@ -626,7 +627,7 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam
626627
const auto wheelDelta = static_cast<short>(HIWORD(wparam));
627628

628629
// Raise an event, so any listeners can handle the mouse wheel event manually.
629-
MouseScrolled.raise(real, wheelDelta);
630+
MouseScrolled.raise(real, wheelDelta, message == WM_MOUSEHWHEEL);
630631
return 0;
631632
}
632633
CATCH_LOG();

0 commit comments

Comments
 (0)