Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
18 changes: 18 additions & 0 deletions src/vs/platform/quickinput/browser/media/quickInput.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@
border-radius: 8px;
}

/* Resize handles */
.quick-input-widget-resize {
position: absolute;
top: 0;
bottom: 0;
width: 6px;
cursor: ew-resize;
z-index: 1;
}

.quick-input-widget-resize.left {
left: -3px;
}

.quick-input-widget-resize.right {
right: -3px;
}

.quick-input-titlebar {
cursor: grab;
display: flex;
Expand Down
125 changes: 123 additions & 2 deletions src/vs/platform/quickinput/browser/quickInputController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const VIEWSTATE_STORAGE_KEY = 'workbench.quickInput.viewState';
type QuickInputViewState = {
readonly top?: number;
readonly left?: number;
readonly width?: number; // custom width in pixels
};

export class QuickInputController extends Disposable {
Expand Down Expand Up @@ -80,6 +81,7 @@ export class QuickInputController extends Disposable {

private viewState: QuickInputViewState | undefined;
private dndController: QuickInputDragAndDropController | undefined;
private resizeController: QuickInputResizeController | undefined;

private readonly inQuickInputContext: IContextKey<boolean>;
private readonly quickInputTypeContext: IContextKey<QuickInputType>;
Expand Down Expand Up @@ -400,6 +402,33 @@ export class QuickInputController extends Disposable {
}
}));

// Resize support
this.resizeController = this._register(new QuickInputResizeController(
this._container,
container,
this.viewState?.width
));

// Resize update layout
this._register(autorun(reader => {
const resizeState = this.resizeController?.resizeViewState.read(reader);
if (!resizeState) {
return;
}

this.viewState = {
...this.viewState,
width: resizeState.width,
};
Comment thread
ipanasenko marked this conversation as resolved.

this.updateLayout();

// Save width and position
if (resizeState.done) {
this.saveViewState(this.viewState);
}
}));

this.ui = {
container,
styleSheet,
Expand Down Expand Up @@ -862,10 +891,18 @@ export class QuickInputController extends Disposable {
this.updateLayout();
}

private getDefaultWidth(): number {
return Math.min(this.dimension!.width * 0.62 /* golden cut */, QuickInputController.MAX_WIDTH);
}

private updateLayout() {
if (this.ui && this.isVisible()) {
const style = this.ui.container.style;
let width = Math.min(this.dimension!.width * 0.62 /* golden cut */, QuickInputController.MAX_WIDTH);
const defaultWidth = this.getDefaultWidth();
const customWidth = this.viewState?.width;
let width = customWidth !== undefined
? Math.max(QuickInputResizeController.MIN_WIDTH, Math.min(customWidth, this.dimension!.width - 20))
: defaultWidth;
style.width = width + 'px';

let listHeight = this.dimension && this.dimension.height * 0.4;
Expand Down Expand Up @@ -978,7 +1015,7 @@ export class QuickInputController extends Disposable {
private loadViewState(): QuickInputViewState | undefined {
try {
const data = JSON.parse(this.storageService.get(VIEWSTATE_STORAGE_KEY, StorageScope.APPLICATION, '{}'));
if (data.top !== undefined || data.left !== undefined) {
if (data.top !== undefined || data.left !== undefined || data.width !== undefined) {
return data;
}
} catch { }
Expand Down Expand Up @@ -1211,3 +1248,87 @@ class QuickInputDragAndDropController extends Disposable {
return Math.round(this._container.clientWidth / 2) - Math.round(this._quickInputContainer.clientWidth / 2);
}
}

class QuickInputResizeController extends Disposable {
static readonly MIN_WIDTH = 300;
static readonly MAX_WIDTH = 900;

readonly resizeViewState = observableValue<{ width?: number; done: boolean } | undefined>(this, undefined);

private readonly _leftHandle: HTMLElement;
private readonly _rightHandle: HTMLElement;

constructor(
private _container: HTMLElement,
private readonly _quickInputContainer: HTMLElement,
initialWidth: number | undefined
) {
super();

// Create resize handles
this._leftHandle = dom.append(this._quickInputContainer, $('.quick-input-widget-resize.left'));
this._rightHandle = dom.append(this._quickInputContainer, $('.quick-input-widget-resize.right'));

this._registerMouseListeners();

if (initialWidth !== undefined) {
this.resizeViewState.set({ width: initialWidth, done: true }, undefined);
}
}

private _registerMouseListeners(): void {
// Left handle
this._registerHandleListeners(this._leftHandle, 'left');

// Right handle
this._registerHandleListeners(this._rightHandle, 'right');

}

private _registerHandleListeners(handle: HTMLElement, side: 'left' | 'right'): void {
this._register(dom.addDisposableGenericMouseDownListener(handle, (e: MouseEvent) => {
const activeWindow = dom.getWindow(this._container);
const originEvent = new StandardMouseEvent(activeWindow, e);
originEvent.preventDefault();
originEvent.stopPropagation();

const startX = originEvent.browserEvent.clientX;
const startWidth = this._quickInputContainer.getBoundingClientRect().width;
const containerWidth = this._container.clientWidth;

let isResizing = false;
const mouseMoveListener = dom.addDisposableGenericMouseMoveListener(activeWindow, (e: MouseEvent) => {
const mouseMoveEvent = new StandardMouseEvent(activeWindow, e);
mouseMoveEvent.preventDefault();

if (!isResizing) {
isResizing = true;
}

const deltaX = mouseMoveEvent.browserEvent.clientX - startX;
let newWidth: number;

if (side === 'right') {
newWidth = startWidth + deltaX * 2;
} else {
newWidth = startWidth - deltaX * 2;
}

newWidth = Math.max(QuickInputResizeController.MIN_WIDTH, Math.min(newWidth, QuickInputResizeController.MAX_WIDTH));
newWidth = Math.min(newWidth, containerWidth - 20);

this.resizeViewState.set({ width: newWidth, done: false }, undefined);
});

const mouseUpListener = dom.addDisposableGenericMouseUpListener(activeWindow, (e: MouseEvent) => {
if (isResizing) {
const state = this.resizeViewState.get();
this.resizeViewState.set({ width: state?.width, done: true }, undefined);
}

mouseMoveListener.dispose();
mouseUpListener.dispose();
});
}));
}
}