diff --git a/data/css/main.css b/data/css/main.css index 3d0e773..bf41497 100644 --- a/data/css/main.css +++ b/data/css/main.css @@ -29,6 +29,10 @@ paned>separator { font-size: 14px; } +.tooltip { + font-size: 8px; +} + .lv { background: transparent; } diff --git a/icons.toml b/icons.toml index f0612fc..6037f66 100644 --- a/icons.toml +++ b/icons.toml @@ -15,4 +15,7 @@ icons = [ "home-filled", "settings-filled", "save-filled", + "timer-filled", + "eye-filled", + "eye-off-filled", ] diff --git a/src/components/app.rs b/src/components/app.rs index 9278ae6..1d147b2 100644 --- a/src/components/app.rs +++ b/src/components/app.rs @@ -6,7 +6,7 @@ use super::{ ControlPanelOutputMsg, TimelineMsg, }; use crate::pipeline::{GridElementImpl, OffscreenRenderer}; -use crate::widgets::AssoElement; +use crate::widgets::{AssoElement, DynamicCol}; use crate::{ actions::register_layer_actions, pipeline::element::{Element, InstantElement, InstantElementDrawerType, TimeSeriesElement}, @@ -53,6 +53,7 @@ use std::{ use tokio::{sync::oneshot, task}; use tracing::{debug, error, info, warn}; use tracing_subscriber::fmt::layer; +use crate::components::sidebar::{SideBarInputMsg, SideBarModel}; relm4::new_action_group!(FileActionGroup, "file"); relm4::new_stateless_action!(OpenAction, FileActionGroup, "open"); @@ -88,6 +89,8 @@ pub struct AppModel { #[do_not_track] render: Controller, #[do_not_track] + sidebar: Controller, + #[do_not_track] layers: Rc>>, #[do_not_track] elements: Vec>>, @@ -161,7 +164,21 @@ impl Component for AppModel { adw::ToastOverlay{ set_hexpand: true, set_vexpand: true, - model.render.widget(), + DynamicCol { + set_end_width: 300, + set_hexpand: true, + set_vexpand: true, + #[wrap(Some)] + #[name="paned"] + set_child_paned = >k::Paned{ + #[local_ref] + #[wrap(Some)] + set_start_child=render->gtk::Frame{}, + #[local_ref] + #[wrap(Some)] + set_end_child=sidebar->gtk::Box{}, + } + } }, }, home_stack_page = view_stack.add_titled(&home_page, Some("home"), "Home") -> adw::ViewStackPage{ @@ -195,13 +212,22 @@ impl Component for AppModel { root: Self::Root, sender: ComponentSender, ) -> ComponentParts { - let layers = Rc::new(RefCell::new(Vec::with_capacity(20))); + let layers = Rc::new(RefCell::new(vec![ + Layer::new(true, "Layer 1".to_string(), AssoElement::Test), + Layer::new(true, "Layer 2".to_string(), AssoElement::Test) + ])); let control = ControlPanelModel::builder().launch(layers.clone()).forward( sender.input_sender(), |msg| match msg { ControlPanelOutputMsg::OpenFile((key, time)) => AppMsg::Close, }, ); + + let sidebar = SideBarModel::builder() + .launch(layers.clone()) + .forward(sender.input_sender(), |msg| match msg { + _ => AppMsg::Close, + }); let render = MonitorModel::builder() .launch(layers.clone()) @@ -215,9 +241,12 @@ impl Component for AppModel { .forward(sender.input_sender(), |a| AppMsg::Close); let mut dispatcher = Rc::new(Dispatcher::new(5, 5, chrono::Duration::minutes(1))); let cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0)); - let dialog_dispatcher = dispatcher.clone(); - let dialog_render_sender = render.sender().clone(); - let dialog = OpenDialog::builder() + + let dialog = { + let dialog_dispatcher = dispatcher.clone(); + let dialog_sidebar_sender = sidebar.sender().clone(); + let dialog_render_sender = render.sender().clone(); + OpenDialog::builder() .transient_for_native(&root) .launch(OpenDialogSettings::default()) .forward(sender.input_sender(), move |response| match response { @@ -241,28 +270,31 @@ impl Component for AppModel { .get_instance(); let layer = Layer::new(true, "New Layer".to_string(), AssoElement::Instant(element)); - dialog_render_sender.emit(MonitorInputMsg::AddMetaItem(meta.to_map())); - dialog_render_sender.emit(MonitorInputMsg::SetRenderRange( - lon_start, lon_end, lat_start, lat_end, - )); + dialog_sidebar_sender.emit(SideBarInputMsg::AddMetaItems(meta.to_map())); + dialog_render_sender.emit(MonitorInputMsg::SetRenderRange(lon_start, lon_end, lat_start, lat_end)); AppMsg::NewLayer(layer) } _ => AppMsg::Close, - }); + })}; let buffer: Buffer = Rc::new(RefCell::new(HashMap::new())); + let model = AppModel { cms, dispatcher, waiting_for: None, elements: Vec::with_capacity(20), open_dialog: dialog, + sidebar, control, render, layers, setting, tracker: 0, }; + + let render = model.render.widget(); + let sidebar = model.sidebar.widget(); let widgets = view_output!(); let mut group = RelmActionGroup::::new(); relm4::main_application().set_accelerators_for_action::(&["O"]); @@ -288,7 +320,7 @@ impl Component for AppModel { match msg { AppMsg::NewLayer(layer) => { (*self.layers).borrow_mut().push(layer); - self.render.sender().send(MonitorInputMsg::RefreshLayerList); + self.sidebar.sender().send(SideBarInputMsg::RefreshList); } AppMsg::CloseRequest => { relm4::main_application().quit(); @@ -309,7 +341,7 @@ impl Component for AppModel { switched_layer.set_associated_element(AssoElement::TimeSeries(element.clone())); self.elements.push(element); } - self.render.sender().send(MonitorInputMsg::RefreshLayerList); + self.sidebar.sender().send(SideBarInputMsg::RefreshList); } AppMsg::OpenDialog => { self.open_dialog.emit(OpenDialogMsg::Open); diff --git a/src/components/mod.rs b/src/components/mod.rs index b210ffb..9e2036c 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -2,6 +2,7 @@ pub mod app; mod control_panel; mod monitor; mod setting; +pub mod sidebar; pub use control_panel::*; diff --git a/src/components/monitor/messages.rs b/src/components/monitor/messages.rs index 7d7c0a1..905faaa 100644 --- a/src/components/monitor/messages.rs +++ b/src/components/monitor/messages.rs @@ -10,11 +10,11 @@ pub enum MonitorInputMsg { RefreshRender, AddWidget(Box), RemoveWidget, - AddMetaItem(HashMap), - ClearMetaItems, - UpdateMetaItem(HashMap), + // AddMetaItem(HashMap), + // ClearMetaItems, + // UpdateMetaItem(HashMap), RefreshTiles, - RefreshLayerList, + // RefreshLayerList, SetRenderRange(f64, f64, f64, f64), ChangeZoom(f64), None, @@ -29,13 +29,9 @@ impl Debug for MonitorInputMsg { MonitorInputMsg::SetRenderRange(_, _, _, _) => { write!(f, "MonitorInputMsg::SetRenderRange") } - MonitorInputMsg::RefreshLayerList => write!(f, "MonitorInputMsg::RefreshLayerList"), MonitorInputMsg::None => write!(f, "MonitorInputMsg::None"), MonitorInputMsg::AddWidget(_) => write!(f, "MonitorInputMsg::AddWidget"), MonitorInputMsg::RemoveWidget => write!(f, "MonitorInputMsg::RemoveWidget"), - MonitorInputMsg::AddMetaItem(_) => write!(f, "MonitorInputMsg::RemoveWidget"), - MonitorInputMsg::ClearMetaItems => write!(f, "MonitorInputMsg::ClearMetaItems"), - MonitorInputMsg::UpdateMetaItem(_) => write!(f, "MonitorInputMsg::UpdateMetaItem"), } } } diff --git a/src/components/monitor/mod.rs b/src/components/monitor/mod.rs index ee7e130..6f38e95 100644 --- a/src/components/monitor/mod.rs +++ b/src/components/monitor/mod.rs @@ -1,4 +1,4 @@ pub mod messages; pub mod monitor; -pub mod sidebar; + pub use monitor::*; diff --git a/src/components/monitor/monitor.rs b/src/components/monitor/monitor.rs index bb39641..a1e383a 100644 --- a/src/components/monitor/monitor.rs +++ b/src/components/monitor/monitor.rs @@ -19,7 +19,7 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use tracing::*; -use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg}; +// use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg}; use crate::coords::Range; use crate::map_tile::MapTile; use crate::map_tile_utils::lat_lon_to_zoom; @@ -54,8 +54,6 @@ pub struct MonitorModel { widgets: Vec, #[no_eq] layers: Rc>>, - #[no_eq] - sidebar: Controller, } pub struct MonitorWidgets { @@ -71,71 +69,50 @@ impl Component for MonitorModel { view! { #[root] - adw::BreakpointBin { - set_hexpand: true, - set_vexpand: true, - set_height_request: 500, - set_width_request: 700, - #[wrap(Some)] - #[name="test"] - set_child = &DynamicCol{ - set_end_width: 300, - set_hexpand: true, - set_vexpand: true, - #[wrap(Some)] - #[name="paned"] - set_child_paned=>k::Paned{ + gtk::Frame{ + add_css_class: "rb", + set_margin_all: 5, + #[name="widget_layer"] + gtk::Overlay{ + #[name = "renderer"] #[wrap(Some)] - #[name="render"] - set_start_child=>k::Frame{ - add_css_class: "rb", - set_margin_all: 5, - #[name="widget_layer"] - gtk::Overlay{ - #[name = "renderer"] - #[wrap(Some)] - set_child = &Render{ - #[track = "model.changed(MonitorModel::render_cfg())"] - set_cfg: model.render_cfg, - #[track = "model.changed(MonitorModel::render_range())"] - set_view: model.render_range, - set_tiles: model.map_tile_getter.clone(), - connect_render_status_notify[sender] => move |r| { - sender.output(MonitorOutputMsg::LayerRenderFinished); - }, - connect_range_changing_notify[sender] => move |r| { - sender.input(MonitorInputMsg::RefreshTiles); - }, - connect_scale_notify[sender] => move |r| { - let scale = r.scale(); - { - let initial = r.scale_rate(); - let mut rate_start = initial_rate.lock().unwrap(); - if rate_start.is_none() { - *rate_start = Some(scale); - } - } - debouncer.call(scale); - }, - set_interior_layers: model.layers.clone(), - }, - add_overlay=>k::Button{ - set_label:"Add", - set_margin_all:10, - set_valign: gtk::Align::Start, - set_halign: gtk::Align::End, - }, - #[track = "model.changed(MonitorModel::new_layer())"] - #[iterate] - add_overlay: &model.widgets + set_child = &Render{ + #[track = "model.changed(MonitorModel::render_cfg())"] + set_cfg: model.render_cfg, + #[track = "model.changed(MonitorModel::render_range())"] + set_view: model.render_range, + set_tiles: model.map_tile_getter.clone(), + connect_render_status_notify[sender] => move |r| { + sender.output(MonitorOutputMsg::LayerRenderFinished); }, - + connect_range_changing_notify[sender] => move |r| { + sender.input(MonitorInputMsg::RefreshTiles); + }, + connect_scale_notify[sender] => move |r| { + let scale = r.scale(); + { + let initial = r.scale_rate(); + let mut rate_start = initial_rate.lock().unwrap(); + if rate_start.is_none() { + *rate_start = Some(scale); + } + } + debouncer.call(scale); + }, + set_interior_layers: model.layers.clone(), }, - #[wrap(Some)] - set_end_child=model.sidebar.widget(), - } + add_overlay=>k::Button{ + set_label:"Add", + set_margin_all:10, + set_valign: gtk::Align::Start, + set_halign: gtk::Align::End, + }, + #[track = "model.changed(MonitorModel::new_layer())"] + #[iterate] + add_overlay: &model.widgets + }, + } - } } fn update_with_view( @@ -150,13 +127,6 @@ impl Component for MonitorModel { MonitorInputMsg::RefreshRender => { widgets.renderer.queue_render(); } - MonitorInputMsg::RefreshLayerList => { - self.sidebar.sender().send(SideBarInputMsg::RefreshList); - sender.input(MonitorInputMsg::RefreshRender); - } - MonitorInputMsg::AddMetaItem(map) => { - self.sidebar.emit(SideBarInputMsg::AddMetaItems(map)) - } MonitorInputMsg::SetRenderRange(lon_start, lon_end, lat_start, lat_end) => { let current_rate = widgets.renderer.scale_rate(); self.set_render_range((lat_start, lat_end, lon_start, lon_end)); @@ -164,11 +134,6 @@ impl Component for MonitorModel { let zoom: f64 = (new_rate / current_rate).log2(); sender.input(MonitorInputMsg::ChangeZoom(zoom)); } - MonitorInputMsg::ClearMetaItems => self.sidebar.emit(SideBarInputMsg::ClearMetaItems), - MonitorInputMsg::UpdateMetaItem(map) => { - self.sidebar.emit(SideBarInputMsg::ClearMetaItems); - self.sidebar.emit(SideBarInputMsg::AddMetaItems(map)) - } MonitorInputMsg::RefreshTiles => { let ((x1, x2), (y1, y2)) = widgets.renderer.render_range(); self.load_tile(&sender, ((y1 as f32, y2 as f32), (x1 as f32, x2 as f32))); @@ -203,16 +168,16 @@ impl Component for MonitorModel { root: Self::Root, sender: ComponentSender, ) -> ComponentParts { - let sidebar_sender = sender.clone(); - let sidebar: Controller = SideBarModel::builder() - .launch(init.clone()) - .forward(sender.input_sender(), move |msg| match msg { - SideBarOutputMsg::SwitchToTimeSeries(layer) => { - sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer)); - MonitorInputMsg::None - } - _ => MonitorInputMsg::None, - }); + // let sidebar_sender = sender.clone(); + // let sidebar: Controller = SideBarModel::builder() + // .launch(init.clone()) + // .forward(sender.input_sender(), move |msg| match msg { + // SideBarOutputMsg::SwitchToTimeSeries(layer) => { + // sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer)); + // MonitorInputMsg::None + // } + // _ => MonitorInputMsg::None, + // }); let render_cfg = RenderConfig { padding: [0.0, 0.0, 0.0, 0.0], @@ -231,7 +196,6 @@ impl Component for MonitorModel { layers: init, zoom: 4, map_tile_getter: Rc::new(MapTile::default()), - sidebar, tracker: 0, }; diff --git a/src/components/monitor/sidebar/bottom_bar.rs b/src/components/sidebar/bottom_bar.rs similarity index 100% rename from src/components/monitor/sidebar/bottom_bar.rs rename to src/components/sidebar/bottom_bar.rs diff --git a/src/components/monitor/sidebar/meta_data_list.rs b/src/components/sidebar/meta_data_list.rs similarity index 90% rename from src/components/monitor/sidebar/meta_data_list.rs rename to src/components/sidebar/meta_data_list.rs index 8fb2ae1..c6df6c3 100644 --- a/src/components/monitor/sidebar/meta_data_list.rs +++ b/src/components/sidebar/meta_data_list.rs @@ -9,7 +9,7 @@ use relm4::{ }; #[derive(Debug, PartialEq, Eq)] -pub(super) struct MyListItem { +pub(in crate::components) struct MyListItem { tag: String, info: String, } @@ -20,7 +20,7 @@ impl MyListItem { } } -pub(super) struct TagColumn; +pub(in crate::components) struct TagColumn; impl LabelColumn for TagColumn { type Item = MyListItem; @@ -38,7 +38,7 @@ impl LabelColumn for TagColumn { } } -pub(super) struct InfoColumn; +pub(in crate::components) struct InfoColumn; impl RelmColumn for InfoColumn { type Root = gtk::Label; diff --git a/src/components/monitor/sidebar/mod.rs b/src/components/sidebar/mod.rs similarity index 100% rename from src/components/monitor/sidebar/mod.rs rename to src/components/sidebar/mod.rs diff --git a/src/components/monitor/sidebar/sidebar.rs b/src/components/sidebar/sidebar.rs similarity index 84% rename from src/components/monitor/sidebar/sidebar.rs rename to src/components/sidebar/sidebar.rs index 6105384..c990132 100644 --- a/src/components/monitor/sidebar/sidebar.rs +++ b/src/components/sidebar/sidebar.rs @@ -281,11 +281,13 @@ impl LayerItem { } struct Widgets { - label: gtk::Label, + label: gtk::EditableLabel, screen_shot: gtk::Image, - status: gtk::Label, - button: gtk::CheckButton, - menu: gtk::PopoverMenu, + status_icon: gtk::Image, + status_label: gtk::Label, + visible: gtk::Button, + opacity: gtk::Label, + menu: gtk::Popover, } impl RelmListItem for LayerItem { @@ -308,6 +310,7 @@ impl RelmListItem for LayerItem { relm4::view! { my_box = gtk::Box { + set_valign: gtk::Align::Center, gtk::Frame{ set_margin_end: 10, #[name = "screen_shot"] @@ -315,31 +318,46 @@ impl RelmListItem for LayerItem { set_size_request: (65, 40), } }, - #[name = "label"] - gtk::Label{ - set_halign: gtk::Align::Start, - }, - #[name = "status"] - gtk::Label{ - set_halign: gtk::Align::Start - }, - gtk::Label{ - set_hexpand: true, - }, - #[name = "button"] - gtk::CheckButton{ - set_halign: gtk::Align::End, + gtk::Grid{ + set_row_homogeneous: true, + attach[0,0,1,1] = >k::Box{ + #[name="status_icon"] + gtk::Image{ + inline_css: "-gtk-icon-transform: scale(0.8);", + }, + #[name="status_label"] + gtk::Label{ + add_css_class:"tooltip", + set_halign: gtk::Align::Start, + } + }, + #[name="label"] + attach[0,1,1,1] = >k::EditableLabel{ + set_hexpand: true, + }, + #[name="opacity"] + attach[0,2,1,1] = >k::Label{ + add_css_class:"tooltip", + set_halign: gtk::Align::Start + }, }, + #[name="visible"] + gtk::Button{ + set_vexpand: false, + set_hexpand: false, + }, #[name = "menu"] - gtk::PopoverMenu::from_model(Some(&main_menu)){} + gtk::Popover{} } } let widgets = Widgets { screen_shot, label, - status, - button, + status_icon, + status_label, + opacity, + visible, menu, }; @@ -349,18 +367,28 @@ impl RelmListItem for LayerItem { fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) { let Widgets { label, - button, + visible, screen_shot, - status, + status_label, + status_icon, + opacity, menu, } = widgets; - status.set_label(&match self.status { + status_label.set_label(&match self.status { LayerStatus::BindToTime(t) => format!("Bind To Time: {}", t), LayerStatus::Instance => "Instance".to_string(), LayerStatus::BindToOtherLayer(idx) => format!("Bind To Layer: {}", idx), }); + status_icon.set_icon_name(Some(match self.status{ + LayerStatus::BindToTime(_) => "timer-filled", + LayerStatus::Instance => "timer-filled", + LayerStatus::BindToOtherLayer(_) => "timer-filled", + })); + + opacity.set_markup(&format!("Opacity: 0.5")); + let gesture_click = gtk::GestureClick::new(); gesture_click.set_button(gtk::gdk::BUTTON_SECONDARY); screen_shot.set_paintable(self.img.as_ref()); @@ -371,8 +399,10 @@ impl RelmListItem for LayerItem { menu.popup(); })); + visible.set_icon_name("eye-filled"); + _root.add_controller(gesture_click); - label.set_label(&self.layer_name); - button.set_active(self.visiable); + label.set_text(&self.layer_name); + // button.set_active(self.visiable); } }