mirror of
https://gitlab.freedesktop.org/pipewire/helvum
synced 2026-03-15 03:26:10 +08:00
Turn struct View into a gtk::Application subclass.
This lets us keep multiple reference-counted copies easier, and lets us emit signals to the controller.
This commit is contained in:
@@ -41,7 +41,7 @@ enum Item {
|
|||||||
/// It also keeps and manages a state object that contains the current state of objects present on the remote.
|
/// It also keeps and manages a state object that contains the current state of objects present on the remote.
|
||||||
pub struct Controller {
|
pub struct Controller {
|
||||||
state: HashMap<u32, Item>,
|
state: HashMap<u32, Item>,
|
||||||
view: Rc<view::View>,
|
view: view::View,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Controller {
|
impl Controller {
|
||||||
@@ -53,7 +53,7 @@ impl Controller {
|
|||||||
/// The returned `Rc` will be the only strong reference kept to the controller, so dropping the `Rc`
|
/// The returned `Rc` will be the only strong reference kept to the controller, so dropping the `Rc`
|
||||||
/// will also drop the controller, unless the `Rc` is cloned outside of this function.
|
/// will also drop the controller, unless the `Rc` is cloned outside of this function.
|
||||||
pub(super) fn new(
|
pub(super) fn new(
|
||||||
view: Rc<view::View>,
|
view: view::View,
|
||||||
gtk_receiver: Receiver<PipewireMessage>,
|
gtk_receiver: Receiver<PipewireMessage>,
|
||||||
) -> Rc<RefCell<Controller>> {
|
) -> Rc<RefCell<Controller>> {
|
||||||
let result = Rc::new(RefCell::new(Controller {
|
let result = Rc::new(RefCell::new(Controller {
|
||||||
|
|||||||
11
src/main.rs
11
src/main.rs
@@ -2,10 +2,11 @@ mod controller;
|
|||||||
mod pipewire_connection;
|
mod pipewire_connection;
|
||||||
mod view;
|
mod view;
|
||||||
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use controller::MediaType;
|
use controller::MediaType;
|
||||||
use gtk::glib::{self, PRIORITY_DEFAULT};
|
use gtk::{
|
||||||
|
glib::{self, PRIORITY_DEFAULT},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
use pipewire::spa::Direction;
|
use pipewire::spa::Direction;
|
||||||
|
|
||||||
/// Messages used GTK thread to command the pipewire thread.
|
/// Messages used GTK thread to command the pipewire thread.
|
||||||
@@ -55,10 +56,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let pw_thread =
|
let pw_thread =
|
||||||
std::thread::spawn(move || pipewire_connection::thread_main(gtk_sender, pw_receiver));
|
std::thread::spawn(move || pipewire_connection::thread_main(gtk_sender, pw_receiver));
|
||||||
|
|
||||||
let view = Rc::new(view::View::new());
|
let view = view::View::new();
|
||||||
let _controller = controller::Controller::new(view.clone(), gtk_receiver);
|
let _controller = controller::Controller::new(view.clone(), gtk_receiver);
|
||||||
|
|
||||||
view.run();
|
view.run(&std::env::args().collect::<Vec<_>>());
|
||||||
|
|
||||||
pw_sender
|
pw_sender
|
||||||
.send(GtkMessage::Terminate)
|
.send(GtkMessage::Terminate)
|
||||||
|
|||||||
119
src/view/mod.rs
119
src/view/mod.rs
@@ -10,8 +10,10 @@ pub use graph_view::GraphView;
|
|||||||
pub use node::Node;
|
pub use node::Node;
|
||||||
|
|
||||||
use gtk::{
|
use gtk::{
|
||||||
|
gio,
|
||||||
glib::{self, clone},
|
glib::{self, clone},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
subclass::prelude::ObjectSubclassExt,
|
||||||
};
|
};
|
||||||
use pipewire::spa::Direction;
|
use pipewire::spa::Direction;
|
||||||
|
|
||||||
@@ -35,38 +37,34 @@ static STYLE: &str = "
|
|||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
/// Manager struct of the view.
|
mod imp {
|
||||||
///
|
use super::*;
|
||||||
/// This struct is responsible for setting up the UI, as well as communication with other components outside the view.
|
use gtk::{glib, subclass::prelude::*};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct View {
|
pub struct View {
|
||||||
app: gtk::Application,
|
pub(super) graphview: GraphView,
|
||||||
graphview: GraphView,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
#[glib::object_subclass]
|
||||||
/// Create the view.
|
impl ObjectSubclass for View {
|
||||||
/// This will set up the entire user interface and prepare it for being run.
|
const NAME: &'static str = "HelvumApplication";
|
||||||
///
|
type Type = super::View;
|
||||||
/// To show and run the interface, its [`run`](`Self::run`) method will need to be called.
|
type ParentType = gtk::Application;
|
||||||
pub(super) fn new() -> Self {
|
|
||||||
let graphview = GraphView::new();
|
|
||||||
|
|
||||||
let app = gtk::Application::new(Some("org.freedesktop.ryuukyu.helvum"), Default::default())
|
fn new() -> Self {
|
||||||
.expect("Application creation failed");
|
View {
|
||||||
|
graphview: GraphView::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
app.connect_startup(|_| {
|
impl ObjectImpl for View {}
|
||||||
// Load CSS from the STYLE variable.
|
impl ApplicationImpl for View {
|
||||||
let provider = gtk::CssProvider::new();
|
fn activate(&self, app: &Self::Type) {
|
||||||
provider.load_from_data(STYLE.as_bytes());
|
let scrollwindow = gtk::ScrolledWindowBuilder::new()
|
||||||
gtk::StyleContext::add_provider_for_display(
|
.child(&self.graphview)
|
||||||
>k::gdk::Display::get_default().expect("Error initializing gtk css provider."),
|
.build();
|
||||||
&provider,
|
|
||||||
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.connect_activate(clone!(@strong graphview => move |app| {
|
|
||||||
let scrollwindow = gtk::ScrolledWindowBuilder::new().child(&graphview).build();
|
|
||||||
let window = gtk::ApplicationWindowBuilder::new()
|
let window = gtk::ApplicationWindowBuilder::new()
|
||||||
.application(app)
|
.application(app)
|
||||||
.default_width(1280)
|
.default_width(1280)
|
||||||
@@ -78,7 +76,38 @@ impl View {
|
|||||||
.get_settings()
|
.get_settings()
|
||||||
.set_property_gtk_application_prefer_dark_theme(true);
|
.set_property_gtk_application_prefer_dark_theme(true);
|
||||||
window.show();
|
window.show();
|
||||||
}));
|
}
|
||||||
|
|
||||||
|
fn startup(&self, app: &Self::Type) {
|
||||||
|
self.parent_startup(app);
|
||||||
|
|
||||||
|
// Load CSS from the STYLE variable.
|
||||||
|
let provider = gtk::CssProvider::new();
|
||||||
|
provider.load_from_data(STYLE.as_bytes());
|
||||||
|
gtk::StyleContext::add_provider_for_display(
|
||||||
|
>k::gdk::Display::get_default().expect("Error initializing gtk css provider."),
|
||||||
|
&provider,
|
||||||
|
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl GtkApplicationImpl for View {}
|
||||||
|
}
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct View(ObjectSubclass<imp::View>)
|
||||||
|
@extends gio::Application, gtk::Application,
|
||||||
|
@implements gio::ActionGroup, gio::ActionMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl View {
|
||||||
|
/// Create the view.
|
||||||
|
/// This will set up the entire user interface and prepare it for being run.
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
let app: View = glib::Object::new(&[("application-id", &"org.freedesktop.ryuukyu.helvum")])
|
||||||
|
.expect("Failed to create new Application");
|
||||||
|
|
||||||
// Add <Control-Q> shortcut for quitting the application.
|
// Add <Control-Q> shortcut for quitting the application.
|
||||||
let quit = gtk::gio::SimpleAction::new("quit", None);
|
let quit = gtk::gio::SimpleAction::new("quit", None);
|
||||||
@@ -88,21 +117,13 @@ impl View {
|
|||||||
app.set_accels_for_action("app.quit", &["<Control>Q"]);
|
app.set_accels_for_action("app.quit", &["<Control>Q"]);
|
||||||
app.add_action(&quit);
|
app.add_action(&quit);
|
||||||
|
|
||||||
Self { app, graphview }
|
app
|
||||||
}
|
|
||||||
|
|
||||||
/// Run the view.
|
|
||||||
///
|
|
||||||
/// This will enter a gtk event loop and remain in that
|
|
||||||
/// until the application is quit by the user.
|
|
||||||
pub(super) fn run(&self) -> i32 {
|
|
||||||
self.app.run(&std::env::args().collect::<Vec<_>>())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new node to the view.
|
/// Add a new node to the view.
|
||||||
pub fn add_node(&self, id: u32, name: &str) {
|
pub fn add_node(&self, id: u32, name: &str) {
|
||||||
let node = crate::view::Node::new(name);
|
let imp = imp::View::from_instance(self);
|
||||||
self.graphview.add_node(id, node);
|
imp.graphview.add_node(id, crate::view::Node::new(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new port to the view.
|
/// Add a new port to the view.
|
||||||
@@ -114,28 +135,36 @@ impl View {
|
|||||||
port_direction: Direction,
|
port_direction: Direction,
|
||||||
port_media_type: Option<MediaType>,
|
port_media_type: Option<MediaType>,
|
||||||
) {
|
) {
|
||||||
let port = port::Port::new(port_id, port_name, port_direction, port_media_type);
|
let imp = imp::View::from_instance(self);
|
||||||
self.graphview.add_port(node_id, port_id, port)
|
imp.graphview.add_port(
|
||||||
|
node_id,
|
||||||
|
port_id,
|
||||||
|
port::Port::new(port_id, port_name, port_direction, port_media_type),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new link to the view.
|
/// Add a new link to the view.
|
||||||
pub fn add_link(&self, id: u32, link: crate::PipewireLink) {
|
pub fn add_link(&self, id: u32, link: crate::PipewireLink) {
|
||||||
self.graphview.add_link(id, link);
|
let imp = imp::View::from_instance(self);
|
||||||
|
imp.graphview.add_link(id, link);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the node with the specified id from the view.
|
/// Remove the node with the specified id from the view.
|
||||||
pub fn remove_node(&self, id: u32) {
|
pub fn remove_node(&self, id: u32) {
|
||||||
self.graphview.remove_node(id);
|
let imp = imp::View::from_instance(self);
|
||||||
|
imp.graphview.remove_node(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the port with the id `id` from the node with the id `node_id`
|
/// Remove the port with the id `id` from the node with the id `node_id`
|
||||||
/// from the view.
|
/// from the view.
|
||||||
pub fn remove_port(&self, id: u32, node_id: u32) {
|
pub fn remove_port(&self, id: u32, node_id: u32) {
|
||||||
self.graphview.remove_port(id, node_id);
|
let imp = imp::View::from_instance(self);
|
||||||
|
imp.graphview.remove_port(id, node_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the link with the specified id from the view.
|
/// Remove the link with the specified id from the view.
|
||||||
pub fn remove_link(&self, id: u32) {
|
pub fn remove_link(&self, id: u32) {
|
||||||
self.graphview.remove_link(id);
|
let imp = imp::View::from_instance(self);
|
||||||
|
imp.graphview.remove_link(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user