主要是 theia extension 开发过程中会遇到的代码
没有相关经验的同学,可以先了解下 InversifyJS, 再直接跑下官方提供的拓展例子,熟悉下基本的开发
命令(Command)
实现 CommandContribution 接口,如果传入的 Command 中带 label 字段的话,则该命令也会显示在命令面板中
@injectable()
export class TestCommandContribution implements CommandContribution {
    registerCommands(commands: CommandRegistry): void {
        commands.registerCommand(SayHello, {
            execute: () => {
                 this.messageService.info('hello hello');    // 提示框
            }
        });
    }
}
// fontend-module.ts
bind(CommandContribution).to(LdpCommandContribution).inSingletonScope();
复制代码
菜单(Menu)
实现 MenuContribution 接口,点击则会执行 Command.id 对应的命令
const SayHello: Command = {
    id: 'say:hello',    // 对应的 command id
    label: 'Say Hello',    // 菜单上显示的文字
    category: 'test'   // 类别
};
@injectable()
export class TestMenuContribution implements MenuContribution {
    registerMenus(menus: MenuModelRegistry): void {
        menus.registerMenuAction(CommonMenus.FILE, {
            commandId: SayHello.id,
            label: SayHello.label,
        });
    }
}
// fontend-module.ts
bind(MenuContribution).to(LdpMenuContribution).inRequestScope();
复制代码
自定义视图
- 实现 BaseWidget或ReactWidget接口,自定义视图
import * as React from 'react';
import { injectable, postConstruct, inject } from 'inversify';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
@injectable()
export class WidgetWidget extends ReactWidget{
    static readonly ID = 'test:widget';
    static readonly LABEL = 'Test Widget';
    static readonly TOGGLE_COMMADND_ID = 'test.widget';
    @postConstruct()
    protected async init(): Promise<void> {
        // 初始化
        this.id = WidgetWidget.ID;
        this.title.label = WidgetWidget.LABEL;
        this.title.caption = WidgetWidget.LABEL;
        this.title.closable = true;
        this.title.iconClass = 'fa fa-window-maximize'; // example widget icon.
        this.update();    // 更新视图
    }
    protected render(): React.ReactNode {
        return (
            <div>自定义视图</div>
        )
    }
}
复制代码- 继承抽象类 AbstractViewContribution,将视图添加到对应的DOM中
这个类中除了实现了 widget 绑定方法外,还包括命令、菜单、快捷键等方法的绑定
import { injectable, inject } from 'inversify';
import { Command, CommandRegistry, MenuModelRegistry, MessageService } from '@theia/core';
import { WidgetWidget } from './widget-widget';
import { AbstractViewContribution, CommonMenus } from '@theia/core/lib/browser';
const TestCommand: Command = {
    id: 'test',
    label: 'test'
}
@injectable()
export class WidgetContribution extends AbstractViewContribution<WidgetWidget> {=
    constructor() {
        super({
            widgetId: WidgetWidget.ID,
            widgetName: WidgetWidget.LABEL,
            defaultWidgetOptions: { area: 'main'},
            toggleCommandId: WidgetWidget.ID
        });
    }
    // 通过改写 registerCommands 方法,绑定其他命令
    registerCommands(commands: CommandRegistry) {
        super.registerCommands(commands);
        commands.registerCommand(TestCommand, {
            execute: () => console.log(123)
        })
    }
    // 通过改写 registerMenus 方法,绑定其他菜单项
    registerMenus(menus: MenuModelRegistry) {
        super.registerMenus(menus);
        menus.registerMenuAction(CommonMenus.VIEW_VIEWS, {
            commandId: TestCommand.id,
            label: TestCommand.label
        })
    }
}
复制代码- 绑定到容器中
import { ContainerModule } from 'inversify';
import { WidgetWidget } from './widget-widget';
import { WidgetContribution } from './widget-contribution';
import { bindViewContribution, FrontendApplicationContribution, WidgetFactory } from '@theia/core/lib/browser';
export default new ContainerModule(bind => {
    bindViewContribution(bind, WidgetContribution);
    bind(FrontendApplicationContribution).toService(WidgetContribution);
    bind(WidgetWidget).toSelf();
    bind(WidgetFactory).toDynamicValue(ctx => ({
        id: WidgetWidget.ID,
        createWidget: () => ctx.container.get<WidgetWidget>(WidgetWidget)
    })).inSingletonScope();
});
复制代码
自定义编辑器显示
也就是拦截 Theia 的默认编辑器
- 实现 OpenHandler接口
WidgetOpenHandler 实际也是实现了 OpenerHandler 接口,当我们有其他定制化需求的时候,就可以自己实现 OpenHandler 接口,具体可以仿照 WidgetOpenHandler 实现,不展开说明
- 继承抽象类 WidgetOpenHandler
// widget.ts
import * as React from 'react';
import { injectable, postConstruct } from 'inversify';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
@injectable()
export class CustomWidget extends ReactWidget {
    static readonly ID = 'test:widget';
    static readonly LABEL = 'Custom Editor';
    protected text: string;
    @postConstruct()
    protected async init(): Promise<void> {
        // 初始化
        this.id = WidgetWidget.ID;
        this.title.label = WidgetWidget.LABEL;
        this.title.caption = WidgetWidget.LABEL;
        this.title.closable = true;
        this.title.iconClass = 'fa fa-window-maximize'; // example widget icon.
        this.update();    // 更新视图
    }
    setText(text: string) {
        this.text = text;
    }
    // 根据接收到的参数显示
    protected render(): React.ReactNode {
        return (
            <React.Fragment>
                <div>自定义编辑器</div>
                <div>{this.text}</div>
            </React.Fragment>
        )
    }
}
复制代码// 继承 WidgetOpenerHandler
import { injectable } from 'inversify';
import { WidgetOpenHandler, WidgetOpenerOptions } from '@theia/core/lib/browser';
import { CustomWidget } from './widget-widget';
import URI from '@theia/core/lib/common/uri';
export interface CustomWidgetOptions {
    text: string;
}
@injectable()
export class CustomOpenHandler extends WidgetOpenHandler<CustomWidget>  {
    readonly id = CustomWidget.ID;
    
    canHandle(uri: URI): number {
        console.log(uri.path.ext);
        if(uri.path.ext === '.json') {
            return 500;
        }
        return 0;
    }
    // 这里可以设置传递给 widget 的参数
    createWidgetOptions(uri: URI, options?: WidgetOpenerOptions): CustomWidgetOptions{
        return { 
            text: '这是 json 文件'
        };
    }
}
复制代码// fontend-module.ts
bind(OpenHandler).toService(CustomOpenHandler);
bind(CustomOpenHandler).toSelf().inSingletonScope();    
bind(CustomWidget).toSelf();
bind(WidgetFactory).toDynamicValue(ctx => ({
    id: CustomWidget.ID,
    createWidget: (options: CustomWidgetOptions) => {
        const widget = ctx.container.get<CustomWidget>(CustomWidget);
        console.log(options);
        widget.setText(options.text);
        return widget;
    }
})).inSingletonScope();
复制代码当打开 json 文件的时候,就会显示我们自定义的编辑器

弹窗
框架自带
MessageService(右下角通知提示框)
@inject(MessageService)
protected messageService: MessageService;
this.messageService.info('hello hello');
复制代码
对话框
commands.registerCommand(DialogCommand, {
    execute: async () => {
        const confirmed = await new ConfirmDialog({
            title: '这是个确认框',
            msg: '确认执行吗?',
            ok: '确认',
            cancel: '取消'
        }).open();
        console.log('确认了吗', confirmed);
    }
});
复制代码
框架还提供了 SingleTextInputDialog 等其他对话框,具体可看 packages/core/src/browser/dialogs.ts 这个目录
FileDialogService(文件/目录选择框)
- showOpenDialog:选择文件/目录,返回选择的- URI
commands.registerCommand(FileDialog, {
    execute: async () => {
       const uri = await this.fileDialogService.showOpenDialog({
           title: '选择目录',
           canSelectFiles: false,
           canSelectFolders: true,
           openLabel: '选择',
       });
       console.log('选择路径', uri);
    }
});
复制代码
- showSaveDialog:保存文件对话框
commands.registerCommand(FileDialog, {
    execute: async () => {
       const uri = await this.fileDialogService.showSaveDialog({
           title: '选择保存目录',
           saveLabel: '保存'
       });
       console.log('保存路径', uri);
    }
});
复制代码
自定义对话框
实现 AbstractDialog 或 ReactDialog 接口 (具体可参考 packages/core/src/browser/dialogs.ts 其他 Dialog 的实现)
// customDialog.tsx
import * as React from 'react';
import { inject, injectable } from 'inversify';
import { ReactDialog } from '@theia/core/lib/browser/dialogs/react-dialog';
import { DialogProps } from '@theia/core/lib/browser/dialogs';
// 定义入参
@injectable()
export class CustomDialogProps extends DialogProps {
    readonly text: string;
    readonly okValue: string;
    readonly cancelValue: string;
}
// 定义返回
interface CustomDialogValue {
    text: string;
}
@injectable()
export class CustomDialog extends ReactDialog<CustomDialogValue> {
    protected readonly text: string;
    constructor(
        @inject(CustomDialogProps) protected readonly props: CustomDialogProps
    ) {
        super(props);
        const { text, okValue, cancelValue } = this.props;
        this.text = text;
        this.appendCloseButton(cancelValue);
        this.appendAcceptButton(okValue);
    }
    protected render(): React.ReactNode {
        return (
            <div>
                {this.text}
            </div>
        );
    }
    get value(): CustomDialogValue {
        return {
            text: this.text
        }
    }
}
复制代码// fontend-module.ts
bind(CustomDialogProps).toSelf();
bind(CustomDialog).toSelf();
复制代码commands.registerCommand(SayHello, {
    execute: async () => {
       const text = await new CustomDialog({
           title: '测试对话框',
           text: '测试',
           okValue: '保存',
           cancelValue: '取消'
       }).open();
        console.log('返回文字', text);
    }
});
复制代码
SourceSaveable
TreeWidget
持续更新中
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
    
























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)
