diff --git a/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx b/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx index d78ec87e..adec9373 100644 --- a/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx @@ -5,6 +5,7 @@ import { Appearance } from "./appearance/Appearance"; import { Transcription } from "./transcription/Transcription"; import { Others } from "./others/Others"; import { AdvancedSettings } from "./advanced_settings/AdvancedSettings"; +import { Vr } from "./vr/Vr"; // import { AboutVrct } from "./about_vrct/AboutVrct"; export const SettingBox = () => { @@ -18,6 +19,8 @@ export const SettingBox = () => { return ; case "others": return ; + case "vr": + return ; case "advanced_settings": return ; // case "about_vrct": 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 280ec3f9..7f96bd86 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 @@ -1,6 +1,7 @@ import React, { useState, useEffect } from "react"; import styles from "./Slider.module.scss"; import MUI_Slider from "@mui/material/Slider"; +import { clsx } from "clsx"; export const Slider = (props) => { const [baseColor, setBaseColor] = useState(""); @@ -17,10 +18,9 @@ export const Slider = (props) => { }, []); return ( -
+
{ min={Number(props.min)} max={Number(props.max)} onChange={(_e, value) => props.onchangeFunction(value)} - onChangeCommitted={(_e, value) => props.onchangeCommittedFunction(value)} + onChangeCommitted={(_e, value) => props.onchangeCommittedFunction ? props.onchangeCommittedFunction(value) : null} marks={props.marks} track={props.track} + orientation={props.orientation} + valueLabelFormat={`${props.valueLabelFormat ? props.valueLabelFormat : props.variable}`} sx={{ color: baseColor, - "& .MuiSlider-thumb": { - backgroundColor: activeColor, - "&:hover, &.Mui-focusVisible, &.Mui-active": { - boxShadow: "0 0 0 0.8rem" + activeColor + "44", - }, - "& .MuiSlider-valueLabel": { - fontSize: "1.4rem", - backgroundColor: toolTipColor, - padding: "0.6rem 1rem", - lineHeight: "1.15", - top: "-1.4rem", - "&::before": { - left: "30%", - width: "1rem", - height: "1rem", - clipPath: "polygon(50% 0, 100% 100%, 0 100%)", - } - } + "& .MuiSlider-thumb": { + backgroundColor: activeColor, + "&:hover, &.Mui-focusVisible, &.Mui-active": { + boxShadow: `0 0 0 0.8rem ${activeColor}44`, }, - "& .MuiSlider-markLabel": { + "& .MuiSlider-valueLabel": { fontSize: "1.4rem", - color: "white" + backgroundColor: toolTipColor, + padding: "0.6rem 1rem", + lineHeight: "1.15", + // top: "-1.4rem", + // "&::before": { + // left: "30%", + // width: "1rem", + // height: "1rem", + // clipPath: "polygon(50% 0, 100% 100%, 0 100%)", + // }, }, - "& .MuiSlider-markLabelActive": { - color: activeColor, - } - }} + }, + "& .MuiSlider-markLabel": { + fontSize: "1.4rem", + color: "white", + whiteSpace: "nowrap", + }, + "& .MuiSlider-markLabelActive": { + color: activeColor, + }, + }} />
); diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.module.scss index 873029b1..645a3a24 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.module.scss @@ -1,12 +1,30 @@ .container { display: flex; flex-direction: column; - align-items: end; + align-items: center; // 中央に揃え justify-content: center; width: 100%; - padding-left: 4rem; + // padding-left: 4rem; } .range_slider { - // max-width: 60rem; -} \ No newline at end of file + // スライダーの幅を調整(必要に応じて調整) + height: 200px; // 縦スライダーの高さ + padding: 10px 0; // スライダーの上下のパディング + + & .MuiSlider-thumb { + // スライダーのつまみのサイズを調整 + height: 24px; + width: 24px; + margin-top: -12px; // つまみが中心に来るように調整 + } + + & .MuiSlider-track { + height: 2px; // トラックの高さを調整 + } + + & .MuiSlider-vertical { + // 縦スライダー用のスタイル + height: 100%; // コンテナの高さにフィット + } +} 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 new file mode 100644 index 00000000..92bc97cf --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx @@ -0,0 +1,271 @@ +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import styles from "./Vr.module.scss"; +import { Slider } from "../_components/"; +import { clsx } from "clsx"; +import { + useOverlaySettings, + useOverlaySmallLogSettings, +} from "@logics_configs"; + +export const Vr = () => { + const { t } = useTranslation(); + const [is_opened_position_controller, setIsOpenedPositionController] = useState(true); + const toggleController = () => { + setIsOpenedPositionController(!is_opened_position_controller); + }; + + const { currentOverlaySmallLogSettings, setOverlaySmallLogSettings } = useOverlaySmallLogSettings(); + + const [settings, setSettings] = useState({ + x_pos: 0, + y_pos: 0, + z_pos: 0, + x_rotation: 0, + y_rotation: 0, + z_rotation: 0, + display_duration: 5, + fadeout_duration: 2, + }); + + const [timeout_id, setTimeoutId] = useState(null); + + const onchangeFunction = (key, value) => { + setSettings((prev) => { + const new_data = { ...prev, [key]: value }; + return new_data; + }); + + if (timeout_id) { + clearTimeout(timeout_id); + } + + const newTimeoutId = setTimeout(() => { + let new_data = settings; + new_data[key] = value; + setOverlaySmallLogSettings(new_data); + }, 50); + + setTimeoutId(newTimeoutId); + }; + + const toggle_button_class_names__position = clsx(styles.controller_type_switcher, { + [styles.is_selected]: is_opened_position_controller, + }); + const toggle_button_class_names__rotation = clsx(styles.controller_type_switcher, { + [styles.is_selected]: !is_opened_position_controller, + }); + + return ( +
+ +
+
+

Position

+
+
+

Rotation

+
+
+
+ {is_opened_position_controller + ? + : + } +
+ +
+ ); +}; + +const CommonControls = () => { + const { t } = useTranslation(); + const { currentOverlaySettings, setOverlaySettings } = useOverlaySettings(); + + const [settings, setSettings] = useState({ + opacity: 1, + ui_scaling: 1, + }); + + const [timeout_id, setTimeoutId] = useState(null); + + const onchangeFunction = (key, value) => { + setSettings((prev) => { + const new_data = { ...prev, [key]: value }; + return new_data; + }); + + if (timeout_id) { + clearTimeout(timeout_id); + } + + const newTimeoutId = setTimeout(() => { + let new_data = settings; + new_data[key] = value; + setOverlaySettings(settings); + }, 50); + + setTimeoutId(newTimeoutId); + }; + + const ui_variable_opacity = (settings.opacity * 100).toFixed(0); + const ui_variable_ui_scaling = (settings.ui_scaling * 100).toFixed(0); + + return ( +
+
+ + onchangeFunction("opacity", value / 100)} + /> +
+
+ + onchangeFunction("ui_scaling", value / 100)} + /> +
+
+ ); +}; + + + + +const PositionControls = ({settings, onchangeFunction}) => { + const { t } = useTranslation(); + + return ( +
+
+ + onchangeFunction("x_pos", value)} + /> +
+
+ + onchangeFunction("y_pos", value)} + orientation="vertical" + /> +
+
+ + onchangeFunction("z_pos", value)} + orientation="vertical" + /> +
+
+ ); +}; + +const RotationControls = ({settings, onchangeFunction}) => { + const { t } = useTranslation(); + + return ( +
+
+ + onchangeFunction("x_rotation", -value)} + orientation="vertical" + /> +
+
+ + onchangeFunction("y_rotation", value)} + /> +
+
+ + onchangeFunction("z_rotation", value)} + orientation="vertical" + /> +
+
+ ); +}; + +const OtherControls = ({settings, onchangeFunction}) => { + const { t } = useTranslation(); + + return( +
+
+ + onchangeFunction("display_duration", value)} + /> +
+
+ + onchangeFunction("fadeout_duration", value)} + /> +
+
+ ); +}; \ 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 new file mode 100644 index 00000000..7a00c692 --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.module.scss @@ -0,0 +1,215 @@ +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + padding: 2rem; +} + +.controller_type_switch { + margin-top: 6rem; + display: flex; + border: 0.1rem solid var(--dark_600_color); + border-radius: 0.4rem; + width: 50%; + align-items: center; + justify-content: center; + cursor: pointer; + color: var(--dark_600_color); + &:hover { + color: var(--dark_400_color); + } +} +.controller_type_switcher { + width: 100%; + &.is_selected { + background-color: var(--dark_850_color); + } + &.is_selected .controller_switcher_label { + color: var(--dark_basic_text_color); + } +} +.controller_switcher_label { + padding: 1rem; + font-size: 1.6rem; +} + +.position_rotation_controls_box { + margin-top: 8rem; + position: relative; + aspect-ratio: 1 / 1; + width: 36%; + max-width: 36rem; + transform: translate(-10%); +} + +// .position_controls { +// background-color: gray; +// } + +// .position_wrapper { +// background-color: gray; +// } + +.slider_label { + font-size: 1.6rem; + width: 100%; +} +.x_position_label { + position: absolute; + bottom: -4rem; + right: -30%; + text-align: end; +} +.y_position_label { + position: absolute; + top: -27%; + left: 10%; +} +.z_position_label { + position: absolute; + top: 30%; + left: 80%; +} + +.x_position_slider { + position: absolute; + bottom: 0; + left: 27%; + width: 100%; + height: 0%; +} + +.y_position_slider { + position: absolute; + bottom: 27%; + left: 0; + width: 0%; + height: 100%; +} + +.z_position_slider { + position: absolute; + bottom: 61%; + left: 61%; + transform: translate(50%,50%) rotate(45deg); + width: 0%; + height: 100%; +} + + + + +// .rotation_controls { +// background-color: gray; +// } + +// .rotation_wrapper { +// background-color: gray; +// } + +.x_rotation_label { + position: absolute; + top: -27%; + left: 10%; +} +.y_rotation_label { + position: absolute; + bottom: -4rem; + right: -30%; + text-align: end; +} +.z_rotation_label { + position: absolute; + top: -10%; + right: -110%; +} + +.x_rotation_slider { + position: absolute; + bottom: 27%; + left: 0; + width: 0%; + height: 100%; +} + +.y_rotation_slider { + position: absolute; + bottom: 0; + left: 27%; + width: 100%; + height: 0%; +} + +.z_rotation_slider { + position: absolute; + bottom: 90%; + left: 100%; + transform: translate(50%,50%) rotate(-45deg); + width: 0%; + height: 100%; +} + + + + +.common_controls { + padding-top: 4rem; + padding-bottom: 2rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + width: 100%; + max-width: 56rem; + border-bottom: 0.1rem solid var(--dark_700_color); +} + +.common_controls_wrapper { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + width: 100%; + position: relative; +} +.common_controls_slider { + margin-left: 18rem; +} + +.common_controls_slider_label { + position: absolute; + font-size: 1.6rem; + width: 100%; +} + + + +.other_controls { + margin-top: 6rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + width: 100%; + max-width: 56rem; +} + +.other_controls_wrapper { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + width: 100%; + position: relative; +} +.other_controls_slider { + margin-left: 18rem; +} + +.other_controls_slider_label { + position: absolute; + font-size: 1.6rem; + width: 100%; +} \ No newline at end of file diff --git a/src-ui/logics/configs/index.js b/src-ui/logics/configs/index.js index 9badb2fd..d5cdffab 100644 --- a/src-ui/logics/configs/index.js +++ b/src-ui/logics/configs/index.js @@ -31,6 +31,9 @@ export { useSpeakerRecordTimeout } from "./transcription/useSpeakerRecordTimeout export { useSpeakerPhraseTimeout } from "./transcription/useSpeakerPhraseTimeout"; export { useSpeakerMaxWords } from "./transcription/useSpeakerMaxWords"; +export { useOverlaySettings } from "./vr/useOverlaySettings"; +export { useOverlaySmallLogSettings } from "./vr/useOverlaySmallLogSettings"; + export { useOscIpAddress } from "./advanced_settings/useOscIpAddress"; export { useOscPort } from "./advanced_settings/useOscPort"; diff --git a/src-ui/logics/configs/vr/useOverlaySettings.js b/src-ui/logics/configs/vr/useOverlaySettings.js new file mode 100644 index 00000000..3394c800 --- /dev/null +++ b/src-ui/logics/configs/vr/useOverlaySettings.js @@ -0,0 +1,24 @@ +import { useStore_OverlaySettings } from "@store"; +import { useStdoutToPython } from "@logics/useStdoutToPython"; + +export const useOverlaySettings = () => { + const { asyncStdoutToPython } = useStdoutToPython(); + const { currentOverlaySettings, updateOverlaySettings, pendingOverlaySettings } = useStore_OverlaySettings(); + + const getOverlaySettings = () => { + // pendingOverlaySettings(); + asyncStdoutToPython("/get/data/overlay_settings"); + }; + + const setOverlaySettings = (overlay_settings) => { + // pendingOverlaySettings(); + asyncStdoutToPython("/set/data/overlay_settings", overlay_settings); + }; + + return { + currentOverlaySettings, + getOverlaySettings, + updateOverlaySettings, + setOverlaySettings, + }; +}; \ No newline at end of file diff --git a/src-ui/logics/configs/vr/useOverlaySmallLogSettings.js b/src-ui/logics/configs/vr/useOverlaySmallLogSettings.js new file mode 100644 index 00000000..03b61393 --- /dev/null +++ b/src-ui/logics/configs/vr/useOverlaySmallLogSettings.js @@ -0,0 +1,24 @@ +import { useStore_OverlaySmallLogSettings } from "@store"; +import { useStdoutToPython } from "@logics/useStdoutToPython"; + +export const useOverlaySmallLogSettings = () => { + const { asyncStdoutToPython } = useStdoutToPython(); + const { currentOverlaySmallLogSettings, updateOverlaySmallLogSettings, pendingOverlaySmallLogSettings } = useStore_OverlaySmallLogSettings(); + + const getOverlaySmallLogSettings = () => { + // pendingOverlaySmallLogSettings(); + asyncStdoutToPython("/get/data/overlay_small_log_settings"); + }; + + const setOverlaySmallLogSettings = (overlay_small_log_settings) => { + // pendingOverlaySmallLogSettings(); + asyncStdoutToPython("/set/data/overlay_small_log_settings", overlay_small_log_settings); + }; + + return { + currentOverlaySmallLogSettings, + getOverlaySmallLogSettings, + updateOverlaySmallLogSettings, + setOverlaySmallLogSettings, + }; +}; \ No newline at end of file diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index fe75557d..639edfd1 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -45,6 +45,8 @@ import { useSpeakerRecordTimeout, useSpeakerPhraseTimeout, useSpeakerMaxWords, + useOverlaySettings, + useOverlaySmallLogSettings, useOscIpAddress, useOscPort, } from "@logics_configs"; @@ -113,6 +115,9 @@ export const useReceiveRoutes = () => { const { updateSpeakerPhraseTimeout } = useSpeakerPhraseTimeout(); const { updateSpeakerMaxWords } = useSpeakerMaxWords(); + const { updateOverlaySettings } = useOverlaySettings(); + const { updateOverlaySmallLogSettings } = useOverlaySmallLogSettings(); + const { updateOscIpAddress } = useOscIpAddress(); const { updateOscPort } = useOscPort(); @@ -311,6 +316,13 @@ export const useReceiveRoutes = () => { "/get/data/speaker_max_phrases": updateSpeakerMaxWords, "/set/data/speaker_max_phrases": updateSpeakerMaxWords, + // VR + "/get/data/overlay_settings": updateOverlaySettings, + "/set/data/overlay_settings": updateOverlaySettings, + + "/get/data/overlay_small_log_settings": updateOverlaySmallLogSettings, + "/set/data/overlay_small_log_settings": updateOverlaySmallLogSettings, + // Others Tab "/get/data/auto_clear_message_box": updateEnableAutoClearMessageInputBox, "/set/enable/auto_clear_message_box": updateEnableAutoClearMessageInputBox, diff --git a/src-ui/store.js b/src-ui/store.js index 2bb95a9d..856a24cb 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -189,6 +189,22 @@ export const { atomInstance: Atom_SpeakerRecordTimeout, useHook: useStore_Speake export const { atomInstance: Atom_SpeakerPhraseTimeout, useHook: useStore_SpeakerPhraseTimeout } = createAtomWithHook(0, "SpeakerPhraseTimeout"); export const { atomInstance: Atom_SpeakerMaxWords, useHook: useStore_SpeakerMaxWords } = createAtomWithHook(0, "SpeakerMaxWords"); +// VR +export const { atomInstance: Atom_OverlaySettings, useHook: useStore_OverlaySettings } = createAtomWithHook({ + opacity: 1.0, + ui_scaling: 1.0, +}, "OverlaySettings"); +export const { atomInstance: Atom_OverlaySmallLogSettings, useHook: useStore_OverlaySmallLogSettings } = createAtomWithHook({ + x_pos: 0.0, + y_pos: 0.0, + z_pos: 0.0, + x_rotation: 0.0, + y_rotation: 0.0, + z_rotation: 0.0, + display_duration: 5, + fadeout_duration: 2, +}, "OverlaySmallLogSettings"); + // Others export const { atomInstance: Atom_EnableAutoClearMessageInputBox, useHook: useStore_EnableAutoClearMessageInputBox } = createAtomWithHook(true, "EnableAutoClearMessageInputBox"); export const { atomInstance: Atom_EnableSendOnlyTranslatedMessages, useHook: useStore_EnableSendOnlyTranslatedMessages } = createAtomWithHook(false, "EnableSendOnlyTranslatedMessages");