refactor: separate modules - app, mapping
This commit is contained in:
+5
-4
@@ -3,10 +3,11 @@
|
|||||||
> Скоуп: src/main.rs (401 строка), src/config.rs (~210), src/winit.rs (45),
|
> Скоуп: src/main.rs (401 строка), src/config.rs (~210), src/winit.rs (45),
|
||||||
> ui/*.slint. Плюс вывод clippy.
|
> ui/*.slint. Плюс вывод clippy.
|
||||||
>
|
>
|
||||||
> Итог рефакторинга: main.rs 401→340 строк (и стал тонким по смыслу),
|
> Итог рефакторинга: main.rs 401→32 строки (чистый скелет), появился launcher.rs
|
||||||
> появился launcher.rs, 12 юнит-тестов, tools/check.ps1 (fmt+clippy -D warnings
|
> и app.rs (`struct App`: new/wire×4 группы/run), 12 юнит-тестов,
|
||||||
> +test) зелёный, дублирование запуска устранено, индекс из UI вместо поиска
|
> tools/check.ps1 (fmt+clippy -D warnings+test) зелёный, дублирование запуска
|
||||||
> по имени, все P0-P2 закрыты. UI проверен скриншотом — идентичен.
|
> устранено, индекс из UI вместо поиска по имени, все P0-P2 закрыты.
|
||||||
|
> UI проверен скриншотом — идентичен. Cold start release 110-133 ms.
|
||||||
|
|
||||||
## P0 — мусор и артефакты правок (чинить сразу)
|
## P0 — мусор и артефакты правок (чинить сразу)
|
||||||
|
|
||||||
|
|||||||
+273
@@ -0,0 +1,273 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use i_slint_backend_winit::WinitWindowAccessor;
|
||||||
|
use slint::{ComponentHandle, Model};
|
||||||
|
|
||||||
|
use crate::config::{self, BrowserEntry, Config};
|
||||||
|
use crate::launcher;
|
||||||
|
use crate::mapping::to_ui_config;
|
||||||
|
use crate::winit;
|
||||||
|
use crate::{BrowserConfig, MainWindow, RememberedEntry, SettingsWindow, Theme};
|
||||||
|
|
||||||
|
const DEFAULT_URL: &str = "ku6epxboctuk.github.io";
|
||||||
|
const ENV_THEME: &str = "BP_THEME";
|
||||||
|
const ENV_VIEW: &str = "BP_VIEW";
|
||||||
|
const VIEW_SETTINGS: &str = "settings";
|
||||||
|
|
||||||
|
pub struct App {
|
||||||
|
main_window: MainWindow,
|
||||||
|
settings_window: SettingsWindow,
|
||||||
|
browser_model: Rc<slint::VecModel<BrowserConfig>>,
|
||||||
|
remembered_model: Rc<slint::VecModel<RememberedEntry>>,
|
||||||
|
config: Rc<RefCell<Config>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn new(cfg: Config, url: Option<String>) -> Self {
|
||||||
|
let config = Rc::new(RefCell::new(cfg));
|
||||||
|
let browser_model = Rc::new(slint::VecModel::<BrowserConfig>::from(vec![]));
|
||||||
|
let remembered_model = Rc::new(slint::VecModel::<RememberedEntry>::from(vec![]));
|
||||||
|
|
||||||
|
let main_window = MainWindow::new().unwrap();
|
||||||
|
let settings_window = SettingsWindow::new().unwrap();
|
||||||
|
|
||||||
|
if std::env::var(ENV_THEME).as_deref() == Ok("light") {
|
||||||
|
main_window.global::<Theme>().set_is_dark(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::refresh_models(&browser_model, &remembered_model, &config.borrow());
|
||||||
|
|
||||||
|
settings_window.set_browsers(browser_model.clone().into());
|
||||||
|
settings_window.set_remembered(remembered_model.clone().into());
|
||||||
|
main_window.set_browser_model(browser_model.clone().into());
|
||||||
|
|
||||||
|
main_window.set_current_url(
|
||||||
|
url.clone()
|
||||||
|
.unwrap_or_else(|| DEFAULT_URL.to_string())
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
if let Some(u) = &url {
|
||||||
|
main_window.set_current_domain(config::domain_of(u).into());
|
||||||
|
}
|
||||||
|
main_window.set_remember_choice(config.borrow().settings.remember_choice);
|
||||||
|
main_window.set_always_ask(config.borrow().settings.always_ask);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
main_window,
|
||||||
|
settings_window,
|
||||||
|
browser_model,
|
||||||
|
remembered_model,
|
||||||
|
config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wire(&self) {
|
||||||
|
self.wire_launch();
|
||||||
|
self.wire_config_editor();
|
||||||
|
self.wire_settings_window();
|
||||||
|
self.wire_window_chrome();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wire_launch(&self) {
|
||||||
|
self.main_window.on_launch_browser({
|
||||||
|
let main_window = self.main_window.clone_strong();
|
||||||
|
let browser_model = self.browser_model.clone();
|
||||||
|
let config = self.config.clone();
|
||||||
|
move |index: i32| {
|
||||||
|
let index = index.max(0) as usize;
|
||||||
|
let Some(ui_entry) = browser_model.row_data(index) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
main_window.set_selected_index(index as i32);
|
||||||
|
let entry = BrowserEntry::from(&ui_entry);
|
||||||
|
let url = main_window.get_current_url().to_string();
|
||||||
|
let remember = main_window.get_remember_choice();
|
||||||
|
if launcher::launch_and_remember(&config, &entry, &url, remember) {
|
||||||
|
let _ = main_window.window().hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.main_window.on_open_clicked({
|
||||||
|
let main_window = self.main_window.clone_strong();
|
||||||
|
let browser_model = self.browser_model.clone();
|
||||||
|
let config = self.config.clone();
|
||||||
|
move || {
|
||||||
|
let index = main_window.get_selected_index().max(0) as usize;
|
||||||
|
let Some(ui_entry) = browser_model.row_data(index) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let entry = BrowserEntry::from(&ui_entry);
|
||||||
|
let url = main_window.get_current_url().to_string();
|
||||||
|
let remember = main_window.get_remember_choice();
|
||||||
|
if launcher::launch_and_remember(&config, &entry, &url, remember) {
|
||||||
|
let _ = main_window.window().hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.main_window.on_toggle_remember({
|
||||||
|
let config = self.config.clone();
|
||||||
|
move |checked| {
|
||||||
|
config.borrow_mut().settings.remember_choice = checked;
|
||||||
|
config::save(&config.borrow());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.main_window.on_toggle_always_ask({
|
||||||
|
let config = self.config.clone();
|
||||||
|
move |checked| {
|
||||||
|
config.borrow_mut().settings.always_ask = checked;
|
||||||
|
config::save(&config.borrow());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wire_config_editor(&self) {
|
||||||
|
self.main_window.on_settings_clicked(move || {
|
||||||
|
let path = config::config_path();
|
||||||
|
if !path.exists() {
|
||||||
|
let mut c = config::load();
|
||||||
|
config::ensure_browsers(&mut c);
|
||||||
|
}
|
||||||
|
let editor = std::process::Command::new("subl")
|
||||||
|
.arg(&path)
|
||||||
|
.spawn()
|
||||||
|
.or_else(|_| std::process::Command::new("notepad").arg(&path).spawn());
|
||||||
|
if let Err(e) = editor {
|
||||||
|
config::log(&format!("cannot open editor: {e}"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wire_settings_window(&self) {
|
||||||
|
self.settings_window.on_remove_browser({
|
||||||
|
let browser_model = self.browser_model.clone();
|
||||||
|
let remembered_model = self.remembered_model.clone();
|
||||||
|
let config = self.config.clone();
|
||||||
|
move |name| {
|
||||||
|
let name = name.to_string();
|
||||||
|
config.borrow_mut().browsers.retain(|b| b.name != name);
|
||||||
|
config::save(&config.borrow());
|
||||||
|
Self::refresh_models(&browser_model, &remembered_model, &config.borrow());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.settings_window.on_remove_remembered({
|
||||||
|
let browser_model = self.browser_model.clone();
|
||||||
|
let remembered_model = self.remembered_model.clone();
|
||||||
|
let config = self.config.clone();
|
||||||
|
move |domain| {
|
||||||
|
let domain = domain.to_string();
|
||||||
|
config.borrow_mut().remembered.remove(&domain);
|
||||||
|
config::save(&config.borrow());
|
||||||
|
Self::refresh_models(&browser_model, &remembered_model, &config.borrow());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.settings_window.on_redetect({
|
||||||
|
let browser_model = self.browser_model.clone();
|
||||||
|
let remembered_model = self.remembered_model.clone();
|
||||||
|
let config = self.config.clone();
|
||||||
|
move || {
|
||||||
|
let detected = config::detect_browsers();
|
||||||
|
let mut cfg_ref = config.borrow_mut();
|
||||||
|
for entry in detected {
|
||||||
|
if !cfg_ref
|
||||||
|
.browsers
|
||||||
|
.iter()
|
||||||
|
.any(|b| b.path.eq_ignore_ascii_case(&entry.path))
|
||||||
|
{
|
||||||
|
cfg_ref.browsers.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config::save(&cfg_ref);
|
||||||
|
drop(cfg_ref);
|
||||||
|
Self::refresh_models(&browser_model, &remembered_model, &config.borrow());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.settings_window.on_esc_pressed({
|
||||||
|
let settings_window = self.settings_window.clone_strong();
|
||||||
|
let main_window = self.main_window.clone_strong();
|
||||||
|
move || {
|
||||||
|
let _ = settings_window.window().hide();
|
||||||
|
main_window.invoke_restore_focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.settings_window.window().on_close_requested({
|
||||||
|
let main_window = self.main_window.clone_strong();
|
||||||
|
move || {
|
||||||
|
main_window.invoke_restore_focus();
|
||||||
|
slint::CloseRequestResponse::HideWindow
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wire_window_chrome(&self) {
|
||||||
|
self.main_window.on_request_drag({
|
||||||
|
let main_window = self.main_window.clone_strong();
|
||||||
|
move || {
|
||||||
|
main_window.window().with_winit_window(|winit_window| {
|
||||||
|
winit_window.drag_window().ok();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.main_window.on_esc_pressed({
|
||||||
|
let main_window = self.main_window.clone_strong();
|
||||||
|
move || {
|
||||||
|
let _ = main_window.window().hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refresh_models(
|
||||||
|
browser_model: &slint::VecModel<BrowserConfig>,
|
||||||
|
remembered_model: &slint::VecModel<RememberedEntry>,
|
||||||
|
cfg: &Config,
|
||||||
|
) {
|
||||||
|
let rows: Vec<BrowserConfig> = cfg.browsers.iter().map(to_ui_config).collect();
|
||||||
|
browser_model.set_vec(rows);
|
||||||
|
let rows: Vec<RememberedEntry> = cfg
|
||||||
|
.remembered
|
||||||
|
.iter()
|
||||||
|
.map(|(d, b)| RememberedEntry {
|
||||||
|
domain: d.clone().into(),
|
||||||
|
browser: b.clone().into(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
remembered_model.set_vec(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(self, start: Instant) {
|
||||||
|
let settings_only = std::env::var(ENV_VIEW).as_deref() == Ok(VIEW_SETTINGS);
|
||||||
|
|
||||||
|
if settings_only {
|
||||||
|
self.settings_window.show().unwrap();
|
||||||
|
let weak = self.settings_window.as_weak();
|
||||||
|
let _ = slint::invoke_from_event_loop(move || {
|
||||||
|
if let Some(sw) = weak.upgrade() {
|
||||||
|
winit::center_window(sw.window());
|
||||||
|
sw.invoke_restore_focus();
|
||||||
|
}
|
||||||
|
config::log(&format!("cold start -> event loop: {:?}", start.elapsed()));
|
||||||
|
});
|
||||||
|
self.settings_window.run().unwrap();
|
||||||
|
} else {
|
||||||
|
self.main_window.show().unwrap();
|
||||||
|
let weak = self.main_window.as_weak();
|
||||||
|
let _ = slint::invoke_from_event_loop(move || {
|
||||||
|
if let Some(mw) = weak.upgrade() {
|
||||||
|
winit::center_window(mw.window());
|
||||||
|
mw.invoke_restore_focus();
|
||||||
|
}
|
||||||
|
config::log(&format!("cold start -> event loop: {:?}", start.elapsed()));
|
||||||
|
});
|
||||||
|
self.main_window.run().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-291
@@ -3,301 +3,14 @@
|
|||||||
windows_subsystem = "windows"
|
windows_subsystem = "windows"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use std::cell::RefCell;
|
mod app;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use i_slint_backend_winit::WinitWindowAccessor;
|
|
||||||
use slint::Model;
|
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod launcher;
|
mod launcher;
|
||||||
|
mod mapping;
|
||||||
mod winit;
|
mod winit;
|
||||||
|
|
||||||
use config::{BrowserEntry, Config};
|
|
||||||
|
|
||||||
slint::include_modules!();
|
slint::include_modules!();
|
||||||
|
|
||||||
const DEFAULT_URL: &str = "ku6epxboctuk.github.io";
|
|
||||||
const ENV_THEME: &str = "BP_THEME";
|
|
||||||
const ENV_VIEW: &str = "BP_VIEW";
|
|
||||||
const VIEW_SETTINGS: &str = "settings";
|
|
||||||
|
|
||||||
impl From<&BrowserConfig> for BrowserEntry {
|
|
||||||
fn from(b: &BrowserConfig) -> Self {
|
|
||||||
Self {
|
|
||||||
name: b.name.to_string(),
|
|
||||||
path: b.path.to_string(),
|
|
||||||
flags: b.flags.to_string(),
|
|
||||||
icon: String::new(),
|
|
||||||
emoji: b.emoji.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_icon(rel: &str) -> slint::Image {
|
|
||||||
let from_exe = std::env::current_exe()
|
|
||||||
.ok()
|
|
||||||
.and_then(|exe| exe.parent().map(|dir| dir.join(rel)));
|
|
||||||
let path = from_exe
|
|
||||||
.filter(|p| p.exists())
|
|
||||||
.unwrap_or_else(|| std::path::PathBuf::from(rel));
|
|
||||||
slint::Image::load_from_path(&path).unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_ui_config(entry: &BrowserEntry) -> BrowserConfig {
|
|
||||||
let icon_rel = if entry.icon.is_empty() {
|
|
||||||
config::icon_for(&entry.name)
|
|
||||||
} else {
|
|
||||||
&entry.icon
|
|
||||||
};
|
|
||||||
BrowserConfig {
|
|
||||||
icon: load_icon(icon_rel),
|
|
||||||
emoji: entry.emoji.clone().into(),
|
|
||||||
icon_mask: entry.icon.is_empty()
|
|
||||||
&& entry.emoji.is_empty()
|
|
||||||
&& !config::is_known_browser(&entry.name),
|
|
||||||
name: entry.name.clone().into(),
|
|
||||||
path: entry.path.clone().into(),
|
|
||||||
flags: entry.flags.clone().into(),
|
|
||||||
is_default: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn refresh_models(
|
|
||||||
browser_model: &slint::VecModel<BrowserConfig>,
|
|
||||||
remembered_model: &slint::VecModel<RememberedEntry>,
|
|
||||||
cfg: &Config,
|
|
||||||
) {
|
|
||||||
let rows: Vec<BrowserConfig> = cfg.browsers.iter().map(to_ui_config).collect();
|
|
||||||
browser_model.set_vec(rows);
|
|
||||||
let rows: Vec<RememberedEntry> = cfg
|
|
||||||
.remembered
|
|
||||||
.iter()
|
|
||||||
.map(|(d, b)| RememberedEntry {
|
|
||||||
domain: d.clone().into(),
|
|
||||||
browser: b.clone().into(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
remembered_model.set_vec(rows);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn launch_index(
|
|
||||||
main_window: &MainWindow,
|
|
||||||
browser_model: &slint::VecModel<BrowserConfig>,
|
|
||||||
cfg: &Rc<RefCell<Config>>,
|
|
||||||
index: usize,
|
|
||||||
) {
|
|
||||||
let Some(ui_entry) = browser_model.row_data(index) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
main_window.set_selected_index(index as i32);
|
|
||||||
|
|
||||||
let entry = BrowserEntry::from(&ui_entry);
|
|
||||||
let url = main_window.get_current_url().to_string();
|
|
||||||
let remember = main_window.get_remember_choice();
|
|
||||||
|
|
||||||
if launcher::launch_and_remember(cfg, &entry, &url, remember) {
|
|
||||||
let _ = main_window.window().hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn open_config_in_editor() {
|
|
||||||
let path = config::config_path();
|
|
||||||
if !path.exists() {
|
|
||||||
let mut c = config::load();
|
|
||||||
config::ensure_browsers(&mut c);
|
|
||||||
}
|
|
||||||
let editor = std::process::Command::new("subl")
|
|
||||||
.arg(&path)
|
|
||||||
.spawn()
|
|
||||||
.or_else(|_| std::process::Command::new("notepad").arg(&path).spawn());
|
|
||||||
if let Err(e) = editor {
|
|
||||||
config::log(&format!("cannot open editor: {e}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(cfg: Config, url: Option<String>) -> (MainWindow, SettingsWindow) {
|
|
||||||
let cfg = Rc::new(RefCell::new(cfg));
|
|
||||||
let browser_model = Rc::new(slint::VecModel::<BrowserConfig>::from(vec![]));
|
|
||||||
let remembered_model = Rc::new(slint::VecModel::<RememberedEntry>::from(vec![]));
|
|
||||||
|
|
||||||
let main_window = MainWindow::new().unwrap();
|
|
||||||
let settings_window = SettingsWindow::new().unwrap();
|
|
||||||
|
|
||||||
if std::env::var(ENV_THEME).as_deref() == Ok("light") {
|
|
||||||
main_window.global::<Theme>().set_is_dark(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh_models(&browser_model, &remembered_model, &cfg.borrow());
|
|
||||||
|
|
||||||
settings_window.set_browsers(browser_model.clone().into());
|
|
||||||
settings_window.set_remembered(remembered_model.clone().into());
|
|
||||||
main_window.set_browser_model(browser_model.clone().into());
|
|
||||||
|
|
||||||
main_window.set_current_url(
|
|
||||||
url.clone()
|
|
||||||
.unwrap_or_else(|| DEFAULT_URL.to_string())
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
if let Some(u) = &url {
|
|
||||||
main_window.set_current_domain(config::domain_of(u).into());
|
|
||||||
}
|
|
||||||
main_window.set_remember_choice(cfg.borrow().settings.remember_choice);
|
|
||||||
main_window.set_always_ask(cfg.borrow().settings.always_ask);
|
|
||||||
|
|
||||||
main_window.on_launch_browser({
|
|
||||||
let main_window = main_window.clone_strong();
|
|
||||||
let browser_model = browser_model.clone();
|
|
||||||
let cfg = cfg.clone();
|
|
||||||
move |index: i32| {
|
|
||||||
launch_index(&main_window, &browser_model, &cfg, index.max(0) as usize);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
main_window.on_open_clicked({
|
|
||||||
let main_window = main_window.clone_strong();
|
|
||||||
let browser_model = browser_model.clone();
|
|
||||||
let cfg = cfg.clone();
|
|
||||||
move || {
|
|
||||||
let index = main_window.get_selected_index().max(0) as usize;
|
|
||||||
launch_index(&main_window, &browser_model, &cfg, index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
main_window.on_toggle_remember({
|
|
||||||
let cfg = cfg.clone();
|
|
||||||
move |checked| {
|
|
||||||
cfg.borrow_mut().settings.remember_choice = checked;
|
|
||||||
config::save(&cfg.borrow());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
main_window.on_toggle_always_ask({
|
|
||||||
let cfg = cfg.clone();
|
|
||||||
move |checked| {
|
|
||||||
cfg.borrow_mut().settings.always_ask = checked;
|
|
||||||
config::save(&cfg.borrow());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
main_window.on_settings_clicked(move || {
|
|
||||||
open_config_in_editor();
|
|
||||||
});
|
|
||||||
|
|
||||||
main_window.on_request_drag({
|
|
||||||
let main_window = main_window.clone_strong();
|
|
||||||
move || {
|
|
||||||
main_window.window().with_winit_window(|winit_window| {
|
|
||||||
winit_window.drag_window().ok();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
main_window.on_esc_pressed({
|
|
||||||
let main_window = main_window.clone_strong();
|
|
||||||
move || {
|
|
||||||
let _ = main_window.window().hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
settings_window.on_remove_browser({
|
|
||||||
let browser_model = browser_model.clone();
|
|
||||||
let remembered_model = remembered_model.clone();
|
|
||||||
let cfg = cfg.clone();
|
|
||||||
move |name| {
|
|
||||||
let name = name.to_string();
|
|
||||||
cfg.borrow_mut().browsers.retain(|b| b.name != name);
|
|
||||||
config::save(&cfg.borrow());
|
|
||||||
refresh_models(&browser_model, &remembered_model, &cfg.borrow());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
settings_window.on_remove_remembered({
|
|
||||||
let browser_model = browser_model.clone();
|
|
||||||
let remembered_model = remembered_model.clone();
|
|
||||||
let cfg = cfg.clone();
|
|
||||||
move |domain| {
|
|
||||||
let domain = domain.to_string();
|
|
||||||
cfg.borrow_mut().remembered.remove(&domain);
|
|
||||||
config::save(&cfg.borrow());
|
|
||||||
refresh_models(&browser_model, &remembered_model, &cfg.borrow());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
settings_window.on_redetect({
|
|
||||||
let browser_model = browser_model.clone();
|
|
||||||
let remembered_model = remembered_model.clone();
|
|
||||||
let cfg = cfg.clone();
|
|
||||||
move || {
|
|
||||||
let detected = config::detect_browsers();
|
|
||||||
let mut cfg_ref = cfg.borrow_mut();
|
|
||||||
for entry in detected {
|
|
||||||
if !cfg_ref
|
|
||||||
.browsers
|
|
||||||
.iter()
|
|
||||||
.any(|b| b.path.eq_ignore_ascii_case(&entry.path))
|
|
||||||
{
|
|
||||||
cfg_ref.browsers.push(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config::save(&cfg_ref);
|
|
||||||
drop(cfg_ref);
|
|
||||||
refresh_models(&browser_model, &remembered_model, &cfg.borrow());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
settings_window.on_esc_pressed({
|
|
||||||
let settings_window = settings_window.clone_strong();
|
|
||||||
let main_window = main_window.clone_strong();
|
|
||||||
move || {
|
|
||||||
let _ = settings_window.window().hide();
|
|
||||||
main_window.invoke_restore_focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
settings_window.window().on_close_requested({
|
|
||||||
let main_window = main_window.clone_strong();
|
|
||||||
move || {
|
|
||||||
main_window.invoke_restore_focus();
|
|
||||||
slint::CloseRequestResponse::HideWindow
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
(main_window, settings_window)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn show_and_run(
|
|
||||||
main_window: MainWindow,
|
|
||||||
settings_window: SettingsWindow,
|
|
||||||
start: std::time::Instant,
|
|
||||||
) {
|
|
||||||
let settings_only = std::env::var(ENV_VIEW).as_deref() == Ok(VIEW_SETTINGS);
|
|
||||||
|
|
||||||
if settings_only {
|
|
||||||
settings_window.show().unwrap();
|
|
||||||
let weak = settings_window.as_weak();
|
|
||||||
let _ = slint::invoke_from_event_loop(move || {
|
|
||||||
if let Some(sw) = weak.upgrade() {
|
|
||||||
winit::center_window(sw.window());
|
|
||||||
sw.invoke_restore_focus();
|
|
||||||
}
|
|
||||||
config::log(&format!("cold start -> event loop: {:?}", start.elapsed()));
|
|
||||||
});
|
|
||||||
settings_window.run().unwrap();
|
|
||||||
} else {
|
|
||||||
main_window.show().unwrap();
|
|
||||||
let weak = main_window.as_weak();
|
|
||||||
let _ = slint::invoke_from_event_loop(move || {
|
|
||||||
if let Some(mw) = weak.upgrade() {
|
|
||||||
winit::center_window(mw.window());
|
|
||||||
mw.invoke_restore_focus();
|
|
||||||
}
|
|
||||||
config::log(&format!("cold start -> event loop: {:?}", start.elapsed()));
|
|
||||||
});
|
|
||||||
main_window.run().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
@@ -321,6 +34,7 @@ pub fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (main_window, settings_window) = init(cfg, url);
|
let app = app::App::new(cfg, url);
|
||||||
show_and_run(main_window, settings_window, start);
|
app.wire();
|
||||||
|
app.run(start);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
use crate::BrowserConfig;
|
||||||
|
use crate::config::{self, BrowserEntry};
|
||||||
|
|
||||||
|
impl From<&BrowserConfig> for BrowserEntry {
|
||||||
|
fn from(b: &BrowserConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
name: b.name.to_string(),
|
||||||
|
path: b.path.to_string(),
|
||||||
|
flags: b.flags.to_string(),
|
||||||
|
icon: String::new(),
|
||||||
|
emoji: b.emoji.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_icon(rel: &str) -> slint::Image {
|
||||||
|
let from_exe = std::env::current_exe()
|
||||||
|
.ok()
|
||||||
|
.and_then(|exe| exe.parent().map(|dir| dir.join(rel)));
|
||||||
|
let path = from_exe
|
||||||
|
.filter(|p| p.exists())
|
||||||
|
.unwrap_or_else(|| std::path::PathBuf::from(rel));
|
||||||
|
slint::Image::load_from_path(&path).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_ui_config(entry: &BrowserEntry) -> BrowserConfig {
|
||||||
|
let icon_rel = if entry.icon.is_empty() {
|
||||||
|
config::icon_for(&entry.name)
|
||||||
|
} else {
|
||||||
|
&entry.icon
|
||||||
|
};
|
||||||
|
BrowserConfig {
|
||||||
|
icon: load_icon(icon_rel),
|
||||||
|
emoji: entry.emoji.clone().into(),
|
||||||
|
icon_mask: entry.icon.is_empty()
|
||||||
|
&& entry.emoji.is_empty()
|
||||||
|
&& !config::is_known_browser(&entry.name),
|
||||||
|
name: entry.name.clone().into(),
|
||||||
|
path: entry.path.clone().into(),
|
||||||
|
flags: entry.flags.clone().into(),
|
||||||
|
is_default: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user