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;
}
.tooltip {
font-size: 8px;
}
.lv {
background: transparent;
}

View File

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

View File

@ -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<MonitorModel>,
#[do_not_track]
sidebar: Controller<SideBarModel>,
#[do_not_track]
layers: Rc<RefCell<Vec<Layer>>>,
#[do_not_track]
elements: Vec<Arc<Mutex<TimeSeriesElement>>>,
@ -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 = &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{
@ -195,13 +212,22 @@ impl Component for AppModel {
root: Self::Root,
sender: ComponentSender<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(
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::<FileActionGroup>::new();
relm4::main_application().set_accelerators_for_action::<OpenAction>(&["<primary>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);

View File

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

View File

@ -10,11 +10,11 @@ pub enum MonitorInputMsg {
RefreshRender,
AddWidget(Box<dyn Widget>),
RemoveWidget,
AddMetaItem(HashMap<String, String>),
ClearMetaItems,
UpdateMetaItem(HashMap<String, String>),
// AddMetaItem(HashMap<String, String>),
// ClearMetaItems,
// UpdateMetaItem(HashMap<String, String>),
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"),
}
}
}

View File

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

View File

@ -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<WidgetFrame>,
#[no_eq]
layers: Rc<RefCell<Vec<Layer>>>,
#[no_eq]
sidebar: Controller<SideBarModel>,
}
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=&gtk::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=&gtk::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=&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
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=&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
},
}
}
}
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<Self>,
) -> ComponentParts<Self> {
let sidebar_sender = sender.clone();
let sidebar: Controller<SideBarModel> = 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> = 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,
};

View File

@ -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;

View File

@ -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] = &gtk::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] = &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"]
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!("<b>Opacity:</b> 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);
}
}