mirror of
https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio.git
synced 2024-11-25 10:30:59 +00:00
plugindialogs: rename module and support properties in element
rename pluginlist module to plugindialogs Add display_plugin_properties to modify properties from an element. Add a way to modify the location if element is URI_TYPE_SRC Able to remove a node from the graph
This commit is contained in:
parent
6c7dccd450
commit
6756b34919
9 changed files with 310 additions and 53 deletions
2
TODO.md
2
TODO.md
|
@ -26,7 +26,9 @@ TODO:
|
|||
## bugs
|
||||
|
||||
- [] crash with x11 on contextual menu
|
||||
- [] check that element exists before creating it on file load.
|
||||
|
||||
## Code cleanup
|
||||
|
||||
[] remove useless code from graphview
|
||||
[] Move render to a specific module
|
||||
|
|
117
src/app.rs
117
src/app.rs
|
@ -25,11 +25,12 @@ use gtk::{
|
|||
FileChooserDialog, PopoverMenu, ResponseType, Statusbar, Viewport,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::{error, ops};
|
||||
|
||||
use crate::pipeline::{Pipeline, PipelineState};
|
||||
use crate::pluginlist;
|
||||
use crate::plugindialogs;
|
||||
|
||||
use crate::graphmanager::{GraphView, Node};
|
||||
|
||||
|
@ -115,6 +116,47 @@ impl GPSApp {
|
|||
app.drop();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_file_from_dialog<F: Fn(GPSApp, String) + 'static>(app: &GPSApp, save: bool, f: F) {
|
||||
let mut message = "Open file";
|
||||
let mut ok_button = "Open";
|
||||
let cancel_button = "Cancel";
|
||||
let mut action = FileChooserAction::Open;
|
||||
if save {
|
||||
message = "Save file";
|
||||
ok_button = "Save";
|
||||
action = FileChooserAction::Save;
|
||||
}
|
||||
|
||||
let file_chooser: FileChooserDialog = FileChooserDialog::new(
|
||||
Some(message),
|
||||
Some(&app.window),
|
||||
action,
|
||||
&[
|
||||
(ok_button, ResponseType::Ok),
|
||||
(cancel_button, ResponseType::Cancel),
|
||||
],
|
||||
);
|
||||
let app_weak = app.downgrade();
|
||||
file_chooser.connect_response(move |d: &FileChooserDialog, response: ResponseType| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
if response == ResponseType::Ok {
|
||||
let file = d.file().expect("Couldn't get file");
|
||||
let filename = String::from(
|
||||
file.path()
|
||||
.expect("Couldn't get file path")
|
||||
.to_str()
|
||||
.expect("unable to convert to string"),
|
||||
);
|
||||
f(app, filename);
|
||||
}
|
||||
|
||||
d.close();
|
||||
});
|
||||
|
||||
file_chooser.show();
|
||||
}
|
||||
|
||||
pub fn show_error_dialog(fatal: bool, message: &str) {
|
||||
let app = gio::Application::default()
|
||||
.expect("No default application")
|
||||
|
@ -165,25 +207,11 @@ impl GPSApp {
|
|||
// Add a dialog to open the graph
|
||||
action.connect_activate(glib::clone!(@weak window => move |_, _| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
let file_chooser = FileChooserDialog::new(
|
||||
Some("Open File"),
|
||||
Some(&window),
|
||||
FileChooserAction::Open,
|
||||
&[("Open", ResponseType::Ok), ("Cancel", ResponseType::Cancel)],
|
||||
);
|
||||
file_chooser.connect_response(move |d: &FileChooserDialog, response: ResponseType| {
|
||||
if response == ResponseType::Ok {
|
||||
let file = d.file().expect("Couldn't get file");
|
||||
let filename = String::from(file.path().expect("Couldn't get file path").to_str().expect("unable to convert to string"));
|
||||
GPSApp::get_file_from_dialog(&app, false, move |app, filename|
|
||||
{
|
||||
println!("Open file {}", filename);
|
||||
app.load_graph(&filename).expect("Unable to open file");
|
||||
}
|
||||
|
||||
d.close();
|
||||
});
|
||||
|
||||
file_chooser.show();
|
||||
|
||||
});
|
||||
}));
|
||||
application.add_action(&action);
|
||||
application.set_accels_for_action("app.open", &["<primary>o"]);
|
||||
|
@ -192,27 +220,14 @@ impl GPSApp {
|
|||
let action = gio::SimpleAction::new("save_as", None);
|
||||
let app_weak = self.downgrade();
|
||||
action.connect_activate(glib::clone!(@weak window => move |_, _| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
let file_chooser = FileChooserDialog::new(
|
||||
Some("Save File"),
|
||||
Some(&window),
|
||||
FileChooserAction::Open,
|
||||
&[("Save", ResponseType::Ok), ("Cancel", ResponseType::Cancel)],
|
||||
);
|
||||
file_chooser.connect_response(move |d: &FileChooserDialog, response: ResponseType| {
|
||||
if response == ResponseType::Ok {
|
||||
let file = d.file().expect("Couldn't get file");
|
||||
let filename = String::from(file.path().expect("Couldn't get file path").to_str().expect("unable to convert to string"));
|
||||
println!("Save file {}", filename);
|
||||
app.save_graph(&filename).expect("Unable to save file");
|
||||
}
|
||||
|
||||
d.close();
|
||||
});
|
||||
|
||||
file_chooser.show();
|
||||
|
||||
}));
|
||||
let app = upgrade_weak!(app_weak);
|
||||
GPSApp::get_file_from_dialog(&app, true, move |app, filename|
|
||||
{
|
||||
println!("Save file {}", filename);
|
||||
app.save_graph(&filename).expect("Unable to save file");
|
||||
});
|
||||
}));
|
||||
|
||||
application.add_action(&action);
|
||||
application.set_accels_for_action("app.save", &["<primary>s"]);
|
||||
|
@ -255,7 +270,7 @@ impl GPSApp {
|
|||
add_button.connect_clicked(glib::clone!(@weak window => move |_| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
let elements = Pipeline::elements_list().expect("Unable to obtain element's list");
|
||||
pluginlist::display_plugin_list(&app, &elements);
|
||||
plugindialogs::display_plugin_list(&app, &elements);
|
||||
}));
|
||||
|
||||
let add_button: Button = self
|
||||
|
@ -319,7 +334,7 @@ impl GPSApp {
|
|||
node_id,
|
||||
Node::new(
|
||||
node_id,
|
||||
"videotestsrc",
|
||||
"filesrc",
|
||||
Pipeline::element_type("videotestsrc"),
|
||||
),
|
||||
0,
|
||||
|
@ -395,8 +410,11 @@ impl GPSApp {
|
|||
height: 0,
|
||||
});
|
||||
let action = gio::SimpleAction::new("node.delete", None);
|
||||
let app_weak = app.downgrade();
|
||||
action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
println!("node.delete {}", node_id);
|
||||
app.graphview.borrow_mut().remove_node(node_id);
|
||||
pop_menu.unparent();
|
||||
}));
|
||||
application.add_action(&action);
|
||||
|
@ -406,15 +424,18 @@ impl GPSApp {
|
|||
println!("node.request-pad {}", node_id);
|
||||
pop_menu.unparent();
|
||||
}));
|
||||
|
||||
application.add_action(&action);
|
||||
let action = gio::SimpleAction::new("node.properties", None);
|
||||
action.connect_activate(glib::clone!(@weak pop_menu => move |_,_| {
|
||||
println!("node.properties {}", node_id);
|
||||
|
||||
let node = app.graphview.borrow().node(&node_id).unwrap();
|
||||
plugindialogs::display_plugin_properties(&app, &node.name(), node_id);
|
||||
pop_menu.unparent();
|
||||
}));
|
||||
application.add_action(&action);
|
||||
|
||||
|
||||
pop_menu.show();
|
||||
None
|
||||
}),
|
||||
|
@ -434,6 +455,15 @@ impl GPSApp {
|
|||
let graph_view = self.graphview.borrow_mut();
|
||||
let node_id = graph_view.next_node_id();
|
||||
let pads = Pipeline::pads(&element_name, false);
|
||||
if Pipeline::element_is_uri_src_handler(&element_name) {
|
||||
GPSApp::get_file_from_dialog(self, false, move |app, filename| {
|
||||
println!("Open file {}", filename);
|
||||
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);
|
||||
});
|
||||
}
|
||||
graph_view.add_node_with_port(
|
||||
node_id,
|
||||
Node::new(
|
||||
|
@ -446,6 +476,11 @@ 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);
|
||||
}
|
||||
|
||||
pub fn save_graph(&self, filename: &str) -> anyhow::Result<(), Box<dyn error::Error>> {
|
||||
let graph_view = self.graphview.borrow_mut();
|
||||
graph_view.render_xml(filename)?;
|
||||
|
|
38
src/gps.ui
38
src/gps.ui
|
@ -68,8 +68,8 @@
|
|||
<property name="default-height">260</property>
|
||||
<property name="hide-on-close">True</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="v_box">
|
||||
<property name="orientation">horizontal</property>
|
||||
<object class="GtkPaned">
|
||||
<property name="position">200</property>s
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||
<property name="hexpand">True</property>
|
||||
|
@ -80,7 +80,7 @@
|
|||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled2_window">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<child>
|
||||
|
@ -91,6 +91,36 @@
|
|||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkDialog" id="dialog-plugin-properties">
|
||||
<property name="transient-for">mainwindow</property>
|
||||
<property name="default-width">320</property>
|
||||
<property name="default-height">260</property>
|
||||
<property name="hide-on-close">True</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box-plugin-properties">
|
||||
<property name="orientation">vertical</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="apply-plugin-properties">
|
||||
<property name="halign">end</property>
|
||||
<property name="hexpand">1</property>
|
||||
<property name="receives-default">1</property>
|
||||
<property name="label" translatable="yes">apply</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkApplicationWindow" id="mainwindow">
|
||||
<property name="title" translatable="yes">GstPipelineStudio</property>
|
||||
<property name="default-width">800</property>
|
||||
|
@ -153,7 +183,7 @@
|
|||
<object class="GtkButton" id="button-clear">
|
||||
<property name="hexpand">1</property>
|
||||
<property name="receives-default">1</property>
|
||||
<property name="label">clear</property>
|
||||
<property name="label" translatable="yes">_clear</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
|
@ -412,6 +412,10 @@ impl GraphView {
|
|||
let private = imp::GraphView::from_instance(self);
|
||||
let mut nodes = private.nodes.borrow_mut();
|
||||
if let Some(node) = nodes.remove(&id) {
|
||||
if let Some(link_id) = self.node_is_linked(node.id()) {
|
||||
let mut links = private.links.borrow_mut();
|
||||
links.remove(&link_id);
|
||||
}
|
||||
node.unparent();
|
||||
} else {
|
||||
warn!("Tried to remove non-existant node (id={}) from graph", id);
|
||||
|
@ -431,14 +435,15 @@ impl GraphView {
|
|||
nodes_list
|
||||
}
|
||||
|
||||
pub fn node(&self, id: &u32) -> Option<super::node::Node> {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
private.nodes.borrow().get(id).cloned()
|
||||
}
|
||||
|
||||
pub fn remove_all_nodes(&self) {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let nodes_list = self.all_nodes(NodeType::All);
|
||||
for node in nodes_list {
|
||||
if let Some(link_id) = self.node_is_linked(node.id()) {
|
||||
let mut links = private.links.borrow_mut();
|
||||
links.remove(&link_id);
|
||||
}
|
||||
self.remove_node(node.id());
|
||||
}
|
||||
private.current_node_id.set(0);
|
||||
|
@ -493,7 +498,6 @@ impl GraphView {
|
|||
}
|
||||
|
||||
// Link related methods
|
||||
|
||||
pub fn all_links(&self) -> Vec<NodeLink> {
|
||||
let private = imp::GraphView::from_instance(self);
|
||||
let links = private.links.borrow();
|
||||
|
@ -668,6 +672,10 @@ impl GraphView {
|
|||
description.push_str(" ! ");
|
||||
}
|
||||
|
||||
for (name, value) in node.properties().iter() {
|
||||
description.push_str(&format!(" {}={}", name, value));
|
||||
}
|
||||
|
||||
println!("{}", description);
|
||||
for port in ports {
|
||||
if let Some((_port_to, node_to)) = self.port_connected_to(port.id()) {
|
||||
|
@ -714,6 +722,15 @@ impl GraphView {
|
|||
)?;
|
||||
writer.write(XMLWEvent::end_element())?;
|
||||
}
|
||||
|
||||
for (name, value) in node.properties().iter() {
|
||||
writer.write(
|
||||
XMLWEvent::start_element("Property")
|
||||
.attr("name", name)
|
||||
.attr("value", value),
|
||||
)?;
|
||||
writer.write(XMLWEvent::end_element())?;
|
||||
}
|
||||
writer.write(XMLWEvent::end_element())?;
|
||||
}
|
||||
//Get the link and write it.
|
||||
|
@ -775,6 +792,17 @@ impl GraphView {
|
|||
NodeType::from_str(node_type.as_str()),
|
||||
));
|
||||
}
|
||||
"Property" => {
|
||||
let name = attrs
|
||||
.get::<String>(&String::from("name"))
|
||||
.expect("Unable to find property name");
|
||||
let value: &String = attrs
|
||||
.get::<String>(&String::from("value"))
|
||||
.expect("Unable to find property value");
|
||||
let node = current_node.clone();
|
||||
node.expect("current node does not exist")
|
||||
.add_property(name.clone(), value.clone());
|
||||
}
|
||||
"Port" => {
|
||||
let id = attrs
|
||||
.get::<String>(&String::from("id"))
|
||||
|
@ -835,6 +863,7 @@ impl GraphView {
|
|||
}
|
||||
current_node = None;
|
||||
}
|
||||
"Property" => {}
|
||||
"Port" => {
|
||||
if let Some(port) = current_port {
|
||||
let node = current_node.clone();
|
||||
|
|
|
@ -67,6 +67,7 @@ mod imp {
|
|||
pub(super) ports: RefCell<HashMap<u32, Port>>,
|
||||
pub(super) num_ports_in: Cell<i32>,
|
||||
pub(super) num_ports_out: Cell<i32>,
|
||||
pub(super) properties: RefCell<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -97,6 +98,7 @@ mod imp {
|
|||
ports: RefCell::new(HashMap::new()),
|
||||
num_ports_in: Cell::new(0),
|
||||
num_ports_out: Cell::new(0),
|
||||
properties: RefCell::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,4 +216,21 @@ impl Node {
|
|||
let private = imp::Node::from_instance(self);
|
||||
private.node_type.get()
|
||||
}
|
||||
|
||||
pub fn add_property(&self, name: String, value: String) {
|
||||
let private = imp::Node::from_instance(self);
|
||||
println!("{} {} updated", name, value);
|
||||
private.properties.borrow_mut().insert(name, value);
|
||||
}
|
||||
|
||||
pub fn update_node_properties(&self, new_properties: &HashMap<String, String>) {
|
||||
for (key, value) in new_properties {
|
||||
self.add_property(key.clone(), value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn properties(&self) -> Ref<HashMap<String, String>> {
|
||||
let private = imp::Node::from_instance(self);
|
||||
private.properties.borrow()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,3 +12,22 @@ macro_rules! upgrade_weak {
|
|||
upgrade_weak!($x, ())
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! str_some_value (
|
||||
($value:expr, $t:ty) => (
|
||||
{
|
||||
let value = $value.get::<$t>().expect("ser_some_value macro");
|
||||
value
|
||||
}
|
||||
);
|
||||
);
|
||||
macro_rules! str_opt_value (
|
||||
($value:expr, $t:ty) => (
|
||||
{
|
||||
match $value.get::<$t>() {
|
||||
Ok(v) => Some(v),
|
||||
Err(_e) => None
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
|
|
@ -23,7 +23,7 @@ mod app;
|
|||
mod common;
|
||||
mod graphmanager;
|
||||
mod pipeline;
|
||||
mod pluginlist;
|
||||
mod plugindialogs;
|
||||
use gtk::prelude::*;
|
||||
|
||||
use crate::app::GPSApp;
|
||||
|
|
|
@ -21,6 +21,7 @@ use crate::graphmanager::NodeType;
|
|||
use gst::prelude::*;
|
||||
use gstreamer as gst;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
|
@ -304,6 +305,64 @@ impl Pipeline {
|
|||
|
||||
element_type
|
||||
}
|
||||
|
||||
fn value_as_str(v: &glib::Value) -> Option<String> {
|
||||
match v.type_() {
|
||||
glib::Type::I8 => Some(str_some_value!(v, i8).to_string()),
|
||||
glib::Type::U8 => Some(str_some_value!(v, u8).to_string()),
|
||||
glib::Type::BOOL => Some(str_some_value!(v, bool).to_string()),
|
||||
glib::Type::I32 => Some(str_some_value!(v, i32).to_string()),
|
||||
glib::Type::U32 => Some(str_some_value!(v, u32).to_string()),
|
||||
glib::Type::I64 => Some(str_some_value!(v, i64).to_string()),
|
||||
glib::Type::U64 => Some(str_some_value!(v, u64).to_string()),
|
||||
glib::Type::F32 => Some(str_some_value!(v, f32).to_string()),
|
||||
glib::Type::F64 => Some(str_some_value!(v, f64).to_string()),
|
||||
glib::Type::STRING => str_opt_value!(v, String),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn element_properties(
|
||||
element_name: &str,
|
||||
) -> anyhow::Result<HashMap<String, String>, Box<dyn error::Error>> {
|
||||
let mut properties_list = HashMap::new();
|
||||
let feature = Pipeline::element_feature(element_name).expect("Unable to get feature");
|
||||
|
||||
let factory = feature
|
||||
.downcast::<gst::ElementFactory>()
|
||||
.expect("Factory not found");
|
||||
let element = factory.create(None)?;
|
||||
let params = element.class().list_properties();
|
||||
|
||||
for param in params {
|
||||
println!("Property_name {}", param.name());
|
||||
if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
||||
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
||||
{
|
||||
let value = Pipeline::value_as_str(&element.property(param.name()).unwrap())
|
||||
.unwrap_or_else(|| String::from(""));
|
||||
properties_list.insert(String::from(param.name()), value);
|
||||
} else if let Some(value) = Pipeline::value_as_str(param.default_value()) {
|
||||
properties_list.insert(String::from(param.name()), value);
|
||||
} else {
|
||||
println!("Unable to add property_name {}", param.name());
|
||||
}
|
||||
}
|
||||
Ok(properties_list)
|
||||
}
|
||||
|
||||
pub fn element_is_uri_src_handler(element_name: &str) -> bool {
|
||||
let feature = Pipeline::element_feature(element_name).expect("Unable to get feature");
|
||||
|
||||
let factory = feature
|
||||
.downcast::<gst::ElementFactory>()
|
||||
.expect("Factory not found");
|
||||
let element = factory.create(None).expect("Unable to get factory");
|
||||
match element.dynamic_cast::<gst::URIHandler>() {
|
||||
Ok(uri_handler) => uri_handler.uri_type() == gst::URIType::Src,
|
||||
Err(_e) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PipelineInner {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// pluginlist.rs
|
||||
// plugindialogs.rs
|
||||
//
|
||||
// Copyright 2021 Stéphane Cerveau <scerveau@collabora.com>
|
||||
//
|
||||
|
@ -22,8 +22,14 @@ use crate::pipeline::Pipeline;
|
|||
use gtk::glib;
|
||||
use gtk::prelude::*;
|
||||
use gtk::TextBuffer;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use gtk::{CellRendererText, Dialog, ListStore, TextView, TreeView, TreeViewColumn};
|
||||
use gtk::{
|
||||
Box, Button, CellRendererText, Dialog, Entry, Label, ListStore, TextView, TreeView,
|
||||
TreeViewColumn,
|
||||
};
|
||||
|
||||
fn create_and_fill_model(elements: &[ElementInfo]) -> ListStore {
|
||||
// Creation of a model with two rows.
|
||||
|
@ -115,3 +121,61 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) {
|
|||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32) {
|
||||
let dialog: Dialog = app
|
||||
.builder
|
||||
.object("dialog-plugin-properties")
|
||||
.expect("Couldn't get window");
|
||||
|
||||
dialog.set_title(Some(&format!("{} properties", element_name)));
|
||||
dialog.set_default_size(640, 480);
|
||||
dialog.set_modal(true);
|
||||
|
||||
let properties_box: Box = app
|
||||
.builder
|
||||
.object("box-plugin-properties")
|
||||
.expect("Couldn't get window");
|
||||
let update_properties: Rc<RefCell<HashMap<String, String>>> =
|
||||
Rc::new(RefCell::new(HashMap::new()));
|
||||
let properties = Pipeline::element_properties(element_name).unwrap();
|
||||
for (name, value) in properties {
|
||||
let entry_box = Box::new(gtk::Orientation::Horizontal, 6);
|
||||
let label = Label::new(Some(&name));
|
||||
label.set_hexpand(true);
|
||||
label.set_halign(gtk::Align::Start);
|
||||
label.set_margin_start(4);
|
||||
entry_box.append(&label);
|
||||
let entry: Entry = Entry::new();
|
||||
entry.set_text(&value);
|
||||
entry.set_hexpand(true);
|
||||
entry.set_halign(gtk::Align::Start);
|
||||
entry.set_widget_name(&name);
|
||||
entry.connect_changed(
|
||||
glib::clone!(@weak entry, @strong update_properties => move |_| {
|
||||
println!("{}:{}", entry.widget_name(), entry.text());
|
||||
update_properties.borrow_mut().insert(entry.widget_name().to_string(), entry.text().to_string());
|
||||
}),
|
||||
);
|
||||
entry_box.append(&entry);
|
||||
properties_box.append(&entry_box);
|
||||
}
|
||||
let properties_apply_btn: Button = app
|
||||
.builder
|
||||
.object("apply-plugin-properties")
|
||||
.expect("Couldn't get window");
|
||||
|
||||
let app_weak = app.downgrade();
|
||||
properties_apply_btn.connect_clicked(
|
||||
glib::clone!(@strong update_properties, @weak dialog => move |_| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
app.update_element_properties(node_id, &update_properties.borrow());
|
||||
dialog.close();
|
||||
}),
|
||||
);
|
||||
|
||||
dialog.show();
|
||||
for p in update_properties.borrow().values() {
|
||||
println!("updated properties {}", p);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue