diff --git a/source/Context/Status.tsx b/source/Context/Status.tsx index 868ea03..adf06fb 100644 --- a/source/Context/Status.tsx +++ b/source/Context/Status.tsx @@ -53,6 +53,7 @@ interface IStatusEvent { labelAttrChange: void; groupAttrChange: void; behaviorAttrChange: void; + clipAttrChange: void; individualChange: void; behaviorChange: void; popupChange: void; @@ -286,6 +287,18 @@ class Status extends Emitter { } } + /** + * 修改剪辑属性 + */ + public changeClipAttrib + (id: ObjectID, key: K, val: Clip[K]) { + const clip = this.model.getClipById(id); + if (clip && clip instanceof Clip) { + clip[key] = val; + this.emit("clipAttrChange"); + } + } + public addGroupBehavior(id: ObjectID, val: Behavior) { const group = this.model.getObjectById(id); if (group && group instanceof Group) { diff --git a/source/Localization/EN-US.ts b/source/Localization/EN-US.ts index 2c9b0f6..edd5f87 100644 --- a/source/Localization/EN-US.ts +++ b/source/Localization/EN-US.ts @@ -56,6 +56,8 @@ const EN_US = { "Panel.Info.Behavior.Details.View": "Edit view Behavior attributes", "Panel.Title.Behavior.Clip.Player": "Recording", "Panel.Info.Behavior.Clip.Player": "Pre render recorded data", + "Panel.Title.Behavior.Clip.Details": "Clip", + "Panel.Info.Behavior.Clip.Details": "Edit view clip attributes", "Panel.Info.Behavior.Clip.Time.Formate": "{current} / {all} / {fps}fps", "Panel.Info.Behavior.Clip.Record.Formate": "Record: {time}", "Panel.Info.Behavior.Clip.Uname.Clip": "Waiting for recording...", @@ -161,6 +163,7 @@ const EN_US = { "Panel.Info.Behavior.Details.Parameter.Key.Vec.Y": "{key} Y", "Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z", "Panel.Info.Clip.List.Error.Nodata": "There is no clip, please click the record button to record, or click the plus sign to create", + "Panel.Info.Clip.Details.Error.Nodata": "Specify a clip to view an attribute", "Info.Hint.Save.After.Close": "Any unsaved progress will be lost. Are you sure you want to continue?", "Info.Hint.Load.File.Title": "Load save", "Info.Hint.Load.File.Intro": "Release to load the dragged save file", diff --git a/source/Localization/ZH-CN.ts b/source/Localization/ZH-CN.ts index d01f108..cf32a73 100644 --- a/source/Localization/ZH-CN.ts +++ b/source/Localization/ZH-CN.ts @@ -56,6 +56,8 @@ const ZH_CN = { "Panel.Info.Behavior.Details.View": "编辑查看行为属性", "Panel.Title.Behavior.Clip.Player": "录制", "Panel.Info.Behavior.Clip.Player": "预渲染录制数据", + "Panel.Title.Behavior.Clip.Details": "剪辑", + "Panel.Info.Behavior.Clip.Details": "编辑查看剪辑片段属性", "Panel.Info.Behavior.Clip.Time.Formate": "{current} / {all} / {fps} fps", "Panel.Info.Behavior.Clip.Record.Formate": "录制: {time}", "Panel.Info.Behavior.Clip.Uname.Clip": "等待录制...", @@ -161,6 +163,7 @@ const ZH_CN = { "Panel.Info.Behavior.Details.Parameter.Key.Vec.Y": "{key} Y 坐标", "Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z 坐标", "Panel.Info.Clip.List.Error.Nodata": "没有剪辑片段,请点击录制按钮录制,或者点击加号创建", + "Panel.Info.Clip.Details.Error.Nodata": "请指定一个剪辑片段以查看属性", "Info.Hint.Save.After.Close": "任何未保存的进度都会丢失, 确定要继续吗?", "Info.Hint.Load.File.Title": "加载存档", "Info.Hint.Load.File.Intro": "释放以加载拽入的存档", diff --git a/source/Model/Model.ts b/source/Model/Model.ts index 612939a..b579e50 100644 --- a/source/Model/Model.ts +++ b/source/Model/Model.ts @@ -349,6 +349,14 @@ class Model extends Emitter { return newClip; } + public getClipById(id: ObjectID): Clip | undefined { + for (let i = 0; i < this.clipPool.length; i++) { + if (this.clipPool[i].id.toString() === id.toString()) { + return this.clipPool[i]; + } + } + } + /** * 删除一个剪辑片段 */ diff --git a/source/Page/SimulatorDesktop/SimulatorDesktop.tsx b/source/Page/SimulatorDesktop/SimulatorDesktop.tsx index 63b0e8d..06b5639 100644 --- a/source/Page/SimulatorDesktop/SimulatorDesktop.tsx +++ b/source/Page/SimulatorDesktop/SimulatorDesktop.tsx @@ -64,7 +64,7 @@ class SimulatorDesktop extends Component { items: [ {panels: ["RenderView"]}, { - items: [{panels: ["BehaviorList"]}, {panels: ["LabelList"]}], + items: [{panels: ["BehaviorList", "ClipPlayer"]}, {panels: ["LabelList"]}], scale: 80, layout: LayoutDirection.X } @@ -76,7 +76,7 @@ class SimulatorDesktop extends Component { items: [{ panels: ["ObjectList"] }, { - panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails"] + panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails", "ClipDetails"] }], scale: 30, layout: LayoutDirection.Y diff --git a/source/Page/SimulatorWeb/SimulatorWeb.tsx b/source/Page/SimulatorWeb/SimulatorWeb.tsx index cfdfd1f..a520ff0 100644 --- a/source/Page/SimulatorWeb/SimulatorWeb.tsx +++ b/source/Page/SimulatorWeb/SimulatorWeb.tsx @@ -55,7 +55,7 @@ class SimulatorWeb extends Component { items: [ {panels: ["RenderView"]}, { - items: [{panels: ["ClipPlayer", "BehaviorList"]}, {panels: ["LabelList"]}], + items: [{panels: ["BehaviorList", "ClipPlayer"]}, {panels: ["LabelList"]}], scale: 80, layout: LayoutDirection.X } @@ -67,7 +67,7 @@ class SimulatorWeb extends Component { items: [{ panels: ["ObjectList"] }, { - panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails"] + panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails", "ClipDetails"] }], scale: 30, layout: LayoutDirection.Y diff --git a/source/Panel/ClipDetails/ClipDetails.scss b/source/Panel/ClipDetails/ClipDetails.scss new file mode 100644 index 0000000..e69de29 diff --git a/source/Panel/ClipDetails/ClipDetails.tsx b/source/Panel/ClipDetails/ClipDetails.tsx new file mode 100644 index 0000000..9176225 --- /dev/null +++ b/source/Panel/ClipDetails/ClipDetails.tsx @@ -0,0 +1,60 @@ +import { Component, ReactNode } from "react"; +import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; +import { TogglesInput } from "@Input/TogglesInput/TogglesInput"; +import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; +import { AttrInput } from "@Input/AttrInput/AttrInput"; +import { Message } from "@Input/Message/Message"; +import { Clip } from "@Model/Clip"; +import "./ClipDetails.scss"; + +@useStatusWithEvent("focusClipChange", "clipAttrChange") +class ClipDetails extends Component { + + private renderFrom(clip: Clip) { + return <> + + + + { + if (this.props.status) { + this.props.status.changeClipAttrib(clip.id, "name", value); + } + }} + /> + + { + const status = this.props.status; + if (status) { + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Delete.Clip.Confirm", + titleI18N: "Popup.Action.Objects.Confirm.Title", + yesI18n: "Popup.Action.Objects.Confirm.Delete", + red: "yes", + yes: () => { + status.setClipObject(); + this.props.status?.actuator.endPlay(); + status.model.deleteClip(clip.id); + } + }); + } + }} + /> + + ; + } + + public render(): ReactNode { + if (this.props.status && this.props.status.focusClip) { + return this.renderFrom(this.props.status.focusClip); + } + return ; + } +} + +export { ClipDetails }; \ No newline at end of file diff --git a/source/Panel/ClipPlayer/ClipPlayer.tsx b/source/Panel/ClipPlayer/ClipPlayer.tsx index 9d27929..1fb8009 100644 --- a/source/Panel/ClipPlayer/ClipPlayer.tsx +++ b/source/Panel/ClipPlayer/ClipPlayer.tsx @@ -1,6 +1,7 @@ import { Component, ReactNode } from "react"; import { ClipList } from "@Component/ClipList/ClipList"; import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; +import { useSetting, IMixinSettingProps } from "@Context/Setting"; import { BackgroundLevel, FontLevel, Theme } from "@Component/Theme/Theme"; import { Message } from "@Input/Message/Message"; import { Clip } from "@Model/Clip"; @@ -9,8 +10,9 @@ import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; import { OfflineRender } from "@Component/OfflineRender/OfflineRender" import "./ClipPlayer.scss"; -@useStatusWithEvent("clipChange", "focusClipChange", "actuatorStartChange") -class ClipPlayer extends Component { +@useSetting +@useStatusWithEvent("clipChange", "focusClipChange", "actuatorStartChange", "clipAttrChange") +class ClipPlayer extends Component { private isInnerClick: boolean = false; @@ -57,6 +59,7 @@ class ClipPlayer extends Component { this.isInnerClick = true; this.props.status?.setClipObject(clip); this.props.status?.actuator.startPlay(clip); + this.props.setting?.layout.focus("ClipDetails"); }} />; } diff --git a/source/Panel/ClipPlayer/ClipRecorder.tsx b/source/Panel/ClipPlayer/ClipRecorder.tsx index 46fda3f..f29780e 100644 --- a/source/Panel/ClipPlayer/ClipRecorder.tsx +++ b/source/Panel/ClipPlayer/ClipRecorder.tsx @@ -3,7 +3,7 @@ import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; import { Recorder } from "@Component/Recorder/Recorder"; import { ActuatorModel } from "@Model/Actuator"; -@useStatusWithEvent("actuatorStartChange", "focusClipChange", "recordLoop") +@useStatusWithEvent("actuatorStartChange", "focusClipChange", "recordLoop", "clipAttrChange") class ClipRecorder extends Component { public render(): ReactNode { diff --git a/source/Panel/Panel.tsx b/source/Panel/Panel.tsx index ef3542b..b4cc8c1 100644 --- a/source/Panel/Panel.tsx +++ b/source/Panel/Panel.tsx @@ -12,6 +12,7 @@ import { BehaviorList } from "@Panel/BehaviorList/BehaviorList"; import { BehaviorDetails } from "@Panel/BehaviorDetails/BehaviorDetails"; import { ClipPlayer } from "@Panel/ClipPlayer/ClipPlayer"; import { ClipRecorder } from "@Panel/ClipPlayer/ClipRecorder"; +import { ClipDetails } from "@Panel/ClipDetails/ClipDetails"; interface IPanelInfo { nameKey: string; @@ -34,6 +35,7 @@ type PanelId = "" | "BehaviorList" // 行为列表 | "BehaviorDetails" // 行为属性 | "ClipPlayer" // 剪辑影片 +| "ClipDetails" // 剪辑详情 ; const PanelInfoMap = new Map(); @@ -73,6 +75,10 @@ PanelInfoMap.set("ClipPlayer", { nameKey: "Panel.Title.Behavior.Clip.Player", introKay: "Panel.Info.Behavior.Clip.Player", class: ClipPlayer, header: ClipRecorder, hidePadding: true }); +PanelInfoMap.set("ClipDetails", { + nameKey: "Panel.Title.Behavior.Clip.Details", introKay: "Panel.Info.Behavior.Clip.Details", + class: ClipDetails +}); function getPanelById(panelId: PanelId): ReactNode { switch (panelId) {