Path: blob/main/src/vs/editor/browser/view/viewController.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IKeyboardEvent } from '../../../base/browser/keyboardEvent.js';6import { CoreNavigationCommands, NavigationCommandRevealType } from '../coreCommands.js';7import { IEditorMouseEvent, IPartialEditorMouseEvent } from '../editorBrowser.js';8import { ViewUserInputEvents } from './viewUserInputEvents.js';9import { Position } from '../../common/core/position.js';10import { Selection } from '../../common/core/selection.js';11import { IEditorConfiguration } from '../../common/config/editorConfiguration.js';12import { IViewModel } from '../../common/viewModel.js';13import { IMouseWheelEvent } from '../../../base/browser/mouseEvent.js';14import { EditorOption } from '../../common/config/editorOptions.js';15import * as platform from '../../../base/common/platform.js';1617export interface IMouseDispatchData {18position: Position;19/**20* Desired mouse column (e.g. when position.column gets clamped to text length -- clicking after text on a line).21*/22mouseColumn: number;23revealType: NavigationCommandRevealType;24startedOnLineNumbers: boolean;2526inSelectionMode: boolean;27mouseDownCount: number;28altKey: boolean;29ctrlKey: boolean;30metaKey: boolean;31shiftKey: boolean;3233leftButton: boolean;34middleButton: boolean;35onInjectedText: boolean;36}3738export interface ICommandDelegate {39paste(text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void;40type(text: string): void;41compositionType(text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): void;42startComposition(): void;43endComposition(): void;44cut(): void;45}4647export class ViewController {4849private readonly configuration: IEditorConfiguration;50private readonly viewModel: IViewModel;51private readonly userInputEvents: ViewUserInputEvents;52private readonly commandDelegate: ICommandDelegate;5354constructor(55configuration: IEditorConfiguration,56viewModel: IViewModel,57userInputEvents: ViewUserInputEvents,58commandDelegate: ICommandDelegate59) {60this.configuration = configuration;61this.viewModel = viewModel;62this.userInputEvents = userInputEvents;63this.commandDelegate = commandDelegate;64}6566public paste(text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void {67this.commandDelegate.paste(text, pasteOnNewLine, multicursorText, mode);68}6970public type(text: string): void {71this.commandDelegate.type(text);72}7374public compositionType(text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): void {75this.commandDelegate.compositionType(text, replacePrevCharCnt, replaceNextCharCnt, positionDelta);76}7778public compositionStart(): void {79this.commandDelegate.startComposition();80}8182public compositionEnd(): void {83this.commandDelegate.endComposition();84}8586public cut(): void {87this.commandDelegate.cut();88}8990public setSelection(modelSelection: Selection): void {91CoreNavigationCommands.SetSelection.runCoreEditorCommand(this.viewModel, {92source: 'keyboard',93selection: modelSelection94});95}9697private _validateViewColumn(viewPosition: Position): Position {98const minColumn = this.viewModel.getLineMinColumn(viewPosition.lineNumber);99if (viewPosition.column < minColumn) {100return new Position(viewPosition.lineNumber, minColumn);101}102return viewPosition;103}104105private _hasMulticursorModifier(data: IMouseDispatchData): boolean {106switch (this.configuration.options.get(EditorOption.multiCursorModifier)) {107case 'altKey':108return data.altKey;109case 'ctrlKey':110return data.ctrlKey;111case 'metaKey':112return data.metaKey;113default:114return false;115}116}117118private _hasNonMulticursorModifier(data: IMouseDispatchData): boolean {119switch (this.configuration.options.get(EditorOption.multiCursorModifier)) {120case 'altKey':121return data.ctrlKey || data.metaKey;122case 'ctrlKey':123return data.altKey || data.metaKey;124case 'metaKey':125return data.ctrlKey || data.altKey;126default:127return false;128}129}130131public dispatchMouse(data: IMouseDispatchData): void {132const options = this.configuration.options;133const selectionClipboardIsOn = (platform.isLinux && options.get(EditorOption.selectionClipboard));134const columnSelection = options.get(EditorOption.columnSelection);135const scrollOnMiddleClick = options.get(EditorOption.scrollOnMiddleClick);136if (data.middleButton && !selectionClipboardIsOn) {137if (scrollOnMiddleClick) {138// nothing to do here, handled in the contribution139} else {140this._columnSelect(data.position, data.mouseColumn, data.inSelectionMode);141}142} else if (data.startedOnLineNumbers) {143// If the dragging started on the gutter, then have operations work on the entire line144if (this._hasMulticursorModifier(data)) {145if (data.inSelectionMode) {146this._lastCursorLineSelect(data.position, data.revealType);147} else {148this._createCursor(data.position, true);149}150} else {151if (data.inSelectionMode) {152this._lineSelectDrag(data.position, data.revealType);153} else {154this._lineSelect(data.position, data.revealType);155}156}157} else if (data.mouseDownCount >= 4) {158this._selectAll();159} else if (data.mouseDownCount === 3) {160if (this._hasMulticursorModifier(data)) {161if (data.inSelectionMode) {162this._lastCursorLineSelectDrag(data.position, data.revealType);163} else {164this._lastCursorLineSelect(data.position, data.revealType);165}166} else {167if (data.inSelectionMode) {168this._lineSelectDrag(data.position, data.revealType);169} else {170this._lineSelect(data.position, data.revealType);171}172}173} else if (data.mouseDownCount === 2) {174if (!data.onInjectedText) {175if (this._hasMulticursorModifier(data)) {176this._lastCursorWordSelect(data.position, data.revealType);177} else {178if (data.inSelectionMode) {179this._wordSelectDrag(data.position, data.revealType);180} else {181this._wordSelect(data.position, data.revealType);182}183}184}185} else {186if (this._hasMulticursorModifier(data)) {187if (!this._hasNonMulticursorModifier(data)) {188if (data.shiftKey) {189this._columnSelect(data.position, data.mouseColumn, true);190} else {191// Do multi-cursor operations only when purely alt is pressed192if (data.inSelectionMode) {193this._lastCursorMoveToSelect(data.position, data.revealType);194} else {195this._createCursor(data.position, false);196}197}198}199} else {200if (data.inSelectionMode) {201if (data.altKey) {202this._columnSelect(data.position, data.mouseColumn, true);203} else {204if (columnSelection) {205this._columnSelect(data.position, data.mouseColumn, true);206} else {207this._moveToSelect(data.position, data.revealType);208}209}210} else {211this.moveTo(data.position, data.revealType);212}213}214}215}216217private _usualArgs(viewPosition: Position, revealType: NavigationCommandRevealType): CoreNavigationCommands.MoveCommandOptions {218viewPosition = this._validateViewColumn(viewPosition);219return {220source: 'mouse',221position: this._convertViewToModelPosition(viewPosition),222viewPosition,223revealType224};225}226227public moveTo(viewPosition: Position, revealType: NavigationCommandRevealType): void {228CoreNavigationCommands.MoveTo.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));229}230231private _moveToSelect(viewPosition: Position, revealType: NavigationCommandRevealType): void {232CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));233}234235private _columnSelect(viewPosition: Position, mouseColumn: number, doColumnSelect: boolean): void {236viewPosition = this._validateViewColumn(viewPosition);237CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(this.viewModel, {238source: 'mouse',239position: this._convertViewToModelPosition(viewPosition),240viewPosition: viewPosition,241mouseColumn: mouseColumn,242doColumnSelect: doColumnSelect243});244}245246private _createCursor(viewPosition: Position, wholeLine: boolean): void {247viewPosition = this._validateViewColumn(viewPosition);248CoreNavigationCommands.CreateCursor.runCoreEditorCommand(this.viewModel, {249source: 'mouse',250position: this._convertViewToModelPosition(viewPosition),251viewPosition: viewPosition,252wholeLine: wholeLine253});254}255256private _lastCursorMoveToSelect(viewPosition: Position, revealType: NavigationCommandRevealType): void {257CoreNavigationCommands.LastCursorMoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));258}259260private _wordSelect(viewPosition: Position, revealType: NavigationCommandRevealType): void {261CoreNavigationCommands.WordSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));262}263264private _wordSelectDrag(viewPosition: Position, revealType: NavigationCommandRevealType): void {265CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));266}267268private _lastCursorWordSelect(viewPosition: Position, revealType: NavigationCommandRevealType): void {269CoreNavigationCommands.LastCursorWordSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));270}271272private _lineSelect(viewPosition: Position, revealType: NavigationCommandRevealType): void {273CoreNavigationCommands.LineSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));274}275276private _lineSelectDrag(viewPosition: Position, revealType: NavigationCommandRevealType): void {277CoreNavigationCommands.LineSelectDrag.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));278}279280private _lastCursorLineSelect(viewPosition: Position, revealType: NavigationCommandRevealType): void {281CoreNavigationCommands.LastCursorLineSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));282}283284private _lastCursorLineSelectDrag(viewPosition: Position, revealType: NavigationCommandRevealType): void {285CoreNavigationCommands.LastCursorLineSelectDrag.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition, revealType));286}287288private _selectAll(): void {289CoreNavigationCommands.SelectAll.runCoreEditorCommand(this.viewModel, { source: 'mouse' });290}291292// ----------------------293294private _convertViewToModelPosition(viewPosition: Position): Position {295return this.viewModel.coordinatesConverter.convertViewPositionToModelPosition(viewPosition);296}297298public emitKeyDown(e: IKeyboardEvent): void {299this.userInputEvents.emitKeyDown(e);300}301302public emitKeyUp(e: IKeyboardEvent): void {303this.userInputEvents.emitKeyUp(e);304}305306public emitContextMenu(e: IEditorMouseEvent): void {307this.userInputEvents.emitContextMenu(e);308}309310public emitMouseMove(e: IEditorMouseEvent): void {311this.userInputEvents.emitMouseMove(e);312}313314public emitMouseLeave(e: IPartialEditorMouseEvent): void {315this.userInputEvents.emitMouseLeave(e);316}317318public emitMouseUp(e: IEditorMouseEvent): void {319this.userInputEvents.emitMouseUp(e);320}321322public emitMouseDown(e: IEditorMouseEvent): void {323this.userInputEvents.emitMouseDown(e);324}325326public emitMouseDrag(e: IEditorMouseEvent): void {327this.userInputEvents.emitMouseDrag(e);328}329330public emitMouseDrop(e: IPartialEditorMouseEvent): void {331this.userInputEvents.emitMouseDrop(e);332}333334public emitMouseDropCanceled(): void {335this.userInputEvents.emitMouseDropCanceled();336}337338public emitMouseWheel(e: IMouseWheelEvent): void {339this.userInputEvents.emitMouseWheel(e);340}341}342343344