mirror of
https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio.git
synced 2024-11-22 09:00:59 +00:00
Merge branch 'dab_save_position' into 'main'
graphview: save position as a hidden property See merge request dabrain34/GstPipelineStudio!3
This commit is contained in:
commit
15a785f821
6 changed files with 152 additions and 39 deletions
4
TODO.md
4
TODO.md
|
@ -40,8 +40,8 @@
|
|||
- [ ] Remove a pad from a node
|
||||
- [ ] Implement graphview unit test
|
||||
- [ ] Implement pipeline unit test
|
||||
- [ ] Save node position in XML
|
||||
- [ ] Autosave the graph
|
||||
- [x] Save node position in XML
|
||||
- [x] Autosave the graph
|
||||
- [ ] Check the pîpeline live
|
||||
- [ ] Display pad properties with tooltip hover
|
||||
- [ ] Render a media file
|
||||
|
|
65
src/app.rs
65
src/app.rs
|
@ -38,7 +38,7 @@ use crate::logger;
|
|||
use crate::pipeline::{Pipeline, PipelineState};
|
||||
use crate::plugindialogs;
|
||||
use crate::settings::Settings;
|
||||
use crate::{GPS_DEBUG, GPS_ERROR};
|
||||
use crate::{GPS_DEBUG, GPS_ERROR, GPS_WARN};
|
||||
|
||||
use crate::graphmanager::{GraphView, Node, PortDirection};
|
||||
|
||||
|
@ -110,6 +110,7 @@ impl GPSApp {
|
|||
plugin_list_initialized: OnceCell::new(),
|
||||
menu_signal_handlers: RefCell::new(HashMap::new()),
|
||||
}));
|
||||
app.graphview.borrow_mut().set_id(0);
|
||||
Ok(app)
|
||||
}
|
||||
|
||||
|
@ -476,6 +477,17 @@ impl GPSApp {
|
|||
|
||||
drawing_area_window.set_child(Some(&*self.graphview.borrow()));
|
||||
|
||||
// Setup the logger to get messages into the TreeView
|
||||
let (ready_tx, ready_rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
let app_weak = self.downgrade();
|
||||
logger::init_logger(ready_tx, logger::LogLevel::Debug);
|
||||
self.setup_logger_list();
|
||||
let _ = ready_rx.attach(None, move |msg: String| {
|
||||
let app = upgrade_weak!(app_weak, glib::Continue(false));
|
||||
app.add_to_logger_list(msg);
|
||||
glib::Continue(true)
|
||||
});
|
||||
|
||||
let window = &self.window;
|
||||
|
||||
window.show();
|
||||
|
@ -575,9 +587,29 @@ impl GPSApp {
|
|||
self.connect_button_action("button-clear", move |_| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
app.clear_graph();
|
||||
//app.load_graph("graphs/compositor.xml").expect("Unable to open file");
|
||||
});
|
||||
|
||||
let app_weak = self.downgrade();
|
||||
self.graphview
|
||||
.borrow()
|
||||
.connect_local(
|
||||
"graph-updated",
|
||||
false,
|
||||
glib::clone!(@weak application => @default-return None, move |values: &[Value]| {
|
||||
let app = upgrade_weak!(app_weak, None);
|
||||
let id = values[1].get::<u32>().expect("id in args[1]");
|
||||
GPS_DEBUG!("Graph updated id={}", id);
|
||||
let _ = app
|
||||
.save_graph(
|
||||
Settings::default_graph_file_path()
|
||||
.to_str()
|
||||
.expect("Unable to convert to string"),
|
||||
)
|
||||
.map_err(|e| GPS_WARN!("Unable to save file {}", e));
|
||||
None
|
||||
}),
|
||||
)
|
||||
.expect("Failed to register graph-updated signal of graphview");
|
||||
// When user clicks on port with right button
|
||||
let app_weak = self.downgrade();
|
||||
self.graphview
|
||||
|
@ -722,16 +754,15 @@ impl GPSApp {
|
|||
// Setup the favorite list
|
||||
self.setup_favorite_list(application);
|
||||
|
||||
// Setup the logger to get messages into the TreeView
|
||||
let (ready_tx, ready_rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
let app_weak = self.downgrade();
|
||||
logger::init_logger(ready_tx, logger::LogLevel::Debug);
|
||||
self.setup_logger_list();
|
||||
let _ = ready_rx.attach(None, move |msg: String| {
|
||||
let app = upgrade_weak!(app_weak, glib::Continue(false));
|
||||
app.add_to_logger_list(msg);
|
||||
glib::Continue(true)
|
||||
});
|
||||
let _ = self
|
||||
.load_graph(
|
||||
Settings::default_graph_file_path()
|
||||
.to_str()
|
||||
.expect("Unable to convert to string"),
|
||||
)
|
||||
.map_err(|_e| {
|
||||
GPS_WARN!("Unable to load default graph");
|
||||
});
|
||||
}
|
||||
|
||||
// Downgrade to a weak reference
|
||||
|
@ -743,7 +774,7 @@ impl GPSApp {
|
|||
fn drop(self) {}
|
||||
|
||||
pub fn add_new_element(&self, element_name: &str) {
|
||||
let graph_view = self.graphview.borrow_mut();
|
||||
let graph_view = self.graphview.borrow();
|
||||
let node_id = graph_view.next_node_id();
|
||||
let pads = Pipeline::pads(element_name, false);
|
||||
if Pipeline::element_is_uri_src_handler(element_name) {
|
||||
|
@ -752,7 +783,7 @@ impl GPSApp {
|
|||
let node = app.graphview.borrow().node(&node_id).unwrap();
|
||||
let mut properties: HashMap<String, String> = HashMap::new();
|
||||
properties.insert(String::from("location"), filename);
|
||||
node.update_node_properties(&properties);
|
||||
node.update_properties(&properties);
|
||||
});
|
||||
}
|
||||
graph_view.add_node_with_port(
|
||||
|
@ -765,7 +796,7 @@ impl GPSApp {
|
|||
|
||||
pub fn update_element_properties(&self, node_id: u32, properties: &HashMap<String, String>) {
|
||||
let node = self.graphview.borrow().node(&node_id).unwrap();
|
||||
node.update_node_properties(properties);
|
||||
node.update_properties(properties);
|
||||
}
|
||||
|
||||
fn clear_graph(&self) {
|
||||
|
@ -774,14 +805,14 @@ impl GPSApp {
|
|||
}
|
||||
|
||||
fn save_graph(&self, filename: &str) -> anyhow::Result<()> {
|
||||
let graph_view = self.graphview.borrow_mut();
|
||||
let graph_view = self.graphview.borrow();
|
||||
graph_view.render_xml(filename)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_graph(&self, filename: &str) -> anyhow::Result<()> {
|
||||
self.clear_graph();
|
||||
let graph_view = self.graphview.borrow_mut();
|
||||
let graph_view = self.graphview.borrow();
|
||||
graph_view.load_xml(filename)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ mod imp {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct GraphView {
|
||||
pub(super) id: Cell<u32>,
|
||||
pub(super) nodes: RefCell<HashMap<u32, Node>>,
|
||||
pub(super) links: RefCell<HashMap<u32, Link>>,
|
||||
pub(super) current_node_id: Cell<u32>,
|
||||
|
@ -126,6 +127,18 @@ mod imp {
|
|||
}
|
||||
),
|
||||
);
|
||||
drag_controller.connect_drag_end(
|
||||
clone!(@strong drag_state => move |drag_controller, _x, _y| {
|
||||
let widget = drag_controller
|
||||
.widget()
|
||||
.expect("drag-end event has no widget")
|
||||
.dynamic_cast::<Self::Type>()
|
||||
.expect("drag-end event is not on the GraphView");
|
||||
widget.graph_updated();
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
obj.add_controller(&drag_controller);
|
||||
|
||||
let gesture = gtk::GestureClick::new();
|
||||
|
@ -252,6 +265,13 @@ mod imp {
|
|||
<()>::static_type().into(),
|
||||
)
|
||||
.build(),
|
||||
Signal::builder(
|
||||
"graph-updated",
|
||||
// returns graph ID
|
||||
&[u32::static_type().into()],
|
||||
<()>::static_type().into(),
|
||||
)
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
SIGNALS.as_ref()
|
||||
|
@ -374,6 +394,16 @@ impl GraphView {
|
|||
);
|
||||
glib::Object::new(&[]).expect("Failed to create GraphView")
|
||||
}
|
||||
pub fn set_id(&self, id: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
private.id.set(id)
|
||||
}
|
||||
|
||||
fn graph_updated(&self) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
self.emit_by_name("graph-updated", &[&private.id.get()])
|
||||
.expect("unable to send signal");
|
||||
}
|
||||
|
||||
pub fn add_node_with_port(&self, id: u32, node: Node, input: u32, output: u32) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
|
@ -423,6 +453,7 @@ impl GraphView {
|
|||
let port_id = self.next_port_id();
|
||||
self.add_port(id, port_id, "out", PortDirection::Output);
|
||||
}
|
||||
self.graph_updated();
|
||||
}
|
||||
|
||||
pub fn add_node(&self, id: u32, node: Node) {
|
||||
|
@ -544,6 +575,7 @@ impl GraphView {
|
|||
let private = imp::GraphView::from_instance(self);
|
||||
if !self.link_exists(&link) {
|
||||
private.links.borrow_mut().insert(link.id, link);
|
||||
self.graph_updated();
|
||||
self.queue_draw();
|
||||
}
|
||||
}
|
||||
|
@ -772,12 +804,14 @@ impl GraphView {
|
|||
}
|
||||
|
||||
pub fn render_xml(&self, filename: &str) -> anyhow::Result<()> {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let mut file = File::create(filename).unwrap();
|
||||
let mut writer = EmitterConfig::new()
|
||||
.perform_indent(true)
|
||||
.create_writer(&mut file);
|
||||
|
||||
writer.write(XMLWEvent::start_element("Graph"))?;
|
||||
writer
|
||||
.write(XMLWEvent::start_element("Graph").attr("id", &private.id.get().to_string()))?;
|
||||
|
||||
//Get the nodes
|
||||
let nodes = self.all_nodes(NodeType::All);
|
||||
|
@ -806,6 +840,20 @@ impl GraphView {
|
|||
)?;
|
||||
writer.write(XMLWEvent::end_element())?;
|
||||
}
|
||||
if let Some(position) = self.node_position(&node.upcast()) {
|
||||
writer.write(
|
||||
XMLWEvent::start_element("Property")
|
||||
.attr("name", "_pos_x")
|
||||
.attr("value", &position.0.to_string()),
|
||||
)?;
|
||||
writer.write(XMLWEvent::end_element())?;
|
||||
writer.write(
|
||||
XMLWEvent::start_element("Property")
|
||||
.attr("name", "_pos_y")
|
||||
.attr("value", &position.1.to_string()),
|
||||
)?;
|
||||
writer.write(XMLWEvent::end_element())?;
|
||||
}
|
||||
writer.write(XMLWEvent::end_element())?;
|
||||
}
|
||||
//Get the link and write it.
|
||||
|
@ -826,7 +874,7 @@ impl GraphView {
|
|||
}
|
||||
|
||||
pub fn load_xml(&self, filename: &str) -> anyhow::Result<()> {
|
||||
let file = File::open(filename).unwrap();
|
||||
let file = File::open(filename)?;
|
||||
let file = BufReader::new(file);
|
||||
|
||||
let parser = EventReader::new(file);
|
||||
|
@ -849,6 +897,9 @@ impl GraphView {
|
|||
match name.to_string().as_str() {
|
||||
"Graph" => {
|
||||
println!("New graph detected");
|
||||
if let Some(id) = attrs.get::<String>(&String::from("id")) {
|
||||
self.set_id(id.parse::<u32>().expect("id should be an u32"));
|
||||
}
|
||||
}
|
||||
"Node" => {
|
||||
let id = attrs
|
||||
|
@ -935,7 +986,21 @@ impl GraphView {
|
|||
"Node" => {
|
||||
if let Some(node) = current_node {
|
||||
let id = node.id();
|
||||
let mut pos_x = 0 as f32;
|
||||
let mut pos_y = 0 as f32;
|
||||
if let Some(value) = node.property("_pos_x") {
|
||||
pos_x = value.parse::<f32>().unwrap();
|
||||
}
|
||||
if let Some(value) = node.property("_pos_y") {
|
||||
pos_y = value.parse::<f32>().unwrap();
|
||||
}
|
||||
|
||||
self.add_node(id, node);
|
||||
if let Some(node) = self.node(&id) {
|
||||
if pos_x != 0.0 || pos_y != 0.0 {
|
||||
self.move_node(&node.upcast(), pos_x, pos_y);
|
||||
}
|
||||
}
|
||||
self.update_current_node_id(id);
|
||||
}
|
||||
current_node = None;
|
||||
|
|
|
@ -71,6 +71,7 @@ mod imp {
|
|||
pub(super) ports: RefCell<HashMap<u32, Port>>,
|
||||
pub(super) num_ports_in: Cell<i32>,
|
||||
pub(super) num_ports_out: Cell<i32>,
|
||||
// Properties are differnet from GObject properties
|
||||
pub(super) properties: RefCell<HashMap<String, String>>,
|
||||
pub(super) selected: Cell<bool>,
|
||||
}
|
||||
|
@ -190,13 +191,18 @@ impl Node {
|
|||
self_.description.set_text(description);
|
||||
println!("{}", description);
|
||||
}
|
||||
pub fn hidden_property(&self, name: &str) -> bool {
|
||||
name.starts_with('_')
|
||||
}
|
||||
|
||||
fn update_description(&self) {
|
||||
let self_ = imp::Node::from_instance(self);
|
||||
let mut description = String::from("");
|
||||
for (name, value) in self_.properties.borrow().iter() {
|
||||
description.push_str(&format!("{}:{}", name, value));
|
||||
description.push('\n');
|
||||
if !self.hidden_property(name) {
|
||||
description.push_str(&format!("{}:{}", name, value));
|
||||
description.push('\n');
|
||||
}
|
||||
}
|
||||
self.set_description(&description);
|
||||
}
|
||||
|
@ -280,8 +286,8 @@ impl Node {
|
|||
self.update_description();
|
||||
}
|
||||
|
||||
pub fn update_node_properties(&self, new_properties: &HashMap<String, String>) {
|
||||
for (key, value) in new_properties {
|
||||
pub fn update_properties(&self, new_node_properties: &HashMap<String, String>) {
|
||||
for (key, value) in new_node_properties {
|
||||
self.add_property(key.clone(), value.clone());
|
||||
}
|
||||
}
|
||||
|
@ -291,6 +297,14 @@ impl Node {
|
|||
private.properties.borrow()
|
||||
}
|
||||
|
||||
pub fn property(&self, name: &str) -> Option<String> {
|
||||
let private = imp::Node::from_instance(self);
|
||||
if let Some(property) = private.properties.borrow().get(name) {
|
||||
return Some(property.clone());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn toggle_selected(&self) {
|
||||
self.set_selected(!self.selected());
|
||||
}
|
||||
|
|
|
@ -127,21 +127,15 @@ impl Pipeline {
|
|||
self.create_pipeline(&self.render_gst_launch(graphview))
|
||||
.map_err(|err| {
|
||||
GPS_ERROR!("Unable to start a pipeline: {}", err);
|
||||
})
|
||||
.unwrap();
|
||||
self.set_state(new_state)
|
||||
.map_err(|_| GPS_ERROR!("Unable to change state"))
|
||||
.unwrap();
|
||||
} else if self.state() == PipelineState::Paused {
|
||||
self.set_state(PipelineState::Playing)
|
||||
.map_err(|_| GPS_ERROR!("Unable to change state"))
|
||||
.unwrap();
|
||||
} else {
|
||||
self.set_state(PipelineState::Paused)
|
||||
.map_err(|_| GPS_ERROR!("Unable to change state"))
|
||||
.unwrap();
|
||||
err
|
||||
})?;
|
||||
}
|
||||
|
||||
self.set_state(new_state).map_err(|error| {
|
||||
GPS_ERROR!("Unable to change state {}", error);
|
||||
error
|
||||
})?;
|
||||
|
||||
Ok(self.state())
|
||||
}
|
||||
|
||||
|
@ -396,7 +390,9 @@ impl Pipeline {
|
|||
description.push_str(&format!("{} name={} ", node.name(), unique_name));
|
||||
elements.insert(unique_name.clone(), unique_name.clone());
|
||||
for (name, value) in node.properties().iter() {
|
||||
description.push_str(&format!("{}={}", name, value));
|
||||
if !node.hidden_property(name) {
|
||||
description.push_str(&format!("{}={}", name, value));
|
||||
}
|
||||
}
|
||||
|
||||
let ports = node.all_ports(PortDirection::Output);
|
||||
|
|
|
@ -42,6 +42,13 @@ impl Settings {
|
|||
path
|
||||
}
|
||||
|
||||
pub fn default_graph_file_path() -> PathBuf {
|
||||
let mut path = glib::user_config_dir();
|
||||
path.push(config::APP_ID);
|
||||
path.push("default_graph.toml");
|
||||
path
|
||||
}
|
||||
|
||||
// Public methods
|
||||
pub fn add_favorite(favorite: &str) {
|
||||
let mut settings = Settings::load_settings();
|
||||
|
|
Loading…
Reference in a new issue