From 48ef38ffd35a49149bbe015dfe615f1f78e52472 Mon Sep 17 00:00:00 2001 From: Andrew Mackenzie Date: Tue, 28 Jan 2025 18:57:09 +0100 Subject: [PATCH 1/2] First pass at a reduced view --- porky/src/porky.rs | 5 +- src/file_helper.rs | 25 +++++---- src/hw/driver.rs | 12 ++--- src/hw/hardware_description.rs | 11 +--- src/hw_definition/description.rs | 30 ++++++++++- src/piggui.rs | 8 +-- src/views/hardware_view.rs | 43 ++++++++++++++-- src/views/layout_menu.rs | 88 +++++++++++++++++++------------- 8 files changed, 145 insertions(+), 77 deletions(-) diff --git a/porky/src/porky.rs b/porky/src/porky.rs index 785f38dd..d9dbb8f6 100644 --- a/porky/src/porky.rs +++ b/porky/src/porky.rs @@ -41,7 +41,6 @@ use embassy_rp::usb::InterruptHandler as USBInterruptHandler; use embassy_rp::watchdog::Watchdog; use embassy_sync::blocking_mutex::raw::{NoopRawMutex, ThreadModeRawMutex}; use embassy_sync::channel::Channel; -use heapless::Vec; use panic_probe as _; use static_cell::StaticCell; @@ -124,9 +123,7 @@ fn hardware_description(serial: &str) -> HardwareDescription { HardwareDescription { details, - pins: PinDescriptionSet { - pins: Vec::from_slice(&PIN_DESCRIPTIONS).unwrap(), - }, + pins: PinDescriptionSet::new(&PIN_DESCRIPTIONS), } } diff --git a/src/file_helper.rs b/src/file_helper.rs index 56c4651d..e33a80a6 100644 --- a/src/file_helper.rs +++ b/src/file_helper.rs @@ -41,13 +41,13 @@ async fn save_via_picker(gpio_config: HardwareConfig) -> io::Result { if let Some(handle) = rfd::AsyncFileDialog::new() .add_filter("Pigg Config", &["pigg"]) .set_title("Choose file") - .set_directory(env::current_dir().unwrap()) + .set_directory(env::current_dir()?) .save_file() .await { let path: std::path::PathBuf = handle.path().to_owned(); let path_str = path.display().to_string(); - gpio_config.save(&path_str).unwrap(); + gpio_config.save(&path_str)?; Ok(true) } else { Ok(false) @@ -56,15 +56,18 @@ async fn save_via_picker(gpio_config: HardwareConfig) -> io::Result { /// Utility function that saves the [HardwareConfig] to a file using `Task::perform` and uses /// the result to return correct [Message] -pub fn save(gpio_config: HardwareConfig) -> Task { - Task::perform(save_via_picker(gpio_config), |result| match result { - Ok(true) => Message::ConfigSaved, - Ok(false) => Message::InfoRow(ShowStatusMessage(Info("File save cancelled".into()))), - Err(e) => Message::InfoRow(ShowStatusMessage(Error( - "Error saving file".into(), - format!("Error saving file. {e}"), - ))), - }) +pub fn save(gpio_config: &HardwareConfig) -> Task { + Task::perform( + save_via_picker(gpio_config.clone()), + |result| match result { + Ok(true) => Message::ConfigSaved, + Ok(false) => Message::InfoRow(ShowStatusMessage(Info("File save cancelled".into()))), + Err(e) => Message::InfoRow(ShowStatusMessage(Error( + "Error saving file".into(), + format!("Error saving file. {e}"), + ))), + }, + ) } /// Utility function that loads config from a file using `Task::perform` of the load picker diff --git a/src/hw/driver.rs b/src/hw/driver.rs index 3445fbb7..c01689d4 100644 --- a/src/hw/driver.rs +++ b/src/hw/driver.rs @@ -76,9 +76,7 @@ impl HW { pub fn description(&self) -> io::Result { Ok(HardwareDescription { details: Self::get_details()?, - pins: PinDescriptionSet { - pins: GPIO_PIN_DESCRIPTIONS.to_vec(), - }, + pins: PinDescriptionSet::new(&GPIO_PIN_DESCRIPTIONS), }) } @@ -459,12 +457,10 @@ mod test { pin7.clone(), pin7.clone(), ]; - let pin_set = PinDescriptionSet { - pins: pins.to_vec(), - }; + let pin_set = PinDescriptionSet::new(&pins); assert_eq!( pin_set - .pins + .pins() .first() .expect("Could not get pin") .bcm @@ -473,7 +469,7 @@ mod test { ); assert_eq!( pin_set - .pins + .pins() .get(1) .expect("Could not get pin") .bcm diff --git a/src/hw/hardware_description.rs b/src/hw/hardware_description.rs index dd8485ea..457445f6 100644 --- a/src/hw/hardware_description.rs +++ b/src/hw/hardware_description.rs @@ -1,8 +1,7 @@ +use crate::hw_definition::description::{HardwareDetails, PinDescription, PinDescriptionSet}; use std::fmt; use std::fmt::{Display, Formatter}; -use crate::hw_definition::description::{HardwareDetails, PinDescription, PinDescriptionSet}; - impl Display for HardwareDetails { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln!(f, "Application: {}", self.app_name)?; @@ -21,18 +20,12 @@ impl Display for HardwareDetails { /// `PinDescriptionSet` describes a set of Pins on a device, using `PinDescription`s impl PinDescriptionSet { - /// Return a slice of PinDescriptions - #[allow(dead_code)] // for piglet - pub fn pins(&self) -> &[PinDescription] { - &self.pins - } - /// Return a set of PinDescriptions *only** for pins that have BCM pin numbering, sorted in /// ascending order of [BCMPinNumber] #[allow(dead_code)] // for piglet build pub fn bcm_pins_sorted(&self) -> Vec<&PinDescription> { let mut pins = self - .pins + .pins() .iter() .filter(|pin| pin.bcm.is_some()) .collect::>(); diff --git a/src/hw_definition/description.rs b/src/hw_definition/description.rs index b003785d..3e394a17 100644 --- a/src/hw_definition/description.rs +++ b/src/hw_definition/description.rs @@ -122,10 +122,10 @@ pub struct WiFiDetails { #[cfg(not(feature = "no_std"))] /// [PinDescription] describes a pins in the connected hardware. -/// Array indexed from 0 so, index = board_pin_number -1, as pin numbering start at 1 +/// Vec indexed from 0 so, index = board_pin_number -1, as pin numbering start at 1 #[derive(Serialize, Debug, Clone, Deserialize)] pub struct PinDescriptionSet { - pub(crate) pins: Vec, + pins: Vec, } #[cfg(feature = "no_std")] @@ -136,6 +136,32 @@ pub struct PinDescriptionSet<'a> { pub pins: Vec, 40>, } +#[cfg(feature = "no_std")] +impl<'a> PinDescriptionSet<'a> { + /// Create a new [PinDescriptionSet] from a slice of pins + pub fn new(pin_slice: &'a [PinDescription]) -> Self { + Self { + pins: Vec::from_slice(pin_slice).unwrap(), + } + } +} + +#[cfg(not(feature = "no_std"))] +impl PinDescriptionSet { + /// Return a slice of PinDescriptions + #[allow(dead_code)] // for piglet + pub fn pins(&self) -> &[PinDescription] { + &self.pins + } + + /// Create a new [PinDescriptionSet] from a slice of pins + pub fn new(pin_slice: &[PinDescription]) -> Self { + Self { + pins: pin_slice.to_vec(), + } + } +} + #[cfg(not(feature = "no_std"))] /// [PinDescription] is used to describe each pin and possible uses it can be put to /// * [board_pin_number] refer to the pins by the number of the pin printed on the board diff --git a/src/piggui.rs b/src/piggui.rs index c7f5ee0a..873f6a08 100644 --- a/src/piggui.rs +++ b/src/piggui.rs @@ -212,11 +212,13 @@ impl Piggui { } } - LayoutChanged(layout) => { - let layout = self.layout_selector.update(layout); + LayoutChanged(new_layout) => { + let layout_size = self + .layout_selector + .update(new_layout, self.hardware_view.get_config()); return window::get_latest().then(move |latest| { if let Some(id) = latest { - window::resize(id, layout) + window::resize(id, layout_size) } else { Task::none() } diff --git a/src/views/hardware_view.rs b/src/views/hardware_view.rs index efaf4eb1..67536894 100644 --- a/src/views/hardware_view.rs +++ b/src/views/hardware_view.rs @@ -127,8 +127,8 @@ impl HardwareView { /// Get the current [HardwareConfig] #[must_use] - pub fn get_config(&self) -> HardwareConfig { - self.hardware_config.clone() + pub fn get_config(&self) -> &HardwareConfig { + &self.hardware_config } /// Get the current [HardwareConnection] @@ -297,8 +297,9 @@ impl HardwareView { if let Some(hw_description) = &self.hardware_description { let pin_layout = match layout { - Layout::BoardLayout => self.board_pin_layout_view(&hw_description.pins), - Layout::BCMLayout => self.bcm_pin_layout_view(&hw_description.pins), + Layout::Board => self.board_pin_layout_view(&hw_description.pins), + Layout::Logical => self.bcm_pin_layout_view(&hw_description.pins), + Layout::Reduced => self.reduced_layout_view(&hw_description.pins), }; return scrollable(pin_layout) @@ -370,6 +371,40 @@ impl HardwareView { .into() } + /// Reduced size view that only lays out configured pins + pub fn reduced_layout_view<'a>( + &'a self, + pin_set: &'a PinDescriptionSet, + ) -> Element<'a, HardwareViewMessage> { + let mut column = Column::new().width(Length::Shrink).height(Length::Shrink); + + for pin_description in pin_set.bcm_pins_sorted() { + if let Some(bcm_pin_number) = &pin_description.bcm { + if self + .hardware_config + .pin_functions + .contains_key(bcm_pin_number) + { + let pin_row = create_pin_view_side( + pin_description, + self.hardware_config + .pin_functions + .get(&pin_description.bcm.unwrap()), + Right, + self.pin_states.get(bcm_pin_number), + ); + + column = column.push(pin_row); + } + } + } + + column + .spacing(SPACE_BETWEEN_PIN_ROWS) + .align_x(Alignment::Start) + .into() + } + /// View that draws the pins laid out as they are on the physical Pi board pub fn board_pin_layout_view<'a>( &'a self, diff --git a/src/views/layout_menu.rs b/src/views/layout_menu.rs index 47c9cdfc..09504ef7 100644 --- a/src/views/layout_menu.rs +++ b/src/views/layout_menu.rs @@ -2,10 +2,12 @@ use crate::Message; use iced::widget::Button; use iced::{Length, Size}; +use crate::hw_definition::config::HardwareConfig; +use crate::views::hardware_styles::SPACE_BETWEEN_PIN_ROWS; use crate::views::hardware_view::HardwareConnection; use crate::views::hardware_view::HardwareConnection::NoConnection; use crate::views::info_row::{menu_bar_button, menu_button}; -use crate::views::layout_menu::Layout::{BCMLayout, BoardLayout}; +use crate::views::layout_menu::Layout::{Board, Logical, Reduced}; use iced::{Renderer, Theme}; use iced_aw::menu::{Item, Menu}; @@ -13,8 +15,9 @@ use iced_aw::menu::{Item, Menu}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum Layout { #[default] - BoardLayout, - BCMLayout, + Board, + Logical, + Reduced, } const BOARD_LAYOUT_SIZE: Size = Size { @@ -24,9 +27,18 @@ const BOARD_LAYOUT_SIZE: Size = Size { const BCM_LAYOUT_SIZE: Size = Size { width: 720.0, - height: 916.0, + height: 910.0, }; +// calculate the height required based on the number of configured pins +fn reduced_layout_size(hardware_config: &HardwareConfig) -> Size { + Size { + width: 720.0, + height: 28.0 + 28.0 /* InfoRow Height */ + + (hardware_config.pin_functions.len() as f32 * (28.0 + SPACE_BETWEEN_PIN_ROWS)), + } +} + #[derive(Clone, PartialEq, Default)] pub struct LayoutSelector { selected_layout: Layout, @@ -44,11 +56,12 @@ impl LayoutSelector { } /// Set the new layout as being selected and return the window size required - pub fn update(&mut self, new_layout: Layout) -> Size { + pub fn update(&mut self, new_layout: Layout, hardware_config: &HardwareConfig) -> Size { self.selected_layout = new_layout; match self.selected_layout { - BoardLayout => BOARD_LAYOUT_SIZE, - BCMLayout => BCM_LAYOUT_SIZE, + Board => BOARD_LAYOUT_SIZE, + Logical => BCM_LAYOUT_SIZE, + Layout::Reduced => reduced_layout_size(hardware_config), } } @@ -64,36 +77,39 @@ impl LayoutSelector { ) -> Item<'a, Message, Theme, Renderer> { let mut menu_items: Vec> = vec![]; + let mut show_bcp_layout = Button::new("BCP Pin Layout") + .width(Length::Fill) + .style(menu_button); + if hardware_connection != &NoConnection && self.selected_layout != Logical { + show_bcp_layout = show_bcp_layout.on_press(Message::LayoutChanged(Logical)); + } + menu_items.push(Item::new(show_bcp_layout)); + + let mut show_physical_layout = Button::new("Board Pin Layout") + .width(Length::Fill) + .style(menu_button); + if hardware_connection != &NoConnection && self.selected_layout != Board { + show_physical_layout = show_physical_layout.on_press(Message::LayoutChanged(Board)); + } + menu_items.push(Item::new(show_physical_layout)); + + let mut show_reduced_layout = Button::new("Reduced Layout") + .width(Length::Fill) + .style(menu_button); + + if hardware_connection != &NoConnection && self.selected_layout != Reduced { + show_reduced_layout = show_reduced_layout.on_press(Message::LayoutChanged(Reduced)); + } + + menu_items.push(Item::new(show_reduced_layout)); + let button = match self.selected_layout { - BoardLayout => { - let mut show_bcp_layout = Button::new("BCP Pin Layout") - .width(Length::Fill) - .style(menu_button); - - if hardware_connection != &NoConnection { - show_bcp_layout = show_bcp_layout.on_press(Message::LayoutChanged(BCMLayout)); - } - menu_items.push(Item::new(show_bcp_layout)); - Button::new("layout: board") - } - BCMLayout => { - let mut show_physical_layout = Button::new("Board Pin Layout") - .width(Length::Fill) - .style(menu_button); - - if hardware_connection != &NoConnection { - show_physical_layout = - show_physical_layout.on_press(Message::LayoutChanged(BoardLayout)); - } - - menu_items.push(Item::new(show_physical_layout)); - Button::new("layout: bcp") - } - }; - - let button = button - .style(menu_bar_button) - .on_press(Message::MenuBarButtonClicked); // Needed for highlighting; + Board => Button::new("layout: board"), + Logical => Button::new("layout: bcp"), + Layout::Reduced => Button::new("layout: reduced"), + } + .style(menu_bar_button) + .on_press(Message::MenuBarButtonClicked); // Needed for highlighting; Item::with_menu(button, Menu::new(menu_items).width(135.0).offset(10.0)) } From 8d4f656854e234dabe0020c89265f3907c08cc2c Mon Sep 17 00:00:00 2001 From: Andrew Mackenzie Date: Tue, 28 Jan 2025 22:00:38 +0100 Subject: [PATCH 2/2] Define constants for heights --- src/piggui.rs | 2 ++ src/views/hardware_styles.rs | 2 ++ src/views/info_row.rs | 2 ++ src/views/layout_menu.rs | 12 +++++++----- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/piggui.rs b/src/piggui.rs index 873f6a08..66856689 100644 --- a/src/piggui.rs +++ b/src/piggui.rs @@ -53,6 +53,8 @@ mod widgets; const PIGGUI_ID: &str = "piggui"; +pub(crate) const WINDOW_TITLE_AREA_HEIGHT: f32 = 28.0; + /// These are the messages that Piggui responds to #[derive(Debug, Clone)] #[allow(clippy::large_enum_variant)] diff --git a/src/views/hardware_styles.rs b/src/views/hardware_styles.rs index ce598712..7f00ee1e 100644 --- a/src/views/hardware_styles.rs +++ b/src/views/hardware_styles.rs @@ -43,6 +43,8 @@ pub(crate) const SPACE_BETWEEN_PIN_COLUMNS: f32 = 10.0; pub(crate) const SPACE_BETWEEN_PIN_ROWS: f32 = 5.0; +pub(crate) const PIN_ROW_HEIGHT: f32 = 28.0; + const PIN_RADIUS: Radius = Radius { top_left: PIN_BUTTON_RADIUS, top_right: PIN_BUTTON_RADIUS, diff --git a/src/views/info_row.rs b/src/views/info_row.rs index d4dee0ff..174dbea2 100644 --- a/src/views/info_row.rs +++ b/src/views/info_row.rs @@ -19,6 +19,8 @@ use iced_futures::Subscription; #[cfg(feature = "discovery")] use std::collections::HashMap; +pub(crate) const INFO_ROW_HEIGHT: f32 = 28.0; + const MENU_BACKGROUND_COLOR: Color = Color::from_rgba(0.15, 0.15, 0.15, 1.0); const MENU_RADIUS: Radius = Radius { diff --git a/src/views/layout_menu.rs b/src/views/layout_menu.rs index 09504ef7..8997a779 100644 --- a/src/views/layout_menu.rs +++ b/src/views/layout_menu.rs @@ -1,12 +1,12 @@ -use crate::Message; +use crate::{Message, WINDOW_TITLE_AREA_HEIGHT}; use iced::widget::Button; use iced::{Length, Size}; use crate::hw_definition::config::HardwareConfig; -use crate::views::hardware_styles::SPACE_BETWEEN_PIN_ROWS; +use crate::views::hardware_styles::{PIN_ROW_HEIGHT, SPACE_BETWEEN_PIN_ROWS}; use crate::views::hardware_view::HardwareConnection; use crate::views::hardware_view::HardwareConnection::NoConnection; -use crate::views::info_row::{menu_bar_button, menu_button}; +use crate::views::info_row::{menu_bar_button, menu_button, INFO_ROW_HEIGHT}; use crate::views::layout_menu::Layout::{Board, Logical, Reduced}; use iced::{Renderer, Theme}; use iced_aw::menu::{Item, Menu}; @@ -34,8 +34,10 @@ const BCM_LAYOUT_SIZE: Size = Size { fn reduced_layout_size(hardware_config: &HardwareConfig) -> Size { Size { width: 720.0, - height: 28.0 + 28.0 /* InfoRow Height */ - + (hardware_config.pin_functions.len() as f32 * (28.0 + SPACE_BETWEEN_PIN_ROWS)), + height: WINDOW_TITLE_AREA_HEIGHT + + INFO_ROW_HEIGHT + + (hardware_config.pin_functions.len() as f32 + * (PIN_ROW_HEIGHT + SPACE_BETWEEN_PIN_ROWS)), } }