diff --git a/src/pages/edit/Editor/components/FrameControl.tsx b/src/pages/edit/Editor/components/FrameControl.tsx
index d44a059..b3a7858 100644
--- a/src/pages/edit/Editor/components/FrameControl.tsx
+++ b/src/pages/edit/Editor/components/FrameControl.tsx
@@ -1,59 +1,150 @@
import { ChevronLeft, ChevronRight } from "@mui/icons-material";
-import { IconButton, Paper } from "@mui/material";
+import { Box, IconButton, Paper, TextField, ToggleButton } from "@mui/material";
+import { useState } from "react";
+import { useInterval } from "react-use";
import { useComponentEditorStore } from "../store";
+import {
+ isValidPlaybackSpeed,
+ MAX_PLAYBACK_SPEED,
+ MIN_PLAYBACK_SPEED,
+} from "../store/slices/core/types";
export default function CCComponentEditorFrameControl() {
- const componentEditorState = useComponentEditorStore()();
+ const componentEditorStore = useComponentEditorStore();
+ const componentEditorState = componentEditorStore();
+ const [speedInput, setSpeedInput] = useState(
+ String(componentEditorState.playbackSpeed),
+ );
+ const isAutomatic = componentEditorState.playbackMode === "auto";
+ const isSpeedValid = isValidPlaybackSpeed(Number(speedInput));
+
+ useInterval(
+ () => {
+ const state = componentEditorStore.getState();
+ if (state.editorMode === "play" && state.playbackMode === "auto") {
+ state.setTimeStep(state.timeStep + 1);
+ }
+ },
+ componentEditorState.editorMode === "play" && isAutomatic
+ ? 1000 / componentEditorState.playbackSpeed
+ : null,
+ );
if (componentEditorState.editorMode === "edit") return null;
return (
-
- {
- componentEditorState.setTimeStep(componentEditorState.timeStep - 1);
+
-
-
- {
- const value = Number.parseInt(e.target.value, 10);
- if (!Number.isNaN(value) && value > 0) {
- componentEditorState.setTimeStep(value - 1);
+ {
+ componentEditorState.setPlaybackMode(
+ isAutomatic ? "manual" : "auto",
+ );
+ }}
+ >
+ Auto
+
+
+ {
+ componentEditorState.setTimeStep(
+ componentEditorState.timeStep - 1,
+ );
+ }}
+ >
+
+
+ {
+ const value = Number(e.target.value);
+ if (Number.isSafeInteger(value) && value > 0) {
+ componentEditorState.setTimeStep(value - 1);
+ }
+ }}
+ />
+ {
+ componentEditorState.setTimeStep(
+ componentEditorState.timeStep + 1,
+ );
+ }}
+ >
+
+
+
+
- {
- componentEditorState.setTimeStep(componentEditorState.timeStep + 1);
- }}
- >
-
-
-
+ slotProps={{
+ htmlInput: {
+ min: MIN_PLAYBACK_SPEED,
+ max: MAX_PLAYBACK_SPEED,
+ step: "any",
+ },
+ }}
+ onChange={(e) => {
+ setSpeedInput(e.target.value);
+ componentEditorState.setPlaybackSpeed(Number(e.target.value));
+ }}
+ onBlur={() => {
+ setSpeedInput(
+ String(componentEditorStore.getState().playbackSpeed),
+ );
+ }}
+ />
+
+
);
}
diff --git a/src/pages/edit/Editor/renderer/Node/components/Display/ConfigSettingButton.tsx b/src/pages/edit/Editor/renderer/Node/components/Display/ConfigSettingButton.tsx
index b44d29b..d72e591 100644
--- a/src/pages/edit/Editor/renderer/Node/components/Display/ConfigSettingButton.tsx
+++ b/src/pages/edit/Editor/renderer/Node/components/Display/ConfigSettingButton.tsx
@@ -3,6 +3,7 @@ import {
Button,
FormLabel,
IconButton,
+ MenuItem,
Popover,
Stack,
TextField,
@@ -97,6 +98,23 @@ export function CCComponentEditorRendererNodeDisplayRendererConfigSettingButton(
}}
/>
+ {
+ const pixelOrder = e.target.value;
+ if (pixelOrder === "row-major" || pixelOrder === "column-major") {
+ setNewConfig((prev) => ({ ...prev, pixelOrder }));
+ }
+ }}
+ size="small"
+ fullWidth
+ sx={{ mt: 2 }}
+ >
+
+
+