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:
Tom A. Wagner
2021-05-08 13:16:13 +02:00
parent b5071c09a0
commit 2cc684d57c
3 changed files with 83 additions and 53 deletions

View File

@@ -10,8 +10,10 @@ pub use graph_view::GraphView;
pub use node::Node;
use gtk::{
gio,
glib::{self, clone},
prelude::*,
subclass::prelude::ObjectSubclassExt,
};
use pipewire::spa::Direction;
@@ -35,38 +37,34 @@ static STYLE: &str = "
}
";
/// Manager struct of the view.
///
/// This struct is responsible for setting up the UI, as well as communication with other components outside the view.
pub struct View {
app: gtk::Application,
graphview: GraphView,
}
mod imp {
use super::*;
use gtk::{glib, subclass::prelude::*};
impl View {
/// Create the view.
/// This will set up the entire user interface and prepare it for being run.
///
/// To show and run the interface, its [`run`](`Self::run`) method will need to be called.
pub(super) fn new() -> Self {
let graphview = GraphView::new();
#[derive(Default)]
pub struct View {
pub(super) graphview: GraphView,
}
let app = gtk::Application::new(Some("org.freedesktop.ryuukyu.helvum"), Default::default())
.expect("Application creation failed");
#[glib::object_subclass]
impl ObjectSubclass for View {
const NAME: &'static str = "HelvumApplication";
type Type = super::View;
type ParentType = gtk::Application;
app.connect_startup(|_| {
// Load CSS from the STYLE variable.
let provider = gtk::CssProvider::new();
provider.load_from_data(STYLE.as_bytes());
gtk::StyleContext::add_provider_for_display(
&gtk::gdk::Display::get_default().expect("Error initializing gtk css provider."),
&provider,
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
});
fn new() -> Self {
View {
graphview: GraphView::new(),
}
}
}
app.connect_activate(clone!(@strong graphview => move |app| {
let scrollwindow = gtk::ScrolledWindowBuilder::new().child(&graphview).build();
impl ObjectImpl for View {}
impl ApplicationImpl for View {
fn activate(&self, app: &Self::Type) {
let scrollwindow = gtk::ScrolledWindowBuilder::new()
.child(&self.graphview)
.build();
let window = gtk::ApplicationWindowBuilder::new()
.application(app)
.default_width(1280)
@@ -78,7 +76,38 @@ impl View {
.get_settings()
.set_property_gtk_application_prefer_dark_theme(true);
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(
&gtk::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.
let quit = gtk::gio::SimpleAction::new("quit", None);
@@ -88,21 +117,13 @@ impl View {
app.set_accels_for_action("app.quit", &["<Control>Q"]);
app.add_action(&quit);
Self { app, graphview }
}
/// 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<_>>())
app
}
/// Add a new node to the view.
pub fn add_node(&self, id: u32, name: &str) {
let node = crate::view::Node::new(name);
self.graphview.add_node(id, node);
let imp = imp::View::from_instance(self);
imp.graphview.add_node(id, crate::view::Node::new(name));
}
/// Add a new port to the view.
@@ -114,28 +135,36 @@ impl View {
port_direction: Direction,
port_media_type: Option<MediaType>,
) {
let port = port::Port::new(port_id, port_name, port_direction, port_media_type);
self.graphview.add_port(node_id, port_id, port)
let imp = imp::View::from_instance(self);
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.
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.
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`
/// from the view.
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.
pub fn remove_link(&self, id: u32) {
self.graphview.remove_link(id);
let imp = imp::View::from_instance(self);
imp.graphview.remove_link(id);
}
}