Add clip details panel

This commit is contained in:
MrKBear 2022-05-02 16:09:02 +08:00
parent 7002d49155
commit e3681f2f32
11 changed files with 103 additions and 7 deletions

View File

@ -53,6 +53,7 @@ interface IStatusEvent {
labelAttrChange: void; labelAttrChange: void;
groupAttrChange: void; groupAttrChange: void;
behaviorAttrChange: void; behaviorAttrChange: void;
clipAttrChange: void;
individualChange: void; individualChange: void;
behaviorChange: void; behaviorChange: void;
popupChange: void; popupChange: void;
@ -286,6 +287,18 @@ class Status extends Emitter<IStatusEvent> {
} }
} }
/**
*
*/
public changeClipAttrib<K extends keyof Clip>
(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) { public addGroupBehavior(id: ObjectID, val: Behavior) {
const group = this.model.getObjectById(id); const group = this.model.getObjectById(id);
if (group && group instanceof Group) { if (group && group instanceof Group) {

View File

@ -56,6 +56,8 @@ const EN_US = {
"Panel.Info.Behavior.Details.View": "Edit view Behavior attributes", "Panel.Info.Behavior.Details.View": "Edit view Behavior attributes",
"Panel.Title.Behavior.Clip.Player": "Recording", "Panel.Title.Behavior.Clip.Player": "Recording",
"Panel.Info.Behavior.Clip.Player": "Pre render recorded data", "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.Time.Formate": "{current} / {all} / {fps}fps",
"Panel.Info.Behavior.Clip.Record.Formate": "Record: {time}", "Panel.Info.Behavior.Clip.Record.Formate": "Record: {time}",
"Panel.Info.Behavior.Clip.Uname.Clip": "Waiting for recording...", "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.Y": "{key} Y",
"Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z", "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.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.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.Title": "Load save",
"Info.Hint.Load.File.Intro": "Release to load the dragged save file", "Info.Hint.Load.File.Intro": "Release to load the dragged save file",

View File

@ -56,6 +56,8 @@ const ZH_CN = {
"Panel.Info.Behavior.Details.View": "编辑查看行为属性", "Panel.Info.Behavior.Details.View": "编辑查看行为属性",
"Panel.Title.Behavior.Clip.Player": "录制", "Panel.Title.Behavior.Clip.Player": "录制",
"Panel.Info.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.Time.Formate": "{current} / {all} / {fps} fps",
"Panel.Info.Behavior.Clip.Record.Formate": "录制: {time}", "Panel.Info.Behavior.Clip.Record.Formate": "录制: {time}",
"Panel.Info.Behavior.Clip.Uname.Clip": "等待录制...", "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.Y": "{key} Y 坐标",
"Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z 坐标", "Panel.Info.Behavior.Details.Parameter.Key.Vec.Z": "{key} Z 坐标",
"Panel.Info.Clip.List.Error.Nodata": "没有剪辑片段,请点击录制按钮录制,或者点击加号创建", "Panel.Info.Clip.List.Error.Nodata": "没有剪辑片段,请点击录制按钮录制,或者点击加号创建",
"Panel.Info.Clip.Details.Error.Nodata": "请指定一个剪辑片段以查看属性",
"Info.Hint.Save.After.Close": "任何未保存的进度都会丢失, 确定要继续吗?", "Info.Hint.Save.After.Close": "任何未保存的进度都会丢失, 确定要继续吗?",
"Info.Hint.Load.File.Title": "加载存档", "Info.Hint.Load.File.Title": "加载存档",
"Info.Hint.Load.File.Intro": "释放以加载拽入的存档", "Info.Hint.Load.File.Intro": "释放以加载拽入的存档",

View File

@ -349,6 +349,14 @@ class Model extends Emitter<ModelEvent> {
return newClip; 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];
}
}
}
/** /**
* *
*/ */

View File

@ -64,7 +64,7 @@ class SimulatorDesktop extends Component {
items: [ items: [
{panels: ["RenderView"]}, {panels: ["RenderView"]},
{ {
items: [{panels: ["BehaviorList"]}, {panels: ["LabelList"]}], items: [{panels: ["BehaviorList", "ClipPlayer"]}, {panels: ["LabelList"]}],
scale: 80, scale: 80,
layout: LayoutDirection.X layout: LayoutDirection.X
} }
@ -76,7 +76,7 @@ class SimulatorDesktop extends Component {
items: [{ items: [{
panels: ["ObjectList"] panels: ["ObjectList"]
}, { }, {
panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails"] panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails", "ClipDetails"]
}], }],
scale: 30, scale: 30,
layout: LayoutDirection.Y layout: LayoutDirection.Y

View File

@ -55,7 +55,7 @@ class SimulatorWeb extends Component {
items: [ items: [
{panels: ["RenderView"]}, {panels: ["RenderView"]},
{ {
items: [{panels: ["ClipPlayer", "BehaviorList"]}, {panels: ["LabelList"]}], items: [{panels: ["BehaviorList", "ClipPlayer"]}, {panels: ["LabelList"]}],
scale: 80, scale: 80,
layout: LayoutDirection.X layout: LayoutDirection.X
} }
@ -67,7 +67,7 @@ class SimulatorWeb extends Component {
items: [{ items: [{
panels: ["ObjectList"] panels: ["ObjectList"]
}, { }, {
panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails"] panels: ["GroupDetails", "RangeDetails", "LabelDetails", "BehaviorDetails", "ClipDetails"]
}], }],
scale: 30, scale: 30,
layout: LayoutDirection.Y layout: LayoutDirection.Y

View File

@ -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<IMixinStatusProps> {
private renderFrom(clip: Clip) {
return <>
<Message i18nKey="Common.Attr.Title.Basic" isTitle first/>
<AttrInput
keyI18n="Common.Attr.Key.Display.Name"
maxLength={15}
value={clip.name}
valueChange={(value) => {
if (this.props.status) {
this.props.status.changeClipAttrib(clip.id, "name", value);
}
}}
/>
<TogglesInput
keyI18n="Common.Attr.Key.Delete" onIconName="delete" red
offIconName="delete" valueChange={() => {
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 <Message i18nKey="Panel.Info.Clip.Details.Error.Nodata"/>;
}
}
export { ClipDetails };

View File

@ -1,6 +1,7 @@
import { Component, ReactNode } from "react"; import { Component, ReactNode } from "react";
import { ClipList } from "@Component/ClipList/ClipList"; import { ClipList } from "@Component/ClipList/ClipList";
import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
import { useSetting, IMixinSettingProps } from "@Context/Setting";
import { BackgroundLevel, FontLevel, Theme } from "@Component/Theme/Theme"; import { BackgroundLevel, FontLevel, Theme } from "@Component/Theme/Theme";
import { Message } from "@Input/Message/Message"; import { Message } from "@Input/Message/Message";
import { Clip } from "@Model/Clip"; import { Clip } from "@Model/Clip";
@ -9,8 +10,9 @@ import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup";
import { OfflineRender } from "@Component/OfflineRender/OfflineRender" import { OfflineRender } from "@Component/OfflineRender/OfflineRender"
import "./ClipPlayer.scss"; import "./ClipPlayer.scss";
@useStatusWithEvent("clipChange", "focusClipChange", "actuatorStartChange") @useSetting
class ClipPlayer extends Component<IMixinStatusProps> { @useStatusWithEvent("clipChange", "focusClipChange", "actuatorStartChange", "clipAttrChange")
class ClipPlayer extends Component<IMixinStatusProps & IMixinSettingProps> {
private isInnerClick: boolean = false; private isInnerClick: boolean = false;
@ -57,6 +59,7 @@ class ClipPlayer extends Component<IMixinStatusProps> {
this.isInnerClick = true; this.isInnerClick = true;
this.props.status?.setClipObject(clip); this.props.status?.setClipObject(clip);
this.props.status?.actuator.startPlay(clip); this.props.status?.actuator.startPlay(clip);
this.props.setting?.layout.focus("ClipDetails");
}} }}
/>; />;
} }

View File

@ -3,7 +3,7 @@ import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
import { Recorder } from "@Component/Recorder/Recorder"; import { Recorder } from "@Component/Recorder/Recorder";
import { ActuatorModel } from "@Model/Actuator"; import { ActuatorModel } from "@Model/Actuator";
@useStatusWithEvent("actuatorStartChange", "focusClipChange", "recordLoop") @useStatusWithEvent("actuatorStartChange", "focusClipChange", "recordLoop", "clipAttrChange")
class ClipRecorder extends Component<IMixinStatusProps> { class ClipRecorder extends Component<IMixinStatusProps> {
public render(): ReactNode { public render(): ReactNode {

View File

@ -12,6 +12,7 @@ import { BehaviorList } from "@Panel/BehaviorList/BehaviorList";
import { BehaviorDetails } from "@Panel/BehaviorDetails/BehaviorDetails"; import { BehaviorDetails } from "@Panel/BehaviorDetails/BehaviorDetails";
import { ClipPlayer } from "@Panel/ClipPlayer/ClipPlayer"; import { ClipPlayer } from "@Panel/ClipPlayer/ClipPlayer";
import { ClipRecorder } from "@Panel/ClipPlayer/ClipRecorder"; import { ClipRecorder } from "@Panel/ClipPlayer/ClipRecorder";
import { ClipDetails } from "@Panel/ClipDetails/ClipDetails";
interface IPanelInfo { interface IPanelInfo {
nameKey: string; nameKey: string;
@ -34,6 +35,7 @@ type PanelId = ""
| "BehaviorList" // 行为列表 | "BehaviorList" // 行为列表
| "BehaviorDetails" // 行为属性 | "BehaviorDetails" // 行为属性
| "ClipPlayer" // 剪辑影片 | "ClipPlayer" // 剪辑影片
| "ClipDetails" // 剪辑详情
; ;
const PanelInfoMap = new Map<PanelId, IPanelInfo>(); const PanelInfoMap = new Map<PanelId, IPanelInfo>();
@ -73,6 +75,10 @@ PanelInfoMap.set("ClipPlayer", {
nameKey: "Panel.Title.Behavior.Clip.Player", introKay: "Panel.Info.Behavior.Clip.Player", nameKey: "Panel.Title.Behavior.Clip.Player", introKay: "Panel.Info.Behavior.Clip.Player",
class: ClipPlayer, header: ClipRecorder, hidePadding: true 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 { function getPanelById(panelId: PanelId): ReactNode {
switch (panelId) { switch (panelId) {