support search node

This commit is contained in:
codetypes committed 2024-04-26 09:37:49 +08:00
1 parent 7dbea171bb
commit 82d1d89311
6 files changed
+112 -8

No files matched your search

+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "behavior3editor", "name": "behavior3editor",
"version": "1.1.2", "version": "1.1.3",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "behavior3editor", "name": "behavior3editor",
"version": "1.1.2", "version": "1.1.3",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "behavior3editor", "name": "behavior3editor",
"version": "1.1.2", "version": "1.1.3",
"main": "dist-electron/main/index.js", "main": "dist-electron/main/index.js",
"description": "行为树编辑器", "description": "行为树编辑器",
"author": "zhandouxiaojiji & codetypes", "author": "zhandouxiaojiji & codetypes",
+104 -2
View File
@@ -10,17 +10,19 @@ import * as b3util from "@/misc/b3util";
import { message } from "@/misc/hooks"; import { message } from "@/misc/hooks";
import { Hotkey, isHotkeyPressed, isMacos, useHotkeys } from "@/misc/keys"; import { Hotkey, isHotkeyPressed, isMacos, useHotkeys } from "@/misc/keys";
import Path from "@/misc/path"; import Path from "@/misc/path";
import { ArrowDownOutlined, ArrowUpOutlined, CloseOutlined } from "@ant-design/icons";
import G6, { G6GraphEvent, Item, TreeGraph } from "@antv/g6"; import G6, { G6GraphEvent, Item, TreeGraph } from "@antv/g6";
import { dialog } from "@electron/remote"; import { dialog } from "@electron/remote";
import { useSize } from "ahooks"; import { useSize } from "ahooks";
import { Dropdown, Flex, FlexProps, MenuProps } from "antd"; import { Button, Dropdown, Flex, FlexProps, Input, MenuProps } from "antd";
import { clipboard } from "electron"; import { clipboard } from "electron";
import * as fs from "fs"; import * as fs from "fs";
import React, { FC, useCallback, useEffect, useMemo, useRef } from "react"; import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { FiDelete } from "react-icons/fi"; import { FiDelete } from "react-icons/fi";
import { IoMdReturnLeft } from "react-icons/io"; import { IoMdReturnLeft } from "react-icons/io";
import { mergeRefs } from "react-merge-refs"; import { mergeRefs } from "react-merge-refs";
import { useDebounceCallback } from "usehooks-ts";
import "./register-node"; import "./register-node";
export interface EditorProps extends React.HTMLAttributes<HTMLElement> { export interface EditorProps extends React.HTMLAttributes<HTMLElement> {
@@ -36,11 +38,27 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
open: useWorkspace((state) => state.open), open: useWorkspace((state) => state.open),
workdir: useWorkspace((state) => state.workdir), workdir: useWorkspace((state) => state.workdir),
}; };
const [results, setResults] = useState<string[]>([]);
const [resultIndex, setResultIndex] = useState(0);
const [showingSearch, setShowingSearch] = useState(false);
const graphRef = useRef(null); const graphRef = useRef(null);
const sizeRef = useRef(null); const sizeRef = useRef(null);
const editorSize = useSize(sizeRef); const editorSize = useSize(sizeRef);
const { t } = useTranslation(); const { t } = useTranslation();
const onSearchChange = useDebounceCallback((filter: string) => {
const ids = filterNodes(filter, findDataById("1"));
setResults(ids);
setResultIndex(0);
if (ids.length > 0) {
editor.graph.focusItem(ids[0]);
selectNode(ids[0]);
}
if (filter.length == 0) {
restoreViewport();
}
}, 200);
const onChange = () => { const onChange = () => {
if (!editor.unsave) { if (!editor.unsave) {
editor.unsave = true; editor.unsave = true;
@@ -93,6 +111,17 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
return editor.graph.findDataById(id) as TreeGraphData; return editor.graph.findDataById(id) as TreeGraphData;
}; };
const filterNodes = (str: string, node: TreeGraphData | null, ids?: string[]): string[] => {
ids ||= [];
if (node && str) {
if (node.name.toLocaleLowerCase().indexOf(str) >= 0) {
ids.push(node.id);
}
node.children?.forEach((child: TreeGraphData) => filterNodes(str, child, ids));
}
return ids;
};
const isAncestor = (ancestor: TreeGraphData, node: TreeGraphData): boolean => { const isAncestor = (ancestor: TreeGraphData, node: TreeGraphData): boolean => {
if (ancestor.id === node.parent) { if (ancestor.id === node.parent) {
return true; return true;
@@ -761,6 +790,7 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
Hotkey.Delete, Hotkey.Delete,
Hotkey.Enter, Hotkey.Enter,
Hotkey.Backspace, Hotkey.Backspace,
Hotkey.SearchNode,
], ],
(event) => { (event) => {
event.preventDefault(); event.preventDefault();
@@ -778,6 +808,8 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
createNode(); createNode();
} else if (isHotkeyPressed(Hotkey.Delete) || isHotkeyPressed(Hotkey.Backspace)) { } else if (isHotkeyPressed(Hotkey.Delete) || isHotkeyPressed(Hotkey.Backspace)) {
deleteNode(); deleteNode();
} else if (isHotkeyPressed(Hotkey.SearchNode)) {
setShowingSearch(true);
} }
} }
); );
@@ -967,6 +999,76 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
tabIndex={-1} tabIndex={-1}
style={{ maxWidth: "inherit", maxHeight: "inherit" }} style={{ maxWidth: "inherit", maxHeight: "inherit" }}
> >
{showingSearch && (
<Flex
style={{
position: "absolute",
marginTop: "10px",
marginLeft: "10px",
backgroundColor: "#161b22",
padding: "4px 10px 4px 10px",
borderRadius: "4px",
borderLeft: "4px solid #f78166",
boxShadow: "0 0 8px 2px #0000005c",
alignItems: "center",
}}
>
<Input
autoFocus
allowClear
size="small"
style={{ borderRadius: "2px" }}
onChange={(e) => onSearchChange(e.currentTarget.value)}
/>
<div style={{ padding: "0 10px 0 5px" }}>
{results.length ? `${resultIndex + 1}/${results.length}` : ""}
</div>
<Button
icon={<ArrowDownOutlined />}
type="text"
size="small"
style={{ width: "30px" }}
disabled={results.length == 0}
onClick={() => {
if (results.length > 0) {
const idx = (resultIndex + 1) % results.length;
setResultIndex(idx);
editor.graph.focusItem(results[idx]);
selectNode(results[idx]);
}
}}
/>
<Button
icon={<ArrowUpOutlined />}
type="text"
size="small"
style={{ width: "30px" }}
disabled={results.length == 0}
onClick={() => {
if (results.length > 0) {
const idx = (resultIndex + results.length - 1) % results.length;
setResultIndex(idx);
console.log(idx, results[idx]);
editor.graph.focusItem(results[idx]);
selectNode(results[idx]);
}
}}
/>
<Button
icon={<CloseOutlined />}
type="text"
size="small"
style={{ width: "30px" }}
onClick={() => {
setShowingSearch(false);
setResults([]);
setResultIndex(0);
keysRef.current?.focus();
}}
/>
</Flex>
)}
<Dropdown menu={{ items: menuItems, onClick }} trigger={["contextMenu"]}> <Dropdown menu={{ items: menuItems, onClick }} trigger={["contextMenu"]}>
<div tabIndex={-1} style={{ width: "100%", height: "100%" }} ref={graphRef} /> <div tabIndex={-1} style={{ width: "100%", height: "100%" }} ref={graphRef} />
</Dropdown> </Dropdown>
+2 -2
View File
@@ -44,7 +44,7 @@ export const Workspace: FC = () => {
const { width = 0, height = 0 } = useWindowSize(); const { width = 0, height = 0 } = useWindowSize();
const keysRef = useHotkeys<HTMLDivElement>( const keysRef = useHotkeys<HTMLDivElement>(
[Hotkey.Save, Hotkey.CloseEditor, Hotkey.Search, Hotkey.Build], [Hotkey.Save, Hotkey.CloseEditor, Hotkey.SearchTree, Hotkey.Build],
(event) => { (event) => {
if (isHotkeyPressed(Hotkey.Save)) { if (isHotkeyPressed(Hotkey.Save)) {
event.preventDefault(); event.preventDefault();
@@ -58,7 +58,7 @@ export const Workspace: FC = () => {
workspace.close(workspace.editing.path); workspace.close(workspace.editing.path);
} }
} }
} else if (isHotkeyPressed(Hotkey.Search)) { } else if (isHotkeyPressed(Hotkey.SearchTree)) {
event.preventDefault(); event.preventDefault();
workspace.onShowingSearch(true); workspace.onShowingSearch(true);
} else if (isHotkeyPressed(Hotkey.Build)) { } else if (isHotkeyPressed(Hotkey.Build)) {
+1
View File
@@ -32,6 +32,7 @@ export type EditEvent =
| "rename" | "rename"
| "updateTree" | "updateTree"
| "updateNode" | "updateNode"
| "searchNode"
| "editSubtree" | "editSubtree"
| "saveAsSubtree"; | "saveAsSubtree";
+2 -1
View File
@@ -57,7 +57,8 @@ export const Hotkey = {
Redo: isMacos ? hotkey("shift+ctrl+z") : hotkey("ctrl+y"), Redo: isMacos ? hotkey("shift+ctrl+z") : hotkey("ctrl+y"),
Replace: hotkey("shift+ctrl+v"), Replace: hotkey("shift+ctrl+v"),
Save: hotkey("ctrl+s"), Save: hotkey("ctrl+s"),
Search: hotkey("ctrl+p"), SearchTree: hotkey("ctrl+p"),
SearchNode: hotkey("ctrl+f"),
Undo: hotkey("ctrl+z"), Undo: hotkey("ctrl+z"),
}; };