feat: new icons + dispatch based on the theme
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
@ -1,4 +1,5 @@
|
||||
import { NativeImage } from '@theia/core/electron-shared/electron';
|
||||
import { ThemeService } from '@theia/core/lib/browser/theming';
|
||||
import {
|
||||
Disposable,
|
||||
DisposableCollection,
|
||||
@ -9,7 +10,11 @@ import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { SketchesError } from '../../common/protocol';
|
||||
import { ConfigServiceClient } from '../config/config-service-client';
|
||||
import { ArduinoMenus } from '../menu/arduino-menus';
|
||||
import { NativeImageCache } from '../native-image-cache';
|
||||
import {
|
||||
isThemeNativeImage,
|
||||
NativeImageCache,
|
||||
ThemeNativeImage,
|
||||
} from '../native-image-cache';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
import { CloudSketchContribution } from './cloud-contribution';
|
||||
import { CommandRegistry, MenuModelRegistry, Sketch } from './contribution';
|
||||
@ -27,21 +32,34 @@ export class OpenRecentSketch extends CloudSketchContribution {
|
||||
private readonly imageCache: NativeImageCache;
|
||||
@inject(ConfigServiceClient)
|
||||
private readonly configServiceClient: ConfigServiceClient;
|
||||
@inject(ThemeService)
|
||||
private readonly themeService: ThemeService;
|
||||
|
||||
private readonly toDispose = new DisposableCollection();
|
||||
private cloudImage: NativeImage | undefined;
|
||||
private readonly toDisposeBeforeRegister = new DisposableCollection();
|
||||
private readonly toDispose = new DisposableCollection(
|
||||
this.toDisposeBeforeRegister
|
||||
);
|
||||
private cloudImage: NativeImage | ThemeNativeImage;
|
||||
|
||||
override onStart(): void {
|
||||
this.notificationCenter.onRecentSketchesDidChange(({ sketches }) =>
|
||||
this.refreshMenu(sketches)
|
||||
);
|
||||
this.imageCache
|
||||
.getImage('cloud')
|
||||
.then((image) => (this.cloudImage = image));
|
||||
this.toDispose.pushAll([
|
||||
this.notificationCenter.onRecentSketchesDidChange(({ sketches }) =>
|
||||
this.refreshMenu(sketches)
|
||||
),
|
||||
this.themeService.onDidColorThemeChange(() => this.update()),
|
||||
]);
|
||||
}
|
||||
|
||||
onStop(): void {
|
||||
this.toDispose.dispose();
|
||||
}
|
||||
|
||||
override async onReady(): Promise<void> {
|
||||
this.update();
|
||||
this.imageCache.getImage('cloud').then((image) => {
|
||||
this.cloudImage = image;
|
||||
this.update();
|
||||
});
|
||||
}
|
||||
|
||||
override registerMenus(registry: MenuModelRegistry): void {
|
||||
@ -65,7 +83,7 @@ export class OpenRecentSketch extends CloudSketchContribution {
|
||||
|
||||
private register(sketches: Sketch[]): void {
|
||||
const order = 0;
|
||||
this.toDispose.dispose();
|
||||
this.toDisposeBeforeRegister.dispose();
|
||||
for (const sketch of sketches) {
|
||||
const { uri } = sketch;
|
||||
const command = { id: `arduino-open-recent--${uri}` };
|
||||
@ -95,7 +113,7 @@ export class OpenRecentSketch extends CloudSketchContribution {
|
||||
ArduinoMenus.FILE__OPEN_RECENT_SUBMENU,
|
||||
menuAction
|
||||
);
|
||||
this.toDispose.pushAll([
|
||||
this.toDisposeBeforeRegister.pushAll([
|
||||
new DisposableCollection(
|
||||
Disposable.create(() =>
|
||||
this.commandRegistry.unregisterCommand(command)
|
||||
@ -109,13 +127,23 @@ export class OpenRecentSketch extends CloudSketchContribution {
|
||||
}
|
||||
|
||||
private assignImage(sketch: Sketch, menuAction: MenuAction): MenuAction {
|
||||
if (this.cloudImage) {
|
||||
const image = this.nativeImageForTheme();
|
||||
if (image) {
|
||||
const dataDirUri = this.configServiceClient.tryGetDataDirUri();
|
||||
const isCloud = this.createFeatures.isCloud(sketch, dataDirUri);
|
||||
if (isCloud) {
|
||||
Object.assign(menuAction, { nativeImage: this.cloudImage });
|
||||
Object.assign(menuAction, { nativeImage: image });
|
||||
}
|
||||
}
|
||||
return menuAction;
|
||||
}
|
||||
|
||||
private nativeImageForTheme(): NativeImage | undefined {
|
||||
const image = this.cloudImage;
|
||||
if (isThemeNativeImage(image)) {
|
||||
const themeType = this.themeService.getCurrentTheme().type;
|
||||
return themeType === 'light' ? image.light : image.dark;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
@ -12,17 +12,32 @@ import fetch from 'cross-fetch';
|
||||
const nativeImageIdentifierLiterals = ['cloud'] as const;
|
||||
export type NativeImageIdentifier =
|
||||
typeof nativeImageIdentifierLiterals[number];
|
||||
export const nativeImages: Record<NativeImageIdentifier, string> = {
|
||||
cloud: 'cloud.png',
|
||||
export const nativeImages: Record<
|
||||
NativeImageIdentifier,
|
||||
string | { light: string; dark: string }
|
||||
> = {
|
||||
cloud: { light: 'cloud-light.png', dark: 'cloud-dark.png' },
|
||||
};
|
||||
|
||||
export interface ThemeNativeImage {
|
||||
readonly light: NativeImage;
|
||||
readonly dark: NativeImage;
|
||||
}
|
||||
|
||||
export function isThemeNativeImage(arg: unknown): arg is ThemeNativeImage {
|
||||
return (
|
||||
typeof arg === 'object' &&
|
||||
(<ThemeNativeImage>arg).light !== undefined &&
|
||||
(<ThemeNativeImage>arg).dark !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
type Image = NativeImage | ThemeNativeImage;
|
||||
|
||||
@injectable()
|
||||
export class NativeImageCache implements FrontendApplicationContribution {
|
||||
private readonly cache = new Map<NativeImageIdentifier, NativeImage>();
|
||||
private readonly loading = new Map<
|
||||
NativeImageIdentifier,
|
||||
Promise<NativeImage>
|
||||
>();
|
||||
private readonly cache = new Map<NativeImageIdentifier, Image>();
|
||||
private readonly loading = new Map<NativeImageIdentifier, Promise<Image>>();
|
||||
|
||||
onStart(): void {
|
||||
Object.keys(nativeImages).forEach((identifier: NativeImageIdentifier) =>
|
||||
@ -30,21 +45,21 @@ export class NativeImageCache implements FrontendApplicationContribution {
|
||||
);
|
||||
}
|
||||
|
||||
tryGetImage(identifier: NativeImageIdentifier): NativeImage | undefined {
|
||||
tryGetImage(identifier: NativeImageIdentifier): Image | undefined {
|
||||
return this.cache.get(identifier);
|
||||
}
|
||||
|
||||
async getImage(identifier: NativeImageIdentifier): Promise<NativeImage> {
|
||||
async getImage(identifier: NativeImageIdentifier): Promise<Image> {
|
||||
const image = this.cache.get(identifier);
|
||||
if (image) {
|
||||
return image;
|
||||
}
|
||||
let loading = this.loading.get(identifier);
|
||||
if (!loading) {
|
||||
const deferred = new Deferred<NativeImage>();
|
||||
const deferred = new Deferred<Image>();
|
||||
loading = deferred.promise;
|
||||
this.loading.set(identifier, loading);
|
||||
this.fetchIconData(identifier).then(
|
||||
this.fetchImage(identifier).then(
|
||||
(image) => {
|
||||
if (!this.cache.has(identifier)) {
|
||||
this.cache.set(identifier, image);
|
||||
@ -61,10 +76,20 @@ export class NativeImageCache implements FrontendApplicationContribution {
|
||||
return loading;
|
||||
}
|
||||
|
||||
private async fetchIconData(
|
||||
identifier: NativeImageIdentifier
|
||||
): Promise<NativeImage> {
|
||||
const path = `nativeImage/${nativeImages[identifier]}`;
|
||||
private async fetchImage(identifier: NativeImageIdentifier): Promise<Image> {
|
||||
const value = nativeImages[identifier];
|
||||
if (typeof value === 'string') {
|
||||
return this.fetchIconData(value);
|
||||
}
|
||||
const [light, dark] = await Promise.all([
|
||||
this.fetchIconData(value.light),
|
||||
this.fetchIconData(value.dark),
|
||||
]);
|
||||
return { light, dark };
|
||||
}
|
||||
|
||||
private async fetchIconData(filename: string): Promise<NativeImage> {
|
||||
const path = `nativeImage/${filename}`;
|
||||
const endpoint = new Endpoint({ path }).getRestUrl().toString();
|
||||
const response = await fetch(endpoint);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
|
BIN
arduino-ide-extension/src/node/static/icons/cloud-dark.png
Normal file
After Width: | Height: | Size: 319 B |
BIN
arduino-ide-extension/src/node/static/icons/cloud-dark@2x.png
Normal file
After Width: | Height: | Size: 586 B |
BIN
arduino-ide-extension/src/node/static/icons/cloud-dark@3x.png
Normal file
After Width: | Height: | Size: 834 B |
BIN
arduino-ide-extension/src/node/static/icons/cloud-light.png
Normal file
After Width: | Height: | Size: 291 B |
BIN
arduino-ide-extension/src/node/static/icons/cloud-light@2x.png
Normal file
After Width: | Height: | Size: 509 B |
BIN
arduino-ide-extension/src/node/static/icons/cloud-light@3x.png
Normal file
After Width: | Height: | Size: 686 B |
Before Width: | Height: | Size: 9.2 KiB |