mirror of https://github.com/wayvr-org/wayvr.git
start dashboard swipe type download button
This commit is contained in:
parent
18d3dc5e93
commit
404b5ea659
|
|
@ -226,6 +226,15 @@
|
|||
},
|
||||
"WHISPER_MODEL": "Speech-to-text model",
|
||||
"WHISPER_MODEL_HELP": "Whisper model to use.\nLarger models produce more accurate\nresults, but consume more VRAM.",
|
||||
"SWIPE_TYPE": {
|
||||
"NEED_TO_DOWNLOAD_MODELS": "The swipe-to-type model needs to be downloaded.\nDownload them now?",
|
||||
"MODELS_DOWNLOADED": "Model downloaded",
|
||||
"MODELS_NOT_DOWNLOADED": "Model not downloaded",
|
||||
"REMOVE_MODELS": "Remove swipe to type model",
|
||||
"DOWNLOAD_MODELS": "Download swipe to type model ({} total)"
|
||||
},
|
||||
"SWIPE_TYPE_MODELS": "Swipe-to-type models",
|
||||
"SWIPE_TYPE_MODELS_HELP": "Model files used by swipe-to-type.\nAll of them are required.",
|
||||
"GRID_OPACITY": "Floor grid opacity",
|
||||
"GRID_OPACITY_HELP": "Opacity of the floor grid when the skybox is enabled",
|
||||
"XWAYLAND_BY_DEFAULT": "Run apps in Compatibility mode by default",
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ pub(crate) enum Task {
|
|||
SavePlayspaceCenter,
|
||||
SetTab(TabNameEnum),
|
||||
SettingUpdated(SettingType),
|
||||
SwipeTypeAction(Rc<str>),
|
||||
UpdateBool(SettingType, bool),
|
||||
UpdateFloat(SettingType, f32),
|
||||
UpdateInt(SettingType, i32),
|
||||
|
|
@ -110,6 +111,17 @@ trait SettingsTab {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn push_task_string(
|
||||
&mut self,
|
||||
_action: &str,
|
||||
_config: &mut GeneralConfig,
|
||||
_change_kind: &mut Option<ConfigChangeKind>,
|
||||
_layout: &mut Layout,
|
||||
_state: &mut ParserState,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn context_menu_custom(
|
||||
&mut self,
|
||||
_action: Rc<str>,
|
||||
|
|
@ -254,6 +266,22 @@ impl<T> Tab<T> for TabSettings<T> {
|
|||
_ => { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
Task::SwipeTypeAction(action) => {
|
||||
if let Some(tab) = self.current_tab.as_mut() {
|
||||
let config = frontend.interface.general_config(data);
|
||||
let mut change_kind: Option<ConfigChangeKind> = None;
|
||||
tab.push_task_string(
|
||||
&action,
|
||||
config,
|
||||
&mut change_kind,
|
||||
&mut frontend.layout,
|
||||
&mut self.state,
|
||||
)?;
|
||||
if let Some(change_kind) = change_kind {
|
||||
frontend.interface.config_changed(data, change_kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -325,7 +353,6 @@ pub(crate) enum SettingType {
|
|||
InvertScrollDirectionY,
|
||||
KeyboardMiddleClick,
|
||||
KeyboardSoundEnabled,
|
||||
KeyboardSwipeToTypeEnabled,
|
||||
Language,
|
||||
LeftHandedMouse,
|
||||
LongPressDuration,
|
||||
|
|
@ -400,7 +427,6 @@ impl SettingType {
|
|||
Self::UsePassthrough => &mut config.use_passthrough,
|
||||
Self::UseSkybox => &mut config.use_skybox,
|
||||
Self::XwaylandByDefault => &mut config.xwayland_by_default,
|
||||
Self::KeyboardSwipeToTypeEnabled => &mut config.keyboard_swipe_to_type_enabled,
|
||||
_ => panic!("Requested bool for non-bool SettingType"),
|
||||
}
|
||||
}
|
||||
|
|
@ -524,7 +550,6 @@ impl SettingType {
|
|||
Self::InvertScrollDirectionY => Ok("APP_SETTINGS.INVERT_SCROLL_DIRECTION_Y"),
|
||||
Self::KeyboardMiddleClick => Ok("APP_SETTINGS.KEYBOARD_MIDDLE_CLICK"),
|
||||
Self::KeyboardSoundEnabled => Ok("APP_SETTINGS.KEYBOARD_SOUND_ENABLED"),
|
||||
Self::KeyboardSwipeToTypeEnabled => Ok("APP_SETTINGS.KEYBOARD_SWIPE_TO_TYPE_ENABLED"),
|
||||
Self::Language => Ok("APP_SETTINGS.LANGUAGE"),
|
||||
Self::LeftHandedMouse => Ok("APP_SETTINGS.LEFT_HANDED_MOUSE"),
|
||||
Self::LongPressDuration => Ok("APP_SETTINGS.LONG_PRESS_DURATION"),
|
||||
|
|
@ -576,7 +601,6 @@ impl SettingType {
|
|||
Self::InputCaptureMethod => Some("APP_SETTINGS.INPUT_CAPTURE_METHOD_HELP"),
|
||||
Self::InputEmulationMethod => Some("APP_SETTINGS.INPUT_EMULATION_METHOD_HELP"),
|
||||
Self::KeyboardMiddleClick => Some("APP_SETTINGS.KEYBOARD_MIDDLE_CLICK_HELP"),
|
||||
Self::KeyboardSwipeToTypeEnabled => Some("APP_SETTINGS.KEYBOARD_SWIPE_TO_TYPE_ENABLED_HELP"),
|
||||
Self::LeftHandedMouse => Some("APP_SETTINGS.LEFT_HANDED_MOUSE_HELP"),
|
||||
Self::MouseAcceleration => Some("APP_SETTINGS.POINTER_ACCELERATION_HELP"),
|
||||
Self::ScreenRenderDown => Some("APP_SETTINGS.SCREEN_RENDER_DOWN_HELP"),
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ use crate::{
|
|||
},
|
||||
util::{
|
||||
popup_manager::PopupHolder,
|
||||
swipe_type::{
|
||||
SWIPE_TYPE_MODELS, SwipeTypeModel, swipe_type_all_models_downloaded,
|
||||
swipe_type_delete_all_models, swipe_type_model_path,
|
||||
},
|
||||
whisper::{
|
||||
WHISPER_MODELS, WhisperModel, whisper_any_models_downloaded, whisper_delete_all_models, whisper_model_from_name,
|
||||
whisper_model_path,
|
||||
|
|
@ -35,6 +39,10 @@ enum Task {
|
|||
WhisperRemoveUnused,
|
||||
WhisperDownload(&'static WhisperModel),
|
||||
WhisperDownloadDone,
|
||||
SwipeTypeDownloadClosed,
|
||||
SwipeTypeRemoveAll,
|
||||
SwipeTypeDownloadAll,
|
||||
SwipeTypeDownloadDone,
|
||||
CloseDialog,
|
||||
ReloadTab,
|
||||
}
|
||||
|
|
@ -46,7 +54,8 @@ pub struct State {
|
|||
globals: WguiGlobals,
|
||||
tasks: Tasks<Task>,
|
||||
parent_tasks: Tasks<ParentTask>,
|
||||
pending_download: Option<&'static WhisperModel>,
|
||||
pending_whisper_download: Option<&'static WhisperModel>,
|
||||
pending_swipe_download: Option<&'static [SwipeTypeModel]>,
|
||||
}
|
||||
|
||||
impl SettingsTab for State {
|
||||
|
|
@ -57,7 +66,7 @@ impl SettingsTab for State {
|
|||
for task in self.tasks.drain() {
|
||||
match task {
|
||||
Task::WhisperDownloadClosed => {
|
||||
if let Some(model) = self.pending_download.take() {
|
||||
if let Some(model) = self.pending_whisper_download.take() {
|
||||
if !whisper_model_path(model.file_name).exists() {
|
||||
// download failed, set to selection to none
|
||||
par.general_config.whisper_model = "".into();
|
||||
|
|
@ -71,11 +80,11 @@ impl SettingsTab for State {
|
|||
let _ = whisper_delete_all_models().log_err("could not remove whisper models");
|
||||
}
|
||||
Task::WhisperDownload(model) => {
|
||||
self.pending_download = Some(model);
|
||||
self.pending_whisper_download = Some(model);
|
||||
self.show_whisper_download_dialog(model, par.executor.clone());
|
||||
}
|
||||
Task::WhisperDownloadDone => {
|
||||
if let Some(model) = self.pending_download.take() {
|
||||
if let Some(model) = self.pending_whisper_download.take() {
|
||||
par.general_config.whisper_model = model.file_name.into();
|
||||
par.config_change_kind.replace(ConfigChangeKind::Other);
|
||||
|
||||
|
|
@ -83,6 +92,28 @@ impl SettingsTab for State {
|
|||
self.parent_tasks.push(ParentTask::SetTab(TabNameEnum::Features));
|
||||
}
|
||||
}
|
||||
Task::SwipeTypeDownloadClosed => {
|
||||
self.pending_swipe_download = None;
|
||||
if !swipe_type_all_models_downloaded().unwrap_or_default() {
|
||||
par.general_config.swipe_type_models_downloaded = false;
|
||||
par.config_change_kind.replace(ConfigChangeKind::Other);
|
||||
self.parent_tasks.push(ParentTask::SetTab(TabNameEnum::Features));
|
||||
}
|
||||
}
|
||||
Task::SwipeTypeRemoveAll => {
|
||||
let _ = swipe_type_delete_all_models().log_err("could not remove swipe type models");
|
||||
}
|
||||
Task::SwipeTypeDownloadAll => {
|
||||
self.pending_swipe_download = Some(SWIPE_TYPE_MODELS);
|
||||
self.show_swipe_type_download_dialog(SWIPE_TYPE_MODELS, par.executor.clone());
|
||||
}
|
||||
Task::SwipeTypeDownloadDone => {
|
||||
if let Some(_) = self.pending_swipe_download.take() {
|
||||
par.general_config.swipe_type_models_downloaded = true;
|
||||
par.config_change_kind.replace(ConfigChangeKind::Other);
|
||||
self.parent_tasks.push(ParentTask::SetTab(TabNameEnum::Features));
|
||||
}
|
||||
}
|
||||
Task::CloseDialog => {
|
||||
let close_dialog = self.popup_dialog.get_close_callback(par.layout);
|
||||
close_dialog();
|
||||
|
|
@ -135,6 +166,27 @@ impl SettingsTab for State {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_task_string(
|
||||
&mut self,
|
||||
action: &str,
|
||||
_config: &mut GeneralConfig,
|
||||
_change_kind: &mut Option<ConfigChangeKind>,
|
||||
_layout: &mut wgui::layout::Layout,
|
||||
_state: &mut wgui::parser::ParserState,
|
||||
) -> anyhow::Result<()> {
|
||||
match action {
|
||||
"swipe_type_download" => {
|
||||
self.show_swipe_type_model_dialog_box_download()?;
|
||||
}
|
||||
"swipe_type_remove" => {
|
||||
self.tasks.push(Task::SwipeTypeRemoveAll);
|
||||
self.tasks.push(Task::ReloadTab);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
|
@ -149,11 +201,14 @@ impl State {
|
|||
whisper_models_dropdown(par.mp, c)?;
|
||||
}
|
||||
|
||||
if par.feats.swipe_to_type {
|
||||
swipe_type_models_button(par.mp, c)?;
|
||||
}
|
||||
|
||||
options_checkbox(par.mp, c, SettingType::NotificationsEnabled)?;
|
||||
options_checkbox(par.mp, c, SettingType::NotificationsSoundEnabled)?;
|
||||
options_checkbox(par.mp, c, SettingType::KeyboardSoundEnabled)?;
|
||||
options_checkbox(par.mp, c, SettingType::KeyboardSwipeToTypeEnabled)?;
|
||||
|
||||
|
||||
if !par.feats.openxr || par.feats.monado {
|
||||
// monado or openvr
|
||||
options_checkbox(par.mp, c, SettingType::BlockGameInput)?;
|
||||
|
|
@ -180,7 +235,8 @@ impl State {
|
|||
popup_dialog,
|
||||
frontend_tasks: par.frontend_tasks.clone(),
|
||||
globals: par.mp.doc_params.globals.clone(),
|
||||
pending_download: None,
|
||||
pending_whisper_download: None,
|
||||
pending_swipe_download: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -199,11 +255,16 @@ impl State {
|
|||
);
|
||||
}
|
||||
|
||||
fn show_swipe_type_download_dialog(&mut self, models: &[SwipeTypeModel], executor: AsyncExecutor) {
|
||||
//TODO: create views::download_files and use that
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn show_whisper_model_dialog_box_download(&mut self, model: &'static WhisperModel) -> anyhow::Result<()> {
|
||||
const ACTION_DOWNLOAD: &str = "download";
|
||||
const ACTION_CANCEL: &str = "cancel";
|
||||
|
||||
self.pending_download = Some(model);
|
||||
self.pending_whisper_download = Some(model);
|
||||
|
||||
let tasks = self.tasks.clone();
|
||||
views::dialog_box::mount_popup(
|
||||
|
|
@ -241,6 +302,45 @@ impl State {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn show_swipe_type_model_dialog_box_download(&mut self) -> anyhow::Result<()> {
|
||||
const ACTION_DOWNLOAD: &str = "download";
|
||||
const ACTION_CANCEL: &str = "cancel";
|
||||
|
||||
let tasks = self.tasks.clone();
|
||||
views::dialog_box::mount_popup(
|
||||
self.popup_dialog.clone(),
|
||||
self.frontend_tasks.clone(),
|
||||
views::dialog_box::Params {
|
||||
globals: self.globals.clone(),
|
||||
message: Translation::from_translation_key("APP_SETTINGS.SWIPE_TYPE.NEED_TO_DOWNLOAD_MODELS"),
|
||||
entries: vec![
|
||||
views::dialog_box::ButtonEntry {
|
||||
content: Translation::from_translation_key("APP_SETTINGS.CANCEL"),
|
||||
icon: "dashboard/close.svg",
|
||||
action: ACTION_CANCEL,
|
||||
},
|
||||
views::dialog_box::ButtonEntry {
|
||||
content: Translation::from_translation_key("DOWNLOAD"),
|
||||
icon: "dashboard/download.svg",
|
||||
action: ACTION_DOWNLOAD,
|
||||
},
|
||||
],
|
||||
on_action_click: Box::new(move |action| match action {
|
||||
ACTION_DOWNLOAD => {
|
||||
tasks.push(Task::SwipeTypeDownloadAll);
|
||||
}
|
||||
ACTION_CANCEL => {
|
||||
tasks.push(Task::CloseDialog);
|
||||
tasks.push(Task::SwipeTypeDownloadClosed);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_whisper_model_dialog_box_cleanup(&mut self) -> anyhow::Result<()> {
|
||||
const ACTION_REMOVE: &str = "remove";
|
||||
const ACTION_CANCEL: &str = "cancel";
|
||||
|
|
@ -357,3 +457,45 @@ fn whisper_models_dropdown(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Re
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn swipe_type_models_button(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
|
||||
let id = mp.idx.to_string();
|
||||
mp.idx += 1;
|
||||
|
||||
let id_cell = horiz_cell(mp.layout, parent)?;
|
||||
|
||||
let all_downloaded = swipe_type_all_models_downloaded().unwrap_or_default();
|
||||
let (translation, icon, action) = if all_downloaded {
|
||||
(
|
||||
"APP_SETTINGS.SWIPE_TYPE.REMOVE_MODELS",
|
||||
"dashboard/trash.svg",
|
||||
"swipe_type_remove",
|
||||
)
|
||||
} else {
|
||||
(
|
||||
"APP_SETTINGS.SWIPE_TYPE.DOWNLOAD_MODELS;5.0 MiB",
|
||||
"dashboard/download.svg",
|
||||
"swipe_type_download",
|
||||
)
|
||||
};
|
||||
|
||||
let mut params = TemplateParams::new();
|
||||
params.insert("id", &id);
|
||||
params.insert("translation", translation);
|
||||
params.insert("icon", icon);
|
||||
|
||||
mp.parser_state
|
||||
.instantiate_template(mp.doc_params, "ButtonText", mp.layout, id_cell, params)?;
|
||||
|
||||
let btn = mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
|
||||
btn.on_click(Rc::new({
|
||||
let parent_tasks = mp.tasks.clone();
|
||||
let action = Rc::<str>::from(action);
|
||||
move |_common, _e: ButtonClickEvent| {
|
||||
parent_tasks.push(ParentTask::SwipeTypeAction(action.clone()));
|
||||
Ok(())
|
||||
}
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -7,3 +7,4 @@ pub mod steam_utils;
|
|||
pub mod toast_manager;
|
||||
pub mod wgui_simple;
|
||||
pub mod whisper;
|
||||
pub mod swipe_type;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
use std::{fs, io, path::PathBuf};
|
||||
|
||||
use wlx_common::data_dir;
|
||||
|
||||
/// The set of model files required by the swipe-to-type engine.
|
||||
/// These correspond to the assets that `super-swipe-type` currently
|
||||
/// downloads via `cached_path` in `SwipeOrchestrator::new()`.
|
||||
pub struct SwipeTypeModel {
|
||||
pub file_name: &'static str,
|
||||
pub url: &'static str,
|
||||
}
|
||||
|
||||
pub const SWIPE_TYPE_MODELS: &[SwipeTypeModel] = &[
|
||||
SwipeTypeModel {
|
||||
file_name: "swipe_encoder_android.onnx",
|
||||
url: "https://wayvr.org/files/swipe_type/swipe_encoder_android.onnx",
|
||||
},
|
||||
SwipeTypeModel {
|
||||
file_name: "swipe_decoder_android.onnx",
|
||||
url: "https://wayvr.org/files/swipe_type/swipe_decoder_android.onnx",
|
||||
},
|
||||
SwipeTypeModel {
|
||||
file_name: "en_wordlist.fst",
|
||||
url: "https://wayvr.org/files/swipe_type/en_wordlist.fst",
|
||||
},
|
||||
SwipeTypeModel {
|
||||
file_name: "en_bigrams.fst",
|
||||
url: "https://wayvr.org/files/swipe_type/en_bigrams.fst",
|
||||
},
|
||||
];
|
||||
|
||||
pub fn swipe_type_model_folder() -> PathBuf {
|
||||
data_dir::get_path("swipe_type")
|
||||
}
|
||||
|
||||
pub fn swipe_type_model_path(file_name: &str) -> PathBuf {
|
||||
swipe_type_model_folder().join(file_name)
|
||||
}
|
||||
|
||||
/// Returns true when every required model file is present on disk.
|
||||
pub fn swipe_type_all_models_downloaded() -> io::Result<bool> {
|
||||
let path = swipe_type_model_folder();
|
||||
if !path.is_dir() {
|
||||
return Ok(false);
|
||||
}
|
||||
for model in SWIPE_TYPE_MODELS {
|
||||
if !path.join(model.file_name).exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn swipe_type_delete_all_models() -> io::Result<()> {
|
||||
let path = swipe_type_model_folder();
|
||||
if !path.is_dir() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for entry in fs::read_dir(path)? {
|
||||
let entry = entry?;
|
||||
let file_type = entry.file_type()?;
|
||||
|
||||
if file_type.is_file() || file_type.is_symlink() {
|
||||
fs::remove_file(entry.path())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -588,6 +588,10 @@ impl DashInterface<AppState> for DashInterfaceLive {
|
|||
whisper: true,
|
||||
#[cfg(not(feature = "whisper"))]
|
||||
whisper: false,
|
||||
#[cfg(feature = "swipe-to-type")]
|
||||
swipe_to_type: true,
|
||||
#[cfg(not(feature = "swipe-to-type"))]
|
||||
swipe_to_type: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ pub struct InterfaceFeats {
|
|||
pub openxr: bool,
|
||||
pub monado: bool,
|
||||
pub whisper: bool,
|
||||
pub swipe_to_type: bool,
|
||||
}
|
||||
|
||||
pub trait DashInterface<T> {
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ impl DashInterface<()> for DashInterfaceEmulated {
|
|||
openxr: true,
|
||||
monado: true,
|
||||
whisper: true,
|
||||
swipe_to_type: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue