1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-02 22:49:09 +02:00

Desktop: Added support for Menu API for plugins

This commit is contained in:
Laurent Cozic
2020-10-13 12:57:03 +01:00
parent 2caaf8e8c1
commit c648f19693
37 changed files with 5561 additions and 34 deletions

View File

@@ -0,0 +1,26 @@
import { MenuItem, MenuItemLocation } from './api/types';
import ViewController from './ViewController';
export default class MenuController extends ViewController {
constructor(id:string, pluginId:string, store:any, label:string, menuItems: MenuItem[], location:MenuItemLocation) {
super(id, pluginId, store);
this.store.dispatch({
type: 'PLUGIN_VIEW_ADD',
pluginId: pluginId,
view: {
id: this.handle,
type: this.type,
label: label,
menuItems: menuItems,
location: location,
},
});
}
public get type():string {
return 'menu';
}
}

View File

@@ -1,6 +1,7 @@
import Plugin from '../Plugin';
import JoplinViewsDialogs from './JoplinViewsDialogs';
import JoplinViewsMenuItems from './JoplinViewsMenuItems';
import JoplinViewsMenus from './JoplinViewsMenus';
import JoplinViewsToolbarButtons from './JoplinViewsToolbarButtons';
import JoplinViewsPanels from './JoplinViewsPanels';
@@ -18,6 +19,7 @@ export default class JoplinViews {
private dialogs_:JoplinViewsDialogs = null;
private panels_:JoplinViewsPanels = null;
private menuItems_:JoplinViewsMenuItems = null;
private menus_:JoplinViewsMenus = null;
private toolbarButtons_:JoplinViewsToolbarButtons = null;
private implementation_:any = null;
@@ -42,6 +44,11 @@ export default class JoplinViews {
return this.menuItems_;
}
public get menus():JoplinViewsMenus {
if (!this.menus_) this.menus_ = new JoplinViewsMenus(this.plugin, this.store);
return this.menus_;
}
public get toolbarButtons():JoplinViewsToolbarButtons {
if (!this.toolbarButtons_) this.toolbarButtons_ = new JoplinViewsToolbarButtons(this.plugin, this.store);
return this.toolbarButtons_;

View File

@@ -0,0 +1,45 @@
import KeymapService from 'lib/services/KeymapService';
import { MenuItem, MenuItemLocation } from './types';
import MenuController from '../MenuController';
import Plugin from '../Plugin';
import createViewHandle from '../utils/createViewHandle';
/**
* Allows creating menus.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/menu)
*/
export default class JoplinViewsMenus {
private store: any;
private plugin: Plugin;
constructor(plugin: Plugin, store: any) {
this.store = store;
this.plugin = plugin;
}
private registerCommandAccelerators(menuItems:MenuItem[]) {
for (const menuItem of menuItems) {
if (menuItem.accelerator) {
KeymapService.instance().registerCommandAccelerator(menuItem.commandName, menuItem.accelerator);
}
if (menuItem.submenu) {
this.registerCommandAccelerators(menuItem.submenu);
}
}
}
/**
* Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the
* menu as a sub-menu of the application build-in menus.
*/
public async create(label:string, menuItems:MenuItem[], location:MenuItemLocation = MenuItemLocation.Tools) {
const handle = createViewHandle(this.plugin);
const controller = new MenuController(handle, this.plugin.id, this.store, label, menuItems, location);
this.plugin.addViewController(controller);
this.registerCommandAccelerators(menuItems);
}
}

View File

@@ -154,17 +154,9 @@ export interface Script {
}
// =================================================================
// View API types
// Menu types
// =================================================================
export type ButtonId = string;
export interface ButtonSpec {
id: ButtonId,
title?: string,
onClick?():void,
}
export interface CreateMenuItemOptions {
accelerator: string,
}
@@ -179,6 +171,41 @@ export enum MenuItemLocation {
Context = 'context',
}
export interface MenuItem {
/**
* Command that should be associated with the menu item. All menu item should
* have a command associated with them unless they are a sub-menu.
*/
commandName?: string,
/**
* Accelerator associated with the menu item
*/
accelerator?: string,
/**
* Menu items that should appear below this menu item. Allows creating a menu tree.
*/
submenu?: MenuItem[],
/**
* Menu item label. If not specified, the command label will be used instead.
*/
label?: string,
}
// =================================================================
// View API types
// =================================================================
export interface ButtonSpec {
id: ButtonId,
title?: string,
onClick?():void,
}
export type ButtonId = string;
export enum ToolbarButtonLocation {
/**
* This toolbar in the top right corner of the application. It applies to the note as a whole, including its metadata.

View File

@@ -34,6 +34,9 @@ export const defaultState:State = {
};
export const utils = {
// It is best to use viewsByType instead as this method creates new objects
// which might trigger unecessary renders even when plugin and views haven't changed.
viewInfosByType: function(plugins:PluginStates, type:string):ViewInfo[] {
const output:ViewInfo[] = [];
@@ -53,6 +56,22 @@ export const utils = {
return output;
},
viewsByType: function(plugins:PluginStates, type:string):any[] {
const output:any[] = [];
for (const pluginId in plugins) {
const plugin = plugins[pluginId];
for (const viewId in plugin.views) {
const view = plugin.views[viewId];
if (view.type !== type) continue;
output.push(view);
}
}
return output;
},
commandNamesFromViews: function(plugins:PluginStates, toolbarType:string):string[] {
const infos = utils.viewInfosByType(plugins, 'toolbarButton');

View File

@@ -23,7 +23,7 @@ export default function manifestFromObject(o:any):PluginManifest {
name: getString('name', true),
version: getString('version', true),
description: getString('description', false),
homepage_url: getString('homepage_url'),
homepage_url: getString('homepage_url', false),
permissions: permissions,
};