@theia/core 1.32.0-next.5 → 1.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/i18n/nls.cs.json +14 -13
- package/i18n/nls.de.json +14 -13
- package/i18n/nls.es.json +14 -13
- package/i18n/nls.fr.json +14 -13
- package/i18n/nls.hu.json +14 -13
- package/i18n/nls.it.json +14 -13
- package/i18n/nls.ja.json +14 -13
- package/i18n/nls.json +16 -15
- package/i18n/nls.pl.json +14 -13
- package/i18n/nls.pt-br.json +14 -13
- package/i18n/nls.pt-pt.json +14 -13
- package/i18n/nls.ru.json +14 -13
- package/i18n/nls.zh-cn.json +23 -22
- package/lib/browser/context-key-service.d.ts +20 -8
- package/lib/browser/context-key-service.d.ts.map +1 -1
- package/lib/browser/context-key-service.js +6 -0
- package/lib/browser/context-key-service.js.map +1 -1
- package/lib/browser/context-menu-renderer.d.ts +2 -0
- package/lib/browser/context-menu-renderer.d.ts.map +1 -1
- package/lib/browser/context-menu-renderer.js.map +1 -1
- package/lib/browser/core-preferences.d.ts.map +1 -1
- package/lib/browser/core-preferences.js +8 -9
- package/lib/browser/core-preferences.js.map +1 -1
- package/lib/browser/frontend-application-module.d.ts.map +1 -1
- package/lib/browser/frontend-application-module.js +4 -1
- package/lib/browser/frontend-application-module.js.map +1 -1
- package/lib/browser/hover-service.d.ts +41 -0
- package/lib/browser/hover-service.d.ts.map +1 -0
- package/lib/browser/hover-service.js +191 -0
- package/lib/browser/hover-service.js.map +1 -0
- package/lib/browser/icon-theme-service.js +1 -1
- package/lib/browser/icon-theme-service.js.map +1 -1
- package/lib/browser/index.d.ts +1 -0
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +1 -0
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/menu/browser-context-menu-renderer.d.ts +1 -1
- package/lib/browser/menu/browser-context-menu-renderer.d.ts.map +1 -1
- package/lib/browser/menu/browser-context-menu-renderer.js +2 -2
- package/lib/browser/menu/browser-context-menu-renderer.js.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.d.ts +4 -3
- package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.js +9 -6
- package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
- package/lib/browser/resource-context-key.d.ts +2 -3
- package/lib/browser/resource-context-key.d.ts.map +1 -1
- package/lib/browser/resource-context-key.js +2 -4
- package/lib/browser/resource-context-key.js.map +1 -1
- package/lib/browser/shell/application-shell.d.ts.map +1 -1
- package/lib/browser/shell/application-shell.js +7 -0
- package/lib/browser/shell/application-shell.js.map +1 -1
- package/lib/browser/shell/sidebar-menu-widget.d.ts +3 -0
- package/lib/browser/shell/sidebar-menu-widget.d.ts.map +1 -1
- package/lib/browser/shell/sidebar-menu-widget.js +15 -1
- package/lib/browser/shell/sidebar-menu-widget.js.map +1 -1
- package/lib/browser/shell/tab-bars.d.ts +4 -1
- package/lib/browser/shell/tab-bars.d.ts.map +1 -1
- package/lib/browser/shell/tab-bars.js +25 -12
- package/lib/browser/shell/tab-bars.js.map +1 -1
- package/lib/browser/status-bar/index.d.ts +0 -1
- package/lib/browser/status-bar/index.d.ts.map +1 -1
- package/lib/browser/status-bar/index.js +0 -3
- package/lib/browser/status-bar/index.js.map +1 -1
- package/lib/browser/status-bar/status-bar.d.ts +3 -3
- package/lib/browser/status-bar/status-bar.d.ts.map +1 -1
- package/lib/browser/status-bar/status-bar.js +10 -6
- package/lib/browser/status-bar/status-bar.js.map +1 -1
- package/lib/browser/view-container.js +1 -1
- package/lib/browser/view-container.js.map +1 -1
- package/lib/common/objects.spec.d.ts +2 -0
- package/lib/common/objects.spec.d.ts.map +1 -0
- package/lib/common/objects.spec.js +102 -0
- package/lib/common/objects.spec.js.map +1 -0
- package/lib/electron-browser/menu/electron-context-menu-renderer.js +2 -2
- package/lib/electron-browser/menu/electron-context-menu-renderer.js.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.d.ts +8 -2
- package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.js +8 -6
- package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
- package/lib/electron-main/messaging/electron-messaging-contribution.d.ts +1 -0
- package/lib/electron-main/messaging/electron-messaging-contribution.d.ts.map +1 -1
- package/lib/electron-main/messaging/electron-messaging-contribution.js +6 -2
- package/lib/electron-main/messaging/electron-messaging-contribution.js.map +1 -1
- package/package.json +6 -6
- package/src/browser/context-key-service.ts +29 -9
- package/src/browser/context-menu-renderer.ts +2 -0
- package/src/browser/core-preferences.ts +8 -12
- package/src/browser/frontend-application-module.ts +5 -1
- package/src/browser/hover-service.ts +189 -0
- package/src/browser/icon-theme-service.ts +1 -1
- package/src/browser/index.ts +1 -0
- package/src/browser/menu/browser-context-menu-renderer.ts +2 -2
- package/src/browser/menu/browser-menu-plugin.ts +10 -7
- package/src/browser/resource-context-key.ts +5 -7
- package/src/browser/shell/application-shell.ts +9 -2
- package/src/browser/shell/sidebar-menu-widget.tsx +93 -79
- package/src/browser/shell/tab-bars.ts +29 -6
- package/src/browser/status-bar/index.ts +0 -3
- package/src/browser/status-bar/status-bar.tsx +7 -3
- package/src/browser/style/hover-service.css +95 -0
- package/src/browser/style/status-bar.css +0 -49
- package/src/browser/view-container.ts +1 -1
- package/src/common/i18n/nls.metadata.json +7972 -6673
- package/src/common/objects.spec.ts +112 -0
- package/src/electron-browser/menu/electron-context-menu-renderer.ts +2 -2
- package/src/electron-browser/menu/electron-main-menu-factory.ts +14 -6
- package/src/electron-main/messaging/electron-messaging-contribution.ts +7 -2
- package/lib/browser/status-bar/status-bar-hover-manager.d.ts +0 -25
- package/lib/browser/status-bar/status-bar-hover-manager.d.ts.map +0 -1
- package/lib/browser/status-bar/status-bar-hover-manager.js +0 -126
- package/lib/browser/status-bar/status-bar-hover-manager.js.map +0 -1
- package/src/browser/status-bar/status-bar-hover-manager.ts +0 -113
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theia/core",
|
|
3
|
-
"version": "1.32.0
|
|
3
|
+
"version": "1.32.0",
|
|
4
4
|
"description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.",
|
|
5
5
|
"main": "lib/common/index.js",
|
|
6
6
|
"typings": "lib/common/index.d.ts",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"@phosphor/signaling": "1",
|
|
17
17
|
"@phosphor/virtualdom": "1",
|
|
18
18
|
"@phosphor/widgets": "1",
|
|
19
|
-
"@theia/application-package": "1.32.0
|
|
20
|
-
"@theia/request": "1.32.0
|
|
19
|
+
"@theia/application-package": "1.32.0",
|
|
20
|
+
"@theia/request": "1.32.0",
|
|
21
21
|
"@types/body-parser": "^1.16.4",
|
|
22
22
|
"@types/cookie": "^0.3.3",
|
|
23
23
|
"@types/dompurify": "^2.2.2",
|
|
@@ -195,12 +195,12 @@
|
|
|
195
195
|
"watch": "theiaext watch"
|
|
196
196
|
},
|
|
197
197
|
"devDependencies": {
|
|
198
|
-
"@theia/ext-scripts": "1.
|
|
199
|
-
"@theia/re-exports": "1.
|
|
198
|
+
"@theia/ext-scripts": "1.32.0",
|
|
199
|
+
"@theia/re-exports": "1.32.0",
|
|
200
200
|
"minimist": "^1.2.0"
|
|
201
201
|
},
|
|
202
202
|
"nyc": {
|
|
203
203
|
"extends": "../../configs/nyc.json"
|
|
204
204
|
},
|
|
205
|
-
"gitHead": "
|
|
205
|
+
"gitHead": "789d0148748b4dc9dea398520b5a3dd2998d71ec"
|
|
206
206
|
}
|
|
@@ -18,7 +18,11 @@ import { injectable } from 'inversify';
|
|
|
18
18
|
import { Disposable } from '../common';
|
|
19
19
|
import { Emitter, Event } from '../common/event';
|
|
20
20
|
|
|
21
|
-
export
|
|
21
|
+
export type ContextKeyValue = null | undefined | boolean | number | string
|
|
22
|
+
| Array<null | undefined | boolean | number | string>
|
|
23
|
+
| Record<string, null | undefined | boolean | number | string>;
|
|
24
|
+
|
|
25
|
+
export interface ContextKey<T extends ContextKeyValue = ContextKeyValue> {
|
|
22
26
|
set(value: T | undefined): void;
|
|
23
27
|
reset(): void;
|
|
24
28
|
get(): T | undefined;
|
|
@@ -38,15 +42,18 @@ export interface ContextKeyChangeEvent {
|
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
export const ContextKeyService = Symbol('ContextKeyService');
|
|
41
|
-
export interface ContextKeyService extends Disposable {
|
|
42
|
-
readonly onDidChange: Event<ContextKeyChangeEvent>;
|
|
43
|
-
|
|
44
|
-
createKey<T>(key: string, defaultValue: T | undefined): ContextKey<T>;
|
|
45
45
|
|
|
46
|
+
export interface ContextMatcher extends Disposable {
|
|
46
47
|
/**
|
|
47
48
|
* Whether the expression is satisfied. If `context` provided, the service will attempt to retrieve a context object associated with that element.
|
|
48
49
|
*/
|
|
49
50
|
match(expression: string, context?: HTMLElement): boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ContextKeyService extends ContextMatcher {
|
|
54
|
+
readonly onDidChange: Event<ContextKeyChangeEvent>;
|
|
55
|
+
|
|
56
|
+
createKey<T extends ContextKeyValue>(key: string, defaultValue: T | undefined): ContextKey<T>;
|
|
50
57
|
|
|
51
58
|
/**
|
|
52
59
|
* @returns a Set of the keys used by the given `expression` or `undefined` if none are used or the expression cannot be parsed.
|
|
@@ -54,8 +61,8 @@ export interface ContextKeyService extends Disposable {
|
|
|
54
61
|
parseKeys(expression: string): Set<string> | undefined;
|
|
55
62
|
|
|
56
63
|
/**
|
|
57
|
-
* Creates a temporary context that will use the `values` passed in when evaluating
|
|
58
|
-
*
|
|
64
|
+
* Creates a temporary context that will use the `values` passed in when evaluating {@link callback}.
|
|
65
|
+
* {@link callback | The callback} must be synchronous.
|
|
59
66
|
*/
|
|
60
67
|
with<T>(values: Record<string, unknown>, callback: () => T): T;
|
|
61
68
|
|
|
@@ -65,13 +72,19 @@ export interface ContextKeyService extends Disposable {
|
|
|
65
72
|
*/
|
|
66
73
|
createScoped(target: HTMLElement): ScopedValueStore;
|
|
67
74
|
|
|
75
|
+
/**
|
|
76
|
+
* @param overlay values to be used in the new {@link ContextKeyService}. These values will be static.
|
|
77
|
+
* Creates a child service with a separate context and a set of fixed values to override parent values.
|
|
78
|
+
*/
|
|
79
|
+
createOverlay(overlay: Iterable<[string, unknown]>): ContextMatcher;
|
|
80
|
+
|
|
68
81
|
/**
|
|
69
82
|
* Set or modify a value in the service's context.
|
|
70
83
|
*/
|
|
71
84
|
setContext(key: string, value: unknown): void;
|
|
72
85
|
}
|
|
73
86
|
|
|
74
|
-
export type ScopedValueStore = Omit<ContextKeyService, 'onDidChange' | 'match' | 'parseKeys' | 'with'>;
|
|
87
|
+
export type ScopedValueStore = Omit<ContextKeyService, 'onDidChange' | 'match' | 'parseKeys' | 'with' | 'createOverlay'>;
|
|
75
88
|
|
|
76
89
|
@injectable()
|
|
77
90
|
export class ContextKeyServiceDummyImpl implements ContextKeyService {
|
|
@@ -82,7 +95,7 @@ export class ContextKeyServiceDummyImpl implements ContextKeyService {
|
|
|
82
95
|
this.onDidChangeEmitter.fire(event);
|
|
83
96
|
}
|
|
84
97
|
|
|
85
|
-
createKey<T>(key: string, defaultValue: T | undefined): ContextKey<T> {
|
|
98
|
+
createKey<T extends ContextKeyValue>(key: string, defaultValue: T | undefined): ContextKey<T> {
|
|
86
99
|
return ContextKey.None;
|
|
87
100
|
}
|
|
88
101
|
/**
|
|
@@ -114,6 +127,13 @@ export class ContextKeyServiceDummyImpl implements ContextKeyService {
|
|
|
114
127
|
return this;
|
|
115
128
|
}
|
|
116
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Details should be implemented by an extension, e.g. the monaco extension.
|
|
132
|
+
*/
|
|
133
|
+
createOverlay(overlay: Iterable<[string, unknown]>): ContextMatcher {
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
|
|
117
137
|
/**
|
|
118
138
|
* Details should be implemented by an extension, e.g. by the monaco extension.
|
|
119
139
|
*/
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { injectable } from 'inversify';
|
|
20
20
|
import { MenuPath } from '../common/menu';
|
|
21
21
|
import { Disposable, DisposableCollection } from '../common/disposable';
|
|
22
|
+
import { ContextMatcher } from './context-key-service';
|
|
22
23
|
|
|
23
24
|
export interface Coordinate { x: number; y: number; }
|
|
24
25
|
export const Coordinate = Symbol('Coordinate');
|
|
@@ -117,5 +118,6 @@ export interface RenderContextMenuOptions {
|
|
|
117
118
|
* of menu items registered for this item.
|
|
118
119
|
*/
|
|
119
120
|
context?: HTMLElement;
|
|
121
|
+
contextKeyService?: ContextMatcher;
|
|
120
122
|
onHide?: () => void;
|
|
121
123
|
}
|
|
@@ -84,15 +84,15 @@ export const corePreferenceSchema: PreferenceSchema = {
|
|
|
84
84
|
type: 'string',
|
|
85
85
|
enum: ['classic', 'visible', 'hidden', 'compact'],
|
|
86
86
|
markdownEnumDescriptions: [
|
|
87
|
-
nls.localizeByDefault('Menu is only hidden in full screen mode.'),
|
|
88
|
-
nls.localizeByDefault('Menu is always visible even in full screen mode.'),
|
|
87
|
+
nls.localizeByDefault('Menu is displayed at the top of the window and only hidden in full screen mode.'),
|
|
88
|
+
nls.localizeByDefault('Menu is always visible at the top of the window even in full screen mode.'),
|
|
89
89
|
nls.localizeByDefault('Menu is always hidden.'),
|
|
90
90
|
nls.localizeByDefault('Menu is displayed as a compact button in the sidebar. This value is ignored when `#window.titleBarStyle#` is `native`.')
|
|
91
91
|
],
|
|
92
92
|
default: 'classic',
|
|
93
93
|
scope: 'application',
|
|
94
94
|
// eslint-disable-next-line max-len
|
|
95
|
-
markdownDescription: nls.localizeByDefault(
|
|
95
|
+
markdownDescription: nls.localizeByDefault('Control the visibility of the menu bar. A setting of \'toggle\' means that the menu bar is hidden and a single press of the Alt key will show it. A setting of \'compact\' will move the menu into the sidebar.'),
|
|
96
96
|
included: !(isOSX && environment.electron.is())
|
|
97
97
|
},
|
|
98
98
|
'window.title': {
|
|
@@ -144,7 +144,7 @@ export const corePreferenceSchema: PreferenceSchema = {
|
|
|
144
144
|
type: 'boolean',
|
|
145
145
|
default: true,
|
|
146
146
|
// eslint-disable-next-line max-len
|
|
147
|
-
description: nls.localizeByDefault('Controls whether CA certificates should be loaded from the OS. (On Windows and macOS a reload of the window is required after turning this off.)'),
|
|
147
|
+
description: nls.localizeByDefault('Controls whether CA certificates should be loaded from the OS. (On Windows and macOS, a reload of the window is required after turning this off.)'),
|
|
148
148
|
scope: 'application'
|
|
149
149
|
},
|
|
150
150
|
'workbench.list.openMode': {
|
|
@@ -155,7 +155,7 @@ export const corePreferenceSchema: PreferenceSchema = {
|
|
|
155
155
|
],
|
|
156
156
|
default: 'singleClick',
|
|
157
157
|
// eslint-disable-next-line max-len
|
|
158
|
-
description: nls.localizeByDefault('Controls how to open items in trees and lists using the mouse (if supported).
|
|
158
|
+
description: nls.localizeByDefault('Controls how to open items in trees and lists using the mouse (if supported). Note that some trees and lists might choose to ignore this setting if it is not applicable.')
|
|
159
159
|
},
|
|
160
160
|
'workbench.editor.highlightModifiedTabs': {
|
|
161
161
|
'type': 'boolean',
|
|
@@ -222,19 +222,15 @@ export const corePreferenceSchema: PreferenceSchema = {
|
|
|
222
222
|
default: 300,
|
|
223
223
|
minimum: 0,
|
|
224
224
|
maximum: 2000,
|
|
225
|
-
|
|
226
|
-
description: nls.localize('theia/core/sashDelay', 'Controls the hover feedback delay in milliseconds of the dragging area in between views/editors.')
|
|
225
|
+
description: nls.localizeByDefault('Controls the hover feedback delay in milliseconds of the dragging area in between views/editors.')
|
|
227
226
|
},
|
|
228
227
|
'workbench.sash.size': {
|
|
229
228
|
type: 'number',
|
|
230
229
|
default: 4,
|
|
231
230
|
minimum: 1,
|
|
232
231
|
maximum: 20,
|
|
233
|
-
//
|
|
234
|
-
description: nls.
|
|
235
|
-
'theia/core/sashSize',
|
|
236
|
-
'Controls the feedback area size in pixels of the dragging area in between views/editors. Set it to a larger value if needed.'
|
|
237
|
-
)
|
|
232
|
+
// eslint-disable-next-line max-len
|
|
233
|
+
description: nls.localizeByDefault('Controls the feedback area size in pixels of the dragging area in between views/editors. Set it to a larger value if you feel it\'s hard to resize views using the mouse.')
|
|
238
234
|
},
|
|
239
235
|
'workbench.tab.maximize': {
|
|
240
236
|
type: 'boolean',
|
|
@@ -135,6 +135,7 @@ import { bindStatusBar } from './status-bar';
|
|
|
135
135
|
import { MarkdownRenderer, MarkdownRendererFactory, MarkdownRendererImpl } from './markdown-rendering/markdown-renderer';
|
|
136
136
|
import { StylingParticipant, StylingService } from './styling-service';
|
|
137
137
|
import { bindCommonStylingParticipants } from './common-styling-participants';
|
|
138
|
+
import { HoverService } from './hover-service';
|
|
138
139
|
|
|
139
140
|
export { bindResourceProvider, bindMessageService, bindPreferenceService };
|
|
140
141
|
|
|
@@ -186,7 +187,8 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is
|
|
|
186
187
|
const selectionService = container.get(SelectionService);
|
|
187
188
|
const commandService = container.get<CommandService>(CommandService);
|
|
188
189
|
const corePreferences = container.get<CorePreferences>(CorePreferences);
|
|
189
|
-
|
|
190
|
+
const hoverService = container.get(HoverService);
|
|
191
|
+
return new TabBarRenderer(contextMenuRenderer, tabBarDecoratorService, iconThemeService, selectionService, commandService, corePreferences, hoverService);
|
|
190
192
|
});
|
|
191
193
|
bind(TheiaDockPanel.Factory).toFactory(({ container }) => options => {
|
|
192
194
|
const corePreferences = container.get<CorePreferences>(CorePreferences);
|
|
@@ -433,6 +435,8 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is
|
|
|
433
435
|
bind(SaveResourceService).toSelf().inSingletonScope();
|
|
434
436
|
bind(UserWorkingDirectoryProvider).toSelf().inSingletonScope();
|
|
435
437
|
|
|
438
|
+
bind(HoverService).toSelf().inSingletonScope();
|
|
439
|
+
|
|
436
440
|
bind(StylingService).toSelf().inSingletonScope();
|
|
437
441
|
bindContributionProvider(bind, StylingParticipant);
|
|
438
442
|
bind(FrontendApplicationContribution).toService(StylingService);
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2022 Ericsson and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { inject, injectable } from 'inversify';
|
|
18
|
+
import { Disposable, DisposableCollection, disposableTimeout, isOSX } from '../common';
|
|
19
|
+
import { MarkdownString } from '../common/markdown-rendering/markdown-string';
|
|
20
|
+
import { animationFrame } from './browser';
|
|
21
|
+
import { MarkdownRenderer, MarkdownRendererFactory } from './markdown-rendering/markdown-renderer';
|
|
22
|
+
import { PreferenceService } from './preferences';
|
|
23
|
+
|
|
24
|
+
import '../../src/browser/style/hover-service.css';
|
|
25
|
+
|
|
26
|
+
export type HoverPosition = 'left' | 'right' | 'top' | 'bottom';
|
|
27
|
+
|
|
28
|
+
export namespace HoverPosition {
|
|
29
|
+
export function invertIfNecessary(position: HoverPosition, target: DOMRect, host: DOMRect, totalWidth: number, totalHeight: number): HoverPosition {
|
|
30
|
+
if (position === 'left') {
|
|
31
|
+
if (target.left - host.width - 5 < 0) {
|
|
32
|
+
return 'right';
|
|
33
|
+
}
|
|
34
|
+
} else if (position === 'right') {
|
|
35
|
+
if (target.right + host.width + 5 > totalWidth) {
|
|
36
|
+
return 'left';
|
|
37
|
+
}
|
|
38
|
+
} else if (position === 'top') {
|
|
39
|
+
if (target.top - host.height - 5 < 0) {
|
|
40
|
+
return 'bottom';
|
|
41
|
+
}
|
|
42
|
+
} else if (position === 'bottom') {
|
|
43
|
+
if (target.bottom + host.height + 5 > totalHeight) {
|
|
44
|
+
return 'top';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return position;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface HoverRequest {
|
|
52
|
+
content: string | MarkdownString | HTMLElement
|
|
53
|
+
target: HTMLElement
|
|
54
|
+
/**
|
|
55
|
+
* The position where the hover should appear.
|
|
56
|
+
* Note that the hover service will try to invert the position (i.e. right -> left)
|
|
57
|
+
* if the specified content does not fit in the window next to the target element
|
|
58
|
+
*/
|
|
59
|
+
position: HoverPosition
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@injectable()
|
|
63
|
+
export class HoverService {
|
|
64
|
+
protected static hostClassName = 'theia-hover';
|
|
65
|
+
protected static styleSheetId = 'theia-hover-style';
|
|
66
|
+
@inject(PreferenceService) protected readonly preferences: PreferenceService;
|
|
67
|
+
@inject(MarkdownRendererFactory) protected readonly markdownRendererFactory: MarkdownRendererFactory;
|
|
68
|
+
|
|
69
|
+
protected _markdownRenderer: MarkdownRenderer | undefined;
|
|
70
|
+
protected get markdownRenderer(): MarkdownRenderer {
|
|
71
|
+
this._markdownRenderer ||= this.markdownRendererFactory();
|
|
72
|
+
return this._markdownRenderer;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
protected _hoverHost: HTMLElement | undefined;
|
|
76
|
+
protected get hoverHost(): HTMLElement {
|
|
77
|
+
if (!this._hoverHost) {
|
|
78
|
+
this._hoverHost = document.createElement('div');
|
|
79
|
+
this._hoverHost.classList.add(HoverService.hostClassName);
|
|
80
|
+
this._hoverHost.style.position = 'absolute';
|
|
81
|
+
}
|
|
82
|
+
return this._hoverHost;
|
|
83
|
+
}
|
|
84
|
+
protected pendingTimeout: Disposable | undefined;
|
|
85
|
+
protected hoverTarget: HTMLElement | undefined;
|
|
86
|
+
protected lastHidHover = Date.now();
|
|
87
|
+
protected readonly disposeOnHide = new DisposableCollection();
|
|
88
|
+
|
|
89
|
+
requestHover(request: HoverRequest): void {
|
|
90
|
+
if (request.target !== this.hoverTarget) {
|
|
91
|
+
this.cancelHover();
|
|
92
|
+
this.pendingTimeout = disposableTimeout(() => this.renderHover(request), this.getHoverDelay());
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
protected getHoverDelay(): number {
|
|
97
|
+
return Date.now() - this.lastHidHover < 200
|
|
98
|
+
? 0
|
|
99
|
+
: this.preferences.get('workbench.hover.delay', isOSX ? 1500 : 500);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
protected async renderHover(request: HoverRequest): Promise<void> {
|
|
103
|
+
const host = this.hoverHost;
|
|
104
|
+
const { target, content, position } = request;
|
|
105
|
+
this.hoverTarget = target;
|
|
106
|
+
if (content instanceof HTMLElement) {
|
|
107
|
+
host.appendChild(content);
|
|
108
|
+
} else if (typeof content === 'string') {
|
|
109
|
+
host.textContent = content;
|
|
110
|
+
} else {
|
|
111
|
+
const renderedContent = this.markdownRenderer.render(content);
|
|
112
|
+
this.disposeOnHide.push(renderedContent);
|
|
113
|
+
host.appendChild(renderedContent.element);
|
|
114
|
+
}
|
|
115
|
+
// browsers might insert linebreaks when the hover appears at the edge of the window
|
|
116
|
+
// resetting the position prevents that
|
|
117
|
+
host.style.left = '0px';
|
|
118
|
+
host.style.top = '0px';
|
|
119
|
+
document.body.append(host);
|
|
120
|
+
await animationFrame(); // Allow the browser to size the host
|
|
121
|
+
const updatedPosition = this.setHostPosition(target, host, position);
|
|
122
|
+
|
|
123
|
+
this.disposeOnHide.push({
|
|
124
|
+
dispose: () => {
|
|
125
|
+
this.lastHidHover = Date.now();
|
|
126
|
+
host.classList.remove(updatedPosition);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
this.listenForMouseOut();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
protected setHostPosition(target: HTMLElement, host: HTMLElement, position: HoverPosition): HoverPosition {
|
|
134
|
+
const targetDimensions = target.getBoundingClientRect();
|
|
135
|
+
const hostDimensions = host.getBoundingClientRect();
|
|
136
|
+
const documentWidth = document.body.getBoundingClientRect().width;
|
|
137
|
+
// document.body.getBoundingClientRect().height doesn't work as expected
|
|
138
|
+
// scrollHeight will always be accurate here: https://stackoverflow.com/a/44077777
|
|
139
|
+
const documentHeight = document.documentElement.scrollHeight;
|
|
140
|
+
position = HoverPosition.invertIfNecessary(position, targetDimensions, hostDimensions, documentWidth, documentHeight);
|
|
141
|
+
if (position === 'top' || position === 'bottom') {
|
|
142
|
+
const targetMiddleWidth = targetDimensions.left + (targetDimensions.width / 2);
|
|
143
|
+
const middleAlignment = targetMiddleWidth - (hostDimensions.width / 2);
|
|
144
|
+
const furthestRight = Math.min(documentWidth - hostDimensions.width, middleAlignment);
|
|
145
|
+
const left = Math.max(0, furthestRight);
|
|
146
|
+
const top = position === 'top'
|
|
147
|
+
? targetDimensions.top - hostDimensions.height - 5
|
|
148
|
+
: targetDimensions.bottom + 5;
|
|
149
|
+
host.style.setProperty('--theia-hover-before-position', `${targetMiddleWidth - left - 5}px`);
|
|
150
|
+
host.style.top = `${top}px`;
|
|
151
|
+
host.style.left = `${left}px`;
|
|
152
|
+
} else {
|
|
153
|
+
const targetMiddleHeight = targetDimensions.top + (targetDimensions.height / 2);
|
|
154
|
+
const middleAlignment = targetMiddleHeight - (hostDimensions.height / 2);
|
|
155
|
+
const furthestTop = Math.min(documentHeight - hostDimensions.height, middleAlignment);
|
|
156
|
+
const top = Math.max(0, furthestTop);
|
|
157
|
+
const left = position === 'left'
|
|
158
|
+
? targetDimensions.left - hostDimensions.width - 5
|
|
159
|
+
: targetDimensions.right + 5;
|
|
160
|
+
host.style.setProperty('--theia-hover-before-position', `${targetMiddleHeight - top - 5}px`);
|
|
161
|
+
host.style.left = `${left}px`;
|
|
162
|
+
host.style.top = `${top}px`;
|
|
163
|
+
}
|
|
164
|
+
host.classList.add(position);
|
|
165
|
+
return position;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
protected listenForMouseOut(): void {
|
|
169
|
+
const handleMouseMove = (e: MouseEvent) => {
|
|
170
|
+
if (e.target instanceof Node && !this.hoverHost.contains(e.target) && !this.hoverTarget?.contains(e.target)) {
|
|
171
|
+
this.cancelHover();
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
175
|
+
this.disposeOnHide.push({ dispose: () => document.removeEventListener('mousemove', handleMouseMove) });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
cancelHover(): void {
|
|
179
|
+
this.pendingTimeout?.dispose();
|
|
180
|
+
this.unRenderHover();
|
|
181
|
+
this.disposeOnHide.dispose();
|
|
182
|
+
this.hoverTarget = undefined;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
protected unRenderHover(): void {
|
|
186
|
+
this.hoverHost.remove();
|
|
187
|
+
this.hoverHost.replaceChildren();
|
|
188
|
+
}
|
|
189
|
+
}
|
package/src/browser/index.ts
CHANGED
|
@@ -34,8 +34,8 @@ export class BrowserContextMenuRenderer extends ContextMenuRenderer {
|
|
|
34
34
|
super();
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
protected doRender({ menuPath, anchor, args, onHide, context }: RenderContextMenuOptions): ContextMenuAccess {
|
|
38
|
-
const contextMenu = this.menuFactory.createContextMenu(menuPath, args, context);
|
|
37
|
+
protected doRender({ menuPath, anchor, args, onHide, context, contextKeyService }: RenderContextMenuOptions): ContextMenuAccess {
|
|
38
|
+
const contextMenu = this.menuFactory.createContextMenu(menuPath, args, context, contextKeyService);
|
|
39
39
|
const { x, y } = coordinateFromAnchor(anchor);
|
|
40
40
|
if (onHide) {
|
|
41
41
|
contextMenu.aboutToClose.connect(() => onHide!());
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from '../../common';
|
|
24
24
|
import { KeybindingRegistry } from '../keybinding';
|
|
25
25
|
import { FrontendApplicationContribution, FrontendApplication } from '../frontend-application';
|
|
26
|
-
import { ContextKeyService } from '../context-key-service';
|
|
26
|
+
import { ContextKeyService, ContextMatcher } from '../context-key-service';
|
|
27
27
|
import { ContextMenuContext } from './context-menu-context';
|
|
28
28
|
import { waitForRevealed } from '../widgets';
|
|
29
29
|
import { ApplicationShell } from '../shell';
|
|
@@ -38,6 +38,7 @@ export abstract class MenuBarWidget extends MenuBar {
|
|
|
38
38
|
export interface BrowserMenuOptions extends MenuWidget.IOptions {
|
|
39
39
|
commands: MenuCommandRegistry,
|
|
40
40
|
context?: HTMLElement,
|
|
41
|
+
contextKeyService?: ContextMatcher;
|
|
41
42
|
rootMenuPath: MenuPath
|
|
42
43
|
};
|
|
43
44
|
|
|
@@ -107,10 +108,10 @@ export class BrowserMainMenuFactory implements MenuWidgetFactory {
|
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
createContextMenu(path: MenuPath, args?: unknown[], context?: HTMLElement): MenuWidget {
|
|
111
|
+
createContextMenu(path: MenuPath, args?: unknown[], context?: HTMLElement, contextKeyService?: ContextMatcher): MenuWidget {
|
|
111
112
|
const menuModel = this.menuProvider.getMenu(path);
|
|
112
113
|
const menuCommandRegistry = this.createMenuCommandRegistry(menuModel, args).snapshot(path);
|
|
113
|
-
const contextMenu = this.createMenuWidget(menuModel, { commands: menuCommandRegistry, context, rootMenuPath: path });
|
|
114
|
+
const contextMenu = this.createMenuWidget(menuModel, { commands: menuCommandRegistry, context, rootMenuPath: path, contextKeyService });
|
|
114
115
|
return contextMenu;
|
|
115
116
|
}
|
|
116
117
|
|
|
@@ -286,7 +287,9 @@ export class DynamicMenuWidget extends MenuWidget {
|
|
|
286
287
|
}
|
|
287
288
|
|
|
288
289
|
protected buildSubMenus(parentItems: MenuWidget.IItemOptions[], menu: MenuNode, commands: MenuCommandRegistry): MenuWidget.IItemOptions[] {
|
|
289
|
-
if (CompoundMenuNode.is(menu)
|
|
290
|
+
if (CompoundMenuNode.is(menu)
|
|
291
|
+
&& menu.children.length
|
|
292
|
+
&& this.undefinedOrMatch(this.options.contextKeyService ?? this.services.contextKeyService, menu.when, this.options.context)) {
|
|
290
293
|
const role = menu === this.menu ? CompoundMenuNodeRole.Group : CompoundMenuNode.getRole(menu);
|
|
291
294
|
if (role === CompoundMenuNodeRole.Submenu) {
|
|
292
295
|
const submenu = this.services.menuWidgetFactory.createMenuWidget(menu, this.options);
|
|
@@ -307,7 +310,7 @@ export class DynamicMenuWidget extends MenuWidget {
|
|
|
307
310
|
}
|
|
308
311
|
} else if (menu.command) {
|
|
309
312
|
const node = menu.altNode && this.services.context.altPressed ? menu.altNode : (menu as MenuNode & CommandMenuNode);
|
|
310
|
-
if (commands.isVisible(node.command) && this.undefinedOrMatch(node.when, this.options.context)) {
|
|
313
|
+
if (commands.isVisible(node.command) && this.undefinedOrMatch(this.options.contextKeyService ?? this.services.contextKeyService, node.when, this.options.context)) {
|
|
311
314
|
parentItems.push({
|
|
312
315
|
command: node.command,
|
|
313
316
|
type: 'command'
|
|
@@ -317,8 +320,8 @@ export class DynamicMenuWidget extends MenuWidget {
|
|
|
317
320
|
return parentItems;
|
|
318
321
|
}
|
|
319
322
|
|
|
320
|
-
protected undefinedOrMatch(expression?: string, context?: HTMLElement): boolean {
|
|
321
|
-
if (expression) { return
|
|
323
|
+
protected undefinedOrMatch(contextKeyService: ContextMatcher, expression?: string, context?: HTMLElement): boolean {
|
|
324
|
+
if (expression) { return contextKeyService.match(expression, context); }
|
|
322
325
|
return true;
|
|
323
326
|
}
|
|
324
327
|
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
|
|
17
17
|
import { injectable, inject, postConstruct } from 'inversify';
|
|
18
18
|
import URI from '../common/uri';
|
|
19
|
-
import { URI as Uri } from 'vscode-uri';
|
|
20
19
|
import { ContextKeyService, ContextKey } from './context-key-service';
|
|
21
20
|
import { LanguageService } from './language-service';
|
|
22
21
|
|
|
@@ -29,7 +28,7 @@ export class ResourceContextKey {
|
|
|
29
28
|
@inject(ContextKeyService)
|
|
30
29
|
protected readonly contextKeyService: ContextKeyService;
|
|
31
30
|
|
|
32
|
-
protected resource: ContextKey<
|
|
31
|
+
protected resource: ContextKey<string>;
|
|
33
32
|
protected resourceSchemeKey: ContextKey<string>;
|
|
34
33
|
protected resourceFileName: ContextKey<string>;
|
|
35
34
|
protected resourceExtname: ContextKey<string>;
|
|
@@ -40,7 +39,7 @@ export class ResourceContextKey {
|
|
|
40
39
|
|
|
41
40
|
@postConstruct()
|
|
42
41
|
protected init(): void {
|
|
43
|
-
this.resource = this.contextKeyService.createKey<
|
|
42
|
+
this.resource = this.contextKeyService.createKey<string>('resource', undefined);
|
|
44
43
|
this.resourceSchemeKey = this.contextKeyService.createKey<string>('resourceScheme', undefined);
|
|
45
44
|
this.resourceFileName = this.contextKeyService.createKey<string>('resourceFilename', undefined);
|
|
46
45
|
this.resourceExtname = this.contextKeyService.createKey<string>('resourceExtname', undefined);
|
|
@@ -50,13 +49,12 @@ export class ResourceContextKey {
|
|
|
50
49
|
this.resourceSet = this.contextKeyService.createKey<boolean>('resourceSet', false);
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
get():
|
|
54
|
-
|
|
55
|
-
return codeUri && new URI(codeUri);
|
|
52
|
+
get(): string | undefined {
|
|
53
|
+
return this.resource.get();
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
set(resourceUri: URI | undefined): void {
|
|
59
|
-
this.resource.set(resourceUri?.
|
|
57
|
+
this.resource.set(resourceUri?.toString());
|
|
60
58
|
this.resourceSchemeKey.set(resourceUri?.scheme);
|
|
61
59
|
this.resourceFileName.set(resourceUri?.path.base);
|
|
62
60
|
this.resourceExtname.set(resourceUri?.path.ext);
|
|
@@ -309,8 +309,8 @@ export class ApplicationShell extends Widget {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
protected initFocusKeyContexts(): void {
|
|
312
|
-
const sideBarFocus = this.contextKeyService.createKey('sideBarFocus', false);
|
|
313
|
-
const panelFocus = this.contextKeyService.createKey('panelFocus', false);
|
|
312
|
+
const sideBarFocus = this.contextKeyService.createKey<boolean>('sideBarFocus', false);
|
|
313
|
+
const panelFocus = this.contextKeyService.createKey<boolean>('panelFocus', false);
|
|
314
314
|
const updateFocusContextKeys = () => {
|
|
315
315
|
const area = this.activeWidget && this.getAreaFor(this.activeWidget);
|
|
316
316
|
sideBarFocus.set(area === 'left');
|
|
@@ -1849,6 +1849,13 @@ export class ApplicationShell extends Widget {
|
|
|
1849
1849
|
}
|
|
1850
1850
|
return true;
|
|
1851
1851
|
} else if (ci === 0) {
|
|
1852
|
+
if (current && current.titles.length > 0) {
|
|
1853
|
+
current.currentIndex = current.titles.length - 1;
|
|
1854
|
+
if (current.currentTitle) {
|
|
1855
|
+
this.activateWidget(current.currentTitle.owner.id);
|
|
1856
|
+
}
|
|
1857
|
+
return true;
|
|
1858
|
+
}
|
|
1852
1859
|
return this.activatePreviousTabBar(current);
|
|
1853
1860
|
}
|
|
1854
1861
|
}
|