Skip to content

Commit

Permalink
add: custom right-click menu
Browse files Browse the repository at this point in the history
  • Loading branch information
windingwind committed Sep 14, 2023
1 parent 73fead2 commit 17d1765
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 20 deletions.
5 changes: 5 additions & 0 deletions addon/locale/en-US/addon.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ prefs-rule-operation = Operation
prefs-rule-data = Data
prefs-rule-shortcut = Shortcut
prefs-rule-enabled = Enabled
prefs-rule-menu = Menu Label
prefs-rule-event-none = None
prefs-rule-event-createItem = Create Item
Expand All @@ -26,3 +27,7 @@ prefs-rule-edit-save = Save
prefs-rule-edit-cancel = Cancel
prefs-rule-edit-shortcut-empty = No Shortcut
prefs-rule-edit-shortcut-placeholder = Press to record shortcut
prefs-rule-edit-menu-placeholder = Leave empty to hide in menu
menupopup-label = Trigger Action
menupopup-placeholder = No actions
5 changes: 5 additions & 0 deletions addon/locale/zh-CN/addon.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ prefs-rule-operation = 操作
prefs-rule-data = 数据
prefs-rule-shortcut = 快捷键
prefs-rule-enabled = 启用
prefs-rule-menu = 菜单项
prefs-rule-event-none =
prefs-rule-event-createItem = 新建条目
Expand All @@ -26,3 +27,7 @@ prefs-rule-edit-save = 保存
prefs-rule-edit-cancel = 取消
prefs-rule-edit-shortcut-empty = 无快捷键
prefs-rule-edit-shortcut-placeholder = 按下键盘以记录快捷键
prefs-rule-edit-menu-placeholder = 若为空则不显示菜单项
menupopup-label = 触发动作
menupopup-placeholder = 无可用动作
13 changes: 11 additions & 2 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { dispatchRuleEvents, dispatchRuleShortcuts } from "./modules/events";
import {
dispatchRuleEvents,
dispatchRuleMenu,
dispatchRuleShortcuts,
} from "./modules/events";
import { ClipboardHelper } from "zotero-plugin-toolkit/dist/helpers/clipboard";

const utils = {
ClipboardHelper,
};

export default { dispatchRuleEvents, dispatchRuleShortcuts, utils };
export default {
dispatchRuleEvents,
dispatchRuleShortcuts,
dispatchRuleMenu,
utils,
};
15 changes: 13 additions & 2 deletions src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { initPrefPane } from "./modules/preferenceWindow";
import { initRules } from "./utils/rules";
import { initNotifierObserver } from "./modules/notify";
import { initShortcuts } from "./modules/shortcuts";
import { initMenu } from "./modules/menu";
import { buildItemMenu, initMenu } from "./modules/menu";

async function onStartup() {
await Promise.all([
Expand All @@ -31,7 +31,7 @@ async function onStartup() {

async function onMainWindowLoad(win: Window): Promise<void> {
initShortcuts(win);
// initMenu();
initMenu();
}

async function onMainWindowUnload(win: Window): Promise<void> {}
Expand Down Expand Up @@ -59,6 +59,16 @@ async function onPrefsEvent(type: string, data: { [key: string]: any }) {
}
}

async function onMenuEvent(type: "showing", data: { [key: string]: any }) {
switch (type) {
case "showing":
buildItemMenu(data.window);
break;
default:
return;
}
}

// Add your hooks here. For element click, etc.
// Keep in mind hooks only do dispatch. Don't add code that does real jobs in hooks.
// Otherwise the code would be hard to read and maintian.
Expand All @@ -69,4 +79,5 @@ export default {
onMainWindowLoad,
onMainWindowUnload,
onPrefsEvent,
onMenuEvent,
};
14 changes: 11 additions & 3 deletions src/modules/events.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TagEventTypes, TagRuleData, applyRule } from "../utils/rules";
import { KeyModifier } from "../utils/shorcut";

export { dispatchRuleEvents, dispatchRuleShortcuts };
export { dispatchRuleEvents, dispatchRuleShortcuts, dispatchRuleMenu };

async function dispatchRuleEvents(eventType: TagEventTypes, data: TagRuleData) {
const rules = getRulesByEvent(eventType);
Expand All @@ -12,7 +12,7 @@ async function dispatchRuleEvents(eventType: TagEventTypes, data: TagRuleData) {

function getRulesByEvent(event: TagEventTypes) {
return Array.from(addon.data.rules.data.values()).filter(
(rule) => rule.event === event && rule.enabled,
(rule) => rule.event === event && rule.enabled
);
}

Expand All @@ -28,6 +28,14 @@ function getRulesByShortcuts(shortcut: KeyModifier) {
(rule) =>
rule.enabled &&
rule.shortcut &&
new KeyModifier(rule.shortcut).equals(shortcut),
new KeyModifier(rule.shortcut).equals(shortcut)
);
}

async function dispatchRuleMenu(key: string, data: TagRuleData) {
const rule = addon.data.rules.data.get(key);
if (!rule) {
return;
}
await applyRule(rule, data);
}
83 changes: 80 additions & 3 deletions src/modules/menu.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,89 @@
import { TagElementProps } from "zotero-plugin-toolkit/dist/tools/ui";
import { config } from "../../package.json";
import { getString } from "../utils/locale";

export { initMenu };
export { initMenu, buildItemMenu };

function initMenu() {
ztoolkit.Menu.register("item", {
tag: "menuitem",
label: getString("menuItem.exportNote"),
tag: "menu",
popupId: `${config.addonRef}-itemPopup`,
label: getString("menupopup-label"),
icon: `chrome://${config.addonRef}/content/icons/favicon.png`,
onpopupshowing: `Zotero.${config.addonInstance}.hooks.onMenuEvent("showing", { window })`,
children: [
{
tag: "menuitem",
label: getString("menupopup-placeholder"),
disabled: true,
},
],
});
}

function buildItemMenu(win: Window) {
const doc = win.document;
const popup = doc.querySelector(
`#${config.addonRef}-itemPopup`
) as XUL.MenuPopup;
// Remove all children in popup
while (popup.firstChild) {
popup.removeChild(popup.firstChild);
}
// Add new children
let elemProp: TagElementProps;
const enabledActions = getRulesByMenu();
if (enabledActions.length === 0) {
elemProp = {
tag: "menuitem",
properties: {
label: getString("menupopup-placeholder"),
disabled: true,
},
};
} else {
ztoolkit.UI.appendElement(
{
tag: "fragment",
children: enabledActions.map((rule) => {
return {
tag: "menuitem",
properties: {
label: rule.menu,
},
listeners: [
{
type: "command",
listener: (event) => {
triggerMenuCommand(rule.key);
},
},
],
};
}),
},
popup
);
}
}

function getRulesByMenu() {
return Array.from(addon.data.rules.data.keys())
.map((key) => {
const rule = addon.data.rules.data.get(key);
if (rule?.menu && rule?.enabled) {
return { key, menu: rule.menu };
}
return null;
})
.filter((rule) => rule) as { key: string; menu: string }[];
}

async function triggerMenuCommand(key: string) {
const items = Zotero.getActiveZoteroPane().getSelectedItems();
for (const item of items) {
await addon.api.dispatchRuleMenu(key, {
itemID: item.id,
});
}
}
41 changes: 35 additions & 6 deletions src/modules/preferenceWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ async function initUI() {
if (!isWindowAlive(addon.data.prefs.window)) return;
updateCachedRuleKeys();
addon.data.prefs.tableHelper = new ztoolkit.VirtualizedTable(
addon.data.prefs.window!,
addon.data.prefs.window!
)
.setContainerId(`${config.addonRef}-table-container`)
.setProp({
Expand All @@ -53,12 +53,16 @@ async function initUI() {
dataKey: "shortcut",
label: getString("prefs-rule-shortcut"),
},
{
dataKey: "menu",
label: getString("prefs-rule-menu"),
},
{
dataKey: "enabled",
label: getString("prefs-rule-enabled"),
type: "checkbox",
fixedWidth: true,
width: 50,
width: 70,
},
] as ColumnOptions[],
showHeader: true,
Expand Down Expand Up @@ -161,11 +165,12 @@ function getRowData(index: number) {
return {
event: getString(`prefs-rule-event-${TagEventTypes[rule.event]}`),
operation: getString(
`prefs-rule-operation-${TagOperationTypes[rule.operation]}`,
`prefs-rule-operation-${TagOperationTypes[rule.operation]}`
),
data: rule.data,
shortcut: rule.shortcut,
enabled: rule.enabled,
menu: rule.menu || "❌",
};
}

Expand Down Expand Up @@ -280,7 +285,7 @@ async function editRule(currentKey?: string) {
const content = await openEditorWindow(dialogData.data);
(
dialog.window.document.querySelector(
"#data-input",
"#data-input"
) as HTMLTextAreaElement
).value = content;
dialogData.data = content;
Expand All @@ -303,6 +308,9 @@ async function editRule(currentKey?: string) {
"data-bind": "shortcut",
"data-prop": "textContent",
},
styles: {
width: "fit-content",
},
listeners: [
{
type: "click",
Expand All @@ -311,7 +319,7 @@ async function editRule(currentKey?: string) {
const key = ev.target as HTMLElement;
const win = dialog.window;
key.textContent = `[${getString(
"prefs-rule-edit-shortcut-placeholder",
"prefs-rule-edit-shortcut-placeholder"
)}]`;
dialogData.shortcut = "";
const keyDownListener = (e: KeyboardEvent) => {
Expand Down Expand Up @@ -342,6 +350,26 @@ async function editRule(currentKey?: string) {
},
],
},
{
tag: "label",
namespace: "html",
properties: {
textContent: getString("prefs-rule-menu"),
},
},
{
tag: "input",
attributes: {
"data-bind": "menu",
"data-prop": "value",
},
properties: {
placeholder: getString("prefs-rule-edit-menu-placeholder"),
},
styles: {
width: "fit-content",
},
},
{
tag: "label",
namespace: "html",
Expand Down Expand Up @@ -384,6 +412,7 @@ async function editRule(currentKey?: string) {
// Replace things inside []
shortcut: dialogData.shortcut.replace(/\[(.*?)\]/g, ""),
enabled: dialogData.enabled,
menu: dialogData.menu,
});
updateUI();
}
Expand All @@ -401,7 +430,7 @@ async function openEditorWindow(content: string) {
const editorWin = addon.data.prefs.window?.openDialog(
"chrome://scaffold/content/monaco/monaco.html",
"monaco",
"chrome,centerscreen,dialog=no,resizable,scrollbars=yes,width=800,height=600",
"chrome,centerscreen,dialog=no,resizable,scrollbars=yes,width=800,height=600"
);
await waitUtilAsync(() => editorWin?.loadMonaco);
const { monaco, editor } = await editorWin.loadMonaco({
Expand Down
10 changes: 6 additions & 4 deletions src/utils/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ interface TagRule<T extends TagOperationTypes = TagOperationTypes> {
data: string;
shortcut?: string;
enabled?: boolean;
menu?: string;
}

const defaultRules: TagRuleMap = new Map([
Expand Down Expand Up @@ -73,6 +74,7 @@ const emptyRule: TagRule = {
data: "",
shortcut: "",
enabled: true,
menu: "",
};

type TagRuleMap = Map<string, TagRule>;
Expand All @@ -86,7 +88,7 @@ function initRules() {
addon.data.rules.data = new ztoolkit.LargePref(
`${config.prefsPrefix}.rules`,
`${config.prefsPrefix}.rules.`,
"parser",
"parser"
).asMapLike() as TagRuleMap;
if (!getPref("rulesInit")) {
for (const key of defaultRules.keys()) {
Expand Down Expand Up @@ -126,7 +128,7 @@ async function applyRule(rule: TagRule, data: TagRuleData) {
item.removeTag(tag);
}
message = `Remove tag ${tags.join(",")} from item ${item.getField(
"title",
"title"
)}`;
break;
}
Expand All @@ -139,7 +141,7 @@ async function applyRule(rule: TagRule, data: TagRuleData) {
}
}
message = `Toggle tag ${tags.join(",")} to item ${item.getField(
"title",
"title"
)}`;
break;
}
Expand All @@ -148,7 +150,7 @@ async function applyRule(rule: TagRule, data: TagRuleData) {
try {
const func = new AsyncFunction("item, data, require", script);
message = await func(item, data, (module: string) =>
ztoolkit.getGlobal(module),
ztoolkit.getGlobal(module)
);
} catch (e) {
ztoolkit.log("Script Error", e);
Expand Down

0 comments on commit 17d1765

Please sign in to comment.