Skip to content

Commit

Permalink
Merge pull request #180 from martinling/gobject-cleanup
Browse files Browse the repository at this point in the history
GObject subclass cleanup
  • Loading branch information
miek authored Aug 27, 2024
2 parents 4494281 + 691e593 commit 6d7cbce
Show file tree
Hide file tree
Showing 11 changed files with 334 additions and 396 deletions.
30 changes: 0 additions & 30 deletions src/expander/imp.rs

This file was deleted.

51 changes: 45 additions & 6 deletions src/expander/mod.rs → src/item_widget.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mod imp;
//! GObject subclass for our custom widget.

use std::cell::RefMut;
use gtk::{
Expand All @@ -13,14 +13,16 @@ use gtk::{
};

glib::wrapper! {
pub struct ExpanderWrapper(ObjectSubclass<imp::ExpanderWrapper>)
/// The outer type exposed to our Rust code.
pub struct ItemWidget(ObjectSubclass<imp::ItemWidget>)
@extends gtk::Box, gtk::Widget,
@implements gtk::Orientable;
}

impl ExpanderWrapper {
pub fn new() -> ExpanderWrapper {
let wrapper: ExpanderWrapper = glib::Object::new::<ExpanderWrapper>();
impl ItemWidget {
/// Create a new widget.
pub fn new() -> ItemWidget {
let wrapper: ItemWidget = glib::Object::new::<ItemWidget>();
wrapper.imp().text_label.replace(
Label::builder()
.ellipsize(EllipsizeMode::End)
Expand All @@ -35,30 +37,67 @@ impl ExpanderWrapper {
wrapper
}

/// Fetch the Expander from the widget.
pub fn expander(&self) -> RefMut<Expander> {
self.imp().expander.borrow_mut()
}

/// Store a signal handler to be retained by this widget.
pub fn set_handler(&self, handler: SignalHandlerId) {
self.imp().handler.replace(Some(handler));
}

/// Take the signal handler retained by this widget.
pub fn take_handler(&self) -> Option<SignalHandlerId> {
self.imp().handler.take().take()
}

/// Set the summary text on this widget.
pub fn set_text(&self, text: String) {
self.imp().text_label.borrow_mut().set_text(&text);
}

/// Set the connecting lines on this widget.
pub fn set_connectors(&self, connectors: String) {
self.imp().conn_label.borrow_mut().set_markup(
format!("<tt>{connectors}</tt>").as_str());
}
}

impl Default for ExpanderWrapper {
impl Default for ItemWidget {
fn default() -> Self {
Self::new()
}
}

/// The internal implementation module.
mod imp {
use gtk::{
self,
subclass::prelude::*,
glib::{self, SignalHandlerId},
Expander,
Label,
};
use std::cell::RefCell;

/// The inner type to be used in the GObject type system.
#[derive(Default)]
pub struct ItemWidget {
pub text_label: RefCell<Label>,
pub conn_label: RefCell<Label>,
pub expander: RefCell<Expander>,
pub handler: RefCell<Option<SignalHandlerId>>,
}

#[glib::object_subclass]
impl ObjectSubclass for ItemWidget {
const NAME: &'static str = "ItemWidget";
type Type = super::ItemWidget;
type ParentType = gtk::Box;
}

impl BoxImpl for ItemWidget {}
impl WidgetImpl for ItemWidget {}
impl ObjectImpl for ItemWidget {}
}
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ mod capture;
mod compact_index;
mod data_stream;
mod decoder;
mod expander;
mod id;
mod index_stream;
mod item_widget;
mod model;
mod pcap;
mod rcu;
Expand Down
173 changes: 173 additions & 0 deletions src/model.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
//! GObject subclasses implementing ListModel for each UI view.

#[cfg(any(test, feature="record-ui-test"))]
use {
std::cell::RefCell,
std::rc::Rc,
};

use gtk::subclass::prelude::*;
use gtk::{gio, glib};

use anyhow::Error;

use crate::capture::{CaptureReader, TrafficItem, DeviceItem};
use crate::tree_list_model::{TreeListModel, ItemNodeRc};

/// Trait implemented by each of our ListModel implementations.
pub trait GenericModel<Item> where Self: Sized {
/// Whether this model has timestamps.
const HAS_TIMES: bool;

/// Create a new model instance for the given capture.
fn new(capture: CaptureReader,
#[cfg(any(test, feature="record-ui-test"))]
on_item_update: Rc<RefCell<dyn FnMut(u32, String)>>)
-> Result<Self, Error>;

/// Set whether a tree node is expanded.
fn set_expanded(&self,
node: &ItemNodeRc<Item>,
position: u32,
expanded: bool)
-> Result<(), Error>;

/// Update the model with new data from the capture.
///
/// Returns true if there will be further updates in future.
fn update(&self) -> Result<bool, Error>;

/// Fetch the description for a given item.
fn description(&self, item: &Item, detail: bool) -> String;

/// Fetch the timestamp for a given item.
fn timestamp(&self, item: &Item) -> u64;

/// Fetch the connecting lines for a given item.
fn connectors(&self, item: &Item) -> String;
}

/// Define the outer type exposed to our Rust code.
macro_rules! model {
($model: ident, $item: ident, $has_times: literal) => {

glib::wrapper! {
pub struct $model(ObjectSubclass<imp::$model>)
@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<RefCell<dyn FnMut(u32, String)>>)
-> Result<Self, Error>
{
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<bool, Error> {
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)
}
}
}
}

// Repeat the above boilerplate for each model.
model!(TrafficModel, TrafficItem, true);
model!(DeviceModel, DeviceItem, false);

/// The internal implementation module.
mod imp {
use gio::subclass::prelude::*;
use gtk::{gio, glib, prelude::*};

use std::cell::RefCell;
use crate::capture::{TrafficItem, DeviceItem};
use crate::row_data::{TrafficRowData, DeviceRowData};
use crate::tree_list_model::TreeListModel;

/// Define the inner type to be used in the GObject type system.
macro_rules! model {
($model:ident, $item:ident, $row_data:ident) => {
#[derive(Default)]
pub struct $model {
pub(super) tree: RefCell<Option<
TreeListModel<$item, super::$model, $row_data>>>,
}

#[glib::object_subclass]
impl ObjectSubclass for $model {
const NAME: &'static str = stringify!($model);
type Type = super::$model;
type Interfaces = (gio::ListModel,);
}

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<glib::Object>
{
match self.tree.borrow().as_ref() {
Some(tree) => tree.item(position),
None => None
}
}
}
}
}

// Repeat the above boilerplate for each model.
model!(TrafficModel, TrafficItem, TrafficRowData);
model!(DeviceModel, DeviceItem, DeviceRowData);
}
80 changes: 0 additions & 80 deletions src/model/imp.rs

This file was deleted.

Loading

0 comments on commit 6d7cbce

Please sign in to comment.