use crate::{ data::{self, CoordType, Radar2d}, plugin_system::init_plugin, render::{predefined::color_mapper::BoundaryNorm, Layer}, PLUGIN_MANAGER, }; use super::{ control_panel::{ControlPanelInputMsg, ControlPanelModel}, messages::MonitorInputMsg, monitor::MonitorModel, setting::SettingModel, TimelineMsg, }; use abi_stable::std_types::RStr; use chrono::{DateTime, Duration, Utc}; use gtk::{ prelude::{ApplicationExt, BoxExt, GtkWindowExt, WidgetExt}, traits::OrientableExt, }; use ndarray::{Array1, Array2, Array3}; use radarg_plugin_interface::{Block, DataShape, PluginId, VecResult}; use relm4::actions::{AccelsPlus, RelmAction, RelmActionGroup}; use relm4::*; use relm4::{gtk, ComponentParts, ComponentSender, RelmWidgetExt, SimpleComponent}; use relm4_components::open_dialog::{ OpenDialog, OpenDialogMsg, OpenDialogResponse, OpenDialogSettings, }; relm4::new_action_group!(FileActionGroup, "file"); relm4::new_stateless_action!(OpenAction, FileActionGroup, "open"); #[derive(Debug)] pub enum AppMsg { CloseRequest, Close, OpenDialog, OpenDialogMulti, OpenFile((DateTime, Layer)), } pub struct AppModel { open_dialog: Controller, control: Controller, render: Controller, setting: Controller, } #[relm4::component(pub)] impl SimpleComponent for AppModel { type Init = (); type Input = AppMsg; type Output = (); view! { #[root] main_window=adw::ApplicationWindow { set_default_width: 1200, set_default_height: 900, set_focus_on_click:true, connect_close_request[sender,app] => move |_| { sender.input(AppMsg::CloseRequest); app.quit(); gtk::Inhibit(true) }, gtk::Box{ set_orientation: gtk::Orientation::Vertical, set_valign:gtk::Align::Fill, set_spacing:2, gtk::HeaderBar{ pack_start=&adw::ViewSwitcher{ set_stack: Some(&view_stack), } }, #[name="view_stack"] adw::ViewStack{ set_hexpand:true, set_vexpand:true, }, }, connect_close_request[sender] => move |_| { sender.input(AppMsg::CloseRequest); gtk::Inhibit(true) } }, popover_child = gtk::Spinner { set_spinning: true, }, } menu! { main_menu: { "File" { "Open" => OpenAction, "Open Folder" => OpenAction, }, "Edit" { "New Layer" => OpenAction, "Undo" => OpenAction, "Redo" => OpenAction, }, "Plugins" { "Plugin1" => OpenAction, "Plugin2" => OpenAction, }, } } fn init( params: Self::Init, root: &Self::Root, sender: ComponentSender, ) -> ComponentParts { let control = ControlPanelModel::builder() .launch(0) .forward(sender.input_sender(), |msg| AppMsg::Close); let render = MonitorModel::builder() .launch(()) .forward(sender.input_sender(), |a| AppMsg::Close); let setting = SettingModel::builder() .launch(()) .forward(sender.input_sender(), |a| AppMsg::Close); let dialog = OpenDialog::builder() .transient_for_native(&root) .launch(OpenDialogSettings::default()) .forward(sender.input_sender(), |response| match response { OpenDialogResponse::Accept(path) => { let plugin = PLUGIN_MANAGER.get_plugin_by_name("etws_loader").unwrap(); let mut result = plugin.load(RStr::from_str(path.to_str().unwrap())).unwrap(); let mut block = result.blocks.pop().unwrap(); data_to_grid_layer(block) } OpenDialogResponse::Cancel => AppMsg::Close, }); let app = relm4::main_application(); relm4_icons::initialize_icons(); let model = AppModel { open_dialog: dialog, control, render, setting, }; let widgets = view_output!(); let mut group = RelmActionGroup::::new(); let page_home = gtk::Box::builder() .hexpand(true) .vexpand(true) .orientation(gtk::Orientation::Vertical) .build(); let setting_bar = gtk::Box::builder() .orientation(gtk::Orientation::Horizontal) .margin_top(2) .margin_start(10) .margin_end(10) .build(); let popover_menu_bar = gtk::PopoverMenuBar::from_model(Some(&main_menu)); popover_menu_bar.set_hexpand(true); setting_bar.append(&popover_menu_bar); page_home.append(&setting_bar); page_home.append(model.control.widget()); page_home.append(model.render.widget()); let view_stack = widgets.view_stack.clone(); let page_home = view_stack.add_titled(&page_home, Some("renderer"), "Render"); page_home.set_icon_name(Some("home-filled")); let page_setting = model.setting.widget(); let page_setting = view_stack.add_titled(page_setting, Some("setting"), "Setting"); page_setting.set_icon_name(Some("settings-filled")); app.set_accelerators_for_action::(&["O"]); let action: RelmAction = { RelmAction::new_stateless(move |_| { sender.input(AppMsg::OpenDialog); }) }; group.add_action(action); group.register_for_widget(&widgets.main_window); ComponentParts { model, widgets } } fn update(&mut self, msg: Self::Input, _sender: ComponentSender) { match msg { AppMsg::OpenFile((time, layer)) => { self.render.sender().emit(MonitorInputMsg::AddLayer(layer)); self.control .sender() .emit(ControlPanelInputMsg::Selection(Some(time))); self.control .sender() .emit(ControlPanelInputMsg::TimeLine(TimelineMsg::SetStart( time - Duration::minutes(30), ))); } AppMsg::CloseRequest => {} AppMsg::Close => {} AppMsg::OpenDialog => { self.open_dialog.emit(OpenDialogMsg::Open); } AppMsg::OpenDialogMulti => {} } } } macro_rules! match_in_macro { ($block:ident,$name:literal, $(($branch:path, $t:ty, $color:expr)),+) => { { match $block.data_type { $( $branch => { let data: $t = $block.into(); let layer = Layer::grid_render_layer(data, format!($name), $color); AppMsg::OpenFile((Utc::now() ,layer)) }, )+ _ => AppMsg::Close, } } }; } fn data_to_grid_layer(block: Block) -> AppMsg { use crate::utils::*; use radarg_plugin_interface::PluginResultType; match block.shape { DataShape::Matrix => match_in_macro!( block, "DBZ", ( PluginResultType::DBZ, Radar2d, create_dbz_boundarynorm() ), (PluginResultType::R, Radar2d, create_dbz_boundarynorm()), (PluginResultType::V, Radar2d, create_vel_boundarynorm()), ( PluginResultType::ZDR, Radar2d, create_zdr_boundarynorm() ), ( PluginResultType::PHIDP, Radar2d, create_phidp_boundarynorm() ), ( PluginResultType::KDP, Radar2d, create_kdp_boundarynorm() ), (PluginResultType::CC, Radar2d, create_cc_boundarynorm()), ( PluginResultType::HCA, Radar2d, create_cpc_boundarynorm() ), ( PluginResultType::QPE, Radar2d, create_vil_boundarynorm() ), ( PluginResultType::QPF, Radar2d, create_vil_boundarynorm() ), ( PluginResultType::VIL, Radar2d, create_vil_boundarynorm() ), ( PluginResultType::OHP, Radar2d, create_vil_boundarynorm() ), ( PluginResultType::THP, Radar2d, create_vil_boundarynorm() ), (PluginResultType::ET, Radar2d, create_et_boundarynorm()), ( PluginResultType::EB, Radar2d, create_hgt_boundarynorm() ) ), _ => AppMsg::Close, } }