2022-01-20 14:06:14 +00:00
|
|
|
// properties.rs
|
|
|
|
//
|
|
|
|
// Copyright 2022 Stéphane Cerveau <scerveau@collabora.com>
|
|
|
|
//
|
2022-02-09 10:28:59 +00:00
|
|
|
// This file is part of GstPipelineStudio
|
2022-01-20 14:06:14 +00:00
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
2022-02-09 10:28:59 +00:00
|
|
|
|
2022-01-20 14:06:14 +00:00
|
|
|
use crate::app::GPSApp;
|
2022-01-20 14:11:10 +00:00
|
|
|
use crate::gps as GPS;
|
2022-01-20 14:06:14 +00:00
|
|
|
use crate::logger;
|
2022-02-08 13:57:00 +00:00
|
|
|
use crate::ui as GPSUI;
|
2022-01-20 17:18:52 +00:00
|
|
|
use crate::{GPS_INFO, GPS_TRACE};
|
2022-01-20 14:06:14 +00:00
|
|
|
use gtk::glib;
|
|
|
|
use gtk::prelude::*;
|
|
|
|
|
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::rc::Rc;
|
|
|
|
|
2022-01-20 17:18:52 +00:00
|
|
|
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 property_to_widget<F: Fn(String, String) + 'static>(
|
|
|
|
app: &GPSApp,
|
|
|
|
node_id: u32,
|
|
|
|
element_name: &str,
|
|
|
|
property_name: &str,
|
|
|
|
param: &glib::ParamSpec,
|
|
|
|
f: F,
|
2022-02-01 14:59:12 +00:00
|
|
|
) -> Option<gtk::Widget> {
|
2022-01-20 17:18:52 +00:00
|
|
|
match param.type_() {
|
|
|
|
_t if param.type_() == glib::ParamSpecBoolean::static_type() => {
|
2022-02-01 14:59:12 +00:00
|
|
|
let check_button = gtk::CheckButton::new();
|
2022-01-20 17:18:52 +00:00
|
|
|
check_button.set_widget_name(property_name);
|
|
|
|
GPS_TRACE!("add CheckBox property : {}", check_button.widget_name());
|
|
|
|
if let Some(value) = app.element_property(node_id, property_name) {
|
|
|
|
check_button.set_active(value.parse::<bool>().unwrap_or(false));
|
|
|
|
} else if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
|
|
|
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
|
|
|
{
|
|
|
|
if let Ok(value) = GPS::ElementInfo::element_property(element_name, param.name()) {
|
|
|
|
check_button.set_active(value.parse::<bool>().unwrap_or(false));
|
|
|
|
}
|
|
|
|
} else if let Some(value) = value_as_str(param.default_value()) {
|
|
|
|
check_button.set_active(value.parse::<bool>().unwrap_or(false));
|
|
|
|
}
|
|
|
|
check_button.connect_toggled(glib::clone!(@weak check_button => move |c| {
|
|
|
|
f(c.widget_name().to_string(), c.is_active().to_string() );
|
|
|
|
}));
|
2022-02-01 14:59:12 +00:00
|
|
|
Some(check_button.upcast::<gtk::Widget>())
|
2022-01-20 17:18:52 +00:00
|
|
|
}
|
|
|
|
t if [
|
|
|
|
glib::ParamSpecInt::static_type(),
|
|
|
|
glib::ParamSpecUInt::static_type(),
|
|
|
|
glib::ParamSpecInt64::static_type(),
|
|
|
|
glib::ParamSpecUInt64::static_type(),
|
|
|
|
glib::ParamSpecString::static_type(),
|
|
|
|
]
|
|
|
|
.contains(&t) =>
|
|
|
|
{
|
2022-02-01 14:59:12 +00:00
|
|
|
let entry = gtk::Entry::new();
|
|
|
|
entry.set_width_request(350);
|
2022-01-20 17:18:52 +00:00
|
|
|
entry.set_widget_name(property_name);
|
|
|
|
GPS_TRACE!("Add Edit property : {}", entry.widget_name());
|
|
|
|
if let Some(value) = app.element_property(node_id, property_name) {
|
|
|
|
entry.set_text(&value);
|
|
|
|
} else if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
|
|
|
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
|
|
|
{
|
|
|
|
if let Ok(value) = GPS::ElementInfo::element_property(element_name, param.name()) {
|
|
|
|
entry.set_text(&value);
|
|
|
|
}
|
|
|
|
} else if let Some(value) = value_as_str(param.default_value()) {
|
|
|
|
entry.set_text(&value);
|
|
|
|
}
|
|
|
|
|
|
|
|
entry.connect_changed(glib::clone!(@weak entry=> move |e| {
|
|
|
|
f(e.widget_name().to_string(), e.text().to_string())
|
|
|
|
}));
|
2022-02-01 14:59:12 +00:00
|
|
|
Some(entry.upcast::<gtk::Widget>())
|
2022-01-20 17:18:52 +00:00
|
|
|
}
|
|
|
|
t if [
|
|
|
|
glib::ParamSpecEnum::static_type(),
|
|
|
|
glib::ParamSpecFlags::static_type(),
|
|
|
|
]
|
|
|
|
.contains(&t) =>
|
|
|
|
{
|
2022-02-01 14:59:12 +00:00
|
|
|
let combo = gtk::ComboBoxText::new();
|
|
|
|
|
2022-01-20 17:18:52 +00:00
|
|
|
combo.set_widget_name(property_name);
|
|
|
|
GPS_TRACE!("add ComboBox property : {}", combo.widget_name());
|
2022-02-08 13:57:00 +00:00
|
|
|
// Add an empty entry to be able to reset the value
|
|
|
|
combo.append_text("");
|
2022-01-20 17:18:52 +00:00
|
|
|
if t.is_a(glib::ParamSpecEnum::static_type()) {
|
|
|
|
let param = param
|
|
|
|
.clone()
|
|
|
|
.downcast::<glib::ParamSpecEnum>()
|
|
|
|
.expect("Should be a ParamSpecEnum");
|
|
|
|
let enums = param.enum_class();
|
|
|
|
for value in enums.values() {
|
|
|
|
combo.append_text(&format!(
|
|
|
|
"{}:{}:{}",
|
|
|
|
value.value(),
|
|
|
|
value.nick(),
|
|
|
|
value.name()
|
|
|
|
));
|
|
|
|
}
|
|
|
|
} else if t.is_a(glib::ParamSpecFlags::static_type()) {
|
|
|
|
let param = param
|
|
|
|
.clone()
|
|
|
|
.downcast::<glib::ParamSpecFlags>()
|
|
|
|
.expect("Should be a ParamSpecEnum");
|
|
|
|
let flags = param.flags_class();
|
|
|
|
for value in flags.values() {
|
|
|
|
combo.append_text(&format!(
|
2022-02-03 15:49:11 +00:00
|
|
|
"{}:{}:{}",
|
2022-01-20 17:18:52 +00:00
|
|
|
value.value(),
|
|
|
|
value.nick(),
|
|
|
|
value.name()
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(value) = app.element_property(node_id, property_name) {
|
|
|
|
//Retrieve the first value (index) from the property
|
2022-02-08 13:57:00 +00:00
|
|
|
combo.set_active(Some(value.parse::<u32>().unwrap_or(0) + 1));
|
2022-01-20 17:18:52 +00:00
|
|
|
} else if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE
|
|
|
|
|| (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE
|
|
|
|
{
|
|
|
|
if let Ok(value) = GPS::ElementInfo::element_property(element_name, param.name()) {
|
2022-02-08 13:57:00 +00:00
|
|
|
combo.set_active(Some(value.parse::<u32>().unwrap_or(0) + 1));
|
2022-01-20 17:18:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
combo.connect_changed(move |c| {
|
|
|
|
if let Some(text) = c.active_text() {
|
2022-02-08 13:34:23 +00:00
|
|
|
let value = text.to_string();
|
|
|
|
let value = value.split_once(':');
|
2022-02-08 13:57:00 +00:00
|
|
|
f(
|
|
|
|
c.widget_name().to_string(),
|
|
|
|
value.unwrap_or_default().0.to_string(),
|
|
|
|
);
|
2022-01-20 17:18:52 +00:00
|
|
|
}
|
|
|
|
});
|
2022-02-01 14:59:12 +00:00
|
|
|
Some(combo.upcast::<gtk::Widget>())
|
2022-01-20 17:18:52 +00:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
GPS_INFO!(
|
|
|
|
"Property not supported : name={} type={}",
|
|
|
|
property_name,
|
|
|
|
param.type_()
|
|
|
|
);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-01-20 14:06:14 +00:00
|
|
|
|
2022-02-04 16:34:56 +00:00
|
|
|
pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32) {
|
2022-01-20 14:06:14 +00:00
|
|
|
let update_properties: Rc<RefCell<HashMap<String, String>>> =
|
|
|
|
Rc::new(RefCell::new(HashMap::new()));
|
2022-01-20 14:11:10 +00:00
|
|
|
let properties = GPS::ElementInfo::element_properties(element_name).unwrap();
|
2022-01-20 15:17:06 +00:00
|
|
|
|
2022-02-01 14:59:12 +00:00
|
|
|
let grid = gtk::Grid::new();
|
|
|
|
grid.set_column_spacing(4);
|
|
|
|
grid.set_row_spacing(4);
|
|
|
|
grid.set_margin_bottom(12);
|
2022-01-20 17:18:52 +00:00
|
|
|
|
|
|
|
let mut properties: Vec<(&String, &glib::ParamSpec)> = properties.iter().collect();
|
|
|
|
properties.sort_by(|a, b| a.0.cmp(b.0));
|
|
|
|
let mut i = 0;
|
|
|
|
for (name, param) in properties {
|
2022-01-20 15:17:06 +00:00
|
|
|
//Entry
|
2022-01-20 17:18:52 +00:00
|
|
|
let widget = property_to_widget(
|
|
|
|
app,
|
|
|
|
node_id,
|
|
|
|
element_name,
|
|
|
|
name,
|
|
|
|
param,
|
|
|
|
glib::clone!(@strong update_properties => move |name, value| {
|
2022-02-08 13:57:00 +00:00
|
|
|
GPS_INFO!("property changed: {}:{}", name, value);
|
2022-01-20 17:18:52 +00:00
|
|
|
update_properties.borrow_mut().insert(name, value);
|
2022-01-20 14:06:14 +00:00
|
|
|
}),
|
|
|
|
);
|
2022-01-20 17:18:52 +00:00
|
|
|
if let Some(widget) = widget {
|
2022-02-10 12:23:22 +00:00
|
|
|
let label = gtk::Label::builder()
|
|
|
|
.label(name)
|
|
|
|
.hexpand(true)
|
|
|
|
.halign(gtk::Align::Start)
|
|
|
|
.margin_start(4)
|
|
|
|
.build();
|
2022-01-20 17:18:52 +00:00
|
|
|
grid.attach(&label, 0, i, 1, 1);
|
|
|
|
grid.attach(&widget, 1, i, 1, 1);
|
|
|
|
i += 1;
|
|
|
|
}
|
2022-01-20 14:06:14 +00:00
|
|
|
}
|
|
|
|
|
2022-02-08 13:57:00 +00:00
|
|
|
let dialog = GPSUI::dialog::create_dialog(
|
2022-02-04 16:34:56 +00:00
|
|
|
&format!("{} properties", element_name),
|
|
|
|
app,
|
|
|
|
&grid,
|
|
|
|
glib::clone!(@strong update_properties => move |app, dialog| {
|
2022-01-20 14:06:14 +00:00
|
|
|
app.update_element_properties(node_id, &update_properties.borrow());
|
|
|
|
dialog.close();
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
dialog.show();
|
|
|
|
}
|
2022-02-03 15:49:11 +00:00
|
|
|
|
|
|
|
pub fn display_pad_properties(
|
|
|
|
app: &GPSApp,
|
|
|
|
element_name: &str,
|
|
|
|
port_name: &str,
|
|
|
|
node_id: u32,
|
|
|
|
port_id: u32,
|
|
|
|
) {
|
|
|
|
let update_properties: Rc<RefCell<HashMap<String, String>>> =
|
|
|
|
Rc::new(RefCell::new(HashMap::new()));
|
|
|
|
|
|
|
|
let grid = gtk::Grid::new();
|
|
|
|
grid.set_column_spacing(4);
|
|
|
|
grid.set_row_spacing(4);
|
|
|
|
grid.set_margin_bottom(12);
|
|
|
|
|
|
|
|
let mut i = 0;
|
|
|
|
let properties = app.pad_properties(node_id, port_id);
|
|
|
|
for (name, value) in properties {
|
2022-02-10 12:23:22 +00:00
|
|
|
let property_name = gtk::Label::builder()
|
|
|
|
.label(&name)
|
|
|
|
.hexpand(true)
|
|
|
|
.halign(gtk::Align::Start)
|
|
|
|
.margin_start(4)
|
|
|
|
.build();
|
2022-02-03 15:49:11 +00:00
|
|
|
let property_value = gtk::Entry::new();
|
|
|
|
property_value.set_width_request(150);
|
|
|
|
property_value.set_text(&value);
|
|
|
|
property_value.connect_changed(
|
|
|
|
glib::clone!(@weak property_name, @weak property_value, @strong update_properties=> move |_| {
|
|
|
|
update_properties.borrow_mut().insert(property_name.text().to_string(), property_value.text().to_string());
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
grid.attach(&property_name, 0, i, 1, 1);
|
|
|
|
grid.attach(&property_value, 1, i, 1, 1);
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a new property allowing to set pads property.
|
2022-02-10 12:23:22 +00:00
|
|
|
let label = gtk::Label::builder()
|
|
|
|
.label("Add a new Property")
|
|
|
|
.hexpand(true)
|
|
|
|
.halign(gtk::Align::Start)
|
|
|
|
.margin_start(4)
|
|
|
|
.build();
|
2022-02-03 15:49:11 +00:00
|
|
|
|
|
|
|
let property_name = gtk::Entry::new();
|
|
|
|
property_name.set_width_request(150);
|
|
|
|
let property_value = gtk::Entry::new();
|
|
|
|
property_value.set_width_request(150);
|
|
|
|
|
|
|
|
property_name.connect_changed(
|
|
|
|
glib::clone!(@weak property_name, @weak property_value, @strong update_properties=> move |_| {
|
|
|
|
update_properties.borrow_mut().insert(property_name.text().to_string(), property_value.text().to_string());
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
property_value.connect_changed(
|
|
|
|
glib::clone!(@weak property_name, @weak property_value, @strong update_properties=> move |_| {
|
|
|
|
update_properties.borrow_mut().insert(property_name.text().to_string(), property_value.text().to_string());
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
grid.attach(&label, 0, i, 1, 1);
|
|
|
|
grid.attach(&property_name, 1, i, 1, 1);
|
|
|
|
grid.attach(&property_value, 2, i, 1, 1);
|
|
|
|
|
|
|
|
// Add all specific properties from the given element
|
|
|
|
|
2022-02-08 13:57:00 +00:00
|
|
|
let dialog = GPSUI::dialog::create_dialog(
|
2022-02-04 16:34:56 +00:00
|
|
|
&format!("{} properties from {}", port_name, element_name),
|
|
|
|
app,
|
|
|
|
&grid,
|
|
|
|
glib::clone!(@strong update_properties => move |app, dialog| {
|
2022-02-03 15:49:11 +00:00
|
|
|
for p in update_properties.borrow().values() {
|
|
|
|
GPS_INFO!("updated properties {}", p);
|
|
|
|
}
|
|
|
|
app.update_pad_properties(node_id, port_id, &update_properties.borrow());
|
|
|
|
dialog.close();
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
|
|
|
|
dialog.show();
|
|
|
|
}
|
2022-02-04 16:34:56 +00:00
|
|
|
|
|
|
|
pub fn display_pipeline_details(app: &GPSApp) {
|
|
|
|
let grid = gtk::Grid::new();
|
|
|
|
grid.set_column_spacing(4);
|
|
|
|
grid.set_row_spacing(4);
|
|
|
|
grid.set_margin_bottom(12);
|
|
|
|
|
|
|
|
if let Some(elements) = app.player.borrow().pipeline_elements() {
|
|
|
|
let elements_list = elements.join(" ");
|
|
|
|
let label = gtk::Label::builder()
|
2022-02-07 11:09:56 +00:00
|
|
|
.label(&format!("{} elements:", elements.len()))
|
2022-02-04 16:34:56 +00:00
|
|
|
.hexpand(true)
|
|
|
|
.halign(gtk::Align::Start)
|
|
|
|
.valign(gtk::Align::Start)
|
|
|
|
.margin_start(4)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
let value = gtk::Label::builder()
|
|
|
|
.label(&elements_list)
|
|
|
|
.hexpand(true)
|
|
|
|
.halign(gtk::Align::Start)
|
|
|
|
.margin_start(4)
|
|
|
|
.wrap(true)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
grid.attach(&label, 0, 0_i32, 1, 1);
|
|
|
|
grid.attach(&value, 1, 0_i32, 1, 1);
|
|
|
|
|
2022-02-08 13:57:00 +00:00
|
|
|
let dialog =
|
|
|
|
GPSUI::dialog::create_dialog("Pipeline properties", app, &grid, move |_app, dialog| {
|
|
|
|
dialog.close();
|
|
|
|
});
|
2022-02-04 16:34:56 +00:00
|
|
|
|
|
|
|
dialog.show();
|
|
|
|
}
|
|
|
|
}
|