Fire onSelectionChange when starting to drag

This commit is contained in:
Daniel Imms
2026-01-08 16:51:45 -08:00
parent 8e4de23c11
commit 613aa6ba23
3 changed files with 69 additions and 0 deletions
@@ -496,5 +496,19 @@ describe('SelectionService', () => {
assert.isFalse(selectionService.areCoordsInSelection([2, 1], [2, 0], [2, 1]));
});
});
describe('onSelectionChange', () => {
it('should not fire when setting model properties directly', () => {
buffer.lines.set(0, stringToRow('foo bar'));
let eventFired = false;
selectionService.onSelectionChange(() => { eventFired = true; });
selectionService.model.selectionStart = [0, 0];
selectionService.model.selectionEnd = undefined;
assert.isFalse(eventFired);
});
});
});
+8
View File
@@ -532,6 +532,9 @@ export class SelectionService extends Disposable implements ISelectionService {
* @param event The mouse event.
*/
private _handleSingleClick(event: MouseEvent): void {
// Track if there was a selection before clearing
const hadSelection = this.hasSelection;
this._model.selectionStartLength = 0;
this._model.isSelectAllActive = false;
this._activeSelectionMode = this.shouldColumnSelect(event) ? SelectionMode.COLUMN : SelectionMode.NORMAL;
@@ -543,6 +546,11 @@ export class SelectionService extends Disposable implements ISelectionService {
}
this._model.selectionEnd = undefined;
// Fire selection change event if a selection was cleared
if (hadSelection) {
this._fireOnSelectionChange(this._model.finalSelectionStart, this._model.finalSelectionEnd, false);
}
// Ensure the line exists
const line = this._bufferService.buffer.lines.get(this._model.selectionStart[1]);
if (!line) {
+47
View File
@@ -374,6 +374,53 @@ test.describe('API Integration Tests', () => {
await pollFor(ctx.page, `window.callCount`, 2);
});
test('onSelectionChange should fire on mousedown when clearing selection', async () => {
await openTerminal(ctx);
await ctx.proxy.write('foo bar baz');
await ctx.page.evaluate(`
window.callCount = 0;
window.term.onSelectionChange(() => window.callCount++);
`);
await ctx.page.evaluate(`window.term.selectAll()`);
await pollFor(ctx.page, `window.callCount`, 1);
const termCoords: any = await ctx.page.evaluate(`
(function() {
const rect = window.term.element.getBoundingClientRect();
const dim = window.term.dimensions;
return { left: rect.left, top: rect.top, cellWidth: dim.css.cell.width, cellHeight: dim.css.cell.height };
})();
`);
const x = termCoords.left + termCoords.cellWidth * 5;
const y = termCoords.top + termCoords.cellHeight * 0.5;
await ctx.page.mouse.click(x, y);
await pollFor(ctx.page, `window.callCount`, 2);
});
test('onSelectionChange should not fire on mousedown when no prior selection', async () => {
await openTerminal(ctx);
await ctx.proxy.write('foo bar baz');
await ctx.page.evaluate(`
window.callCount = 0;
window.term.onSelectionChange(() => window.callCount++);
`);
await pollFor(ctx.page, `window.callCount`, 0);
const termCoords: any = await ctx.page.evaluate(`
(function() {
const rect = window.term.element.getBoundingClientRect();
const dim = window.term.dimensions;
return { left: rect.left, top: rect.top, cellWidth: dim.css.cell.width, cellHeight: dim.css.cell.height };
})();
`);
const x = termCoords.left + termCoords.cellWidth * 5;
const y = termCoords.top + termCoords.cellHeight * 0.5;
await ctx.page.mouse.click(x, y);
await pollFor(ctx.page, `window.callCount`, 0);
});
test('onRender', async () => {
await openTerminal(ctx);
await timeout(20); // Ensure all init events are fired