Skip to content

Commit a993ada

Browse files
authored
Merge pull request microsoft#103738 from jeanp413:fix-feedback-widget-position
Fixes feedback widget appears wrong when invoked from command palette
2 parents 39e0f72 + 0dfb032 commit a993ada

File tree

2 files changed

+66
-77
lines changed

2 files changed

+66
-77
lines changed

src/vs/workbench/contrib/feedback/browser/feedback.ts

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55

66
import 'vs/css!./media/feedback';
77
import * as nls from 'vs/nls';
8-
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
9-
import { Dropdown } from 'vs/base/browser/ui/dropdown/dropdown';
8+
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
109
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
1110
import * as dom from 'vs/base/browser/dom';
1211
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -24,6 +23,8 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
2423
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
2524
import { KeyCode } from 'vs/base/common/keyCodes';
2625
import { Codicon } from 'vs/base/common/codicons';
26+
import { Emitter } from 'vs/base/common/event';
27+
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
2728

2829
export interface IFeedback {
2930
feedback: string;
@@ -36,17 +37,18 @@ export interface IFeedbackDelegate {
3637
}
3738

3839
export interface IFeedbackWidgetOptions {
39-
contextViewProvider: IContextViewService;
4040
feedbackService: IFeedbackDelegate;
41-
onFeedbackVisibilityChange?: (visible: boolean) => void;
4241
}
4342

44-
export class FeedbackWidget extends Dropdown {
43+
export class FeedbackWidget extends Disposable {
44+
private visible: boolean | undefined;
45+
private _onDidChangeVisibility = new Emitter<boolean>();
46+
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
47+
4548
private maxFeedbackCharacters: number;
4649

4750
private feedback: string = '';
4851
private sentiment: number = 1;
49-
private autoHideTimeout?: number;
5052

5153
private readonly feedbackDelegate: IFeedbackDelegate;
5254

@@ -63,8 +65,9 @@ export class FeedbackWidget extends Dropdown {
6365
private isPure: boolean = true;
6466

6567
constructor(
66-
container: HTMLElement,
67-
private options: IFeedbackWidgetOptions,
68+
options: IFeedbackWidgetOptions,
69+
@IContextViewService private readonly contextViewService: IContextViewService,
70+
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
6871
@ICommandService private readonly commandService: ICommandService,
6972
@ITelemetryService private readonly telemetryService: ITelemetryService,
7073
@IIntegrityService private readonly integrityService: IIntegrityService,
@@ -73,7 +76,7 @@ export class FeedbackWidget extends Dropdown {
7376
@IProductService productService: IProductService,
7477
@IOpenerService private readonly openerService: IOpenerService
7578
) {
76-
super(container, options);
79+
super();
7780

7881
this.feedbackDelegate = options.feedbackService;
7982
this.maxFeedbackCharacters = this.feedbackDelegate.getCharacterLimit(this.sentiment);
@@ -88,22 +91,24 @@ export class FeedbackWidget extends Dropdown {
8891
}
8992
});
9093

91-
this.element.classList.add('send-feedback');
92-
this.element.title = nls.localize('sendFeedback', "Tweet Feedback");
94+
// Hide feedback widget whenever notifications appear
95+
this._register(this.layoutService.onDidChangeNotificationsVisibility(visible => {
96+
if (visible) {
97+
this.hide();
98+
}
99+
}));
93100
}
94101

95-
protected override getAnchor(): HTMLElement | IAnchor {
96-
const position = dom.getDomNodePagePosition(this.element);
102+
private getAnchor(): HTMLElement | IAnchor {
103+
const dimension = this.layoutService.dimension;
97104

98105
return {
99-
x: position.left + position.width, // center above the container
100-
y: position.top - 26, // above status bar and beak
101-
width: position.width,
102-
height: position.height
106+
x: dimension.width - 8,
107+
y: dimension.height - 31
103108
};
104109
}
105110

106-
protected override renderContents(container: HTMLElement): IDisposable {
111+
private renderContents(container: HTMLElement): IDisposable {
107112
const disposables = new DisposableStore();
108113

109114
container.classList.add('monaco-menu-container');
@@ -379,40 +384,53 @@ export class FeedbackWidget extends Dropdown {
379384
return element;
380385
}
381386

382-
override show(): void {
383-
super.show();
384-
385-
if (this.options.onFeedbackVisibilityChange) {
386-
this.options.onFeedbackVisibilityChange(true);
387+
show(): void {
388+
if (this.visible) {
389+
return;
387390
}
388391

392+
this.visible = true;
393+
this.contextViewService.showContextView({
394+
getAnchor: () => this.getAnchor(),
395+
396+
render: (container) => {
397+
return this.renderContents(container);
398+
},
399+
400+
onDOMEvent: (e, activeElement) => {
401+
this.onEvent(e, activeElement);
402+
},
403+
404+
onHide: () => this._onDidChangeVisibility.fire(false)
405+
});
406+
407+
this._onDidChangeVisibility.fire(true);
408+
389409
this.updateCharCountText();
390410
}
391411

392-
protected override onHide(): void {
393-
if (this.options.onFeedbackVisibilityChange) {
394-
this.options.onFeedbackVisibilityChange(false);
412+
hide(): void {
413+
if (!this.visible) {
414+
return;
395415
}
396-
}
397416

398-
override hide(): void {
399417
if (this.feedbackDescriptionInput) {
400418
this.feedback = this.feedbackDescriptionInput.value;
401419
}
402420

403-
if (this.autoHideTimeout) {
404-
clearTimeout(this.autoHideTimeout);
405-
this.autoHideTimeout = undefined;
406-
}
407-
408421
if (this.hideButton && !this.hideButton.checked) {
409422
this.statusbarService.updateEntryVisibility('status.feedback', false);
410423
}
411424

412-
super.hide();
425+
this.visible = false;
426+
this.contextViewService.hideContextView();
427+
}
428+
429+
isVisible(): boolean {
430+
return !!this.visible;
413431
}
414432

415-
override onEvent(e: Event, activeElement: HTMLElement): void {
433+
private onEvent(e: Event, activeElement: HTMLElement): void {
416434
if (e instanceof KeyboardEvent) {
417435
const keyboardEvent = <KeyboardEvent>e;
418436
if (keyboardEvent.keyCode === 27) { // Escape

src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts

Lines changed: 13 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import { Disposable } from 'vs/base/common/lifecycle';
77
import { FeedbackWidget, IFeedback, IFeedbackDelegate } from 'vs/workbench/contrib/feedback/browser/feedback';
8-
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
98
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
109
import { IProductService } from 'vs/platform/product/common/productService';
1110
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
@@ -16,8 +15,6 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
1615
import { URI } from 'vs/base/common/uri';
1716
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
1817
import { CATEGORIES } from 'vs/workbench/common/actions';
19-
import { assertIsDefined } from 'vs/base/common/types';
20-
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
2118
import { HIDE_NOTIFICATIONS_CENTER, HIDE_NOTIFICATION_TOAST } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
2219
import { isIOS } from 'vs/base/common/platform';
2320

@@ -65,15 +62,11 @@ export class FeedbackStatusbarConribution extends Disposable implements IWorkben
6562
@IStatusbarService private readonly statusbarService: IStatusbarService,
6663
@IProductService productService: IProductService,
6764
@IInstantiationService private readonly instantiationService: IInstantiationService,
68-
@IContextViewService private readonly contextViewService: IContextViewService,
69-
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
70-
@ICommandService private readonly commandService: ICommandService
71-
) {
65+
@ICommandService private readonly commandService: ICommandService) {
7266
super();
7367

7468
if (productService.sendASmile && !isIOS) {
7569
this.createFeedbackStatusEntry();
76-
this.registerListeners();
7770
}
7871
}
7972

@@ -93,44 +86,22 @@ export class FeedbackStatusbarConribution extends Disposable implements IWorkben
9386
});
9487
}
9588

96-
private registerListeners(): void {
97-
98-
// Hide feedback widget whenever notifications appear
99-
this._register(this.layoutService.onDidChangeNotificationsVisibility(visible => {
100-
if (visible) {
101-
this.widget?.hide();
102-
}
103-
}));
104-
}
105-
106-
private createFeedbackWidget(): void {
107-
const statusContainer = document.getElementById('status.feedback');
108-
if (statusContainer) {
109-
const icon = assertIsDefined(statusContainer.getElementsByClassName('codicon').item(0) as HTMLElement | null);
110-
111-
this.widget = this._register(this.instantiationService.createInstance(FeedbackWidget, icon, {
112-
contextViewProvider: this.contextViewService,
113-
feedbackService: this.instantiationService.createInstance(TwitterFeedbackService),
114-
onFeedbackVisibilityChange: visible => this.entry?.update(this.getStatusEntry(visible))
115-
}));
116-
}
117-
}
118-
11989
private toggleFeedback(): void {
12090
if (!this.widget) {
121-
this.createFeedbackWidget();
122-
}
123-
124-
// Hide when visible
125-
if (this.widget?.isVisible()) {
126-
this.widget.hide();
91+
this.widget = this._register(this.instantiationService.createInstance(FeedbackWidget, {
92+
feedbackService: this.instantiationService.createInstance(TwitterFeedbackService)
93+
}));
94+
this._register(this.widget.onDidChangeVisibility(visible => this.entry!.update(this.getStatusEntry(visible))));
12795
}
12896

129-
// Show when hidden
130-
else {
131-
this.commandService.executeCommand(HIDE_NOTIFICATION_TOAST);
132-
this.commandService.executeCommand(HIDE_NOTIFICATIONS_CENTER);
133-
this.widget?.show();
97+
if (this.widget) {
98+
if (!this.widget.isVisible()) {
99+
this.commandService.executeCommand(HIDE_NOTIFICATION_TOAST);
100+
this.commandService.executeCommand(HIDE_NOTIFICATIONS_CENTER);
101+
this.widget.show();
102+
} else {
103+
this.widget.hide();
104+
}
134105
}
135106
}
136107

0 commit comments

Comments
 (0)