Handle mouseEventsRequireAlt option changes in mouse mode

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-05-27 21:59:23 +00:00
co-authored by Daniel Imms
parent f8cef535c7
commit 7711d780c2
3 changed files with 100 additions and 8 deletions
+1
View File
@@ -623,6 +623,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
this.element.classList.add('enable-mouse-events');
} else {
this._selectionService.enable();
this.element.classList.remove('enable-mouse-events');
}
if (this.options.screenReaderMode) {
+80
View File
@@ -7,6 +7,7 @@ import { MouseService } from 'browser/services/MouseService';
import { MouseStateService } from 'common/services/MouseStateService';
import { CoreMouseAction, CoreMouseButton } from 'common/Types';
import { IBufferService, ICoreService, ILogService, IOptionsService } from 'common/services/Services';
import { OptionsService } from 'common/services/OptionsService';
import { MockCoreBrowserService, MockRenderService, MockSelectionService } from 'browser/TestUtils.test';
function toBytes(s: string | undefined): number[] {
@@ -42,6 +43,36 @@ const logService: ILogService = {
error: () => {}
} as any;
class TestSelectionService extends MockSelectionService {
public enableCount = 0;
public disableCount = 0;
public override enable(): void {
this.enableCount++;
}
public override disable(): void {
this.disableCount++;
}
public override shouldForceSelection(): boolean {
return false;
}
}
function createTestMouseTargetElement(): HTMLElement {
const classes = new Set<string>();
return {
classList: {
add: (className: string) => classes.add(className),
remove: (className: string) => classes.delete(className),
contains: (className: string) => classes.has(className)
},
addEventListener: () => {},
removeEventListener: () => {}
} as any;
}
describe('MouseService _triggerMouseEvent', () => {
let mouseService: MouseService;
let mouseStateService: MouseStateService;
@@ -211,3 +242,52 @@ describe('MouseService _triggerMouseEvent', () => {
reports = [];
});
});
describe('MouseService mouseEventsRequireAlt', () => {
it('should update selection state and cursor class when toggled while mouse events are active', () => {
const mouseStateService = new MouseStateService();
const optionsService = new OptionsService({});
const selectionService = new TestSelectionService();
const mouseService = new MouseService(
new MockRenderService(),
{
getMouseReportCoords: (_ev: MouseEvent, _el: HTMLElement) => ({ col: 0, row: 0, x: 0, y: 0 })
} as any,
mouseStateService,
{
triggerDataEvent: () => {},
triggerBinaryEvent: () => {},
decPrivateModes: { applicationCursorKeys: false }
} as any,
bufferService,
optionsService,
selectionService,
logService,
new MockCoreBrowserService()
);
const element = createTestMouseTargetElement();
const screenElement = createTestMouseTargetElement();
const document = {
addEventListener: () => {},
removeEventListener: () => {}
} as any;
mouseService.bindMouse({
element,
screenElement,
document
}, disposable => disposable, () => {});
mouseStateService.activeProtocol = 'ANY';
assert.isTrue(element.classList.contains('enable-mouse-events'));
assert.equal(selectionService.disableCount, 1);
optionsService.options.mouseEventsRequireAlt = true;
assert.isFalse(element.classList.contains('enable-mouse-events'));
assert.equal(selectionService.enableCount, 2);
optionsService.options.mouseEventsRequireAlt = false;
assert.isTrue(element.classList.contains('enable-mouse-events'));
assert.equal(selectionService.disableCount, 2);
});
});
+19 -8
View File
@@ -66,6 +66,9 @@ export class MouseService implements IMouseService {
register(this._mouseStateService.onProtocolChange(events => {
this._handleProtocolChange(ctx, eventListeners, events);
}));
register(this._optionsService.onSpecificOptionChange('mouseEventsRequireAlt', () => {
this._syncMouseModeState(element);
}));
// force initial onProtocolChange so we dont miss early mouse requests
this._mouseStateService.activeProtocol = this._mouseStateService.activeProtocol;
@@ -360,6 +363,21 @@ export class MouseService implements IMouseService {
this._touchScrollAccumulator = 0;
}
private _syncMouseModeState(element: HTMLElement): void {
if (this._mouseStateService.areMouseEventsActive) {
if (this._optionsService.rawOptions.mouseEventsRequireAlt) {
element.classList.remove('enable-mouse-events');
this._selectionService.enable();
} else {
element.classList.add('enable-mouse-events');
this._selectionService.disable();
}
} else {
element.classList.remove('enable-mouse-events');
this._selectionService.enable();
}
}
private _handleProtocolChange(ctx: IMouseBindContext, eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener>, events: CoreMouseEventType): void {
const { element, document } = ctx.target;
const { requestedEvents } = ctx;
@@ -368,17 +386,10 @@ export class MouseService implements IMouseService {
if (this._optionsService.rawOptions.logLevel === 'debug') {
this._logService.debug('Binding to mouse events:', this._explainEvents(events));
}
if (this._optionsService.rawOptions.mouseEventsRequireAlt) {
this._selectionService.enable();
} else {
element.classList.add('enable-mouse-events');
this._selectionService.disable();
}
} else {
this._logService.debug('Unbinding from mouse events.');
element.classList.remove('enable-mouse-events');
this._selectionService.enable();
}
this._syncMouseModeState(element);
// add/remove handlers from requestedEvents
if (!(events & CoreMouseEventType.MOVE)) {