408 lines
13 KiB
Rust
408 lines
13 KiB
Rust
use crate::actions::*;
|
|
use crate::widgets::AssoElement;
|
|
use abi_stable::type_level::trait_marker::Hash;
|
|
use chrono::{DateTime, Utc};
|
|
use glib_macros::clone;
|
|
use gtk::glib;
|
|
use gtk::prelude::WidgetExt;
|
|
use gtk::prelude::*;
|
|
use relm4::actions::{AccelsPlus, RelmAction};
|
|
use relm4::{
|
|
binding::{Binding, U8Binding},
|
|
factory::{DynamicIndex, FactoryComponent, FactorySender, FactoryVecDeque},
|
|
gtk::gio,
|
|
prelude::*,
|
|
typed_view::{
|
|
column::TypedColumnView,
|
|
list::{RelmListItem, TypedListView},
|
|
},
|
|
RelmObjectExt,
|
|
};
|
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
|
|
|
use crate::components::app::AppMsg;
|
|
use crate::{chart::Chart, predefined::color_mapper::BoundaryNorm, widgets::render::Layer};
|
|
|
|
use super::{
|
|
bottom_bar::BottomBarModel,
|
|
meta_data_list::{InfoColumn, MyListItem, TagColumn},
|
|
};
|
|
|
|
pub struct SideBarModel {
|
|
layers: Rc<RefCell<Vec<Layer>>>,
|
|
selected_layer_idx: usize,
|
|
counter: u8,
|
|
list_view_wrapper: TypedListView<LayerItem, gtk::MultiSelection>,
|
|
bottom_bar_vec: FactoryVecDeque<BottomBarModel>,
|
|
meta_list_view: TypedColumnView<MyListItem, gtk::NoSelection>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum SideBarInputMsg {
|
|
AddMetaItems(HashMap<String, String>),
|
|
ClearMetaItems,
|
|
RefreshList,
|
|
None,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum SideBarOutputMsg {
|
|
SelectLayer(Vec<usize>),
|
|
NewLayer(Layer),
|
|
SwitchToTimeSeries(usize),
|
|
}
|
|
|
|
#[relm4::component(pub)]
|
|
impl SimpleComponent for SideBarModel {
|
|
type Init = Rc<RefCell<Vec<Layer>>>;
|
|
type Output = SideBarOutputMsg;
|
|
type Input = SideBarInputMsg;
|
|
|
|
view! {
|
|
#[root]
|
|
gtk::Box {
|
|
set_orientation: gtk::Orientation::Vertical,
|
|
set_spacing: 5,
|
|
set_margin_all: 5,
|
|
|
|
gtk::Paned{
|
|
set_orientation: gtk::Orientation::Vertical,
|
|
set_position: 200,
|
|
#[wrap(Some)]
|
|
set_start_child = >k::Box{
|
|
set_orientation: gtk::Orientation::Vertical,
|
|
set_spacing: 5,
|
|
gtk::Frame{
|
|
add_css_class: "rb",
|
|
#[name="meta_panel"]
|
|
gtk::Notebook::builder().vexpand(true).hexpand(true).build() -> gtk::Notebook{}
|
|
},
|
|
},
|
|
|
|
#[wrap(Some)]
|
|
set_end_child=>k::Box{
|
|
set_orientation: gtk::Orientation::Vertical,
|
|
set_vexpand: true,
|
|
set_hexpand: true,
|
|
#[name="bottom_panel"]
|
|
gtk::Notebook::builder().vexpand(true).build() -> gtk::Notebook{
|
|
set_margin_top: 10,
|
|
set_margin_bottom: 5
|
|
},
|
|
#[local_ref]
|
|
counter_box -> gtk::Box{
|
|
set_spacing: 5,
|
|
}
|
|
}
|
|
}
|
|
},
|
|
layer_page = gtk::ScrolledWindow::builder()
|
|
.vexpand(true)
|
|
.hexpand(true)
|
|
.build() -> gtk::ScrolledWindow{
|
|
#[wrap(Some)]
|
|
#[local_ref]
|
|
set_child=my_view -> gtk::ListView{
|
|
},
|
|
set_margin_horizontal:5,
|
|
set_margin_vertical:3
|
|
},
|
|
#[local_ref]
|
|
meta_view -> gtk::ColumnView{
|
|
set_hexpand:true,
|
|
set_vexpand:true,
|
|
set_show_column_separators: true,
|
|
set_show_row_separators: true,
|
|
set_enable_rubberband:true,
|
|
set_reorderable:false,
|
|
},
|
|
bottom_panel.append_page(&layer_page, Some(>k::Label::new(Some("Layers")))),
|
|
meta_panel.append_page(meta_view, Some(>k::Label::new(Some("Meta")))),
|
|
meta_panel.append_page(&Chart::new(), Some(>k::Label::new(Some("Chart")))),
|
|
#[local_ref]
|
|
info_c -> gtk::ColumnViewColumn{
|
|
set_expand: true
|
|
}
|
|
}
|
|
|
|
fn init(
|
|
init: Self::Init,
|
|
root: Self::Root,
|
|
sender: ComponentSender<Self>,
|
|
) -> ComponentParts<Self> {
|
|
// Initialize the ListView wrapper
|
|
let mut list_view_wrapper: TypedListView<LayerItem, gtk::MultiSelection> =
|
|
TypedListView::with_sorting();
|
|
|
|
list_view_wrapper.selection_model.connect_selection_changed(
|
|
clone!(@strong sender => move |s,_, _| {
|
|
let selection = s.selection();
|
|
|
|
let (iter, first) = gtk::BitsetIter::init_first(&selection).unwrap();
|
|
let mut result = vec![first as usize];
|
|
result.extend(iter.map(|v| v as usize));
|
|
sender.output(SideBarOutputMsg::SelectLayer(result));
|
|
}),
|
|
);
|
|
// let mut bottom_bar_vec = FactoryVecDeque::new(gtk::Box::default(), sender.input_sender());
|
|
|
|
let mut bottom_bar_vec =
|
|
FactoryVecDeque::builder()
|
|
.launch_default()
|
|
.forward(sender.input_sender(), |msg| match msg {
|
|
_ => SideBarInputMsg::None,
|
|
});
|
|
|
|
let app = relm4::main_application();
|
|
|
|
{
|
|
let mut bottom_bar_vec_guard = bottom_bar_vec.guard();
|
|
bottom_bar_vec_guard.push_back(BottomBarModel::new("add-filled".to_string()));
|
|
bottom_bar_vec_guard.push_back(BottomBarModel::new("delete-filled".to_string()));
|
|
bottom_bar_vec_guard.push_back(BottomBarModel::new("chevron-up-filled".to_string()));
|
|
bottom_bar_vec_guard.push_back(BottomBarModel::new("chevron-down-filled".to_string()));
|
|
}
|
|
let mut meta_list_view = TypedColumnView::new();
|
|
meta_list_view.append_column::<TagColumn>();
|
|
meta_list_view.append_column::<InfoColumn>();
|
|
|
|
let mut model = SideBarModel {
|
|
meta_list_view,
|
|
layers: init,
|
|
selected_layer_idx: 0,
|
|
counter: 0,
|
|
list_view_wrapper,
|
|
bottom_bar_vec,
|
|
};
|
|
let my_view = &model.list_view_wrapper.view;
|
|
let counter_box = model.bottom_bar_vec.widget();
|
|
let meta_view = &model.meta_list_view.view;
|
|
let columns = model.meta_list_view.get_columns();
|
|
let info_c = columns.get("info").unwrap();
|
|
let widgets = view_output!();
|
|
|
|
{
|
|
let mut list = model
|
|
.layers
|
|
.borrow()
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(idx, v)| {
|
|
LayerItem::new(
|
|
idx as u32,
|
|
v.name.clone(),
|
|
v.visiable,
|
|
v.get_thumbnail(),
|
|
match v.get_associated_element() {
|
|
AssoElement::TimeSeries(_) => LayerStatus::BindToTime(Utc::now()),
|
|
AssoElement::Instant(_) => LayerStatus::Instance,
|
|
_ => LayerStatus::Instance,
|
|
},
|
|
)
|
|
})
|
|
.collect::<Vec<_>>();
|
|
model.list_view_wrapper.extend_from_iter(list);
|
|
}
|
|
|
|
ComponentParts { model, widgets }
|
|
}
|
|
|
|
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
|
|
match message {
|
|
SideBarInputMsg::RefreshList => {
|
|
let mut list = self
|
|
.layers
|
|
.borrow()
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(idx, v)| {
|
|
LayerItem::new(
|
|
idx as u32,
|
|
v.name.clone(),
|
|
v.visiable,
|
|
v.get_thumbnail(),
|
|
match v.get_associated_element() {
|
|
AssoElement::TimeSeries(_) => LayerStatus::BindToTime(Utc::now()),
|
|
AssoElement::Instant(_) => LayerStatus::Instance,
|
|
_ => LayerStatus::Instance,
|
|
},
|
|
)
|
|
})
|
|
.collect::<Vec<_>>();
|
|
self.list_view_wrapper.clear();
|
|
self.list_view_wrapper.extend_from_iter(list);
|
|
}
|
|
SideBarInputMsg::AddMetaItems(hs) => {
|
|
for (k, v) in hs {
|
|
self.meta_list_view.append(MyListItem::new(k, v));
|
|
}
|
|
}
|
|
SideBarInputMsg::ClearMetaItems => {
|
|
self.meta_list_view.clear();
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
enum LayerStatus {
|
|
BindToTime(DateTime<Utc>),
|
|
Instance,
|
|
BindToOtherLayer(usize),
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
struct LayerItem {
|
|
key: u32,
|
|
layer_name: String,
|
|
visiable: bool,
|
|
status: LayerStatus,
|
|
img: Option<gtk::gdk::Texture>,
|
|
}
|
|
|
|
impl LayerItem {
|
|
fn new(
|
|
key: u32,
|
|
name: String,
|
|
visiable: bool,
|
|
img: Option<gtk::gdk::Texture>,
|
|
status: LayerStatus,
|
|
) -> Self {
|
|
Self {
|
|
key,
|
|
layer_name: name,
|
|
visiable,
|
|
status,
|
|
img,
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Widgets {
|
|
label: gtk::EditableLabel,
|
|
screen_shot: gtk::Image,
|
|
status_icon: gtk::Image,
|
|
status_label: gtk::Label,
|
|
visible: gtk::Button,
|
|
opacity: gtk::Label,
|
|
menu: gtk::Popover,
|
|
}
|
|
|
|
impl RelmListItem for LayerItem {
|
|
type Root = gtk::Box;
|
|
type Widgets = Widgets;
|
|
|
|
fn setup(_item: >k::ListItem) -> (gtk::Box, Widgets) {
|
|
let position = _item.position() as u8;
|
|
|
|
relm4::menu! {
|
|
main_menu: {
|
|
custom: "MyWidget",
|
|
"Remove" => RemoveLayerAction,
|
|
section!{
|
|
"test" => RemoveLayerAction,
|
|
"select" => AddLayerAction
|
|
}
|
|
}
|
|
}
|
|
|
|
relm4::view! {
|
|
my_box = gtk::Box {
|
|
set_valign: gtk::Align::Center,
|
|
gtk::Frame{
|
|
set_margin_end: 10,
|
|
#[name = "screen_shot"]
|
|
gtk::Image{
|
|
set_size_request: (65, 40),
|
|
}
|
|
},
|
|
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::Popover{}
|
|
}
|
|
}
|
|
|
|
let widgets = Widgets {
|
|
screen_shot,
|
|
label,
|
|
status_icon,
|
|
status_label,
|
|
opacity,
|
|
visible,
|
|
menu,
|
|
};
|
|
|
|
(my_box, widgets)
|
|
}
|
|
|
|
fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) {
|
|
let Widgets {
|
|
label,
|
|
visible,
|
|
screen_shot,
|
|
status_label,
|
|
status_icon,
|
|
opacity,
|
|
menu,
|
|
} = widgets;
|
|
|
|
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());
|
|
|
|
let menu = menu.clone();
|
|
gesture_click.connect_released(clone!(@weak menu => move |gesture_click, _, x, y| {
|
|
menu.set_pointing_to(Some(>k::gdk::Rectangle::new(x as i32, y as i32, 1, 1)));
|
|
menu.popup();
|
|
}));
|
|
|
|
visible.set_icon_name("eye-filled");
|
|
|
|
_root.add_controller(gesture_click);
|
|
label.set_text(&self.layer_name);
|
|
// button.set_active(self.visiable);
|
|
}
|
|
}
|