diff --git a/source/Component/ClipList/ClipList.scss b/source/Component/ClipList/ClipList.scss new file mode 100644 index 0000000..2dc7023 --- /dev/null +++ b/source/Component/ClipList/ClipList.scss @@ -0,0 +1,131 @@ +@import "../Theme/Theme.scss"; + +$clip-item-height: 45px; + +div.clip-list-root { + margin: -5px; + display: flex; + flex-wrap: wrap; + + div.clip-item { + margin: 5px; + height: $clip-item-height; + user-select: none; + border-radius: 3px; + overflow: hidden; + cursor: pointer; + display: flex; + + div.clip-item-hole-view { + height: 100%; + width: 10px; + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-items: center; + justify-content: space-between; + padding: 5px; + padding-right: 0; + + div.clip-item-hole { + width: 5px; + height: 5px; + background-color: #000000; + flex-shrink: 0; + } + } + + div.clip-icon-view { + width: $clip-item-height; + height: $clip-item-height; + display: flex; + justify-content: center; + align-items: center; + + i.icon { + display: inline-block; + font-size: 25px; + } + + i.delete { + display: none; + } + + i.delete:hover { + color: $lt-red; + } + } + + div.clip-item-content { + height: $clip-item-height; + display: flex; + flex-direction: column; + justify-content: center; + + } + } + + div.clip-item:hover { + + div.clip-icon-view { + + i.icon { + display: none; + } + + i.delete { + display: inline-block; + } + } + } + + div.add-button { + width: 26px; + height: 26px; + display: flex; + justify-content: center; + align-items: center; + } +} + +div.dark.clip-list-root { + + div.clip-item { + background-color: $lt-bg-color-lvl3-dark; + + div.clip-item-hole-view div.clip-item-hole { + background-color: $lt-bg-color-lvl4-dark; + } + } + + div.clip-item:hover { + color: $lt-font-color-lvl2-dark; + background-color: $lt-bg-color-lvl2-dark; + } + + div.clip-item.focus { + color: $lt-font-color-lvl1-dark; + background-color: $lt-bg-color-lvl1-dark; + } +} + +div.light.clip-list-root { + + div.clip-item { + background-color: $lt-bg-color-lvl3-light; + + div.clip-item-hole-view div.clip-item-hole { + background-color: $lt-bg-color-lvl4-light; + } + } + + div.clip-item:hover { + color: $lt-font-color-lvl2-light; + background-color: $lt-bg-color-lvl2-light; + } + + div.clip-item.focus { + color: $lt-font-color-lvl1-light; + background-color: $lt-bg-color-lvl1-light; + } +} \ No newline at end of file diff --git a/source/Component/ClipList/ClipList.tsx b/source/Component/ClipList/ClipList.tsx new file mode 100644 index 0000000..9d2c7e2 --- /dev/null +++ b/source/Component/ClipList/ClipList.tsx @@ -0,0 +1,61 @@ +import { Theme } from "@Component/Theme/Theme"; +import { Icon } from "@fluentui/react"; +import { Clip } from "@Model/Clip"; +import { Component, ReactNode } from "react"; +import "./ClipList.scss"; + +interface IClipListProps { + clips: Clip[]; + add?: () => any; + delete?: (clip: Clip) => any; +} + +class ClipList extends Component { + + private renderClip(clip: Clip) { + return
+
+ {new Array(4).fill(0).map(() => { + return
+ })} +
+
+ + { + this.props.delete && this.props.delete(clip); + }} + /> +
+
+
{clip.name}
+
{clip.frames.length}
+
+
; + } + + private renderAddButton(): ReactNode { + return
+ +
+ } + + public render(): ReactNode { + return + { this.props.clips.map((clip => { + return this.renderClip(clip); + })) } + { this.renderAddButton() } + ; + } +} + +export { ClipList }; \ No newline at end of file diff --git a/source/Component/Recorder/Recorder.scss b/source/Component/Recorder/Recorder.scss index 33a22cc..2f03595 100644 --- a/source/Component/Recorder/Recorder.scss +++ b/source/Component/Recorder/Recorder.scss @@ -3,7 +3,7 @@ div.recorder-root { width: 100%; box-sizing: border-box; - padding: 10px; + padding: 10px 10px 0 10px; div.recorder-slider { width: 100%; diff --git a/source/Component/Recorder/Recorder.tsx b/source/Component/Recorder/Recorder.tsx index 64ec2b1..d75e3d9 100644 --- a/source/Component/Recorder/Recorder.tsx +++ b/source/Component/Recorder/Recorder.tsx @@ -62,7 +62,7 @@ class Recorder extends Component { if (this.props.running) { return "Stop"; } else { - return "CircleStop"; + return "StatusCircleRing"; } } return "Play"; diff --git a/source/Context/Status.tsx b/source/Context/Status.tsx index 9821508..2ac2e14 100644 --- a/source/Context/Status.tsx +++ b/source/Context/Status.tsx @@ -424,6 +424,22 @@ class Status extends Emitter { return label; } + public newClip() { + let searchKey = I18N(this.setting.language, "Object.List.New.Clip", { id: "" }); + let nextIndex = 1; + this.model.clipPool.forEach((obj) => { + nextIndex = Math.max(nextIndex, this.getNextNumber( + obj.name, searchKey + )); + }); + const clip = this.model.addClip( + I18N(this.setting.language, "Object.List.New.Clip", { + id: nextIndex.toString() + }) + ); + return clip; + } + public setMouseMod(mod: MouseMod) { this.mouseMod = mod; if (this.renderer instanceof ClassicRenderer) { diff --git a/source/Localization/EN-US.ts b/source/Localization/EN-US.ts index dd250fc..d748ce2 100644 --- a/source/Localization/EN-US.ts +++ b/source/Localization/EN-US.ts @@ -31,6 +31,7 @@ const EN_US = { "Object.List.New.Group": "Group object {id}", "Object.List.New.Range": "Range object {id}", "Object.List.New.Label": "Label {id}", + "Object.List.New.Clip": "Clip {id}", "Object.List.No.Data": "There are no objects in the model, click the button to create it", "Object.Picker.List.No.Data": "There is no model in the model for this option", "Behavior.Picker.Add.Button": "Click here to assign behavior to this group", @@ -148,6 +149,7 @@ const EN_US = { "Panel.Info.Behavior.Details.Parameter.Key.Vec.X": "{key} X", "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", "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 8f44553..77c55ea 100644 --- a/source/Localization/ZH-CN.ts +++ b/source/Localization/ZH-CN.ts @@ -31,6 +31,7 @@ const ZH_CN = { "Object.List.New.Group": "群对象 {id}", "Object.List.New.Range": "范围对象 {id}", "Object.List.New.Label": "标签 {id}", + "Object.List.New.Clip": "剪辑片段 {id}", "Object.List.No.Data": "模型中没有任何对象,点击按钮以创建", "Object.Picker.List.No.Data": "模型中没有合适此选项的模型", "Behavior.Picker.Add.Button": "点击此处以赋予行为到此群", @@ -148,6 +149,7 @@ const ZH_CN = { "Panel.Info.Behavior.Details.Parameter.Key.Vec.X": "{key} X 坐标", "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": "没有剪辑片段,请点击录制按钮录制,或者点击加号创建", "Info.Hint.Save.After.Close": "任何未保存的进度都会丢失, 确定要继续吗?", "Info.Hint.Load.File.Title": "加载存档", "Info.Hint.Load.File.Intro": "释放以加载拽入的存档", diff --git a/source/Panel/ClipPlayer/ClipPlayer.scss b/source/Panel/ClipPlayer/ClipPlayer.scss index e69de29..abfe431 100644 --- a/source/Panel/ClipPlayer/ClipPlayer.scss +++ b/source/Panel/ClipPlayer/ClipPlayer.scss @@ -0,0 +1,6 @@ +div.Clip-player-clip-list-root { + width: 100%; + height: 100%; + box-sizing: border-box; + padding: 10px; +} \ No newline at end of file diff --git a/source/Panel/ClipPlayer/ClipPlayer.tsx b/source/Panel/ClipPlayer/ClipPlayer.tsx index 557871c..088127a 100644 --- a/source/Panel/ClipPlayer/ClipPlayer.tsx +++ b/source/Panel/ClipPlayer/ClipPlayer.tsx @@ -1,12 +1,31 @@ import { Component, ReactNode } from "react"; +import { ClipList } from "@Component/ClipList/ClipList"; +import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; +import { Theme } from "@Component/Theme/Theme"; +import { Message } from "@Input/Message/Message"; +import { Clip } from "@Model/Clip"; import "./ClipPlayer.scss"; -class ClipPlayer extends Component { +@useStatusWithEvent("clipChange", "focusClipChange", "actuatorStartChange") +class ClipPlayer extends Component { + + private renderMessage(): ReactNode { + return ; + } + + private renderClipList(clipList: Clip[]): ReactNode { + return ; + } public render(): ReactNode { - return <> - - ; + const clipList = this.props.status?.model.clipPool ?? []; + + return + { clipList.length > 0 ? null : this.renderMessage() } + { this.renderClipList(clipList) } + ; } } diff --git a/source/Panel/ClipPlayer/ClipRecorder.tsx b/source/Panel/ClipPlayer/ClipRecorder.tsx index 65f0a93..8115f5d 100644 --- a/source/Panel/ClipPlayer/ClipRecorder.tsx +++ b/source/Panel/ClipPlayer/ClipRecorder.tsx @@ -37,7 +37,7 @@ class ClipRecorder extends Component { if (mod === "R" && !runner) { // 获取新实例 - let newClip = this.props.status?.model.addClip(); + let newClip = this.props.status?.newClip(); // 开启录制时钟 this.props.status?.actuator.startRecord(newClip!);