This commit is contained in:
tsuki 2024-03-22 00:36:12 +08:00
parent 59a886f222
commit 09b7e83669
11 changed files with 168 additions and 138 deletions

View File

@ -29,6 +29,10 @@ paned>separator {
font-size: 14px; font-size: 14px;
} }
.tooltip {
font-size: 8px;
}
.lv { .lv {
background: transparent; background: transparent;
} }

View File

@ -15,4 +15,7 @@ icons = [
"home-filled", "home-filled",
"settings-filled", "settings-filled",
"save-filled", "save-filled",
"timer-filled",
"eye-filled",
"eye-off-filled",
] ]

View File

@ -6,7 +6,7 @@ use super::{
ControlPanelOutputMsg, TimelineMsg, ControlPanelOutputMsg, TimelineMsg,
}; };
use crate::pipeline::{GridElementImpl, OffscreenRenderer}; use crate::pipeline::{GridElementImpl, OffscreenRenderer};
use crate::widgets::AssoElement; use crate::widgets::{AssoElement, DynamicCol};
use crate::{ use crate::{
actions::register_layer_actions, actions::register_layer_actions,
pipeline::element::{Element, InstantElement, InstantElementDrawerType, TimeSeriesElement}, pipeline::element::{Element, InstantElement, InstantElementDrawerType, TimeSeriesElement},
@ -53,6 +53,7 @@ use std::{
use tokio::{sync::oneshot, task}; use tokio::{sync::oneshot, task};
use tracing::{debug, error, info, warn}; use tracing::{debug, error, info, warn};
use tracing_subscriber::fmt::layer; use tracing_subscriber::fmt::layer;
use crate::components::sidebar::{SideBarInputMsg, SideBarModel};
relm4::new_action_group!(FileActionGroup, "file"); relm4::new_action_group!(FileActionGroup, "file");
relm4::new_stateless_action!(OpenAction, FileActionGroup, "open"); relm4::new_stateless_action!(OpenAction, FileActionGroup, "open");
@ -88,6 +89,8 @@ pub struct AppModel {
#[do_not_track] #[do_not_track]
render: Controller<MonitorModel>, render: Controller<MonitorModel>,
#[do_not_track] #[do_not_track]
sidebar: Controller<SideBarModel>,
#[do_not_track]
layers: Rc<RefCell<Vec<Layer>>>, layers: Rc<RefCell<Vec<Layer>>>,
#[do_not_track] #[do_not_track]
elements: Vec<Arc<Mutex<TimeSeriesElement>>>, elements: Vec<Arc<Mutex<TimeSeriesElement>>>,
@ -161,7 +164,21 @@ impl Component for AppModel {
adw::ToastOverlay{ adw::ToastOverlay{
set_hexpand: true, set_hexpand: true,
set_vexpand: 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 = &gtk::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{ 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, root: Self::Root,
sender: ComponentSender<Self>, sender: ComponentSender<Self>,
) -> ComponentParts<Self> { ) -> ComponentParts<Self> {
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( let control = ControlPanelModel::builder().launch(layers.clone()).forward(
sender.input_sender(), sender.input_sender(),
|msg| match msg { |msg| match msg {
ControlPanelOutputMsg::OpenFile((key, time)) => AppMsg::Close, ControlPanelOutputMsg::OpenFile((key, time)) => AppMsg::Close,
}, },
); );
let sidebar = SideBarModel::builder()
.launch(layers.clone())
.forward(sender.input_sender(), |msg| match msg {
_ => AppMsg::Close,
});
let render = let render =
MonitorModel::builder() MonitorModel::builder()
.launch(layers.clone()) .launch(layers.clone())
@ -215,9 +241,12 @@ impl Component for AppModel {
.forward(sender.input_sender(), |a| AppMsg::Close); .forward(sender.input_sender(), |a| AppMsg::Close);
let mut dispatcher = Rc::new(Dispatcher::new(5, 5, chrono::Duration::minutes(1))); 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 cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0));
let dialog_dispatcher = dispatcher.clone();
let dialog_render_sender = render.sender().clone(); let dialog = {
let dialog = OpenDialog::builder() 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) .transient_for_native(&root)
.launch(OpenDialogSettings::default()) .launch(OpenDialogSettings::default())
.forward(sender.input_sender(), move |response| match response { .forward(sender.input_sender(), move |response| match response {
@ -241,28 +270,31 @@ impl Component for AppModel {
.get_instance(); .get_instance();
let layer = let layer =
Layer::new(true, "New Layer".to_string(), AssoElement::Instant(element)); Layer::new(true, "New Layer".to_string(), AssoElement::Instant(element));
dialog_render_sender.emit(MonitorInputMsg::AddMetaItem(meta.to_map())); dialog_sidebar_sender.emit(SideBarInputMsg::AddMetaItems(meta.to_map()));
dialog_render_sender.emit(MonitorInputMsg::SetRenderRange( dialog_render_sender.emit(MonitorInputMsg::SetRenderRange(lon_start, lon_end, lat_start, lat_end));
lon_start, lon_end, lat_start, lat_end,
));
AppMsg::NewLayer(layer) AppMsg::NewLayer(layer)
} }
_ => AppMsg::Close, _ => AppMsg::Close,
}); })};
let buffer: Buffer = Rc::new(RefCell::new(HashMap::new())); let buffer: Buffer = Rc::new(RefCell::new(HashMap::new()));
let model = AppModel { let model = AppModel {
cms, cms,
dispatcher, dispatcher,
waiting_for: None, waiting_for: None,
elements: Vec::with_capacity(20), elements: Vec::with_capacity(20),
open_dialog: dialog, open_dialog: dialog,
sidebar,
control, control,
render, render,
layers, layers,
setting, setting,
tracker: 0, tracker: 0,
}; };
let render = model.render.widget();
let sidebar = model.sidebar.widget();
let widgets = view_output!(); let widgets = view_output!();
let mut group = RelmActionGroup::<FileActionGroup>::new(); let mut group = RelmActionGroup::<FileActionGroup>::new();
relm4::main_application().set_accelerators_for_action::<OpenAction>(&["<primary>O"]); relm4::main_application().set_accelerators_for_action::<OpenAction>(&["<primary>O"]);
@ -288,7 +320,7 @@ impl Component for AppModel {
match msg { match msg {
AppMsg::NewLayer(layer) => { AppMsg::NewLayer(layer) => {
(*self.layers).borrow_mut().push(layer); (*self.layers).borrow_mut().push(layer);
self.render.sender().send(MonitorInputMsg::RefreshLayerList); self.sidebar.sender().send(SideBarInputMsg::RefreshList);
} }
AppMsg::CloseRequest => { AppMsg::CloseRequest => {
relm4::main_application().quit(); relm4::main_application().quit();
@ -309,7 +341,7 @@ impl Component for AppModel {
switched_layer.set_associated_element(AssoElement::TimeSeries(element.clone())); switched_layer.set_associated_element(AssoElement::TimeSeries(element.clone()));
self.elements.push(element); self.elements.push(element);
} }
self.render.sender().send(MonitorInputMsg::RefreshLayerList); self.sidebar.sender().send(SideBarInputMsg::RefreshList);
} }
AppMsg::OpenDialog => { AppMsg::OpenDialog => {
self.open_dialog.emit(OpenDialogMsg::Open); self.open_dialog.emit(OpenDialogMsg::Open);

View File

@ -2,6 +2,7 @@ pub mod app;
mod control_panel; mod control_panel;
mod monitor; mod monitor;
mod setting; mod setting;
pub mod sidebar;
pub use control_panel::*; pub use control_panel::*;

View File

@ -10,11 +10,11 @@ pub enum MonitorInputMsg {
RefreshRender, RefreshRender,
AddWidget(Box<dyn Widget>), AddWidget(Box<dyn Widget>),
RemoveWidget, RemoveWidget,
AddMetaItem(HashMap<String, String>), // AddMetaItem(HashMap<String, String>),
ClearMetaItems, // ClearMetaItems,
UpdateMetaItem(HashMap<String, String>), // UpdateMetaItem(HashMap<String, String>),
RefreshTiles, RefreshTiles,
RefreshLayerList, // RefreshLayerList,
SetRenderRange(f64, f64, f64, f64), SetRenderRange(f64, f64, f64, f64),
ChangeZoom(f64), ChangeZoom(f64),
None, None,
@ -29,13 +29,9 @@ impl Debug for MonitorInputMsg {
MonitorInputMsg::SetRenderRange(_, _, _, _) => { MonitorInputMsg::SetRenderRange(_, _, _, _) => {
write!(f, "MonitorInputMsg::SetRenderRange") write!(f, "MonitorInputMsg::SetRenderRange")
} }
MonitorInputMsg::RefreshLayerList => write!(f, "MonitorInputMsg::RefreshLayerList"),
MonitorInputMsg::None => write!(f, "MonitorInputMsg::None"), MonitorInputMsg::None => write!(f, "MonitorInputMsg::None"),
MonitorInputMsg::AddWidget(_) => write!(f, "MonitorInputMsg::AddWidget"), MonitorInputMsg::AddWidget(_) => write!(f, "MonitorInputMsg::AddWidget"),
MonitorInputMsg::RemoveWidget => write!(f, "MonitorInputMsg::RemoveWidget"), MonitorInputMsg::RemoveWidget => write!(f, "MonitorInputMsg::RemoveWidget"),
MonitorInputMsg::AddMetaItem(_) => write!(f, "MonitorInputMsg::RemoveWidget"),
MonitorInputMsg::ClearMetaItems => write!(f, "MonitorInputMsg::ClearMetaItems"),
MonitorInputMsg::UpdateMetaItem(_) => write!(f, "MonitorInputMsg::UpdateMetaItem"),
} }
} }
} }

View File

@ -1,4 +1,4 @@
pub mod messages; pub mod messages;
pub mod monitor; pub mod monitor;
pub mod sidebar;
pub use monitor::*; pub use monitor::*;

View File

@ -19,7 +19,7 @@ use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tracing::*; use tracing::*;
use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg}; // use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg};
use crate::coords::Range; use crate::coords::Range;
use crate::map_tile::MapTile; use crate::map_tile::MapTile;
use crate::map_tile_utils::lat_lon_to_zoom; use crate::map_tile_utils::lat_lon_to_zoom;
@ -54,8 +54,6 @@ pub struct MonitorModel {
widgets: Vec<WidgetFrame>, widgets: Vec<WidgetFrame>,
#[no_eq] #[no_eq]
layers: Rc<RefCell<Vec<Layer>>>, layers: Rc<RefCell<Vec<Layer>>>,
#[no_eq]
sidebar: Controller<SideBarModel>,
} }
pub struct MonitorWidgets { pub struct MonitorWidgets {
@ -71,71 +69,50 @@ impl Component for MonitorModel {
view! { view! {
#[root] #[root]
adw::BreakpointBin { gtk::Frame{
set_hexpand: true, add_css_class: "rb",
set_vexpand: true, set_margin_all: 5,
set_height_request: 500, #[name="widget_layer"]
set_width_request: 700, gtk::Overlay{
#[wrap(Some)] #[name = "renderer"]
#[name="test"]
set_child = &DynamicCol{
set_end_width: 300,
set_hexpand: true,
set_vexpand: true,
#[wrap(Some)]
#[name="paned"]
set_child_paned=&gtk::Paned{
#[wrap(Some)] #[wrap(Some)]
#[name="render"] set_child = &Render{
set_start_child=&gtk::Frame{ #[track = "model.changed(MonitorModel::render_cfg())"]
add_css_class: "rb", set_cfg: model.render_cfg,
set_margin_all: 5, #[track = "model.changed(MonitorModel::render_range())"]
#[name="widget_layer"] set_view: model.render_range,
gtk::Overlay{ set_tiles: model.map_tile_getter.clone(),
#[name = "renderer"] connect_render_status_notify[sender] => move |r| {
#[wrap(Some)] sender.output(MonitorOutputMsg::LayerRenderFinished);
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=&gtk::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
}, },
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)] add_overlay=&gtk::Button{
set_end_child=model.sidebar.widget(), 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( fn update_with_view(
@ -150,13 +127,6 @@ impl Component for MonitorModel {
MonitorInputMsg::RefreshRender => { MonitorInputMsg::RefreshRender => {
widgets.renderer.queue_render(); 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) => { MonitorInputMsg::SetRenderRange(lon_start, lon_end, lat_start, lat_end) => {
let current_rate = widgets.renderer.scale_rate(); let current_rate = widgets.renderer.scale_rate();
self.set_render_range((lat_start, lat_end, lon_start, lon_end)); 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(); let zoom: f64 = (new_rate / current_rate).log2();
sender.input(MonitorInputMsg::ChangeZoom(zoom)); 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 => { MonitorInputMsg::RefreshTiles => {
let ((x1, x2), (y1, y2)) = widgets.renderer.render_range(); 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))); 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, root: Self::Root,
sender: ComponentSender<Self>, sender: ComponentSender<Self>,
) -> ComponentParts<Self> { ) -> ComponentParts<Self> {
let sidebar_sender = sender.clone(); // let sidebar_sender = sender.clone();
let sidebar: Controller<SideBarModel> = SideBarModel::builder() // let sidebar: Controller<SideBarModel> = SideBarModel::builder()
.launch(init.clone()) // .launch(init.clone())
.forward(sender.input_sender(), move |msg| match msg { // .forward(sender.input_sender(), move |msg| match msg {
SideBarOutputMsg::SwitchToTimeSeries(layer) => { // SideBarOutputMsg::SwitchToTimeSeries(layer) => {
sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer)); // sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer));
MonitorInputMsg::None // MonitorInputMsg::None
} // }
_ => MonitorInputMsg::None, // _ => MonitorInputMsg::None,
}); // });
let render_cfg = RenderConfig { let render_cfg = RenderConfig {
padding: [0.0, 0.0, 0.0, 0.0], padding: [0.0, 0.0, 0.0, 0.0],
@ -231,7 +196,6 @@ impl Component for MonitorModel {
layers: init, layers: init,
zoom: 4, zoom: 4,
map_tile_getter: Rc::new(MapTile::default()), map_tile_getter: Rc::new(MapTile::default()),
sidebar,
tracker: 0, tracker: 0,
}; };

View File

@ -9,7 +9,7 @@ use relm4::{
}; };
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub(super) struct MyListItem { pub(in crate::components) struct MyListItem {
tag: String, tag: String,
info: String, info: String,
} }
@ -20,7 +20,7 @@ impl MyListItem {
} }
} }
pub(super) struct TagColumn; pub(in crate::components) struct TagColumn;
impl LabelColumn for TagColumn { impl LabelColumn for TagColumn {
type Item = MyListItem; 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 { impl RelmColumn for InfoColumn {
type Root = gtk::Label; type Root = gtk::Label;

View File

@ -281,11 +281,13 @@ impl LayerItem {
} }
struct Widgets { struct Widgets {
label: gtk::Label, label: gtk::EditableLabel,
screen_shot: gtk::Image, screen_shot: gtk::Image,
status: gtk::Label, status_icon: gtk::Image,
button: gtk::CheckButton, status_label: gtk::Label,
menu: gtk::PopoverMenu, visible: gtk::Button,
opacity: gtk::Label,
menu: gtk::Popover,
} }
impl RelmListItem for LayerItem { impl RelmListItem for LayerItem {
@ -308,6 +310,7 @@ impl RelmListItem for LayerItem {
relm4::view! { relm4::view! {
my_box = gtk::Box { my_box = gtk::Box {
set_valign: gtk::Align::Center,
gtk::Frame{ gtk::Frame{
set_margin_end: 10, set_margin_end: 10,
#[name = "screen_shot"] #[name = "screen_shot"]
@ -315,31 +318,46 @@ impl RelmListItem for LayerItem {
set_size_request: (65, 40), set_size_request: (65, 40),
} }
}, },
#[name = "label"] gtk::Grid{
gtk::Label{ set_row_homogeneous: true,
set_halign: gtk::Align::Start, attach[0,0,1,1] = &gtk::Box{
}, #[name="status_icon"]
#[name = "status"] gtk::Image{
gtk::Label{ inline_css: "-gtk-icon-transform: scale(0.8);",
set_halign: gtk::Align::Start },
}, #[name="status_label"]
gtk::Label{ gtk::Label{
set_hexpand: true, add_css_class:"tooltip",
}, set_halign: gtk::Align::Start,
#[name = "button"] }
gtk::CheckButton{ },
set_halign: gtk::Align::End, #[name="label"]
attach[0,1,1,1] = &gtk::EditableLabel{
set_hexpand: true,
},
#[name="opacity"]
attach[0,2,1,1] = &gtk::Label{
add_css_class:"tooltip",
set_halign: gtk::Align::Start
},
}, },
#[name="visible"]
gtk::Button{
set_vexpand: false,
set_hexpand: false,
},
#[name = "menu"] #[name = "menu"]
gtk::PopoverMenu::from_model(Some(&main_menu)){} gtk::Popover{}
} }
} }
let widgets = Widgets { let widgets = Widgets {
screen_shot, screen_shot,
label, label,
status, status_icon,
button, status_label,
opacity,
visible,
menu, menu,
}; };
@ -349,18 +367,28 @@ impl RelmListItem for LayerItem {
fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) { fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) {
let Widgets { let Widgets {
label, label,
button, visible,
screen_shot, screen_shot,
status, status_label,
status_icon,
opacity,
menu, menu,
} = widgets; } = widgets;
status.set_label(&match self.status { status_label.set_label(&match self.status {
LayerStatus::BindToTime(t) => format!("Bind To Time: {}", t), LayerStatus::BindToTime(t) => format!("Bind To Time: {}", t),
LayerStatus::Instance => "Instance".to_string(), LayerStatus::Instance => "Instance".to_string(),
LayerStatus::BindToOtherLayer(idx) => format!("Bind To Layer: {}", idx), 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!("<b>Opacity:</b> 0.5"));
let gesture_click = gtk::GestureClick::new(); let gesture_click = gtk::GestureClick::new();
gesture_click.set_button(gtk::gdk::BUTTON_SECONDARY); gesture_click.set_button(gtk::gdk::BUTTON_SECONDARY);
screen_shot.set_paintable(self.img.as_ref()); screen_shot.set_paintable(self.img.as_ref());
@ -371,8 +399,10 @@ impl RelmListItem for LayerItem {
menu.popup(); menu.popup();
})); }));
visible.set_icon_name("eye-filled");
_root.add_controller(gesture_click); _root.add_controller(gesture_click);
label.set_label(&self.layer_name); label.set_text(&self.layer_name);
button.set_active(self.visiable); // button.set_active(self.visiable);
} }
} }