lib: Introduce Clapper GTK integration library

An easy to use GTK integration library. Meant as a GtkVideo alternative.

While GtkVideo is more of a simple example of video playback under GTK4, this
acts as a full-fledged video player in the form of a GtkWidget that can be placed
anywhere within application.

The widget offers customization options for both top header and bottom playback
controls panels. The Clapper playback API is exposed under widget "player" property
making it easy for the programmer to set media and control playback programically.

The new library will be distributed with Clapper player. This includes public headers
and GObject Introspection support.

Licensed under LGPL-2.1-or-later.
This commit is contained in:
Rafał Dzięgiel
2024-03-13 20:58:00 +01:00
parent d7f069d6c3
commit 675ddc85a1
91 changed files with 11030 additions and 0 deletions

View File

@@ -0,0 +1,344 @@
/* Clapper GTK Integration Library
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* ClapperGtkSimpleControls:
*
* A minimalistic playback controls panel widget.
*
* #ClapperGtkSimpleControls is a simple, ready to be used playback controls widget.
* It is meant to be placed as an overlay (either fading or not) of [class@ClapperGtk.Video]
* as-is, providing minimal yet universal playback controls for your app.
*
* If you need a further customized controls, please use individual widgets this
* widget consists of to build your own controls implementation instead.
*/
#include "config.h"
#include <clapper/clapper.h>
#include "clapper-gtk-simple-controls.h"
#include "clapper-gtk-seek-bar.h"
#define DEFAULT_FULLSCREENABLE TRUE
#define DEFAULT_SEEK_METHOD CLAPPER_PLAYER_SEEK_METHOD_NORMAL
#define IS_REVEALED(widget) (gtk_revealer_get_child_revealed ((GtkRevealer *) (widget)))
#define IS_REVEAL(widget) (gtk_revealer_get_reveal_child ((GtkRevealer *) (widget)))
#define SET_REVEAL(widget,reveal) (gtk_revealer_set_reveal_child ((GtkRevealer *) (widget), reveal))
#define GST_CAT_DEFAULT clapper_gtk_simple_controls_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkSimpleControls
{
ClapperGtkContainer parent;
GtkWidget *seek_bar;
GtkWidget *extra_menu_button;
GtkWidget *fullscreen_top_revealer;
GtkWidget *fullscreen_bottom_revealer;
GtkWidget *controls_slide_revealer;
gboolean fullscreenable;
ClapperPlayerSeekMethod seek_method;
gboolean adapt;
};
#define parent_class clapper_gtk_simple_controls_parent_class
G_DEFINE_TYPE (ClapperGtkSimpleControls, clapper_gtk_simple_controls, CLAPPER_GTK_TYPE_CONTAINER)
enum
{
PROP_0,
PROP_FULLSCREENABLE,
PROP_SEEK_METHOD,
PROP_EXTRA_MENU_BUTTON,
PROP_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static void
initial_adapt_cb (ClapperGtkContainer *container, gboolean adapt,
ClapperGtkSimpleControls *self)
{
GST_DEBUG_OBJECT (self, "Initially adapted: %s", (adapt) ? "yes" : "no");
clapper_gtk_seek_bar_set_reveal_labels (CLAPPER_GTK_SEEK_BAR (self->seek_bar), !adapt);
}
static void
full_adapt_cb (ClapperGtkContainer *container, gboolean adapt,
ClapperGtkSimpleControls *self)
{
self->adapt = adapt;
GST_DEBUG_OBJECT (self, "Width adapted: %s", (self->adapt) ? "yes" : "no");
/* Take different action, depending on transition step we are currently at */
if (self->adapt) {
if (IS_REVEAL (self->fullscreen_bottom_revealer))
SET_REVEAL (self->fullscreen_bottom_revealer, FALSE);
else if (IS_REVEAL (self->controls_slide_revealer))
SET_REVEAL (self->controls_slide_revealer, FALSE);
else
SET_REVEAL (self->fullscreen_top_revealer, TRUE);
} else {
if (IS_REVEAL (self->fullscreen_top_revealer))
SET_REVEAL (self->fullscreen_top_revealer, FALSE);
else if (!IS_REVEAL (self->controls_slide_revealer))
SET_REVEAL (self->controls_slide_revealer, TRUE);
else
SET_REVEAL (self->fullscreen_bottom_revealer, TRUE);
}
}
static void
controls_revealed_cb (GtkRevealer *revealer,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkSimpleControls *self)
{
gboolean revealed = IS_REVEALED (revealer);
GST_DEBUG_OBJECT (self, "Slide revealed: %s", (revealed) ? "yes" : "no");
/* We should be hidden when adapted, otherwise go back */
if (G_UNLIKELY (revealed == self->adapt))
gtk_revealer_set_reveal_child (revealer, !revealed);
}
/**
* clapper_gtk_simple_controls_new:
*
* Creates a new #ClapperGtkSimpleControls instance.
*
* Returns: a new simple controls #GtkWidget.
*/
GtkWidget *
clapper_gtk_simple_controls_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_SIMPLE_CONTROLS, NULL);
}
/**
* clapper_gtk_simple_controls_set_fullscreenable:
* @controls: a #ClapperGtkSimpleControls
* @fullscreenable: whether show button for toggling fullscreen state
*
* Set whether [class@ClapperGtk.ToggleFullscreenButton] button in the @controls
* should be visible.
*
* You might want to consider setting this to %FALSE, if your application
* does not implement [signal@ClapperGtk.Video::toggle-fullscreen] signal.
*/
void
clapper_gtk_simple_controls_set_fullscreenable (ClapperGtkSimpleControls *self, gboolean fullscreenable)
{
g_return_if_fail (CLAPPER_GTK_IS_SIMPLE_CONTROLS (self));
if (self->fullscreenable != fullscreenable) {
self->fullscreenable = fullscreenable;
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_FULLSCREENABLE]);
}
}
/**
* clapper_gtk_simple_controls_get_fullscreenable:
* @controls: a #ClapperGtkSimpleControls
*
* Get whether [class@ClapperGtk.ToggleFullscreenButton] button in the @controls
* is set to be visible.
*/
gboolean
clapper_gtk_simple_controls_get_fullscreenable (ClapperGtkSimpleControls *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_SIMPLE_CONTROLS (self), FALSE);
return self->fullscreenable;
}
/**
* clapper_gtk_simple_controls_set_seek_method:
* @controls: a #ClapperGtkSimpleControls
* @method: a #ClapperPlayerSeekMethod
*
* Set [enum@Clapper.PlayerSeekMethod] to use when seeking with progress bar.
*/
void
clapper_gtk_simple_controls_set_seek_method (ClapperGtkSimpleControls *self,
ClapperPlayerSeekMethod method)
{
g_return_if_fail (CLAPPER_GTK_IS_SIMPLE_CONTROLS (self));
clapper_gtk_seek_bar_set_seek_method (CLAPPER_GTK_SEEK_BAR (self->seek_bar), method);
}
/**
* clapper_gtk_simple_controls_get_seek_method:
* @controls: a #ClapperGtkSimpleControls
*
* Get [enum@Clapper.PlayerSeekMethod] used when seeking with progress bar.
*
* Returns: #ClapperPlayerSeekMethod used for seeking.
*/
ClapperPlayerSeekMethod
clapper_gtk_simple_controls_get_seek_method (ClapperGtkSimpleControls *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_SIMPLE_CONTROLS (self), DEFAULT_SEEK_METHOD);
return clapper_gtk_seek_bar_get_seek_method (CLAPPER_GTK_SEEK_BAR (self->seek_bar));
}
/**
* clapper_gtk_simple_controls_get_extra_menu_button:
* @controls: a #ClapperGtkSimpleControls
*
* Get [class@ClapperGtk.ExtraMenuButton] that resides within @controls.
*
* Returns: (transfer none): #ClapperGtkExtraMenuButton within simple controls panel.
*/
ClapperGtkExtraMenuButton *
clapper_gtk_simple_controls_get_extra_menu_button (ClapperGtkSimpleControls *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_SIMPLE_CONTROLS (self), NULL);
return CLAPPER_GTK_EXTRA_MENU_BUTTON (self->extra_menu_button);
}
static void
clapper_gtk_simple_controls_init (ClapperGtkSimpleControls *self)
{
self->fullscreenable = DEFAULT_FULLSCREENABLE;
self->seek_method = DEFAULT_SEEK_METHOD;
gtk_widget_init_template (GTK_WIDGET (self));
/* Set our defaults to children */
clapper_gtk_seek_bar_set_seek_method (
CLAPPER_GTK_SEEK_BAR_CAST (self->seek_bar), self->seek_method);
}
static void
clapper_gtk_simple_controls_dispose (GObject *object)
{
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_GTK_TYPE_SIMPLE_CONTROLS);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_gtk_simple_controls_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperGtkSimpleControls *self = CLAPPER_GTK_SIMPLE_CONTROLS_CAST (object);
switch (prop_id) {
case PROP_FULLSCREENABLE:
g_value_set_boolean (value, clapper_gtk_simple_controls_get_fullscreenable (self));
break;
case PROP_SEEK_METHOD:
g_value_set_enum (value, clapper_gtk_simple_controls_get_seek_method (self));
break;
case PROP_EXTRA_MENU_BUTTON:
g_value_set_object (value, clapper_gtk_simple_controls_get_extra_menu_button (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_simple_controls_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperGtkSimpleControls *self = CLAPPER_GTK_SIMPLE_CONTROLS_CAST (object);
switch (prop_id) {
case PROP_FULLSCREENABLE:
clapper_gtk_simple_controls_set_fullscreenable (self, g_value_get_boolean (value));
break;
case PROP_SEEK_METHOD:
clapper_gtk_simple_controls_set_seek_method (self, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_simple_controls_class_init (ClapperGtkSimpleControlsClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtksimplecontrols", 0,
"Clapper GTK Simple Controls");
gobject_class->get_property = clapper_gtk_simple_controls_get_property;
gobject_class->set_property = clapper_gtk_simple_controls_set_property;
gobject_class->dispose = clapper_gtk_simple_controls_dispose;
/**
* ClapperGtkSimpleControls:fullscreenable:
*
* Whether toggle fullscreen button should be visible.
*/
param_specs[PROP_FULLSCREENABLE] = g_param_spec_boolean ("fullscreenable",
NULL, NULL, DEFAULT_FULLSCREENABLE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkSimpleControls:seek-method:
*
* Method used for seeking.
*/
param_specs[PROP_SEEK_METHOD] = g_param_spec_enum ("seek-method",
NULL, NULL, CLAPPER_TYPE_PLAYER_SEEK_METHOD, DEFAULT_SEEK_METHOD,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkSimpleControls:extra-menu-button:
*
* Access to extra menu button within controls.
*/
param_specs[PROP_EXTRA_MENU_BUTTON] = g_param_spec_object ("extra-menu-button",
NULL, NULL, CLAPPER_GTK_TYPE_EXTRA_MENU_BUTTON,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
gtk_widget_class_set_template_from_resource (widget_class,
CLAPPER_GTK_RESOURCE_PREFIX "/ui/clapper-gtk-simple-controls.ui");
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSimpleControls, seek_bar);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSimpleControls, extra_menu_button);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSimpleControls, fullscreen_top_revealer);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSimpleControls, fullscreen_bottom_revealer);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSimpleControls, controls_slide_revealer);
gtk_widget_class_bind_template_callback (widget_class, initial_adapt_cb);
gtk_widget_class_bind_template_callback (widget_class, full_adapt_cb);
gtk_widget_class_bind_template_callback (widget_class, controls_revealed_cb);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-simple-controls");
}