use crate::{
LibraryModel, MainWindow, PerformanceFilterRow,
bridge::{library::source::LibrarySource, render_thread::RenderContext},
gui::{
library::{
detail::{export_diagram_file_via_source, load_diagram_detail_via_source},
search::{refresh_diagram_list, refresh_diagram_list_via_source},
},
show_toast,
},
};
use indicatrix_vault::{db::sqlite::Database, model::filter::AttributeRanges};
use slint::{ComponentHandle, Model, ModelRc, SharedString, VecModel};
use std::sync::{Arc, Mutex};
use tracing::info;
pub(in crate::gui) fn load_filter_options_and_initial_list(
ui: &MainWindow,
db: &Arc<Mutex<Database>>,
) {
{
let db_guard = db.lock().unwrap();
let mut shape_opts = vec!["All Shapes".to_string()];
if let Ok(shapes) = db_guard.get_unique_shapes() {
shape_opts.extend(shapes);
}
let shape_model: Vec<SharedString> = shape_opts
.into_iter()
.map(std::convert::Into::into)
.collect();
ui.global::<LibraryModel>()
.set_shape_options(ModelRc::new(VecModel::from(shape_model)));
let mut gear_opts = vec!["All Gears".to_string()];
if let Ok(gears) = db_guard.get_unique_gears() {
gear_opts.extend(gears);
}
drop(db_guard);
let gear_model: Vec<SharedString> = gear_opts
.into_iter()
.map(std::convert::Into::into)
.collect();
ui.global::<LibraryModel>()
.set_gear_options(ModelRc::new(VecModel::from(gear_model)));
}
sync_range_bounds_to_ui(ui, db);
refresh_diagram_list(ui, db, "", "All Shapes", "All Gears");
let total_count = db.lock().unwrap().get_total_count().unwrap_or(0);
ui.global::<LibraryModel>()
.set_status_message(format!("Database loaded: {total_count} diagrams available.").into());
}
pub fn sync_range_bounds_to_ui(ui: &MainWindow, db: &Arc<Mutex<Database>>) {
let Some(ranges) = fetch_attribute_ranges(db) else {
return;
};
apply_attribute_ranges_to_ui(ui, &ranges);
}
pub fn fetch_attribute_ranges(db: &Arc<Mutex<Database>>) -> Option<AttributeRanges> {
db.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.get_attribute_ranges()
.ok()
}
pub fn apply_attribute_ranges_to_ui(ui: &MainWindow, ranges: &AttributeRanges) {
ui.global::<LibraryModel>()
.set_ri_bounds_min(ranges.ri.0 as f32);
ui.global::<LibraryModel>()
.set_ri_bounds_max(ranges.ri.1 as f32);
ui.global::<LibraryModel>()
.set_ri_filter_min(ranges.ri.0 as f32);
ui.global::<LibraryModel>()
.set_ri_filter_max(ranges.ri.1 as f32);
ui.global::<LibraryModel>()
.set_lw_bounds_min(ranges.lw_ratio.0 as f32);
ui.global::<LibraryModel>()
.set_lw_bounds_max(ranges.lw_ratio.1 as f32);
ui.global::<LibraryModel>()
.set_lw_filter_min(ranges.lw_ratio.0 as f32);
ui.global::<LibraryModel>()
.set_lw_filter_max(ranges.lw_ratio.1 as f32);
ui.global::<LibraryModel>()
.set_volume_bounds_min(ranges.volume.0 as f32);
ui.global::<LibraryModel>()
.set_volume_bounds_max(ranges.volume.1 as f32);
ui.global::<LibraryModel>()
.set_volume_filter_min(ranges.volume.0 as f32);
ui.global::<LibraryModel>()
.set_volume_filter_max(ranges.volume.1 as f32);
ui.global::<LibraryModel>()
.set_facets_bounds_min(ranges.facets.0 as f32);
ui.global::<LibraryModel>()
.set_facets_bounds_max(ranges.facets.1 as f32);
ui.global::<LibraryModel>()
.set_facets_filter_min(ranges.facets.0 as f32);
ui.global::<LibraryModel>()
.set_facets_filter_max(ranges.facets.1 as f32);
}
pub(in crate::gui) fn setup_search_and_filter_callbacks(
ui: &MainWindow,
db: &Arc<Mutex<Database>>,
source: &Arc<Mutex<LibrarySource>>,
) {
let db_search = Arc::clone(db);
let source_search = Arc::clone(source);
let ui_weak_search = ui.as_weak();
ui.global::<LibraryModel>()
.on_search_changed(move |text: SharedString| {
if let Some(ui) = ui_weak_search.upgrade() {
let shape_idx = ui.global::<LibraryModel>().get_selected_shape_index() as usize;
let shape = ui
.global::<LibraryModel>()
.get_shape_options()
.row_data(shape_idx)
.unwrap_or_default();
let gear_idx = ui.global::<LibraryModel>().get_selected_gear_index() as usize;
let gear = ui
.global::<LibraryModel>()
.get_gear_options()
.row_data(gear_idx)
.unwrap_or_default();
refresh_diagram_list_via_source(
&ui,
&db_search,
&source_search,
&text,
&shape,
&gear,
);
}
});
let db_shape = Arc::clone(db);
let source_shape = Arc::clone(source);
let ui_weak_shape = ui.as_weak();
ui.global::<LibraryModel>()
.on_filter_shape_changed(move |shape: SharedString| {
if let Some(ui) = ui_weak_shape.upgrade() {
let search = ui.global::<LibraryModel>().get_search_text();
let gear_idx = ui.global::<LibraryModel>().get_selected_gear_index() as usize;
let gear = ui
.global::<LibraryModel>()
.get_gear_options()
.row_data(gear_idx)
.unwrap_or_default();
refresh_diagram_list_via_source(
&ui,
&db_shape,
&source_shape,
&search,
&shape,
&gear,
);
}
});
let db_gear = Arc::clone(db);
let source_gear = Arc::clone(source);
let ui_weak_gear = ui.as_weak();
ui.global::<LibraryModel>()
.on_filter_gear_changed(move |gear: SharedString| {
if let Some(ui) = ui_weak_gear.upgrade() {
let search = ui.global::<LibraryModel>().get_search_text();
let shape_idx = ui.global::<LibraryModel>().get_selected_shape_index() as usize;
let shape = ui
.global::<LibraryModel>()
.get_shape_options()
.row_data(shape_idx)
.unwrap_or_default();
refresh_diagram_list_via_source(
&ui,
&db_gear,
&source_gear,
&search,
&shape,
&gear,
);
}
});
let db_range = Arc::clone(db);
let source_range = Arc::clone(source);
let ui_weak_range = ui.as_weak();
ui.global::<LibraryModel>().on_filters_changed(move || {
if let Some(ui) = ui_weak_range.upgrade() {
let search = ui.global::<LibraryModel>().get_search_text();
let shape_idx = ui.global::<LibraryModel>().get_selected_shape_index() as usize;
let shape = ui
.global::<LibraryModel>()
.get_shape_options()
.row_data(shape_idx)
.unwrap_or_default();
let gear_idx = ui.global::<LibraryModel>().get_selected_gear_index() as usize;
let gear = ui
.global::<LibraryModel>()
.get_gear_options()
.row_data(gear_idx)
.unwrap_or_default();
refresh_diagram_list_via_source(&ui, &db_range, &source_range, &search, &shape, &gear);
}
});
}
pub(in crate::gui) fn setup_performance_filter_callbacks(ui: &MainWindow) {
let ui_weak_add = ui.as_weak();
ui.global::<LibraryModel>()
.on_performance_filter_add(move |row: PerformanceFilterRow| {
if let Some(ui) = ui_weak_add.upgrade() {
let mut rows: Vec<PerformanceFilterRow> = ui
.global::<LibraryModel>()
.get_performance_filters()
.iter()
.collect();
rows.push(row);
ui.global::<LibraryModel>()
.set_performance_filters(ModelRc::new(VecModel::from(rows)));
}
});
let ui_weak_remove = ui.as_weak();
ui.global::<LibraryModel>()
.on_performance_filter_remove(move |idx: i32| {
if let Some(ui) = ui_weak_remove.upgrade() {
let mut rows: Vec<PerformanceFilterRow> = ui
.global::<LibraryModel>()
.get_performance_filters()
.iter()
.collect();
if let Ok(idx) = usize::try_from(idx)
&& idx < rows.len()
{
rows.remove(idx);
}
ui.global::<LibraryModel>()
.set_performance_filters(ModelRc::new(VecModel::from(rows)));
}
});
let ui_weak_clear = ui.as_weak();
ui.global::<LibraryModel>()
.on_performance_filters_clear(move || {
if let Some(ui) = ui_weak_clear.upgrade() {
ui.global::<LibraryModel>()
.set_performance_filters(ModelRc::new(VecModel::from(Vec::<
PerformanceFilterRow,
>::new(
))));
}
});
}
pub(in crate::gui) fn setup_diagram_selection_and_export_callbacks(
ui: &MainWindow,
db: &Arc<Mutex<Database>>,
source: &Arc<Mutex<LibrarySource>>,
render_ctx: &Arc<Mutex<RenderContext>>,
) {
let db_select = Arc::clone(db);
let source_select = Arc::clone(source);
let render_ctx_select = render_ctx.clone();
let ui_weak_select = ui.as_weak();
ui.global::<LibraryModel>()
.on_select_diagram(move |id: i32| {
if let Some(ui) = ui_weak_select.upgrade() {
load_diagram_detail_via_source(
&ui,
&db_select,
&source_select,
&render_ctx_select,
i64::from(id),
);
}
});
ui.global::<LibraryModel>()
.on_open_diagram_url(move |url: SharedString| {
let url_str = url.to_string();
info!("Opening URL: {}", url_str);
#[cfg(target_os = "linux")]
let _ = std::process::Command::new("xdg-open").arg(&url_str).spawn();
#[cfg(target_os = "windows")]
let _ = std::process::Command::new("cmd")
.args(["/C", "start", &url_str])
.spawn();
#[cfg(target_os = "macos")]
let _ = std::process::Command::new("open").arg(&url_str).spawn();
});
let db_export = Arc::clone(db);
let source_export = Arc::clone(source);
let ui_weak_export = ui.as_weak();
ui.global::<LibraryModel>()
.on_export_file(move |file_name: SharedString| {
if let Some(ui) = ui_weak_export.upgrade() {
let entry_id = ui.global::<LibraryModel>().get_selected_entry_id();
if entry_id >= 0 {
export_diagram_file_via_source(
&ui,
&db_export,
&source_export,
i64::from(entry_id),
&file_name,
);
} else {
show_toast(&ui, "No diagram selected for export.", "error");
}
}
});
}