clean up some bindings ui code

This commit is contained in:
galister 2026-07-02 10:17:29 +09:00
parent 4edc6053ce
commit 8a8c41bd9e
3 changed files with 90 additions and 127 deletions

View File

@ -59,10 +59,10 @@
"SQUEEZE": "Grip" "SQUEEZE": "Grip"
}, },
"CLICK": { "CLICK": {
"ANY": "-", "ANY": "Any",
"DOUBLE": "Double-click", "DOUBLE": "Double-tap",
"TRIPLE": "Triple-click", "TRIPLE": "Triple-tap",
"TYPE": "Click count" "TYPE": "Tap count"
}, },
"ACTION": { "ACTION": {
"CLICK": "Click*", "CLICK": "Click*",

View File

@ -1,6 +1,6 @@
use std::{collections::BTreeMap, io::Read, rc::Rc}; use std::{collections::BTreeMap, io::Read, rc::Rc};
use anyhow::{Context, bail}; use anyhow::{bail, Context};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum::{AsRefStr, EnumProperty, EnumString}; use strum::{AsRefStr, EnumProperty, EnumString};
use wgui::i18n::Translation; use wgui::i18n::Translation;
@ -128,14 +128,23 @@ pub enum IdentifierType {
Squeeze, Squeeze,
} }
impl IdentifierType { impl BindingsDropdown for IdentifierType {
pub fn translation(&self) -> Translation { fn translation(&self) -> Translation {
self self
.get_str("Translation") .get_str("Translation")
.map(Translation::from_translation_key) .map(Translation::from_translation_key)
.or_else(|| self.get_str("Text").map(Translation::from_raw_text)) .or_else(|| self.get_str("Text").map(Translation::from_raw_text))
.unwrap_or_else(|| Translation::from_raw_text(self.as_ref())) .unwrap_or_else(|| Translation::from_raw_text(self.as_ref()))
} }
fn action_str(&self, action: &str, side: Side) -> Rc<str> {
let value = self.as_ref();
let side = side.as_ref();
format!("subpath;{action};{side};{value}").into()
}
fn clear_str(action: &str, side: Side) -> Option<Rc<str>> {
let side = side.as_ref();
Some(format!("subpath;{action};{side};-").into())
}
} }
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, EnumString, AsRefStr, EnumProperty)] #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, EnumString, AsRefStr, EnumProperty)]
@ -167,14 +176,22 @@ pub enum Component {
Other, Other,
} }
impl Component { impl BindingsDropdown for Component {
pub fn translation(&self) -> Translation { fn translation(&self) -> Translation {
self self
.get_str("Translation") .get_str("Translation")
.map(Translation::from_translation_key) .map(Translation::from_translation_key)
.or_else(|| self.get_str("Text").map(Translation::from_raw_text)) .or_else(|| self.get_str("Text").map(Translation::from_raw_text))
.unwrap_or_else(|| Translation::from_raw_text(self.as_ref())) .unwrap_or_else(|| Translation::from_raw_text(self.as_ref()))
} }
fn action_str(&self, action: &str, side: Side) -> Rc<str> {
let value = self.as_ref();
let side = side.as_ref();
format!("comp;{action};{side};{value}").into()
}
fn clear_str(_action: &str, _side: Side) -> Option<Rc<str>> {
None
}
} }
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, EnumString, AsRefStr)] #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, EnumString, AsRefStr)]
@ -244,12 +261,25 @@ pub enum ClickType {
Triple, Triple,
} }
impl ClickType { impl BindingsDropdown for ClickType {
pub fn translation(&self) -> Translation { fn translation(&self) -> Translation {
self self
.get_str("Translation") .get_str("Translation")
.map(Translation::from_translation_key) .map(Translation::from_translation_key)
.or_else(|| self.get_str("Text").map(Translation::from_raw_text)) .or_else(|| self.get_str("Text").map(Translation::from_raw_text))
.unwrap_or_else(|| Translation::from_raw_text(self.as_ref())) .unwrap_or_else(|| Translation::from_raw_text(self.as_ref()))
} }
fn action_str(&self, action: &str, _side: Side) -> Rc<str> {
let value = self.as_ref();
format!("click;{action};-;{value}").into()
}
fn clear_str(_action: &str, _side: Side) -> Option<Rc<str>> {
None
}
}
pub trait BindingsDropdown {
fn translation(&self) -> Translation;
fn action_str(&self, action: &str, side: Side) -> Rc<str>;
fn clear_str(action: &str, side: Side) -> Option<Rc<str>>;
} }

View File

@ -22,7 +22,7 @@ use crate::{
frontend::{FrontendTask, FrontendTasks}, frontend::{FrontendTask, FrontendTasks},
tab::settings::horiz_cell, tab::settings::horiz_cell,
util::{ util::{
openxr_bindings_schema::{ClickType, Component, IdentifierType, ParsedOpenXrInputPath, Profile, Side}, openxr_bindings_schema::{BindingsDropdown, ClickType, Component, IdentifierType, ParsedOpenXrInputPath, Profile, Side},
popup_manager::{MountPopupOnceParams, MountPopupOnceParamsExtra, PopupHolder, PopupPadding}, popup_manager::{MountPopupOnceParams, MountPopupOnceParamsExtra, PopupHolder, PopupPadding},
wgui_simple, wgui_simple,
}, },
@ -418,11 +418,7 @@ fn subpath_dropdown(
available: Rc<[IdentifierType]>, available: Rc<[IdentifierType]>,
current: Option<IdentifierType>, current: Option<IdentifierType>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let id = mp.idx.to_string();
mp.idx += 1;
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new(); let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
params.insert(Rc::from("tooltip"), Rc::from("APP_SETTINGS.BINDINGS.SUBPATH")); params.insert(Rc::from("tooltip"), Rc::from("APP_SETTINGS.BINDINGS.SUBPATH"));
params.insert(Rc::from("min_width"), Rc::from("100")); params.insert(Rc::from("min_width"), Rc::from("100"));
@ -434,55 +430,11 @@ fn subpath_dropdown(
AssetPath::BuiltIn(&format!("dashboard/hand_{}.svg", side.as_ref().to_lowercase())), AssetPath::BuiltIn(&format!("dashboard/hand_{}.svg", side.as_ref().to_lowercase())),
)?; )?;
mp.parser_state let current_text = current
.instantiate_template(mp.doc_params, "DropdownButton", mp.layout, parent, params)?;
let title = current
.map(|c| c.translation()) .map(|c| c.translation())
.unwrap_or_else(|| Translation::from_translation_key("APP_SETTINGS.OPTION.NONE")); .unwrap_or_else(|| Translation::from_translation_key("APP_SETTINGS.OPTION.NONE"));
{ create_dropdown(mp, parent, params, action, side, current_text, available)?;
let mut label = mp
.parser_state
.fetch_widget_as::<WidgetLabel>(&mp.layout.state, &format!("{id}_value"))?;
label.set_text_simple(&mut mp.layout.state.globals.get(), title);
}
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
btn.on_click(Rc::new({
let available = available.clone();
let tasks = mp.tasks.clone();
move |_common, e: ButtonClickEvent| {
let side = side.as_ref();
let mut cells = available
.iter()
.map(|item| {
let value = item.as_ref();
let title = item.translation();
context_menu::Cell {
action_name: Some(format!("subpath;{id};{action};{side};{value}").into()),
title,
tooltip: None,
attribs: vec![],
}
})
.collect::<Vec<_>>();
cells.insert(
0,
context_menu::Cell {
action_name: Some(format!("clear;{id};{action};{side};-").into()),
title: Translation::from_translation_key("APP_SETTINGS.OPTION.NONE"),
tooltip: None,
attribs: vec![],
},
);
tasks.push(Task::OpenContextMenu(e.mouse_pos_absolute.unwrap_or_default(), cells));
Ok(())
}
}));
Ok(()) Ok(())
} }
@ -499,27 +451,46 @@ fn component_dropdown(
return Ok(false); return Ok(false);
} }
let id = mp.idx.to_string();
mp.idx += 1;
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new(); let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
params.insert(Rc::from("text"), Rc::from("")); params.insert(Rc::from("text"), Rc::from(""));
params.insert(Rc::from("tooltip"), Rc::from("APP_SETTINGS.BINDINGS.COMPONENT")); params.insert(Rc::from("tooltip"), Rc::from("APP_SETTINGS.BINDINGS.COMPONENT"));
params.insert(Rc::from("min_width"), Rc::from("100")); params.insert(Rc::from("min_width"), Rc::from("100"));
mp.parser_state let current_text = current
.instantiate_template(mp.doc_params, "DropdownButton", mp.layout, parent, params)?;
let title = current
.map(|c| c.translation()) .map(|c| c.translation())
.unwrap_or_else(|| Translation::from_raw_text_rc(Default::default())); .unwrap_or_else(|| Translation::from_raw_text_rc(Default::default()));
create_dropdown(mp, parent, params, action, side, current_text, available)?;
Ok(true)
}
fn clicks_dropdown(mp: &mut MacroParams, parent: WidgetID, action: Rc<str>, current: ClickType) -> anyhow::Result<()> {
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert(Rc::from("text"), Rc::from(""));
params.insert(Rc::from("tooltip"), Rc::from("APP_SETTINGS.BINDINGS.CLICK.TYPE"));
params.insert(Rc::from("min_width"), Rc::from("100"));
let current_text = current.translation();
let available = [ClickType::Any, ClickType::Double, ClickType::Triple].into();
create_dropdown(mp, parent, params, action, Side::Left, current_text, available)?;
Ok(())
}
fn create_dropdown<B: 'static + BindingsDropdown>(mp: &mut MacroParams, parent: WidgetID, mut params: HashMap<Rc<str>, Rc<str>>, action: Rc<str>, side: Side, current_text: Translation, available: Rc<[B]>) -> anyhow::Result<()> {
let id = mp.idx.to_string();
mp.idx += 1;
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
mp.parser_state
.instantiate_template(mp.doc_params, "DropdownButton", mp.layout, parent, params)?;
{ {
let mut label = mp let mut label = mp
.parser_state .parser_state
.fetch_widget_as::<WidgetLabel>(&mp.layout.state, &format!("{id}_value"))?; .fetch_widget_as::<WidgetLabel>(&mp.layout.state, &format!("{id}_value"))?;
label.set_text_simple(&mut mp.layout.state.globals.get(), title); label.set_text_simple(&mut mp.layout.state.globals.get(), current_text);
} }
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?; let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
@ -527,73 +498,35 @@ fn component_dropdown(
let tasks = mp.tasks.clone(); let tasks = mp.tasks.clone();
let available = available.clone(); let available = available.clone();
move |_common, e: ButtonClickEvent| { move |_common, e: ButtonClickEvent| {
tasks.push(Task::OpenContextMenu( let mut cells = available
e.mouse_pos_absolute.unwrap_or_default(),
available
.iter() .iter()
.filter_map(|item| { .map(|item| {
let value = item.as_ref();
let title = item.translation(); let title = item.translation();
let side = side.as_ref();
Some(context_menu::Cell { context_menu::Cell {
action_name: Some(format!("comp;{id};{action};{side};{value}").into()), action_name: Some(item.action_str(&*action, side)),
title, title,
tooltip: None, tooltip: None,
attribs: vec![], attribs: vec![],
}
}) })
}) .collect::<Vec<_>>();
.collect(),
));
Ok(())
}
}));
Ok(true) if let Some(action_str) = B::clear_str(&*action, side) {
} cells.insert(
0,
fn clicks_dropdown(mp: &mut MacroParams, parent: WidgetID, action: Rc<str>, current: ClickType) -> anyhow::Result<()> { context_menu::Cell {
let id = mp.idx.to_string(); action_name: Some(action_str),
mp.idx += 1; title: Translation::from_translation_key("APP_SETTINGS.OPTION.NONE"),
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
params.insert(Rc::from("id"), Rc::from(id.as_ref()));
params.insert(Rc::from("text"), Rc::from(""));
params.insert(Rc::from("tooltip"), Rc::from("APP_SETTINGS.BINDINGS.CLICK.TYPE"));
params.insert(Rc::from("min_width"), Rc::from("100"));
mp.parser_state
.instantiate_template(mp.doc_params, "DropdownButton", mp.layout, parent, params)?;
let title = current.translation();
{
let mut label = mp
.parser_state
.fetch_widget_as::<WidgetLabel>(&mp.layout.state, &format!("{id}_value"))?;
label.set_text_simple(&mut mp.layout.state.globals.get(), title);
}
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
btn.on_click(Rc::new({
let tasks = mp.tasks.clone();
move |_common, e: ButtonClickEvent| {
tasks.push(Task::OpenContextMenu(
e.mouse_pos_absolute.unwrap_or_default(),
[ClickType::Any, ClickType::Double, ClickType::Triple]
.iter()
.filter_map(|item| {
let value = item.as_ref();
let title = item.translation();
Some(context_menu::Cell {
action_name: Some(format!("click;{id};{action};-;{value}").into()),
title,
tooltip: None, tooltip: None,
attribs: vec![], attribs: vec![],
}) },
}) );
.collect(), }
tasks.push(Task::OpenContextMenu(
e.mouse_pos_absolute.unwrap_or_default(),
cells,
)); ));
Ok(()) Ok(())
} }