Merge pull request #558 from wayvr-org/feat/welcome2

Welcome screen continued
This commit is contained in:
oo8dev 2026-07-02 21:29:28 +02:00 committed by GitHub
commit ad537c9d92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 628 additions and 71 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Iconoir by Luca Burgio - https://github.com/iconoir-icons/iconoir/blob/main/LICENSE --><g fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="1.5"><path d="M16 6.28a2.28 2.28 0 0 1-.662 1.606c-.976.984-1.923 2.01-2.936 2.958a.597.597 0 0 1-.822-.017l-2.918-2.94a2.28 2.28 0 0 1 0-3.214a2.277 2.277 0 0 1 3.232 0L12 4.78l.106-.107A2.276 2.276 0 0 1 16 6.28Z"/><path stroke-linecap="round" d="m18 20l3.824-3.824a.6.6 0 0 0 .176-.424V10.5A1.5 1.5 0 0 0 20.5 9v0a1.5 1.5 0 0 0-1.5 1.5V15"/><path stroke-linecap="round" d="m18 16l.858-.858a.48.48 0 0 0 .142-.343v0a.49.49 0 0 0-.268-.433l-.443-.221a2 2 0 0 0-2.308.374l-.895.895a2 2 0 0 0-.586 1.414V20M6 20l-3.824-3.824A.6.6 0 0 1 2 15.752V10.5A1.5 1.5 0 0 1 3.5 9v0A1.5 1.5 0 0 1 5 10.5V15"/><path stroke-linecap="round" d="m6 16l-.858-.858A.5.5 0 0 1 5 14.799v0c0-.183.104-.35.268-.433l.443-.221a2 2 0 0 1 2.308.374l.895.895a2 2 0 0 1 .586 1.414V20"/></g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 256"><!-- Icon from SVG Logos by Gil Barbara - https://raw.githubusercontent.com/gilbarbara/logos/master/LICENSE.txt --><path fill="#b8d3f4" d="M209.765 128.15c0 16.013-4.9 31.34-13.266 44.575l33.447 33.447a128.9 128.9 0 0 0 25.783-77.35c0-29.265-9.754-55.751-26.47-77.336l-32.776 32.729a79.44 79.44 0 0 1 13.267 43.888z"/><path fill="#7fadf2" d="M127.513 210.355c-45.367-.086-82.125-36.838-82.22-82.205c.069-45.386 36.835-82.166 82.22-82.252c16.732 0 32.058 4.901 44.607 13.267l32.744-32.776A128.97 128.97 0 0 0 127.514.636C57.123.636 0 57.76 0 128.15c0 70.359 57.124 127.513 127.513 127.513a125.13 125.13 0 0 0 77.351-26.501l-33.447-33.431c-11.861 9.754-27.157 14.624-43.904 14.624"/></svg>

After

Width:  |  Height:  |  Size: 772 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -0,0 +1,47 @@
<layout>
<include src="../t_group_box.xml" />
<!-- icon, nickname -->
<template name="Avatar">
<div flex_direction="column" gap="12" align_items="center">
<image src_builtin="${icon}" width="72" height="72" round="100%" position="relative">
<rectangle position="absolute" margin_left="-2" margin_top="-2" width="76" height="76" color="#FFFFFF" round="100%"/>
</image>
<label size="14" text="${nickname}"/>
</div>
</template>
<elements>
<div position="absolute" width="100%" height="100%" padding="4">
<!-- <rectangle width="100%" height="100%" color="#000000" round="6"/> -->
</div>
<div margin="32" flex_direction="column" align_items="center" justify_content="center" gap="16" height="100%">
<div gap="16" align_items="center" width="100%">
<sprite color="#FFAAAA" src_builtin="dashboard/donate.svg" min_width="64" min_height="64"/>
<label wrap="1" size="20" translation="DONATE.DESCRIPTION"/>
</div>
<div flex_direction="column" gap="8" align_items="center" margin_top="16">
<Button id="btn_donate" tooltip="DONATE.BUTTON_TOOLTIP" align_self="baseline">
<image src_builtin="dashboard/opencollective_logo.svg" width="252" height="42" margin="8" margin_left="16" margin_right="16"/>
</Button>
<label text="opencollective.com/wayvr-org" color="~color-accent"/>
</div>
<label weight="bold" size="24" translation="DONATE.THANK_YOU_WAYVR"/>
<div gap="32" margin_top="16">
<Avatar icon="dashboard/avatars/galister.png" nickname="galister"/>
<Avatar icon="dashboard/avatars/oo8dev.png" nickname="oo8dev"/>
</div>
<!--
<rectangle color="#FFFFFF33" width="100%" height="2"/>
<rectangle macro="group_box" align_self="start" width="100%">
<label weight="bold" size="18" translation="DONATE.CURRENT_SUPPORTERS"/>
</rectangle>
-->
</div>
</elements>
</layout>

View File

@ -8,7 +8,7 @@
align_items="center" align_items="center"
flex_grow="1" flex_grow="1"
gap="24"> gap="24">
<sprite src_builtin="dashboard/wayvr_dashboard.png" width="96" height="96" /> <image src_builtin="dashboard/splash.png" width="300" height="89" />
<label id="label_hello" size="32" weight="bold" /> <label id="label_hello" size="32" weight="bold" />
<!-- main button list --> <!-- main button list -->
@ -19,9 +19,19 @@
<MenuButton id="btn_settings" icon="dashboard/settings.svg" translation="SETTINGS" /> <MenuButton id="btn_settings" icon="dashboard/settings.svg" translation="SETTINGS" />
</div> </div>
<div gap="8">
<Button id="btn_welcome_screen" macro="home_button" tooltip="APP_SETTINGS.SHOW_WELCOME_SCREEN"> <Button id="btn_welcome_screen" macro="home_button" tooltip="APP_SETTINGS.SHOW_WELCOME_SCREEN">
<sprite src_builtin="dashboard/welcome.svg" width="32" height="32" margin="16"/> <sprite src_builtin="dashboard/welcome.svg" width="32" height="32" margin="16"/>
</Button> </Button>
<Button id="btn_website" macro="home_button" tooltip="wayvr.org">
<sprite src_builtin="dashboard/globe.svg" width="32" height="32" margin="16"/>
</Button>
<Button id="btn_donate" macro="home_button" tooltip="DONATE.SUPPORT_US">
<sprite src_builtin="dashboard/opencollective.svg" width="32" height="32" margin="16"/>
</Button>
</div>
</div> </div>
</elements> </elements>
</layout> </layout>

View File

@ -3,17 +3,17 @@
<template name="PageTitle"> <template name="PageTitle">
<div position="absolute" align_self="start" justify_self="center" width="100%" margin_top="16" gap="8" align_items="center" justify_content="center"> <div position="absolute" align_self="start" justify_self="center" width="100%" margin_top="16" gap="8" align_items="center" justify_content="center">
<sprite width="48" height="48" src_builtin="${icon}" color="#FFFFFF"/> <sprite width="48" height="48" src_builtin="${icon}" color="#FFFFFF"/>
<label size="28" weight="bold" text="${title}" shadow="#000000" shadow_x="3" shadow_y="3" /> <label size="28" weight="bold" translation="${translation}" shadow="#000000" shadow_x="3" shadow_y="3" />
</div> </div>
</template> </template>
<!-- text --> <!-- text -->
<template name="TextDesc"> <template name="TextDesc">
<label wrap="1" text="${text}" size="20" width="500" shadow="#000000" weight="bold"/> <label wrap="1" translation="${translation}" size="20" width="500" shadow="#000000" weight="bold"/>
</template> </template>
<template name="TextDescBig"> <template name="TextDescBig">
<label text="${text}" size="24" shadow="#000000" weight="bold"/> <label translation="${translation}" size="24" shadow="#000000" weight="bold" color="${color}"/>
</template> </template>
<macro name="video_overlay_component" looping="1" width="100%" height="auto" max_height="100%" aspect_ratio="1.777777" position="relative"/> <macro name="video_overlay_component" looping="1" width="100%" height="auto" max_height="100%" aspect_ratio="1.777777" position="relative"/>

View File

@ -1,15 +1,26 @@
<layout> <layout>
<elements> <elements>
<image src_builtin="dashboard/splash.png" min_width="431" min_height="128" /> <image src_builtin="dashboard/splash.png" min_width="336" min_height="100" />
<rectangle color="#FFFFFF11" width="100%" height="2"/> <rectangle color="#FFFFFF11" width="100%" height="2"/>
<label weight="bold" size="32" text="Hi there!"/> <label weight="bold" size="28" translation="WELCOME.GREETING"/>
<label weight="bold" size="20" text="Thank you for installing WayVR ❤️"/> <label weight="bold" size="16" translation="WELCOME.THANKS_FOR_INSTALLING"/>
<label size="20" margin_top="16" text="Let us guide you through your first steps."/> <label size="20" translation="WELCOME.GUIDE_INTRO"/>
<label size="20" text="Press &quot;Next&quot; button below to get started."/>
<div height="42"/> <!-- empty space --> <div gap="4" align_items="center" margin_top="8">
<sprite min_width="16" min_height="16" src_builtin="dashboard/info.svg"/>
<label wrap="1" size="14" translation="WELCOME.REPOSITION_THIS_WINDOW_BY_GRABBING"/>
</div>
<div gap="4" align_items="center">
<sprite min_width="16" min_height="16" src_builtin="dashboard/info.svg"/>
<label wrap="1" size="14" translation="WELCOME.LAST_PAGE_INFO"/>
</div>
<label size="20" weight="bold" translation="WELCOME.NEXT_BUTTON"/>
<div height="38"/> <!-- empty space -->
</elements> </elements>
</layout> </layout>

View File

@ -5,9 +5,9 @@
<rectangle color="#103142" width="100%" height="100%" position="absolute"/> <rectangle color="#103142" width="100%" height="100%" position="absolute"/>
<Video macro="video_overlay_component" src_builtin="video/onboarding_watch.ivf" aspect_ratio="1.0"> <Video macro="video_overlay_component" src_builtin="video/onboarding_watch.ivf" aspect_ratio="1.0">
<div macro="video_overlay_content"> <div macro="video_overlay_content">
<PageTitle icon="dashboard/watch.svg" title="Watch"/> <PageTitle icon="dashboard/watch.svg" translation="WELCOME.WATCH"/>
<div position="relative" align_self="end" padding="16"> <div position="relative" align_self="end" padding="16">
<TextDesc text="Look at your left wrist. That's your watch."/> <TextDesc translation="WELCOME.LOOK_AT_LEFT_WRIST"/>
</div> </div>
</div> </div>
</Video> </Video>

View File

@ -4,9 +4,9 @@
<elements> <elements>
<Video macro="video_overlay_component" src_builtin="video/onboarding_double_press.ivf"> <Video macro="video_overlay_component" src_builtin="video/onboarding_double_press.ivf">
<div macro="video_overlay_content"> <div macro="video_overlay_content">
<PageTitle icon="dashboard/panorama.svg" title="Working Set"/> <PageTitle icon="dashboard/panorama.svg" translation="WELCOME.WORKING_SET"/>
<div position="relative" align_self="end" padding="16"> <div position="relative" align_self="end" padding="16">
<TextDesc text="Double-press B (on Index) or Y (on Meta) to toggle the visibility of your workspace. Try it now."/> <TextDesc translation="WELCOME.DOUBLE_PRESS_TO_TOGGLE"/>
</div> </div>
</div> </div>
</Video> </Video>

View File

@ -4,14 +4,14 @@
<elements> <elements>
<Video macro="video_overlay_component" src_builtin="video/onboarding_laser_colors.ivf"> <Video macro="video_overlay_component" src_builtin="video/onboarding_laser_colors.ivf">
<div macro="video_overlay_content"> <div macro="video_overlay_content">
<PageTitle icon="dashboard/click.svg" title="Laser Colors"/> <PageTitle icon="dashboard/click.svg" translation="WELCOME.LASER_COLORS"/>
<div position="relative" flex_direction="row" gap="16" margin="16" align_self="end" justify_self="end" align_items="center"> <div position="relative" flex_direction="row" gap="16" margin="16" align_self="end" justify_self="end" align_items="center">
<rectangle flex_direction="column" gap="8" padding="8" round="4" border_color="~color_accent" border="2" > <rectangle flex_direction="column" gap="8" padding="8" round="4" border_color="~color_accent" border="2" >
<label size="16" weight="bold" color="#00FFFF" text="— Regular Mode: Blue laser"/> <label size="12" weight="bold" color="#00FFFF" translation="WELCOME.REGULAR_MODE_BLUE_LASER"/>
<label size="16" weight="bold" color="#FFAA00" text="— Right-click Mode: Orange laser" /> <label size="12" weight="bold" color="#FFAA00" translation="WELCOME.RIGHT_CLICK_MODE_ORANGE_LASER"/>
<label size="16" weight="bold" color="#FF88FF" text="— Middle-click Mode: Purple laser" /> <label size="12" weight="bold" color="#FF88FF" translation="WELCOME.MIDDLE_CLICK_MODE_PURPLE_LASER"/>
</rectangle> </rectangle>
<label wrap="1" size="18" width="450" weight="bold" shadow="#000000" text="Much of the functionality in WayVR depends on what color of laser is used to interact with a UI element. You don't need to press these buttons, just touch them!"/> <label wrap="1" size="18" width="450" weight="bold" shadow="#000000" translation="WELCOME.LASER_FUNCTIONALITY"/>
</div> </div>
</div> </div>
</Video> </Video>

View File

@ -5,13 +5,13 @@
<rectangle color="#0A2939" width="100%" height="100%" position="absolute"/> <rectangle color="#0A2939" width="100%" height="100%" position="absolute"/>
<Video macro="video_overlay_component" src_builtin="video/onboarding_center_marker.ivf" aspect_ratio="1.33333"> <Video macro="video_overlay_component" src_builtin="video/onboarding_center_marker.ivf" aspect_ratio="1.33333">
<div macro="video_overlay_content"> <div macro="video_overlay_content">
<PageTitle icon="dashboard/panorama.svg" title="Moving your set"/> <PageTitle icon="dashboard/panorama.svg" translation="WELCOME.MOVING_YOUR_SET"/>
<div position="relative" align_self="end" padding="16" flex_direction="column" gap="4"> <div position="relative" align_self="end" padding="16" flex_direction="column" gap="4">
<label shadow="#000000" size="14" weight="bold" wrap="1" color="#aaffcc" text="— Grab it with one hand to move your set."/> <label shadow="#000000" size="14" weight="bold" wrap="1" color="#aaffcc" translation="WELCOME.GRAB_ONE_HAND"/>
<label shadow="#000000" size="14" weight="bold" wrap="1" color="#ccaaff" text="— Grab it with both hands if you want to move a specific overlay in your set."/> <label shadow="#000000" size="14" weight="bold" wrap="1" color="#ccaaff" translation="WELCOME.GRAB_BOTH_HANDS"/>
<div align_items="center" gap="8"> <div align_items="center" gap="8">
<sprite src_builtin="dashboard/info.svg" width="24" height="24" color="#ffff00"/> <sprite src_builtin="dashboard/info.svg" width="24" height="24" color="#ffff00"/>
<label shadow="#000000" size="16" weight="bold" wrap="1" color="#ffff00" text="Center marker determines the center or your set."/> <label shadow="#000000" size="16" weight="bold" wrap="1" color="#ffff00" translation="WELCOME.CENTER_MARKER"/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,15 +4,24 @@
<elements> <elements>
<div flex_direction="row" align_items="center"> <div flex_direction="row" align_items="center">
<image src_builtin="dashboard/check_3d.png" width="256" height="256"/> <image src_builtin="dashboard/check_3d.png" width="256" height="256"/>
<div flex_direction="column" gap="8" max_width="480"> <div flex_direction="column" gap="16" max_width="480">
<TextDescBig text="You're all set!"/> <TextDescBig translation="WELCOME.ALL_SET" color="#AAFFAA"/>
<label size="16" wrap="1" text="These basics will allow you to start using WayVR with ease."/>
<label size="16" wrap="1" text="There are many other features worth customizing. For more information, check the documentation at"/> <rectangle color="#AAFFAA33" width="100%" min_height="2"/>
<div flex_direction="row" align_items="center" gap="8">
<sprite src_builtin="dashboard/globe.svg" width="24" height="24" color="~color_accent"/> <label size="16" wrap="1" translation="WELCOME.BASICS"/>
<label size="20" weight="bold" text="wayvr.org" color="~color_accent"/> <label size="16" wrap="1" translation="WELCOME.FEATURES_DOCUMENTATION"/>
</div> <Button id="btn_wayvr_org" align_self="baseline" min_height="32">
<Button margin_top="16" sprite_src_builtin="dashboard/home.svg" id="btn_home_screen" text="Go back to Home screen" min_height="32" align_self="baseline"/> <sprite src_builtin="dashboard/globe.svg" width="24" height="24" color="~color_accent" margin="8"/>
<label size="18" weight="bold" text="wayvr.org" margin_right="8" />
</Button>
<rectangle color="#FFFFFF33" width="100%" min_height="2"/>
<Button id="btn_home_screen" min_height="48">
<sprite src_builtin="dashboard/check.svg" width="32" height="32" color="~color_accent" margin="8"/>
<label size="20" weight="bold" translation="WELCOME.FINISH_TUTORIAL" margin_right="8" />
</Button>
</div> </div>
</div> </div>
</elements> </elements>

View File

@ -257,5 +257,37 @@
"APPLY": "Anwenden", "APPLY": "Anwenden",
"RELOAD_FROM_DISK": "Von Festplatte neu laden", "RELOAD_FROM_DISK": "Von Festplatte neu laden",
"TARGET_PATH": "Zielpfad", "TARGET_PATH": "Zielpfad",
"VERSION": "Version" "VERSION": "Version",
"DONATE": {
"SUPPORT_US": "Unterstütze uns",
"DESCRIPTION": "Wir sind ein kleines Team aus zwei Entwicklern, die WayVR in ihrer Freizeit bauen. Wenn Ihnen die Nutzung dieser Software Spaß macht, unterstützen Sie uns bitte jede Spende hilft uns, weiterzumachen.",
"BUTTON_TOOLTIP": "Spenden über Open Collective",
"CURRENT_SUPPORTERS": "Aktuelle Unterstützer",
"THANK_YOU_WAYVR": "Vielen Dank vom WayVR-Team!"
},
"WELCOME": {
"ALL_SET": "Du bist startklar!",
"BASICS": "Diese Grundlagen ermöglichen Ihnen einen einfachen Einstieg in die Nutzung von WayVR.",
"CENTER_MARKER": "Der Mittelpunkt-Marker bestimmt das Zentrum Ihres Sets.",
"DOUBLE_PRESS_TO_TOGGLE": "Drücken Sie zweimal B (auf Index) oder Y (auf Meta), um die Sichtbarkeit Ihres Arbeitsbereichs umzuschalten. Probieren Sie es jetzt aus.",
"FEATURES_DOCUMENTATION": "Es gibt viele weitere Funktionen, die es wert sind, angepasst zu werden. Weitere Informationen finden Sie in der Dokumentation unter",
"FINISH_TUTORIAL": "Tutorial beenden",
"GRAB_BOTH_HANDS": "Greifen Sie es mit beiden Händen, wenn Sie ein bestimmtes Overlay in Ihrem Set bewegen möchten.",
"GRAB_ONE_HAND": "Greife es mit einer Hand, um dein Set zu bewegen.",
"GREETING": "Hallo!",
"GUIDE_INTRO": "Lassen Sie sich durch Ihre ersten Schritte führen.",
"LASER_COLORS": "Laserfarben",
"LASER_FUNCTIONALITY": "Ein Großteil der Funktionalität in WayVR hängt davon ab, welche Laserfarbe verwendet wird, um mit einem UI-Element zu interagieren. Sie müssen diese Schaltflächen nicht drücken, berühren Sie sie einfach!",
"LAST_PAGE_INFO": "Dieses Tutorial wird nicht mehr angezeigt, sobald du die letzte Seite erreicht hast.",
"LOOK_AT_LEFT_WRIST": "Schau auf dein linkes Handgelenk. Das ist deine Watch.",
"MIDDLE_CLICK_MODE_PURPLE_LASER": "— Mittlere Maustaste-Modus: Lila Laser",
"MOVING_YOUR_SET": "Ihr Set wird verschoben",
"NEXT_BUTTON": "Drücke die Schaltfläche „Weiter“ unten, um zu beginnen.",
"REGULAR_MODE_BLUE_LASER": "— Regulärer Modus: Blauer Laser",
"REPOSITION_THIS_WINDOW_BY_GRABBING": "Positionieren Sie dieses Fenster neu, indem Sie die Greifaktion Ihres Controllers verwenden.",
"RIGHT_CLICK_MODE_ORANGE_LASER": "— Rechtsklick-Modus: Oranger Laser",
"THANKS_FOR_INSTALLING": "Vielen Dank für die Installation von WayVR!",
"WATCH": "Watch",
"WORKING_SET": "Arbeits-Set"
}
} }

View File

@ -167,7 +167,6 @@
"ENABLE_WATCH": "Enable watch", "ENABLE_WATCH": "Enable watch",
"SETS_ON_WATCH": "Sets on watch", "SETS_ON_WATCH": "Sets on watch",
"SHOW_WELCOME_SCREEN": "Show welcome screen", "SHOW_WELCOME_SCREEN": "Show welcome screen",
"SHOW_WELCOME_SCREEN_HELP": "Show welcome screen on next start",
"SKYBOX": "Skybox", "SKYBOX": "Skybox",
"SKYMAP_ALREADY_DOWNLOADED": "This skymap is already downloaded. Select desired action.", "SKYMAP_ALREADY_DOWNLOADED": "This skymap is already downloaded. Select desired action.",
"SPACE_DRAG": "Space drag", "SPACE_DRAG": "Space drag",
@ -221,7 +220,39 @@
"CREATION_DATE": "Creation date", "CREATION_DATE": "Creation date",
"DEBUG_INFO": "Debug info", "DEBUG_INFO": "Debug info",
"DISPLAY_BRIGHTNESS": "Display brightness", "DISPLAY_BRIGHTNESS": "Display brightness",
"DONATE": {
"SUPPORT_US": "Support us",
"DESCRIPTION": "We're a small team of 2 developers building WayVR in our free time. If you enjoy using this software, please consider supporting us - every donation helps us keep going.",
"THANK_YOU_WAYVR": "Thank you from the WayVR Team!",
"BUTTON_TOOLTIP": "Donate via Open Collective",
"CURRENT_SUPPORTERS": "Current Supporters"
},
"DOWNLOADER": "Downloader", "DOWNLOADER": "Downloader",
"WELCOME": {
"ALL_SET": "You're all set!",
"BASICS": "These basics will allow you to start using WayVR with ease.",
"CENTER_MARKER": "Center marker determines the center or your set.",
"DOUBLE_PRESS_TO_TOGGLE": "Double-press B (on Index) or Y (on Meta) to toggle the visibility of your workspace. Try it now.",
"FEATURES_DOCUMENTATION": "There are many other features worth customizing. For more information, check the documentation at",
"FINISH_TUTORIAL": "Finish tutorial",
"GRAB_BOTH_HANDS": "Grab it with both hands if you want to move a specific overlay in your set.",
"GRAB_ONE_HAND": "Grab it with one hand to move your set.",
"GREETING": "Hi there!",
"GUIDE_INTRO": "Let us guide you through your first steps.",
"LASER_COLORS": "Laser Colors",
"LASER_FUNCTIONALITY": "Much of the functionality in WayVR depends on what color of laser is used to interact with a UI element. You don't need to press these buttons, just touch them!",
"LAST_PAGE_INFO": "This tutorial will stop showing up once you get to the last page.",
"LOOK_AT_LEFT_WRIST": "Look at your left wrist. That's your watch.",
"MIDDLE_CLICK_MODE_PURPLE_LASER": "— Middle-click Mode: Purple laser",
"MOVING_YOUR_SET": "Moving your set",
"NEXT_BUTTON": "Press \"Next\" button below to get started.",
"REGULAR_MODE_BLUE_LASER": "— Regular Mode: Blue laser",
"REPOSITION_THIS_WINDOW_BY_GRABBING": "Reposition this window using your controller's grip action.",
"RIGHT_CLICK_MODE_ORANGE_LASER": "— Right-click Mode: Orange laser",
"THANKS_FOR_INSTALLING": "Thank you for installing WayVR!",
"WATCH": "Watch",
"WORKING_SET": "Working Set"
},
"DOWNLOAD_AGAIN": "Download again", "DOWNLOAD_AGAIN": "Download again",
"DOWNLOADING_FILE": "Downloading file...", "DOWNLOADING_FILE": "Downloading file...",
"FAILED_TO_LAUNCH_APPLICATION": "Failed to launch a application:", "FAILED_TO_LAUNCH_APPLICATION": "Failed to launch a application:",

View File

@ -257,5 +257,37 @@
"APPLY": "Aplicar", "APPLY": "Aplicar",
"RELOAD_FROM_DISK": "Recargar desde el disco", "RELOAD_FROM_DISK": "Recargar desde el disco",
"TARGET_PATH": "Ruta de destino", "TARGET_PATH": "Ruta de destino",
"VERSION": "Versión" "VERSION": "Versión",
"DONATE": {
"SUPPORT_US": "Apóyanos",
"DESCRIPTION": "Somos un pequeño equipo de 2 desarrolladores que creamos WayVR en nuestro tiempo libre. Si disfrutas usando este software, por favor considera apoyarnos; cada donación nos ayuda a seguir adelante.",
"BUTTON_TOOLTIP": "Donar vía Open Collective",
"CURRENT_SUPPORTERS": "Colaboradores actuales",
"THANK_YOU_WAYVR": "¡Gracias de parte del equipo de WayVR!"
},
"WELCOME": {
"ALL_SET": "¡Todo listo!",
"BASICS": "Estos conceptos básicos te permitirán empezar a usar WayVR con facilidad.",
"CENTER_MARKER": "El marcador central determina el centro de tu set.",
"DOUBLE_PRESS_TO_TOGGLE": "Pulsa dos veces B (en Index) o Y (en Meta) para alternar la visibilidad de tu espacio de trabajo. Pruébalo ahora.",
"FEATURES_DOCUMENTATION": "Hay muchas otras funciones que merece la pena personalizar. Para más información, consulta la documentación en",
"FINISH_TUTORIAL": "Finalizar tutorial",
"GRAB_BOTH_HANDS": "Agarra el elemento con ambas manos si quieres mover un overlay específico en tu set.",
"GRAB_ONE_HAND": "Agárralo con una mano para mover tu set.",
"GREETING": "¡Hola!",
"GUIDE_INTRO": "Permítenos guiarte en tus primeros pasos.",
"LASER_COLORS": "Colores del láser",
"LASER_FUNCTIONALITY": "Gran parte de la funcionalidad de WayVR depende del color de láser que se utilice para interactuar con un elemento de la interfaz. ¡No hace falta pulsar estos botones, solo tocarlos!",
"LAST_PAGE_INFO": "Este tutorial dejará de aparecer una vez que llegues a la última página.",
"LOOK_AT_LEFT_WRIST": "Mira tu muñeca izquierda. Ese es tu watch.",
"MIDDLE_CLICK_MODE_PURPLE_LASER": "— Modo de clic central: Láser morado",
"MOVING_YOUR_SET": "Moviendo tu set",
"NEXT_BUTTON": "Pulsa el botón \"Siguiente\" de abajo para comenzar.",
"REGULAR_MODE_BLUE_LASER": "— Modo Regular: Láser azul",
"REPOSITION_THIS_WINDOW_BY_GRABBING": "Reposiciona esta ventana usando la acción de agarre de tu controlador.",
"RIGHT_CLICK_MODE_ORANGE_LASER": "— Modo de clic derecho: Láser naranja",
"THANKS_FOR_INSTALLING": "¡Gracias por instalar WayVR!",
"WATCH": "Watch",
"WORKING_SET": "Set de trabajo"
}
} }

View File

@ -257,5 +257,37 @@
"APPLY": "Applica", "APPLY": "Applica",
"RELOAD_FROM_DISK": "Ricarica da disco", "RELOAD_FROM_DISK": "Ricarica da disco",
"TARGET_PATH": "Percorso di destinazione", "TARGET_PATH": "Percorso di destinazione",
"VERSION": "Versione" "VERSION": "Versione",
"DONATE": {
"SUPPORT_US": "Sostienici",
"DESCRIPTION": "Siamo un piccolo team di 2 sviluppatori che crea WayVR nel tempo libero. Se ti piace usare questo software, considera di supportarci: ogni donazione ci aiuta a continuare.",
"BUTTON_TOOLTIP": "Dona tramite Open Collective",
"CURRENT_SUPPORTERS": "Sostenitori attuali",
"THANK_YOU_WAYVR": "Grazie dal team WayVR!"
},
"WELCOME": {
"ALL_SET": "Sei pronto!",
"BASICS": "Queste nozioni di base ti permetteranno di iniziare a usare WayVR con facilità.",
"CENTER_MARKER": "Il marker centrale determina il centro del tuo set.",
"DOUBLE_PRESS_TO_TOGGLE": "Premi due volte B (su Index) o Y (su Meta) per attivare/disattivare la visibilità del tuo workspace. Provalo subito.",
"FEATURES_DOCUMENTATION": "Ci sono molte altre funzioni che vale la pena personalizzare. Per ulteriori informazioni, consulta la documentazione all'indirizzo",
"FINISH_TUTORIAL": "Termina tutorial",
"GRAB_BOTH_HANDS": "Afferralo con entrambe le mani se vuoi spostare un overlay specifico nel tuo set.",
"GRAB_ONE_HAND": "Afferralo con una mano per muovere il tuo set.",
"GREETING": "Ciao!",
"GUIDE_INTRO": "Lascia che ti guidiamo nei tuoi primi passi.",
"LASER_COLORS": "Colori Laser",
"LASER_FUNCTIONALITY": "Gran parte delle funzionalità in WayVR dipende dal colore del laser utilizzato per interagire con un elemento dell'interfaccia utente. Non è necessario premere questi pulsanti, basta toccarli!",
"LAST_PAGE_INFO": "Questo tutorial smetterà di apparire una volta raggiunta l'ultima pagina.",
"LOOK_AT_LEFT_WRIST": "Guarda il tuo polso sinistro. È il tuo watch.",
"MIDDLE_CLICK_MODE_PURPLE_LASER": "— Modalità click centrale: Laser viola",
"MOVING_YOUR_SET": "Spostamento del tuo set",
"NEXT_BUTTON": "Premi il pulsante \"Avanti\" qui sotto per iniziare.",
"REGULAR_MODE_BLUE_LASER": "— Modalità Standard: Laser blu",
"REPOSITION_THIS_WINDOW_BY_GRABBING": "Riposiziona questa finestra usando l'azione grip del tuo controller.",
"RIGHT_CLICK_MODE_ORANGE_LASER": "— Modalità tasto destro: Laser arancione",
"THANKS_FOR_INSTALLING": "Grazie per aver installato WayVR!",
"WATCH": "Watch",
"WORKING_SET": "Set di lavoro"
}
} }

View File

@ -257,5 +257,37 @@
"APPLY": "適用", "APPLY": "適用",
"RELOAD_FROM_DISK": "ディスクからリロード", "RELOAD_FROM_DISK": "ディスクからリロード",
"TARGET_PATH": "ターゲットパス", "TARGET_PATH": "ターゲットパス",
"VERSION": "バージョン" "VERSION": "バージョン",
"DONATE": {
"SUPPORT_US": "私たちを支援する",
"DESCRIPTION": "私たちは、空き時間にWayVRを開発している2人の開発者からなる小さなチームです。もしこのソフトウェアを気に入っていただけたなら、ぜひ支援をご検討ください。皆様からの寄付は、開発を継続していくための大きな助けとなります。",
"BUTTON_TOOLTIP": "Open Collectiveで寄付する",
"CURRENT_SUPPORTERS": "現在のサポーター",
"THANK_YOU_WAYVR": "WayVRチーム一同より、感謝を込めて"
},
"WELCOME": {
"ALL_SET": "準備完了です!",
"BASICS": "これらの基本操作を学ぶことで、WayVRを簡単に使い始めることができます。",
"CENTER_MARKER": "センターマーカーは、セットの中心を決定します。",
"DOUBLE_PRESS_TO_TOGGLE": "Bボタン (Index) または Yボタン (Meta) をダブルプレスして、ワークスペースの表示/非表示を切り替えます。今すぐ試してみてください。",
"FEATURES_DOCUMENTATION": "他にもカスタマイズする価値のある機能がたくさんあります。詳細については、以下のドキュメントを確認してください。",
"FINISH_TUTORIAL": "チュートリアルを終了する",
"GRAB_BOTH_HANDS": "セット内の特定のオーバーレイを移動させたい場合は、両手で掴んでください。",
"GRAB_ONE_HAND": "片手で掴んでセットを移動させます。",
"GREETING": "こんにちは!",
"GUIDE_INTRO": "最初の手順をご案内します。",
"LASER_COLORS": "レーザーの色",
"LASER_FUNCTIONALITY": "WayVRの機能の多くは、UI要素を操作する際に使用されるレーザーの色に依存しています。これらのボタンは押す必要はなく、ただ触れるだけで操作できます",
"LAST_PAGE_INFO": "最後のページに到達すると、このチュートリアルは表示されなくなります。",
"LOOK_AT_LEFT_WRIST": "左手首を見てください。それがあなたのWatchです。",
"MIDDLE_CLICK_MODE_PURPLE_LASER": "— ミドルクリックモード:紫色のレーザー",
"MOVING_YOUR_SET": "セットを移動中",
"NEXT_BUTTON": "「次へ」ボタンを押して開始してください。",
"REGULAR_MODE_BLUE_LASER": "— 通常モード: ブルーレーザー",
"REPOSITION_THIS_WINDOW_BY_GRABBING": "コントローラーのグリップアクションを使用して、このウィンドウの位置を再設定します。",
"RIGHT_CLICK_MODE_ORANGE_LASER": "— 右クリックモード:オレンジ色のレーザー",
"THANKS_FOR_INSTALLING": "WayVRをインストールしていただきありがとうございます",
"WATCH": "Watch",
"WORKING_SET": "ワーキングセット"
}
} }

View File

@ -257,5 +257,37 @@
"APPLY": "Zastosuj", "APPLY": "Zastosuj",
"RELOAD_FROM_DISK": "Przeładuj z dysku", "RELOAD_FROM_DISK": "Przeładuj z dysku",
"TARGET_PATH": "Ścieżka docelowa", "TARGET_PATH": "Ścieżka docelowa",
"VERSION": "Wersja" "VERSION": "Wersja",
"DONATE": {
"SUPPORT_US": "Wesprzyj nas",
"DESCRIPTION": "Jesteśmy małym, dwuosobowym zespołem programistów, którzy tworzą WayVR w wolnym czasie. Jeśli korzystanie z tego oprogramowania sprawia Ci przyjemność, rozważ wsparcie nas każda darowizna pomaga nam działać dalej.",
"BUTTON_TOOLTIP": "Przekaż darowiznę przez Open Collective",
"CURRENT_SUPPORTERS": "Obecni Wspierający",
"THANK_YOU_WAYVR": "Dziękujemy od zespołu WayVR!"
},
"WELCOME": {
"ALL_SET": "Wszystko gotowe!",
"BASICS": "Te podstawowe informacje pozwolą Ci z łatwością zacząć korzystać z WayVR.",
"CENTER_MARKER": "Znacznik środka określa środek Twojego zestawu.",
"DOUBLE_PRESS_TO_TOGGLE": "Naciśnij dwukrotnie przycisk B (na Index) lub Y (na Meta), aby przełączyć widoczność swojej przestrzeni roboczej. Spróbuj teraz.",
"FEATURES_DOCUMENTATION": "Istnieje wiele innych funkcji, które warto spersonalizować. Aby uzyskać więcej informacji, sprawdź dokumentację pod adresem:",
"FINISH_TUTORIAL": "Zakończ samouczek",
"GRAB_BOTH_HANDS": "Chwyć to obiema rękami, jeśli chcesz przenieść konkretny overlay ze swojego zestawu.",
"GRAB_ONE_HAND": "Chwyć to jedną ręką, aby przemieścić swój zestaw.",
"GREETING": "Cześć!",
"GUIDE_INTRO": "Pozwól nam przeprowadzić Cię przez Twoje pierwsze kroki.",
"LASER_COLORS": "Kolory lasera",
"LASER_FUNCTIONALITY": "Wiele funkcji w WayVR zależy od koloru lasera używanego do interakcji z elementami interfejsu użytkownika. Nie musisz naciskać tych przycisków, wystarczy je dotknąć!",
"LAST_PAGE_INFO": "Ten samouczek przestanie się pojawiać po dotarciu do ostatniej strony.",
"LOOK_AT_LEFT_WRIST": "Spójrz na swój lewy nadgarstek. To jest Twój watch.",
"MIDDLE_CLICK_MODE_PURPLE_LASER": "— Tryb środkowego przycisku: Fioletowy laser",
"MOVING_YOUR_SET": "Przesuwanie Twojego zestawu",
"NEXT_BUTTON": "Naciśnij poniższy przycisk „Dalej”, aby rozpocząć.",
"REGULAR_MODE_BLUE_LASER": "— Tryb regularny: Niebieski laser",
"REPOSITION_THIS_WINDOW_BY_GRABBING": "Zmień położenie tego okna, używając przycisku chwytu (grip) na kontrolerze.",
"RIGHT_CLICK_MODE_ORANGE_LASER": "— Tryb prawego przycisku: Pomarańczowy laser",
"THANKS_FOR_INSTALLING": "Dziękujemy za zainstalowanie WayVR!",
"WATCH": "Watch",
"WORKING_SET": "Aktywny zestaw"
}
} }

View File

@ -257,5 +257,37 @@
"APPLY": "应用", "APPLY": "应用",
"RELOAD_FROM_DISK": "从磁盘重新加载", "RELOAD_FROM_DISK": "从磁盘重新加载",
"TARGET_PATH": "目标路径", "TARGET_PATH": "目标路径",
"VERSION": "版本" "VERSION": "版本",
"DONATE": {
"SUPPORT_US": "支持我们",
"DESCRIPTION": "我们是一个由 2 名开发者组成的精简团队,在业余时间开发 WayVR。如果您喜欢这款软件请考虑支持我们 —— 每一次捐赠都能帮助我们继续开发下去。",
"BUTTON_TOOLTIP": "通过 Open Collective 捐赠",
"CURRENT_SUPPORTERS": "当前支持者",
"THANK_YOU_WAYVR": "WayVR 团队向您表示感谢!"
},
"WELCOME": {
"ALL_SET": "你已准备就绪!",
"BASICS": "这些基础知识将帮助您轻松开始使用 WayVR。",
"CENTER_MARKER": "中心标记确定了您 Set 的中心位置。",
"DOUBLE_PRESS_TO_TOGGLE": "双击 BIndex 控制器)或 YMeta 控制器)来切换工作区显示。立即尝试一下。",
"FEATURES_DOCUMENTATION": "还有许多其他值得自定义的功能。欲了解更多信息,请查看文档:",
"FINISH_TUTORIAL": "完成教程",
"GRAB_BOTH_HANDS": "如果你想移动 Set 中的特定叠加层,请用双手抓取。",
"GRAB_ONE_HAND": "用一只手抓取即可移动你的 Set。",
"GREETING": "你好!",
"GUIDE_INTRO": "让我们引导你完成初步设置。",
"LASER_COLORS": "激光颜色",
"LASER_FUNCTIONALITY": "WayVR 中的许多功能都取决于使用何种颜色的激光来与 UI 元素进行交互。您不需要按下这些按钮,只需触摸它们即可!",
"LAST_PAGE_INFO": "一旦你到达最后一页,此教程将不再显示。",
"LOOK_AT_LEFT_WRIST": "看向你的左手腕。那就是你的手表。",
"MIDDLE_CLICK_MODE_PURPLE_LASER": "— 中键模式:紫色激光",
"MOVING_YOUR_SET": "正在移动您的 Set",
"NEXT_BUTTON": "按下下方的“下一步”按钮以开始。",
"REGULAR_MODE_BLUE_LASER": "— 普通模式:蓝色激光",
"REPOSITION_THIS_WINDOW_BY_GRABBING": "使用控制器的抓取操作重新定位此窗口。",
"RIGHT_CLICK_MODE_ORANGE_LASER": "— 右键模式:橙色激光",
"THANKS_FOR_INSTALLING": "感谢安装 WayVR",
"WATCH": "Watch",
"WORKING_SET": "工作集"
}
} }

View File

@ -21,7 +21,7 @@ use wgui::{
use wlx_common::{ use wlx_common::{
async_executor::AsyncExecutor, async_executor::AsyncExecutor,
audio, audio,
dash_interface::{BoxDashInterface, RecenterMode}, dash_interface::{BoxDashInterface, ConfigChangeKind, RecenterMode},
locale::WayVRLangProvider, locale::WayVRLangProvider,
timestep::{self, Timestep}, timestep::{self, Timestep},
}; };
@ -29,8 +29,8 @@ use wlx_common::{
use crate::{ use crate::{
assets, assets,
tab::{ tab::{
Tab, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, settings::TabSettings, Tab, TabType, apps::TabApps, donate::TabDonate, games::TabGames, home::TabHome, monado::TabMonado,
welcome::TabWelcome, settings::TabSettings, welcome::TabWelcome,
}, },
util::{ util::{
popup_manager::{MountPopupOnceParams, PopupManager, PopupManagerParams}, popup_manager::{MountPopupOnceParams, PopupManager, PopupManagerParams},
@ -91,6 +91,7 @@ pub struct FrontendUpdateResult {
pub struct InitParams<'a, T> { pub struct InitParams<'a, T> {
pub interface: BoxDashInterface<T>, pub interface: BoxDashInterface<T>,
pub lang_provider: &'a WayVRLangProvider, pub lang_provider: &'a WayVRLangProvider,
pub show_welcome: bool,
pub has_monado: bool, pub has_monado: bool,
pub theme: Rc<WguiTheme>, pub theme: Rc<WguiTheme>,
} }
@ -113,7 +114,9 @@ pub enum FrontendTask {
RecenterPlayspace, RecenterPlayspace,
PushToast(Translation), PushToast(Translation),
PlaySound(SoundType), PlaySound(SoundType),
OpenURL(Rc<str>),
HideDashboard, HideDashboard,
MarkTutorialGraduated,
} }
impl<T: 'static> Frontend<T> { impl<T: 'static> Frontend<T> {
@ -156,7 +159,13 @@ impl<T: 'static> Frontend<T> {
let toast_manager = ToastManager::new(); let toast_manager = ToastManager::new();
let tasks = FrontendTasks::new(); let tasks = FrontendTasks::new();
tasks.push(FrontendTask::SetTab(TabType::Home));
let init_tab = if params.show_welcome {
TabType::Welcome
} else {
TabType::Home
};
tasks.push(FrontendTask::SetTab(init_tab));
let id_label_time = state.get_widget_id("label_time")?; let id_label_time = state.get_widget_id("label_time")?;
let id_rect_content = state.get_widget_id("rect_content")?; let id_rect_content = state.get_widget_id("rect_content")?;
@ -360,6 +369,8 @@ impl<T: 'static> Frontend<T> {
FrontendTask::PushToast(content) => self.toast_manager.push(content), FrontendTask::PushToast(content) => self.toast_manager.push(content),
FrontendTask::PlaySound(sound_type) => self.queue_play_sound(sound_type), FrontendTask::PlaySound(sound_type) => self.queue_play_sound(sound_type),
FrontendTask::HideDashboard => self.action_hide_dashboard(params.data), FrontendTask::HideDashboard => self.action_hide_dashboard(params.data),
FrontendTask::OpenURL(url) => self.action_open_url(url),
FrontendTask::MarkTutorialGraduated => self.action_tutorial_graduated(params.data),
}; };
Ok(()) Ok(())
} }
@ -407,6 +418,7 @@ impl<T: 'static> Frontend<T> {
TabType::Games => ("GAMES", "dashboard/games.svg"), TabType::Games => ("GAMES", "dashboard/games.svg"),
TabType::Monado => ("MONADO_RUNTIME", "dashboard/monado.svg"), TabType::Monado => ("MONADO_RUNTIME", "dashboard/monado.svg"),
TabType::Settings => ("SETTINGS", "dashboard/settings.svg"), TabType::Settings => ("SETTINGS", "dashboard/settings.svg"),
TabType::Donate => ("DONATE.SUPPORT_US", "dashboard/opencollective.svg"),
}; };
self.set_tab_title(tab_translation, icon_path)?; self.set_tab_title(tab_translation, icon_path)?;
@ -418,6 +430,7 @@ impl<T: 'static> Frontend<T> {
TabType::Games => Box::new(TabGames::new(self, widget_content.id)?), TabType::Games => Box::new(TabGames::new(self, widget_content.id)?),
TabType::Monado => Box::new(TabMonado::new(self, widget_content.id)?), TabType::Monado => Box::new(TabMonado::new(self, widget_content.id)?),
TabType::Settings => Box::new(TabSettings::new(self, widget_content.id, data)?), TabType::Settings => Box::new(TabSettings::new(self, widget_content.id, data)?),
TabType::Donate => Box::new(TabDonate::new(self, widget_content.id, data)?),
}; };
self.current_tab = Some(tab); self.current_tab = Some(tab);
@ -533,4 +546,20 @@ impl<T: 'static> Frontend<T> {
fn action_hide_dashboard(&mut self, data: &mut T) { fn action_hide_dashboard(&mut self, data: &mut T) {
self.interface.toggle_dashboard(data); self.interface.toggle_dashboard(data);
} }
fn action_tutorial_graduated(&mut self, data: &mut T) {
let config = self.interface.general_config(data);
config.tutorial_graduated = true;
self.interface.config_changed(data, ConfigChangeKind::Other);
}
fn action_open_url(&mut self, url: Rc<str>) {
let _ = std::process::Command::new("xdg-open").arg(url.as_ref()).spawn();
self
.tasks
.push(FrontendTask::PushToast(Translation::from_raw_text_string(format!(
"Opened URL: {}",
url
))));
}
} }

View File

@ -0,0 +1,68 @@
use std::{marker::PhantomData, rc::Rc};
use wgui::{
assets::AssetPath,
components::button::ComponentButton,
globals::WguiGlobals,
layout::WidgetID,
parser::{Fetchable, ParseDocumentParams, ParserState},
task::Tasks,
};
use crate::{
frontend::{Frontend, FrontendTask},
tab::{Tab, TabType},
};
#[derive(Clone)]
#[allow(clippy::enum_variant_names)]
enum Task {}
pub struct TabDonate<T> {
#[allow(dead_code)]
pub state: ParserState,
marker: PhantomData<T>,
#[allow(dead_code)]
tasks: Tasks<Task>,
}
impl<T> Tab<T> for TabDonate<T> {
fn get_type(&self) -> TabType {
TabType::Donate
}
fn update(&mut self, _frontend: &mut Frontend<T>, _time_ms: u32, _user_data: &mut T) -> anyhow::Result<()> {
// for task in self.tasks.drain() {
// match task {}
// }
Ok(())
}
}
fn doc_params(globals: &WguiGlobals) -> ParseDocumentParams<'_> {
ParseDocumentParams {
globals: globals.clone(),
path: AssetPath::BuiltIn("gui/tab/donate.xml"),
extra: Default::default(),
}
}
impl<T> TabDonate<T> {
pub fn new(frontend: &mut Frontend<T>, parent_id: WidgetID, _data: &mut T) -> anyhow::Result<Self> {
let state = wgui::parser::parse_from_assets(&doc_params(&frontend.globals), &mut frontend.layout, parent_id)?;
frontend.tasks.handle_button(
&state.fetch_component_as::<ComponentButton>("btn_donate")?,
FrontendTask::OpenURL(Rc::from("https://opencollective.com/wayvr-org")),
);
let tasks = Tasks::<Task>::new();
Ok(Self {
state,
marker: PhantomData,
tasks,
})
}
}

View File

@ -71,6 +71,8 @@ impl<T> TabHome<T> {
let btn_monado = state.fetch_component_as::<ComponentButton>("btn_monado")?; let btn_monado = state.fetch_component_as::<ComponentButton>("btn_monado")?;
let btn_settings = state.fetch_component_as::<ComponentButton>("btn_settings")?; let btn_settings = state.fetch_component_as::<ComponentButton>("btn_settings")?;
let btn_welcome_screen = state.fetch_component_as::<ComponentButton>("btn_welcome_screen")?; let btn_welcome_screen = state.fetch_component_as::<ComponentButton>("btn_welcome_screen")?;
let btn_donate = state.fetch_component_as::<ComponentButton>("btn_donate")?;
let btn_website = state.fetch_component_as::<ComponentButton>("btn_website")?;
let tasks = &mut frontend.tasks; let tasks = &mut frontend.tasks;
tasks.handle_button(&btn_apps, FrontendTask::SetTab(TabType::Apps)); tasks.handle_button(&btn_apps, FrontendTask::SetTab(TabType::Apps));
@ -78,6 +80,8 @@ impl<T> TabHome<T> {
tasks.handle_button(&btn_monado, FrontendTask::SetTab(TabType::Monado)); tasks.handle_button(&btn_monado, FrontendTask::SetTab(TabType::Monado));
tasks.handle_button(&btn_settings, FrontendTask::SetTab(TabType::Settings)); tasks.handle_button(&btn_settings, FrontendTask::SetTab(TabType::Settings));
tasks.handle_button(&btn_welcome_screen, FrontendTask::SetTab(TabType::Welcome)); tasks.handle_button(&btn_welcome_screen, FrontendTask::SetTab(TabType::Welcome));
tasks.handle_button(&btn_donate, FrontendTask::SetTab(TabType::Donate));
tasks.handle_button(&btn_website, FrontendTask::OpenURL("https://wayvr.org".into()));
Ok(Self { Ok(Self {
state, state,

View File

@ -1,6 +1,7 @@
use crate::frontend::Frontend; use crate::frontend::Frontend;
pub mod apps; pub mod apps;
pub mod donate;
pub mod games; pub mod games;
pub mod home; pub mod home;
pub mod monado; pub mod monado;
@ -15,12 +16,13 @@ pub enum TabType {
Monado, Monado,
Settings, Settings,
Welcome, Welcome,
Donate,
} }
impl TabType { impl TabType {
pub fn get_preferred_padding(&self) -> f32 { pub fn get_preferred_padding(&self) -> f32 {
match self { match self {
TabType::Welcome => 0.0, TabType::Welcome | TabType::Donate => 0.0,
_ => 16.0, _ => 16.0,
} }
} }

View File

@ -81,7 +81,6 @@ enum Task {
RestartSoftware, RestartSoftware,
SetTab(TabNameEnum), SetTab(TabNameEnum),
SettingUpdated(SettingType), SettingUpdated(SettingType),
ShowWelcomeScreen,
UpdateBool(SettingType, bool), UpdateBool(SettingType, bool),
UpdateFloat(SettingType, f32), UpdateFloat(SettingType, f32),
UpdateInt(SettingType, i32), UpdateInt(SettingType, i32),
@ -150,9 +149,6 @@ impl<T> Tab<T> for TabSettings<T> {
Task::SetTab(tab) => { Task::SetTab(tab) => {
self.set_tab(frontend, data, tab)?; self.set_tab(frontend, data, tab)?;
} }
Task::ShowWelcomeScreen => {
self.frontend_tasks.push(FrontendTask::SetTab(TabType::Welcome));
}
Task::UpdateBool(setting, n) => { Task::UpdateBool(setting, n) => {
self.tasks.push(Task::SettingUpdated(setting)); self.tasks.push(Task::SettingUpdated(setting));
if let Some(task) = setting.get_frontend_task() { if let Some(task) = setting.get_frontend_task() {

View File

@ -50,13 +50,6 @@ impl State {
"dashboard/refresh.svg", "dashboard/refresh.svg",
Task::RestartSoftware, Task::RestartSoftware,
)?; )?;
options_danger_button(
par.mp,
c,
"APP_SETTINGS.SHOW_WELCOME_SCREEN",
"dashboard/welcome.svg",
Task::ShowWelcomeScreen,
)?;
Ok(State {}) Ok(State {})
} }
} }

View File

@ -140,11 +140,18 @@ impl<T> TabWelcome<T> {
let tasks = self.frontend_tasks.clone(); let tasks = self.frontend_tasks.clone();
Rc::new(move |_, _| { Rc::new(move |_, _| {
tasks.push(FrontendTask::SetTab(TabType::Home)); tasks.push(FrontendTask::SetTab(TabType::Home));
tasks.push(FrontendTask::MarkTutorialGraduated);
Ok(()) Ok(())
}) })
}); });
} }
if let Ok(btn) = state.fetch_component_as::<ComponentButton>("btn_wayvr_org") {
self
.frontend_tasks
.handle_button(&btn, FrontendTask::OpenURL(Rc::from("https://wayvr.org")));
}
self.state_tab = Some(state); self.state_tab = Some(state);
Ok(()) Ok(())

View File

@ -91,7 +91,7 @@ pub struct ParsedOpenXrInputPath {
pub component: XrInputComponent, pub component: XrInputComponent,
} }
impl<'a> TryFrom<&'a str> for ParsedOpenXrInputPath { impl TryFrom<&str> for ParsedOpenXrInputPath {
type Error = anyhow::Error; type Error = anyhow::Error;
fn try_from(path: &str) -> anyhow::Result<Self> { fn try_from(path: &str) -> anyhow::Result<Self> {

View File

@ -91,7 +91,7 @@ impl ViewTrait for View {
} }
Task::UpdateThreshold(action_name, side, i, val) => { Task::UpdateThreshold(action_name, side, i, val) => {
let cur_profile = &mut self.profiles[self.cur_profile_idx]; let cur_profile = &mut self.profiles[self.cur_profile_idx];
let action_mut = get_action_mut(cur_profile, &*action_name); let action_mut = get_action_mut(cur_profile, &action_name);
let threshold = if matches!(side, XrInputSide::Right) { let threshold = if matches!(side, XrInputSide::Right) {
action_mut.threshold_right.get_or_insert(DEFAULT_BUTTON_THRESHOLDS) action_mut.threshold_right.get_or_insert(DEFAULT_BUTTON_THRESHOLDS)
@ -124,10 +124,10 @@ impl ViewTrait for View {
*side_mut = None; *side_mut = None;
} }
"subpath" => { "subpath" => {
apply_subpath(side_mut, &side, &value, self.controller_profile); apply_subpath(side_mut, side, value, self.controller_profile);
} }
"comp" => { "comp" => {
apply_comp(side_mut, &side, &value); apply_comp(side_mut, side, value);
} }
"click" => match value { "click" => match value {
"triple" => { "triple" => {
@ -244,7 +244,7 @@ impl View {
&mut mp, &mut mp,
self.list_parent, self.list_parent,
action.into(), action.into(),
&self.controller_profile, self.controller_profile,
current, current,
)?; )?;
} }
@ -616,7 +616,7 @@ fn create_dropdown<B: 'static + BindingsDropdown>(
let title = item.translation(); let title = item.translation();
context_menu::Cell { context_menu::Cell {
action_name: Some(item.action_str(&*action, side)), action_name: Some(item.action_str(&action, side)),
title, title,
tooltip: None, tooltip: None,
attribs: vec![], attribs: vec![],
@ -624,7 +624,7 @@ fn create_dropdown<B: 'static + BindingsDropdown>(
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if let Some(action_str) = B::clear_str(&*action, side) { if let Some(action_str) = B::clear_str(&action, side) {
cells.insert( cells.insert(
0, 0,
context_menu::Cell { context_menu::Cell {

View File

@ -16,6 +16,7 @@ impl TestbedDashboard {
let frontend = frontend::Frontend::new(frontend::InitParams { let frontend = frontend::Frontend::new(frontend::InitParams {
interface: Box::new(interface), interface: Box::new(interface),
show_welcome: false,
has_monado: true, has_monado: true,
lang_provider: &lang_provider, lang_provider: &lang_provider,
theme: Rc::new(WguiTheme::default()), theme: Rc::new(WguiTheme::default()),

View File

@ -743,6 +743,20 @@ fn start_grab(
grab_anchor, grab_anchor,
}); });
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Id(id),
Box::new({
let pos = state.positioning;
let name = name.clone();
move |app, o| {
let _ = o
.backend
.notify(app, OverlayEventData::OverlayGrabbed { name, pos, editing })
.inspect_err(|e| log::warn!("Error during Notify OverlayGrabbed: {e:?}"));
}
}),
)));
// Show anchor // Show anchor
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify( app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(ANCHOR_NAME.clone()), OverlaySelector::Name(ANCHOR_NAME.clone()),

View File

@ -10,7 +10,7 @@ use wlx_common::{
astr_containers::AStrMap, astr_containers::AStrMap,
config::{ config::{
AltModifier, CaptureMethod, ChromaKeyParams, GeneralConfig, HandsfreePointer, AltModifier, CaptureMethod, ChromaKeyParams, GeneralConfig, HandsfreePointer,
SerializedWindowSet, SerializedWindowStates, InputEmulationMethod, SerializedWindowSet, SerializedWindowStates,
}, },
config_io, config_io,
locale::Language, locale::Language,
@ -182,6 +182,8 @@ pub struct AutoSettings {
pub handsfree_pointer: HandsfreePointer, pub handsfree_pointer: HandsfreePointer,
pub language: Option<Language>, pub language: Option<Language>,
pub chroma_key_params: ChromaKeyParams, pub chroma_key_params: ChromaKeyParams,
pub input_emulation_method: InputEmulationMethod,
pub tutorial_graduated: bool,
} }
fn get_settings_path() -> PathBuf { fn get_settings_path() -> PathBuf {
@ -242,6 +244,8 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> {
handsfree_pointer: config.handsfree_pointer, handsfree_pointer: config.handsfree_pointer,
language: config.language, language: config.language,
chroma_key_params: config.chroma_key_params.clone(), chroma_key_params: config.chroma_key_params.clone(),
input_emulation_method: config.input_emulation_method,
tutorial_graduated: config.tutorial_graduated,
}; };
let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic let json = serde_json::to_string_pretty(&conf).unwrap(); // want panic

View File

@ -1,7 +1,11 @@
use std::sync::atomic::Ordering; use std::{
ops::Add,
sync::{Arc, atomic::Ordering},
time::{Duration, Instant},
};
use dash_frontend::frontend::{self, FrontendTask, FrontendUpdateParams}; use dash_frontend::frontend::{self, FrontendTask, FrontendUpdateParams};
use glam::{Affine2, Affine3A, Vec2, vec2, vec3}; use glam::{Affine2, Affine3A, Quat, Vec2, vec2, vec3};
use wayvr_ipc::{ use wayvr_ipc::{
packet_client::WvrProcessLaunchParams, packet_client::WvrProcessLaunchParams,
packet_server::{WvrProcess, WvrProcessHandle, WvrWindow, WvrWindowHandle}, packet_server::{WvrProcess, WvrProcessHandle, WvrWindow, WvrWindowHandle},
@ -49,7 +53,7 @@ use crate::{
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender, FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
ui_transform, ui_transform,
}, },
window::{OverlayCategory, OverlayWindowConfig}, window::{OverlayCategory, OverlayWindowConfig, realign},
}, },
}; };
@ -65,6 +69,7 @@ pub struct DashFrontend {
timestep: Timestep, timestep: Timestep,
has_focus: [bool; 2], has_focus: [bool; 2],
context: WguiContext, context: WguiContext,
tutorial: bool,
} }
const GUI_SCALE: f32 = 2.0; const GUI_SCALE: f32 = 2.0;
@ -81,9 +86,12 @@ impl DashFrontend {
} }
} }
let tutorial = !app.session.config.tutorial_graduated;
let frontend = frontend::Frontend::new(frontend::InitParams { let frontend = frontend::Frontend::new(frontend::InitParams {
interface: Box::new(interface), interface: Box::new(interface),
lang_provider: &WayVRLangProvider::from_config(&app.session.config), lang_provider: &WayVRLangProvider::from_config(&app.session.config),
show_welcome: tutorial,
has_monado: matches!(app.xr_backend, XrBackend::OpenXR), has_monado: matches!(app.xr_backend, XrBackend::OpenXR),
theme: app.wgui_theme.clone(), theme: app.wgui_theme.clone(),
})?; })?;
@ -100,6 +108,7 @@ impl DashFrontend {
timestep: Timestep::new(60.0), timestep: Timestep::new(60.0),
has_focus: [false, false], has_focus: [false, false],
context, context,
tutorial,
}) })
} }
@ -197,7 +206,26 @@ impl OverlayBackend for DashFrontend {
}) })
} }
fn notify(&mut self, _app: &mut AppState, _data: OverlayEventData) -> anyhow::Result<()> { fn notify(&mut self, app: &mut AppState, data: OverlayEventData) -> anyhow::Result<()> {
if !self.tutorial {
return Ok(());
}
// if we're grabbed, stop following the hmd
if let OverlayEventData::OverlayGrabbed { name, .. } = data {
if &*name == DASH_NAME {
self.tutorial = false;
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(name),
Box::new(|_app, owc| {
if let Some(active_state) = owc.active_state.as_mut() {
active_state.positioning = Positioning::Floating;
}
}),
)));
}
}
Ok(()) Ok(())
} }
@ -290,7 +318,56 @@ impl OverlayBackend for DashFrontend {
} }
} }
fn tutorial_spawn_effect(app: &mut AppState) -> anyhow::Result<()> {
let dash_name: Arc<str> = DASH_NAME.into();
app.tasks.enqueue_at(
TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(dash_name.clone()),
Box::new(|app, owc| {
let hmd = &app.input_state.hmd;
let pos = hmd.translation + hmd.z_axis * -2.0;
let mut transform = Affine3A::from_rotation_translation(Quat::IDENTITY, pos.into());
realign(&mut transform, hmd, 1.0);
owc.active_state = Some(OverlayWindowState {
saved_transform: Some(Affine3A::from_translation(vec3(0., -0.1, -0.9))),
transform,
grabbable: true,
interactable: true,
positioning: Positioning::FollowHead { lerp: 0.01 },
curvature: Some(0.15),
alpha: 0.1,
..Default::default()
});
}),
)),
Instant::now().add(Duration::from_millis(500)),
);
// hacky fade in over time
for i in 0..46 {
app.tasks.enqueue_at(
TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(dash_name.clone()),
Box::new(|_app, owc| {
if let Some(active_state) = owc.active_state.as_mut() {
active_state.alpha = (active_state.alpha + 0.025).min(1.0);
}
}),
)),
Instant::now().add(Duration::from_millis(500 + 40 * i)),
);
}
Ok(())
}
pub fn create_dash_frontend(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> { pub fn create_dash_frontend(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
if !app.session.config.tutorial_graduated {
tutorial_spawn_effect(app)?;
}
Ok(OverlayWindowConfig { Ok(OverlayWindowConfig {
name: DASH_NAME.into(), name: DASH_NAME.into(),
default_state: OverlayWindowState { default_state: OverlayWindowState {
@ -465,6 +542,7 @@ impl DashInterface<AppState> for DashInterfaceLive {
.enqueue(TaskType::OpenXR(OpenXrTask::EnvironmentChanged)); .enqueue(TaskType::OpenXR(OpenXrTask::EnvironmentChanged));
} }
} }
_ => {}
} }
} }

View File

@ -427,4 +427,7 @@ pub struct GeneralConfig {
#[serde(default)] #[serde(default)]
pub chroma_key_params: ChromaKeyParams, pub chroma_key_params: ChromaKeyParams,
#[serde(default)]
pub tutorial_graduated: bool,
} }

View File

@ -81,6 +81,8 @@ pub trait DashInterface<T> {
pub enum ConfigChangeKind { pub enum ConfigChangeKind {
OverlayConfig, OverlayConfig,
EnvironmentBlend, EnvironmentBlend,
/// Marks the config for saving but doesn't notify any components
Other,
} }
pub type BoxDashInterface<T> = Box<dyn DashInterface<T>>; pub type BoxDashInterface<T> = Box<dyn DashInterface<T>>;