Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/side details #241

Merged
merged 6 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs-pg/ProjectGraph开发进程图.json
Original file line number Diff line number Diff line change
Expand Up @@ -1411,7 +1411,7 @@
"text": "解决win10系统无法打开地球仪的问题",
"uuid": "7bde46b8-bc89-47fb-a375-8c3b87880015",
"details": "",
"color": [0, 0, 0, 0],
"color": [22, 163, 74, 1],
"type": "core:text_node"
},
{
Expand All @@ -1420,7 +1420,7 @@
"text": "@较真小猫",
"uuid": "284d3026-36da-436e-b656-1e0eb7b45fd3",
"details": "",
"color": [0, 0, 0, 0],
"color": [22, 163, 74, 1],
"type": "core:text_node"
},
{
Expand Down
5 changes: 4 additions & 1 deletion src/components/ui/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type InputProps<T extends boolean = false> = {
multiline?: boolean;
onChange?: (value: T extends true ? number : string) => void; // 使用条件类型来定义 onChange 的参数
number?: T;
enableFocusOpacity?: boolean;
[key: string]: any;
};

Expand All @@ -20,6 +21,7 @@ export default function Input<T extends boolean = false>({
placeholder = "",
number = false as T,
multiline = false,
enableFocusOpacity = true,
...props
}: React.PropsWithChildren<InputProps<T>>) {
const handleChange = (
Expand All @@ -37,7 +39,8 @@ export default function Input<T extends boolean = false>({
<Box
as={multiline ? "textarea" : "input"}
className={cn(
"px-3 py-2 outline-none hover:opacity-80 focus:opacity-80",
"px-3 py-2 outline-none",
enableFocusOpacity && "hover:opacity-80 focus:opacity-80",
className,
)}
value={value}
Expand Down
16 changes: 16 additions & 0 deletions src/core/MouseLocation.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Vector } from "./dataStruct/Vector";

export namespace MouseLocation {
export let x: number = 0;
export let y: number = 0;
Expand All @@ -6,6 +8,20 @@ export namespace MouseLocation {
window.addEventListener("mousemove", (event) => {
x = event.clientX;
y = event.clientY;

// 维护一个Vector对象
vectorObject.x = x;
vectorObject.y = y;
});
}

const vectorObject = new Vector(x, y);

/**
* 返回的时视野坐标系中的鼠标位置
* @returns
*/
export function vector(): Vector {
return vectorObject;
}
}
19 changes: 19 additions & 0 deletions src/core/algorithm/numberFunctions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,23 @@ export namespace NumberFunctions {
): boolean {
return Math.abs(number1 - number2) <= tolerance;
}

/**
* 此函数用于放在循环函数中,生成一个周期震荡的数字
* @param maxValue 震荡的最大值
* @param minValue 震荡的最小值
* @param cycleTime 周期时间,单位为秒
*/
export function sinNumberByTime(
maxValue: number,
minValue: number,
cycleTime: number,
) {
const t = performance.now() / 1000;
return (
Math.sin(((t % cycleTime) * (Math.PI * 2)) / cycleTime) *
(maxValue - minValue) +
minValue
);
}
}
16 changes: 16 additions & 0 deletions src/core/controller/concrete/ControllerNodeEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ ControllerNodeEdit.mouseDoubleClick = (event: MouseEvent) => {
}
};

ControllerNodeEdit.mouseup = (event: MouseEvent) => {
if (event.button !== 0) {
return;
}

const pressLocation = Renderer.transformView2World(
new Vector(event.clientX, event.clientY),
);
for (const entity of StageManager.getEntities()) {
if (entity.isMouseInDetailsButton(pressLocation)) {
editNodeDetails(entity);
return;
}
}
};

ControllerNodeEdit.mousemove = (event: MouseEvent) => {
/**
* 如果一直显示详细信息,则不显示鼠标悬停效果
Expand Down
8 changes: 8 additions & 0 deletions src/core/controller/concrete/utilsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,18 @@ export function editNode(clickedNode: TextNode) {
* 一个全局对象,用于编辑节点的钩子函数
*/
export const editTextNodeHookGlobal = {
/**
* 编辑节点的钩子函数,用于开始编辑,弹窗触发
* @param _
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
hookFunctionStart(_: Entity) {
// 在外部将被修改
},
/**
* 编辑节点的钩子函数,用于结束编辑,弹窗关闭
* @param _
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
hookFunctionEnd(_: Entity) {
// 在外部将被修改
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { MouseLocation } from "../../../MouseLocation";
import { NumberFunctions } from "../../../algorithm/numberFunctions";
import { Vector } from "../../../dataStruct/Vector";
import { Camera } from "../../../stage/Camera";
import { Entity } from "../../../stageObject/StageObject";
import { StageStyleManager } from "../../../stageStyle/StageStyleManager";
import { RenderUtils } from "../RenderUtils";
import { Renderer } from "../renderer";
/**
* 仅仅渲染一个节点右上角的按钮
*/
export function EntityDetailsButtonRenderer(entity: Entity) {
if (!entity.details) {
return;
}
// RenderUtils.renderRect(
// entity.detailsButtonRectangle().transformWorld2View(),
// StageStyleManager.currentStyle.DetailsDebugTextColor,
// StageStyleManager.currentStyle.DetailsDebugTextColor,
// 2 * Camera.currentScale,
// Renderer.NODE_ROUNDED_RADIUS * Camera.currentScale,
// );
let isMouseHovering = false;
// 鼠标悬浮在按钮上提示文字
if (
entity
.detailsButtonRectangle()
.isPointIn(Renderer.transformView2World(MouseLocation.vector()))
) {
isMouseHovering = true;
if (!entity.isEditingDetails)
// 鼠标悬浮在这上面
RenderUtils.renderText(
"点击展开或关闭节点注释详情",
Renderer.transformWorld2View(
entity.detailsButtonRectangle().topCenter.subtract(new Vector(0, 12)),
),
12 * Camera.currentScale,
StageStyleManager.currentStyle.DetailsDebugTextColor,
);
}
RenderUtils.renderText(
entity.isEditingDetails ? "✏️" : "📃",
Renderer.transformWorld2View(entity.detailsButtonRectangle().leftTop),
isMouseHovering ? getFontSizeByTime() : 20 * Camera.currentScale,
);
}

function getFontSizeByTime() {
const r = NumberFunctions.sinNumberByTime(19, 21, 0.25);
return r * Camera.currentScale;
}
3 changes: 3 additions & 0 deletions src/core/render/canvas2d/entityRenderer/EntityRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { StageStyleManager } from "../../../stageStyle/StageStyleManager";
import { ImageNode } from "../../../stageObject/entity/ImageNode";
import { ImageRenderer } from "../ImageRenderer";
import { Entity } from "../../../stageObject/StageObject";
import { EntityDetailsButtonRenderer } from "./EntityDetailsButtonRenderer";

/**
* 处理节点相关的绘制
Expand Down Expand Up @@ -41,6 +42,8 @@ export namespace EntityRenderer {
} else if (entity instanceof ImageNode) {
renderImageNode(entity);
}
// details右上角小按钮
EntityDetailsButtonRenderer(entity);
}
function renderSection(section: Section) {
if (section.isHiddenBySectionCollapse) {
Expand Down
13 changes: 13 additions & 0 deletions src/core/stageObject/StageObject.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Rectangle } from "../dataStruct/shape/Rectangle";
import { Vector } from "../dataStruct/Vector";
import { StageManager } from "../stage/stageManager/StageManager";
import { CollisionBox } from "./collisionBox/collisionBox";
Expand Down Expand Up @@ -48,6 +49,18 @@ export abstract class Entity extends StageObject {
changeDetails(details: string) {
this.details = details;
}

public detailsButtonRectangle(): Rectangle {
const thisRectangle = this.collisionBox.getRectangle();
return new Rectangle(
thisRectangle.rightTop.subtract(new Vector(20, 20)),
new Vector(20, 20),
);
}
public isMouseInDetailsButton(mouseWorldLocation: Vector): boolean {
return this.detailsButtonRectangle().isPointIn(mouseWorldLocation);
}

/**
* 由于自身位置的移动,递归的更新所有父级Section的位置和大小
*/
Expand Down
4 changes: 4 additions & 0 deletions src/pages/_details_edit_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { Renderer } from "../core/render/canvas2d/renderer";
import { Camera } from "../core/stage/Camera";
import { Entity } from "../core/stageObject/StageObject";

/**
* 2025年1月4日,这个打算被侧边栏取代 ——littlefean
* @returns
*/
export default function DetailsEditPanel() {
const [inputCurrentDetails, setInputCurrentDetails] = React.useState("");
const [isNodeTextEditing, setIsNodeTextEditing] = React.useState(false);
Expand Down
83 changes: 83 additions & 0 deletions src/pages/_details_edit_side_panel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from "react";
import Button from "../components/ui/Button";
import Input from "../components/ui/Input";
import { editTextNodeHookGlobal } from "../core/controller/concrete/utilsControl";
import { Controller } from "../core/controller/Controller";
import { Entity } from "../core/stageObject/StageObject";
import { cn } from "../utils/cn";
import IconButton from "../components/ui/IconButton";
import { ArrowLeftFromLine, ArrowRightFromLine } from "lucide-react";

export default function DetailsEditSidePanel() {
const [inputCurrentDetails, setInputCurrentDetails] = React.useState("");
const [isNodeTextEditing, setIsNodeTextEditing] = React.useState(false);
const [clickedNode, setClickedNode] = React.useState<Entity>();
const setInputCurrentDetailsHandler = (value: string) => {
setInputCurrentDetails(value);
};
const handleConfirmDetailsEdit = () => {
setIsNodeTextEditing(false);
if (clickedNode) {
editTextNodeHookGlobal.hookFunctionEnd(clickedNode);
} else {
console.warn("没有点击节点");
}
};
const handleCancelDetailsEdit = () => {
setIsNodeTextEditing(false);
Controller.isCameraLocked = false;
if (clickedNode) {
clickedNode.isEditingDetails = false;
}
};
editTextNodeHookGlobal.hookFunctionStart = (entity: Entity) => {
setInputCurrentDetails(entity.details);
setClickedNode(entity);
setIsNodeTextEditing(true);
};
editTextNodeHookGlobal.hookFunctionEnd = (entity: Entity) => {
entity.changeDetails(inputCurrentDetails);
Controller.isCameraLocked = false;
entity.isEditingDetails = false;
};

const [isFullScreen, setIsFullScreen] = React.useState(false);
// 将整个面板侧向伸展成全屏或缩小到原来的尺寸
const switchPanelSize = () => {
setIsFullScreen(!isFullScreen);
};
return (
<>
{
<div
className={cn(
"fixed top-0 z-50 flex h-full flex-col transition-all",
isFullScreen ? "right-0 w-full" : "-right-96 w-96",
isNodeTextEditing && "right-0",
)}
>
{/* 顶部空白 */}
<div className="h-16" />
<div className="flex gap-2">
<IconButton onClick={switchPanelSize}>
{isFullScreen ? <ArrowRightFromLine /> : <ArrowLeftFromLine />}
</IconButton>
<Button className="flex-1">编辑模式</Button>
<Button onClick={handleConfirmDetailsEdit}>确认修改</Button>
{/* 取消,关闭 */}
<Button onClick={handleCancelDetailsEdit}>取消修改</Button>
</div>
<Input
multiline
onChange={setInputCurrentDetailsHandler}
value={inputCurrentDetails}
className="my-2 flex-1"
enableFocusOpacity={false}
/>
{/* 底部空白 */}
<div className="h-8" />
</div>
}
</>
);
}
7 changes: 5 additions & 2 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { Canvas } from "../core/stage/Canvas";
import { Stage } from "../core/stage/Stage";
import { StageStyleManager } from "../core/stageStyle/StageStyleManager";
import { Dialog } from "../utils/dialog";
import DetailsEditPanel from "./_details_edit_panel";
// import DetailsEditPanel from "./_details_edit_panel";
import HintText from "./_hint_text";
import SearchingNodePanel from "./_searching_node_panel";
import Toolbar from "./_toolbar";
import DetailsEditSidePanel from "./_details_edit_side_panel";

export default function Home() {
const canvasRef: React.RefObject<HTMLCanvasElement | null> = useRef(null);
Expand Down Expand Up @@ -94,7 +95,9 @@ export default function Home() {
<>
<Toolbar />
<SearchingNodePanel />
<DetailsEditPanel />
{/* 这个打算被取代 */}
{/* <DetailsEditPanel /> */}
<DetailsEditSidePanel />
<HintText />
{/* TODO: 下面这个写法有点奇怪 rgba值太长了 */}
<div
Expand Down