mirror of
https://github.com/jsmorabito/obsidian-commander.git
synced 2026-07-22 06:40:31 +00:00
Bare setTimeout/activeWindow.requestAnimationFrame calls should resolve via window explicitly for popout-window compatibility - if a command is added from a floating window, the timer needs to run against that window rather than whatever window happens to be "active". Affects macro DELAY actions, the page-header button refresh, and a couple of confetti-timed link opens.
190 lines
5.6 KiB
TypeScript
190 lines
5.6 KiB
TypeScript
import { ItemView, Menu, WorkspaceLeaf } from "obsidian";
|
|
import t from "src/l10n";
|
|
import CommanderPlugin from "src/main";
|
|
import { CommandIconPair } from "src/types";
|
|
import ChooseCustomNameModal from "src/ui/chooseCustomNameModal";
|
|
import ChooseIconModal from "src/ui/chooseIconModal";
|
|
import ConfirmDeleteModal from "src/ui/confirmDeleteModal";
|
|
import { chooseNewCommand, isModeActive } from "src/util";
|
|
import CommandManagerBase from "./commandManager";
|
|
|
|
export default class PageHeaderManager extends CommandManagerBase {
|
|
public buttons = new WeakMap<ItemView, Map<string, HTMLElement>>();
|
|
|
|
public constructor(plugin: CommanderPlugin, pairArray: CommandIconPair[]) {
|
|
super(plugin, pairArray);
|
|
this.init();
|
|
}
|
|
|
|
private addPageHeaderButton(
|
|
leaf: WorkspaceLeaf,
|
|
pair: CommandIconPair
|
|
): void {
|
|
const { id, icon, name } = pair;
|
|
const { view } = leaf;
|
|
if (!(view instanceof ItemView)) return;
|
|
const buttons = this.buttonsFor(leaf, true);
|
|
if (!buttons || buttons.has(id)) return;
|
|
|
|
const buttonIcon = view.addAction(icon, name, () => {
|
|
this.plugin.app.workspace.setActiveLeaf(leaf, { focus: true });
|
|
this.plugin.app.commands.executeCommandById(id);
|
|
});
|
|
buttons.set(id, buttonIcon);
|
|
|
|
buttonIcon.addClasses(["cmdr-page-header", id]);
|
|
buttonIcon.style.color =
|
|
pair.color === "#000000" || pair.color === undefined
|
|
? "inherit"
|
|
: pair.color;
|
|
buttonIcon.addEventListener("contextmenu", (event) => {
|
|
event.stopImmediatePropagation();
|
|
new Menu()
|
|
.addItem((item) => {
|
|
item.setTitle(t("Add command"))
|
|
.setIcon("command")
|
|
.onClick(async () => {
|
|
const pair = await chooseNewCommand(this.plugin);
|
|
await this.addCommand(pair);
|
|
});
|
|
})
|
|
.addSeparator()
|
|
.addItem((item) => {
|
|
item.setTitle(t("Change Icon"))
|
|
.setIcon("box")
|
|
.onClick(async () => {
|
|
const newIcon = await new ChooseIconModal(
|
|
this.plugin
|
|
).awaitSelection();
|
|
if (newIcon && newIcon !== pair.icon) {
|
|
pair.icon = newIcon;
|
|
await this.plugin.saveSettings();
|
|
await this.reorder();
|
|
}
|
|
});
|
|
})
|
|
.addItem((item) => {
|
|
item.setTitle(t("Rename"))
|
|
.setIcon("text-cursor-input")
|
|
.onClick(async () => {
|
|
const newName = await new ChooseCustomNameModal(
|
|
pair.name,
|
|
this.plugin
|
|
).awaitSelection();
|
|
if (newName && newName !== pair.name) {
|
|
pair.name = newName;
|
|
await this.plugin.saveSettings();
|
|
await this.reorder();
|
|
}
|
|
});
|
|
})
|
|
.addItem((item) => {
|
|
item.dom.addClass("is-warning");
|
|
item.setTitle(t("Delete"))
|
|
.setIcon("lucide-trash")
|
|
.onClick(async () => {
|
|
if (
|
|
!this.plugin.settings.confirmDeletion ||
|
|
(await new ConfirmDeleteModal(
|
|
this.plugin
|
|
).didChooseRemove())
|
|
) {
|
|
await this.removeCommand(pair);
|
|
}
|
|
});
|
|
})
|
|
.showAtMouseEvent(event);
|
|
});
|
|
}
|
|
|
|
private init(): void {
|
|
this.plugin.register(() => {
|
|
// Remove all buttons on plugin unload
|
|
this.removeButtonsFromAllLeaves();
|
|
});
|
|
this.plugin.registerEvent(
|
|
this.plugin.app.workspace.on("layout-change", () => {
|
|
this.addButtonsToAllLeaves();
|
|
})
|
|
);
|
|
this.plugin.app.workspace.onLayoutReady(() =>
|
|
window.setTimeout(() => this.addButtonsToAllLeaves(), 100)
|
|
);
|
|
}
|
|
|
|
private addAdderButton(leaf: WorkspaceLeaf): void {
|
|
const { view } = leaf;
|
|
const id = "cmdr-adder";
|
|
if (!(view instanceof ItemView)) return;
|
|
if (this.buttons.get(view)?.has(id)) return;
|
|
const buttonIcon = view.addAction("plus", t("Add new"), async () => {
|
|
await this.addCommand(await chooseNewCommand(this.plugin));
|
|
});
|
|
buttonIcon.addClasses(["cmdr", id]);
|
|
if (!this.buttons.has(view)) this.buttons.set(view, new Map());
|
|
this.buttons.get(view)!.set(id, buttonIcon);
|
|
}
|
|
|
|
private addButtonsToAllLeaves(refresh = false): void {
|
|
window.requestAnimationFrame(() =>
|
|
this.plugin.app.workspace.iterateAllLeaves((leaf) =>
|
|
this.addButtonsToLeaf(leaf, refresh)
|
|
)
|
|
);
|
|
}
|
|
|
|
private removeButtonsFromAllLeaves(): void {
|
|
window.requestAnimationFrame(() =>
|
|
this.plugin.app.workspace.iterateAllLeaves((leaf) =>
|
|
this.removeButtonsFromLeaf(leaf)
|
|
)
|
|
);
|
|
}
|
|
|
|
private buttonsFor(leaf: WorkspaceLeaf, create = false): Map<string, HTMLElement> | undefined {
|
|
if (!(leaf.view instanceof ItemView)) return;
|
|
if (create && !this.buttons.has(leaf.view))
|
|
this.buttons.set(leaf.view, new Map());
|
|
return this.buttons.get(leaf.view);
|
|
}
|
|
|
|
private addButtonsToLeaf(leaf: WorkspaceLeaf, refresh = false): void {
|
|
if (!(leaf.view instanceof ItemView)) return;
|
|
if (refresh) {
|
|
this.removeButtonsFromLeaf(leaf);
|
|
} else if (this.buttonsFor(leaf)?.size) {
|
|
// View already has buttons and we're not doing a full refresh
|
|
return;
|
|
}
|
|
for (let i = this.pairs.length - 1; i >= 0; i--) {
|
|
const pair = this.pairs[i];
|
|
if (isModeActive(pair.mode, this.plugin))
|
|
this.addPageHeaderButton(leaf, pair);
|
|
}
|
|
if (this.plugin.settings.showAddCommand) this.addAdderButton(leaf);
|
|
}
|
|
|
|
private removeButtonsFromLeaf(leaf: WorkspaceLeaf): void {
|
|
const buttons = this.buttonsFor(leaf);
|
|
if (buttons) {
|
|
for (const button of buttons.values()) button.detach();
|
|
buttons?.clear();
|
|
}
|
|
}
|
|
|
|
public reorder(): void | Promise<void> {
|
|
this.addButtonsToAllLeaves(true);
|
|
}
|
|
|
|
public async addCommand(pair: CommandIconPair): Promise<void> {
|
|
this.pairs.push(pair);
|
|
this.addButtonsToAllLeaves(true);
|
|
await this.plugin.saveSettings();
|
|
}
|
|
|
|
public async removeCommand(pair: CommandIconPair): Promise<void> {
|
|
this.pairs.remove(pair);
|
|
this.addButtonsToAllLeaves(true);
|
|
await this.plugin.saveSettings();
|
|
}
|
|
}
|