dash-frontend: dedicated "Space drag" settings tab, deduplicate overlay shift code in xr&vr, account for gravity shift for static overlays

This commit is contained in:
Aleksander 2026-05-24 20:35:45 +02:00
parent c7037f8941
commit 2f6131f8d0
10 changed files with 93 additions and 55 deletions

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="currentColor" d="m12 22l-4.25-4.25l1.425-1.425L11 18.15V13H5.875L7.7 14.8l-1.45 1.45L2 12l4.225-4.225L7.65 9.2L5.85 11H11V5.85L9.175 7.675L7.75 6.25L12 2l4.25 4.25l-1.425 1.425L13 5.85V11h5.125L16.3 9.2l1.45-1.45L22 12l-4.25 4.25l-1.425-1.425L18.15 13H13v5.125l1.8-1.825l1.45 1.45z"/></svg>

After

Width:  |  Height:  |  Size: 500 B

View File

@ -13,13 +13,13 @@
</template>
<template name="SliderSetting">
<label text="${text}" translation="${translation}" />
<Slider id="${id}" width="250" height="24" min_value="${min}" max_value="${max}" step="${step}" value="${value}" tooltip="${tooltip}" />
<Slider id="${id}" width="200" height="24" min_value="${min}" max_value="${max}" step="${step}" value="${value}" tooltip="${tooltip}" />
<label text="${text}" weight="bold" translation="${translation}" />
</template>
<template name="RangeSetting">
<label text="${text}" translation="${translation}" />
<Slider id="${id}" width="250" height="24" min_value="${min}" max_value="${max}" step="${step}" value="${value}" value2="${value2}" tooltip="${tooltip}" />
<Slider id="${id}" width="200" height="24" min_value="${min}" max_value="${max}" step="${step}" value="${value}" value2="${value2}" tooltip="${tooltip}" />
<label text="${text}" weight="bold" translation="${translation}" />
</template>
<template name="SelectSetting">
@ -56,6 +56,7 @@
<Tab name="skybox" translation="APP_SETTINGS.SKYBOX" sprite_src_builtin="dashboard/globe.svg" />
<Tab name="features" translation="APP_SETTINGS.FEATURES" sprite_src_builtin="dashboard/options.svg" />
<Tab name="controls" translation="APP_SETTINGS.CONTROLS" sprite_src_builtin="dashboard/controller.svg" />
<Tab name="space_drag" translation="APP_SETTINGS.SPACE_DRAG" sprite_src_builtin="dashboard/drag.svg" />
<Tab name="misc" translation="APP_SETTINGS.MISC" sprite_src_builtin="dashboard/blocks.svg" />
<Tab name="autostart_apps" translation="APP_SETTINGS.AUTOSTART_APPS" sprite_src_builtin="dashboard/apps.svg" />
<Tab name="troubleshooting" translation="APP_SETTINGS.TROUBLESHOOTING" sprite_src_builtin="dashboard/cpu.svg" />

View File

@ -103,6 +103,7 @@
"SETS_ON_WATCH": "Sets on watch",
"SKYBOX": "Skybox",
"SKYMAP_ALREADY_DOWNLOADED": "This skymap is already downloaded. Select desired action.",
"SPACE_DRAG": "Space drag",
"SPACE_DRAG_FLING_STRENGTH": "Fling strength",
"SPACE_DRAG_DAMPING": "Damping",
"SPACE_DRAG_GRAVITY": "Gravity",

View File

@ -39,12 +39,14 @@ mod tab_features;
mod tab_look_and_feel;
mod tab_misc;
mod tab_skybox;
mod tab_space_drag;
mod tab_troubleshooting;
#[derive(Clone)]
enum TabNameEnum {
LookAndFeel,
Features,
SpaceDrag,
Controls,
Misc,
AutostartApps,
@ -58,6 +60,7 @@ impl TabNameEnum {
"look_and_feel" => Some(TabNameEnum::LookAndFeel),
"features" => Some(TabNameEnum::Features),
"controls" => Some(TabNameEnum::Controls),
"space_drag" => Some(TabNameEnum::SpaceDrag),
"misc" => Some(TabNameEnum::Misc),
"autostart_apps" => Some(TabNameEnum::AutostartApps),
"troubleshooting" => Some(TabNameEnum::Troubleshooting),
@ -584,6 +587,9 @@ impl<T> TabSettings<T> {
TabNameEnum::Features => {
self.current_tab = Some(Box::new(tab_features::State::mount(settings_mount_params)?));
}
TabNameEnum::SpaceDrag => {
self.current_tab = Some(Box::new(tab_space_drag::State::mount(settings_mount_params)?));
}
TabNameEnum::Controls => {
self.current_tab = Some(Box::new(tab_controls::State::mount(settings_mount_params)?));
}

View File

@ -1,6 +1,6 @@
use crate::tab::settings::{
SettingType, SettingsMountParams, SettingsTab,
macros::{options_category, options_checkbox, options_range_f32, options_slider_f32},
macros::{options_category, options_checkbox, options_range_f32},
};
pub struct State {}
@ -13,18 +13,6 @@ impl State {
options_checkbox(par.mp, c, SettingType::NotificationsEnabled)?;
options_checkbox(par.mp, c, SettingType::NotificationsSoundEnabled)?;
options_checkbox(par.mp, c, SettingType::KeyboardSoundEnabled)?;
if !par.feats.openxr || par.feats.monado {
// monado or openvr
options_checkbox(par.mp, c, SettingType::SpaceDragUnlocked)?;
options_slider_f32(par.mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5)?;
options_slider_f32(par.mp, c, SettingType::SpaceDragGravity, 0.0, 10.0, 0.5)?;
options_slider_f32(par.mp, c, SettingType::SpaceDragDamping, 0.1, 1.0, 0.01)?;
options_slider_f32(par.mp, c, SettingType::SpaceDragFlingStrength, 0.0, 3.0, 0.1)?;
}
if par.feats.monado {
// openvr can only ever rotate yaw
options_checkbox(par.mp, c, SettingType::SpaceRotateUnlocked)?;
}
if !par.feats.openxr || par.feats.monado {
// monado or openvr
options_checkbox(par.mp, c, SettingType::BlockGameInput)?;

View File

@ -0,0 +1,27 @@
use crate::tab::settings::{
SettingType, SettingsMountParams, SettingsTab,
macros::{options_category, options_checkbox, options_slider_f32},
};
pub struct State {}
impl SettingsTab for State {}
impl State {
pub fn mount(par: SettingsMountParams) -> anyhow::Result<State> {
let c = options_category(par.mp, par.id_parent, "APP_SETTINGS.SPACE_DRAG", "dashboard/drag.svg")?;
if !par.feats.openxr || par.feats.monado {
// monado or openvr
options_checkbox(par.mp, c, SettingType::SpaceDragUnlocked)?;
options_slider_f32(par.mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5)?;
options_slider_f32(par.mp, c, SettingType::SpaceDragGravity, 0.0, 10.0, 0.5)?;
options_slider_f32(par.mp, c, SettingType::SpaceDragDamping, 0.1, 1.0, 0.01)?;
options_slider_f32(par.mp, c, SettingType::SpaceDragFlingStrength, 0.0, 3.0, 0.1)?;
}
if par.feats.monado {
// openvr can only ever rotate yaw
options_checkbox(par.mp, c, SettingType::SpaceRotateUnlocked)?;
}
Ok(State {})
}
}

View File

@ -6,7 +6,7 @@ use ovr_overlay::{
};
use crate::{
backend::{input::InputState, task::PlayspaceTask},
backend::{input::InputState, playspace_common, task::PlayspaceTask},
state::AppState,
windowing::manager::OverlayWindowManager,
};
@ -137,16 +137,7 @@ impl PlayspaceMover {
}
let overlay_offset = data.pose.inverse().transform_vector3a(relative_pos) * -1.0;
overlays.values_mut().for_each(|overlay| {
let Some(state) = overlay.config.active_state.as_mut() else {
return;
};
if state.positioning.moves_with_space() {
state.transform.translation += overlay_offset;
overlay.config.dirty = true;
}
});
playspace_common::shift_overlays(overlays, overlay_offset);
data.pose.translation += relative_pos;
data.hand_pose = new_hand;

View File

@ -5,7 +5,7 @@ use wgui::log::LogErr;
use crate::{
backend::{
input::InputState,
playspace_common::{SpaceGravity, SpaceGravityUpdateParams},
playspace_common::{self, SpaceGravity, SpaceGravityUpdateParams},
task::PlayspaceTask,
},
state::AppState,
@ -174,16 +174,7 @@ impl PlayspaceMover {
}
let overlay_offset = data.pose.inverse().transform_vector3a(relative_pos) * -1.0;
overlays.values_mut().for_each(|overlay| {
let Some(state) = overlay.config.active_state.as_mut() else {
return;
};
if state.positioning.moves_with_space() {
state.transform.translation += overlay_offset;
}
overlay.config.dirty = true;
});
playspace_common::shift_overlays(overlays, overlay_offset);
data.pose.translation += relative_pos;
data.hand_pose = new_hand;
@ -220,15 +211,17 @@ impl PlayspaceMover {
}
}
if let Some(playspace_pos) = self.gravity.update(SpaceGravityUpdateParams {
if let Some(res) = self.gravity.update(SpaceGravityUpdateParams {
dt: app.delta_time,
dragging: self.drag.is_some(),
config: &app.session.config,
}) {
apply_offset(
Affine3A::from_translation(playspace_pos.into()),
Affine3A::from_translation(res.playspace_pos.into()),
&mut monado.ipc,
);
playspace_common::shift_overlays(overlays, -res.playspace_pos_offset);
}
}

View File

@ -1,6 +1,8 @@
use glam::Vec3A;
use wlx_common::config::GeneralConfig;
use crate::windowing::manager::OverlayWindowManager;
pub struct SpaceGravityUpdateParams<'a> {
pub dt: f32,
pub dragging: bool,
@ -12,6 +14,26 @@ pub struct SpaceGravity {
space_pos: Vec3A,
}
pub fn shift_overlays<OverlayData>(
overlays: &mut OverlayWindowManager<OverlayData>,
overlay_offset: Vec3A,
) {
overlays.values_mut().for_each(|overlay| {
let Some(state) = overlay.config.active_state.as_mut() else {
return;
};
if state.positioning.moves_with_space() {
state.transform.translation += overlay_offset;
}
overlay.config.dirty = true;
});
}
pub struct SpaceGravityUpdateResult {
pub playspace_pos: Vec3A,
pub playspace_pos_offset: Vec3A, // position difference compared to previous update() call
}
impl SpaceGravity {
pub fn new() -> Self {
Self {
@ -31,21 +53,29 @@ impl SpaceGravity {
self.space_pos = space_pos;
}
pub fn update(&mut self, par: SpaceGravityUpdateParams) -> Option<Vec3A> {
if !par.dragging {
self.velocity.y += par.config.space_drag_gravity * par.dt;
// terminal velocity
self.velocity.y = self.velocity.y.min(200.0);
pub fn update(&mut self, par: SpaceGravityUpdateParams) -> Option<SpaceGravityUpdateResult> {
if par.dragging {
return None;
}
self.velocity *= (par.config.space_drag_damping).powf(par.dt * 10.0);
self.space_pos += self.velocity * par.dt;
let prev_pos = self.space_pos;
self.space_pos.y = self.space_pos.y.min(0.0);
self.velocity.y += par.config.space_drag_gravity * par.dt;
if self.velocity.length_squared() > 0.00003 {
// log::info!("velocity {}", self.velocity);
return Some(self.space_pos);
}
// terminal velocity
self.velocity.y = self.velocity.y.min(200.0);
self.velocity *= (par.config.space_drag_damping).powf(par.dt * 10.0);
self.space_pos += self.velocity * par.dt;
self.space_pos.y = self.space_pos.y.min(0.0);
if self.velocity.length_squared() > 0.00003 {
// Space position changed
return Some(SpaceGravityUpdateResult {
playspace_pos: self.space_pos,
playspace_pos_offset: self.space_pos - prev_pos,
});
}
None

View File

@ -346,8 +346,8 @@ impl State {
}
}
const BODY_COLOR: drawing::Color = drawing::Color::new(0.6, 0.65, 0.7, 0.2);
const BODY_BORDER_COLOR: drawing::Color = drawing::Color::new(0.4, 0.45, 0.5, 1.0);
const BODY_COLOR: drawing::Color = drawing::Color::new(0.6, 0.65, 0.7, 0.1);
const BODY_BORDER_COLOR: drawing::Color = drawing::Color::new(0.4, 0.45, 0.5, 0.6);
const HANDLE_BORDER_COLOR: drawing::Color = drawing::Color::new(0.85, 0.85, 0.85, 1.0);
const HANDLE_BORDER_COLOR_HOVERED: drawing::Color = drawing::Color::new(0.0, 0.0, 0.0, 1.0);
const HANDLE_COLOR: drawing::Color = drawing::Color::new(1.0, 1.0, 1.0, 1.0);