diff --git a/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts b/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts index c17fd79a..dfc02b7a 100644 --- a/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts +++ b/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts @@ -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 { 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; + } } diff --git a/arduino-ide-extension/src/browser/native-image-cache.ts b/arduino-ide-extension/src/browser/native-image-cache.ts index 268a96ae..13948fee 100644 --- a/arduino-ide-extension/src/browser/native-image-cache.ts +++ b/arduino-ide-extension/src/browser/native-image-cache.ts @@ -12,17 +12,32 @@ import fetch from 'cross-fetch'; const nativeImageIdentifierLiterals = ['cloud'] as const; export type NativeImageIdentifier = typeof nativeImageIdentifierLiterals[number]; -export const nativeImages: Record = { - 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' && + (arg).light !== undefined && + (arg).dark !== undefined + ); +} + +type Image = NativeImage | ThemeNativeImage; + @injectable() export class NativeImageCache implements FrontendApplicationContribution { - private readonly cache = new Map(); - private readonly loading = new Map< - NativeImageIdentifier, - Promise - >(); + private readonly cache = new Map(); + private readonly loading = new Map>(); 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 { + async getImage(identifier: NativeImageIdentifier): Promise { const image = this.cache.get(identifier); if (image) { return image; } let loading = this.loading.get(identifier); if (!loading) { - const deferred = new Deferred(); + const deferred = new Deferred(); 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 { - const path = `nativeImage/${nativeImages[identifier]}`; + private async fetchImage(identifier: NativeImageIdentifier): Promise { + 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 { + const path = `nativeImage/${filename}`; const endpoint = new Endpoint({ path }).getRestUrl().toString(); const response = await fetch(endpoint); const arrayBuffer = await response.arrayBuffer(); diff --git a/arduino-ide-extension/src/node/static/icons/cloud-dark.png b/arduino-ide-extension/src/node/static/icons/cloud-dark.png new file mode 100644 index 00000000..464646dd Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-dark.png differ diff --git a/arduino-ide-extension/src/node/static/icons/cloud-dark@2x.png b/arduino-ide-extension/src/node/static/icons/cloud-dark@2x.png new file mode 100644 index 00000000..14af1ce5 Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-dark@2x.png differ diff --git a/arduino-ide-extension/src/node/static/icons/cloud-dark@3x.png b/arduino-ide-extension/src/node/static/icons/cloud-dark@3x.png new file mode 100644 index 00000000..4d857c1d Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-dark@3x.png differ diff --git a/arduino-ide-extension/src/node/static/icons/cloud-light.png b/arduino-ide-extension/src/node/static/icons/cloud-light.png new file mode 100644 index 00000000..51bf2be8 Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-light.png differ diff --git a/arduino-ide-extension/src/node/static/icons/cloud-light@2x.png b/arduino-ide-extension/src/node/static/icons/cloud-light@2x.png new file mode 100644 index 00000000..d42259fb Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-light@2x.png differ diff --git a/arduino-ide-extension/src/node/static/icons/cloud-light@3x.png b/arduino-ide-extension/src/node/static/icons/cloud-light@3x.png new file mode 100644 index 00000000..777688e0 Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-light@3x.png differ diff --git a/arduino-ide-extension/src/node/static/icons/cloud.png b/arduino-ide-extension/src/node/static/icons/cloud.png deleted file mode 100644 index fb4426ce..00000000 Binary files a/arduino-ide-extension/src/node/static/icons/cloud.png and /dev/null differ