You've already forked joplin
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:
26
ReactNativeClient/lib/services/plugins/MenuController.ts
Normal file
26
ReactNativeClient/lib/services/plugins/MenuController.ts
Normal 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';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user