gtk4: add custom widget automatically updating the window size

Use it in the example and debug window but let's not make it public yet.
Plan is to have a proper bin on top of gtk4paintablesink at some point.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1680>
This commit is contained in:
Guillaume Desmottes 2024-08-05 17:07:17 +02:00 committed by GStreamer Marge Bot
parent 17910dd532
commit cfe9968a77
6 changed files with 146 additions and 38 deletions

View file

@ -62,23 +62,8 @@ fn create_ui(app: &gtk::Application) {
let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
let picture = gtk::Picture::new();
picture.set_paintable(Some(&paintable));
#[cfg(feature = "gtk_v4_14")]
{
let offload = gtk::GraphicsOffload::new(Some(&picture));
offload.set_enabled(gtk::GraphicsOffloadEnabled::Enabled);
#[cfg(feature = "gtk_v4_16")]
{
offload.set_black_background(true);
}
vbox.append(&offload);
}
#[cfg(not(feature = "gtk_v4_14"))]
{
vbox.append(&picture);
}
let gst_widget = gstgtk4::RenderWidget::new(&gtksink);
vbox.append(&gst_widget);
let label = gtk::Label::new(Some("Position: 00:00:00"));
vbox.append(&label);

View file

@ -21,6 +21,10 @@ mod sink;
mod utils;
pub use sink::frame::Orientation;
pub use sink::paintable::Paintable;
// The widget needs to be public so it can be used by the example and element debug window but
// we don't want it be part of the official API for now.
#[doc(hidden)]
pub use sink::render_widget::RenderWidget;
pub use sink::PaintableSink;
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {

View file

@ -831,29 +831,11 @@ impl PaintableSink {
return;
}
let paintable = match &*self_.paintable.lock().unwrap() {
Some(paintable) => paintable.get_ref().clone(),
None => return,
};
let window = gtk::Window::new();
let picture = gtk::Picture::new();
picture.set_paintable(Some(&paintable));
#[cfg(feature = "gtk_v4_14")]
{
let offload = gtk::GraphicsOffload::new(Some(&picture));
offload.set_enabled(gtk::GraphicsOffloadEnabled::Enabled);
#[cfg(feature = "gtk_v4_16")]
{
offload.set_black_background(true);
}
window.set_child(Some(&offload));
}
#[cfg(not(feature = "gtk_v4_14"))]
{
window.set_child(Some(&picture));
}
let gst_widget = crate::RenderWidget::new(self_.obj().as_ref().upcast_ref());
window.set_child(Some(&gst_widget));
window.set_default_size(640, 480);
if std::env::var("GST_GTK4_WINDOW_FULLSCREEN").as_deref() == Ok("1") {
window.set_fullscreened(true);

View file

@ -41,6 +41,7 @@ use gtk::glib::prelude::*;
pub(super) mod frame;
pub(super) mod imp;
pub(super) mod paintable;
pub(super) mod render_widget;
enum SinkEvent {
FrameChanged,

View file

@ -0,0 +1,113 @@
//
// Copyright (C) 2024 Guillaume Desmottes <guillaume@desmottes.be>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use std::cell::{Cell, RefCell};
use gtk::{gdk, glib, prelude::*, subclass::prelude::*};
use once_cell::sync::Lazy;
#[derive(Default)]
pub struct RenderWidget {
element: RefCell<Option<crate::PaintableSink>>,
window_size: Cell<(u32, u32)>,
}
#[glib::object_subclass]
impl ObjectSubclass for RenderWidget {
const NAME: &'static str = "GstGtk4ExampleRenderWidget";
type Type = super::RenderWidget;
type ParentType = gtk::Widget;
fn class_init(klass: &mut Self::Class) {
klass.set_layout_manager_type::<gtk::BinLayout>();
}
}
impl ObjectImpl for RenderWidget {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpecObject::builder::<crate::PaintableSink>("element")
.nick("Element")
.blurb("The GTK4 Paintable Sink GStreamer element")
.construct_only()
.build(),
]
});
PROPERTIES.as_ref()
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"element" => self.element.borrow().to_value(),
_ => unimplemented!(),
}
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"element" => {
*self.element.borrow_mut() = value.get::<Option<crate::PaintableSink>>().unwrap();
}
_ => unimplemented!(),
}
}
fn constructed(&self) {
self.parent_constructed();
let element = self.element.borrow();
let element = element.as_ref().unwrap();
let paintable = element.property::<gdk::Paintable>("paintable");
let picture = gtk::Picture::new();
picture.set_paintable(Some(&paintable));
#[cfg(feature = "gtk_v4_14")]
{
let offload = gtk::GraphicsOffload::new(Some(&picture));
offload.set_enabled(gtk::GraphicsOffloadEnabled::Enabled);
#[cfg(feature = "gtk_v4_16")]
{
offload.set_black_background(true);
}
offload.set_parent(self.obj().as_ref());
}
#[cfg(not(feature = "gtk_v4_14"))]
{
picture.set_parent(self.obj().as_ref());
}
}
fn dispose(&self) {
while let Some(child) = self.obj().first_child() {
child.unparent();
}
}
}
impl WidgetImpl for RenderWidget {
fn snapshot(&self, snapshot: &gtk::Snapshot) {
let window_width = self.obj().width() as u32;
let window_height = self.obj().height() as u32;
let new_size = (window_width, window_height);
let updated = self.window_size.replace(new_size) != new_size;
if updated {
let element = self.element.borrow();
let element = element.as_ref().unwrap();
element.set_property("window-width", window_width);
element.set_property("window-height", window_height);
}
self.parent_snapshot(snapshot)
}
}

View file

@ -0,0 +1,23 @@
//
// Copyright (C) 2024 Guillaume Desmottes <guillaume@desmottes.be>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use gtk::glib;
mod imp;
/// Use a simple container widget to automatically pass the window size to gtk4paintablesink.
glib::wrapper! {
pub struct RenderWidget(ObjectSubclass<imp::RenderWidget>) @extends gtk::Widget;
}
impl RenderWidget {
pub fn new(element: &gst::Element) -> Self {
glib::Object::builder().property("element", element).build()
}
}