From 9bdeb4a022e7929c35b182312853a4590f25b6d2 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Tue, 20 Aug 2024 23:54:27 +0100 Subject: [PATCH 1/6] Define macros to avoid repetition in GObject subclass code. --- src/model/imp.rs | 100 ++++++++++---------------- src/model/mod.rs | 170 ++++++++++++++++---------------------------- src/row_data/imp.rs | 43 +++++------ src/row_data/mod.rs | 71 ++++++++---------- 4 files changed, 146 insertions(+), 238 deletions(-) diff --git a/src/model/imp.rs b/src/model/imp.rs index 33a251ec..028e4c5b 100644 --- a/src/model/imp.rs +++ b/src/model/imp.rs @@ -8,73 +8,47 @@ use crate::capture::{TrafficItem, DeviceItem}; use crate::row_data::{TrafficRowData, DeviceRowData}; use crate::tree_list_model::TreeListModel; -#[derive(Default)] -pub struct TrafficModel { - pub(super) tree: RefCell>>, -} - -#[derive(Default)] -pub struct DeviceModel { - pub(super) tree: RefCell>>, -} - -/// Basic declaration of our type for the GObject type system -#[glib::object_subclass] -impl ObjectSubclass for TrafficModel { - const NAME: &'static str = "TrafficModel"; - type Type = super::TrafficModel; - type Interfaces = (gio::ListModel,); -} -#[glib::object_subclass] -impl ObjectSubclass for DeviceModel { - const NAME: &'static str = "DeviceModel"; - type Type = super::DeviceModel; - type Interfaces = (gio::ListModel,); -} - -impl ObjectImpl for TrafficModel {} -impl ObjectImpl for DeviceModel {} - -impl ListModelImpl for TrafficModel { - fn item_type(&self) -> glib::Type { - TrafficRowData::static_type() - } - - fn n_items(&self) -> u32 { - match self.tree.borrow().as_ref() { - Some(tree) => tree.n_items(), - None => 0 +macro_rules! model { + ($model:ident, $item:ident, $row_data:ident) => { + #[derive(Default)] + pub struct $model { + pub(super) tree: RefCell>>, } - } - fn item(&self, position: u32) - -> Option - { - match self.tree.borrow().as_ref() { - Some(tree) => tree.item(position), - None => None + /// Basic declaration of our type for the GObject type system + #[glib::object_subclass] + impl ObjectSubclass for $model { + const NAME: &'static str = stringify!($model); + type Type = super::$model; + type Interfaces = (gio::ListModel,); } - } -} - -impl ListModelImpl for DeviceModel { - fn item_type(&self) -> glib::Type { - DeviceRowData::static_type() - } - fn n_items(&self) -> u32 { - match self.tree.borrow().as_ref() { - Some(tree) => tree.n_items(), - None => 0 - } - } - - fn item(&self, position: u32) - -> Option - { - match self.tree.borrow().as_ref() { - Some(tree) => tree.item(position), - None => None + impl ObjectImpl for $model {} + + impl ListModelImpl for $model { + fn item_type(&self) -> glib::Type { + $row_data::static_type() + } + + fn n_items(&self) -> u32 { + match self.tree.borrow().as_ref() { + Some(tree) => tree.n_items(), + None => 0 + } + } + + fn item(&self, position: u32) + -> Option + { + match self.tree.borrow().as_ref() { + Some(tree) => tree.item(position), + None => None + } + } } } } + +model!(TrafficModel, TrafficItem, TrafficRowData); +model!(DeviceModel, DeviceItem, DeviceRowData); diff --git a/src/model/mod.rs b/src/model/mod.rs index c64a6736..87cf786c 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -16,14 +16,6 @@ use anyhow::Error; use crate::capture::{CaptureReader, TrafficItem, DeviceItem}; use crate::tree_list_model::{TreeListModel, ItemNodeRc}; -// Public part of the Model type. -glib::wrapper! { - pub struct TrafficModel(ObjectSubclass) @implements gio::ListModel; -} -glib::wrapper! { - pub struct DeviceModel(ObjectSubclass) @implements gio::ListModel; -} - pub trait GenericModel where Self: Sized { const HAS_TIMES: bool; fn new(capture: CaptureReader, @@ -41,106 +33,68 @@ pub trait GenericModel where Self: Sized { fn connectors(&self, item: &Item) -> String; } -impl GenericModel for TrafficModel { - const HAS_TIMES: bool = true; - - fn new(capture: CaptureReader, - #[cfg(any(test, feature="record-ui-test"))] - on_item_update: Rc>) - -> Result - { - let model: TrafficModel = glib::Object::new::(); - let tree = TreeListModel::new( - capture, - #[cfg(any(test, feature="record-ui-test"))] - on_item_update)?; - model.imp().tree.replace(Some(tree)); - Ok(model) - } - - fn set_expanded(&self, - node: &ItemNodeRc, - position: u32, - expanded: bool) - -> Result<(), Error> - { - let tree_opt = self.imp().tree.borrow(); - let tree = tree_opt.as_ref().unwrap(); - tree.set_expanded(self, node, position as u64, expanded) - } - - fn update(&self) -> Result { - let tree_opt = self.imp().tree.borrow(); - let tree = tree_opt.as_ref().unwrap(); - tree.update(self) - } - - fn description(&self, item: &TrafficItem, detail: bool) -> String { - let tree_opt = self.imp().tree.borrow(); - let tree = tree_opt.as_ref().unwrap(); - tree.description(item, detail) - } - - fn timestamp(&self, item: &TrafficItem) -> u64 { - let tree_opt = self.imp().tree.borrow(); - let tree = tree_opt.as_ref().unwrap(); - tree.timestamp(item) - } - - fn connectors(&self, item: &TrafficItem) -> String { - let tree_opt = self.imp().tree.borrow(); - let tree = tree_opt.as_ref().unwrap(); - tree.connectors(item) +macro_rules! model { + ($model: ident, $item: ident, $has_times: literal) => { + // Public part of the Model type. + glib::wrapper! { + pub struct $model(ObjectSubclass) + @implements gio::ListModel; + } + + impl GenericModel<$item> for $model { + const HAS_TIMES: bool = $has_times; + + fn new(capture: CaptureReader, + #[cfg(any(test, feature="record-ui-test"))] + on_item_update: Rc>) + -> Result + { + let model: $model = glib::Object::new::<$model>(); + let tree = TreeListModel::new( + capture, + #[cfg(any(test, feature="record-ui-test"))] + on_item_update)?; + model.imp().tree.replace(Some(tree)); + Ok(model) + } + + fn set_expanded(&self, + node: &ItemNodeRc<$item>, + position: u32, + expanded: bool) + -> Result<(), Error> + { + let tree_opt = self.imp().tree.borrow(); + let tree = tree_opt.as_ref().unwrap(); + tree.set_expanded(self, node, position as u64, expanded) + } + + fn update(&self) -> Result { + let tree_opt = self.imp().tree.borrow(); + let tree = tree_opt.as_ref().unwrap(); + tree.update(self) + } + + fn description(&self, item: &$item, detail: bool) -> String { + let tree_opt = self.imp().tree.borrow(); + let tree = tree_opt.as_ref().unwrap(); + tree.description(item, detail) + } + + fn timestamp(&self, item: &$item) -> u64 { + let tree_opt = self.imp().tree.borrow(); + let tree = tree_opt.as_ref().unwrap(); + tree.timestamp(item) + } + + fn connectors(&self, item: &$item) -> String { + let tree_opt = self.imp().tree.borrow(); + let tree = tree_opt.as_ref().unwrap(); + tree.connectors(item) + } + } } } -impl GenericModel for DeviceModel { - const HAS_TIMES: bool = false; - - fn new(capture: CaptureReader, - #[cfg(any(test, feature="record-ui-test"))] - on_item_update: Rc>) - -> Result - { - let model: DeviceModel = glib::Object::new::(); - let tree = TreeListModel::new( - capture, - #[cfg(any(test, feature="record-ui-test"))] - on_item_update)?; - model.imp().tree.replace(Some(tree)); - Ok(model) - } - - fn set_expanded(&self, - node: &ItemNodeRc, - position: u32, - expanded: bool) - -> Result<(), Error> - { - let tree_opt = self.imp().tree.borrow(); - let tree = tree_opt.as_ref().unwrap(); - tree.set_expanded(self, node, position as u64, expanded) - } - - fn update(&self) -> Result { - let tree_opt = self.imp().tree.borrow(); - let tree = tree_opt.as_ref().unwrap(); - tree.update(self) - } - - fn description(&self, item: &DeviceItem, detail: bool) -> String { - let tree_opt = self.imp().tree.borrow(); - let tree = tree_opt.as_ref().unwrap(); - tree.description(item, detail) - } - - fn timestamp(&self, _item: &DeviceItem) -> u64 { - unreachable!(); - } - - fn connectors(&self, item: &DeviceItem) -> String { - let tree_opt = self.imp().tree.borrow(); - let tree = tree_opt.as_ref().unwrap(); - tree.connectors(item) - } -} +model!(TrafficModel, TrafficItem, true); +model!(DeviceModel, DeviceItem, false); diff --git a/src/row_data/imp.rs b/src/row_data/imp.rs index f022ca34..66d3addf 100644 --- a/src/row_data/imp.rs +++ b/src/row_data/imp.rs @@ -4,31 +4,26 @@ use std::cell::RefCell; use crate::capture::{TrafficItem, DeviceItem}; use crate::tree_list_model::ItemNodeRc; -// The actual data structure that stores our values. This is not accessible -// directly from the outside. -#[derive(Default)] -pub struct TrafficRowData { - pub(super) node: RefCell, String>>>, +macro_rules! row_data { + ($row_data: ident, $item: ident) => { + // The actual data structure that stores our values. This is not accessible + // directly from the outside. + #[derive(Default)] + pub struct $row_data { + pub(super) node: RefCell, String>>>, + } -} - -#[derive(Default)] -pub struct DeviceRowData { - pub(super) node: RefCell, String>>>, -} - -// Basic declaration of our type for the GObject type system -#[glib::object_subclass] -impl ObjectSubclass for TrafficRowData { - const NAME: &'static str = "TrafficRowData"; - type Type = super::TrafficRowData; -} + // Basic declaration of our type for the GObject type system + #[glib::object_subclass] + impl ObjectSubclass for $row_data { + const NAME: &'static str = stringify!($row_data); + type Type = super::$row_data; + } -#[glib::object_subclass] -impl ObjectSubclass for DeviceRowData { - const NAME: &'static str = "DeviceRowData"; - type Type = super::DeviceRowData; + impl ObjectImpl for $row_data {} + } } -impl ObjectImpl for TrafficRowData {} -impl ObjectImpl for DeviceRowData {} +row_data!(TrafficRowData, TrafficItem); +row_data!(DeviceRowData, DeviceItem); diff --git a/src/row_data/mod.rs b/src/row_data/mod.rs index 54306925..da725666 100644 --- a/src/row_data/mod.rs +++ b/src/row_data/mod.rs @@ -15,59 +15,44 @@ use gtk::prelude::Cast; use crate::capture::{TrafficItem, DeviceItem}; use crate::tree_list_model::ItemNodeRc; -// Public part of the RowData type. This behaves like a normal gtk-rs-style GObject -// binding -glib::wrapper! { - pub struct TrafficRowData(ObjectSubclass); -} -glib::wrapper! { - pub struct DeviceRowData(ObjectSubclass); -} - pub trait GenericRowData where Item: Copy { fn new(node: Result, String>) -> Self where Self: Sized; fn node(&self) -> Result, String>; } -impl GenericRowData for TrafficRowData { - fn new(node: Result, String>) -> TrafficRowData { - let row: TrafficRowData = glib::Object::new::(); - row.imp().node.replace(Some(node)); - row - } - - fn node(&self) -> Result, String> { - self.imp().node.borrow().as_ref().unwrap().clone() - } -} - -impl GenericRowData for DeviceRowData { - fn new(node: Result, String>) -> DeviceRowData { - let row: DeviceRowData = glib::Object::new::(); - row.imp().node.replace(Some(node)); - row - } - - fn node(&self) -> Result, String> { - self.imp().node.borrow().as_ref().unwrap().clone() - } -} - pub trait ToGenericRowData { #[cfg(any(test, feature="record-ui-test"))] fn to_generic_row_data(self) -> Box>; } -impl ToGenericRowData for glib::Object { - #[cfg(any(test, feature="record-ui-test"))] - fn to_generic_row_data(self) -> Box> { - Box::new(self.downcast::().unwrap()) +macro_rules! row_data { + ($row_data: ident, $item: ident) => { + // Public part of the RowData type. This behaves like a normal gtk-rs-style GObject + // binding + glib::wrapper! { + pub struct $row_data(ObjectSubclass); + } + + impl GenericRowData<$item> for $row_data { + fn new(node: Result, String>) -> $row_data{ + let row: $row_data = glib::Object::new::<$row_data>(); + row.imp().node.replace(Some(node)); + row + } + + fn node(&self) -> Result, String> { + self.imp().node.borrow().as_ref().unwrap().clone() + } + } + + impl ToGenericRowData<$item> for glib::Object { + #[cfg(any(test, feature="record-ui-test"))] + fn to_generic_row_data(self) -> Box> { + Box::new(self.downcast::<$row_data>().unwrap()) + } + } } } -impl ToGenericRowData for glib::Object { - #[cfg(any(test, feature="record-ui-test"))] - fn to_generic_row_data(self) -> Box> { - Box::new(self.downcast::().unwrap()) - } -} +row_data!(TrafficRowData, TrafficItem); +row_data!(DeviceRowData, DeviceItem); From 4de87b0b3f0085c7534cbfbc1dc0566e75696440 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 21 Aug 2024 04:49:57 +0100 Subject: [PATCH 2/6] Merge each GObject implementation into a single file. --- src/{expander/mod.rs => expander.rs} | 35 ++++++++++++++++- src/expander/imp.rs | 30 --------------- src/{model/mod.rs => model.rs} | 57 +++++++++++++++++++++++++++- src/model/imp.rs | 54 -------------------------- src/{row_data/mod.rs => row_data.rs} | 34 ++++++++++++++++- src/row_data/imp.rs | 29 -------------- 6 files changed, 120 insertions(+), 119 deletions(-) rename src/{expander/mod.rs => expander.rs} (66%) delete mode 100644 src/expander/imp.rs rename src/{model/mod.rs => model.rs} (65%) delete mode 100644 src/model/imp.rs rename src/{row_data/mod.rs => row_data.rs} (65%) delete mode 100644 src/row_data/imp.rs diff --git a/src/expander/mod.rs b/src/expander.rs similarity index 66% rename from src/expander/mod.rs rename to src/expander.rs index 1c939903..f23d2fe2 100644 --- a/src/expander/mod.rs +++ b/src/expander.rs @@ -1,5 +1,3 @@ -mod imp; - use std::cell::RefMut; use gtk::{ self, @@ -62,3 +60,36 @@ impl Default for ExpanderWrapper { Self::new() } } + +mod imp { + use gtk::{ + self, + subclass::prelude::*, + glib::{self, SignalHandlerId}, + Expander, + Label, + }; + use std::cell::RefCell; + + unsafe impl IsSubclassable for Expander {} + + #[derive(Default)] + pub struct ExpanderWrapper { + pub text_label: RefCell