主要是 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