From e6152cc29f6bc2bc82fe53bf2893fe058b5eb38e Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 17 May 2019 16:34:47 -0700 Subject: [PATCH] icons for menu bar --- src/vs/editor/common/modes.ts | 1 + .../workbench/api/common/extHostComments.ts | 53 +++++++++++--- .../contrib/comments/browser/commentMenus.ts | 12 ++-- .../contrib/comments/browser/commentNode.ts | 70 ++++++++++++------- .../comments/browser/commentThreadWidget.ts | 57 +++++++++++++-- .../browser/commentsEditorContribution.ts | 2 +- .../contrib/comments/browser/media/review.css | 10 +++ 7 files changed, 155 insertions(+), 50 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index a71c1680e74..d6ec3a832a8 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1360,6 +1360,7 @@ export enum CommentMode { */ export interface Comment { readonly commentId: string; + readonly uniqueIdInThread?: number; readonly body: IMarkdownString; readonly userName: string; readonly userIconPath?: string; diff --git a/src/vs/workbench/api/common/extHostComments.ts b/src/vs/workbench/api/common/extHostComments.ts index 6b07a0eab22..3182fb118d0 100644 --- a/src/vs/workbench/api/common/extHostComments.ts +++ b/src/vs/workbench/api/common/extHostComments.ts @@ -69,6 +69,23 @@ export class ExtHostComments implements ExtHostCommentsShape { } return commentThread; + } else if (arg && arg.$mid === 8) { + const commentController = this._commentControllers.get(arg.thread.commentControlHandle); + + if (!commentController) { + return arg; + } + + const commentThread = commentController.getCommentThread(arg.thread.commentThreadHandle); + + if (!commentThread) { + return arg; + } + + return { + thread: commentThread, + text: arg.text + }; } return arg; @@ -190,7 +207,7 @@ export class ExtHostComments implements ExtHostCommentsShape { const commentController = this._commentControllers.get(commentControllerHandle); if (!commentController) { - return Promise.resolve(false); + return Promise.resolve(false); } if (!(commentController as any).emptyCommentThreadFactory) { @@ -396,12 +413,14 @@ export class ExtHostComments implements ExtHostCommentsShape { export class ExtHostCommentThread implements vscode.CommentThread { private static _handlePool: number = 0; readonly handle = ExtHostCommentThread._handlePool++; + public commentHandle: number = 0; + get threadId(): string { - return this._id; + return this._id!; } get id(): string { - return this._id; + return this._id!; } get resource(): vscode.Uri { @@ -495,15 +514,21 @@ export class ExtHostCommentThread implements vscode.CommentThread { return this._isDiposed; } + private _commentsMap: Map = new Map(); + constructor( private _proxy: MainThreadCommentsShape, private readonly _commandsConverter: CommandsConverter, private _commentController: ExtHostCommentController, - private _id: string, + private _id: string | undefined, private _uri: vscode.Uri, private _range: vscode.Range, private _comments: vscode.Comment[] ) { + if (this._id === undefined) { + this._id = `${_commentController.id}.${this.handle}`; + } + this._proxy.$createCommentThread( this._commentController.handle, this.handle, @@ -527,7 +552,7 @@ export class ExtHostCommentThread implements vscode.CommentThread { eventuallyUpdateCommentThread(): void { const commentThreadRange = extHostTypeConverter.Range.from(this._range); const label = this.label; - const comments = this._comments.map(cmt => { return convertToModeComment(this._commentController, cmt, this._commandsConverter); }); + const comments = this._comments.map(cmt => { return convertToModeComment2(this, this._commentController, cmt, this._commandsConverter, this._commentsMap); }); const acceptInputCommand = this._acceptInputCommand ? this._commandsConverter.toInternal(this._acceptInputCommand) : undefined; const additionalCommands = this._additionalCommands ? this._additionalCommands.map(x => this._commandsConverter.toInternal(x)) : []; const deleteCommand = this._deleteCommand ? this._commandsConverter.toInternal(this._deleteCommand) : undefined; @@ -536,7 +561,7 @@ export class ExtHostCommentThread implements vscode.CommentThread { this._proxy.$updateCommentThread( this._commentController.handle, this.handle, - this._id, + this._id!, this._uri, commentThreadRange, label, @@ -670,14 +695,14 @@ class ExtHostCommentController implements vscode.CommentController { this._threads.set(commentThread.handle, commentThread); return commentThread; } else { - const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, '', arg0 as vscode.Uri, arg1 as vscode.Range, arg2 as vscode.Comment[]); + const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, undefined, arg0 as vscode.Uri, arg1 as vscode.Range, arg2 as vscode.Comment[]); this._threads.set(commentThread.handle, commentThread); return commentThread; } } $createCommentThreadTemplate(uriComponents: UriComponents, range: IRange) { - const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, '', URI.revive(uriComponents), extHostTypeConverter.Range.to(range), []); + const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, undefined, URI.revive(uriComponents), extHostTypeConverter.Range.to(range), []); commentThread.collapsibleState = modes.CommentThreadCollapsibleState.Expanded; this._threads.set(commentThread.handle, commentThread); return commentThread; @@ -775,12 +800,18 @@ function convertFromComment(comment: modes.Comment): vscode.Comment { }; } -function convertToModeComment(commentController: ExtHostCommentController, vscodeComment: vscode.Comment, commandsConverter: CommandsConverter): modes.Comment { - const iconPath = vscodeComment.author && vscodeComment.author.iconPath ? vscodeComment.author.iconPath.toString() : - (vscodeComment.userIconPath ? vscodeComment.userIconPath.toString() : vscodeComment.gravatar); +function convertToModeComment2(thread: ExtHostCommentThread, commentController: ExtHostCommentController, vscodeComment: vscode.Comment, commandsConverter: CommandsConverter, commentsMap: Map): modes.Comment { + let commentUniqueId = commentsMap.get(vscodeComment)!; + if (!commentUniqueId) { + commentUniqueId = ++thread.commentHandle; + commentsMap.set(vscodeComment, commentUniqueId); + } + + const iconPath = vscodeComment.author && vscodeComment.author.iconPath ? vscodeComment.author.iconPath.toString() : undefined; return { commentId: vscodeComment.id || vscodeComment.commentId, + uniqueIdInThread: commentUniqueId, body: extHostTypeConverter.MarkdownString.from(vscodeComment.body), userName: vscodeComment.author ? vscodeComment.author.name : vscodeComment.userName, userIconPath: iconPath, diff --git a/src/vs/workbench/contrib/comments/browser/commentMenus.ts b/src/vs/workbench/contrib/comments/browser/commentMenus.ts index 4e57d92e625..bec3348343d 100644 --- a/src/vs/workbench/contrib/comments/browser/commentMenus.ts +++ b/src/vs/workbench/contrib/comments/browser/commentMenus.ts @@ -30,25 +30,23 @@ export class CommentMenus implements IDisposable { } getCommentThreadTitleActions(commentThread: CommentThread2): IAction[] { - return this.getActions(MenuId.CommentThreadTitle, commentThread).primary; + return this.getActions(MenuId.CommentThreadTitle).primary; } getCommentThreadActions(commentThread: CommentThread2): IAction[] { - return []; + return this.getActions(MenuId.CommentThreadActions).primary; } getCommentTitleActions(comment: Comment): IAction[] { - return []; + return this.getActions(MenuId.CommentTitle).primary; } getCommentActions(comment: Comment): IAction[] { - return []; + return this.getActions(MenuId.CommentActions).primary; } - - private getActions(menuId: MenuId, thread: CommentThread2) { + private getActions(menuId: MenuId) { const contextKeyService = this.contextKeyService.createScoped(); - // contextKeyService.createKey('commentThread', thread.threadId); const menu = this.menuService.createMenu(menuId, contextKeyService); const primary: IAction[] = []; diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 8b4b7bbfe0a..1e5b13762f7 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -8,7 +8,7 @@ import * as dom from 'vs/base/browser/dom'; import * as modes from 'vs/editor/common/modes'; import { ActionsOrientation, ActionViewItem, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button } from 'vs/base/browser/ui/button/button'; -import { Action, IActionRunner } from 'vs/base/common/actions'; +import { Action, IActionRunner, IAction } from 'vs/base/common/actions'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ITextModel } from 'vs/editor/common/model'; @@ -35,6 +35,9 @@ import { ToggleReactionsAction, ReactionAction, ReactionActionViewItem } from '. import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget'; +import { MenuItemAction } from 'vs/platform/actions/common/actions'; +import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; const UPDATE_COMMENT_LABEL = nls.localize('label.updateComment', "Update comment"); const UPDATE_IN_PROGRESS_LABEL = nls.localize('label.updatingComment', "Updating comment..."); @@ -85,6 +88,7 @@ export class CommentNode extends Disposable { @IModelService private modelService: IModelService, @IModeService private modeService: IModeService, @IDialogService private dialogService: IDialogService, + @IKeybindingService private keybindingService: IKeybindingService, @INotificationService private notificationService: INotificationService, @IContextMenuService private contextMenuService: IContextMenuService ) { @@ -139,7 +143,7 @@ export class CommentNode extends Disposable { } private createActionsToolbar() { - const actions: Action[] = []; + const actions: IAction[] = []; let reactionGroup = this.commentService.getReactionGroup(this.owner); if (reactionGroup && reactionGroup.length) { @@ -163,6 +167,10 @@ export class CommentNode extends Disposable { actions.push(this._deleteAction); } + let commentMenus = this.commentService.getCommentMenus(this.owner); + let titleActions = commentMenus.getCommentTitleActions(this.comment); + actions.push(...titleActions); + if (actions.length) { this.toolbar = new ToolBar(this._actionsToolbarContainer, this.contextMenuService, { actionViewItemProvider: action => { @@ -187,6 +195,7 @@ export class CommentNode extends Disposable { this.registerActionBarListeners(this._actionsToolbarContainer); this.toolbar.setActions(actions, [])(); + this.toolbar.context = this.comment; this._toDispose.push(this.toolbar); } } @@ -196,12 +205,15 @@ export class CommentNode extends Disposable { if (action.id === 'comment.delete' || action.id === 'comment.edit' || action.id === ToggleReactionsAction.ID) { options = { label: false, icon: true }; } else { - options = { label: true, icon: true }; + options = { label: false, icon: true }; } if (action.id === ReactionAction.ID) { let item = new ReactionActionViewItem(action); return item; + } else if (action instanceof MenuItemAction) { + let item = new MenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + return item; } else { let item = new ActionViewItem({}, action, options); return item; @@ -514,37 +526,41 @@ export class CommentNode extends Disposable { private createEditAction(commentDetailsContainer: HTMLElement): Action { return new Action('comment.edit', nls.localize('label.edit', "Edit"), 'octicon octicon-pencil', true, () => { - this.isEditing = true; - this._body.classList.add('hidden'); - this._commentEditContainer = dom.append(commentDetailsContainer, dom.$('.edit-container')); - this.createCommentEditor(); + return this.editCommentAction(commentDetailsContainer); + }); + } - this._errorEditingContainer = dom.append(this._commentEditContainer, dom.$('.validation-error.hidden')); - const formActions = dom.append(this._commentEditContainer, dom.$('.form-actions')); + private editCommentAction(commentDetailsContainer: HTMLElement) { + this.isEditing = true; + this._body.classList.add('hidden'); + this._commentEditContainer = dom.append(commentDetailsContainer, dom.$('.edit-container')); + this.createCommentEditor(); - const cancelEditButton = new Button(formActions); - cancelEditButton.label = nls.localize('label.cancel', "Cancel"); - this._toDispose.push(attachButtonStyler(cancelEditButton, this.themeService)); + this._errorEditingContainer = dom.append(this._commentEditContainer, dom.$('.validation-error.hidden')); + const formActions = dom.append(this._commentEditContainer, dom.$('.form-actions')); - this._toDispose.push(cancelEditButton.onDidClick(_ => { - this.removeCommentEditor(); - })); + const cancelEditButton = new Button(formActions); + cancelEditButton.label = nls.localize('label.cancel', "Cancel"); + this._toDispose.push(attachButtonStyler(cancelEditButton, this.themeService)); - this._updateCommentButton = new Button(formActions); - this._updateCommentButton.label = UPDATE_COMMENT_LABEL; - this._toDispose.push(attachButtonStyler(this._updateCommentButton, this.themeService)); + this._toDispose.push(cancelEditButton.onDidClick(_ => { + this.removeCommentEditor(); + })); - this._toDispose.push(this._updateCommentButton.onDidClick(_ => { - this.editComment(); - })); + this._updateCommentButton = new Button(formActions); + this._updateCommentButton.label = UPDATE_COMMENT_LABEL; + this._toDispose.push(attachButtonStyler(this._updateCommentButton, this.themeService)); - this._commentEditorDisposables.push(this._commentEditor!.onDidChangeModelContent(_ => { - this._updateCommentButton.enabled = !!this._commentEditor!.getValue(); - })); + this._toDispose.push(this._updateCommentButton.onDidClick(_ => { + this.editComment(); + })); - this._editAction.enabled = false; - return Promise.resolve(); - }); + this._commentEditorDisposables.push(this._commentEditor!.onDidChangeModelContent(_ => { + this._updateCommentButton.enabled = !!this._commentEditor!.getValue(); + })); + + this._editAction.enabled = false; + return Promise.resolve(); } private registerActionBarListeners(actionsContainer: HTMLElement): void { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 595b9c1a4b6..06fce1d7ac8 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar, ActionViewItem, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button } from 'vs/base/browser/ui/button/button'; import { Action, IAction } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; @@ -39,9 +39,14 @@ import { generateUuid } from 'vs/base/common/uuid'; import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget'; import { withNullAsUndefined } from 'vs/base/common/types'; import { CommentMenus } from 'vs/workbench/contrib/comments/browser/commentMenus'; +import { MenuItemAction } from 'vs/platform/actions/common/actions'; +import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration'; -const COLLAPSE_ACTION_CLASS = 'expand-review-action octicon octicon-x'; +const COLLAPSE_ACTION_CLASS = 'expand-review-action octicon octicon-chevron-up'; const COMMENT_SCHEME = 'comment'; @@ -101,7 +106,10 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget @IModelService private modelService: IModelService, @IThemeService private themeService: IThemeService, @ICommentService private commentService: ICommentService, - @IOpenerService private openerService: IOpenerService + @IOpenerService private openerService: IOpenerService, + @IKeybindingService private keybindingService: IKeybindingService, + @INotificationService private notificationService: INotificationService, + @IContextMenuService private contextMenuService: IContextMenuService ) { super(editor, { keepEditorSelection: true }); this._resizeObserver = null; @@ -202,7 +210,18 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this.createThreadLabel(); const actionsContainer = dom.append(this._headElement, dom.$('.review-actions')); - this._actionbarWidget = new ActionBar(actionsContainer, {}); + this._actionbarWidget = new ActionBar(actionsContainer, { + actionViewItemProvider: (action: IAction) => { + if (action instanceof MenuItemAction) { + let item = new MenuEntryActionViewItem(action, this.keybindingService, this.notificationService, this.contextMenuService); + return item; + } else { + let item = new ActionViewItem({}, action, { label: false, icon: true }); + return item; + } + } + }); + this._disposables.push(this._actionbarWidget); this._collapseAction = new Action('review.expand', nls.localize('label.collapse', "Collapse"), COLLAPSE_ACTION_CLASS, true, () => this.collapse()); @@ -698,6 +717,25 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget })); }); } + + let actions = this._commentMenus.getCommentThreadActions(commentThread); + + actions.forEach(action => { + const button = new Button(container); + this._disposables.push(attachButtonStyler(button, this.themeService)); + + button.label = action.label; + + this._disposables.push(button.onDidClick(async () => { + action.run({ + thread: this._commentThread, + text: this._commentEditor.getValue(), + $mid: 8 + }); + + this.hideReplyArea(); + })); + }); } private createNewCommentNode(comment: modes.Comment): CommentNode { @@ -849,6 +887,17 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } } + private hideReplyArea() { + this._commentEditor.setValue(''); + this._pendingComment = ''; + if (dom.hasClass(this._commentForm, 'expand')) { + dom.removeClass(this._commentForm, 'expand'); + } + this._commentEditor.getDomNode()!.style.outline = ''; + this._error.textContent = ''; + dom.addClass(this._error, 'hidden'); + } + private createReplyButton() { this._reviewThreadReplyButton = dom.append(this._commentForm, dom.$('button.review-thread-reply-button')); if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined) { diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index 1cd7b487f01..e1888faedda 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -423,7 +423,7 @@ export class ReviewController implements IEditorContribution { } removed.forEach(thread => { - let matchedZones = this._commentWidgets.filter(zoneWidget => zoneWidget.owner === e.owner && zoneWidget.commentThread.threadId === thread.threadId); + let matchedZones = this._commentWidgets.filter(zoneWidget => zoneWidget.owner === e.owner && zoneWidget.commentThread.threadId === thread.threadId && zoneWidget.commentThread.threadId !== ''); if (matchedZones.length) { let matchedZone = matchedZones[0]; let index = this._commentWidgets.indexOf(matchedZone); diff --git a/src/vs/workbench/contrib/comments/browser/media/review.css b/src/vs/workbench/contrib/comments/browser/media/review.css index aa195a0cd7a..4ed2c4be005 100644 --- a/src/vs/workbench/contrib/comments/browser/media/review.css +++ b/src/vs/workbench/contrib/comments/browser/media/review.css @@ -198,6 +198,16 @@ background-image: url(./reaction-hc.svg); } +.monaco-editor .review-widget .body .review-comment .comment-title .action-label { + display: block; + height: 18px; + line-height: 18px; + min-width: 28px; + background-size: 16px; + background-position: center center; + background-repeat: no-repeat; +} + .monaco-editor .review-widget .body .review-comment .comment-title .action-label.toolbar-toggle-pickReactions { background-image: url(./reaction.svg); width: 18px; -- GitLab