import * as React from '@theia/core/shared/react'; import { injectable, inject, postConstruct, } from '@theia/core/shared/inversify'; import { Widget } from '@theia/core/shared/@phosphor/widgets'; import { Message } from '@theia/core/shared/@phosphor/messaging'; import { DialogError, DialogProps, ReactWidget } from '@theia/core/lib/browser'; import { Settings, SettingsService } from './settings'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { FileDialogService } from '@theia/filesystem/lib/browser/file-dialog/file-dialog-service'; import { nls } from '@theia/core/lib/common'; import { SettingsComponent } from './settings-component'; import { AsyncLocalizationProvider } from '@theia/core/lib/common/i18n/localization'; import { AdditionalUrls } from '../../../common/protocol'; import { AbstractDialog } from '../../theia/dialogs/dialogs'; import { ThemeService } from '@theia/core/lib/browser/theming'; @injectable() export class SettingsWidget extends ReactWidget { @inject(SettingsService) protected readonly settingsService: SettingsService; @inject(FileService) protected readonly fileService: FileService; @inject(FileDialogService) protected readonly fileDialogService: FileDialogService; @inject(WindowService) protected readonly windowService: WindowService; @inject(AsyncLocalizationProvider) protected readonly localizationProvider: AsyncLocalizationProvider; protected render(): React.ReactNode { return ( <SettingsComponent settingsService={this.settingsService} fileService={this.fileService} fileDialogService={this.fileDialogService} windowService={this.windowService} localizationProvider={this.localizationProvider} /> ); } } @injectable() export class SettingsDialogProps extends DialogProps {} @injectable() export class SettingsDialog extends AbstractDialog<Promise<Settings>> { @inject(SettingsService) protected readonly settingsService: SettingsService; @inject(SettingsWidget) protected readonly widget: SettingsWidget; constructor( @inject(SettingsDialogProps) protected override readonly props: SettingsDialogProps ) { super(props); this.node.id = 'arduino-settings-dialog-container'; this.contentNode.classList.add('arduino-settings-dialog'); this.appendCloseButton( nls.localize('vscode/issueMainService/cancel', 'Cancel') ); this.appendAcceptButton(nls.localize('vscode/issueMainService/ok', 'OK')); } @postConstruct() protected init(): void { this.toDispose.push( this.settingsService.onDidChange(this.validate.bind(this)) ); } protected override async isValid( settings: Promise<Settings> ): Promise<DialogError> { const result = await this.settingsService.validate(settings); if (typeof result === 'string') { return result; } return ''; } get value(): Promise<Settings> { return this.settingsService.settings(); } protected override onAfterAttach(msg: Message): void { if (this.widget.isAttached) { Widget.detach(this.widget); } Widget.attach(this.widget, this.contentNode); this.toDisposeOnDetach.push( this.settingsService.onDidChange(() => this.update()) ); super.onAfterAttach(msg); this.update(); } protected override onUpdateRequest(msg: Message): void { super.onUpdateRequest(msg); this.widget.update(); } protected override onActivateRequest(msg: Message): void { super.onActivateRequest(msg); // calling settingsService.reset() in order to reload the settings from the preferenceService // and update the UI including changes triggered from the command palette this.settingsService.reset(); this.widget.activate(); } override async open(): Promise<Promise<Settings> | undefined> { const themeIdBeforeOpen = ThemeService.get().getCurrentTheme().id; const result = await super.open(); if (!result) { if (ThemeService.get().getCurrentTheme().id !== themeIdBeforeOpen) { ThemeService.get().setCurrentTheme(themeIdBeforeOpen); } } return result; } } export class AdditionalUrlsDialog extends AbstractDialog<string[]> { protected readonly textArea: HTMLTextAreaElement; constructor(urls: string[], windowService: WindowService) { super({ title: nls.localize( 'arduino/preferences/additionalManagerURLs', 'Additional Boards Manager URLs' ), }); this.contentNode.classList.add('additional-urls-dialog'); const description = document.createElement('div'); description.textContent = nls.localize( 'arduino/preferences/enterAdditionalURLs', 'Enter additional URLs, one for each row' ); description.style.marginBottom = '5px'; this.contentNode.appendChild(description); this.textArea = document.createElement('textarea'); this.textArea.className = 'theia-input'; this.textArea.setAttribute('style', 'flex: 0;'); this.textArea.value = urls .filter((url) => url.trim()) .filter((url) => !!url) .join('\n'); this.textArea.wrap = 'soft'; this.textArea.cols = 90; this.textArea.rows = 5; this.contentNode.appendChild(this.textArea); const anchor = document.createElement('div'); anchor.classList.add('link'); anchor.textContent = nls.localize( 'arduino/preferences/unofficialBoardSupport', 'Click for a list of unofficial board support URLs' ); anchor.style.marginTop = '5px'; anchor.style.cursor = 'pointer'; this.addEventListener(anchor, 'click', () => windowService.openNewWindow( 'https://github.com/arduino/Arduino/wiki/Unofficial-list-of-3rd-party-boards-support-urls', { external: true } ) ); this.contentNode.appendChild(anchor); this.appendAcceptButton(nls.localize('vscode/issueMainService/ok', 'OK')); this.appendCloseButton( nls.localize('vscode/issueMainService/cancel', 'Cancel') ); } get value(): string[] { return AdditionalUrls.parse(this.textArea.value, 'newline'); } protected override onAfterAttach(message: Message): void { super.onAfterAttach(message); this.addUpdateListener(this.textArea, 'input'); } protected override onActivateRequest(message: Message): void { super.onActivateRequest(message); this.textArea.focus(); } protected override handleEnter(event: KeyboardEvent): boolean | void { if (event.target instanceof HTMLInputElement) { return super.handleEnter(event); } return false; } }