diff --git a/src/ui/composable_view_BACKUP_21160.rs b/src/ui/composable_view_BACKUP_21160.rs deleted file mode 100644 index db7aeb14cc83446d5f4206752848a26a7411ef39..0000000000000000000000000000000000000000 --- a/src/ui/composable_view_BACKUP_21160.rs +++ /dev/null @@ -1,545 +0,0 @@ -use crate::{ - error::ErrInstrument, - mavlink, msg_broker, - serial::{get_first_stm32_serial_port, list_all_serial_ports}, - ui::panes::PaneKind, -}; - -use super::{ - panes::{Pane, PaneBehavior}, - persistency::{LayoutManager, LayoutManagerWindow}, - shortcuts, - utils::maximized_pane_ui, - widget_gallery::WidgetGallery, -}; -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use egui::{Align2, Button, ComboBox, Key, Modifiers, Vec2}; -use egui_extras::{Size, StripBuilder}; -use egui_tiles::{Behavior, Container, Linear, LinearDir, Tile, TileId, Tiles, Tree}; -use serde::{Deserialize, Serialize}; -use tracing::{debug, error, trace}; - -#[derive(Default)] -pub struct ComposableView { - /// Persistent state of the app - state: ComposableViewState, - layout_manager: LayoutManager, - widget_gallery: WidgetGallery, - behavior: ComposableBehavior, - maximized_pane: Option<TileId>, - - // == Windows == - sources_window: SourceWindow, - layout_manager_window: LayoutManagerWindow, -} - -// An app must implement the `App` trait to define how the ui is built -impl eframe::App for ComposableView { - // The update function is called each time the UI needs repainting! - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - let panes_tree = &mut self.state.panes_tree; - - // Get the id of the hovered pane, in order to apply actions to it - let hovered_pane = panes_tree - .tiles - .iter() - .find(|(_, tile)| matches!(tile, Tile::Pane(pane) if pane.contains_pointer())) - .map(|(id, _)| *id); - trace!("Hovered pane: {:?}", hovered_pane); - - // Capture any pane action generated by pane children - let mut pane_action = self.behavior.action.take(); - trace!("Pane action: {:?}", pane_action); - - if let Some(hovered_tile) = hovered_pane { - // Capture any pane action generated by keyboard shortcuts - let key_action_pairs = [ - ((Modifiers::NONE, Key::V), PaneAction::SplitV), - ((Modifiers::NONE, Key::H), PaneAction::SplitH), - ((Modifiers::NONE, Key::C), PaneAction::Close), - ( - (Modifiers::NONE, Key::R), - PaneAction::ReplaceThroughGallery(Some(hovered_tile)), - ), - ((Modifiers::SHIFT, Key::Escape), PaneAction::Maximize), - ((Modifiers::NONE, Key::Escape), PaneAction::Exit), - ]; - pane_action = pane_action.or(shortcuts::map_to_action(ctx, &key_action_pairs[..])); - } - - // If an action was triggered, we consume it - if let Some(action) = pane_action.take() { - match action { - PaneAction::SplitH => { - if let Some(hovered_tile) = hovered_pane { - if self.maximized_pane.is_none() { - debug!("Called SplitH on tile {:?}", hovered_tile); - let hovered_tile_pane = panes_tree - .tiles - .remove(hovered_tile) - .log_expect("Hovered tile not found"); - let left_pane = panes_tree.tiles.insert_new(hovered_tile_pane); - let right_pane = panes_tree.tiles.insert_pane(Pane::default()); - panes_tree.tiles.insert( - hovered_tile, - Tile::Container(Container::Linear(Linear::new_binary( - LinearDir::Horizontal, - [left_pane, right_pane], - 0.5, - ))), - ); - } - } - } - PaneAction::SplitV => { - if self.maximized_pane.is_none() { - if let Some(hovered_tile) = hovered_pane { - debug!("Called SplitV on tile {:?}", hovered_tile); - let hovered_tile_pane = panes_tree - .tiles - .remove(hovered_tile) - .log_expect("Hovered tile not found"); - let replaced = panes_tree.tiles.insert_new(hovered_tile_pane); - let lower_pane = panes_tree.tiles.insert_pane(Pane::default()); - panes_tree.tiles.insert( - hovered_tile, - Tile::Container(Container::Linear(Linear::new_binary( - LinearDir::Vertical, - [replaced, lower_pane], - 0.5, - ))), - ); - } - } - } - PaneAction::Close => { - if let Some(hovered_tile) = hovered_pane { - debug!("Called Close on tile {:?}", hovered_tile); - // Ignore if the root pane is the only one - if panes_tree.tiles.len() != 1 && self.maximized_pane.is_none() { - panes_tree.remove_recursively(hovered_tile); - } - } - } - PaneAction::Replace(tile_id, new_pane) => { - debug!( - "Called Replace on tile {:?} with pane {:?}", - tile_id, new_pane - ); - panes_tree.tiles.insert(tile_id, Tile::Pane(*new_pane)); - } - PaneAction::ReplaceThroughGallery(Some(source_tile)) => { - self.widget_gallery.replace_tile(source_tile); - } - PaneAction::Maximize => { - // This is a toggle: if there is not currently a maximized pane, - // maximize the hovered pane, otherwize remove the maximized pane. - if self.maximized_pane.is_some() { - self.maximized_pane = None; - } else if let Some(hovered_tile) = hovered_pane { - let hovered_pane_is_default = panes_tree - .tiles - .get(hovered_tile) - .map(|hovered_pane| { - matches!( - hovered_pane, - Tile::Pane(Pane { - pane: PaneKind::Default(_), - }) - ) - }) - .unwrap_or(false); - if !hovered_pane_is_default { - self.maximized_pane = Some(hovered_tile); - } - } - } - PaneAction::Exit => { - if self.maximized_pane.is_some() { - self.maximized_pane = None; - } - } - _ => panic!("Unable to handle action"), - } - } - - // Show a panel at the bottom of the screen with few global controls - egui::TopBottomPanel::bottom("bottom_control").show(ctx, |ui| { - // Horizontal belt of controls - Sides::new().show( - ui, - |ui| { - ui.label("Informative side here!"); - }, - |ui| { - ui.horizontal(|ui| { - egui::global_theme_preference_switch(ui); - - // Window for the sources - self.sources_window.show_window(ui); - -<<<<<<< HEAD - if ui.button("Sources").clicked() { - self.sources_window.visible = !self.sources_window.visible; - } - if ui.button("Layout Manager").clicked() { - self.layout_manager_window - .toggle_open_state(&self.layout_manager); - } - - // If a pane is maximized show a visual clue - if self.maximized_pane.is_some() { - ui.label("Pane Maximized!"); - } - }) -======= - if ui - .add(Button::new("🔌").frame(false)) - .on_hover_text("Open the Sources") - .clicked() - { - self.sources_window.visible = !self.sources_window.visible; - } - if ui - .add(Button::new("💾").frame(false)) - .on_hover_text("Open the Layout Manager") - .clicked() - { - self.layout_manager_window - .toggle_open_state(&self.layout_manager); - } - }); - }, - ); ->>>>>>> 9dc29a7 ([StatusBar] added right side (action bar) with icons instead of text) - }); - - // A central panel covers the remainder of the screen, i.e. whatever area is left after adding other panels. - egui::CentralPanel::default().show(ctx, |ui| { - if let Some(maximized_pane) = self.maximized_pane { - if let Some(Tile::Pane(pane)) = panes_tree.tiles.get_mut(maximized_pane) { - maximized_pane_ui(ui, maximized_pane, pane); - } else { - panic!("Maximized pane not found in tree!"); - } - } else { - panes_tree.ui(&mut self.behavior, ui); - } - }); - - self.layout_manager_window - .show(ctx, &mut self.layout_manager, &mut self.state); - if let Some(action) = self.widget_gallery.show(ctx) { - debug!("Widget gallery returned action {action:?}"); - self.behavior.action = Some(action); - } - } - - fn save(&mut self, storage: &mut dyn eframe::Storage) { - self.layout_manager.save_current_layout(storage); - } -} - -impl ComposableView { - pub fn new(app_name: &str, storage: &dyn eframe::Storage) -> Self { - let layout_manager = LayoutManager::new(app_name, storage); - let mut s = Self { - layout_manager, - ..Self::default() - }; - // Load the selected layout if valid and existing - if let Some(layout) = s.layout_manager.current_layout().cloned() { - s.layout_manager - .load_layout(layout, &mut s.state) - .unwrap_or_else(|e| { - error!("Error loading layout: {}", e); - }); - } - s - } -} - -#[derive(Serialize, Deserialize, Clone, PartialEq)] -pub struct ComposableViewState { - pub panes_tree: Tree<Pane>, -} - -impl Default for ComposableViewState { - fn default() -> Self { - let mut tiles = Tiles::default(); - let root = tiles.insert_pane(Pane::default()); - let panes_tree = egui_tiles::Tree::new("main_tree", root, tiles); - - Self { panes_tree } - } -} - -impl ComposableViewState { - pub fn from_file(path: &PathBuf) -> anyhow::Result<Self> { - fs::read_to_string(path) - .and_then(|json| serde_json::from_str::<ComposableViewState>(&json).map_err(Into::into)) - .map_err(|e| anyhow::anyhow!("Error deserializing layout: {}", e)) - } - - pub fn to_file(&self, path: &Path) -> anyhow::Result<()> { - // Check if the parent path exists, if not create it - if let Some(parent) = path.parent() { - if !parent.exists() { - fs::create_dir_all(parent) - .map_err(|e| anyhow::anyhow!("Error creating directory: {}", e))?; - debug!("Created directory {:?}", parent); - } - } - - let serialized_layout = serde_json::to_string_pretty(self) - .map_err(|e| anyhow::anyhow!("Error serializing layout: {}", e))?; - debug!("Serialized layout: {}", serialized_layout); - fs::write(path, serialized_layout) - .map_err(|e| anyhow::anyhow!("Error writing layout: {}", e))?; - - Ok(()) - } -} - -#[derive(Debug, PartialEq, Eq, Default)] -enum ConnectionKind { - #[default] - Ethernet, - Serial, -} - -#[derive(Debug)] -enum ConnectionDetails { - Ethernet { port: u16 }, - Serial { port: String, baud_rate: u32 }, -} - -impl Default for ConnectionDetails { - fn default() -> Self { - ConnectionDetails::Ethernet { - port: mavlink::DEFAULT_ETHERNET_PORT, - } - } -} - -#[derive(Debug, Default)] -struct SourceWindow { - visible: bool, - connected: bool, - connection_kind: ConnectionKind, - connection_details: ConnectionDetails, -} - -impl SourceWindow { - fn show_window(&mut self, ui: &mut egui::Ui) { - let mut window_is_open = self.visible; - let mut can_be_closed = false; - egui::Window::new("Sources") - .id(ui.id()) -<<<<<<< HEAD - .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) - .max_width(200.0) - .collapsible(false) - .resizable(false) -======= - .auto_sized() - .collapsible(false) - .movable(false) - .anchor(Align2::CENTER_CENTER, (0.0, 0.0)) ->>>>>>> 9dc29a7 ([StatusBar] added right side (action bar) with icons instead of text) - .open(&mut window_is_open) - .show(ui.ctx(), |ui| { - self.ui(ui, &mut can_be_closed); - }); - self.visible = window_is_open && !can_be_closed; - } - - fn ui(&mut self, ui: &mut egui::Ui, can_be_closed: &mut bool) { - let SourceWindow { - connected, - connection_kind, - connection_details, - .. - } = self; - ui.label("Select Source:"); - ui.horizontal_top(|ui| { - ui.radio_value(connection_kind, ConnectionKind::Ethernet, "Ethernet"); - ui.radio_value(connection_kind, ConnectionKind::Serial, "Serial"); - }); - - ui.separator(); - - match *connection_kind { - ConnectionKind::Ethernet => { - if !matches!(connection_details, ConnectionDetails::Ethernet { .. }) { - *connection_details = ConnectionDetails::Ethernet { - port: mavlink::DEFAULT_ETHERNET_PORT, - }; - } - let ConnectionDetails::Ethernet { port } = connection_details else { - error!("UNREACHABLE: Connection kind is not Ethernet"); - unreachable!("Connection kind is not Ethernet"); - }; - - egui::Grid::new("grid") - .num_columns(2) - .spacing([10.0, 5.0]) - .show(ui, |ui| { - ui.label("Ethernet Port:"); - ui.add(egui::DragValue::new(port).range(0..=65535).speed(10)); - ui.end_row(); - }); - } - ConnectionKind::Serial => { - if !matches!(connection_details, ConnectionDetails::Serial { .. }) { - *connection_details = ConnectionDetails::Serial { - // Default to the first STM32 serial port if available, otherwise - // default to the first serial port available - port: get_first_stm32_serial_port().unwrap_or( - list_all_serial_ports() - .ok() - .and_then(|ports| ports.first().cloned()) - .unwrap_or_default(), - ), - baud_rate: 115200, - }; - } - let ConnectionDetails::Serial { port, baud_rate } = connection_details else { - error!("UNREACHABLE: Connection kind is not Serial"); - unreachable!("Connection kind is not Serial"); - }; - - egui::Grid::new("grid") - .num_columns(2) - .spacing([10.0, 5.0]) - .show(ui, |ui| { - ui.label("Serial Port:"); - ComboBox::from_id_salt("serial_port") - .selected_text(port.clone()) - .show_ui(ui, |ui| { - for available_port in list_all_serial_ports().unwrap_or_default() { - ui.selectable_value( - port, - available_port.clone(), - available_port, - ); - } - }); - ui.end_row(); - ui.label("Baud Rate:"); - ui.add( - egui::DragValue::new(baud_rate) - .range(110..=256000) - .speed(100), - ); - ui.end_row(); - }); - } - }; - - ui.separator(); - - ui.allocate_ui(Vec2::new(ui.available_width(), 20.0), |ui| { - StripBuilder::new(ui) - .sizes(Size::remainder(), 2) // top cell - .horizontal(|mut strip| { - strip.cell(|ui| { - let btn1 = Button::new("Connect"); - ui.add_enabled_ui(!*connected, |ui| { - if ui.add_sized(ui.available_size(), btn1).clicked() { - match connection_details { - ConnectionDetails::Ethernet { port } => { - msg_broker!().listen_from_ethernet_port(*port); - } - ConnectionDetails::Serial { port, baud_rate } => { - msg_broker!() - .listen_from_serial_port(port.clone(), *baud_rate); - } - } - *can_be_closed = true; - *connected = true; - } - }); - }); - strip.cell(|ui| { - let btn2 = Button::new("Disconnect"); - ui.add_enabled_ui(*connected, |ui| { - if ui.add_sized(ui.available_size(), btn2).clicked() { - msg_broker!().stop_listening(); - *connected = false; - } - }); - }); - }); - }); - } -} - -/// Behavior for the tree of panes in the composable view -#[derive(Default)] -pub struct ComposableBehavior { - pub action: Option<PaneAction>, -} - -impl Behavior<Pane> for ComposableBehavior { - fn pane_ui( - &mut self, - ui: &mut egui::Ui, - tile_id: TileId, - pane: &mut Pane, - ) -> egui_tiles::UiResponse { - let PaneResponse { - action_called, - drag_response, - } = pane.ui(ui, tile_id); - // Capture the action and store it to be consumed in the update function - if let Some(action_called) = action_called { - self.action = Some(action_called); - } - drag_response - } - - fn tab_title_for_pane(&mut self, _pane: &Pane) -> egui::WidgetText { - "Tab".into() - } -} - -#[derive(Clone, Debug)] -pub struct PaneResponse { - pub action_called: Option<PaneAction>, - pub drag_response: egui_tiles::UiResponse, -} - -impl PaneResponse { - pub fn set_action(&mut self, action: PaneAction) { - self.action_called = Some(action); - } - - pub fn set_drag_started(&mut self) { - self.drag_response = egui_tiles::UiResponse::DragStarted; - } -} - -impl Default for PaneResponse { - fn default() -> Self { - Self { - action_called: None, - drag_response: egui_tiles::UiResponse::None, - } - } -} - -#[derive(Clone, Debug)] -pub enum PaneAction { - SplitH, - SplitV, - Close, - Replace(TileId, Box<Pane>), - ReplaceThroughGallery(Option<TileId>), - Maximize, - Exit, -} diff --git a/src/ui/composable_view_BASE_21160.rs b/src/ui/composable_view_BASE_21160.rs deleted file mode 100644 index 8ba208a0713d8adf3f48efe560bd5cc48d8c49f9..0000000000000000000000000000000000000000 --- a/src/ui/composable_view_BASE_21160.rs +++ /dev/null @@ -1,318 +0,0 @@ -use crate::{error::ErrInstrument, mavlink, msg_broker}; - -use super::{ - panes::{Pane, PaneBehavior}, - persistency::{LayoutManager, LayoutManagerWindow}, - shortcuts, -}; -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use egui::{Key, Modifiers}; -use egui_tiles::{Behavior, Container, Linear, LinearDir, Tile, TileId, Tiles, Tree}; -use serde::{Deserialize, Serialize}; -use tracing::{debug, error, trace}; - -#[derive(Default)] -pub struct ComposableView { - /// Persistent state of the app - state: ComposableViewState, - layout_manager: LayoutManager, - behavior: ComposableBehavior, - - // == Windows == - sources_window: SourceWindow, - layout_manager_window: LayoutManagerWindow, -} - -// An app must implement the `App` trait to define how the ui is built -impl eframe::App for ComposableView { - // The update function is called each time the UI needs repainting! - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - let panes_tree = &mut self.state.panes_tree; - - // Get the id of the hovered pane, in order to apply actions to it - let hovered_pane = panes_tree - .tiles - .iter() - .find(|(_, tile)| matches!(tile, Tile::Pane(pane) if pane.contains_pointer())) - .map(|(id, _)| *id); - trace!("Hovered pane: {:?}", hovered_pane); - - // Capture any pane action generated by pane children - let pane_action = self.behavior.action.take(); - let mut pane_action = pane_action.zip(hovered_pane); - trace!("Pane action: {:?}", pane_action); - - // Capture any pane action generated by keyboard shortcuts - if let Some(hovered_pane) = hovered_pane { - let key_action_pairs = [ - ((Modifiers::NONE, Key::V), PaneAction::SplitV), - ((Modifiers::NONE, Key::H), PaneAction::SplitH), - ((Modifiers::NONE, Key::C), PaneAction::Close), - ]; - pane_action = pane_action.or(shortcuts::map_to_action(ctx, &key_action_pairs[..]) - .map(|action| (action, hovered_pane))); - } - - // If an action was triggered, we consume it - if let Some((action, hovered_tile)) = pane_action.take() { - match action { - PaneAction::SplitH => { - debug!("Called SplitH on tile {:?}", hovered_tile); - let hovered_tile_pane = panes_tree - .tiles - .remove(hovered_tile) - .log_expect("Hovered tile not found"); - let left_pane = panes_tree.tiles.insert_new(hovered_tile_pane); - let right_pane = panes_tree.tiles.insert_pane(Pane::default()); - panes_tree.tiles.insert( - hovered_tile, - Tile::Container(Container::Linear(Linear::new_binary( - LinearDir::Horizontal, - [left_pane, right_pane], - 0.5, - ))), - ); - } - PaneAction::SplitV => { - debug!("Called SplitV on tile {:?}", hovered_tile); - let hovered_tile_pane = panes_tree - .tiles - .remove(hovered_tile) - .log_expect("Hovered tile not found"); - let replaced = panes_tree.tiles.insert_new(hovered_tile_pane); - let lower_pane = panes_tree.tiles.insert_pane(Pane::default()); - panes_tree.tiles.insert( - hovered_tile, - Tile::Container(Container::Linear(Linear::new_binary( - LinearDir::Vertical, - [replaced, lower_pane], - 0.5, - ))), - ); - } - PaneAction::Close => { - debug!("Called Close on tile {:?}", hovered_tile); - // Ignore if the root pane is the only one - if panes_tree.tiles.len() != 1 { - panes_tree.remove_recursively(hovered_tile); - } - } - PaneAction::Replace(new_pane) => { - debug!( - "Called Replace on tile {:?} with pane {:?}", - hovered_tile, new_pane - ); - panes_tree.tiles.insert(hovered_tile, Tile::Pane(*new_pane)); - } - } - } - - // Show a panel at the bottom of the screen with few global controls - egui::TopBottomPanel::bottom("bottom_control").show(ctx, |ui| { - // Horizontal belt of controls - ui.horizontal(|ui| { - egui::global_theme_preference_switch(ui); - - // Window for the sources - self.sources_window.show_window(ui); - - if ui.button("Sources").clicked() { - self.sources_window.visible = !self.sources_window.visible; - } - if ui.button("Layout Manager").clicked() { - self.layout_manager_window - .toggle_open_state(&self.layout_manager); - } - }) - }); - - // A central panel covers the remainder of the screen, i.e. whatever area is left after adding other panels. - egui::CentralPanel::default().show(ctx, |ui| { - panes_tree.ui(&mut self.behavior, ui); - }); - - self.layout_manager_window - .show(ctx, &mut self.layout_manager, &mut self.state); - } - - fn save(&mut self, storage: &mut dyn eframe::Storage) { - self.layout_manager.save_current_layout(storage); - } -} - -impl ComposableView { - pub fn new(app_name: &str, storage: &dyn eframe::Storage) -> Self { - let layout_manager = LayoutManager::new(app_name, storage); - let mut s = Self { - layout_manager, - ..Self::default() - }; - // Load the selected layout if valid and existing - if let Some(layout) = s.layout_manager.current_layout().cloned() { - s.layout_manager - .load_layout(layout, &mut s.state) - .unwrap_or_else(|e| { - error!("Error loading layout: {}", e); - }); - } - s - } -} - -#[derive(Serialize, Deserialize, Clone, PartialEq)] -pub struct ComposableViewState { - pub panes_tree: Tree<Pane>, -} - -impl Default for ComposableViewState { - fn default() -> Self { - let mut tiles = Tiles::default(); - let root = tiles.insert_pane(Pane::default()); - let panes_tree = egui_tiles::Tree::new("main_tree", root, tiles); - - Self { panes_tree } - } -} - -impl ComposableViewState { - pub fn from_file(path: &PathBuf) -> anyhow::Result<Self> { - fs::read_to_string(path) - .and_then(|json| serde_json::from_str::<ComposableViewState>(&json).map_err(Into::into)) - .map_err(|e| anyhow::anyhow!("Error deserializing layout: {}", e)) - } - - pub fn to_file(&self, path: &Path) -> anyhow::Result<()> { - // Check if the parent path exists, if not create it - if let Some(parent) = path.parent() { - if !parent.exists() { - fs::create_dir_all(parent) - .map_err(|e| anyhow::anyhow!("Error creating directory: {}", e))?; - debug!("Created directory {:?}", parent); - } - } - - let serialized_layout = serde_json::to_string_pretty(self) - .map_err(|e| anyhow::anyhow!("Error serializing layout: {}", e))?; - debug!("Serialized layout: {}", serialized_layout); - fs::write(path, serialized_layout) - .map_err(|e| anyhow::anyhow!("Error writing layout: {}", e))?; - - Ok(()) - } -} - -struct SourceWindow { - port: u16, - visible: bool, -} - -impl Default for SourceWindow { - fn default() -> Self { - Self { - port: mavlink::DEFAULT_ETHERNET_PORT, - visible: false, - } - } -} - -impl SourceWindow { - fn show_window(&mut self, ui: &mut egui::Ui) { - let mut window_is_open = self.visible; - let mut can_be_closed = false; - egui::Window::new("Sources") - .id(ui.id()) - .auto_sized() - .collapsible(true) - .movable(true) - .open(&mut window_is_open) - .show(ui.ctx(), |ui| { - self.ui(ui, &mut can_be_closed); - }); - self.visible = window_is_open && !can_be_closed; - } - - fn ui(&mut self, ui: &mut egui::Ui, can_be_closed: &mut bool) { - egui::Grid::new(ui.id()) - .num_columns(2) - .spacing([10.0, 5.0]) - .show(ui, |ui| { - ui.label("Ethernet Port:"); - ui.add( - egui::DragValue::new(&mut self.port) - .range(0..=65535) - .speed(10), - ); - ui.end_row(); - }); - if ui.button("Connect").clicked() { - msg_broker!().listen_from_ethernet_port(self.port); - *can_be_closed = true; - } - } -} - -/// Behavior for the tree of panes in the composable view -#[derive(Default)] -pub struct ComposableBehavior { - pub action: Option<PaneAction>, -} - -impl Behavior<Pane> for ComposableBehavior { - fn pane_ui( - &mut self, - ui: &mut egui::Ui, - _tile_id: TileId, - pane: &mut Pane, - ) -> egui_tiles::UiResponse { - let PaneResponse { - action_called, - drag_response, - } = pane.ui(ui); - // Capture the action and store it to be consumed in the update function - if let Some(action_called) = action_called { - self.action = Some(action_called); - } - drag_response - } - - fn tab_title_for_pane(&mut self, _pane: &Pane) -> egui::WidgetText { - "Tab".into() - } -} - -#[derive(Clone, Debug)] -pub struct PaneResponse { - pub action_called: Option<PaneAction>, - pub drag_response: egui_tiles::UiResponse, -} - -impl PaneResponse { - pub fn set_action(&mut self, action: PaneAction) { - self.action_called = Some(action); - } - - pub fn set_drag_started(&mut self) { - self.drag_response = egui_tiles::UiResponse::DragStarted; - } -} - -impl Default for PaneResponse { - fn default() -> Self { - Self { - action_called: None, - drag_response: egui_tiles::UiResponse::None, - } - } -} - -#[derive(Clone, Debug)] -pub enum PaneAction { - SplitH, - SplitV, - Close, - Replace(Box<Pane>), -} diff --git a/src/ui/composable_view_LOCAL_21160.rs b/src/ui/composable_view_LOCAL_21160.rs deleted file mode 100644 index 743f366a8301290a22b5ebcf1054a562b5c921f9..0000000000000000000000000000000000000000 --- a/src/ui/composable_view_LOCAL_21160.rs +++ /dev/null @@ -1,511 +0,0 @@ -use crate::{ - error::ErrInstrument, - mavlink, msg_broker, - serial::{get_first_stm32_serial_port, list_all_serial_ports}, - ui::panes::PaneKind, -}; - -use super::{ - panes::{Pane, PaneBehavior}, - persistency::{LayoutManager, LayoutManagerWindow}, - shortcuts, - utils::maximized_pane_ui, - widget_gallery::WidgetGallery, -}; -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use egui::{Align2, Button, ComboBox, Key, Modifiers, Vec2}; -use egui_extras::{Size, StripBuilder}; -use egui_tiles::{Behavior, Container, Linear, LinearDir, Tile, TileId, Tiles, Tree}; -use serde::{Deserialize, Serialize}; -use tracing::{debug, error, trace}; - -#[derive(Default)] -pub struct ComposableView { - /// Persistent state of the app - state: ComposableViewState, - layout_manager: LayoutManager, - widget_gallery: WidgetGallery, - behavior: ComposableBehavior, - maximized_pane: Option<TileId>, - - // == Windows == - sources_window: SourceWindow, - layout_manager_window: LayoutManagerWindow, -} - -// An app must implement the `App` trait to define how the ui is built -impl eframe::App for ComposableView { - // The update function is called each time the UI needs repainting! - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - let panes_tree = &mut self.state.panes_tree; - - // Get the id of the hovered pane, in order to apply actions to it - let hovered_pane = panes_tree - .tiles - .iter() - .find(|(_, tile)| matches!(tile, Tile::Pane(pane) if pane.contains_pointer())) - .map(|(id, _)| *id); - trace!("Hovered pane: {:?}", hovered_pane); - - // Capture any pane action generated by pane children - let mut pane_action = self.behavior.action.take(); - trace!("Pane action: {:?}", pane_action); - - if let Some(hovered_tile) = hovered_pane { - // Capture any pane action generated by keyboard shortcuts - let key_action_pairs = [ - ((Modifiers::NONE, Key::V), PaneAction::SplitV), - ((Modifiers::NONE, Key::H), PaneAction::SplitH), - ((Modifiers::NONE, Key::C), PaneAction::Close), - ( - (Modifiers::NONE, Key::R), - PaneAction::ReplaceThroughGallery(Some(hovered_tile)), - ), - ((Modifiers::SHIFT, Key::Escape), PaneAction::Maximize), - ((Modifiers::NONE, Key::Escape), PaneAction::Exit), - ]; - pane_action = pane_action.or(shortcuts::map_to_action(ctx, &key_action_pairs[..])); - } - - // If an action was triggered, we consume it - if let Some(action) = pane_action.take() { - match action { - PaneAction::SplitH => { - if let Some(hovered_tile) = hovered_pane { - if self.maximized_pane.is_none() { - debug!("Called SplitH on tile {:?}", hovered_tile); - let hovered_tile_pane = panes_tree - .tiles - .remove(hovered_tile) - .log_expect("Hovered tile not found"); - let left_pane = panes_tree.tiles.insert_new(hovered_tile_pane); - let right_pane = panes_tree.tiles.insert_pane(Pane::default()); - panes_tree.tiles.insert( - hovered_tile, - Tile::Container(Container::Linear(Linear::new_binary( - LinearDir::Horizontal, - [left_pane, right_pane], - 0.5, - ))), - ); - } - } - } - PaneAction::SplitV => { - if self.maximized_pane.is_none() { - if let Some(hovered_tile) = hovered_pane { - debug!("Called SplitV on tile {:?}", hovered_tile); - let hovered_tile_pane = panes_tree - .tiles - .remove(hovered_tile) - .log_expect("Hovered tile not found"); - let replaced = panes_tree.tiles.insert_new(hovered_tile_pane); - let lower_pane = panes_tree.tiles.insert_pane(Pane::default()); - panes_tree.tiles.insert( - hovered_tile, - Tile::Container(Container::Linear(Linear::new_binary( - LinearDir::Vertical, - [replaced, lower_pane], - 0.5, - ))), - ); - } - } - } - PaneAction::Close => { - if let Some(hovered_tile) = hovered_pane { - debug!("Called Close on tile {:?}", hovered_tile); - // Ignore if the root pane is the only one - if panes_tree.tiles.len() != 1 && self.maximized_pane.is_none() { - panes_tree.remove_recursively(hovered_tile); - } - } - } - PaneAction::Replace(tile_id, new_pane) => { - debug!( - "Called Replace on tile {:?} with pane {:?}", - tile_id, new_pane - ); - panes_tree.tiles.insert(tile_id, Tile::Pane(*new_pane)); - } - PaneAction::ReplaceThroughGallery(Some(source_tile)) => { - self.widget_gallery.replace_tile(source_tile); - } - PaneAction::Maximize => { - // This is a toggle: if there is not currently a maximized pane, - // maximize the hovered pane, otherwize remove the maximized pane. - if self.maximized_pane.is_some() { - self.maximized_pane = None; - } else if let Some(hovered_tile) = hovered_pane { - let hovered_pane_is_default = panes_tree - .tiles - .get(hovered_tile) - .map(|hovered_pane| { - matches!( - hovered_pane, - Tile::Pane(Pane { - pane: PaneKind::Default(_), - }) - ) - }) - .unwrap_or(false); - if !hovered_pane_is_default { - self.maximized_pane = Some(hovered_tile); - } - } - } - PaneAction::Exit => { - if self.maximized_pane.is_some() { - self.maximized_pane = None; - } - } - _ => panic!("Unable to handle action"), - } - } - - // Show a panel at the bottom of the screen with few global controls - egui::TopBottomPanel::bottom("bottom_control").show(ctx, |ui| { - // Horizontal belt of controls - ui.horizontal(|ui| { - egui::global_theme_preference_switch(ui); - - // Window for the sources - self.sources_window.show_window(ui); - - if ui.button("Sources").clicked() { - self.sources_window.visible = !self.sources_window.visible; - } - if ui.button("Layout Manager").clicked() { - self.layout_manager_window - .toggle_open_state(&self.layout_manager); - } - - // If a pane is maximized show a visual clue - if self.maximized_pane.is_some() { - ui.label("Pane Maximized!"); - } - }) - }); - - // A central panel covers the remainder of the screen, i.e. whatever area is left after adding other panels. - egui::CentralPanel::default().show(ctx, |ui| { - if let Some(maximized_pane) = self.maximized_pane { - if let Some(Tile::Pane(pane)) = panes_tree.tiles.get_mut(maximized_pane) { - maximized_pane_ui(ui, maximized_pane, pane); - } else { - panic!("Maximized pane not found in tree!"); - } - } else { - panes_tree.ui(&mut self.behavior, ui); - } - }); - - self.layout_manager_window - .show(ctx, &mut self.layout_manager, &mut self.state); - if let Some(action) = self.widget_gallery.show(ctx) { - debug!("Widget gallery returned action {action:?}"); - self.behavior.action = Some(action); - } - } - - fn save(&mut self, storage: &mut dyn eframe::Storage) { - self.layout_manager.save_current_layout(storage); - } -} - -impl ComposableView { - pub fn new(app_name: &str, storage: &dyn eframe::Storage) -> Self { - let layout_manager = LayoutManager::new(app_name, storage); - let mut s = Self { - layout_manager, - ..Self::default() - }; - // Load the selected layout if valid and existing - if let Some(layout) = s.layout_manager.current_layout().cloned() { - s.layout_manager - .load_layout(layout, &mut s.state) - .unwrap_or_else(|e| { - error!("Error loading layout: {}", e); - }); - } - s - } -} - -#[derive(Serialize, Deserialize, Clone, PartialEq)] -pub struct ComposableViewState { - pub panes_tree: Tree<Pane>, -} - -impl Default for ComposableViewState { - fn default() -> Self { - let mut tiles = Tiles::default(); - let root = tiles.insert_pane(Pane::default()); - let panes_tree = egui_tiles::Tree::new("main_tree", root, tiles); - - Self { panes_tree } - } -} - -impl ComposableViewState { - pub fn from_file(path: &PathBuf) -> anyhow::Result<Self> { - fs::read_to_string(path) - .and_then(|json| serde_json::from_str::<ComposableViewState>(&json).map_err(Into::into)) - .map_err(|e| anyhow::anyhow!("Error deserializing layout: {}", e)) - } - - pub fn to_file(&self, path: &Path) -> anyhow::Result<()> { - // Check if the parent path exists, if not create it - if let Some(parent) = path.parent() { - if !parent.exists() { - fs::create_dir_all(parent) - .map_err(|e| anyhow::anyhow!("Error creating directory: {}", e))?; - debug!("Created directory {:?}", parent); - } - } - - let serialized_layout = serde_json::to_string_pretty(self) - .map_err(|e| anyhow::anyhow!("Error serializing layout: {}", e))?; - debug!("Serialized layout: {}", serialized_layout); - fs::write(path, serialized_layout) - .map_err(|e| anyhow::anyhow!("Error writing layout: {}", e))?; - - Ok(()) - } -} - -#[derive(Debug, PartialEq, Eq, Default)] -enum ConnectionKind { - #[default] - Ethernet, - Serial, -} - -#[derive(Debug)] -enum ConnectionDetails { - Ethernet { port: u16 }, - Serial { port: String, baud_rate: u32 }, -} - -impl Default for ConnectionDetails { - fn default() -> Self { - ConnectionDetails::Ethernet { - port: mavlink::DEFAULT_ETHERNET_PORT, - } - } -} - -#[derive(Debug, Default)] -struct SourceWindow { - visible: bool, - connected: bool, - connection_kind: ConnectionKind, - connection_details: ConnectionDetails, -} - -impl SourceWindow { - fn show_window(&mut self, ui: &mut egui::Ui) { - let mut window_is_open = self.visible; - let mut can_be_closed = false; - egui::Window::new("Sources") - .id(ui.id()) - .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) - .max_width(200.0) - .collapsible(false) - .resizable(false) - .open(&mut window_is_open) - .show(ui.ctx(), |ui| { - self.ui(ui, &mut can_be_closed); - }); - self.visible = window_is_open && !can_be_closed; - } - - fn ui(&mut self, ui: &mut egui::Ui, can_be_closed: &mut bool) { - let SourceWindow { - connected, - connection_kind, - connection_details, - .. - } = self; - ui.label("Select Source:"); - ui.horizontal_top(|ui| { - ui.radio_value(connection_kind, ConnectionKind::Ethernet, "Ethernet"); - ui.radio_value(connection_kind, ConnectionKind::Serial, "Serial"); - }); - - ui.separator(); - - match *connection_kind { - ConnectionKind::Ethernet => { - if !matches!(connection_details, ConnectionDetails::Ethernet { .. }) { - *connection_details = ConnectionDetails::Ethernet { - port: mavlink::DEFAULT_ETHERNET_PORT, - }; - } - let ConnectionDetails::Ethernet { port } = connection_details else { - error!("UNREACHABLE: Connection kind is not Ethernet"); - unreachable!("Connection kind is not Ethernet"); - }; - - egui::Grid::new("grid") - .num_columns(2) - .spacing([10.0, 5.0]) - .show(ui, |ui| { - ui.label("Ethernet Port:"); - ui.add(egui::DragValue::new(port).range(0..=65535).speed(10)); - ui.end_row(); - }); - } - ConnectionKind::Serial => { - if !matches!(connection_details, ConnectionDetails::Serial { .. }) { - *connection_details = ConnectionDetails::Serial { - // Default to the first STM32 serial port if available, otherwise - // default to the first serial port available - port: get_first_stm32_serial_port().unwrap_or( - list_all_serial_ports() - .ok() - .and_then(|ports| ports.first().cloned()) - .unwrap_or_default(), - ), - baud_rate: 115200, - }; - } - let ConnectionDetails::Serial { port, baud_rate } = connection_details else { - error!("UNREACHABLE: Connection kind is not Serial"); - unreachable!("Connection kind is not Serial"); - }; - - egui::Grid::new("grid") - .num_columns(2) - .spacing([10.0, 5.0]) - .show(ui, |ui| { - ui.label("Serial Port:"); - ComboBox::from_id_salt("serial_port") - .selected_text(port.clone()) - .show_ui(ui, |ui| { - for available_port in list_all_serial_ports().unwrap_or_default() { - ui.selectable_value( - port, - available_port.clone(), - available_port, - ); - } - }); - ui.end_row(); - ui.label("Baud Rate:"); - ui.add( - egui::DragValue::new(baud_rate) - .range(110..=256000) - .speed(100), - ); - ui.end_row(); - }); - } - }; - - ui.separator(); - - ui.allocate_ui(Vec2::new(ui.available_width(), 20.0), |ui| { - StripBuilder::new(ui) - .sizes(Size::remainder(), 2) // top cell - .horizontal(|mut strip| { - strip.cell(|ui| { - let btn1 = Button::new("Connect"); - ui.add_enabled_ui(!*connected, |ui| { - if ui.add_sized(ui.available_size(), btn1).clicked() { - match connection_details { - ConnectionDetails::Ethernet { port } => { - msg_broker!().listen_from_ethernet_port(*port); - } - ConnectionDetails::Serial { port, baud_rate } => { - msg_broker!() - .listen_from_serial_port(port.clone(), *baud_rate); - } - } - *can_be_closed = true; - *connected = true; - } - }); - }); - strip.cell(|ui| { - let btn2 = Button::new("Disconnect"); - ui.add_enabled_ui(*connected, |ui| { - if ui.add_sized(ui.available_size(), btn2).clicked() { - msg_broker!().stop_listening(); - *connected = false; - } - }); - }); - }); - }); - } -} - -/// Behavior for the tree of panes in the composable view -#[derive(Default)] -pub struct ComposableBehavior { - pub action: Option<PaneAction>, -} - -impl Behavior<Pane> for ComposableBehavior { - fn pane_ui( - &mut self, - ui: &mut egui::Ui, - tile_id: TileId, - pane: &mut Pane, - ) -> egui_tiles::UiResponse { - let PaneResponse { - action_called, - drag_response, - } = pane.ui(ui, tile_id); - // Capture the action and store it to be consumed in the update function - if let Some(action_called) = action_called { - self.action = Some(action_called); - } - drag_response - } - - fn tab_title_for_pane(&mut self, _pane: &Pane) -> egui::WidgetText { - "Tab".into() - } -} - -#[derive(Clone, Debug)] -pub struct PaneResponse { - pub action_called: Option<PaneAction>, - pub drag_response: egui_tiles::UiResponse, -} - -impl PaneResponse { - pub fn set_action(&mut self, action: PaneAction) { - self.action_called = Some(action); - } - - pub fn set_drag_started(&mut self) { - self.drag_response = egui_tiles::UiResponse::DragStarted; - } -} - -impl Default for PaneResponse { - fn default() -> Self { - Self { - action_called: None, - drag_response: egui_tiles::UiResponse::None, - } - } -} - -#[derive(Clone, Debug)] -pub enum PaneAction { - SplitH, - SplitV, - Close, - Replace(TileId, Box<Pane>), - ReplaceThroughGallery(Option<TileId>), - Maximize, - Exit, -} diff --git a/src/ui/composable_view_REMOTE_21160.rs b/src/ui/composable_view_REMOTE_21160.rs deleted file mode 100644 index 1aa41512ec66494079001e1ac4ba61b9b4e66fc3..0000000000000000000000000000000000000000 --- a/src/ui/composable_view_REMOTE_21160.rs +++ /dev/null @@ -1,336 +0,0 @@ -use crate::{error::ErrInstrument, mavlink, msg_broker}; - -use super::{ - panes::{Pane, PaneBehavior}, - persistency::{LayoutManager, LayoutManagerWindow}, - shortcuts, -}; -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use egui::{Align2, Button, Key, Modifiers, Sides}; -use egui_extras::{Size, StripBuilder}; -use egui_tiles::{Behavior, Container, Linear, LinearDir, Tile, TileId, Tiles, Tree}; -use serde::{Deserialize, Serialize}; -use tracing::{debug, error, trace}; - -#[derive(Default)] -pub struct ComposableView { - /// Persistent state of the app - state: ComposableViewState, - layout_manager: LayoutManager, - behavior: ComposableBehavior, - - // == Windows == - sources_window: SourceWindow, - layout_manager_window: LayoutManagerWindow, -} - -// An app must implement the `App` trait to define how the ui is built -impl eframe::App for ComposableView { - // The update function is called each time the UI needs repainting! - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - let panes_tree = &mut self.state.panes_tree; - - // Get the id of the hovered pane, in order to apply actions to it - let hovered_pane = panes_tree - .tiles - .iter() - .find(|(_, tile)| matches!(tile, Tile::Pane(pane) if pane.contains_pointer())) - .map(|(id, _)| *id); - trace!("Hovered pane: {:?}", hovered_pane); - - // Capture any pane action generated by pane children - let pane_action = self.behavior.action.take(); - let mut pane_action = pane_action.zip(hovered_pane); - trace!("Pane action: {:?}", pane_action); - - // Capture any pane action generated by keyboard shortcuts - if let Some(hovered_pane) = hovered_pane { - let key_action_pairs = [ - ((Modifiers::NONE, Key::V), PaneAction::SplitV), - ((Modifiers::NONE, Key::H), PaneAction::SplitH), - ((Modifiers::NONE, Key::C), PaneAction::Close), - ]; - pane_action = pane_action.or(shortcuts::map_to_action(ctx, &key_action_pairs[..]) - .map(|action| (action, hovered_pane))); - } - - // If an action was triggered, we consume it - if let Some((action, hovered_tile)) = pane_action.take() { - match action { - PaneAction::SplitH => { - debug!("Called SplitH on tile {:?}", hovered_tile); - let hovered_tile_pane = panes_tree - .tiles - .remove(hovered_tile) - .log_expect("Hovered tile not found"); - let left_pane = panes_tree.tiles.insert_new(hovered_tile_pane); - let right_pane = panes_tree.tiles.insert_pane(Pane::default()); - panes_tree.tiles.insert( - hovered_tile, - Tile::Container(Container::Linear(Linear::new_binary( - LinearDir::Horizontal, - [left_pane, right_pane], - 0.5, - ))), - ); - } - PaneAction::SplitV => { - debug!("Called SplitV on tile {:?}", hovered_tile); - let hovered_tile_pane = panes_tree - .tiles - .remove(hovered_tile) - .log_expect("Hovered tile not found"); - let replaced = panes_tree.tiles.insert_new(hovered_tile_pane); - let lower_pane = panes_tree.tiles.insert_pane(Pane::default()); - panes_tree.tiles.insert( - hovered_tile, - Tile::Container(Container::Linear(Linear::new_binary( - LinearDir::Vertical, - [replaced, lower_pane], - 0.5, - ))), - ); - } - PaneAction::Close => { - debug!("Called Close on tile {:?}", hovered_tile); - // Ignore if the root pane is the only one - if panes_tree.tiles.len() != 1 { - panes_tree.remove_recursively(hovered_tile); - } - } - PaneAction::Replace(new_pane) => { - debug!( - "Called Replace on tile {:?} with pane {:?}", - hovered_tile, new_pane - ); - panes_tree.tiles.insert(hovered_tile, Tile::Pane(*new_pane)); - } - } - } - - // Show a panel at the bottom of the screen with few global controls - egui::TopBottomPanel::bottom("bottom_control").show(ctx, |ui| { - // Horizontal belt of controls - Sides::new().show( - ui, - |ui| { - ui.label("Informative side here!"); - }, - |ui| { - ui.horizontal(|ui| { - egui::global_theme_preference_switch(ui); - - // Window for the sources - self.sources_window.show_window(ui); - - if ui - .add(Button::new("🔌").frame(false)) - .on_hover_text("Open the Sources") - .clicked() - { - self.sources_window.visible = !self.sources_window.visible; - } - if ui - .add(Button::new("💾").frame(false)) - .on_hover_text("Open the Layout Manager") - .clicked() - { - self.layout_manager_window - .toggle_open_state(&self.layout_manager); - } - }); - }, - ); - }); - - // A central panel covers the remainder of the screen, i.e. whatever area is left after adding other panels. - egui::CentralPanel::default().show(ctx, |ui| { - panes_tree.ui(&mut self.behavior, ui); - }); - - self.layout_manager_window - .show(ctx, &mut self.layout_manager, &mut self.state); - } - - fn save(&mut self, storage: &mut dyn eframe::Storage) { - self.layout_manager.save_current_layout(storage); - } -} - -impl ComposableView { - pub fn new(app_name: &str, storage: &dyn eframe::Storage) -> Self { - let layout_manager = LayoutManager::new(app_name, storage); - let mut s = Self { - layout_manager, - ..Self::default() - }; - // Load the selected layout if valid and existing - if let Some(layout) = s.layout_manager.current_layout().cloned() { - s.layout_manager - .load_layout(layout, &mut s.state) - .unwrap_or_else(|e| { - error!("Error loading layout: {}", e); - }); - } - s - } -} - -#[derive(Serialize, Deserialize, Clone, PartialEq)] -pub struct ComposableViewState { - pub panes_tree: Tree<Pane>, -} - -impl Default for ComposableViewState { - fn default() -> Self { - let mut tiles = Tiles::default(); - let root = tiles.insert_pane(Pane::default()); - let panes_tree = egui_tiles::Tree::new("main_tree", root, tiles); - - Self { panes_tree } - } -} - -impl ComposableViewState { - pub fn from_file(path: &PathBuf) -> anyhow::Result<Self> { - fs::read_to_string(path) - .and_then(|json| serde_json::from_str::<ComposableViewState>(&json).map_err(Into::into)) - .map_err(|e| anyhow::anyhow!("Error deserializing layout: {}", e)) - } - - pub fn to_file(&self, path: &Path) -> anyhow::Result<()> { - // Check if the parent path exists, if not create it - if let Some(parent) = path.parent() { - if !parent.exists() { - fs::create_dir_all(parent) - .map_err(|e| anyhow::anyhow!("Error creating directory: {}", e))?; - debug!("Created directory {:?}", parent); - } - } - - let serialized_layout = serde_json::to_string_pretty(self) - .map_err(|e| anyhow::anyhow!("Error serializing layout: {}", e))?; - debug!("Serialized layout: {}", serialized_layout); - fs::write(path, serialized_layout) - .map_err(|e| anyhow::anyhow!("Error writing layout: {}", e))?; - - Ok(()) - } -} - -struct SourceWindow { - port: u16, - visible: bool, -} - -impl Default for SourceWindow { - fn default() -> Self { - Self { - port: mavlink::DEFAULT_ETHERNET_PORT, - visible: false, - } - } -} - -impl SourceWindow { - fn show_window(&mut self, ui: &mut egui::Ui) { - let mut window_is_open = self.visible; - let mut can_be_closed = false; - egui::Window::new("Sources") - .id(ui.id()) - .auto_sized() - .collapsible(false) - .movable(false) - .anchor(Align2::CENTER_CENTER, (0.0, 0.0)) - .open(&mut window_is_open) - .show(ui.ctx(), |ui| { - self.ui(ui, &mut can_be_closed); - }); - self.visible = window_is_open && !can_be_closed; - } - - fn ui(&mut self, ui: &mut egui::Ui, can_be_closed: &mut bool) { - egui::Grid::new(ui.id()) - .num_columns(2) - .spacing([10.0, 5.0]) - .show(ui, |ui| { - ui.label("Ethernet Port:"); - ui.add( - egui::DragValue::new(&mut self.port) - .range(0..=65535) - .speed(10), - ); - ui.end_row(); - }); - if ui.button("Connect").clicked() { - msg_broker!().listen_from_ethernet_port(self.port); - *can_be_closed = true; - } - } -} - -/// Behavior for the tree of panes in the composable view -#[derive(Default)] -pub struct ComposableBehavior { - pub action: Option<PaneAction>, -} - -impl Behavior<Pane> for ComposableBehavior { - fn pane_ui( - &mut self, - ui: &mut egui::Ui, - _tile_id: TileId, - pane: &mut Pane, - ) -> egui_tiles::UiResponse { - let PaneResponse { - action_called, - drag_response, - } = pane.ui(ui); - // Capture the action and store it to be consumed in the update function - if let Some(action_called) = action_called { - self.action = Some(action_called); - } - drag_response - } - - fn tab_title_for_pane(&mut self, _pane: &Pane) -> egui::WidgetText { - "Tab".into() - } -} - -#[derive(Clone, Debug)] -pub struct PaneResponse { - pub action_called: Option<PaneAction>, - pub drag_response: egui_tiles::UiResponse, -} - -impl PaneResponse { - pub fn set_action(&mut self, action: PaneAction) { - self.action_called = Some(action); - } - - pub fn set_drag_started(&mut self) { - self.drag_response = egui_tiles::UiResponse::DragStarted; - } -} - -impl Default for PaneResponse { - fn default() -> Self { - Self { - action_called: None, - drag_response: egui_tiles::UiResponse::None, - } - } -} - -#[derive(Clone, Debug)] -pub enum PaneAction { - SplitH, - SplitV, - Close, - Replace(Box<Pane>), -}