use super::{
fetch::{
DisplaySearchOptions, apply_diagram_list_to_ui, fetch_diagram_list_with_options,
fetch_diagram_list_with_own_connection,
},
filters::{
read_id_filter, read_local_only, read_range_filter, read_sort_order, read_tag_filter,
},
};
use crate::{LibraryModel, MainWindow};
use indicatrix_vault::{
db::sqlite::{Database, SortOrder},
model::filter::RangeFilter,
};
use slint::ComponentHandle;
use std::{
cell::{Cell, RefCell},
sync::{Arc, Mutex},
thread,
time::Duration,
};
use tracing::error;
const SEARCH_DEBOUNCE_DELAY: Duration = Duration::from_millis(120);
const TEXT_FILTER_DEBOUNCE: Duration = Duration::from_millis(120);
thread_local! {
static SEARCH_SEQ: Cell<u64> = const { Cell::new(0) };
static SEARCH_DEBOUNCE: RefCell<Option<slint::Timer>> = const { RefCell::new(None) };
static TEXT_DEBOUNCE: RefCell<Option<slint::Timer>> = const { RefCell::new(None) };
}
pub(super) fn debounce_text_refresh(refresh: impl FnOnce() + 'static) {
let mut refresh = Some(refresh);
let timer = slint::Timer::default();
timer.start(
slint::TimerMode::SingleShot,
TEXT_FILTER_DEBOUNCE,
move || {
if let Some(refresh) = refresh.take() {
refresh();
}
},
);
TEXT_DEBOUNCE.with(|cell| *cell.borrow_mut() = Some(timer));
}
pub(in crate::gui) fn bump_search_seq() -> u64 {
SEARCH_SEQ.with(|cell| {
let next = cell.get() + 1;
cell.set(next);
next
})
}
pub(in crate::gui) fn is_current_search(seq: u64) -> bool {
SEARCH_SEQ.with(|cell| cell.get() == seq)
}
#[derive(Clone)]
struct BackgroundSearchOptions {
order: SortOrder,
local_only: bool,
tag_filter: Option<String>,
id_filter: Option<Vec<i64>>,
}
#[derive(Clone)]
struct BackgroundSearchRequest {
seq: u64,
search: String,
shape_filter: String,
gear_filter: String,
range: RangeFilter,
options: BackgroundSearchOptions,
}
fn dispatch_background_search(
ui: &MainWindow,
db_mutex: &Arc<Mutex<Database>>,
request: BackgroundSearchRequest,
) {
ui.global::<LibraryModel>()
.set_status_message("Searching...".into());
let ui_weak = ui.as_weak();
let db_mutex = Arc::clone(db_mutex);
let timer = slint::Timer::default();
timer.start(
slint::TimerMode::SingleShot,
SEARCH_DEBOUNCE_DELAY,
move || {
let db_mutex = Arc::clone(&db_mutex);
let ui_weak = ui_weak.clone();
let request = request.clone();
thread::spawn(move || {
let BackgroundSearchRequest {
seq,
search,
shape_filter,
gear_filter,
range,
options,
} = request;
let result = fetch_diagram_list_with_own_connection(
&db_mutex,
&search,
&shape_filter,
&gear_filter,
&range,
DisplaySearchOptions {
order: options.order,
local_only: options.local_only,
tag_filter: options.tag_filter.as_deref(),
id_filter: options.id_filter.as_deref(),
},
);
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
if !is_current_search(seq) {
return;
}
match result {
Ok(fetched) => {
apply_diagram_list_to_ui(&ui, fetched);
ui.global::<LibraryModel>()
.set_status_message(String::new().into());
}
Err(e) => {
error!("Failed to search diagrams: {:?}", e);
ui.global::<LibraryModel>().set_status_message(
format!("Error searching database: {e}").into(),
);
}
}
});
});
},
);
SEARCH_DEBOUNCE.with(|cell| *cell.borrow_mut() = Some(timer));
}
pub fn refresh_diagram_list(
ui: &MainWindow,
db_mutex: &Arc<Mutex<Database>>,
search: &str,
shape_filter: &str,
gear_filter: &str,
) {
SEARCH_DEBOUNCE.with(|cell| {
cell.borrow_mut().take();
});
TEXT_DEBOUNCE.with(|cell| {
cell.borrow_mut().take();
});
let seq = bump_search_seq();
let range = read_range_filter(ui);
let order = read_sort_order(ui);
let local_only = read_local_only(ui);
let tag_filter = read_tag_filter(ui);
let id_filter = read_id_filter(ui);
if range.performance.is_empty() {
match fetch_diagram_list_with_options(
db_mutex,
search,
shape_filter,
gear_filter,
&range,
DisplaySearchOptions {
order,
local_only,
tag_filter: tag_filter.as_deref(),
id_filter: id_filter.as_deref(),
},
) {
Ok(fetched) => {
apply_diagram_list_to_ui(ui, fetched);
if ui.global::<LibraryModel>().get_status_message() == "Searching..." {
ui.global::<LibraryModel>()
.set_status_message(String::new().into());
}
}
Err(e) => {
error!("Failed to search diagrams: {:?}", e);
ui.global::<LibraryModel>()
.set_status_message(format!("Error searching database: {e}").into());
}
}
return;
}
dispatch_background_search(
ui,
db_mutex,
BackgroundSearchRequest {
seq,
search: search.to_string(),
shape_filter: shape_filter.to_string(),
gear_filter: gear_filter.to_string(),
range,
options: BackgroundSearchOptions {
order,
local_only,
tag_filter,
id_filter,
},
},
);
}