mirror of
https://github.com/Microsoft/vscode
synced 2024-10-02 17:32:41 +00:00
rename: re-use Button class & styling
This commit is contained in:
parent
bd1536407a
commit
9b2e567ee2
|
@ -78,6 +78,9 @@ export class Button extends Disposable implements IButton {
|
|||
private _onDidClick = this._register(new Emitter<Event>());
|
||||
get onDidClick(): BaseEvent<Event> { return this._onDidClick.event; }
|
||||
|
||||
private _onDidEscape = this._register(new Emitter<Event>());
|
||||
get onDidEscape(): BaseEvent<Event> { return this._onDidEscape.event; }
|
||||
|
||||
private focusTracker: IFocusTracker;
|
||||
|
||||
constructor(container: HTMLElement, options: IButtonOptions) {
|
||||
|
@ -134,6 +137,7 @@ export class Button extends Disposable implements IButton {
|
|||
this._onDidClick.fire(e);
|
||||
eventHandled = true;
|
||||
} else if (event.equals(KeyCode.Escape)) {
|
||||
this._onDidEscape.fire(e);
|
||||
this._element.blur();
|
||||
eventHandled = true;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
.monaco-editor .rename-box .rename-input {
|
||||
padding: 3px;
|
||||
border-radius: 2px;
|
||||
width: calc(100% - 8px); /* 4px padding on each side */
|
||||
}
|
||||
|
||||
.monaco-editor .rename-box .rename-label {
|
||||
|
@ -23,15 +24,15 @@
|
|||
opacity: .8;
|
||||
}
|
||||
|
||||
.monaco-editor .rename-box .new-name-candidates-container {
|
||||
margin-top: 4px;
|
||||
.rename-box .new-name-candidates-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.monaco-editor .rename-box .new-name-candidate {
|
||||
/* FIXME@ulugbekna: adapt colors to be nice */
|
||||
background-color: rgb(2, 96, 190);
|
||||
color: white;
|
||||
|
||||
.rename-box .new-name-candidates-container > .monaco-text-button {
|
||||
width: auto;
|
||||
margin: 2px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { assertType } from 'vs/base/common/types';
|
||||
import 'vs/css!./renameInputField';
|
||||
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
|
@ -16,6 +18,7 @@ import { ScrollType } from 'vs/editor/common/editorCommon';
|
|||
import { localize } from 'vs/nls';
|
||||
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
|
||||
import { editorWidgetBackground, inputBackground, inputBorder, inputForeground, widgetBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
|
@ -79,10 +82,11 @@ export class RenameInputField implements IContentWidget {
|
|||
this._input.setAttribute('aria-label', localize('renameAriaLabel', "Rename input. Type new name and press Enter to commit."));
|
||||
this._domNode.appendChild(this._input);
|
||||
|
||||
// TODO@ulugbekna: add keyboard support for cycling through the candidates
|
||||
// TODO@ulugbekna: support accept/escape corresponding to the keybindings
|
||||
this._newNameCandidates = new NewSymbolNameCandidates();
|
||||
this._newNameCandidates.onAccept(() => this.acceptInput(false)); // FIXME@ulugbekna: need to handle preview
|
||||
this._newNameCandidates.onEscape(() => this._input!.focus());
|
||||
this._domNode.appendChild(this._newNameCandidates!.domNode);
|
||||
this._disposables.add(this._newNameCandidates);
|
||||
|
||||
this._label = document.createElement('div');
|
||||
this._label.className = 'rename-label';
|
||||
|
@ -144,6 +148,10 @@ export class RenameInputField implements IContentWidget {
|
|||
beforeRender(): IDimension | null {
|
||||
const [accept, preview] = this._acceptKeybindings;
|
||||
this._label!.innerText = localize({ key: 'label', comment: ['placeholders are keybindings, e.g "F2 to Rename, Shift+F2 to Preview"'] }, "{0} to Rename, {1} to Preview", this._keybindingService.lookupKeybinding(accept)?.getLabel(), this._keybindingService.lookupKeybinding(preview)?.getLabel());
|
||||
// TODO@ulugbekna: elements larger than maxWidth shouldn't overflow
|
||||
const maxWidth = Math.ceil(this._editor.getLayoutInfo().contentWidth / 4);
|
||||
this._domNode!.style.maxWidth = `${maxWidth}px`;
|
||||
this._domNode!.style.minWidth = `250px`; // to prevent from widening when candidates come in
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -179,7 +187,9 @@ export class RenameInputField implements IContentWidget {
|
|||
const disposeOnDone = new DisposableStore();
|
||||
|
||||
newNameCandidates.then(candidates => {
|
||||
this._newNameCandidates!.setCandidates(candidates);
|
||||
if (!token.isCancellationRequested) { // TODO@ulugbekna: make sure this's the correct token to check
|
||||
this._newNameCandidates!.setCandidates(candidates);
|
||||
}
|
||||
});
|
||||
|
||||
return new Promise<RenameInputFieldResult | boolean>(resolve => {
|
||||
|
@ -247,44 +257,49 @@ export class RenameInputField implements IContentWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class NewSymbolNameCandidates implements IDisposable {
|
||||
export class NewSymbolNameCandidates {
|
||||
|
||||
public readonly domNode: HTMLDivElement;
|
||||
public readonly domNode: HTMLElement;
|
||||
|
||||
private _candidates: HTMLSpanElement[] = [];
|
||||
private _disposables = new DisposableStore();
|
||||
private _onAcceptEmitter = new Emitter<string>();
|
||||
public readonly onAccept = this._onAcceptEmitter.event;
|
||||
private _onEscapeEmitter = new Emitter<void>();
|
||||
public readonly onEscape = this._onEscapeEmitter.event;
|
||||
|
||||
private _candidates: Button[] = [];
|
||||
|
||||
private _candidateDisposables: DisposableStore | undefined;
|
||||
|
||||
// TODO@ulugbekna: pressing escape when focus is on a candidate should return the focus to the input field
|
||||
constructor() {
|
||||
this.domNode = document.createElement('div');
|
||||
this.domNode.className = 'new-name-candidates-container';
|
||||
this.domNode.className = 'rename-box new-name-candidates-container';
|
||||
this.domNode.tabIndex = -1; // Make the div unfocusable
|
||||
}
|
||||
|
||||
get selectedCandidate(): string | undefined {
|
||||
const activeDocument = dom.getActiveDocument();
|
||||
const activeElement = activeDocument.activeElement;
|
||||
const index = this._candidates.indexOf(activeElement as HTMLSpanElement);
|
||||
return index !== -1 ? this._candidates[index].innerText : undefined;
|
||||
const selected = this._candidates.find(c => c.hasFocus());
|
||||
return selected === undefined ? undefined : (
|
||||
assertType(typeof selected.label === 'string', 'string'),
|
||||
selected.label
|
||||
);
|
||||
}
|
||||
|
||||
setCandidates(candidates: string[]): void {
|
||||
this._candidateDisposables = new DisposableStore();
|
||||
for (let i = 0; i < candidates.length; i++) {
|
||||
const candidate = candidates[i];
|
||||
const candidateElt = document.createElement('span');
|
||||
candidateElt.className = 'new-name-candidate';
|
||||
candidateElt.innerText = candidate;
|
||||
candidateElt.tabIndex = 0;
|
||||
this.domNode.appendChild(candidateElt);
|
||||
const candidateElt = new Button(this.domNode, defaultButtonStyles);
|
||||
this._candidateDisposables.add(candidateElt.onDidClick(() => this._onAcceptEmitter.fire(candidate)));
|
||||
this._candidateDisposables.add(candidateElt.onDidEscape(() => this._onEscapeEmitter.fire()));
|
||||
candidateElt.label = candidate;
|
||||
this._candidates.push(candidateElt);
|
||||
}
|
||||
}
|
||||
|
||||
clearCandidates(): void {
|
||||
this.domNode.innerText = ''; // TODO@ulugbekna: make sure this is the right way to clean up children
|
||||
this._candidateDisposables?.dispose();
|
||||
this.domNode.innerText = '';
|
||||
this._candidates = [];
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue