From 09865d02beaa7c9ec1c9417c69d3484cb559b6eb Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Mon, 2 Jun 2025 12:17:00 +0900 Subject: [PATCH 1/6] [Update] Config Page: VR: Add adjust buttons to each position and rotation sliders for become much easier handling on VR controller. --- .../setting_box/_components/slider/Slider.jsx | 115 +++++++--- .../setting_section/setting_box/vr/Vr.jsx | 205 ++++++++++++++++-- .../setting_box/vr/Vr.module.scss | 120 ++++++++-- src-ui/ui_configs.js | 6 + 4 files changed, 388 insertions(+), 58 deletions(-) diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.jsx index 8f61d725..827c0253 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.jsx @@ -4,44 +4,107 @@ import MUI_Slider from "@mui/material/Slider"; import clsx from "clsx"; export const Slider = (props) => { + const location = props.valueLabelDisplayLocation || "top"; + + const sliderSx = { + color: "var(--dark_700_color)", + "& .MuiSlider-thumb": { + backgroundColor: "var(--primary_600_color)", + "&:hover, &.Mui-focusVisible, &.Mui-active": { + boxShadow: `0 0 0 0.8rem var(--primary_600_color_44)`, + }, + "& .MuiSlider-valueLabel": { + position: "absolute", + backgroundColor: "var(--dark_800_color)", + width: "fit-content", + minWidth: "4.8rem", + padding: "0.4rem 0.8rem", + lineHeight: "1.15", + "& .MuiSlider-valueLabelLabel": { + fontSize: "1.4rem", + }, + ...(location === "top" && { + top: "-110%", + left: "50%", + transform: "translate(-50%, -50%) scale(0)", + transformOrigin: "bottom center", + "&.MuiSlider-valueLabelOpen": { + transform: "translate(-50%, -50%) scale(1)", + }, + "&::before": { + bottom: "0%", + left: "50%", + }, + }), + ...(location === "right" && { + top: "50%", + left: "150%", + transform: "translate(0, -50%) scale(0)", + transformOrigin: "left center", + "&.MuiSlider-valueLabelOpen": { + transform: "translate(0, -50%) scale(1)", + }, + "&::before": { + bottom: "50%", + left: "0", + }, + }), + ...(location === "left" && { + // top: "50%", + // right: "50%", + // transform: "translate(-50%, -50%) scale(0)", + // transformOrigin: "bottom center", + // "&.MuiSlider-valueLabelOpen": { + // transform: "translate(-50%, -50%) scale(1)", + // }, + // "&::before": { + // bottom: "50%", + // left: "100%", + // }, + }), + }, + }, + "& .MuiSlider-markLabel": { + fontSize: "1.4rem", + color: "var(--dark_550_color)", + whiteSpace: "nowrap", + }, + "& .MuiSlider-markLabelActive": { + color: "var(--primary_300_color)", + }, + }; + return ( -
{t("config_page.vr.x_position")} @@ -205,8 +249,16 @@ const PositionControls = ({settings, onchangeFunction, selectFunction, ui_config min={ui_configs.x_pos.min} max={ui_configs.x_pos.max} onchangeFunction={(value) => onchangeFunction("x_pos", value)} + valueLabelDisplay={x_variable_display} + valueLabelDisplayLocation="top" /> +
{t("config_page.vr.y_position")} @@ -221,8 +273,16 @@ const PositionControls = ({settings, onchangeFunction, selectFunction, ui_config max={ui_configs.y_pos.max} onchangeFunction={(value) => onchangeFunction("y_pos", value)} orientation="vertical" + valueLabelDisplay={y_variable_display} + valueLabelDisplayLocation="right" /> +
{t("config_page.vr.z_position")} @@ -237,69 +297,127 @@ const PositionControls = ({settings, onchangeFunction, selectFunction, ui_config max={ui_configs.z_pos.max} onchangeFunction={(value) => onchangeFunction("z_pos", value)} orientation="vertical" + valueLabelDisplay={z_variable_display} + valueLabelDisplayLocation="left" /> +
{t("config_page.vr.x_rotation")}
-
{t("config_page.vr.y_rotation")}
-
{t("config_page.vr.z_rotation")}
-
{label}
); +}; + + + +const useVariableControl = (key, settings, onchangeFunction, ui_configs) => { + const [variable_display, setVariableDisplay] = useState("auto"); + + const [is_max, setIsMax] = useState(settings[key] >= ui_configs[key].max); + const [is_min, setIsMin] = useState(settings[key] <= ui_configs[key].min); + + const timerRef = useRef(); + + // アンマウント時にタイマーをクリアする + useEffect(() => { + return () => { + clearTimeout(timerRef.current); + }; + }, []); + + const triggerDisplay = () => { + setVariableDisplay("on"); + clearTimeout(timerRef.current); + timerRef.current = setTimeout(() => { + setVariableDisplay("auto"); + }, 2000); + }; + + useEffect(() => { + setIsMax(settings[key] >= ui_configs[key].max); + setIsMin(settings[key] <= ui_configs[key].min); + }, [settings[key]]); + + const countUp = () => { + if (is_max) return; + const step = ui_configs[key].step; + const new_value = parseFloat((settings[key] + step).toFixed(2)); + onchangeFunction(key, new_value); + triggerDisplay(); + }; + + const countDown = () => { + if (is_min) return; + const step = ui_configs[key].step; + const new_value = parseFloat((settings[key] - step).toFixed(2)); + onchangeFunction(key, new_value); + triggerDisplay(); + }; + + return { + variable_display, + is_max, + is_min, + countUp, + countDown, + }; }; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/vr/Vr.module.scss b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.module.scss index 35798530..eb37c7ea 100644 --- a/src-ui/app/config_page/setting_section/setting_box/vr/Vr.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.module.scss @@ -16,7 +16,7 @@ justify-content: center; width: 100%; max-width: 56rem; - gap: 2rem; + gap: 4rem; } .controller_type_switch { @@ -58,12 +58,13 @@ .sample_text_button_wrapper { position: absolute; - bottom: 0; - left: -74%; + bottom: -12%; + left: -80%; display: flex; justify-content: center; align-items: center; flex-direction: column; + // transform: translate(-50%, -50%); } .sample_text_button { background-color: var(--dark_850_color); @@ -121,20 +122,20 @@ } .x_position_label { position: absolute; - bottom: -4.6rem; + bottom: -5rem; right: -46%; justify-content: end; } .y_position_label { position: absolute; - top: -44%; - left: 10%; - justify-content: start; + bottom: 110%; + right: 119%; + justify-content: end; } .z_position_label { position: absolute; - top: 30%; - left: 80%; + top: 14%; + left: 110%; } .x_position_slider { @@ -155,14 +156,72 @@ .z_position_slider { position: absolute; - bottom: 61%; - left: 61%; + bottom: 80%; + left: 88%; transform: translate(50%,50%) rotate(45deg); width: 0%; height: 100%; } +%variable-button { + width: 3.8rem; + border-radius: 0.4rem; + aspect-ratio: 1.2 / 1; + background-color: var(--dark_850_color); + display: flex; + justify-content: center; + align-items: center; + font-size: 1.6rem; + cursor: pointer; + + &:hover { + background-color: var(--primary_500_color); + } + &:active { + background-color: var(--primary_600_color); + } + &.is_disabled { + pointer-events: none; + background-color: var(--dark_875_color); + color: var(--dark_600_color); + } +} + +@mixin variable-button-wrapper($vertical-pos, $vertical-value, $horizontal-pos, $horizontal-value, $rotate: 0deg) { + position: absolute; + #{$vertical-pos}: $vertical-value; + #{$horizontal-pos}: $horizontal-value; + display: flex; + gap: 1.6rem; + flex-direction: column; + transform: translate(-50%) rotate($rotate); +} + +.y_position_button_wrapper { + @include variable-button-wrapper(top, 30%, left, -26%); +} +.y_position_button_up, +.y_position_button_down { + @extend %variable-button; +} + +.x_position_button_wrapper { + @include variable-button-wrapper(bottom, -38%, left, 46%, 90deg); +} +.x_position_button_up, +.x_position_button_down { + @extend %variable-button; +} + +.z_position_button_wrapper { + @include variable-button-wrapper(bottom, 26%, right, -4%, 45deg); +} +.z_position_button_up, +.z_position_button_down { + @extend %variable-button; +} + // .rotation_controls { @@ -175,19 +234,20 @@ .x_rotation_label { position: absolute; - top: -44%; - left: 10%; + bottom: 110%; + right: 119%; + justify-content: end; } .y_rotation_label { position: absolute; - bottom: -4.6rem; + bottom: -5rem; right: -46%; justify-content: end; } .z_rotation_label { position: absolute; - top: -10%; - right: -110%; + top: -20%; + right: -100%; } .x_rotation_slider { @@ -216,6 +276,34 @@ } + +.x_rotation_button_wrapper { + @include variable-button-wrapper(top, 30%, left, -26%); +} +.x_rotation_button_up, +.x_rotation_button_down { + @extend %variable-button; +} + +.y_rotation_button_wrapper { + @include variable-button-wrapper(bottom, -38%, left, 46%, 90deg); +} +.y_rotation_button_up, +.y_rotation_button_down { + @extend %variable-button; +} + +.z_rotation_button_wrapper { + @include variable-button-wrapper(bottom, 50%, right, -60%, -45deg); +} +.z_rotation_button_up, +.z_rotation_button_down { + @extend %variable-button; +} + + + + .slider_reset_button { background-color: var(--dark_875_color); padding: 0.6rem; diff --git a/src-ui/ui_configs.js b/src-ui/ui_configs.js index d6ef28eb..915d6a6b 100644 --- a/src-ui/ui_configs.js +++ b/src-ui/ui_configs.js @@ -7,12 +7,18 @@ export const ui_configs = { x_pos: { step: 0.05, min: -0.5, max: 0.5 }, y_pos: { step: 0.05, min: -0.8, max: 0.8 }, z_pos: { step: 0.05, min: -0.5, max: 1.5 }, + x_rotation: { min: -180, max: 180, step: 5 }, + y_rotation: { min: -180, max: 180, step: 5 }, + z_rotation: { min: -180, max: 180, step: 5 }, ui_scaling: { step: 10, min: 40, max: 200 }, }, overlay_large_log: { x_pos: { step: 0.05, min: -0.5, max: 0.5 }, y_pos: { step: 0.05, min: -0.8, max: 0.8 }, z_pos: { step: 0.05, min: -0.5, max: 1.5 }, + x_rotation: { min: -180, max: 180, step: 5 }, + y_rotation: { min: -180, max: 180, step: 5 }, + z_rotation: { min: -180, max: 180, step: 5 }, ui_scaling: { step: 10, min: 40, max: 200 }, }, From 2ea0a3b5291ee096a34ccd2ef2fa28c24963ae77 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Mon, 2 Jun 2025 13:33:00 +0900 Subject: [PATCH 2/6] [Update/Refactor] Config Page: VR: Change triangle icon from symbol to svg. Refactor the common component and function. --- .../setting_section/setting_box/vr/Vr.jsx | 141 ++++++++++-------- .../setting_box/vr/Vr.module.scss | 51 ++++--- 2 files changed, 105 insertions(+), 87 deletions(-) diff --git a/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx index 4c70e61c..b145bb44 100644 --- a/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx @@ -25,6 +25,10 @@ import { import RedoSvg from "@images/redo.svg?react"; +import SquareSvg from "@images/square.svg?react"; +import TriangleSvg from "@images/triangle.svg?react"; +import { randomIntMinMax } from "@utils"; + export const Vr = () => { const { t } = useTranslation(); const [is_opened_small_settings, setIsOpenedSmallSettings] = useState(true); @@ -188,15 +192,9 @@ const PageSwitcherContainer = (props) => { }; -/** - * PositionControls: - * - x_pos, y_pos, z_pos 用のスライダーとリセットボタン、上下ボタンを表示 - * - useVariableControl を使って、is_max/is_min/variable_display/countUp/countDown を共通化 - */ -const PositionControls = ({ settings, onchangeFunction, selectFunction, ui_configs, default_ui_configs }) => { +export const PositionControls = ({ settings, onchangeFunction, selectFunction, ui_configs, default_ui_configs }) => { const { t } = useTranslation(); - // x_pos 用のフック呼び出し const { variable_display: x_variable_display, is_max: is_max_position_x, @@ -205,7 +203,6 @@ const PositionControls = ({ settings, onchangeFunction, selectFunction, ui_confi countDown: countDownPositionX, } = useVariableControl("x_pos", settings, onchangeFunction, ui_configs); - // y_pos 用のフック呼び出し const { variable_display: y_variable_display, is_max: is_max_position_y, @@ -214,7 +211,6 @@ const PositionControls = ({ settings, onchangeFunction, selectFunction, ui_confi countDown: countDownPositionY, } = useVariableControl("y_pos", settings, onchangeFunction, ui_configs); - // z_pos 用のフック呼び出し const { variable_display: z_variable_display, is_max: is_max_position_z, @@ -223,19 +219,8 @@ const PositionControls = ({ settings, onchangeFunction, selectFunction, ui_confi countDown: countDownPositionZ, } = useVariableControl("z_pos", settings, onchangeFunction, ui_configs); - // 各ボタンの className を生成 - const x_position_button_up_classname = clsx(styles.x_position_button_up, { [styles.is_disabled]: is_max_position_x }); - const x_position_button_down_classname = clsx(styles.x_position_button_down, { [styles.is_disabled]: is_min_position_x }); - - const y_position_button_up_classname = clsx(styles.y_position_button_up, { [styles.is_disabled]: is_max_position_y }); - const y_position_button_down_classname = clsx(styles.y_position_button_down, { [styles.is_disabled]: is_min_position_y }); - - const z_position_button_up_classname = clsx(styles.z_position_button_up, { [styles.is_disabled]: is_max_position_z }); - const z_position_button_down_classname = clsx(styles.z_position_button_down, { [styles.is_disabled]: is_min_position_z }); - return ({t("config_page.vr.x_position")} @@ -252,13 +237,15 @@ const PositionControls = ({ settings, onchangeFunction, selectFunction, ui_confi valueLabelDisplay={x_variable_display} valueLabelDisplayLocation="top" /> -
{t("config_page.vr.y_position")} @@ -276,13 +263,15 @@ const PositionControls = ({ settings, onchangeFunction, selectFunction, ui_confi valueLabelDisplay={y_variable_display} valueLabelDisplayLocation="right" /> -
{t("config_page.vr.z_position")} @@ -300,16 +289,19 @@ const PositionControls = ({ settings, onchangeFunction, selectFunction, ui_confi valueLabelDisplay={z_variable_display} valueLabelDisplayLocation="left" /> -
{t("config_page.vr.y_rotation")} @@ -386,11 +373,15 @@ const RotationControls = ({settings, onchangeFunction, selectFunction, ui_config valueLabelDisplay={y_variable_display} valueLabelDisplayLocation="top" /> -
{t("config_page.vr.z_rotation")} @@ -408,10 +399,43 @@ const RotationControls = ({settings, onchangeFunction, selectFunction, ui_config valueLabelDisplay={z_variable_display} valueLabelDisplayLocation="left" /> -
{props.desc}
+{`- ${props.desc}`}
{settings.message}
-