dashboard welcome mode

This commit is contained in:
galister 2026-07-03 02:12:40 +09:00
parent 4daff7300f
commit 6090be3d9c
6 changed files with 114 additions and 8 deletions

View File

@ -29,8 +29,8 @@ use wlx_common::{
use crate::{
assets,
tab::{
Tab, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, settings::TabSettings,
welcome::TabWelcome,
apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, settings::TabSettings, welcome::TabWelcome, Tab,
TabType,
},
util::{
popup_manager::{MountPopupOnceParams, PopupManager, PopupManagerParams},
@ -91,6 +91,7 @@ pub struct FrontendUpdateResult {
pub struct InitParams<'a, T> {
pub interface: BoxDashInterface<T>,
pub lang_provider: &'a WayVRLangProvider,
pub show_welcome: bool,
pub has_monado: bool,
pub theme: Rc<WguiTheme>,
}
@ -156,7 +157,13 @@ impl<T: 'static> Frontend<T> {
let toast_manager = ToastManager::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_rect_content = state.get_widget_id("rect_content")?;

View File

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

View File

@ -743,6 +743,20 @@ fn start_grab(
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
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
OverlaySelector::Name(ANCHOR_NAME.clone()),

View File

@ -10,7 +10,7 @@ use wlx_common::{
astr_containers::AStrMap,
config::{
AltModifier, CaptureMethod, ChromaKeyParams, GeneralConfig, HandsfreePointer,
SerializedWindowSet, SerializedWindowStates,
InputEmulationMethod, SerializedWindowSet, SerializedWindowStates,
},
config_io,
locale::Language,
@ -182,6 +182,8 @@ pub struct AutoSettings {
pub handsfree_pointer: HandsfreePointer,
pub language: Option<Language>,
pub chroma_key_params: ChromaKeyParams,
pub input_emulation_method: InputEmulationMethod,
pub tutorial_graduated: bool,
}
fn get_settings_path() -> PathBuf {
@ -242,6 +244,8 @@ pub fn save_settings(config: &GeneralConfig) -> anyhow::Result<()> {
handsfree_pointer: config.handsfree_pointer,
language: config.language,
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

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 glam::{Affine2, Affine3A, Vec2, vec2, vec3};
use glam::{Affine2, Affine3A, Quat, Vec2, vec2, vec3};
use wayvr_ipc::{
packet_client::WvrProcessLaunchParams,
packet_server::{WvrProcess, WvrProcessHandle, WvrWindow, WvrWindowHandle},
@ -49,7 +53,7 @@ use crate::{
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
ui_transform,
},
window::{OverlayCategory, OverlayWindowConfig},
window::{OverlayCategory, OverlayWindowConfig, realign},
},
};
@ -65,6 +69,7 @@ pub struct DashFrontend {
timestep: Timestep,
has_focus: [bool; 2],
context: WguiContext,
tutorial: bool,
}
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 {
interface: Box::new(interface),
lang_provider: &WayVRLangProvider::from_config(&app.session.config),
show_welcome: tutorial,
has_monado: matches!(app.xr_backend, XrBackend::OpenXR),
theme: app.wgui_theme.clone(),
})?;
@ -100,6 +108,7 @@ impl DashFrontend {
timestep: Timestep::new(60.0),
has_focus: [false, false],
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(())
}
@ -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., -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> {
if !app.session.config.tutorial_graduated {
tutorial_spawn_effect(app)?;
}
Ok(OverlayWindowConfig {
name: DASH_NAME.into(),
default_state: OverlayWindowState {

View File

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