diff --git a/dash-frontend/src/tab/settings/mod.rs b/dash-frontend/src/tab/settings/mod.rs index 2f467da1..dfef32b0 100644 --- a/dash-frontend/src/tab/settings/mod.rs +++ b/dash-frontend/src/tab/settings/mod.rs @@ -28,7 +28,7 @@ use wlx_common::{ use crate::{ frontend::{Frontend, FrontendTask, FrontendTasks}, - tab::{settings::macros::MacroParams, Tab, TabType}, + tab::{Tab, TabType, settings::macros::MacroParams}, views::ViewUpdateParams, }; @@ -246,10 +246,11 @@ impl Tab for TabSettings { // Dropdown handling if let TickResult::Action(name) = self.context_menu.tick(&mut frontend.layout, &mut self.state)? { - if name.starts_with(';') && let Some(tab) = self.current_tab.as_mut() { + if name.starts_with(';') + && let Some(tab) = self.current_tab.as_mut() + { let config = frontend.interface.general_config(data); - tab.context_menu_custom(name, config, &mut frontend.layout, &mut self.state)?; - + tab.context_menu_custom(name, config, &mut frontend.layout, &mut self.state)?; } else if let (Some(setting), Some(id), Some(value), Some(text), Some(translated)) = { let mut s = name.splitn(5, ';'); (s.next(), s.next(), s.next(), s.next(), s.next()) diff --git a/dash-frontend/src/tab/settings/tab_features.rs b/dash-frontend/src/tab/settings/tab_features.rs index 4be0cc83..bac00ca4 100644 --- a/dash-frontend/src/tab/settings/tab_features.rs +++ b/dash-frontend/src/tab/settings/tab_features.rs @@ -1,18 +1,30 @@ use std::rc::Rc; use wgui::{ - components::button::{ButtonClickEvent, ComponentButton}, globals::WguiGlobals, i18n::Translation, layout::WidgetID, log::LogErr, parser::{Fetchable, TemplateParams}, task::Tasks, widget::label::WidgetLabel, windowing::context_menu::{self} + components::button::{ButtonClickEvent, ComponentButton}, + globals::WguiGlobals, + i18n::Translation, + layout::WidgetID, + log::LogErr, + parser::{Fetchable, TemplateParams}, + task::Tasks, + widget::label::WidgetLabel, + windowing::context_menu::{self}, }; use wlx_common::{async_executor::AsyncExecutor, config::GeneralConfig}; use crate::{ frontend::FrontendTasks, tab::settings::{ - SettingType, SettingsMountParams, SettingsTab, TabNameEnum, Task as ParentTask, horiz_cell, macros::{MacroParams, options_category, options_checkbox, options_range_f32} + SettingType, SettingsMountParams, SettingsTab, TabNameEnum, Task as ParentTask, horiz_cell, + macros::{MacroParams, options_category, options_checkbox, options_range_f32}, }, util::{ popup_manager::PopupHolder, - whisper::{WHISPER_MODELS, WhisperModel, whisper_any_models_downloaded, whisper_delete_all_models, whisper_model_from_name, whisper_model_path}, + whisper::{ + WHISPER_MODELS, WhisperModel, whisper_any_models_downloaded, whisper_delete_all_models, whisper_model_from_name, + whisper_model_path, + }, }, views::{self, ViewUpdateParams}, }; @@ -24,6 +36,7 @@ enum Task { WhisperDownload(&'static WhisperModel), WhisperDownloadDone, CloseDialog, + ReloadTab, } pub struct State { @@ -48,24 +61,32 @@ impl SettingsTab for State { if !whisper_model_path(model.file_name).exists() { // download failed, set to selection to none par.general_config.whisper_model = "".into(); - // re-init the whole tab + // reload the tab self.parent_tasks.push(ParentTask::SetTab(TabNameEnum::Features)); } } } Task::WhisperRemoveUnused => { let _ = whisper_delete_all_models().log_err("could not remove whisper models"); - }, + } Task::WhisperDownload(model) => { self.pending_download = Some(model); self.show_whisper_download_dialog(model, par.executor.clone()); - }, + } Task::WhisperDownloadDone => { - // we're happy - }, + if let Some(model) = self.pending_download.take() { + par.general_config.whisper_model = model.file_name.into(); + + // reload tab so that the downloaded checkmarks get populated + self.parent_tasks.push(ParentTask::SetTab(TabNameEnum::Features)); + } + } Task::CloseDialog => { - let close_dialog = self.popup_dialog.get_close_callback(par.layout); - close_dialog(); + let close_dialog = self.popup_dialog.get_close_callback(par.layout); + close_dialog(); + } + Task::ReloadTab => { + self.parent_tasks.push(ParentTask::SetTab(TabNameEnum::Features)); } } } @@ -84,19 +105,27 @@ impl SettingsTab for State { let mut s = action.splitn(3, ';'); (s.next(), s.next(), s.next()) } { - if !model_name.is_empty() && let Some(model) = whisper_model_from_name(model_name) - { - let mut common = layout.common(); - let mut label = state.fetch_widget_as::(common.state, &format!("{id}_value"))?; - label.set_text(&mut common, Translation::from_raw_text(model.display_name)); - - config.whisper_model = action.as_ref().into(); - - self.show_whisper_model_dialog_box_download(model)?; - } else if whisper_any_models_downloaded().unwrap_or_default() { - self.show_whisper_model_dialog_box_cleanup()?; + if !model_name.is_empty() + && let Some(model) = whisper_model_from_name(model_name) + { + let mut common = layout.common(); + let mut label = state.fetch_widget_as::(common.state, &format!("{id}_value"))?; + label.set_text(&mut common, Translation::from_raw_text(model.display_name)); + + config.whisper_model = model.file_name.into(); + if !whisper_model_path(model.file_name).exists() { + self.show_whisper_model_dialog_box_download(model)?; + } + } else { + config.whisper_model = "".into(); + // re-init the whole tab + if whisper_any_models_downloaded().unwrap_or_default() { + self.show_whisper_model_dialog_box_cleanup()?; + } else { + self.parent_tasks.push(ParentTask::SetTab(TabNameEnum::Features)); + } + } } - } Ok(()) } @@ -145,7 +174,7 @@ impl State { }) } - fn show_whisper_download_dialog(&mut self, model: &WhisperModel, executor: AsyncExecutor) { + fn show_whisper_download_dialog(&mut self, model: &WhisperModel, executor: AsyncExecutor) { views::download_file::mount_popup( self.popup_download.clone(), self.frontend_tasks.clone(), @@ -174,24 +203,23 @@ impl State { globals: self.globals.clone(), message: Translation::from_translation_key("APP_SETTINGS.WHISPER.NEED_TO_DOWNLOAD_MODEL"), 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, - }, - ], + 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::WhisperDownload(model)); } ACTION_CANCEL => { tasks.push(Task::CloseDialog); - // treat as failed download tasks.push(Task::WhisperDownloadClosed); } @@ -216,23 +244,26 @@ impl State { globals: self.globals.clone(), message: Translation::from_translation_key("APP_SETTINGS.WHISPER.REMOVE_UNUSED_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("REMOVE"), - icon: "dashboard/trash.svg", - action: ACTION_REMOVE, - }, - ], + 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("REMOVE"), + icon: "dashboard/trash.svg", + action: ACTION_REMOVE, + }, + ], on_action_click: Box::new(move |action| match action { ACTION_REMOVE => { tasks.push(Task::WhisperRemoveUnused); + tasks.push(Task::CloseDialog); + tasks.push(Task::ReloadTab); } ACTION_CANCEL => { tasks.push(Task::CloseDialog); + tasks.push(Task::ReloadTab); } _ => unreachable!(), }), @@ -249,6 +280,13 @@ fn whisper_models_dropdown(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Re let parent = horiz_cell(mp.layout, parent)?; + let mut downloaded_models = vec![]; + for (idx, m) in WHISPER_MODELS.iter().enumerate() { + if whisper_model_path(m.file_name).exists() { + downloaded_models.push(idx); + } + } + let current_file = mp.config.whisper_model.as_ref(); let current_translation = whisper_model_from_name(current_file) .map_or(Translation::from_translation_key("APP_SETTINGS.OPTION.NONE"), |x| { @@ -276,9 +314,14 @@ fn whisper_models_dropdown(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Re move |_common, e: ButtonClickEvent| { let mut cells = WHISPER_MODELS .iter() - .map(|item| context_menu::Cell { + .enumerate() + .map(|(idx, item)| context_menu::Cell { action_name: Some(format!(";{id};{}", item.file_name).into()), - title: Translation::from_raw_text(item.display_name), + title: Translation::from_raw_text_string(format!( + "{}{}", + item.display_name, + if downloaded_models.contains(&idx) { " ✅" } else { "" } + )), tooltip: None, attribs: vec![], }) diff --git a/dash-frontend/src/util/networking/http_client.rs b/dash-frontend/src/util/networking/http_client.rs index 1f525aaa..0b8bd75c 100644 --- a/dash-frontend/src/util/networking/http_client.rs +++ b/dash-frontend/src/util/networking/http_client.rs @@ -161,7 +161,8 @@ async fn fetch_and_follow_redirects( fn uri_try_from_str(s: &str) -> anyhow::Result { use std::convert::TryInto; let uri: hyper::Uri = s.try_into().context("invalid URI")?; - uri.path_and_query() + uri + .path_and_query() .ok_or_else(|| anyhow::anyhow!("URI has no path and query component")) .cloned() } @@ -183,7 +184,9 @@ async fn follow_single_redirect( hyper::Uri::try_from(location_str).context("invalid redirect location")? } else { let mut parts = original_uri.into_parts(); - parts.path_and_query = uri_try_from_str(location_str).context("invalid redirect location")?.into(); + parts.path_and_query = uri_try_from_str(location_str) + .context("invalid redirect location")? + .into(); hyper::Uri::from_parts(parts).context("failed to construct redirect URI")? }; diff --git a/wlx-common/src/lib.rs b/wlx-common/src/lib.rs index 09fe1a07..c8140e79 100644 --- a/wlx-common/src/lib.rs +++ b/wlx-common/src/lib.rs @@ -5,9 +5,9 @@ pub mod cache_dir; pub mod common; pub mod config; pub mod config_io; -pub mod data_dir; pub mod dash_interface; pub mod dash_interface_emulated; +pub mod data_dir; pub mod desktop_finder; mod handle; pub mod locale;