From a75d0ca540d65ef773011f86e8887fb7f58b33cc Mon Sep 17 00:00:00 2001 From: SuiltaPico <256329717@qq.com> Date: Tue, 19 Nov 2024 18:15:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=94=B9=EF=BC=9A=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20slice=20=E6=8E=A5=E5=8F=A3=EF=BC=8C=E4=B8=BA=20BreakLine=20?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E6=8F=90=E4=BE=9B=E5=87=86=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/signal.ts | 9 +++- src/components/base/mix_editor/Area.ts | 4 +- .../base/mix_editor/event/BreakLine.ts | 45 ++++++++++++++++--- .../base/mix_editor/plugins/paragraph.tsx | 10 +++++ .../base/mix_editor/plugins/text.tsx | 6 +++ src/components/base/mix_editor/root.ts | 3 ++ src/components/base/mix_editor/save.ts | 6 +++ src/components/base/mix_editor/selection.ts | 11 ----- 8 files changed, 75 insertions(+), 19 deletions(-) diff --git a/src/common/signal.ts b/src/common/signal.ts index 80d705f..c93019c 100644 --- a/src/common/signal.ts +++ b/src/common/signal.ts @@ -1,4 +1,9 @@ -import { Accessor, Setter, Signal, createSignal as solid_createSignal } from "solid-js"; +import { + Accessor, + Setter, + Signal, + createSignal as solid_createSignal, +} from "solid-js"; export class WrappedSignal { get: Accessor; @@ -31,4 +36,4 @@ export class EmitterSignal { export function createEmitterSignal() { return new EmitterSignal(); -} +} \ No newline at end of file diff --git a/src/components/base/mix_editor/Area.ts b/src/components/base/mix_editor/Area.ts index 622acc5..ca27bcf 100644 --- a/src/components/base/mix_editor/Area.ts +++ b/src/components/base/mix_editor/Area.ts @@ -6,8 +6,10 @@ import { BlockSavedData, InlineSavedData, InlineTagSavedData } from "./save"; import { WrappedSignal } from "@/common/signal"; export interface BaseArea { + /** 切割当前区域。 */ + slice(from: number, to: number): MaybePromise; /** 生成保存数据。 */ - save: () => MaybePromise; + save(): MaybePromise; /** 获取子区域数量。 */ children_count(): number; diff --git a/src/components/base/mix_editor/event/BreakLine.ts b/src/components/base/mix_editor/event/BreakLine.ts index 7992b49..8080e85 100644 --- a/src/components/base/mix_editor/event/BreakLine.ts +++ b/src/components/base/mix_editor/event/BreakLine.ts @@ -2,12 +2,11 @@ import { MaybePromise } from "@/common/async"; import { Area } from "../Area"; import { BaseEvent } from "../event"; import { Selection } from "../selection"; +import { find_index_in_parent_area } from "../utils/area"; export type BreakLineEventCommand = | { type: "break"; - /** 输入的结束位置。 */ - to: number; } | { type: "no_break"; @@ -17,17 +16,53 @@ export const BreakLineEventCommand = { /** 不接受断行。 */ no_break: { type: "no_break" } satisfies BreakLineEventCommand, /** 接受断行,并把光标移动到指定位置。 */ - break: (to: number) => ({ type: "break", to } satisfies BreakLineEventCommand), + break: { type: "break" } satisfies BreakLineEventCommand, }; /** 输入事件。 */ export interface BreakLineEvent extends BaseEvent { event_type: "break_line"; - /** 输入的位置。 */ + /** 要断行的位置。 */ to: number; + /** 是否从子节点进入。 */ + from_child: boolean; } export type BreakLineEventPair = { event: BreakLineEvent; result: MaybePromise; -}; \ No newline at end of file +}; + +export async function handle_break_line_event_command( + selection: Selection, + curr_area: Area, + command: BreakLineEventCommand | void, + to: number +) { + let break_points: [Area, number][] = [[curr_area, to]]; + while (true) { + command ??= BreakLineEventCommand.no_break; + + if (command.type === "no_break") { + // 1. 自顶向下,切割每个区域的后半区域(不含要被切割的区域) + // 2. 自底向上,将自身补到上级的开头 + // 3. 自顶向下,递归删除被切割的区域 + } else if (command.type === "break") { + // 让父节点处理 + const context = selection.editor.get_context(curr_area); + if (!context?.parent) return; + + const index_in_parent = find_index_in_parent_area( + curr_area, + context.parent + ); + + curr_area = context.parent; + command = await curr_area.handle_event?.({ + event_type: "break_line", + to: index_in_parent, + from_child: true, + }); + } + } +} diff --git a/src/components/base/mix_editor/plugins/paragraph.tsx b/src/components/base/mix_editor/plugins/paragraph.tsx index 1629ee4..f0fd22f 100644 --- a/src/components/base/mix_editor/plugins/paragraph.tsx +++ b/src/components/base/mix_editor/plugins/paragraph.tsx @@ -155,6 +155,16 @@ export class ParagraphBlock> { area_type = "block" as const; type = "paragraph" as const; + slice(from: number, to: number): this { + return new ParagraphBlock( + { + inlines: createSignal(this.data.inlines.get().slice(from, to), { + equals: false, + }), + }, + this.editor + ) as this; + } async save() { return create_BlockSaveData(this.type, { inlines: await save_inlines(this.data.inlines.get()), diff --git a/src/components/base/mix_editor/plugins/text.tsx b/src/components/base/mix_editor/plugins/text.tsx index be56611..42b43df 100644 --- a/src/components/base/mix_editor/plugins/text.tsx +++ b/src/components/base/mix_editor/plugins/text.tsx @@ -107,6 +107,12 @@ export class TextInline area_type = "inline" as const; type = "text" as const; data: { value: WrappedSignal; tags: WrappedSignal }; + slice(from: number, to: number) { + return new TextInline( + { value: this.data.value.get().slice(from, to) }, + this.editor + ) as this; + } async save() { return create_InlineSaveData( this.type, diff --git a/src/components/base/mix_editor/root.ts b/src/components/base/mix_editor/root.ts index c89b7d1..4b0fdb1 100644 --- a/src/components/base/mix_editor/root.ts +++ b/src/components/base/mix_editor/root.ts @@ -102,6 +102,9 @@ export class RootArea implements Block<"root", {}> { area_type = "block" as const; type = "root" as const; data = {}; + slice(from: number, to: number): this { + throw new Error("根区域的切割未实现。"); + } save() { return { type: "root", diff --git a/src/components/base/mix_editor/save.ts b/src/components/base/mix_editor/save.ts index 49aa447..492b9f9 100644 --- a/src/components/base/mix_editor/save.ts +++ b/src/components/base/mix_editor/save.ts @@ -101,6 +101,9 @@ export class LoadingErrorBlock { area_type = "block" as const; type = "loading_error" as const; + slice(from: number, to: number) { + return new LoadingErrorBlock(this.data) as this; + } save() { return this.data.original; } @@ -129,6 +132,9 @@ export class LoadingErrorInline { area_type = "inline" as const; type = "loading_error" as const; + slice(from: number, to: number) { + return new LoadingErrorInline(this.data) as this; + } save() { return this.data.original; } diff --git a/src/components/base/mix_editor/selection.ts b/src/components/base/mix_editor/selection.ts index ee2b074..0f08d02 100644 --- a/src/components/base/mix_editor/selection.ts +++ b/src/components/base/mix_editor/selection.ts @@ -5,12 +5,10 @@ import { CaretMoveEnterEventPair, handle_caret_move_enter_event_command } from "./event/CaretMoveEnter"; -import { CombineEventPair } from "./event/Combine"; import { DeleteEventPair, handle_delete_event_command } from "./event/Delete"; -import { EnterEventPair, handle_enter_result } from "./event/Enter"; import { MixEditor } from "./MixEditor"; export type SelectedAreaInfo = { @@ -59,15 +57,6 @@ export class Selection { return this.selected.get(); } - async enter(area: Area, to: number) { - const result = await area.handle_event?.({ - event_type: "enter", - to, - }); - - return await handle_enter_result(this, result, area, to); - } - private async move(direction: "backward" | "forward") { const selected = this.selected.get(); if (!selected) return;