Skip to content

Commit 5540170

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
feat: removed the non official themes from the UI
Closes #1283 Ref eclipse-theia/theia#11151 Signed-off-by: Akos Kitta <[email protected]>
1 parent 7cc252f commit 5540170

File tree

6 files changed

+642
-34
lines changed

6 files changed

+642
-34
lines changed

arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+17-6
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@ import {
238238
UploadFirmwareDialog,
239239
UploadFirmwareDialogProps,
240240
} from './dialogs/firmware-uploader/firmware-uploader-dialog';
241-
242241
import { UploadCertificate } from './contributions/upload-certificate';
243242
import {
244243
ArduinoFirmwareUploader,
@@ -328,9 +327,13 @@ import { NewCloudSketch } from './contributions/new-cloud-sketch';
328327
import { SketchbookCompositeWidget } from './widgets/sketchbook/sketchbook-composite-widget';
329328
import { WindowTitleUpdater } from './theia/core/window-title-updater';
330329
import { WindowTitleUpdater as TheiaWindowTitleUpdater } from '@theia/core/lib/browser/window/window-title-updater';
331-
import { ThemeServiceWithDB } from './theia/core/theming';
332-
import { ThemeServiceWithDB as TheiaThemeServiceWithDB } from '@theia/monaco/lib/browser/monaco-indexed-db';
333-
import { MonacoThemingService } from './theia/monaco/monaco-theming-service';
330+
import {
331+
MonacoThemingService,
332+
CleanupObsoleteThemes,
333+
ThemesRegistrationSummary,
334+
MonacoThemeRegistry,
335+
} from './theia/monaco/monaco-theming-service';
336+
import { MonacoThemeRegistry as TheiaMonacoThemeRegistry } from '@theia/monaco/lib/browser/textmate/monaco-theme-registry';
334337
import { MonacoThemingService as TheiaMonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service';
335338
import { TypeHierarchyServiceProvider } from './theia/typehierarchy/type-hierarchy-service';
336339
import { TypeHierarchyServiceProvider as TheiaTypeHierarchyServiceProvider } from '@theia/typehierarchy/lib/browser/typehierarchy-service';
@@ -973,11 +976,19 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
973976
rebind(TheiaWindowTitleUpdater).toService(WindowTitleUpdater);
974977

975978
// register Arduino themes
976-
bind(ThemeServiceWithDB).toSelf().inSingletonScope();
977-
rebind(TheiaThemeServiceWithDB).toService(ThemeServiceWithDB);
978979
bind(MonacoThemingService).toSelf().inSingletonScope();
979980
rebind(TheiaMonacoThemingService).toService(MonacoThemingService);
980981

982+
// workaround for themes cannot be removed after registration
983+
// https://github.com/eclipse-theia/theia/issues/11151
984+
bind(CleanupObsoleteThemes).toSelf().inSingletonScope();
985+
bind(FrontendApplicationContribution).toService(
986+
CleanupObsoleteThemes
987+
);
988+
bind(ThemesRegistrationSummary).toSelf().inSingletonScope();
989+
bind(MonacoThemeRegistry).toSelf().inSingletonScope();
990+
rebind(TheiaMonacoThemeRegistry).toService(MonacoThemeRegistry);
991+
981992
// disable type-hierarchy support
982993
// https://github.com/eclipse-theia/theia/commit/16c88a584bac37f5cf3cc5eb92ffdaa541bda5be
983994
bind(TypeHierarchyServiceProvider).toSelf().inSingletonScope();

arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx

+50-8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ import {
2424
} from '@theia/core/lib/common/i18n/localization';
2525
import SettingsStepInput from './settings-step-input';
2626
import { InterfaceScale } from '../../contributions/interface-scale';
27+
import {
28+
userConfigurableThemes,
29+
themeLabelForSettings,
30+
arduinoThemeTypeOf,
31+
} from '../../theia/core/theming';
32+
import { Theme } from '@theia/core/lib/common/theme';
2733

2834
const maxScale = InterfaceScale.ZoomLevel.toPercentage(
2935
InterfaceScale.ZoomLevel.MAX
@@ -218,14 +224,10 @@ export class SettingsComponent extends React.Component<
218224
<div className="flex-line">
219225
<select
220226
className="theia-select"
221-
value={this.props.themeService.getCurrentTheme().label}
227+
value={this.currentThemeLabel}
222228
onChange={this.themeDidChange}
223229
>
224-
{this.props.themeService.getThemes().map(({ id, label }) => (
225-
<option key={id} value={label}>
226-
{label}
227-
</option>
228-
))}
230+
{this.themeSelectOptions}
229231
</select>
230232
</div>
231233
<div className="flex-line">
@@ -333,6 +335,46 @@ export class SettingsComponent extends React.Component<
333335
);
334336
}
335337

338+
private get currentThemeLabel(): string {
339+
const currentTheme = this.props.themeService.getCurrentTheme();
340+
return themeLabelForSettings(currentTheme);
341+
}
342+
343+
private get separatedThemes(): (Theme | string)[] {
344+
const separatedThemes: (Theme | string)[] = [];
345+
const groupedThemes = userConfigurableThemes(this.props.themeService);
346+
for (const group of groupedThemes) {
347+
for (let i = 0; i < group.length; i++) {
348+
const theme = group[i];
349+
if (i === 0 && separatedThemes.length) {
350+
const arduinoThemeType = arduinoThemeTypeOf(theme);
351+
separatedThemes.push(`separator-${arduinoThemeType}`);
352+
}
353+
separatedThemes.push(theme);
354+
}
355+
}
356+
return separatedThemes;
357+
}
358+
359+
private get themeSelectOptions(): React.ReactNode[] {
360+
return this.separatedThemes.map((item) => {
361+
if (typeof item === 'string') {
362+
return (
363+
// &#9472; -> BOX DRAWINGS LIGHT HORIZONTAL
364+
<option key={item} disabled>
365+
&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;
366+
</option>
367+
);
368+
}
369+
const label = themeLabelForSettings(item);
370+
return (
371+
<option key={item.id} value={label}>
372+
{label}
373+
</option>
374+
);
375+
});
376+
}
377+
336378
private toSelectOptions(language: string | LanguageInfo): JSX.Element {
337379
const plain = typeof language === 'string';
338380
const key = plain ? language : language.languageId;
@@ -610,8 +652,8 @@ export class SettingsComponent extends React.Component<
610652
event: React.ChangeEvent<HTMLSelectElement>
611653
): void => {
612654
const { selectedIndex } = event.target.options;
613-
const theme = this.props.themeService.getThemes()[selectedIndex];
614-
if (theme) {
655+
const theme = this.separatedThemes[selectedIndex];
656+
if (theme && typeof theme !== 'string') {
615657
this.setState({ themeId: theme.id });
616658
if (this.props.themeService.getCurrentTheme().id !== theme.id) {
617659
this.props.themeService.setCurrentTheme(theme.id);
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,186 @@
1-
import type { Theme } from '@theia/core/lib/common/theme';
2-
import { injectable } from '@theia/core/shared/inversify';
3-
import { ThemeServiceWithDB as TheiaThemeServiceWithDB } from '@theia/monaco/lib/browser/monaco-indexed-db';
1+
import {
2+
BuiltinThemeProvider,
3+
ThemeService,
4+
} from '@theia/core/lib/browser/theming';
5+
import { nls } from '@theia/core/lib/common/nls';
6+
import type { Theme, ThemeType } from '@theia/core/lib/common/theme';
7+
import { assertUnreachable } from '../../../common/utils';
48

59
export namespace ArduinoThemes {
6-
export const Light: Theme = {
10+
export const light: Theme = {
711
id: 'arduino-theme',
812
type: 'light',
913
label: 'Light (Arduino)',
1014
editorTheme: 'arduino-theme',
1115
};
12-
export const Dark: Theme = {
16+
export const dark: Theme = {
1317
id: 'arduino-theme-dark',
1418
type: 'dark',
1519
label: 'Dark (Arduino)',
1620
editorTheme: 'arduino-theme-dark',
1721
};
1822
}
1923

20-
@injectable()
21-
export class ThemeServiceWithDB extends TheiaThemeServiceWithDB {
22-
protected override init(): void {
23-
this.register(ArduinoThemes.Light, ArduinoThemes.Dark);
24-
super.init();
24+
const builtInThemeIds = new Set(
25+
[
26+
ArduinoThemes.light,
27+
ArduinoThemes.dark,
28+
BuiltinThemeProvider.hcTheme,
29+
// TODO: add the HC light theme after Theia 1.36
30+
].map(({ id }) => id)
31+
);
32+
const deprecatedThemeIds = new Set(
33+
[BuiltinThemeProvider.lightTheme, BuiltinThemeProvider.darkTheme].map(
34+
({ id }) => id
35+
)
36+
);
37+
38+
export const lightThemeLabel = nls.localize('arduino/theme/light', 'Light');
39+
export const darkThemeLabel = nls.localize('arduino/theme/dark', 'Dark');
40+
export const hcThemeLabel = nls.localize('arduino/theme/hc', 'High Contrast');
41+
export function userThemeLabel(theme: Theme): string {
42+
return nls.localize('arduino/theme/user', '{0} (user)', theme.label);
43+
}
44+
export function deprecatedThemeLabel(theme: Theme): string {
45+
return nls.localize(
46+
'arduino/theme/deprecated',
47+
'{0} (deprecated)',
48+
theme.label
49+
);
50+
}
51+
52+
export function themeLabelForSettings(theme: Theme): string {
53+
switch (theme.id) {
54+
case ArduinoThemes.light.id:
55+
return lightThemeLabel;
56+
case ArduinoThemes.dark.id:
57+
return darkThemeLabel;
58+
case BuiltinThemeProvider.hcTheme.id:
59+
return hcThemeLabel;
60+
case BuiltinThemeProvider.lightTheme.id: // fall-through
61+
case BuiltinThemeProvider.darkTheme.id:
62+
return deprecatedThemeLabel(theme);
63+
default:
64+
return userThemeLabel(theme);
65+
}
66+
}
67+
68+
export function compatibleBuiltInTheme(theme: Theme): Theme {
69+
switch (theme.type) {
70+
case 'light':
71+
return ArduinoThemes.light;
72+
case 'dark':
73+
return ArduinoThemes.dark;
74+
case 'hc':
75+
return BuiltinThemeProvider.hcTheme;
76+
default: {
77+
console.warn(
78+
`Unhandled theme type: ${theme.type}. Theme ID: ${theme.id}, label: ${theme.label}`
79+
);
80+
return ArduinoThemes.light;
81+
}
82+
}
83+
}
84+
85+
// For tests without DI
86+
interface ThemeProvider {
87+
themes(): Theme[];
88+
currentTheme(): Theme;
89+
}
90+
91+
/**
92+
* Returns with a list of built-in themes officially supported by IDE2 (https://github.com/arduino/arduino-ide/issues/1283).
93+
* The themes in the array follow the following order:
94+
* - built-in themes first (in `Light`, `Dark`, `High Contrast`), // TODO -> High Contrast will be split up to HC Dark and HC Light after the Theia version uplift
95+
* - followed by user installed (VSIX) themes grouped by theme type, then alphabetical order,
96+
* - if the `currentTheme` is either Light (Theia) or Dark (Theia), the last item of the array will be the selected theme with `(deprecated)` suffix.
97+
*/
98+
export function userConfigurableThemes(service: ThemeService): Theme[][];
99+
export function userConfigurableThemes(provider: ThemeProvider): Theme[][];
100+
export function userConfigurableThemes(
101+
serviceOrProvider: ThemeService | ThemeProvider
102+
): Theme[][] {
103+
const provider =
104+
serviceOrProvider instanceof ThemeService
105+
? {
106+
currentTheme: () => serviceOrProvider.getCurrentTheme(),
107+
themes: () => serviceOrProvider.getThemes(),
108+
}
109+
: serviceOrProvider;
110+
const currentTheme = provider.currentTheme();
111+
const allThemes = provider
112+
.themes()
113+
.map((theme) => ({ ...theme, arduinoThemeType: arduinoThemeTypeOf(theme) }))
114+
.filter(
115+
(theme) =>
116+
theme.arduinoThemeType !== 'deprecated' || currentTheme.id === theme.id
117+
)
118+
.sort((left, right) => {
119+
const leftArduinoThemeType = left.arduinoThemeType;
120+
const rightArduinoThemeType = right.arduinoThemeType;
121+
if (leftArduinoThemeType === rightArduinoThemeType) {
122+
const result = themeTypeOrder[left.type] - themeTypeOrder[right.type];
123+
if (result) {
124+
return result;
125+
}
126+
return left.label.localeCompare(right.label); // alphabetical order
127+
}
128+
return (
129+
arduinoThemeTypeOrder[leftArduinoThemeType] -
130+
arduinoThemeTypeOrder[rightArduinoThemeType]
131+
);
132+
});
133+
const builtInThemes: Theme[] = [];
134+
const userThemes: Theme[] = [];
135+
const deprecatedThemes: Theme[] = [];
136+
allThemes.forEach((theme) => {
137+
const { arduinoThemeType } = theme;
138+
switch (arduinoThemeType) {
139+
case 'built-in':
140+
builtInThemes.push(theme);
141+
break;
142+
case 'user':
143+
userThemes.push(theme);
144+
break;
145+
case 'deprecated':
146+
deprecatedThemes.push(theme);
147+
break;
148+
default:
149+
assertUnreachable(arduinoThemeType);
150+
}
151+
});
152+
const groupedThemes: Theme[][] = [];
153+
if (builtInThemes.length) {
154+
groupedThemes.push(builtInThemes);
155+
}
156+
if (userThemes.length) {
157+
groupedThemes.push(userThemes);
158+
}
159+
if (deprecatedThemes.length) {
160+
groupedThemes.push(deprecatedThemes);
161+
}
162+
return groupedThemes;
163+
}
164+
165+
export type ArduinoThemeType = 'built-in' | 'user' | 'deprecated';
166+
const arduinoThemeTypeOrder: Record<ArduinoThemeType, number> = {
167+
'built-in': 0,
168+
user: 1,
169+
deprecated: 2,
170+
};
171+
const themeTypeOrder: Record<ThemeType, number> = {
172+
light: 0,
173+
dark: 1,
174+
hc: 2,
175+
};
176+
177+
export function arduinoThemeTypeOf(theme: Theme | string): ArduinoThemeType {
178+
const themeId = typeof theme === 'string' ? theme : theme.id;
179+
if (builtInThemeIds.has(themeId)) {
180+
return 'built-in';
181+
}
182+
if (deprecatedThemeIds.has(themeId)) {
183+
return 'deprecated';
25184
}
185+
return 'user';
26186
}

0 commit comments

Comments
 (0)