Skip to content

Commit

Permalink
Add PwNodeFilterModel and use it for all the node types.
Browse files Browse the repository at this point in the history
Also introduce getters to avoid accessing models via imp().

And clean up code.
  • Loading branch information
saivert committed May 9, 2024
1 parent 883c5fd commit 1e244b9
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 63 deletions.
84 changes: 63 additions & 21 deletions src/backend/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,42 @@ use crate::{
ui::PwvucontrolWindowView,
backend::pwnodemodel::PwNodeModel,
backend::pwdeviceobject::PwDeviceObject,
backend::pwnodeobject,
ui::PwvucontrolWindow,
PwvucontrolApplication,
backend::NodeType
backend::NodeType,
backend::PwNodeFilterModel
};
use crate::macros::*;
use once_cell::unsync::OnceCell;

mod imp {

use super::*;

#[derive(Default, Properties)]
#[derive(Properties)]
#[properties(wrapper_type = super::PwvucontrolManager)]
pub struct PwvucontrolManager {
#[property(get)]
pub wp_core: OnceCell<wp::core::Core>,
pub wp_object_manager: OnceCell<wp::registry::ObjectManager>,

pub nodemodel: PwNodeModel,
pub sourcemodel: PwNodeModel,
pub sinkmodel: PwNodeModel,
pub devicemodel: OnceCell<gio::ListStore>,
#[property(get)]
pub(crate) node_model: PwNodeModel,

#[property(get)]
pub(crate) stream_output_model: PwNodeFilterModel,

#[property(get)]
pub(crate) stream_input_model: PwNodeFilterModel,

#[property(get)]
pub(crate) source_model: PwNodeFilterModel,

#[property(get)]
pub(crate) sink_model: PwNodeFilterModel,

#[property(get)]
pub(crate) device_model: gio::ListStore,

pub metadata_om: OnceCell<wp::registry::ObjectManager>,
pub metadata: RefCell<Option<wp::pw::Metadata>>,
Expand All @@ -53,6 +67,26 @@ mod imp {
application: RefCell<Option<PwvucontrolApplication>>,
}

impl Default for PwvucontrolManager {
fn default() -> Self {
Self {
wp_core: Default::default(),
wp_object_manager: Default::default(),
node_model: Default::default(),
stream_input_model: PwNodeFilterModel::new(NodeType::StreamInput, None::<gio::ListModel>),
stream_output_model: PwNodeFilterModel::new(NodeType::StreamOutput, None::<gio::ListModel>),
source_model: PwNodeFilterModel::new(NodeType::Source, None::<gio::ListModel>),
sink_model: PwNodeFilterModel::new(NodeType::Sink, None::<gio::ListModel>),
device_model: gio::ListStore::new::<PwDeviceObject>(),
metadata_om: Default::default(),
metadata: Default::default(),
default_nodes_api: Default::default(),
mixer_api: Default::default(),
application: Default::default(),
}
}
}

#[glib::object_subclass]
impl ObjectSubclass for PwvucontrolManager {
const NAME: &'static str = "PwvucontrolManager";
Expand All @@ -63,7 +97,11 @@ mod imp {
impl ObjectImpl for PwvucontrolManager {
fn constructed(&self) {
self.parent_constructed();
self.devicemodel.set(gio::ListStore::new::<PwDeviceObject>()).expect("devicemodel not set");

self.stream_input_model.set_model(Some(self.node_model.clone()));
self.stream_output_model.set_model(Some(self.node_model.clone()));
self.sink_model.set_model(Some(self.node_model.clone()));
self.source_model.set_model(Some(self.node_model.clone()));

self.setup_wp_connection();
self.setup_metadata_om();
Expand Down Expand Up @@ -151,7 +189,7 @@ mod imp {

wp_om.connect_object_added(
clone!(@weak self as imp, @weak wp_core as core => move |_, object| {
let devicemodel = imp.devicemodel.get().expect("devicemodel");
let devicemodel = &imp.device_model;
if let Some(node) = object.dynamic_cast_ref::<wp::pw::Node>() {
// Hide ourselves
if node.name() == Some("pwvucontrol-peak-detect".to_string()) {
Expand All @@ -173,11 +211,7 @@ mod imp {
}
pwvucontrol_info!("Got node: {} bound id {}", node.name().unwrap_or_default(), node.bound_id());
let pwobj = PwNodeObject::new(node);
let model = match pwobj.nodetype() {
NodeType::Sink => &imp.sinkmodel,
NodeType::Source => &imp.sourcemodel,
_ => &imp.nodemodel
};
let model = &imp.node_model;
model.append(&pwobj);
} else if let Some(device) = object.dynamic_cast_ref::<wp::pw::Device>() {
let n: String = device.pw_property("device.name").unwrap();
Expand All @@ -190,14 +224,10 @@ mod imp {
);

wp_om.connect_object_removed(clone!(@weak self as imp => move |_, object| {
let devicemodel = imp.devicemodel.get().expect("devicemodel");
let devicemodel = &imp.device_model;
if let Some(node) = object.dynamic_cast_ref::<wp::pw::Node>() {
pwvucontrol_info!("removed: {:?} id: {}", node.name(), node.bound_id());
let model = match pwnodeobject::get_node_type_for_node(node) {
NodeType::Sink => &imp.sinkmodel,
NodeType::Source => &imp.sourcemodel,
_ => &imp.nodemodel
};
let model = &imp.node_model;
model.remove(node.bound_id());

} else if let Some(device) = object.dynamic_cast_ref::<wp::pw::Device>() {
Expand Down Expand Up @@ -303,7 +333,7 @@ impl PwvucontrolManager {
}

pub fn get_device_by_id(&self, id: u32) -> Option<PwDeviceObject> {
let devicemodel = self.imp().devicemodel.get().expect("devicemodel");
let devicemodel = &self.imp().device_model;
for device in devicemodel.iter::<PwDeviceObject>() {
if let Ok(device) = device {
if device.wpdevice().bound_id() == id {
Expand All @@ -313,6 +343,18 @@ impl PwvucontrolManager {
}
None
}

pub fn get_node_by_id(&self, id: u32) -> Option<PwNodeObject> {
let nodemodel = &self.imp().node_model;
for node in nodemodel.iter::<PwNodeObject>() {
if let Ok(node) = node {
if node.wpnode().bound_id() == id {
return Some(node);
}
}
}
None
}
}

impl Default for PwvucontrolManager {
Expand Down
4 changes: 3 additions & 1 deletion src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod pwnodeobject;
mod pwrouteobject;
mod routedirection;
mod pwroutefiltermodel;
mod pwnodefiltermodel;

pub use paramavailability::ParamAvailability;
pub use pwchannelobject::PwChannelObject;
Expand All @@ -18,4 +19,5 @@ pub use pwnodemodel::PwNodeModel;
pub use pwnodeobject::{PwNodeObject, NodeType};
pub use pwrouteobject::PwRouteObject;
pub use routedirection::RouteDirection;
pub use pwroutefiltermodel::PwRouteFilterModel;
pub use pwroutefiltermodel::PwRouteFilterModel;
pub use pwnodefiltermodel::PwNodeFilterModel;
122 changes: 122 additions & 0 deletions src/backend/pwnodefiltermodel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: GPL-3.0-or-later

use glib::{closure_local, subclass::prelude::*, Properties};
use gtk::{gio, prelude::*, subclass::prelude::*};
use std::cell::{Cell, RefCell, OnceCell};
use super::{NodeType, PwNodeObject};

mod imp {
use glib::SignalHandlerId;

use crate::backend::PwNodeObject;

use super::*;

#[derive(Debug, Properties, Default)]
#[properties(wrapper_type = super::PwNodeFilterModel)]
pub struct PwNodeFilterModel {
/// Contains the items that matches the filter predicate.
pub(super) filtered_model: OnceCell<gtk::FilterListModel>,

#[property(get, set, construct_only, builder(NodeType::Undefined))]
pub(super) nodetype: Cell<NodeType>,

/// The model we are filtering.
#[property(get, set = Self::set_model, nullable)]
pub(super) model: RefCell<Option<gio::ListModel>>,

pub(crate) signalid: RefCell<Option<SignalHandlerId>>,
}

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

#[glib::derived_properties]
impl ObjectImpl for PwNodeFilterModel {
fn constructed(&self) {
self.parent_constructed();

let nodetype = self.nodetype.get();

let filter = gtk::CustomFilter::new(move |obj| {
let node: &PwNodeObject = obj.downcast_ref().expect("PwNodeObject");
node.nodetype() == nodetype
});

self.filtered_model.set(gtk::FilterListModel::new(None::<gio::ListModel>, Some(filter))).expect("filtered model not set");

}
}

impl ListModelImpl for PwNodeFilterModel {
fn item_type(&self) -> glib::Type {
PwNodeObject::static_type()
}
fn n_items(&self) -> u32 {
self.filtered_model.get().expect("Filtered model").n_items()
}
fn item(&self, position: u32) -> Option<glib::Object> {
self.filtered_model.get().expect("Filtered model").item(position)
}
}

impl PwNodeFilterModel {
pub fn set_model(&self, new_model: Option<gio::ListModel>) {
let filtered_model = self.filtered_model.get().expect("Filtered model");
let removed = filtered_model.n_items();
let widget = self.obj();

self.disconnect();

if let Some(new_model) = new_model {

assert!(self.item_type().is_a(new_model.item_type()));

let handler = closure_local!(@watch widget => move |_listmodel: &gio::ListModel, position: u32, removed: u32, added: u32| {
widget.items_changed(position, removed, added);
});
//handler.invoke::<()>(&[&new_model, &0u32, &0u32, &0u32]);
self.signalid.replace(Some(filtered_model.connect_closure("items-changed", true, handler)));

filtered_model.set_model(Some(&new_model));

self.model.replace(Some(new_model));
} else {
widget.items_changed(0, removed, 0);
}
}

pub fn disconnect(&self) {
let filtered_model = self.filtered_model.get().expect("Filtered model");
filtered_model.set_model(gio::ListModel::NONE);
if let Some(id) = self.signalid.take() {
filtered_model.disconnect(id);
}
}
}
}

glib::wrapper! {
pub struct PwNodeFilterModel(ObjectSubclass<imp::PwNodeFilterModel>) @implements gio::ListModel;
}

impl PwNodeFilterModel {
pub(crate) fn new(nodetype: NodeType, model: Option<impl glib::IsA<gio::ListModel>>) -> Self
{
glib::Object::builder()
.property("model", &model)
.property("nodetype", nodetype)
.build()
}

pub fn get_node_pos_from_id(&self, id: u32) -> Option<u32> {
let pos: Option<usize> = self.iter::<PwNodeObject>().position(|item| {
item.map_or(false, |item| item.boundid() == id)
});
pos.map(|x| x as u32)
}
}
4 changes: 2 additions & 2 deletions src/backend/pwnodeobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ impl PwNodeObject {
target_serial.as_str(),
true),
].iter().collect::<Interest<wp::pw::Node>>()) {
return manager.imp().sinkmodel.get_node(sinknode.bound_id()).ok();
return manager.get_node_by_id(sinknode.bound_id());
};
}
}
Expand All @@ -533,7 +533,7 @@ impl PwNodeObject {
target_node.as_str(),
true),
].iter().collect::<Interest<wp::pw::Node>>()) {
return manager.imp().sinkmodel.get_node(sinknode.bound_id()).ok();
return manager.get_node_by_id(sinknode.bound_id());
};
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/ui/output_dropdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ mod imp {
self.outputdevice_dropdown.set_factory(Some(&factory));
self.outputdevice_dropdown.set_list_factory(default_dropdown_factory.as_ref());

let sinkmodel = &manager.imp().sinkmodel;
let sinkmodel = &manager.sink_model();

self.outputdevice_dropdown.set_enable_search(true);

Expand Down
4 changes: 2 additions & 2 deletions src/ui/outputbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,15 @@ impl PwOutputBox {
pub(crate) fn update_output_device_dropdown(&self) {
let manager = PwvucontrolManager::default();

let sinkmodel = &manager.imp().sinkmodel;
let sinkmodel = manager.sink_model();

let imp = self.imp();

let output_dropdown = imp.output_dropdown.get();

let id = self.default_node();

let string = if let Ok(node) = sinkmodel.get_node(id) {
let string = if let Some(node) = manager.get_node_by_id(id) {
format!("Default ({})", node.name())
} else {
"Default".to_string()
Expand Down
Loading

0 comments on commit 1e244b9

Please sign in to comment.