-
-
Notifications
You must be signed in to change notification settings - Fork 409
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: icon for cloud sketch in File > Open Recent
Ref: #1826 Signed-off-by: Akos Kitta <[email protected]>
- Loading branch information
Akos Kitta
committed
Feb 8, 2023
1 parent
9687fc6
commit 42f6237
Showing
7 changed files
with
238 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { | ||
NativeImage, | ||
nativeImage, | ||
Size, | ||
} from '@theia/core/electron-shared/electron'; | ||
import { Endpoint } from '@theia/core/lib/browser/endpoint'; | ||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'; | ||
import { Deferred } from '@theia/core/lib/common/promise-util'; | ||
import { injectable } from '@theia/core/shared/inversify'; | ||
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', | ||
}; | ||
|
||
@injectable() | ||
export class NativeImageCache implements FrontendApplicationContribution { | ||
private readonly cache = new Map<NativeImageIdentifier, NativeImage>(); | ||
private readonly loading = new Map< | ||
NativeImageIdentifier, | ||
Promise<NativeImage> | ||
>(); | ||
|
||
onStart(): void { | ||
Object.keys(nativeImages).forEach((identifier: NativeImageIdentifier) => | ||
this.getImage(identifier) | ||
); | ||
} | ||
|
||
tryGetImage(identifier: NativeImageIdentifier): NativeImage | undefined { | ||
return this.cache.get(identifier); | ||
} | ||
|
||
async getImage(identifier: NativeImageIdentifier): Promise<NativeImage> { | ||
const image = this.cache.get(identifier); | ||
if (image) { | ||
return image; | ||
} | ||
let loading = this.loading.get(identifier); | ||
if (!loading) { | ||
const deferred = new Deferred<NativeImage>(); | ||
loading = deferred.promise; | ||
this.loading.set(identifier, loading); | ||
this.fetchIconData(identifier).then( | ||
(image) => { | ||
if (!this.cache.has(identifier)) { | ||
this.cache.set(identifier, image); | ||
} | ||
this.loading.delete(identifier); | ||
deferred.resolve(image); | ||
}, | ||
(err) => { | ||
this.loading.delete(identifier); | ||
deferred.reject(err); | ||
} | ||
); | ||
} | ||
return loading; | ||
} | ||
|
||
private async fetchIconData( | ||
identifier: NativeImageIdentifier | ||
): Promise<NativeImage> { | ||
const path = `nativeImage/${nativeImages[identifier]}`; | ||
const endpoint = new Endpoint({ path }).getRestUrl().toString(); | ||
const response = await fetch(endpoint); | ||
const arrayBuffer = await response.arrayBuffer(); | ||
const view = new Uint8Array(arrayBuffer); | ||
const buffer = Buffer.alloc(arrayBuffer.byteLength); | ||
buffer.forEach((_, index) => (buffer[index] = view[index])); | ||
const image = nativeImage.createFromBuffer(buffer); | ||
return this.maybeResize(image); | ||
} | ||
|
||
private maybeResize(image: NativeImage): NativeImage { | ||
const currentSize = image.getSize(); | ||
if (sizeEquals(currentSize, preferredSize)) { | ||
return image; | ||
} | ||
return image.resize(preferredSize); | ||
} | ||
} | ||
|
||
const pixel = 16; | ||
const preferredSize: Size = { height: pixel, width: pixel }; | ||
function sizeEquals(left: Size, right: Size): boolean { | ||
return left.height === right.height && left.width === right.width; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
arduino-ide-extension/src/node/native-image-data-provider.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { Deferred } from '@theia/core/lib/common/promise-util'; | ||
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application'; | ||
import { Application } from '@theia/core/shared/express'; | ||
import { injectable } from '@theia/core/shared/inversify'; | ||
import { promises as fs } from 'fs'; | ||
import { join } from 'path'; | ||
import { ErrnoException } from './utils/errors'; | ||
|
||
@injectable() | ||
export class NativeImageDataProvider implements BackendApplicationContribution { | ||
private readonly rootPath = join(__dirname, '../../src/node/static/icons'); | ||
private readonly dataCache = new Map<string, Promise<Buffer | undefined>>(); | ||
|
||
onStart(): void { | ||
console.log(`Serving native images from ${this.rootPath}`); | ||
} | ||
|
||
configure(app: Application): void { | ||
app.get('/nativeImage/:filename', async (req, resp) => { | ||
const filename = req.params.filename; | ||
if (!filename) { | ||
resp.status(400).send('Bad Request'); | ||
return; | ||
} | ||
try { | ||
const data = await this.getOrCreateData(filename); | ||
if (!data) { | ||
resp.status(404).send('Not found'); | ||
return; | ||
} | ||
resp.send(data); | ||
} catch (err) { | ||
resp.status(500).send(err instanceof Error ? err.message : String(err)); | ||
} | ||
}); | ||
} | ||
|
||
private async getOrCreateData(filename: string): Promise<Buffer | undefined> { | ||
let data = this.dataCache.get(filename); | ||
if (!data) { | ||
const deferred = new Deferred<Buffer | undefined>(); | ||
data = deferred.promise; | ||
this.dataCache.set(filename, data); | ||
const path = join(this.rootPath, filename); | ||
fs.readFile(path).then( | ||
(buffer) => deferred.resolve(buffer), | ||
(err) => { | ||
if (ErrnoException.isENOENT(err)) { | ||
console.error(`File not found: ${path}`); | ||
deferred.resolve(undefined); | ||
} else { | ||
console.error(`Failed to load file: ${path}`, err); | ||
this.dataCache.delete(filename); | ||
deferred.reject(err); | ||
} | ||
} | ||
); | ||
} | ||
return data; | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.