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,63 @@
[library]
version = "@CLAPPER_VERSION@"
browse_url = "https://github.com/Rafostar/clapper/"
repository_url = "https://github.com/Rafostar/clapper.git"
website_url = "https://rafostar.github.io/clapper/"
docs_url = "https://rafostar.github.io/clapper/doc/clapper-gtk/"
authors = "Rafał Dzięgiel"
logo_url = "clapper-logo.svg"
license = "LGPL-2.1-or-later"
description = "Clapper GTK integration library"
devhelp = true
search_index = true
dependencies = ["Clapper@CLAPPER_VERSION_SUFFIX@", "Gtk-4.0"]
[dependencies."Clapper@CLAPPER_VERSION_SUFFIX@"]
name = "Clapper"
description = "Clapper playback library"
docs_url = "https://rafostar.github.io/clapper/doc/clapper/"
[dependencies."Gtk-4.0"]
name = "Gtk"
description = "The GTK toolkit"
docs_url = "https://docs.gtk.org/gtk4/"
related = ["GLib-2.0", "GObject-2.0", "Gio-2.0", "Gst-1.0"]
[related."GLib-2.0"]
name = "GLib"
description = "A general-purpose, portable utility library"
docs_url = "https://docs.gtk.org/glib/"
[related."GObject-2.0"]
name = "GObject"
description = "The base type system library"
docs_url = "https://docs.gtk.org/gobject/"
[related."Gio-2.0"]
name = "Gio"
description = "GObject Interfaces and Objects, Networking, IPC, and I/O"
docs_url = "https://docs.gtk.org/gio/"
[related."Gst-1.0"]
name = "Gst"
description = "GStreamer core library"
docs_url = "https://gstreamer.freedesktop.org/documentation/gstreamer/gi-index.html"
[theme]
name = "basic"
show_index_summary = true
show_class_hierarchy = true
[source-location]
base_url = "https://github.com/Rafostar/clapper/tree/master/"
[extra]
# The same order will be used when generating the index
content_files = [
]
content_images = [
"images/clapper-logo.svg",
]
urlmap_file = "urlmap.js"

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 256 256" width="256" height="256">
<defs>
<path id="b1nGq5BrLC" d="M27.2 243.52C27.2 236.16 27.2 199.83 27.2 134.22C47.64 134.22 211.12 134.22 231.56 134.22C231.56 199.83 231.56 236.16 231.56 243.52C231.56 250.4 225.96 256 218.92 256C183.07 256 57.77 256 39.84 256C32.8 256 27.2 250.4 27.2 243.52Z"></path>
<path id="a3jkaoNn4k" d="M24.32 103.65C24.32 106.22 24.32 127.02 24.32 129.58C24.32 132.78 26.72 135.18 29.92 135.18C50.41 135.18 215.08 135.18 235.72 135.18C238.76 135.18 241.32 132.78 241.32 129.58C241.32 127.02 241.32 106.22 241.32 103.65C241.32 100.45 238.76 98.05 235.72 98.05C194.59 98.05 50.41 98.05 29.92 98.05C26.72 98.05 24.32 100.45 24.32 103.65Z"></path>
<path id="atpVQ8mnd" d="M174.59 135.18L211.87 98.05L171.07 98.05L133.78 135.18L174.59 135.18Z"></path>
<path id="bMtYoNHu0" d="M76.81 135.18L114.1 98.05L73.13 98.05L35.84 135.18L76.81 135.18Z"></path>
<path id="b5oP0Glp4" d="M19.04 69.41C19.84 71.97 25.92 91.97 26.72 94.37C27.68 97.41 30.72 99.01 33.76 98.05C54.09 91.81 216.68 42.04 237 35.8C240.04 35 241.64 31.8 240.84 28.92C240.04 26.36 233.96 6.36 233.16 3.96C232.2 0.92 229.16 -0.68 226.12 0.28C185.47 12.6 43.21 56.29 22.88 62.53C19.84 63.33 18.24 66.53 19.04 69.41Z"></path>
<path id="lwBgev6DR" d="M176.51 54.37L129.94 29.4L169.15 17.56L215.72 42.52L176.51 54.37Z"></path>
<path id="cUsjEMRUu" d="M81.61 83.49L35.04 58.69L74.25 46.69L120.82 71.49L81.61 83.49Z"></path>
<path id="c1bcHZGXe" d="M14.72 66.69C14.72 72.93 14.72 123.02 14.72 129.26C14.72 132.62 17.44 135.18 20.64 135.18C26.56 135.18 74.09 135.18 80.01 135.18C84.33 135.18 87.21 130.86 85.61 127.02C82.89 120.78 61.45 70.53 58.73 64.29C57.77 62.05 55.69 60.77 53.29 60.77C46.73 60.77 24 60.77 20.64 60.77C17.44 60.77 14.72 63.33 14.72 66.69Z"></path>
<path id="f2PtH0V1vC" d="M32.64 60.61C31.52 60.61 21.92 60.61 20.64 60.61C17.44 60.61 14.72 63.33 14.72 66.53C14.72 72.77 14.72 123.02 14.72 129.26C14.72 132.46 17.44 135.18 20.64 135.18C21.92 135.18 31.52 135.18 32.64 135.18C29.44 135.18 26.72 132.46 26.72 129.26C26.72 116.62 26.72 72.77 26.72 66.53C26.72 63.33 29.44 60.61 32.64 60.61Z"></path>
<path id="a1SvrrkqVm" d="M231.56 135.18C231.56 143.82 231.56 148.46 231.56 149.42C231.56 149.42 231.56 149.42 231.56 149.42C108.98 149.42 40.8 149.42 27.2 149.42C27.2 149.42 27.2 149.42 27.2 149.42C27.2 140.94 27.2 136.14 27.2 135.18C27.2 135.18 27.2 135.18 27.2 135.18C149.78 135.18 217.96 135.18 231.56 135.18C231.56 135.18 231.56 135.18 231.56 135.18Z"></path>
<path id="agXcvKqh8" d="M104.22 162.46L104.22 234.46L163.22 198.54L104.22 162.46Z"></path>
</defs>
<g>
<g><use xlink:href="#b1nGq5BrLC" fill="#4747d1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
<g><use xlink:href="#a3jkaoNn4k" fill="#4747d1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
<g><use xlink:href="#atpVQ8mnd" fill="#f1f1f1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
<g><use xlink:href="#bMtYoNHu0" fill="#f1f1f1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
<g><use xlink:href="#b5oP0Glp4" fill="#4747d1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
<g><use xlink:href="#lwBgev6DR" fill="#f1f1f1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
<g><use xlink:href="#cUsjEMRUu" fill="#f1f1f1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
<g><use xlink:href="#c1bcHZGXe" fill="#a9a9a9" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
<g><use xlink:href="#f2PtH0V1vC" opacity="0.2" fill="#000000" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
<g><use xlink:href="#a1SvrrkqVm" opacity="0.2" fill="#000000" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
<g><use xlink:href="#agXcvKqh8" fill="#f1f1f1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,29 @@
clappergtk_toml = configure_file(
input: 'clapper-gtk.toml.in',
output: 'clapper-gtk.toml',
configuration: doc_version_conf,
install: true,
install_dir: join_paths(datadir, 'doc', 'clapper-gtk'),
)
custom_target('clapper-gtk-doc',
input: [
clappergtk_toml,
clappergtk_gir[0],
],
output: 'clapper-gtk',
command: [
gi_docgen,
'generate',
gi_docgen_common_args,
'--add-include-path=@0@'.format(join_paths(meson.project_build_root(), 'src', 'lib', 'clapper-gtk')),
'--config=@INPUT0@',
'--output-dir=@OUTPUT@',
'--content-dir=@0@'.format(meson.current_build_dir()),
'--content-dir=@0@'.format(meson.current_source_dir()),
'@INPUT1@',
],
build_by_default: true,
install: true,
install_dir: join_paths(datadir, 'doc'),
)

View File

@@ -0,0 +1,8 @@
baseURLs = [
['GLib', 'https://docs.gtk.org/glib/'],
['GObject', 'https://docs.gtk.org/gobject/'],
['Gio', 'https://docs.gtk.org/gio/'],
['Gtk', 'https://docs.gtk.org/gtk4/'],
['Gst', 'https://gstreamer.freedesktop.org/documentation/gstreamer/gi-index.html?'],
['Clapper', 'https://rafostar.github.io/clapper/doc/clapper/'],
]

View File

@@ -13,3 +13,6 @@ endif
if build_clapper
subdir('clapper')
endif
if build_clappergtk
subdir('clapper-gtk')
endif

View File

@@ -130,6 +130,7 @@ summary({
}, section: 'Directories')
summary('clapper', build_clapper ? 'Yes' : 'No', section: 'Build')
summary('clapper-gtk', build_clappergtk ? 'Yes' : 'No', section: 'Build')
summary('gst-plugin', build_gst_plugin ? 'Yes' : 'No', section: 'Build')
summary('introspection', build_gir ? 'Yes' : 'No', section: 'Build')
summary('vapi', build_vapi ? 'Yes' : 'No', section: 'Build')

View File

@@ -4,6 +4,11 @@ option('clapper',
value: 'auto',
description: 'Build Clapper library'
)
option('clapper-gtk',
type: 'feature',
value: 'auto',
description: 'Build Clapper GTK integration library'
)
option('gst-plugin',
type: 'feature',
value: 'auto',

View File

@@ -0,0 +1,464 @@
/* 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.
*/
/**
* ClapperGtkBillboard:
*
* A layer where various messages can be displayed.
*
* #ClapperGtkBillboard widget is meant to be overlaid on top of
* [class@ClapperGtk.Video] as a normal (non-fading) overlay.
*
* It is used to display various messages/announcements and later
* takes care of fading them on its own.
*
* If automatic volume/speed change notifications when their values do
* change are desired, functions for announcing them can be run in callbacks
* to corresponding property notify signals on the [class@Clapper.Player].
*/
#include "config.h"
#include <math.h>
#include <gst/gst.h>
#include "clapper-gtk-billboard.h"
#include "clapper-gtk-utils-private.h"
#define PERCENTAGE_ROUND(a) (round ((gdouble) a / 0.01) * 0.01)
#define WORDS_PER_MSECOND 0.004
#define GST_CAT_DEFAULT clapper_gtk_billboard_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkBillboard
{
ClapperGtkContainer parent;
GtkWidget *side_revealer;
GtkWidget *progress_revealer;
GtkWidget *progress_box;
GtkWidget *top_progress;
GtkWidget *bottom_progress;
GtkWidget *progress_image;
GtkWidget *progress_label;
GtkWidget *message_revealer;
GtkWidget *message_image;
GtkWidget *message_label;
gboolean mute;
gboolean has_pinned;
guint side_timeout;
guint message_timeout;
ClapperPlayer *player;
};
#define parent_class clapper_gtk_billboard_parent_class
G_DEFINE_TYPE (ClapperGtkBillboard, clapper_gtk_billboard, CLAPPER_GTK_TYPE_CONTAINER)
/* We calculate estimated read time. This allows
* translated text to be displayed as long as
* necessary without app developer caring. */
static guint
_estimate_read_time (const gchar *text)
{
guint i, n_words = 1;
guint read_time;
for (i = 0; text[i] != '\0'; ++i) {
if (text[i] == ' ' || text[i] == '\n')
n_words++;
}
read_time = MAX (1500, (n_words / WORDS_PER_MSECOND) + 500);
GST_DEBUG ("Estimated message read time: %u", read_time);
return read_time;
}
static void
_unreveal_side_delay_cb (ClapperGtkBillboard *self)
{
GST_LOG_OBJECT (self, "Unreveal side handler reached");
self->side_timeout = 0;
gtk_revealer_set_reveal_child (GTK_REVEALER (self->side_revealer), FALSE);
}
static void
_unreveal_message_delay_cb (ClapperGtkBillboard *self)
{
GST_LOG_OBJECT (self, "Unreveal message handler reached");
self->message_timeout = 0;
gtk_revealer_set_reveal_child (GTK_REVEALER (self->message_revealer), FALSE);
}
static void
_reset_fade_side_timeout (ClapperGtkBillboard *self)
{
GST_TRACE_OBJECT (self, "Fade side timeout reset");
g_clear_handle_id (&self->side_timeout, g_source_remove);
self->side_timeout = g_timeout_add_once (1500,
(GSourceOnceFunc) _unreveal_side_delay_cb, self);
}
static void
_reset_fade_message_timeout (ClapperGtkBillboard *self)
{
const gchar *text = gtk_label_get_text (GTK_LABEL (self->message_label));
GST_TRACE_OBJECT (self, "Fade side timeout reset");
g_clear_handle_id (&self->message_timeout, g_source_remove);
self->message_timeout = g_timeout_add_once (
_estimate_read_time (text),
(GSourceOnceFunc) _unreveal_message_delay_cb, self);
}
static void
adapt_cb (ClapperGtkContainer *container, gboolean adapt,
ClapperGtkBillboard *self)
{
GST_DEBUG_OBJECT (self, "Adapted: %s", (adapt) ? "yes" : "no");
gtk_revealer_set_reveal_child (GTK_REVEALER (self->progress_revealer), !adapt);
}
static void
revealer_revealed_cb (GtkRevealer *revealer,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkBillboard *self)
{
if (!gtk_revealer_get_child_revealed (revealer)) {
GtkWidget *other_revealer = (GTK_WIDGET (revealer) == self->side_revealer)
? self->message_revealer
: self->side_revealer;
gtk_widget_set_visible (GTK_WIDGET (revealer), FALSE);
/* We only hide here when nothing is posted on the board,
* visiblity is set to TRUE when post is made */
if (!gtk_revealer_get_child_revealed (GTK_REVEALER (other_revealer)))
gtk_widget_set_visible (GTK_WIDGET (self), FALSE);
} else {
if ((GTK_WIDGET (revealer) == self->side_revealer))
_reset_fade_side_timeout (self);
else if (!self->has_pinned)
_reset_fade_message_timeout (self);
}
}
static void
reveal_side (ClapperGtkBillboard *self)
{
g_clear_handle_id (&self->side_timeout, g_source_remove);
gtk_widget_set_visible (GTK_WIDGET (self), TRUE);
gtk_widget_set_visible (self->side_revealer, TRUE);
gtk_revealer_set_reveal_child (GTK_REVEALER (self->side_revealer), TRUE);
if (gtk_revealer_get_child_revealed (GTK_REVEALER (self->side_revealer)))
_reset_fade_side_timeout (self);
}
static void
_post_message_internal (ClapperGtkBillboard *self,
const gchar *icon_name, const gchar *message, gboolean pin)
{
if (self->has_pinned)
return;
self->has_pinned = pin;
gtk_image_set_from_icon_name (GTK_IMAGE (self->message_image), icon_name);
gtk_label_set_label (GTK_LABEL (self->message_label), message);
g_clear_handle_id (&self->message_timeout, g_source_remove);
gtk_widget_set_visible (GTK_WIDGET (self), TRUE);
gtk_widget_set_visible (self->message_revealer, TRUE);
gtk_revealer_set_reveal_child (GTK_REVEALER (self->message_revealer), TRUE);
if (!self->has_pinned
&& gtk_revealer_get_child_revealed (GTK_REVEALER (self->message_revealer)))
_reset_fade_message_timeout (self);
}
static void
_player_mute_changed_cb (ClapperPlayer *player,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkBillboard *self)
{
self->mute = clapper_player_get_mute (player);
clapper_gtk_billboard_announce_volume (self);
}
/**
* clapper_gtk_billboard_new:
*
* Creates a new #ClapperGtkBillboard instance.
*
* Returns: a new billboard #GtkWidget.
*/
GtkWidget *
clapper_gtk_billboard_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_BILLBOARD, NULL);
}
/**
* clapper_gtk_billboard_post_message:
* @billboard: a #ClapperGtkBillboard
* @icon_name: an icon name
* @message: a message text
*
* Posts a temporary message on the @billboard.
*
* Duration how long a message will stay is automatically
* calculated based on amount of text.
*/
void
clapper_gtk_billboard_post_message (ClapperGtkBillboard *self,
const gchar *icon_name, const gchar *message)
{
_post_message_internal (self, icon_name, message, FALSE);
}
/**
* clapper_gtk_billboard_pin_message:
* @billboard: a #ClapperGtkBillboard
* @icon_name: an icon name
* @message: a message text
*
* Pins a permanent message on the @billboard.
*
* The message will stay on the @billboard until a
* [method@ClapperGtk.Billboard.unpin_pinned_message] is called.
*/
void
clapper_gtk_billboard_pin_message (ClapperGtkBillboard *self,
const gchar *icon_name, const gchar *message)
{
_post_message_internal (self, icon_name, message, TRUE);
}
/**
* clapper_gtk_billboard_unpin_pinned_message:
* @billboard: a #ClapperGtkBillboard
*
* Unpins previously pinned message on the @billboard.
*
* If no message was pinned this function will do nothing,
* so it is safe to call when unsure.
*/
void
clapper_gtk_billboard_unpin_pinned_message (ClapperGtkBillboard *self)
{
if (!self->has_pinned)
return;
_unreveal_message_delay_cb (self);
self->has_pinned = FALSE;
}
/**
* clapper_gtk_billboard_announce_volume:
* @billboard: a #ClapperGtkBillboard
*
* Temporarily displays current volume level on the
* side of @billboard.
*
* Use this if you want to present current volume level to the user.
* Note that @billboard also automatically announces volume changes.
*/
void
clapper_gtk_billboard_announce_volume (ClapperGtkBillboard *self)
{
gdouble volume = PERCENTAGE_ROUND (clapper_player_get_volume (self->player));
gchar *percent_str;
gboolean has_overamp;
/* Revert popup_speed changes */
gtk_progress_bar_set_inverted (GTK_PROGRESS_BAR (self->bottom_progress), TRUE);
has_overamp = gtk_widget_has_css_class (self->progress_box, "overamp");
percent_str = g_strdup_printf ("%.0lf%%", volume * 100);
if (volume <= 1.0) {
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (self->top_progress), 0.0);
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (self->bottom_progress), volume);
if (has_overamp)
gtk_widget_remove_css_class (self->progress_box, "overamp");
} else {
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (self->top_progress), volume - 1.0);
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (self->bottom_progress), 1.0);
if (!has_overamp)
gtk_widget_add_css_class (self->progress_box, "overamp");
}
gtk_image_set_from_icon_name (GTK_IMAGE (self->progress_image),
clapper_gtk_get_icon_name_for_volume ((!self->mute) ? volume : 0));
gtk_label_set_label (GTK_LABEL (self->progress_label), percent_str);
g_free (percent_str);
reveal_side (self);
}
/**
* clapper_gtk_billboard_announce_speed:
* @billboard: a #ClapperGtkBillboard
*
* Temporarily displays current speed value on the
* side of @billboard.
*
* Use this if you want to present current speed value to the user.
* Note that @billboard also automatically announces speed changes.
*/
void
clapper_gtk_billboard_announce_speed (ClapperGtkBillboard *self)
{
gdouble speed = PERCENTAGE_ROUND (clapper_player_get_speed (self->player));
gchar *speed_str;
/* Revert popup_volume changes */
if (gtk_widget_has_css_class (self->progress_box, "overamp"))
gtk_widget_remove_css_class (self->progress_box, "overamp");
gtk_progress_bar_set_inverted (GTK_PROGRESS_BAR (self->bottom_progress), FALSE);
speed_str = g_strdup_printf ("%.2lfx", speed);
if (speed <= 1.0) {
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (self->top_progress), 0.0);
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (self->bottom_progress), 1.0 - speed);
} else {
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (self->top_progress), speed - 1.0);
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (self->bottom_progress), 0.0);
}
gtk_image_set_from_icon_name (GTK_IMAGE (self->progress_image),
clapper_gtk_get_icon_name_for_speed (speed));
gtk_label_set_label (GTK_LABEL (self->progress_label), speed_str);
g_free (speed_str);
reveal_side (self);
}
static void
clapper_gtk_billboard_root (GtkWidget *widget)
{
ClapperGtkBillboard *self = CLAPPER_GTK_BILLBOARD_CAST (widget);
GTK_WIDGET_CLASS (parent_class)->root (widget);
if ((self->player = clapper_gtk_get_player_from_ancestor (widget))) {
g_signal_connect (self->player, "notify::mute",
G_CALLBACK (_player_mute_changed_cb), self);
self->mute = clapper_player_get_mute (self->player);
}
}
static void
clapper_gtk_billboard_unroot (GtkWidget *widget)
{
ClapperGtkBillboard *self = CLAPPER_GTK_BILLBOARD_CAST (widget);
if (self->player) {
g_signal_handlers_disconnect_by_func (self->player, _player_mute_changed_cb, self);
self->player = NULL;
}
/* Reset in case of rooted again not within video widget */
self->mute = FALSE;
GTK_WIDGET_CLASS (parent_class)->unroot (widget);
}
static void
clapper_gtk_billboard_init (ClapperGtkBillboard *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
static void
clapper_gtk_billboard_dispose (GObject *object)
{
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_GTK_TYPE_BILLBOARD);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_gtk_billboard_finalize (GObject *object)
{
ClapperGtkBillboard *self = CLAPPER_GTK_BILLBOARD_CAST (object);
g_clear_handle_id (&self->side_timeout, g_source_remove);
g_clear_handle_id (&self->message_timeout, g_source_remove);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_gtk_billboard_class_init (ClapperGtkBillboardClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtkbillboard", 0,
"Clapper GTK Billboard");
gobject_class->dispose = clapper_gtk_billboard_dispose;
gobject_class->finalize = clapper_gtk_billboard_finalize;
/* Using root/unroot since initially invisible (unrealized)
* and we want for signals to stay connected as long as parented */
widget_class->root = clapper_gtk_billboard_root;
widget_class->unroot = clapper_gtk_billboard_unroot;
gtk_widget_class_set_template_from_resource (widget_class,
CLAPPER_GTK_RESOURCE_PREFIX "/ui/clapper-gtk-billboard.ui");
gtk_widget_class_bind_template_child (widget_class, ClapperGtkBillboard, side_revealer);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkBillboard, progress_box);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkBillboard, progress_revealer);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkBillboard, top_progress);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkBillboard, bottom_progress);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkBillboard, progress_image);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkBillboard, progress_label);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkBillboard, message_revealer);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkBillboard, message_image);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkBillboard, message_label);
gtk_widget_class_bind_template_callback (widget_class, adapt_cb);
gtk_widget_class_bind_template_callback (widget_class, revealer_revealed_cb);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-billboard");
}

View File

@@ -0,0 +1,51 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-container.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_BILLBOARD (clapper_gtk_billboard_get_type())
#define CLAPPER_GTK_BILLBOARD_CAST(obj) ((ClapperGtkBillboard *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkBillboard, clapper_gtk_billboard, CLAPPER_GTK, BILLBOARD, ClapperGtkContainer)
GtkWidget * clapper_gtk_billboard_new (void);
void clapper_gtk_billboard_post_message (ClapperGtkBillboard *billboard, const gchar *icon_name, const gchar *message);
void clapper_gtk_billboard_pin_message (ClapperGtkBillboard *billboard, const gchar *icon_name, const gchar *message);
void clapper_gtk_billboard_unpin_pinned_message (ClapperGtkBillboard *billboard);
void clapper_gtk_billboard_announce_volume (ClapperGtkBillboard *billboard);
void clapper_gtk_billboard_announce_speed (ClapperGtkBillboard *billboard);
G_END_DECLS

View File

@@ -0,0 +1,41 @@
/* 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.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include "clapper-gtk-container.h"
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_BUFFERING_ANIMATION (clapper_gtk_buffering_animation_get_type())
#define CLAPPER_GTK_BUFFERING_ANIMATION_CAST(obj) ((ClapperGtkBufferingAnimation *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkBufferingAnimation, clapper_gtk_buffering_animation, CLAPPER_GTK, BUFFERING_ANIMATION, ClapperGtkContainer)
G_GNUC_INTERNAL
void clapper_gtk_buffering_animation_start (ClapperGtkBufferingAnimation *animation);
G_GNUC_INTERNAL
void clapper_gtk_buffering_animation_stop (ClapperGtkBufferingAnimation *animation);
G_END_DECLS

View File

@@ -0,0 +1,140 @@
/* 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.
*/
#include <clapper/clapper.h>
#include "clapper-gtk-buffering-animation-private.h"
#include "clapper-gtk-buffering-paintable-private.h"
#define MIN_STEP_DELAY 30000
#define GST_CAT_DEFAULT clapper_gtk_buffering_animation_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkBufferingAnimation
{
ClapperGtkContainer parent;
ClapperGtkBufferingPaintable *buffering_paintable;
guint tick_id;
gint64 last_tick;
};
#define parent_class clapper_gtk_buffering_animation_parent_class
G_DEFINE_TYPE (ClapperGtkBufferingAnimation, clapper_gtk_buffering_animation, CLAPPER_GTK_TYPE_CONTAINER)
static gboolean
_animation_tick (GtkWidget *picture, GdkFrameClock *frame_clock, ClapperGtkBufferingAnimation *self)
{
gint64 now = gdk_frame_clock_get_frame_time (frame_clock);
/* We do not want for this animation to move too fast */
if (now - self->last_tick >= MIN_STEP_DELAY) {
GST_LOG_OBJECT (self, "Animation step, last: %" G_GINT64_FORMAT
", now: %" G_GINT64_FORMAT, self->last_tick, now);
clapper_gtk_buffering_paintable_step (self->buffering_paintable);
self->last_tick = now;
}
return G_SOURCE_CONTINUE;
}
void
clapper_gtk_buffering_animation_start (ClapperGtkBufferingAnimation *self)
{
GtkWidget *picture;
if (self->tick_id != 0)
return;
GST_DEBUG_OBJECT (self, "Animation start");
picture = clapper_gtk_container_get_child (CLAPPER_GTK_CONTAINER (self));
self->tick_id = gtk_widget_add_tick_callback (picture,
(GtkTickCallback) _animation_tick, self, NULL);
}
void
clapper_gtk_buffering_animation_stop (ClapperGtkBufferingAnimation *self)
{
GtkWidget *picture;
if (self->tick_id == 0)
return;
GST_DEBUG_OBJECT (self, "Animation stop");
picture = clapper_gtk_container_get_child (CLAPPER_GTK_CONTAINER (self));
gtk_widget_remove_tick_callback (picture, self->tick_id);
self->tick_id = 0;
self->last_tick = 0;
clapper_gtk_buffering_paintable_reset (self->buffering_paintable);
}
static void
clapper_gtk_buffering_animation_init (ClapperGtkBufferingAnimation *self)
{
GtkWidget *picture = gtk_picture_new ();
self->buffering_paintable = clapper_gtk_buffering_paintable_new ();
gtk_picture_set_paintable (GTK_PICTURE (picture),
GDK_PAINTABLE (self->buffering_paintable));
clapper_gtk_container_set_child (CLAPPER_GTK_CONTAINER (self), picture);
}
static void
clapper_gtk_buffering_animation_unmap (GtkWidget *widget)
{
ClapperGtkBufferingAnimation *self = CLAPPER_GTK_BUFFERING_ANIMATION_CAST (widget);
clapper_gtk_buffering_animation_stop (self);
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
static void
clapper_gtk_buffering_animation_finalize (GObject *object)
{
ClapperGtkBufferingAnimation *self = CLAPPER_GTK_BUFFERING_ANIMATION_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
g_object_unref (self->buffering_paintable);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_gtk_buffering_animation_class_init (ClapperGtkBufferingAnimationClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtkbufferinganimation", 0,
"Clapper GTK Buffering Animation");
gobject_class->finalize = clapper_gtk_buffering_animation_finalize;
widget_class->unmap = clapper_gtk_buffering_animation_unmap;
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-buffering-animation");
}

View File

@@ -0,0 +1,42 @@
/* 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.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_BUFFERING_PAINTABLE (clapper_gtk_buffering_paintable_get_type())
#define CLAPPER_GTK_BUFFERING_PAINTABLE_CAST(obj) ((ClapperGtkBufferingPaintable *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkBufferingPaintable, clapper_gtk_buffering_paintable, CLAPPER_GTK, BUFFERING_PAINTABLE, GObject)
G_GNUC_INTERNAL
ClapperGtkBufferingPaintable * clapper_gtk_buffering_paintable_new (void);
G_GNUC_INTERNAL
void clapper_gtk_buffering_paintable_step (ClapperGtkBufferingPaintable *buffering_paintable);
G_GNUC_INTERNAL
void clapper_gtk_buffering_paintable_reset (ClapperGtkBufferingPaintable *buffering_paintable);
G_END_DECLS

View File

@@ -0,0 +1,177 @@
/* 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.
*/
#include "clapper-gtk-buffering-paintable-private.h"
#define CIRCLE_MAX_SIZE 48
#define CIRCLE_SPACING 10
#define CIRCLE_OUTLINE 2
#define INTRINSIC_SIZE 184 // 3 * CIRCLE_MAX_SIZE + 4 * CIRCLE_SPACING
#define BLACK ((GdkRGBA) { 0, 0, 0, 1 })
#define WHITE ((GdkRGBA) { 1, 1, 1, 1 })
struct _ClapperGtkBufferingPaintable
{
GObject parent;
gfloat sizes[3]; // current size of each circle
gboolean reverses[3]; // grow/shrink direction
gboolean initialized[3]; // big enough to start growing the next one
};
static GdkPaintableFlags
clapper_gtk_buffering_paintable_get_flags (GdkPaintable *paintable)
{
return GDK_PAINTABLE_STATIC_SIZE;
}
static gint
clapper_gtk_buffering_paintable_get_intrinsic_size (GdkPaintable *paintable)
{
return INTRINSIC_SIZE;
}
static void
_draw_scaled_circle (GdkSnapshot *snapshot, gfloat scale)
{
GskRoundedRect outline;
gfloat half_size = ((gfloat) CIRCLE_MAX_SIZE / 2) * scale;
gfloat inside_size = ((gfloat) (CIRCLE_MAX_SIZE - 2 * CIRCLE_OUTLINE) / 2) * scale;
/* Draw white inner circle */
gsk_rounded_rect_init_from_rect (&outline,
&GRAPHENE_RECT_INIT (-inside_size, -inside_size, 2 * inside_size, 2 * inside_size), inside_size);
gtk_snapshot_append_border (snapshot, &outline,
(gfloat[4]) { inside_size, inside_size, inside_size, inside_size },
(GdkRGBA[4]) { WHITE, WHITE, WHITE, WHITE });
/* Draw black circle border */
gsk_rounded_rect_init_from_rect (&outline,
&GRAPHENE_RECT_INIT (-half_size, -half_size, 2 * half_size, 2 * half_size), half_size);
gtk_snapshot_append_border (snapshot, &outline,
(gfloat[4]) { CIRCLE_OUTLINE, CIRCLE_OUTLINE, CIRCLE_OUTLINE, CIRCLE_OUTLINE },
(GdkRGBA[4]) { BLACK, BLACK, BLACK, BLACK });
}
static void
clapper_gtk_buffering_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot, gdouble width, gdouble height)
{
ClapperGtkBufferingPaintable *self = CLAPPER_GTK_BUFFERING_PAINTABLE_CAST (paintable);
guint i;
gtk_snapshot_save (snapshot);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0, height / 2));
gtk_snapshot_scale (snapshot, MIN (width, height) / INTRINSIC_SIZE, MIN (width, height) / INTRINSIC_SIZE);
for (i = 0; i < 3; ++i) {
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (CIRCLE_SPACING + CIRCLE_MAX_SIZE / 2, 0));
_draw_scaled_circle (snapshot, self->sizes[i]);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (CIRCLE_MAX_SIZE / 2, 0));
}
gtk_snapshot_restore (snapshot);
}
static GdkPaintable *
clapper_gtk_buffering_paintable_get_current_image (GdkPaintable *paintable)
{
ClapperGtkBufferingPaintable *self = CLAPPER_GTK_BUFFERING_PAINTABLE_CAST (paintable);
ClapperGtkBufferingPaintable *copy = clapper_gtk_buffering_paintable_new ();
guint i;
/* Only current sizes are needed for static image */
for (i = 0; i < 3; ++i)
copy->sizes[i] = self->sizes[i];
return GDK_PAINTABLE (copy);
}
static void
_paintable_iface_init (GdkPaintableInterface *iface)
{
iface->get_flags = clapper_gtk_buffering_paintable_get_flags;
iface->get_intrinsic_width = clapper_gtk_buffering_paintable_get_intrinsic_size;
iface->get_intrinsic_height = clapper_gtk_buffering_paintable_get_intrinsic_size;
iface->snapshot = clapper_gtk_buffering_paintable_snapshot;
iface->get_current_image = clapper_gtk_buffering_paintable_get_current_image;
}
#define parent_class clapper_gtk_buffering_paintable_parent_class
G_DEFINE_TYPE_WITH_CODE (ClapperGtkBufferingPaintable, clapper_gtk_buffering_paintable, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, _paintable_iface_init))
ClapperGtkBufferingPaintable *
clapper_gtk_buffering_paintable_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_BUFFERING_PAINTABLE, NULL);
}
void
clapper_gtk_buffering_paintable_step (ClapperGtkBufferingPaintable *self)
{
guint i;
for (i = 0; i < 3; ++i) {
/* If previous circle is not big enough
* do not start animating the next one */
if (i > 0 && !self->initialized[i - 1])
break;
if (!self->initialized[i] && self->sizes[i] >= 0.3)
self->initialized[i] = TRUE;
self->sizes[i] += (self->reverses[i]) ? -0.04 : 0.04;
if (self->sizes[i] > 1.0) {
self->sizes[i] = 1.0;
self->reverses[i] = TRUE;
} else if (self->sizes[i] < 0.0) {
self->sizes[i] = 0.0;
self->reverses[i] = FALSE;
}
}
gdk_paintable_invalidate_contents ((GdkPaintable *) self);
}
void
clapper_gtk_buffering_paintable_reset (ClapperGtkBufferingPaintable *self)
{
guint i;
for (i = 0; i < 3; ++i) {
self->sizes[i] = 0;
self->reverses[i] = FALSE;
self->initialized[i] = FALSE;
}
gdk_paintable_invalidate_contents ((GdkPaintable *) self);
}
static void
clapper_gtk_buffering_paintable_init (ClapperGtkBufferingPaintable *self)
{
}
static void
clapper_gtk_buffering_paintable_class_init (ClapperGtkBufferingPaintableClass *klass)
{
}

View File

@@ -0,0 +1,28 @@
/* 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.
*/
#pragma once
#include "clapper-gtk-container.h"
G_BEGIN_DECLS
void clapper_gtk_container_emit_adapt (ClapperGtkContainer *container, gboolean adapt);
G_END_DECLS

View File

@@ -0,0 +1,413 @@
/* 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.
*/
/**
* ClapperGtkContainer:
*
* A simple container widget that holds just one child.
*
* It is designed to work well with OSD overlay, adding some useful functionalities
* to it, such as width and height that widget should target. This helps with
* implementing simple adaptive widgets by observing its own width and signalling
* when adaptive threshold is reached.
*
* You can use this when you need to create a widget that is adaptive or should have
* a limited maximal width/height.
*
* If you need to have more then single widget as child, place a widget that
* can hold multiple children such as [class@Gtk.Box] as a single conatiner child
* and then your widgets into that child.
*/
#include "clapper-gtk-container.h"
#include "clapper-gtk-container-private.h"
#include "clapper-gtk-limited-layout-private.h"
#define parent_class clapper_gtk_container_parent_class
G_DEFINE_TYPE (ClapperGtkContainer, clapper_gtk_container, GTK_TYPE_WIDGET)
enum
{
PROP_0,
PROP_WIDTH_TARGET,
PROP_HEIGHT_TARGET,
PROP_ADAPTIVE_WIDTH,
PROP_ADAPTIVE_HEIGHT,
PROP_LAST
};
enum
{
SIGNAL_ADAPT,
SIGNAL_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static guint signals[SIGNAL_LAST] = { 0, };
static inline void
_unparent_child (ClapperGtkContainer *self)
{
GtkWidget *child;
if ((child = gtk_widget_get_first_child (GTK_WIDGET (self))))
gtk_widget_unparent (child);
}
/**
* clapper_gtk_container_new:
*
* Creates a new #ClapperGtkContainer instance.
*
* Returns: a new container #GtkWidget.
*/
GtkWidget *
clapper_gtk_container_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_CONTAINER, NULL);
}
/**
* clapper_gtk_container_set_child:
* @container: a #ClapperGtkContainer
*
* Set a child #GtkWidget of @container.
*/
void
clapper_gtk_container_set_child (ClapperGtkContainer *self, GtkWidget *child)
{
g_return_if_fail (CLAPPER_GTK_IS_CONTAINER (self));
g_return_if_fail (GTK_IS_WIDGET (child));
_unparent_child (self);
gtk_widget_set_parent (child, GTK_WIDGET (self));
}
/**
* clapper_gtk_container_get_child:
* @container: a #ClapperGtkContainer
*
* Get a child #GtkWidget of @container.
*
* Returns: (transfer none) (nullable): #GtkWidget set as child.
*/
GtkWidget *
clapper_gtk_container_get_child (ClapperGtkContainer *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_CONTAINER (self), NULL);
return gtk_widget_get_first_child (GTK_WIDGET (self));
}
/**
* clapper_gtk_container_set_width_target:
* @container: a #ClapperGtkContainer
* @width: width to target -1 to restore default behavior
*
* Set a width that @container should target. When set container
* will not stretch beyond set @width while still expanding into
* possible boundaries trying to reach its target.
*/
void
clapper_gtk_container_set_width_target (ClapperGtkContainer *self, gint width)
{
GtkLayoutManager *layout;
g_return_if_fail (CLAPPER_GTK_IS_CONTAINER (self));
layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
clapper_gtk_limited_layout_set_max_width (CLAPPER_GTK_LIMITED_LAYOUT_CAST (layout), width);
}
/**
* clapper_gtk_container_get_width_target:
* @container: a #ClapperGtkContainer
*
* Get a @container width target.
*
* Returns: width target set by user or -1 when none.
*/
gint
clapper_gtk_container_get_width_target (ClapperGtkContainer *self)
{
GtkLayoutManager *layout;
g_return_val_if_fail (CLAPPER_GTK_IS_CONTAINER (self), -1);
layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
return clapper_gtk_limited_layout_get_max_width (CLAPPER_GTK_LIMITED_LAYOUT_CAST (layout));
}
/**
* clapper_gtk_container_set_height_target:
* @container: a #ClapperGtkContainer
* @height: height to target or -1 to restore default behavior
*
* Same as clapper_gtk_container_set_width_target() but for widget height.
*/
void
clapper_gtk_container_set_height_target (ClapperGtkContainer *self, gint height)
{
GtkLayoutManager *layout;
g_return_if_fail (CLAPPER_GTK_IS_CONTAINER (self));
layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
clapper_gtk_limited_layout_set_max_height (CLAPPER_GTK_LIMITED_LAYOUT_CAST (layout), height);
}
/**
* clapper_gtk_container_get_height_target:
* @container: a #ClapperGtkContainer
*
* Get a @container height target.
*
* Returns: height target set by user or -1 when none.
*/
gint
clapper_gtk_container_get_height_target (ClapperGtkContainer *self)
{
GtkLayoutManager *layout;
g_return_val_if_fail (CLAPPER_GTK_IS_CONTAINER (self), -1);
layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
return clapper_gtk_limited_layout_get_max_height (CLAPPER_GTK_LIMITED_LAYOUT_CAST (layout));
}
/**
* clapper_gtk_container_set_adaptive_width:
* @container: a #ClapperGtkContainer
* @width: a threshold on which adapt signal should be triggered or -1 to disable.
*
* Set an adaptive width threshold. When widget is resized to value or lower,
* an [signal@ClapperGtk.Container::adapt] signal will be emitted with %TRUE to
* notify implementation about mobile adaptation request, otherwise %FALSE when
* both threshold values are exceeded.
*/
void
clapper_gtk_container_set_adaptive_width (ClapperGtkContainer *self, gint width)
{
GtkLayoutManager *layout;
g_return_if_fail (CLAPPER_GTK_IS_CONTAINER (self));
layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
clapper_gtk_limited_layout_set_adaptive_width (
CLAPPER_GTK_LIMITED_LAYOUT_CAST (layout), width);
}
/**
* clapper_gtk_container_get_adaptive_width:
* @container: a #ClapperGtkContainer
*
* Get a @container adaptive width threshold.
*
* Returns: adaptive width set by user or -1 when none.
*/
gint
clapper_gtk_container_get_adaptive_width (ClapperGtkContainer *self)
{
GtkLayoutManager *layout;
g_return_val_if_fail (CLAPPER_GTK_IS_CONTAINER (self), -1);
layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
return clapper_gtk_limited_layout_get_adaptive_width (CLAPPER_GTK_LIMITED_LAYOUT_CAST (layout));
}
/**
* clapper_gtk_container_set_adaptive_height:
* @container: a #ClapperGtkContainer
* @height: a threshold on which adapt signal should be triggered or -1 to disable.
*
* Set an adaptive height threshold. When widget is resized to value or lower,
* an [signal@ClapperGtk.Container::adapt] signal will be emitted with %TRUE to
* notify implementation about mobile adaptation request, otherwise %FALSE when
* both threshold values are exceeded.
*/
void
clapper_gtk_container_set_adaptive_height (ClapperGtkContainer *self, gint height)
{
GtkLayoutManager *layout;
g_return_if_fail (CLAPPER_GTK_IS_CONTAINER (self));
layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
clapper_gtk_limited_layout_set_adaptive_height (
CLAPPER_GTK_LIMITED_LAYOUT_CAST (layout), height);
}
/**
* clapper_gtk_container_get_adaptive_height:
* @container: a #ClapperGtkContainer
*
* Get a @container adaptive height threshold.
*
* Returns: adaptive height set by user or -1 when none.
*/
gint
clapper_gtk_container_get_adaptive_height (ClapperGtkContainer *self)
{
GtkLayoutManager *layout;
g_return_val_if_fail (CLAPPER_GTK_IS_CONTAINER (self), -1);
layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
return clapper_gtk_limited_layout_get_adaptive_height (CLAPPER_GTK_LIMITED_LAYOUT_CAST (layout));
}
void
clapper_gtk_container_emit_adapt (ClapperGtkContainer *self, gboolean adapt)
{
if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
signals[SIGNAL_ADAPT], 0, NULL, NULL, NULL) != 0) {
g_signal_emit (self, signals[SIGNAL_ADAPT], 0, adapt);
}
}
static void
clapper_gtk_container_init (ClapperGtkContainer *self)
{
}
static void
clapper_gtk_container_dispose (GObject *object)
{
ClapperGtkContainer *self = CLAPPER_GTK_CONTAINER_CAST (object);
_unparent_child (self);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_gtk_container_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperGtkContainer *self = CLAPPER_GTK_CONTAINER_CAST (object);
switch (prop_id) {
case PROP_WIDTH_TARGET:
g_value_set_int (value, clapper_gtk_container_get_width_target (self));
break;
case PROP_HEIGHT_TARGET:
g_value_set_int (value, clapper_gtk_container_get_height_target (self));
break;
case PROP_ADAPTIVE_WIDTH:
g_value_set_int (value, clapper_gtk_container_get_adaptive_width (self));
break;
case PROP_ADAPTIVE_HEIGHT:
g_value_set_int (value, clapper_gtk_container_get_adaptive_height (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_container_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperGtkContainer *self = CLAPPER_GTK_CONTAINER_CAST (object);
switch (prop_id) {
case PROP_WIDTH_TARGET:
clapper_gtk_container_set_width_target (self, g_value_get_int (value));
break;
case PROP_HEIGHT_TARGET:
clapper_gtk_container_set_height_target (self, g_value_get_int (value));
break;
case PROP_ADAPTIVE_WIDTH:
clapper_gtk_container_set_adaptive_width (self, g_value_get_int (value));
break;
case PROP_ADAPTIVE_HEIGHT:
clapper_gtk_container_set_adaptive_height (self, g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_container_class_init (ClapperGtkContainerClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
gobject_class->get_property = clapper_gtk_container_get_property;
gobject_class->set_property = clapper_gtk_container_set_property;
gobject_class->dispose = clapper_gtk_container_dispose;
/**
* ClapperGtkContainer:width-target:
*
* Width that container should target.
*/
param_specs[PROP_WIDTH_TARGET] = g_param_spec_int ("width-target",
NULL, NULL, -1, G_MAXINT, -1,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkContainer:height-target:
*
* Height that container should target.
*/
param_specs[PROP_HEIGHT_TARGET] = g_param_spec_int ("height-target",
NULL, NULL, -1, G_MAXINT, -1,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkContainer:adaptive-width:
*
* Adaptive width threshold that triggers [signal@ClapperGtk.Container::adapt] signal.
*/
param_specs[PROP_ADAPTIVE_WIDTH] = g_param_spec_int ("adaptive-width",
NULL, NULL, -1, G_MAXINT, -1,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkContainer:adaptive-height:
*
* Adaptive height threshold that triggers [signal@ClapperGtk.Container::adapt] signal.
*/
param_specs[PROP_ADAPTIVE_HEIGHT] = g_param_spec_int ("adaptive-height",
NULL, NULL, -1, G_MAXINT, -1,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkContainer::adapt:
* @container: a #ClapperGtkContainer
* @adapt: %TRUE if narrowness reached adaptive threshold, %FALSE otherwise
*
* A helper signal for implementing mobile/narrow adaptive
* behavior on descendants.
*/
signals[SIGNAL_ADAPT] = g_signal_new ("adapt",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
gtk_widget_class_set_layout_manager_type (widget_class, CLAPPER_GTK_TYPE_LIMITED_LAYOUT);
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GENERIC);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-container");
}

View File

@@ -0,0 +1,67 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_CONTAINER (clapper_gtk_container_get_type())
#define CLAPPER_GTK_CONTAINER_CAST(obj) ((ClapperGtkContainer *)(obj))
G_DECLARE_DERIVABLE_TYPE (ClapperGtkContainer, clapper_gtk_container, CLAPPER_GTK, CONTAINER, GtkWidget)
struct _ClapperGtkContainerClass
{
GtkWidgetClass parent_class;
/*< private >*/
gpointer padding[4];
};
GtkWidget * clapper_gtk_container_new (void);
void clapper_gtk_container_set_child (ClapperGtkContainer *container, GtkWidget *child);
GtkWidget * clapper_gtk_container_get_child (ClapperGtkContainer *container);
void clapper_gtk_container_set_width_target (ClapperGtkContainer *container, gint width);
gint clapper_gtk_container_get_width_target (ClapperGtkContainer *container);
void clapper_gtk_container_set_height_target (ClapperGtkContainer *container, gint height);
gint clapper_gtk_container_get_height_target (ClapperGtkContainer *container);
void clapper_gtk_container_set_adaptive_width (ClapperGtkContainer *container, gint width);
gint clapper_gtk_container_get_adaptive_width (ClapperGtkContainer *container);
void clapper_gtk_container_set_adaptive_height (ClapperGtkContainer *container, gint height);
gint clapper_gtk_container_get_adaptive_height (ClapperGtkContainer *container);
G_END_DECLS

View File

@@ -0,0 +1,50 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <clapper-gtk/clapper-gtk-enum-types.h>
G_BEGIN_DECLS
/**
* ClapperGtkVideoActionMask:
* @CLAPPER_GTK_VIDEO_ACTION_NONE: No action
* @CLAPPER_GTK_VIDEO_ACTION_REVEAL_OVERLAYS: Reveal fading overlays
* @CLAPPER_GTK_VIDEO_ACTION_TOGGLE_PLAY: Toggle playback (triggered by single click/tap)
* @CLAPPER_GTK_VIDEO_ACTION_TOGGLE_FULLSCREEN: Toggle fullscreen (triggered by double click/tap)
* @CLAPPER_GTK_VIDEO_ACTION_SEEK_REQUEST: Seek request (triggered by double tap on screen side)
* @CLAPPER_GTK_VIDEO_ACTION_ANY: All of the above
*/
typedef enum
{
CLAPPER_GTK_VIDEO_ACTION_NONE = 0,
CLAPPER_GTK_VIDEO_ACTION_REVEAL_OVERLAYS = 1 << 0,
CLAPPER_GTK_VIDEO_ACTION_TOGGLE_PLAY = 1 << 1,
CLAPPER_GTK_VIDEO_ACTION_TOGGLE_FULLSCREEN = 1 << 2,
CLAPPER_GTK_VIDEO_ACTION_SEEK_REQUEST = 1 << 3,
CLAPPER_GTK_VIDEO_ACTION_ANY = 0x3FFFFFF
} ClapperGtkVideoActionMask;
G_END_DECLS

View File

@@ -0,0 +1,801 @@
/* 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.
*/
/**
* ClapperGtkExtraMenuButton:
*
* A menu button with extra options.
*/
#include "config.h"
#include <math.h>
#include <glib/gi18n-lib.h>
#include <clapper/clapper.h>
#include "clapper-gtk-extra-menu-button.h"
#include "clapper-gtk-utils-private.h"
#define PERCENTAGE_ROUND(a) (round ((gdouble) a / 0.01) * 0.01)
#define DEFAULT_CAN_OPEN_SUBTITLES FALSE
#define GST_CAT_DEFAULT clapper_gtk_extra_menu_button_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkExtraMenuButton
{
GtkWidget parent;
GtkWidget *menu_button;
GtkWidget *volume_box;
GtkWidget *volume_button;
GtkWidget *volume_spin;
GtkWidget *speed_box;
GtkWidget *speed_button;
GtkWidget *speed_spin;
GtkWidget *top_separator;
GtkWidget *video_list_view;
GtkScrolledWindow *video_sw;
GtkWidget *audio_list_view;
GtkScrolledWindow *audio_sw;
GtkWidget *subtitle_list_view;
GtkScrolledWindow *subtitle_sw;
ClapperPlayer *player;
ClapperMediaItem *current_item;
GSimpleActionGroup *action_group;
gboolean mute;
GBinding *volume_binding;
GBinding *speed_binding;
GBinding *video_binding;
GBinding *audio_binding;
GBinding *subtitle_binding;
gboolean can_open_subtitles;
};
#define parent_class clapper_gtk_extra_menu_button_parent_class
G_DEFINE_TYPE (ClapperGtkExtraMenuButton, clapper_gtk_extra_menu_button, GTK_TYPE_WIDGET)
enum
{
PROP_0,
PROP_VOLUME_VISIBLE,
PROP_SPEED_VISIBLE,
PROP_CAN_OPEN_SUBTITLES,
PROP_LAST
};
enum
{
SIGNAL_OPEN_SUBTITLES,
SIGNAL_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static guint signals[SIGNAL_LAST] = { 0, };
static void
_set_action_enabled (ClapperGtkExtraMenuButton *self, const gchar *name, gboolean enabled)
{
GAction *action = g_action_map_lookup_action (G_ACTION_MAP (self->action_group), name);
gboolean was_enabled = g_action_get_enabled (action);
if (was_enabled == enabled)
return;
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled);
}
static gboolean
_double_transform_func (GBinding *binding, const GValue *from_value,
GValue *to_value, gpointer user_data G_GNUC_UNUSED)
{
gdouble val_dbl = g_value_get_double (from_value);
g_value_set_double (to_value, PERCENTAGE_ROUND (val_dbl));
return TRUE;
}
static gint
volume_spin_input_cb (GtkSpinButton *spin_button, gdouble *value, ClapperGtkExtraMenuButton *self)
{
const gchar *text = gtk_editable_get_text (GTK_EDITABLE (spin_button));
gchar *sign = NULL;
gdouble volume = g_ascii_strtod (text, &sign);
if (volume < 0 || volume > 200
|| (sign && sign[0] != '\0' && sign[0] != '%'))
return GTK_INPUT_ERROR;
volume /= 100.0;
if (volume > 0.99 && volume < 1.01)
volume = 1.0;
*value = volume;
return TRUE;
}
static gboolean
volume_spin_output_cb (GtkSpinButton *spin_button, ClapperGtkExtraMenuButton *self)
{
GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
gdouble volume = gtk_adjustment_get_value (adjustment);
gchar *volume_str;
volume_str = g_strdup_printf ("%.0lf%%", volume * 100);
gtk_editable_set_text (GTK_EDITABLE (spin_button), volume_str);
g_free (volume_str);
return TRUE;
}
static void
volume_spin_changed_cb (GtkSpinButton *spin_button, ClapperGtkExtraMenuButton *self)
{
GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
gdouble volume = gtk_adjustment_get_value (adjustment);
gtk_button_set_icon_name (GTK_BUTTON (self->volume_button),
clapper_gtk_get_icon_name_for_volume ((!self->mute) ? volume : 0));
}
static gint
speed_spin_input_cb (GtkSpinButton *spin_button, gdouble *value, ClapperGtkExtraMenuButton *self)
{
const gchar *text = gtk_editable_get_text (GTK_EDITABLE (spin_button));
gchar *sign = NULL;
gdouble speed = g_ascii_strtod (text, &sign);
if (speed < 0.05 || speed > 2.0
|| (sign && sign[0] != '\0' && sign[0] != 'x'))
return GTK_INPUT_ERROR;
if (speed > 0.99 && speed < 1.01)
speed = 1.0;
*value = speed;
return TRUE;
}
static gboolean
speed_spin_output_cb (GtkSpinButton *spin_button, ClapperGtkExtraMenuButton *self)
{
GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
gdouble speed = gtk_adjustment_get_value (adjustment);
gchar *speed_str;
speed_str = g_strdup_printf ("%.2lfx", speed);
gtk_editable_set_text (GTK_EDITABLE (spin_button), speed_str);
g_free (speed_str);
return TRUE;
}
static void
speed_spin_changed_cb (GtkSpinButton *spin_button, ClapperGtkExtraMenuButton *self)
{
GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
gdouble speed = gtk_adjustment_get_value (adjustment);
gtk_button_set_icon_name (GTK_BUTTON (self->speed_button),
clapper_gtk_get_icon_name_for_speed (speed));
}
static void
visible_submenu_changed_cb (GtkPopoverMenu *popover_menu,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkExtraMenuButton *self)
{
gchar *name = NULL;
gboolean in_video, in_audio, in_subtitles;
g_object_get (popover_menu, "visible-submenu", &name, NULL);
/* TODO: Check if we have to compare translated strings here */
GST_DEBUG ("Visible submenu changed to: \"%s\"", name);
in_video = (g_strcmp0 (name, _("Video")) == 0);
in_audio = (g_strcmp0 (name, _("Audio")) == 0);
in_subtitles = (g_strcmp0 (name, _("Subtitles")) == 0);
/* XXX: This works around the issue where popover does not adapt its
* width when navigating submenus making spin buttons unnecesary centered */
gtk_scrolled_window_set_propagate_natural_width (self->video_sw, in_video);
gtk_scrolled_window_set_propagate_natural_width (self->audio_sw, in_audio);
gtk_scrolled_window_set_propagate_natural_width (self->subtitle_sw, in_subtitles);
g_free (name);
}
static void
_subtitles_enabled_changed_cb (ClapperPlayer *player,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkExtraMenuButton *self)
{
GAction *action = g_action_map_lookup_action (
G_ACTION_MAP (self->action_group), "subtitle-stream-enabled");
GVariant *variant = g_action_get_state (action);
gboolean was_enabled, enabled;
was_enabled = g_variant_get_boolean (variant);
enabled = clapper_player_get_subtitles_enabled (player);
g_variant_unref (variant);
if (was_enabled == enabled)
return;
variant = g_variant_ref_sink (g_variant_new_boolean (enabled));
g_simple_action_set_state (G_SIMPLE_ACTION (action), variant);
g_variant_unref (variant);
}
static void
_player_mute_changed_cb (ClapperPlayer *player,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkExtraMenuButton *self)
{
self->mute = clapper_player_get_mute (player);
volume_spin_changed_cb (GTK_SPIN_BUTTON (self->volume_spin), self);
}
static void
_queue_current_item_changed_cb (ClapperQueue *queue,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkExtraMenuButton *self)
{
ClapperMediaItem *current_item = clapper_queue_get_current_item (queue);
/* NOTE: This is also called after popover "map" signal */
if (gst_object_replace ((GstObject **) &self->current_item, GST_OBJECT_CAST (current_item))) {
_set_action_enabled (self, "open-subtitle-stream",
(self->can_open_subtitles && self->current_item != NULL));
}
gst_clear_object (&current_item);
}
static void
change_subtitle_stream_enabled (GSimpleAction *action, GVariant *value, gpointer user_data)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (user_data);
gboolean enable = g_variant_get_boolean (value);
if (G_LIKELY (self->player != NULL))
clapper_player_set_subtitles_enabled (self->player, enable);
g_simple_action_set_state (action, value);
}
static void
open_subtitle_stream (GSimpleAction *action, GVariant *param, gpointer user_data)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (user_data);
/* We should not be here otherwise */
if (G_LIKELY (self->can_open_subtitles && self->current_item != NULL))
g_signal_emit (self, signals[SIGNAL_OPEN_SUBTITLES], 0, self->current_item);
}
static void
_determine_top_separator_visibility (ClapperGtkExtraMenuButton *self)
{
gboolean visible = gtk_widget_get_visible (self->volume_box)
|| gtk_widget_get_visible (self->speed_box);
gtk_widget_set_visible (self->top_separator, visible);
}
/**
* clapper_gtk_extra_menu_button_new:
*
* Creates a new #ClapperGtkExtraMenuButton instance.
*
* Returns: a new extra menu button #GtkWidget.
*/
GtkWidget *
clapper_gtk_extra_menu_button_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_EXTRA_MENU_BUTTON, NULL);
}
/**
* clapper_gtk_extra_menu_button_set_volume_visible:
* @button: a #ClapperGtkExtraMenuButton
* @visible: whether visible
*
* Set whether volume control inside popover should be visible.
*/
void
clapper_gtk_extra_menu_button_set_volume_visible (ClapperGtkExtraMenuButton *self, gboolean visible)
{
g_return_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self));
if (gtk_widget_get_visible (self->volume_box) != visible) {
gtk_widget_set_visible (self->volume_box, visible);
_determine_top_separator_visibility (self);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_VOLUME_VISIBLE]);
}
}
/**
* clapper_gtk_extra_menu_button_get_volume_visible:
* @button: a #ClapperGtkExtraMenuButton
*
* Get whether volume control inside popover is visible.
*
* Returns: TRUE if volume control is visible, %FALSE otherwise.
*/
gboolean
clapper_gtk_extra_menu_button_get_volume_visible (ClapperGtkExtraMenuButton *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self), FALSE);
return gtk_widget_get_visible (self->volume_box);
}
/**
* clapper_gtk_extra_menu_button_set_speed_visible:
* @button: a #ClapperGtkExtraMenuButton
* @visible: whether visible
*
* Set whether speed control inside popover should be visible.
*/
void
clapper_gtk_extra_menu_button_set_speed_visible (ClapperGtkExtraMenuButton *self, gboolean visible)
{
g_return_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self));
if (gtk_widget_get_visible (self->speed_box) != visible) {
gtk_widget_set_visible (self->speed_box, visible);
_determine_top_separator_visibility (self);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_SPEED_VISIBLE]);
}
}
/**
* clapper_gtk_extra_menu_button_get_speed_visible:
* @button: a #ClapperGtkExtraMenuButton
*
* Get whether speed control inside popover is visible.
*
* Returns: %TRUE if speed control is visible, %FALSE otherwise.
*/
gboolean
clapper_gtk_extra_menu_button_get_speed_visible (ClapperGtkExtraMenuButton *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self), FALSE);
return gtk_widget_get_visible (self->speed_box);
}
/**
* clapper_gtk_extra_menu_button_set_can_open_subtitles:
* @button: a #ClapperGtkExtraMenuButton
* @allowed: whether opening subtitles should be allowed
*
* Set whether an option to open external subtitle stream should be allowed.
*
* Note that this [class@Gtk.Widget] can only add subtitles to currently playing
* [class@Clapper.MediaItem]. When no media is selected, option to open subtitles
* will not be shown regardless how this option is set.
*/
void
clapper_gtk_extra_menu_button_set_can_open_subtitles (ClapperGtkExtraMenuButton *self, gboolean allowed)
{
gboolean changed;
g_return_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self));
if ((changed = self->can_open_subtitles != allowed)) {
self->can_open_subtitles = allowed;
_set_action_enabled (self, "open-subtitle-stream",
(self->can_open_subtitles && self->current_item != NULL));
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_CAN_OPEN_SUBTITLES]);
}
}
/**
* clapper_gtk_extra_menu_button_get_can_open_subtitles:
* @button: a #ClapperGtkExtraMenuButton
*
* Get whether an option to open external subtitle stream inside popover is visible.
*
* Returns: %TRUE if open subtitles is visible, %FALSE otherwise.
*/
gboolean
clapper_gtk_extra_menu_button_get_can_open_subtitles (ClapperGtkExtraMenuButton *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self), FALSE);
return self->can_open_subtitles;
}
static void
clapper_gtk_extra_menu_button_init (ClapperGtkExtraMenuButton *self)
{
static GActionEntry action_entries[] = {
{ "subtitle-stream-enabled", NULL, NULL, "true", change_subtitle_stream_enabled },
{ "open-subtitle-stream", open_subtitle_stream, NULL, NULL, NULL },
};
gtk_widget_init_template (GTK_WIDGET (self));
self->action_group = g_simple_action_group_new ();
g_object_bind_property (self, "css-classes", self->menu_button,
"css-classes", G_BINDING_DEFAULT);
g_action_map_add_action_entries (G_ACTION_MAP (self->action_group),
action_entries, G_N_ELEMENTS (action_entries), self);
gtk_widget_insert_action_group (GTK_WIDGET (self),
"clappergtk", G_ACTION_GROUP (self->action_group));
/* Set default values */
self->can_open_subtitles = DEFAULT_CAN_OPEN_SUBTITLES;
_set_action_enabled (self, "open-subtitle-stream", self->can_open_subtitles);
}
static void
clapper_gtk_extra_menu_button_compute_expand (GtkWidget *widget,
gboolean *hexpand_p, gboolean *vexpand_p)
{
GtkWidget *child;
gboolean hexpand = FALSE;
gboolean vexpand = FALSE;
if ((child = gtk_widget_get_first_child (widget))) {
hexpand = gtk_widget_compute_expand (child, GTK_ORIENTATION_HORIZONTAL);
vexpand = gtk_widget_compute_expand (child, GTK_ORIENTATION_VERTICAL);
}
*hexpand_p = hexpand;
*vexpand_p = vexpand;
}
static void
clapper_gtk_extra_menu_button_realize (GtkWidget *widget)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (widget);
GST_TRACE_OBJECT (self, "Realize");
if ((self->player = clapper_gtk_get_player_from_ancestor (GTK_WIDGET (self)))) {
ClapperStreamList *stream_list;
GtkSingleSelection *selection;
g_signal_connect (self->player, "notify::mute",
G_CALLBACK (_player_mute_changed_cb), self);
self->mute = clapper_player_get_mute (self->player);
stream_list = clapper_player_get_video_streams (self->player);
selection = gtk_single_selection_new (gst_object_ref (stream_list));
gtk_single_selection_set_autoselect (selection, FALSE);
self->video_binding = g_object_bind_property (stream_list, "current-index",
selection, "selected", G_BINDING_SYNC_CREATE);
gtk_list_view_set_model (GTK_LIST_VIEW (self->video_list_view),
GTK_SELECTION_MODEL (selection));
g_object_unref (selection);
stream_list = clapper_player_get_audio_streams (self->player);
selection = gtk_single_selection_new (gst_object_ref (stream_list));
gtk_single_selection_set_autoselect (selection, FALSE);
self->audio_binding = g_object_bind_property (stream_list, "current-index",
selection, "selected", G_BINDING_SYNC_CREATE);
gtk_list_view_set_model (GTK_LIST_VIEW (self->audio_list_view),
GTK_SELECTION_MODEL (selection));
g_object_unref (selection);
stream_list = clapper_player_get_subtitle_streams (self->player);
selection = gtk_single_selection_new (gst_object_ref (stream_list));
gtk_single_selection_set_autoselect (selection, FALSE);
self->subtitle_binding = g_object_bind_property (stream_list, "current-index",
selection, "selected", G_BINDING_SYNC_CREATE);
gtk_list_view_set_model (GTK_LIST_VIEW (self->subtitle_list_view),
GTK_SELECTION_MODEL (selection));
g_object_unref (selection);
}
GTK_WIDGET_CLASS (parent_class)->realize (widget);
}
static void
clapper_gtk_extra_menu_button_unrealize (GtkWidget *widget)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (widget);
GST_TRACE_OBJECT (self, "Unrealize");
g_clear_pointer (&self->video_binding, g_binding_unbind);
g_clear_pointer (&self->audio_binding, g_binding_unbind);
g_clear_pointer (&self->subtitle_binding, g_binding_unbind);
gtk_list_view_set_model (GTK_LIST_VIEW (self->video_list_view), NULL);
gtk_list_view_set_model (GTK_LIST_VIEW (self->audio_list_view), NULL);
gtk_list_view_set_model (GTK_LIST_VIEW (self->subtitle_list_view), NULL);
if (self->player) {
g_signal_handlers_disconnect_by_func (self->player, _player_mute_changed_cb, self);
self->player = NULL;
}
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}
static void
popover_map_cb (GtkWidget *widget, ClapperGtkExtraMenuButton *self)
{
ClapperQueue *queue;
GST_TRACE_OBJECT (self, "Popover map");
if (G_UNLIKELY (self->player == NULL))
return;
queue = clapper_player_get_queue (self->player);
self->volume_binding = g_object_bind_property_full (self->player, "volume",
self->volume_spin, "value",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
(GBindingTransformFunc) _double_transform_func,
(GBindingTransformFunc) _double_transform_func,
NULL, NULL);
self->speed_binding = g_object_bind_property_full (self->player, "speed",
self->speed_spin, "value",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
(GBindingTransformFunc) _double_transform_func,
(GBindingTransformFunc) _double_transform_func,
NULL, NULL);
g_signal_connect (self->player, "notify::subtitles-enabled",
G_CALLBACK (_subtitles_enabled_changed_cb), self);
_subtitles_enabled_changed_cb (self->player, NULL, self);
g_signal_connect (queue, "notify::current-item",
G_CALLBACK (_queue_current_item_changed_cb), self);
_queue_current_item_changed_cb (queue, NULL, self);
}
static void
popover_unmap_cb (GtkWidget *widget, ClapperGtkExtraMenuButton *self)
{
ClapperQueue *queue;
GST_TRACE_OBJECT (self, "Popover unmap");
if (G_UNLIKELY (self->player == NULL))
return;
queue = clapper_player_get_queue (self->player);
g_clear_pointer (&self->volume_binding, g_binding_unbind);
g_clear_pointer (&self->speed_binding, g_binding_unbind);
g_signal_handlers_disconnect_by_func (self->player, _subtitles_enabled_changed_cb, self);
g_signal_handlers_disconnect_by_func (queue, _queue_current_item_changed_cb, self);
}
static void
clapper_gtk_extra_menu_button_dispose (GObject *object)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (object);
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_GTK_TYPE_EXTRA_MENU_BUTTON);
g_clear_pointer (&self->menu_button, gtk_widget_unparent);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_gtk_extra_menu_button_finalize (GObject *object)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
gst_clear_object (&self->current_item);
g_object_unref (self->action_group);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_gtk_extra_menu_button_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (object);
switch (prop_id) {
case PROP_VOLUME_VISIBLE:
g_value_set_boolean (value, clapper_gtk_extra_menu_button_get_volume_visible (self));
break;
case PROP_SPEED_VISIBLE:
g_value_set_boolean (value, clapper_gtk_extra_menu_button_get_speed_visible (self));
break;
case PROP_CAN_OPEN_SUBTITLES:
g_value_set_boolean (value, clapper_gtk_extra_menu_button_get_can_open_subtitles (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_extra_menu_button_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (object);
switch (prop_id) {
case PROP_VOLUME_VISIBLE:
clapper_gtk_extra_menu_button_set_volume_visible (self, g_value_get_boolean (value));
break;
case PROP_SPEED_VISIBLE:
clapper_gtk_extra_menu_button_set_speed_visible (self, g_value_get_boolean (value));
break;
case PROP_CAN_OPEN_SUBTITLES:
clapper_gtk_extra_menu_button_set_can_open_subtitles (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_extra_menu_button_class_init (ClapperGtkExtraMenuButtonClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtkextramenubutton", 0,
"Clapper GTK Extra Menu Button");
clapper_gtk_init_translations ();
gobject_class->get_property = clapper_gtk_extra_menu_button_get_property;
gobject_class->set_property = clapper_gtk_extra_menu_button_set_property;
gobject_class->dispose = clapper_gtk_extra_menu_button_dispose;
gobject_class->finalize = clapper_gtk_extra_menu_button_finalize;
widget_class->compute_expand = clapper_gtk_extra_menu_button_compute_expand;
widget_class->realize = clapper_gtk_extra_menu_button_realize;
widget_class->unrealize = clapper_gtk_extra_menu_button_unrealize;
/**
* ClapperGtkExtraMenuButton:volume-visible:
*
* Visibility of volume control inside popover.
*/
param_specs[PROP_VOLUME_VISIBLE] = g_param_spec_boolean ("volume-visible",
NULL, NULL, TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkExtraMenuButton:speed-visible:
*
* Visibility of speed control inside popover.
*/
param_specs[PROP_SPEED_VISIBLE] = g_param_spec_boolean ("speed-visible",
NULL, NULL, TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkExtraMenuButton:can-open-subtitles:
*
* Visibility of open subtitles option inside popover.
*/
param_specs[PROP_CAN_OPEN_SUBTITLES] = g_param_spec_boolean ("can-open-subtitles",
NULL, NULL, FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkExtraMenuButton::open-subtitles:
* @button: a #ClapperGtkExtraMenuButton
* @item: a #ClapperMediaItem
*
* A signal that user wants to open subtitles file.
*
* Implementation should add a way for user to select subtitles to open
* such as by e.g. using [class@Gtk.FileDialog] and then add them to the
* @item using [method@Clapper.MediaItem.set_suburi] method.
*
* This signal will pass the [class@Clapper.MediaItem] that was current when
* user clicked the open button and subtitles should be added to this @item.
* This avoids situations where another item starts playing before user selects
* subtitles file to be opened. When using asynchronous operations to open file,
* implementation should [method@GObject.Object.ref] the item to ensure that it
* stays valid until finish.
*
* Note that this signal will not be emitted if open button is not visible by
* setting [method@ClapperGtk.ExtraMenuButton.set_can_open_subtitles] to %TRUE,
* so you do not have to implement handler for it otherwise.
*/
signals[SIGNAL_OPEN_SUBTITLES] = g_signal_new ("open-subtitles",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0, NULL, NULL, NULL, G_TYPE_NONE, 1, CLAPPER_TYPE_MEDIA_ITEM);
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-extra-menu-button.ui");
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, menu_button);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, volume_box);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, volume_button);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, volume_spin);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, speed_box);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, speed_button);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, speed_spin);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, top_separator);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, video_list_view);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, video_sw);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, audio_list_view);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, audio_sw);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, subtitle_list_view);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, subtitle_sw);
gtk_widget_class_bind_template_callback (widget_class, volume_spin_input_cb);
gtk_widget_class_bind_template_callback (widget_class, volume_spin_output_cb);
gtk_widget_class_bind_template_callback (widget_class, volume_spin_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, speed_spin_input_cb);
gtk_widget_class_bind_template_callback (widget_class, speed_spin_output_cb);
gtk_widget_class_bind_template_callback (widget_class, speed_spin_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, popover_map_cb);
gtk_widget_class_bind_template_callback (widget_class, popover_unmap_cb);
gtk_widget_class_bind_template_callback (widget_class, visible_submenu_changed_cb);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_BUTTON);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-extra-menu-button");
}

View File

@@ -0,0 +1,51 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_EXTRA_MENU_BUTTON (clapper_gtk_extra_menu_button_get_type())
#define CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST(obj) ((ClapperGtkExtraMenuButton *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkExtraMenuButton, clapper_gtk_extra_menu_button, CLAPPER_GTK, EXTRA_MENU_BUTTON, GtkWidget)
GtkWidget * clapper_gtk_extra_menu_button_new (void);
void clapper_gtk_extra_menu_button_set_volume_visible (ClapperGtkExtraMenuButton *button, gboolean visible);
gboolean clapper_gtk_extra_menu_button_get_volume_visible (ClapperGtkExtraMenuButton *button);
void clapper_gtk_extra_menu_button_set_speed_visible (ClapperGtkExtraMenuButton *button, gboolean visible);
gboolean clapper_gtk_extra_menu_button_get_speed_visible (ClapperGtkExtraMenuButton *button);
void clapper_gtk_extra_menu_button_set_can_open_subtitles (ClapperGtkExtraMenuButton *button, gboolean allowed);
gboolean clapper_gtk_extra_menu_button_get_can_open_subtitles (ClapperGtkExtraMenuButton *button);
G_END_DECLS

View File

@@ -0,0 +1,235 @@
/* 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.
*/
/**
* ClapperGtkLeadContainer:
*
* A #ClapperGtkContainer that can take priority in user interactions with the #ClapperGtkVideo.
*
* #ClapperGtkLeadContainer is a special type of [class@ClapperGtk.Container] that can
* lead in interaction events. When "leading", it is assumed that user interactions
* over it which would normally trigger actions can be blocked/ignored when set in mask
* of actions that this widget should block.
*
* This kind of container is useful when creating some statically visible overlays
* covering top of [class@ClapperGtk.Video] that you want to take priority instead of
* triggering default actions such as toggle play on click or revealing fading overlays.
*
* For more info how container widget works see [class@ClapperGtk.Container] documentation.
*/
#include "clapper-gtk-lead-container.h"
#define DEFAULT_LEADING TRUE
#define DEFAULT_BLOCKED_ACTIONS CLAPPER_GTK_VIDEO_ACTION_NONE
typedef struct _ClapperGtkLeadContainerPrivate ClapperGtkLeadContainerPrivate;
struct _ClapperGtkLeadContainerPrivate
{
gboolean leading;
ClapperGtkVideoActionMask blocked_actions;
};
#define parent_class clapper_gtk_lead_container_parent_class
G_DEFINE_TYPE_WITH_PRIVATE (ClapperGtkLeadContainer, clapper_gtk_lead_container, CLAPPER_GTK_TYPE_CONTAINER)
enum
{
PROP_0,
PROP_LEADING,
PROP_BLOCKED_ACTIONS,
PROP_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
/**
* clapper_gtk_lead_container_new:
*
* Creates a new #ClapperGtkLeadContainer instance.
*
* Returns: a new lead container #GtkWidget.
*/
GtkWidget *
clapper_gtk_lead_container_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_LEAD_CONTAINER, NULL);
}
/**
* clapper_gtk_lead_container_set_leading:
* @lead_container: a #ClapperGtkLeadContainer
* @leading: enable leadership
*
* Set if @lead_container leadership should be enabled.
*
* When enabled, interactions with @lead_container will not trigger
* their default behavior, instead container and its contents will take priority.
*/
void
clapper_gtk_lead_container_set_leading (ClapperGtkLeadContainer *self, gboolean leading)
{
ClapperGtkLeadContainerPrivate *priv;
g_return_if_fail (CLAPPER_GTK_IS_LEAD_CONTAINER (self));
priv = clapper_gtk_lead_container_get_instance_private (self);
priv->leading = leading;
}
/**
* clapper_gtk_lead_container_get_leading:
* @lead_container: a #ClapperGtkLeadContainer
*
* Get a whenever @lead_container has leadership set.
*
* Returns: %TRUE if container is leading, %FALSE otherwise.
*/
gboolean
clapper_gtk_lead_container_get_leading (ClapperGtkLeadContainer *self)
{
ClapperGtkLeadContainerPrivate *priv;
g_return_val_if_fail (CLAPPER_GTK_IS_LEAD_CONTAINER (self), FALSE);
priv = clapper_gtk_lead_container_get_instance_private (self);
return priv->leading;
}
/**
* clapper_gtk_lead_container_set_blocked_actions:
* @lead_container: a #ClapperGtkLeadContainer
* @actions: a #ClapperGtkVideoActionMask of actions to block
*
* Set @actions that #ClapperGtkVideo should skip when #GdkEvent which
* would normally trigger them happens inside @lead_container.
*/
void
clapper_gtk_lead_container_set_blocked_actions (ClapperGtkLeadContainer *self, ClapperGtkVideoActionMask actions)
{
ClapperGtkLeadContainerPrivate *priv;
g_return_if_fail (CLAPPER_GTK_IS_LEAD_CONTAINER (self));
priv = clapper_gtk_lead_container_get_instance_private (self);
priv->blocked_actions = actions;
}
/**
* clapper_gtk_lead_container_get_blocked_actions:
* @lead_container: a #ClapperGtkLeadContainer
*
* Get @actions that were set for this @lead_container to block.
*
* Returns: a mask of actions that container blocks from being triggered on video.
*/
ClapperGtkVideoActionMask
clapper_gtk_lead_container_get_blocked_actions (ClapperGtkLeadContainer *self)
{
ClapperGtkLeadContainerPrivate *priv;
g_return_val_if_fail (CLAPPER_GTK_IS_LEAD_CONTAINER (self), 0);
priv = clapper_gtk_lead_container_get_instance_private (self);
return priv->blocked_actions;
}
static void
clapper_gtk_lead_container_init (ClapperGtkLeadContainer *self)
{
ClapperGtkLeadContainerPrivate *priv = clapper_gtk_lead_container_get_instance_private (self);
priv->leading = DEFAULT_LEADING;
priv->blocked_actions = DEFAULT_BLOCKED_ACTIONS;
}
static void
clapper_gtk_lead_container_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperGtkLeadContainer *self = CLAPPER_GTK_LEAD_CONTAINER_CAST (object);
switch (prop_id) {
case PROP_LEADING:
g_value_set_boolean (value, clapper_gtk_lead_container_get_leading (self));
break;
case PROP_BLOCKED_ACTIONS:
g_value_set_flags (value, clapper_gtk_lead_container_get_blocked_actions (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_lead_container_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperGtkLeadContainer *self = CLAPPER_GTK_LEAD_CONTAINER_CAST (object);
switch (prop_id) {
case PROP_LEADING:
clapper_gtk_lead_container_set_leading (self, g_value_get_boolean (value));
break;
case PROP_BLOCKED_ACTIONS:
clapper_gtk_lead_container_set_blocked_actions (self, g_value_get_flags (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_lead_container_class_init (ClapperGtkLeadContainerClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
gobject_class->get_property = clapper_gtk_lead_container_get_property;
gobject_class->set_property = clapper_gtk_lead_container_set_property;
/**
* ClapperGtkLeadContainer:leading:
*
* Width that container should target.
*/
param_specs[PROP_LEADING] = g_param_spec_boolean ("leading",
NULL, NULL, DEFAULT_LEADING,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkLeadContainer:blocked-actions:
*
* Mask of actions that container blocks from being triggered on video.
*/
param_specs[PROP_BLOCKED_ACTIONS] = g_param_spec_flags ("blocked-actions",
NULL, NULL, CLAPPER_GTK_TYPE_VIDEO_ACTION_MASK, DEFAULT_BLOCKED_ACTIONS,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-lead-container");
}

View File

@@ -0,0 +1,58 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-enums.h>
#include <clapper-gtk/clapper-gtk-container.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_LEAD_CONTAINER (clapper_gtk_lead_container_get_type())
#define CLAPPER_GTK_LEAD_CONTAINER_CAST(obj) ((ClapperGtkLeadContainer *)(obj))
G_DECLARE_DERIVABLE_TYPE (ClapperGtkLeadContainer, clapper_gtk_lead_container, CLAPPER_GTK, LEAD_CONTAINER, ClapperGtkContainer)
struct _ClapperGtkLeadContainerClass
{
ClapperGtkContainerClass parent_class;
/*< private >*/
gpointer padding[4];
};
GtkWidget * clapper_gtk_lead_container_new (void);
void clapper_gtk_lead_container_set_leading (ClapperGtkLeadContainer *lead_container, gboolean leading);
gboolean clapper_gtk_lead_container_get_leading (ClapperGtkLeadContainer *lead_container);
void clapper_gtk_lead_container_set_blocked_actions (ClapperGtkLeadContainer *lead_container, ClapperGtkVideoActionMask actions);
ClapperGtkVideoActionMask clapper_gtk_lead_container_get_blocked_actions (ClapperGtkLeadContainer *lead_container);
G_END_DECLS

View File

@@ -0,0 +1,57 @@
/* 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.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_LIMITED_LAYOUT (clapper_gtk_limited_layout_get_type())
#define CLAPPER_GTK_LIMITED_LAYOUT_CAST(obj) ((ClapperGtkLimitedLayout *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkLimitedLayout, clapper_gtk_limited_layout, CLAPPER_GTK, LIMITED_LAYOUT, GtkLayoutManager)
G_GNUC_INTERNAL
void clapper_gtk_limited_layout_set_max_width (ClapperGtkLimitedLayout *layout, gint max_width);
G_GNUC_INTERNAL
gint clapper_gtk_limited_layout_get_max_width (ClapperGtkLimitedLayout *layout);
G_GNUC_INTERNAL
void clapper_gtk_limited_layout_set_max_height (ClapperGtkLimitedLayout *layout, gint max_height);
G_GNUC_INTERNAL
gint clapper_gtk_limited_layout_get_max_height (ClapperGtkLimitedLayout *layout);
G_GNUC_INTERNAL
void clapper_gtk_limited_layout_set_adaptive_width (ClapperGtkLimitedLayout *layout, gint width);
G_GNUC_INTERNAL
gint clapper_gtk_limited_layout_get_adaptive_width (ClapperGtkLimitedLayout *layout);
G_GNUC_INTERNAL
void clapper_gtk_limited_layout_set_adaptive_height (ClapperGtkLimitedLayout *layout, gint height);
G_GNUC_INTERNAL
gint clapper_gtk_limited_layout_get_adaptive_height (ClapperGtkLimitedLayout *layout);
G_END_DECLS

View File

@@ -0,0 +1,159 @@
/* 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.
*/
#include <clapper/clapper.h>
#include "clapper-gtk-limited-layout-private.h"
#include "clapper-gtk-container-private.h"
#define GST_CAT_DEFAULT clapper_gtk_limited_layout_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkLimitedLayout
{
GtkLayoutManager parent;
gint max_width;
gint max_height;
gint adaptive_width;
gint adaptive_height;
gboolean adapt;
};
#define parent_class clapper_gtk_limited_layout_parent_class
G_DEFINE_TYPE (ClapperGtkLimitedLayout, clapper_gtk_limited_layout, GTK_TYPE_LAYOUT_MANAGER)
void
clapper_gtk_limited_layout_set_max_width (ClapperGtkLimitedLayout *self, gint max_width)
{
self->max_width = max_width;
}
gint
clapper_gtk_limited_layout_get_max_width (ClapperGtkLimitedLayout *self)
{
return self->max_width;
}
void
clapper_gtk_limited_layout_set_max_height (ClapperGtkLimitedLayout *self, gint max_height)
{
self->max_height = max_height;
}
gint
clapper_gtk_limited_layout_get_max_height (ClapperGtkLimitedLayout *self)
{
return self->max_height;
}
void
clapper_gtk_limited_layout_set_adaptive_width (ClapperGtkLimitedLayout *self, gint width)
{
self->adaptive_width = width;
}
gint
clapper_gtk_limited_layout_get_adaptive_width (ClapperGtkLimitedLayout *self)
{
return self->adaptive_width;
}
void
clapper_gtk_limited_layout_set_adaptive_height (ClapperGtkLimitedLayout *self, gint height)
{
self->adaptive_height = height;
}
gint
clapper_gtk_limited_layout_get_adaptive_height (ClapperGtkLimitedLayout *self)
{
return self->adaptive_height;
}
static void
clapper_gtk_limited_layout_measure (GtkLayoutManager *layout_manager,
GtkWidget *widget, GtkOrientation orientation, gint for_size,
gint *minimum, gint *natural, gint *minimum_baseline, gint *natural_baseline)
{
ClapperGtkLimitedLayout *self = CLAPPER_GTK_LIMITED_LAYOUT_CAST (layout_manager);
GtkWidget *child = gtk_widget_get_first_child (widget);
if (child && gtk_widget_should_layout (child)) {
gint child_min = 0, child_nat = 0;
gint child_min_baseline = -1, child_nat_baseline = -1;
gtk_widget_measure (child, orientation, for_size,
&child_min, &child_nat,
&child_min_baseline, &child_nat_baseline);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
*natural = (self->max_width < 0) ? MAX (*natural, child_nat) : self->max_width;
else if (orientation == GTK_ORIENTATION_VERTICAL)
*natural = (self->max_height < 0) ? MAX (*natural, child_nat) : self->max_height;
*minimum = MAX (*minimum, child_min);
if (child_min_baseline > -1)
*minimum_baseline = MAX (*minimum_baseline, child_min_baseline);
if (child_nat_baseline > -1)
*natural_baseline = MAX (*natural_baseline, child_nat_baseline);
}
}
static void
clapper_gtk_limited_layout_allocate (GtkLayoutManager *layout_manager,
GtkWidget *widget, gint width, gint height, gint baseline)
{
ClapperGtkLimitedLayout *self = CLAPPER_GTK_LIMITED_LAYOUT_CAST (layout_manager);
GtkWidget *child = gtk_widget_get_first_child (widget);
gboolean adapt = (width <= self->adaptive_width || height <= self->adaptive_height);
if (child && gtk_widget_should_layout (child))
gtk_widget_allocate (child, width, height, baseline, NULL);
if (G_UNLIKELY (self->adapt != adapt)) {
self->adapt = adapt;
clapper_gtk_container_emit_adapt (CLAPPER_GTK_CONTAINER_CAST (widget), adapt);
}
}
static void
clapper_gtk_limited_layout_init (ClapperGtkLimitedLayout *self)
{
self->max_width = -1;
self->max_height = -1;
self->adaptive_width = -1;
self->adaptive_height = -1;
}
static void
clapper_gtk_limited_layout_class_init (ClapperGtkLimitedLayoutClass *klass)
{
GtkLayoutManagerClass *layout_manager_class = (GtkLayoutManagerClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtklimitedlayout", 0,
"Clapper GTK Limited Layout");
layout_manager_class->measure = clapper_gtk_limited_layout_measure;
layout_manager_class->allocate = clapper_gtk_limited_layout_allocate;
}

View File

@@ -0,0 +1,132 @@
/* 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.
*/
/**
* ClapperGtkNextItemButton:
*
* A #GtkButton for selecting next queue item.
*/
#include <clapper/clapper.h>
#include "clapper-gtk-next-item-button.h"
#include "clapper-gtk-utils.h"
#define GST_CAT_DEFAULT clapper_gtk_next_item_button_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkNextItemButton
{
GtkButton parent;
GBinding *n_items_binding;
GBinding *current_index_binding;
};
#define parent_class clapper_gtk_next_item_button_parent_class
G_DEFINE_TYPE (ClapperGtkNextItemButton, clapper_gtk_next_item_button, GTK_TYPE_BUTTON)
static gboolean
_transform_sensitive_func (GBinding *binding, const GValue *from_value,
GValue *to_value, ClapperGtkNextItemButton *self)
{
ClapperQueue *queue = CLAPPER_QUEUE_CAST (g_binding_dup_source (binding));
guint current_index;
gboolean can_skip;
if (G_UNLIKELY (queue == NULL))
return FALSE;
current_index = clapper_queue_get_current_index (queue);
can_skip = (current_index != CLAPPER_QUEUE_INVALID_POSITION
&& current_index < clapper_queue_get_n_items (queue) - 1);
gst_object_unref (queue);
g_value_set_boolean (to_value, can_skip);
GST_DEBUG_OBJECT (self, "Set sensitive: %s", (can_skip) ? "yes" : "no");
return TRUE;
}
/**
* clapper_gtk_next_item_button_new:
*
* Creates a new #ClapperGtkNextItemButton to play next #ClapperMediaItem.
*
* Returns: a new next item button #GtkWidget.
*/
GtkWidget *
clapper_gtk_next_item_button_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_NEXT_ITEM_BUTTON, NULL);
}
static void
clapper_gtk_next_item_button_init (ClapperGtkNextItemButton *self)
{
gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
gtk_button_set_icon_name (GTK_BUTTON (self), "media-skip-forward-symbolic");
gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "video.next-item");
}
static void
clapper_gtk_next_item_button_map (GtkWidget *widget)
{
ClapperGtkNextItemButton *self = CLAPPER_GTK_NEXT_ITEM_BUTTON_CAST (widget);
ClapperPlayer *player;
if ((player = clapper_gtk_get_player_from_ancestor (widget))) {
ClapperQueue *queue = clapper_player_get_queue (player);
self->n_items_binding = g_object_bind_property_full (queue, "n-items",
self, "sensitive", G_BINDING_DEFAULT, // Since we sync below, no need to do it twice
(GBindingTransformFunc) _transform_sensitive_func,
NULL, self, NULL);
self->current_index_binding = g_object_bind_property_full (queue, "current-index",
self, "sensitive", G_BINDING_SYNC_CREATE,
(GBindingTransformFunc) _transform_sensitive_func,
NULL, self, NULL);
}
GTK_WIDGET_CLASS (parent_class)->map (widget);
}
static void
clapper_gtk_next_item_button_unmap (GtkWidget *widget)
{
ClapperGtkNextItemButton *self = CLAPPER_GTK_NEXT_ITEM_BUTTON_CAST (widget);
g_clear_pointer (&self->n_items_binding, g_binding_unbind);
g_clear_pointer (&self->current_index_binding, g_binding_unbind);
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
static void
clapper_gtk_next_item_button_class_init (ClapperGtkNextItemButtonClass *klass)
{
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtknextitembutton", 0,
"Clapper GTK Next Item Button");
widget_class->map = clapper_gtk_next_item_button_map;
widget_class->unmap = clapper_gtk_next_item_button_unmap;
}

View File

@@ -0,0 +1,39 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_NEXT_ITEM_BUTTON (clapper_gtk_next_item_button_get_type())
#define CLAPPER_GTK_NEXT_ITEM_BUTTON_CAST(obj) ((ClapperGtkNextItemButton *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkNextItemButton, clapper_gtk_next_item_button, CLAPPER_GTK, NEXT_ITEM_BUTTON, GtkButton)
GtkWidget * clapper_gtk_next_item_button_new (void);
G_END_DECLS

View File

@@ -0,0 +1,132 @@
/* 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.
*/
/**
* ClapperGtkPreviousItemButton:
*
* A #GtkButton for selecting previous queue item.
*/
#include <clapper/clapper.h>
#include "clapper-gtk-previous-item-button.h"
#include "clapper-gtk-utils.h"
#define GST_CAT_DEFAULT clapper_gtk_previous_item_button_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkPreviousItemButton
{
GtkButton parent;
GBinding *n_items_binding;
GBinding *current_index_binding;
};
#define parent_class clapper_gtk_previous_item_button_parent_class
G_DEFINE_TYPE (ClapperGtkPreviousItemButton, clapper_gtk_previous_item_button, GTK_TYPE_BUTTON)
static gboolean
_transform_sensitive_func (GBinding *binding, const GValue *from_value,
GValue *to_value, ClapperGtkPreviousItemButton *self)
{
ClapperQueue *queue = CLAPPER_QUEUE_CAST (g_binding_dup_source (binding));
guint current_index;
gboolean can_skip;
if (G_UNLIKELY (queue == NULL))
return FALSE;
current_index = clapper_queue_get_current_index (queue);
can_skip = (current_index != CLAPPER_QUEUE_INVALID_POSITION
&& current_index > 0);
gst_object_unref (queue);
g_value_set_boolean (to_value, can_skip);
GST_DEBUG_OBJECT (self, "Set sensitive: %s", (can_skip) ? "yes" : "no");
return TRUE;
}
/**
* clapper_gtk_previous_item_button_new:
*
* Creates a new #ClapperGtkPreviousItemButton to play previous #ClapperMediaItem.
*
* Returns: a new previous item button #GtkWidget.
*/
GtkWidget *
clapper_gtk_previous_item_button_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_PREVIOUS_ITEM_BUTTON, NULL);
}
static void
clapper_gtk_previous_item_button_init (ClapperGtkPreviousItemButton *self)
{
gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
gtk_button_set_icon_name (GTK_BUTTON (self), "media-skip-backward-symbolic");
gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "video.previous-item");
}
static void
clapper_gtk_previous_item_button_map (GtkWidget *widget)
{
ClapperGtkPreviousItemButton *self = CLAPPER_GTK_PREVIOUS_ITEM_BUTTON_CAST (widget);
ClapperPlayer *player;
if ((player = clapper_gtk_get_player_from_ancestor (widget))) {
ClapperQueue *queue = clapper_player_get_queue (player);
self->n_items_binding = g_object_bind_property_full (queue, "n-items",
self, "sensitive", G_BINDING_DEFAULT, // Since we sync below, no need to do it twice
(GBindingTransformFunc) _transform_sensitive_func,
NULL, self, NULL);
self->current_index_binding = g_object_bind_property_full (queue, "current-index",
self, "sensitive", G_BINDING_SYNC_CREATE,
(GBindingTransformFunc) _transform_sensitive_func,
NULL, self, NULL);
}
GTK_WIDGET_CLASS (parent_class)->map (widget);
}
static void
clapper_gtk_previous_item_button_unmap (GtkWidget *widget)
{
ClapperGtkPreviousItemButton *self = CLAPPER_GTK_PREVIOUS_ITEM_BUTTON_CAST (widget);
g_clear_pointer (&self->n_items_binding, g_binding_unbind);
g_clear_pointer (&self->current_index_binding, g_binding_unbind);
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
static void
clapper_gtk_previous_item_button_class_init (ClapperGtkPreviousItemButtonClass *klass)
{
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtkpreviousitembutton", 0,
"Clapper GTK Previous Item Button");
widget_class->map = clapper_gtk_previous_item_button_map;
widget_class->unmap = clapper_gtk_previous_item_button_unmap;
}

View File

@@ -0,0 +1,39 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_PREVIOUS_ITEM_BUTTON (clapper_gtk_previous_item_button_get_type())
#define CLAPPER_GTK_PREVIOUS_ITEM_BUTTON_CAST(obj) ((ClapperGtkPreviousItemButton *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkPreviousItemButton, clapper_gtk_previous_item_button, CLAPPER_GTK, PREVIOUS_ITEM_BUTTON, GtkButton)
GtkWidget * clapper_gtk_previous_item_button_new (void);
G_END_DECLS

View File

@@ -0,0 +1,783 @@
/* 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.
*/
/**
* ClapperGtkSeekBar:
*
* A bar for seeking and displaying playback position.
*/
#include "config.h"
#include <clapper/clapper.h>
#include "clapper-gtk-seek-bar.h"
#include "clapper-gtk-container.h"
#include "clapper-gtk-utils.h"
#define DEFAULT_REVEAL_LABELS TRUE
#define DEFAULT_SEEK_METHOD CLAPPER_PLAYER_SEEK_METHOD_NORMAL
#define GST_CAT_DEFAULT clapper_gtk_seek_bar_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkSeekBar
{
GtkWidget parent;
GtkWidget *position_revealer;
GtkWidget *position_label;
GtkWidget *scale;
GtkPopover *popover;
GtkLabel *popover_label;
GtkWidget *duration_revealer;
GtkWidget *duration_label;
gboolean has_hours;
gboolean has_markers;
gboolean dragging;
guint position_uint;
gulong position_signal_id;
gboolean reveal_labels;
ClapperPlayerSeekMethod seek_method;
ClapperPlayer *player;
ClapperMediaItem *current_item;
/* Cache */
gdouble curr_marker_start;
gdouble next_marker_start;
};
static void
clapper_gtk_seek_bar_add_child (GtkBuildable *buildable,
GtkBuilder *builder, GObject *child, const char *type)
{
if (GTK_IS_WIDGET (child)) {
gtk_widget_insert_before (GTK_WIDGET (child), GTK_WIDGET (buildable), NULL);
} else {
GtkBuildableIface *parent_iface = g_type_interface_peek_parent (GTK_BUILDABLE_GET_IFACE (buildable));
parent_iface->add_child (buildable, builder, child, type);
}
}
static void
_buildable_iface_init (GtkBuildableIface *iface)
{
iface->add_child = clapper_gtk_seek_bar_add_child;
}
#define parent_class clapper_gtk_seek_bar_parent_class
G_DEFINE_TYPE_WITH_CODE (ClapperGtkSeekBar, clapper_gtk_seek_bar, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, _buildable_iface_init))
enum
{
PROP_0,
PROP_REVEAL_LABELS,
PROP_SEEK_METHOD,
PROP_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static inline gboolean
_prepare_popover (ClapperGtkSeekBar *self, gdouble x,
gdouble pointing_val, gdouble upper)
{
/* Avoid iterating through markers if within last marker range
* (currently set title label remains the same) */
gboolean found_title = (pointing_val >= self->curr_marker_start
&& pointing_val < self->next_marker_start);
if (!found_title) {
ClapperTimeline *timeline = clapper_media_item_get_timeline (self->current_item);
guint i = clapper_timeline_get_n_markers (timeline);
GST_DEBUG ("Searching for marker at: %lf", pointing_val);
/* We start from the end of scale */
self->next_marker_start = upper;
while (i--) {
ClapperMarker *marker = clapper_timeline_get_marker (timeline, i);
self->curr_marker_start = clapper_marker_get_start (marker);
if (self->curr_marker_start <= pointing_val) {
const gchar *title = clapper_marker_get_title (marker);
GST_DEBUG ("Found marker, range: (%lf-%lf), title: \"%s\"",
self->curr_marker_start, self->next_marker_start,
GST_STR_NULL (title));
/* XXX: It does string comparison internally, so its more efficient
* for us and we do not have to compare strings here too */
gtk_label_set_label (self->popover_label, title);
found_title = (title != NULL);
}
gst_object_unref (marker);
if (found_title)
break;
self->next_marker_start = self->curr_marker_start;
}
}
gtk_popover_set_pointing_to (self->popover,
&(const GdkRectangle){ x, 0, 1, 1 });
return found_title;
}
static inline gboolean
_compute_scale_coords (ClapperGtkSeekBar *self,
gdouble *min_pointing_val, gdouble *max_pointing_val)
{
graphene_rect_t slider_bounds;
if (!gtk_widget_compute_bounds (GTK_WIDGET (self), self->scale, &slider_bounds))
return FALSE;
/* XXX: Number "2" is the correction for range protruding rounded sides
* compared to how marks above/below it are positioned */
*min_pointing_val = -slider_bounds.origin.x + 2;
*max_pointing_val = slider_bounds.size.width + slider_bounds.origin.x - 2;
return TRUE;
}
static void
_player_position_changed_cb (ClapperPlayer *player,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkSeekBar *self)
{
GtkAdjustment *adjustment;
gdouble position;
if (self->dragging)
return;
position = clapper_player_get_position (player);
if (ABS (self->position_uint - position) < 1)
return;
GST_LOG_OBJECT (self, "Position changed: %lf", position);
self->position_uint = (guint) position;
adjustment = gtk_range_get_adjustment (GTK_RANGE (self->scale));
gtk_adjustment_set_value (adjustment, position);
}
static void
_player_state_changed_cb (ClapperPlayer *player,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkSeekBar *self)
{
switch (clapper_player_get_state (player)) {
case CLAPPER_PLAYER_STATE_PAUSED:
/* Force refresh, so scale always reaches end after playback */
self->position_uint = G_MAXUINT;
_player_position_changed_cb (player, NULL, self);
break;
default:
break;
}
}
static void
_player_seek_done_cb (ClapperPlayer *player, ClapperGtkSeekBar *self)
{
GST_DEBUG ("Seek done");
if (self->position_signal_id == 0) {
self->position_signal_id = g_signal_connect (self->player,
"notify::position", G_CALLBACK (_player_position_changed_cb), self);
}
_player_position_changed_cb (player, NULL, self);
}
static void
scale_value_changed_cb (GtkRange *range, ClapperGtkSeekBar *self)
{
gdouble value = gtk_range_get_value (range);
gchar *position_str = g_strdup_printf ("%" CLAPPER_TIME_FORMAT, CLAPPER_TIME_ARGS (value));
gtk_label_set_label (GTK_LABEL (self->position_label),
(self->has_hours) ? position_str : position_str + 3);
g_free (position_str);
if (self->dragging && self->has_markers) {
gdouble min_pointing_val, max_pointing_val;
gdouble x, upper, scaling;
if (!_compute_scale_coords (self, &min_pointing_val, &max_pointing_val)) {
gtk_popover_popdown (self->popover);
return;
}
upper = gtk_adjustment_get_upper (
gtk_range_get_adjustment (GTK_RANGE (self->scale)));
scaling = (upper / (max_pointing_val - min_pointing_val));
x = min_pointing_val + (value / scaling);
if (_prepare_popover (self, x, value, upper))
gtk_popover_popup (self->popover);
else
gtk_popover_popdown (self->popover);
}
}
static void
scale_css_classes_changed_cb (GtkWidget *widget,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkSeekBar *self)
{
const gboolean dragging = gtk_widget_has_css_class (widget, "dragging");
gdouble value;
if (self->dragging == dragging)
return;
if ((self->dragging = dragging)) {
GST_DEBUG_OBJECT (self, "Scale drag started");
return;
}
value = gtk_range_get_value (GTK_RANGE (widget));
GST_DEBUG_OBJECT (self, "Scale dropped at: %lf", value);
if (G_UNLIKELY (self->player == NULL))
return;
if (self->position_signal_id != 0) {
g_signal_handler_disconnect (self->player, self->position_signal_id);
self->position_signal_id = 0;
}
/* We should be ALWAYS doing normal seeks if dropped at marker position */
if (self->has_markers
&& G_APPROX_VALUE (self->curr_marker_start, value, FLT_EPSILON)) {
GST_DEBUG ("Seeking to marker");
clapper_player_seek (self->player, value);
} else {
clapper_player_seek_custom (self->player, value, self->seek_method);
}
}
static void
motion_cb (GtkEventControllerMotion *motion,
gdouble x, gdouble y, ClapperGtkSeekBar *self)
{
gdouble min_pointing_val, max_pointing_val, pointing_val;
gdouble upper, scaling;
/* If no markers, popover should never popup,
* so we do not try to pop it down here */
if (!self->has_markers)
return;
if (!_compute_scale_coords (self, &min_pointing_val, &max_pointing_val)
|| (x < min_pointing_val || x > max_pointing_val)) {
gtk_popover_popdown (self->popover);
return;
}
upper = gtk_adjustment_get_upper (
gtk_range_get_adjustment (GTK_RANGE (self->scale)));
scaling = (upper / (max_pointing_val - min_pointing_val));
pointing_val = (x - min_pointing_val) * scaling;
GST_LOG ("Cursor pointing to: %lf", pointing_val);
if (_prepare_popover (self, x, pointing_val, upper))
gtk_popover_popup (self->popover);
else
gtk_popover_popdown (self->popover);
}
static void
motion_leave_cb (GtkEventControllerMotion *motion, ClapperGtkSeekBar *self)
{
gtk_popover_popdown (self->popover);
}
static void
touch_released_cb (GtkGestureClick *click, gint n_press,
gdouble x, gdouble y, ClapperGtkSeekBar *self)
{
gtk_popover_popdown (self->popover);
}
static void
_update_duration_label (ClapperGtkSeekBar *self, gdouble duration)
{
GtkAdjustment *adjustment = gtk_range_get_adjustment (GTK_RANGE (self->scale));
gchar *duration_str = g_strdup_printf ("%" CLAPPER_TIME_FORMAT, CLAPPER_TIME_ARGS (duration));
gboolean has_hours = (duration >= 3600);
GST_LOG_OBJECT (self, "Duration changed: %lf", duration);
/* Refresh position label when changing text length */
if (has_hours != self->has_hours) {
self->has_hours = has_hours;
scale_value_changed_cb (GTK_RANGE (self->scale), self);
}
gtk_label_set_label (GTK_LABEL (self->duration_label),
(self->has_hours) ? duration_str : duration_str + 3);
g_free (duration_str);
gtk_adjustment_set_upper (adjustment, duration);
}
static void
_update_scale_marks (ClapperGtkSeekBar *self, ClapperTimeline *timeline)
{
GtkAdjustment *adjustment;
guint i, n_markers = clapper_timeline_get_n_markers (timeline);
GST_DEBUG_OBJECT (self, "Placing %u markers on scale", n_markers);
gtk_scale_clear_marks (GTK_SCALE (self->scale));
self->curr_marker_start = -1;
self->next_marker_start = -1;
self->has_markers = FALSE;
if (n_markers == 0) {
gtk_popover_popdown (self->popover);
return;
}
adjustment = gtk_range_get_adjustment (GTK_RANGE (self->scale));
/* Avoid placing marks when duration is zero. Otherwise we may
* end up with a single mark at zero until another refresh. */
if (gtk_adjustment_get_upper (adjustment) <= 0)
return;
for (i = 0; i < n_markers; ++i) {
ClapperMarker *marker = clapper_timeline_get_marker (timeline, i);
gdouble start = clapper_marker_get_start (marker);
gtk_scale_add_mark (GTK_SCALE (self->scale), start, GTK_POS_TOP, NULL);
gtk_scale_add_mark (GTK_SCALE (self->scale), start, GTK_POS_BOTTOM, NULL);
gst_object_unref (marker);
}
self->has_markers = TRUE;
}
static void
_current_item_duration_changed_cb (ClapperMediaItem *current_item,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkSeekBar *self)
{
/* GtkScale ignores markers placed post its adjustment upper range.
* We need to place them again on scale AFTER duration changes. */
_update_duration_label (self, clapper_media_item_get_duration (current_item));
_update_scale_marks (self, clapper_media_item_get_timeline (current_item));
}
static void
_timeline_markers_changed_cb (GListModel *list_model, guint position,
guint removed, guint added, ClapperGtkSeekBar *self)
{
_update_scale_marks (self, CLAPPER_TIMELINE (list_model));
}
static void
_queue_current_item_changed_cb (ClapperQueue *queue,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkSeekBar *self)
{
ClapperMediaItem *current_item = clapper_queue_get_current_item (queue);
/* Disconnect signals from old item */
if (self->current_item) {
ClapperTimeline *timeline = clapper_media_item_get_timeline (self->current_item);
g_signal_handlers_disconnect_by_func (self->current_item,
_current_item_duration_changed_cb, self);
g_signal_handlers_disconnect_by_func (timeline,
_timeline_markers_changed_cb, self);
}
gst_object_replace ((GstObject **) &self->current_item, GST_OBJECT_CAST (current_item));
gst_clear_object (&current_item);
/* Reconnect signals to new item */
if (self->current_item) {
ClapperTimeline *timeline = clapper_media_item_get_timeline (self->current_item);
g_signal_connect (self->current_item, "notify::duration",
G_CALLBACK (_current_item_duration_changed_cb), self);
g_signal_connect (timeline, "items-changed",
G_CALLBACK (_timeline_markers_changed_cb), self);
_update_duration_label (self, clapper_media_item_get_duration (self->current_item));
_update_scale_marks (self, timeline);
} else {
gtk_scale_clear_marks (GTK_SCALE (self->scale));
_update_duration_label (self, 0);
}
}
/**
* clapper_gtk_seek_bar_new:
*
* Creates a new #ClapperGtkSeekBar instance.
*
* Returns: a new seek bar #GtkWidget.
*/
GtkWidget *
clapper_gtk_seek_bar_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_SEEK_BAR, NULL);
}
/**
* clapper_gtk_seek_bar_set_reveal_labels:
* @seek_bar: a #ClapperGtkSeekBar
* @reveal: whether to reveal labels
*
* Set whether the position and duration labels should be revealed.
*/
void
clapper_gtk_seek_bar_set_reveal_labels (ClapperGtkSeekBar *self, gboolean reveal)
{
g_return_if_fail (CLAPPER_GTK_IS_SEEK_BAR (self));
if (self->reveal_labels != reveal) {
self->reveal_labels = reveal;
gtk_revealer_set_reveal_child (GTK_REVEALER (self->position_revealer), reveal);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_REVEAL_LABELS]);
}
}
/**
* clapper_gtk_seek_bar_get_reveal_labels:
* @seek_bar: a #ClapperGtkSeekBar
*
* Get whether the position and duration labels are going to be revealed.
*
* Returns: TRUE if the labels are going to be revealed, %FALSE otherwise.
*/
gboolean
clapper_gtk_seek_bar_get_reveal_labels (ClapperGtkSeekBar *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_SEEK_BAR (self), FALSE);
return self->reveal_labels;
}
/**
* clapper_gtk_seek_bar_set_seek_method:
* @seek_bar: a #ClapperGtkSeekBar
* @method: a #ClapperPlayerSeekMethod
*
* Set [enum@Clapper.PlayerSeekMethod] to use when seeking with seek bar.
*/
void
clapper_gtk_seek_bar_set_seek_method (ClapperGtkSeekBar *self, ClapperPlayerSeekMethod method)
{
g_return_if_fail (CLAPPER_GTK_IS_SEEK_BAR (self));
if (self->seek_method != method) {
self->seek_method = method;
GST_DEBUG_OBJECT (self, "Set seek method to: %i", self->seek_method);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_SEEK_METHOD]);
}
}
/**
* clapper_gtk_seek_bar_get_seek_method:
* @seek_bar: a #ClapperGtkSeekBar
*
* Get [enum@Clapper.PlayerSeekMethod] used when seeking with seek bar.
*
* Returns: #ClapperPlayerSeekMethod used for seeking.
*/
ClapperPlayerSeekMethod
clapper_gtk_seek_bar_get_seek_method (ClapperGtkSeekBar *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_SEEK_BAR (self), DEFAULT_SEEK_METHOD);
return self->seek_method;
}
static void
clapper_gtk_seek_bar_init (ClapperGtkSeekBar *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
self->reveal_labels = DEFAULT_REVEAL_LABELS;
self->seek_method = DEFAULT_SEEK_METHOD;
self->curr_marker_start = -1;
self->next_marker_start = -1;
gtk_revealer_set_reveal_child (GTK_REVEALER (self->position_revealer), self->reveal_labels);
/* Correction for calculated popover position when marks are drawn */
gtk_popover_set_offset (self->popover, 0, -2);
}
static void
clapper_gtk_seek_bar_compute_expand (GtkWidget *widget,
gboolean *hexpand_p, gboolean *vexpand_p)
{
GtkWidget *w;
gboolean hexpand = FALSE;
gboolean vexpand = FALSE;
for (w = gtk_widget_get_first_child (widget); w != NULL; w = gtk_widget_get_next_sibling (w)) {
hexpand = (hexpand || gtk_widget_compute_expand (w, GTK_ORIENTATION_HORIZONTAL));
vexpand = (vexpand || gtk_widget_compute_expand (w, GTK_ORIENTATION_VERTICAL));
}
*hexpand_p = hexpand;
*vexpand_p = vexpand;
}
static void
clapper_gtk_seek_bar_size_allocate (GtkWidget *widget,
gint width, gint height, gint baseline)
{
ClapperGtkSeekBar *self = CLAPPER_GTK_SEEK_BAR_CAST (widget);
gtk_popover_present (self->popover);
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, width, height, baseline);
}
static void
clapper_gtk_seek_bar_realize (GtkWidget *widget)
{
ClapperGtkSeekBar *self = CLAPPER_GTK_SEEK_BAR_CAST (widget);
if ((self->player = clapper_gtk_get_player_from_ancestor (widget))) {
ClapperQueue *queue = clapper_player_get_queue (self->player);
g_signal_connect (queue, "notify::current-item",
G_CALLBACK (_queue_current_item_changed_cb), self);
_queue_current_item_changed_cb (queue, NULL, self);
}
GTK_WIDGET_CLASS (parent_class)->realize (widget);
}
static void
clapper_gtk_seek_bar_unrealize (GtkWidget *widget)
{
ClapperGtkSeekBar *self = CLAPPER_GTK_SEEK_BAR_CAST (widget);
if (self->player) {
ClapperQueue *queue = clapper_player_get_queue (self->player);
if (self->position_signal_id != 0) {
g_signal_handler_disconnect (self->player, self->position_signal_id);
self->position_signal_id = 0;
}
g_signal_handlers_disconnect_by_func (queue, _queue_current_item_changed_cb, self);
self->player = NULL;
}
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}
static void
clapper_gtk_seek_bar_map (GtkWidget *widget)
{
ClapperGtkSeekBar *self = CLAPPER_GTK_SEEK_BAR_CAST (widget);
if (self->player) {
if (self->position_signal_id == 0) {
self->position_signal_id = g_signal_connect (self->player,
"notify::position", G_CALLBACK (_player_position_changed_cb), self);
}
g_signal_connect (self->player, "notify::state",
G_CALLBACK (_player_state_changed_cb), self);
g_signal_connect (self->player, "seek-done",
G_CALLBACK (_player_seek_done_cb), self);
_player_position_changed_cb (self->player, NULL, self);
}
GTK_WIDGET_CLASS (parent_class)->map (widget);
}
static void
clapper_gtk_seek_bar_unmap (GtkWidget *widget)
{
ClapperGtkSeekBar *self = CLAPPER_GTK_SEEK_BAR_CAST (widget);
if (self->player) {
if (self->position_signal_id != 0) {
g_signal_handler_disconnect (self->player, self->position_signal_id);
self->position_signal_id = 0;
}
g_signal_handlers_disconnect_by_func (self->player, _player_state_changed_cb, self);
g_signal_handlers_disconnect_by_func (self->player, _player_seek_done_cb, self);
}
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
static void
_popover_unparent (GtkPopover *popover)
{
gtk_widget_unparent (GTK_WIDGET (popover));
}
static void
clapper_gtk_seek_bar_dispose (GObject *object)
{
ClapperGtkSeekBar *self = CLAPPER_GTK_SEEK_BAR_CAST (object);
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_GTK_TYPE_SEEK_BAR);
g_clear_pointer (&self->position_revealer, gtk_widget_unparent);
g_clear_pointer (&self->scale, gtk_widget_unparent);
g_clear_pointer (&self->popover, _popover_unparent);
g_clear_pointer (&self->duration_revealer, gtk_widget_unparent);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_gtk_seek_bar_finalize (GObject *object)
{
ClapperGtkSeekBar *self = CLAPPER_GTK_SEEK_BAR_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
gst_clear_object (&self->current_item);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_gtk_seek_bar_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperGtkSeekBar *self = CLAPPER_GTK_SEEK_BAR_CAST (object);
switch (prop_id) {
case PROP_REVEAL_LABELS:
g_value_set_boolean (value, clapper_gtk_seek_bar_get_reveal_labels (self));
break;
case PROP_SEEK_METHOD:
g_value_set_enum (value, clapper_gtk_seek_bar_get_seek_method (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_seek_bar_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperGtkSeekBar *self = CLAPPER_GTK_SEEK_BAR_CAST (object);
switch (prop_id) {
case PROP_REVEAL_LABELS:
clapper_gtk_seek_bar_set_reveal_labels (self, g_value_get_boolean (value));
break;
case PROP_SEEK_METHOD:
clapper_gtk_seek_bar_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_seek_bar_class_init (ClapperGtkSeekBarClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtkseekbar", 0,
"Clapper GTK Seek Bar");
gobject_class->get_property = clapper_gtk_seek_bar_get_property;
gobject_class->set_property = clapper_gtk_seek_bar_set_property;
gobject_class->dispose = clapper_gtk_seek_bar_dispose;
gobject_class->finalize = clapper_gtk_seek_bar_finalize;
widget_class->compute_expand = clapper_gtk_seek_bar_compute_expand;
widget_class->size_allocate = clapper_gtk_seek_bar_size_allocate;
widget_class->realize = clapper_gtk_seek_bar_realize;
widget_class->unrealize = clapper_gtk_seek_bar_unrealize;
widget_class->map = clapper_gtk_seek_bar_map;
widget_class->unmap = clapper_gtk_seek_bar_unmap;
/**
* ClapperGtkSeekBar:reveal-labels:
*
* Reveal state of the position and duration labels.
*/
param_specs[PROP_REVEAL_LABELS] = g_param_spec_boolean ("reveal-labels",
NULL, NULL, DEFAULT_REVEAL_LABELS,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkSeekBar: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_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-seek-bar.ui");
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSeekBar, position_revealer);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSeekBar, position_label);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSeekBar, scale);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSeekBar, popover);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSeekBar, popover_label);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSeekBar, duration_revealer);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkSeekBar, duration_label);
gtk_widget_class_bind_template_callback (widget_class, scale_value_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, scale_css_classes_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, motion_cb);
gtk_widget_class_bind_template_callback (widget_class, motion_leave_cb);
gtk_widget_class_bind_template_callback (widget_class, touch_released_cb);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GENERIC);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-seek-bar");
}

View File

@@ -0,0 +1,48 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper/clapper.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_SEEK_BAR (clapper_gtk_seek_bar_get_type())
#define CLAPPER_GTK_SEEK_BAR_CAST(obj) ((ClapperGtkSeekBar *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkSeekBar, clapper_gtk_seek_bar, CLAPPER_GTK, SEEK_BAR, GtkWidget)
GtkWidget * clapper_gtk_seek_bar_new (void);
void clapper_gtk_seek_bar_set_reveal_labels (ClapperGtkSeekBar *seek_bar, gboolean reveal);
gboolean clapper_gtk_seek_bar_get_reveal_labels (ClapperGtkSeekBar *seek_bar);
void clapper_gtk_seek_bar_set_seek_method (ClapperGtkSeekBar *seek_bar, ClapperPlayerSeekMethod method);
ClapperPlayerSeekMethod clapper_gtk_seek_bar_get_seek_method (ClapperGtkSeekBar *seek_bar);
G_END_DECLS

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");
}

View File

@@ -0,0 +1,51 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-container.h>
#include <clapper-gtk/clapper-gtk-extra-menu-button.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_SIMPLE_CONTROLS (clapper_gtk_simple_controls_get_type())
#define CLAPPER_GTK_SIMPLE_CONTROLS_CAST(obj) ((ClapperGtkSimpleControls *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkSimpleControls, clapper_gtk_simple_controls, CLAPPER_GTK, SIMPLE_CONTROLS, ClapperGtkContainer)
GtkWidget * clapper_gtk_simple_controls_new (void);
void clapper_gtk_simple_controls_set_fullscreenable (ClapperGtkSimpleControls *controls, gboolean fullscreenable);
gboolean clapper_gtk_simple_controls_get_fullscreenable (ClapperGtkSimpleControls *controls);
void clapper_gtk_simple_controls_set_seek_method (ClapperGtkSimpleControls *controls, ClapperPlayerSeekMethod method);
ClapperPlayerSeekMethod clapper_gtk_simple_controls_get_seek_method (ClapperGtkSimpleControls *controls);
ClapperGtkExtraMenuButton * clapper_gtk_simple_controls_get_extra_menu_button (ClapperGtkSimpleControls *controls);
G_END_DECLS

View File

@@ -0,0 +1,44 @@
/* 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.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include "clapper-gtk-container.h"
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_STATUS (clapper_gtk_status_get_type())
#define CLAPPER_GTK_STATUS_CAST(obj) ((ClapperGtkStatus *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkStatus, clapper_gtk_status, CLAPPER_GTK, STATUS, ClapperGtkContainer)
G_GNUC_INTERNAL
void clapper_gtk_status_set_error (ClapperGtkStatus *status, const GError *error);
G_GNUC_INTERNAL
void clapper_gtk_status_set_missing_plugin (ClapperGtkStatus *status, const gchar *name);
G_GNUC_INTERNAL
void clapper_gtk_status_clear (ClapperGtkStatus *status);
G_END_DECLS

View File

@@ -0,0 +1,137 @@
/* 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.
*/
#include "config.h"
#include <gst/gst.h>
#include "clapper-gtk-status-private.h"
#define NORMAL_SPACING 16
#define ADAPT_SPACING 8
#define GST_CAT_DEFAULT clapper_gtk_status_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkStatus
{
ClapperGtkContainer parent;
GtkWidget *status_box;
GtkWidget *image;
GtkWidget *title_label;
GtkWidget *description_label;
};
#define parent_class clapper_gtk_status_parent_class
G_DEFINE_TYPE (ClapperGtkStatus, clapper_gtk_status, CLAPPER_GTK_TYPE_CONTAINER)
static void
adapt_cb (ClapperGtkContainer *container, gboolean adapt,
ClapperGtkStatus *self)
{
GST_DEBUG_OBJECT (self, "Adapted: %s", (adapt) ? "yes" : "no");
gtk_box_set_spacing (GTK_BOX (self->status_box), (adapt) ? ADAPT_SPACING : NORMAL_SPACING);
if (adapt) {
gtk_widget_add_css_class (GTK_WIDGET (self), "adapted");
gtk_widget_add_css_class (GTK_WIDGET (self->title_label), "title-2");
} else {
gtk_widget_remove_css_class (GTK_WIDGET (self), "adapted");
gtk_widget_remove_css_class (GTK_WIDGET (self->title_label), "title-2");
}
}
static void
_set_status (ClapperGtkStatus *self, const gchar *icon_name,
const gchar *title, const gchar *description)
{
gtk_image_set_from_icon_name (GTK_IMAGE (self->image), icon_name);
gtk_label_set_label (GTK_LABEL (self->title_label), title);
gtk_label_set_label (GTK_LABEL (self->description_label), description);
gtk_widget_set_visible (GTK_WIDGET (self), TRUE);
}
void
clapper_gtk_status_set_error (ClapperGtkStatus *self, const GError *error)
{
GST_DEBUG_OBJECT (self, "Status set to \"error\"");
_set_status (self, "dialog-warning-symbolic", "Unplayable Content", error->message);
}
void
clapper_gtk_status_set_missing_plugin (ClapperGtkStatus *self, const gchar *name)
{
gchar *description;
GST_DEBUG_OBJECT (self, "Status set to \"missing-plugin\"");
description = g_strdup_printf ("Your GStreamer installation is missing a plugin: %s", name);
_set_status (self, "dialog-information-symbolic", "Missing Plugin", description);
g_free (description);
}
void
clapper_gtk_status_clear (ClapperGtkStatus *self)
{
GST_DEBUG_OBJECT (self, "Status cleared");
gtk_widget_set_visible (GTK_WIDGET (self), FALSE);
}
static void
clapper_gtk_status_init (ClapperGtkStatus *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
gtk_box_set_spacing (GTK_BOX (self->status_box), NORMAL_SPACING);
}
static void
clapper_gtk_status_dispose (GObject *object)
{
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_GTK_TYPE_STATUS);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_gtk_status_class_init (ClapperGtkStatusClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtkstatus", 0,
"Clapper GTK Status");
gobject_class->dispose = clapper_gtk_status_dispose;
gtk_widget_class_set_template_from_resource (widget_class,
CLAPPER_GTK_RESOURCE_PREFIX "/ui/clapper-gtk-status.ui");
gtk_widget_class_bind_template_child (widget_class, ClapperGtkStatus, status_box);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkStatus, image);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkStatus, title_label);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkStatus, description_label);
gtk_widget_class_bind_template_callback (widget_class, adapt_cb);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-status");
}

View File

@@ -0,0 +1,33 @@
/* 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.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_STREAM_CHECK_BUTTON (clapper_gtk_stream_check_button_get_type())
#define CLAPPER_GTK_STREAM_CHECK_BUTTON_CAST(obj) ((ClapperGtkStreamCheckButton *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkStreamCheckButton, clapper_gtk_stream_check_button, CLAPPER_GTK, STREAM_CHECK_BUTTON, GtkCheckButton)
G_END_DECLS

View File

@@ -0,0 +1,284 @@
/* 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.
*/
#include "config.h"
#include <glib/gi18n-lib.h>
#include <clapper/clapper.h>
#include "clapper-gtk-stream-check-button-private.h"
#include "clapper-gtk-utils-private.h"
#define MAX_SIGNALS 4
struct _ClapperGtkStreamCheckButton
{
GtkCheckButton parent;
ClapperStream *stream;
GtkWidget *fallback_check_button;
gulong signal_ids[MAX_SIGNALS];
gboolean grouped;
};
#define parent_class clapper_gtk_stream_check_button_parent_class
G_DEFINE_TYPE (ClapperGtkStreamCheckButton, clapper_gtk_stream_check_button, GTK_TYPE_CHECK_BUTTON)
enum
{
PROP_0,
PROP_STREAM,
PROP_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static inline gchar *
_get_video_stream_label (ClapperVideoStream *vstream)
{
gchar *label, *codec = clapper_video_stream_get_codec (vstream);
gint height = clapper_video_stream_get_height (vstream);
gdouble fps = clapper_video_stream_get_fps (vstream);
if (codec) {
label = g_strdup_printf ("%ip@%.3f [%s]", height, fps, codec);
g_free (codec);
} else {
label = g_strdup_printf ("%ip@%.3f", height, fps);
}
return label;
}
static inline gchar *
_get_audio_stream_label (ClapperAudioStream *astream)
{
gchar *label, *title, *codec = clapper_audio_stream_get_codec (astream);
gint channels = clapper_audio_stream_get_channels (astream);
if (!(title = clapper_stream_get_title (CLAPPER_STREAM_CAST (astream))))
title = clapper_audio_stream_get_lang_name (astream);
if (codec) {
label = g_strdup_printf ("%s [%s, %i %s]",
(title) ? title : _("Undetermined"), codec, channels, _("Channels"));
g_free (title);
g_free (codec);
} else {
label = title;
}
return label;
}
static inline gchar *
_get_subtitle_stream_label (ClapperSubtitleStream *sstream)
{
gchar *title;
if (!(title = clapper_stream_get_title (CLAPPER_STREAM_CAST (sstream))))
title = clapper_subtitle_stream_get_lang_name (sstream);
return title;
}
static void
_refresh_label_cb (ClapperStream *stream,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkStreamCheckButton *self)
{
gchar *label = NULL;
if (stream) {
switch (clapper_stream_get_stream_type (stream)) {
case CLAPPER_STREAM_TYPE_VIDEO:
label = _get_video_stream_label (CLAPPER_VIDEO_STREAM (stream));
break;
case CLAPPER_STREAM_TYPE_AUDIO:
label = _get_audio_stream_label (CLAPPER_AUDIO_STREAM (stream));
break;
case CLAPPER_STREAM_TYPE_SUBTITLE:
label = _get_subtitle_stream_label (CLAPPER_SUBTITLE_STREAM (stream));
break;
default:
break;
}
}
gtk_check_button_set_label (GTK_CHECK_BUTTON (self),
(label != NULL) ? label : _("Undetermined"));
g_free (label);
}
static inline void
_disconnect_current_signals (ClapperGtkStreamCheckButton *self)
{
guint i;
for (i = 0; i < MAX_SIGNALS; ++i) {
/* No more signals connected */
if (self->signal_ids[i] == 0)
break;
g_signal_handler_disconnect (self->stream, self->signal_ids[i]);
self->signal_ids[i] = 0;
}
}
static inline void
clapper_gtk_stream_check_button_set_stream (ClapperGtkStreamCheckButton *self, ClapperStream *stream)
{
guint i = 0;
_disconnect_current_signals (self);
gst_object_replace ((GstObject **) &self->stream, GST_OBJECT_CAST (stream));
if (!self->stream)
return;
switch (clapper_stream_get_stream_type (stream)) {
case CLAPPER_STREAM_TYPE_VIDEO:
self->signal_ids[i++] = g_signal_connect (self->stream, "notify::codec",
G_CALLBACK (_refresh_label_cb), self);
self->signal_ids[i++] = g_signal_connect (self->stream, "notify::height",
G_CALLBACK (_refresh_label_cb), self);
self->signal_ids[i++] = g_signal_connect (self->stream, "notify::fps",
G_CALLBACK (_refresh_label_cb), self);
break;
case CLAPPER_STREAM_TYPE_AUDIO:
self->signal_ids[i++] = g_signal_connect (self->stream, "notify::codec",
G_CALLBACK (_refresh_label_cb), self);
self->signal_ids[i++] = g_signal_connect (self->stream, "notify::channels",
G_CALLBACK (_refresh_label_cb), self);
G_GNUC_FALLTHROUGH;
case CLAPPER_STREAM_TYPE_SUBTITLE:
self->signal_ids[i++] = g_signal_connect (self->stream, "notify::title",
G_CALLBACK (_refresh_label_cb), self);
self->signal_ids[i++] = g_signal_connect (self->stream, "notify::lang-name",
G_CALLBACK (_refresh_label_cb), self);
break;
default:
break;
}
_refresh_label_cb (self->stream, NULL, self);
}
static void
clapper_gtk_stream_check_button_realize (GtkWidget *widget)
{
ClapperGtkStreamCheckButton *self = CLAPPER_GTK_STREAM_CHECK_BUTTON_CAST (widget);
GtkWidget *other_widget;
GST_TRACE_OBJECT (self, "Realize");
/* Set same group as previous check button in the same list view */
if (!self->grouped) {
if ((other_widget = gtk_widget_get_parent (widget))
&& (other_widget = gtk_widget_get_prev_sibling (other_widget))
&& (other_widget = gtk_widget_get_first_child (other_widget))
&& CLAPPER_GTK_IS_STREAM_CHECK_BUTTON (other_widget)) {
gtk_check_button_set_group (GTK_CHECK_BUTTON (self), GTK_CHECK_BUTTON (other_widget));
} else {
if (!self->fallback_check_button)
self->fallback_check_button = g_object_ref_sink (gtk_check_button_new ());
gtk_check_button_set_group (GTK_CHECK_BUTTON (self), GTK_CHECK_BUTTON (self->fallback_check_button));
}
self->grouped = TRUE;
}
GTK_WIDGET_CLASS (parent_class)->realize (widget);
}
static void
clapper_gtk_stream_check_button_toggled (GtkCheckButton *check_button)
{
ClapperGtkStreamCheckButton *self = CLAPPER_GTK_STREAM_CHECK_BUTTON_CAST (check_button);
if (gtk_check_button_get_active (check_button) && self->stream) {
ClapperStreamList *stream_list = CLAPPER_STREAM_LIST (gst_object_get_parent (GST_OBJECT (self->stream)));
if (G_LIKELY (stream_list != NULL)) {
GST_INFO_OBJECT (self, "Toggled: %" GST_PTR_FORMAT, self->stream);
clapper_stream_list_select_stream (stream_list, self->stream);
gst_object_unref (stream_list);
}
}
}
static void
clapper_gtk_stream_check_button_init (ClapperGtkStreamCheckButton *self)
{
}
static void
clapper_gtk_stream_check_button_finalize (GObject *object)
{
ClapperGtkStreamCheckButton *self = CLAPPER_GTK_STREAM_CHECK_BUTTON_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
_disconnect_current_signals (self);
gst_clear_object (&self->stream);
g_clear_object (&self->fallback_check_button);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_gtk_stream_check_button_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperGtkStreamCheckButton *self = CLAPPER_GTK_STREAM_CHECK_BUTTON_CAST (object);
switch (prop_id) {
case PROP_STREAM:
clapper_gtk_stream_check_button_set_stream (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_stream_check_button_class_init (ClapperGtkStreamCheckButtonClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GtkCheckButtonClass *check_button_class = (GtkCheckButtonClass *) klass;
clapper_gtk_init_translations ();
gobject_class->set_property = clapper_gtk_stream_check_button_set_property;
gobject_class->finalize = clapper_gtk_stream_check_button_finalize;
widget_class->realize = clapper_gtk_stream_check_button_realize;
check_button_class->toggled = clapper_gtk_stream_check_button_toggled;
param_specs[PROP_STREAM] = g_param_spec_object ("stream",
NULL, NULL, CLAPPER_TYPE_STREAM,
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
}

View File

@@ -0,0 +1,228 @@
/* 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.
*/
/**
* ClapperGtkTitleHeader:
*
* A header panel widget that displays current media title.
*
* #ClapperGtkTitleHeader is a simple, ready to be used header widget that
* displays current media title. It can be placed as-is as a [class@ClapperGtk.Video]
* overlay (either fading or not).
*
* If you need a further customized header, you can use [class@ClapperGtk.TitleLabel]
* which is used by this widget to build your own implementation instead.
*/
#include "config.h"
#include <gst/gst.h>
#include "clapper-gtk-title-header.h"
#include "clapper-gtk-title-label.h"
#define DEFAULT_FALLBACK_TO_URI FALSE
#define GST_CAT_DEFAULT clapper_gtk_title_header_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkTitleHeader
{
ClapperGtkLeadContainer parent;
ClapperGtkTitleLabel *label;
};
#define parent_class clapper_gtk_title_header_parent_class
G_DEFINE_TYPE (ClapperGtkTitleHeader, clapper_gtk_title_header, CLAPPER_GTK_TYPE_LEAD_CONTAINER)
enum
{
PROP_0,
PROP_CURRENT_TITLE,
PROP_FALLBACK_TO_URI,
PROP_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static void
_label_current_title_changed_cb (ClapperGtkTitleLabel *label G_GNUC_UNUSED,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkTitleHeader *self)
{
/* Forward current title changed notify from internal label */
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_CURRENT_TITLE]);
}
/**
* clapper_gtk_title_header_new:
*
* Creates a new #ClapperGtkTitleHeader instance.
*
* Returns: a new title header #GtkWidget.
*/
GtkWidget *
clapper_gtk_title_header_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_TITLE_HEADER, NULL);
}
/**
* clapper_gtk_title_header_get_current_title:
* @header: a #ClapperGtkTitleHeader
*
* Get currently displayed title by @header.
*
* Returns: (transfer none): text of title label.
*/
const gchar *
clapper_gtk_title_header_get_current_title (ClapperGtkTitleHeader *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_TITLE_HEADER (self), NULL);
return clapper_gtk_title_label_get_current_title (self->label);
}
/**
* clapper_gtk_title_header_set_fallback_to_uri:
* @header: a #ClapperGtkTitleHeader
* @enabled: whether enabled
*
* Set whether a [property@Clapper.MediaItem:uri] property should
* be displayed as a header text when no other title could be determined.
*/
void
clapper_gtk_title_header_set_fallback_to_uri (ClapperGtkTitleHeader *self, gboolean enabled)
{
g_return_if_fail (CLAPPER_GTK_IS_TITLE_HEADER (self));
clapper_gtk_title_label_set_fallback_to_uri (self->label, enabled);
}
/**
* clapper_gtk_title_header_get_fallback_to_uri:
* @header: a #ClapperGtkTitleHeader
*
* Get whether a [property@Clapper.MediaItem:uri] property is going
* be displayed as a header text when no other title could be determined.
*/
gboolean
clapper_gtk_title_header_get_fallback_to_uri (ClapperGtkTitleHeader *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_TITLE_HEADER (self), FALSE);
return clapper_gtk_title_label_get_fallback_to_uri (self->label);
}
static void
clapper_gtk_title_header_init (ClapperGtkTitleHeader *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
clapper_gtk_title_label_set_fallback_to_uri (self->label, DEFAULT_FALLBACK_TO_URI);
g_object_bind_property (self->label, "fallback-to-uri",
self, "fallback-to-uri", G_BINDING_DEFAULT);
g_signal_connect (self->label, "notify::current-title",
G_CALLBACK (_label_current_title_changed_cb), self);
}
static void
clapper_gtk_title_header_dispose (GObject *object)
{
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_GTK_TYPE_TITLE_HEADER);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_gtk_title_header_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperGtkTitleHeader *self = CLAPPER_GTK_TITLE_HEADER_CAST (object);
switch (prop_id) {
case PROP_CURRENT_TITLE:
g_value_set_string (value, clapper_gtk_title_header_get_current_title (self));
break;
case PROP_FALLBACK_TO_URI:
g_value_set_boolean (value, clapper_gtk_title_header_get_fallback_to_uri (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_title_header_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperGtkTitleHeader *self = CLAPPER_GTK_TITLE_HEADER_CAST (object);
switch (prop_id) {
case PROP_FALLBACK_TO_URI:
clapper_gtk_title_header_set_fallback_to_uri (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_title_header_class_init (ClapperGtkTitleHeaderClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtktitleheader", 0,
"Clapper GTK Title Header");
gobject_class->get_property = clapper_gtk_title_header_get_property;
gobject_class->set_property = clapper_gtk_title_header_set_property;
gobject_class->dispose = clapper_gtk_title_header_dispose;
/**
* ClapperGtkTitleHeader:current-title:
*
* Currently displayed title.
*/
param_specs[PROP_CURRENT_TITLE] = g_param_spec_string ("current-title",
NULL, NULL, NULL,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkTitleHeader:fallback-to-uri:
*
* When title cannot be determined, show URI instead.
*/
param_specs[PROP_FALLBACK_TO_URI] = g_param_spec_boolean ("fallback-to-uri",
NULL, NULL, DEFAULT_FALLBACK_TO_URI,
G_PARAM_READWRITE | 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-title-header.ui");
gtk_widget_class_bind_template_child (widget_class, ClapperGtkTitleHeader, label);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-title-header");
}

View File

@@ -0,0 +1,46 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-lead-container.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_TITLE_HEADER (clapper_gtk_title_header_get_type())
#define CLAPPER_GTK_TITLE_HEADER_CAST(obj) ((ClapperGtkTitleHeader *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkTitleHeader, clapper_gtk_title_header, CLAPPER_GTK, TITLE_HEADER, ClapperGtkLeadContainer)
GtkWidget * clapper_gtk_title_header_new (void);
const gchar * clapper_gtk_title_header_get_current_title (ClapperGtkTitleHeader *header);
void clapper_gtk_title_header_set_fallback_to_uri (ClapperGtkTitleHeader *header, gboolean enabled);
gboolean clapper_gtk_title_header_get_fallback_to_uri (ClapperGtkTitleHeader *header);
G_END_DECLS

View File

@@ -0,0 +1,488 @@
/* 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.
*/
/**
* ClapperGtkTitleLabel:
*
* A label showing an up to date title of media item.
*
* By default #ClapperGtkTitleLabel will automatically show title
* of [property@Clapper.Queue:current-item] when placed within
* [class@ClapperGtk.Video] widget hierarchy.
*
* Setting [property@ClapperGtk.TitleLabel:media-item] property will
* make it show title of that particular [class@Clapper.MediaItem]
* instead. Providing an item to read title from also allows using
* this [class@Gtk.Widget] outside of [class@ClapperGtk.Video].
*/
#include "config.h"
#include <glib/gi18n-lib.h>
#include <gst/gst.h>
#include "clapper-gtk-title-label.h"
#include "clapper-gtk-utils-private.h"
#define DEFAULT_FALLBACK_TO_URI FALSE
#define GST_CAT_DEFAULT clapper_gtk_title_label_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkTitleLabel
{
GtkWidget parent;
GtkLabel *label;
ClapperMediaItem *current_item;
ClapperMediaItem *custom_item;
gboolean fallback_to_uri;
ClapperPlayer *player;
};
#define parent_class clapper_gtk_title_label_parent_class
G_DEFINE_TYPE (ClapperGtkTitleLabel, clapper_gtk_title_label, GTK_TYPE_WIDGET)
enum
{
PROP_0,
PROP_MEDIA_ITEM,
PROP_CURRENT_TITLE,
PROP_FALLBACK_TO_URI,
PROP_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static void
_label_changed_cb (GtkLabel *label G_GNUC_UNUSED,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkTitleLabel *self)
{
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_CURRENT_TITLE]);
}
static void
_refresh_title (ClapperGtkTitleLabel *self)
{
ClapperMediaItem *item;
gchar *title;
item = (self->custom_item) ? self->custom_item : self->current_item;
if (!item) {
gtk_label_set_label (self->label, _("No media"));
return;
}
title = clapper_media_item_get_title (item);
if (title) {
gtk_label_set_label (self->label, title);
g_free (title);
} else if (self->fallback_to_uri) {
gtk_label_set_label (self->label, clapper_media_item_get_uri (item));
} else {
gtk_label_set_label (self->label, _("Unknown title"));
}
}
static void
_media_item_title_changed_cb (ClapperMediaItem *item G_GNUC_UNUSED,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkTitleLabel *self)
{
_refresh_title (self);
}
static void
_set_current_item (ClapperGtkTitleLabel *self, ClapperMediaItem *current_item)
{
/* Disconnect signal from old item */
if (self->current_item) {
g_signal_handlers_disconnect_by_func (self->current_item,
_media_item_title_changed_cb, self);
}
gst_object_replace ((GstObject **) &self->current_item, GST_OBJECT_CAST (current_item));
GST_DEBUG ("Current item changed to: %" GST_PTR_FORMAT, self->current_item);
/* Reconnect signal to new item */
if (self->current_item) {
g_signal_connect (self->current_item, "notify::title",
G_CALLBACK (_media_item_title_changed_cb), self);
}
}
static void
_queue_current_item_changed_cb (ClapperQueue *queue,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkTitleLabel *self)
{
ClapperMediaItem *current_item = clapper_queue_get_current_item (queue);
_set_current_item (self, current_item);
_refresh_title (self);
gst_clear_object (&current_item);
}
static void
_bind_current_item (ClapperGtkTitleLabel *self)
{
ClapperQueue *queue = clapper_player_get_queue (self->player);
ClapperMediaItem *current_item;
GST_DEBUG ("Binding current item");
g_signal_connect (queue, "notify::current-item",
G_CALLBACK (_queue_current_item_changed_cb), self);
current_item = clapper_queue_get_current_item (queue);
_set_current_item (self, current_item);
gst_clear_object (&current_item);
}
static void
_unbind_current_item (ClapperGtkTitleLabel *self)
{
ClapperQueue *queue = clapper_player_get_queue (self->player);
GST_DEBUG ("Unbinding current item");
g_signal_handlers_disconnect_by_func (queue,
_queue_current_item_changed_cb, self);
_set_current_item (self, NULL);
}
/**
* clapper_gtk_title_label_new:
*
* Creates a new #ClapperGtkTitleLabel instance.
*
* Returns: a new title label #GtkWidget.
*/
GtkWidget *
clapper_gtk_title_label_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_TITLE_LABEL, NULL);
}
/**
* clapper_gtk_title_label_set_media_item:
* @label: a #ClapperGtkTitleLabel
* @item: (nullable): a #ClapperMediaItem
*
* Set a media item to display title of as label. When set to %NULL,
* @label will use default behavior (showing title of current queue item).
*/
void
clapper_gtk_title_label_set_media_item (ClapperGtkTitleLabel *self, ClapperMediaItem *item)
{
g_return_if_fail (CLAPPER_GTK_IS_TITLE_LABEL (self));
g_return_if_fail (item == NULL || CLAPPER_IS_MEDIA_ITEM (item));
if (self->custom_item == item)
return;
if (self->player) {
_unbind_current_item (self);
self->player = NULL;
}
if (self->custom_item) {
g_signal_handlers_disconnect_by_func (self->custom_item,
_media_item_title_changed_cb, self);
}
gst_object_replace ((GstObject **) &self->custom_item, GST_OBJECT_CAST (item));
GST_DEBUG ("Set media item: %" GST_PTR_FORMAT, self->custom_item);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_MEDIA_ITEM]);
if (self->custom_item) {
g_signal_connect (self->custom_item, "notify::title",
G_CALLBACK (_media_item_title_changed_cb), self);
} else if ((self->player = clapper_gtk_get_player_from_ancestor (GTK_WIDGET (self)))) {
_bind_current_item (self);
}
_refresh_title (self);
}
/**
* clapper_gtk_title_label_get_media_item:
* @label: a #ClapperGtkTitleLabel
*
* Get currently set media item to display title of.
*
* Returns: (transfer none) (nullable): currently set media item.
*/
ClapperMediaItem *
clapper_gtk_title_label_get_media_item (ClapperGtkTitleLabel *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_TITLE_LABEL (self), NULL);
return self->custom_item;
}
/**
* clapper_gtk_title_label_get_current_title:
* @label: a #ClapperGtkTitleLabel
*
* Get currently displayed title by @label.
*
* Returns: (transfer none): text of title label.
*/
const gchar *
clapper_gtk_title_label_get_current_title (ClapperGtkTitleLabel *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_TITLE_LABEL (self), NULL);
return gtk_label_get_label (self->label);
}
/**
* clapper_gtk_title_label_set_fallback_to_uri:
* @label: a #ClapperGtkTitleLabel
* @enabled: whether enabled
*
* Set whether a [property@Clapper.MediaItem:uri] property should
* be displayed as a label text when no other title could be determined.
*/
void
clapper_gtk_title_label_set_fallback_to_uri (ClapperGtkTitleLabel *self, gboolean enabled)
{
g_return_if_fail (CLAPPER_GTK_IS_TITLE_LABEL (self));
if (self->fallback_to_uri != enabled) {
self->fallback_to_uri = enabled;
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_FALLBACK_TO_URI]);
_refresh_title (self);
}
}
/**
* clapper_gtk_title_label_get_fallback_to_uri:
* @label: a #ClapperGtkTitleLabel
*
* Get whether a [property@Clapper.MediaItem:uri] property is going
* be displayed as a label text when no other title could be determined.
*/
gboolean
clapper_gtk_title_label_get_fallback_to_uri (ClapperGtkTitleLabel *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_TITLE_LABEL (self), FALSE);
return self->fallback_to_uri;
}
static void
clapper_gtk_title_label_init (ClapperGtkTitleLabel *self)
{
self->label = GTK_LABEL (gtk_label_new (NULL));
gtk_label_set_single_line_mode (self->label, TRUE);
gtk_label_set_ellipsize (self->label, PANGO_ELLIPSIZE_END);
gtk_widget_set_can_target (GTK_WIDGET (self->label), FALSE);
gtk_widget_set_parent (GTK_WIDGET (self->label), GTK_WIDGET (self));
self->fallback_to_uri = DEFAULT_FALLBACK_TO_URI;
/* Apply CSS styles to internal label */
g_object_bind_property (self, "css-classes",
self->label, "css-classes", G_BINDING_DEFAULT);
}
static void
clapper_gtk_title_label_constructed (GObject *object)
{
ClapperGtkTitleLabel *self = CLAPPER_GTK_TITLE_LABEL_CAST (object);
/* Ensure label if no custom item set yet */
if (!self->custom_item)
_refresh_title (self);
/* This avoids us from comparing label changes as GTK will do this
* for us and emit this signal only when label text actually changes. */
g_signal_connect (self->label, "notify::label",
G_CALLBACK (_label_changed_cb), self);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
clapper_gtk_title_label_compute_expand (GtkWidget *widget,
gboolean *hexpand_p, gboolean *vexpand_p)
{
ClapperGtkTitleLabel *self = CLAPPER_GTK_TITLE_LABEL_CAST (widget);
*hexpand_p = gtk_widget_compute_expand ((GtkWidget *) self->label, GTK_ORIENTATION_HORIZONTAL);
*vexpand_p = gtk_widget_compute_expand ((GtkWidget *) self->label, GTK_ORIENTATION_VERTICAL);
}
static void
clapper_gtk_title_label_root (GtkWidget *widget)
{
ClapperGtkTitleLabel *self = CLAPPER_GTK_TITLE_LABEL_CAST (widget);
GTK_WIDGET_CLASS (parent_class)->root (widget);
if (!self->custom_item
&& (self->player = clapper_gtk_get_player_from_ancestor (widget))) {
GST_LOG ("Label placed without media item set");
_bind_current_item (self);
_refresh_title (self);
}
}
static void
clapper_gtk_title_label_unroot (GtkWidget *widget)
{
ClapperGtkTitleLabel *self = CLAPPER_GTK_TITLE_LABEL_CAST (widget);
if (self->player) {
_unbind_current_item (self);
self->player = NULL;
}
GTK_WIDGET_CLASS (parent_class)->unroot (widget);
}
static void
_label_unparent (GtkLabel *label)
{
gtk_widget_unparent (GTK_WIDGET (label));
}
static void
clapper_gtk_title_label_dispose (GObject *object)
{
ClapperGtkTitleLabel *self = CLAPPER_GTK_TITLE_LABEL_CAST (object);
if (self->custom_item) {
g_signal_handlers_disconnect_by_func (self->custom_item,
_media_item_title_changed_cb, self);
}
if (self->label) {
g_signal_handlers_disconnect_by_func (self->label,
_label_changed_cb, self);
}
gst_clear_object (&self->current_item);
gst_clear_object (&self->custom_item);
g_clear_pointer (&self->label, _label_unparent);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_gtk_title_label_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperGtkTitleLabel *self = CLAPPER_GTK_TITLE_LABEL_CAST (object);
switch (prop_id) {
case PROP_MEDIA_ITEM:
g_value_set_object (value, clapper_gtk_title_label_get_media_item (self));
break;
case PROP_CURRENT_TITLE:
g_value_set_string (value, clapper_gtk_title_label_get_current_title (self));
break;
case PROP_FALLBACK_TO_URI:
g_value_set_boolean (value, clapper_gtk_title_label_get_fallback_to_uri (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_title_label_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperGtkTitleLabel *self = CLAPPER_GTK_TITLE_LABEL_CAST (object);
switch (prop_id) {
case PROP_MEDIA_ITEM:
clapper_gtk_title_label_set_media_item (self, g_value_get_object (value));
break;
case PROP_FALLBACK_TO_URI:
clapper_gtk_title_label_set_fallback_to_uri (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_title_label_class_init (ClapperGtkTitleLabelClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtktitlelabel", 0,
"Clapper GTK Title Label");
clapper_gtk_init_translations ();
gobject_class->constructed = clapper_gtk_title_label_constructed;
gobject_class->get_property = clapper_gtk_title_label_get_property;
gobject_class->set_property = clapper_gtk_title_label_set_property;
gobject_class->dispose = clapper_gtk_title_label_dispose;
widget_class->compute_expand = clapper_gtk_title_label_compute_expand;
/* Using root/unroot so label "current-title" is immediately
* updated and can be accessed before label was made visible */
widget_class->root = clapper_gtk_title_label_root;
widget_class->unroot = clapper_gtk_title_label_unroot;
/**
* ClapperGtkTitleLabel:media-item:
*
* Currently set media item to display title of.
*/
param_specs[PROP_MEDIA_ITEM] = g_param_spec_object ("media-item",
NULL, NULL, CLAPPER_TYPE_MEDIA_ITEM,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkTitleLabel:current-title:
*
* Currently displayed title.
*/
param_specs[PROP_CURRENT_TITLE] = g_param_spec_string ("current-title",
NULL, NULL, NULL,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkTitleLabel:fallback-to-uri:
*
* When title cannot be determined, show URI instead.
*/
param_specs[PROP_FALLBACK_TO_URI] = g_param_spec_boolean ("fallback-to-uri",
NULL, NULL, DEFAULT_FALLBACK_TO_URI,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GENERIC);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-title-label");
}

View File

@@ -0,0 +1,50 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper/clapper.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_TITLE_LABEL (clapper_gtk_title_label_get_type())
#define CLAPPER_GTK_TITLE_LABEL_CAST(obj) ((ClapperGtkTitleLabel *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkTitleLabel, clapper_gtk_title_label, CLAPPER_GTK, TITLE_LABEL, GtkWidget)
GtkWidget * clapper_gtk_title_label_new (void);
void clapper_gtk_title_label_set_media_item (ClapperGtkTitleLabel *label, ClapperMediaItem *item);
ClapperMediaItem * clapper_gtk_title_label_get_media_item (ClapperGtkTitleLabel *label);
const gchar * clapper_gtk_title_label_get_current_title (ClapperGtkTitleLabel *label);
void clapper_gtk_title_label_set_fallback_to_uri (ClapperGtkTitleLabel *label, gboolean enabled);
gboolean clapper_gtk_title_label_get_fallback_to_uri (ClapperGtkTitleLabel *label);
G_END_DECLS

View File

@@ -0,0 +1,154 @@
/* 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.
*/
/**
* ClapperGtkToggleFullscreenButton:
*
* A #GtkButton for toggling fullscreen state.
*/
#include <gst/gst.h>
#include "clapper-gtk-toggle-fullscreen-button.h"
#include "clapper-gtk-video.h"
#define ENTER_FULLSCREEN_ICON_NAME "view-fullscreen-symbolic"
#define LEAVE_FULLSCREEN_ICON_NAME "view-restore-symbolic"
#define GST_CAT_DEFAULT clapper_gtk_toggle_fullscreen_button_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkToggleFullscreenButton
{
GtkButton parent;
gboolean is_fullscreen;
};
#define parent_class clapper_gtk_toggle_fullscreen_button_parent_class
G_DEFINE_TYPE (ClapperGtkToggleFullscreenButton, clapper_gtk_toggle_fullscreen_button, GTK_TYPE_BUTTON)
static void
_toplevel_state_changed_cb (GdkToplevel *toplevel,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkToggleFullscreenButton *self)
{
GdkToplevelState state = gdk_toplevel_get_state (toplevel);
gboolean is_fullscreen = (state & GDK_TOPLEVEL_STATE_FULLSCREEN);
if (self->is_fullscreen == is_fullscreen)
return;
self->is_fullscreen = is_fullscreen;
GST_DEBUG_OBJECT (self, "Toplevel state changed, fullscreen: %s",
(self->is_fullscreen) ? "yes" : "no");
gtk_button_set_icon_name (GTK_BUTTON (self),
(!self->is_fullscreen) ? ENTER_FULLSCREEN_ICON_NAME : LEAVE_FULLSCREEN_ICON_NAME);
}
/**
* clapper_gtk_toggle_fullscreen_button_new:
*
* Creates a new #ClapperGtkToggleFullscreenButton instance.
*
* Returns: a new toggle fullscreen button #GtkWidget.
*/
GtkWidget *
clapper_gtk_toggle_fullscreen_button_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_TOGGLE_FULLSCREEN_BUTTON, NULL);
}
static void
clapper_gtk_toggle_fullscreen_button_init (ClapperGtkToggleFullscreenButton *self)
{
gtk_button_set_icon_name (GTK_BUTTON (self), ENTER_FULLSCREEN_ICON_NAME);
}
static void
clapper_gtk_toggle_fullscreen_button_map (GtkWidget *widget)
{
ClapperGtkToggleFullscreenButton *self = CLAPPER_GTK_TOGGLE_FULLSCREEN_BUTTON_CAST (widget);
GtkRoot *root;
GdkSurface *surface;
GST_TRACE_OBJECT (self, "Map");
root = gtk_widget_get_root (widget);
surface = gtk_native_get_surface (GTK_NATIVE (root));
if (G_LIKELY (GDK_IS_TOPLEVEL (surface))) {
GdkToplevel *toplevel = GDK_TOPLEVEL (surface);
g_signal_connect (toplevel, "notify::state",
G_CALLBACK (_toplevel_state_changed_cb), self);
_toplevel_state_changed_cb (toplevel, NULL, self);
}
GTK_WIDGET_CLASS (parent_class)->map (widget);
}
static void
clapper_gtk_toggle_fullscreen_button_unmap (GtkWidget *widget)
{
ClapperGtkToggleFullscreenButton *self = CLAPPER_GTK_TOGGLE_FULLSCREEN_BUTTON_CAST (widget);
GtkRoot *root;
GdkSurface *surface;
GST_TRACE_OBJECT (self, "Unmap");
root = gtk_widget_get_root (widget);
surface = gtk_native_get_surface (GTK_NATIVE (root));
if (G_LIKELY (GDK_IS_TOPLEVEL (surface))) {
GdkToplevel *toplevel = GDK_TOPLEVEL (surface);
g_signal_handlers_disconnect_by_func (toplevel,
_toplevel_state_changed_cb, self);
}
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
static void
clapper_gtk_toggle_fullscreen_button_clicked (GtkButton* button)
{
GtkWidget *video;
GST_DEBUG_OBJECT (button, "Clicked");
if ((video = gtk_widget_get_ancestor (GTK_WIDGET (button), CLAPPER_GTK_TYPE_VIDEO)))
g_signal_emit_by_name (video, "toggle-fullscreen");
}
static void
clapper_gtk_toggle_fullscreen_button_class_init (ClapperGtkToggleFullscreenButtonClass *klass)
{
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GtkButtonClass *button_class = (GtkButtonClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtktogglefullscreenbutton", 0,
"Clapper GTK Toggle Fullscreen Button");
widget_class->map = clapper_gtk_toggle_fullscreen_button_map;
widget_class->unmap = clapper_gtk_toggle_fullscreen_button_unmap;
button_class->clicked = clapper_gtk_toggle_fullscreen_button_clicked;
}

View File

@@ -0,0 +1,39 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_TOGGLE_FULLSCREEN_BUTTON (clapper_gtk_toggle_fullscreen_button_get_type())
#define CLAPPER_GTK_TOGGLE_FULLSCREEN_BUTTON_CAST(obj) ((ClapperGtkToggleFullscreenButton *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkToggleFullscreenButton, clapper_gtk_toggle_fullscreen_button, CLAPPER_GTK, TOGGLE_FULLSCREEN_BUTTON, GtkButton)
GtkWidget * clapper_gtk_toggle_fullscreen_button_new (void);
G_END_DECLS

View File

@@ -0,0 +1,126 @@
/* 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.
*/
/**
* ClapperGtkTogglePlayButton:
*
* A #GtkButton for toggling play/pause of playback.
*/
#include <clapper/clapper.h>
#include "clapper-gtk-toggle-play-button.h"
#include "clapper-gtk-utils.h"
#define PLAY_ICON_NAME "media-playback-start-symbolic"
#define PAUSE_ICON_NAME "media-playback-pause-symbolic"
#define GST_CAT_DEFAULT clapper_gtk_toggle_play_button_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkTogglePlayButton
{
GtkButton parent;
GBinding *state_binding;
};
#define parent_class clapper_gtk_toggle_play_button_parent_class
G_DEFINE_TYPE (ClapperGtkTogglePlayButton, clapper_gtk_toggle_play_button, GTK_TYPE_BUTTON)
static gboolean
_transform_state_func (GBinding *binding, const GValue *from_value,
GValue *to_value, ClapperGtkTogglePlayButton *self)
{
ClapperPlayerState state = g_value_get_enum (from_value);
GST_DEBUG_OBJECT (self, "Reflecting player state change, now: %i", state);
switch (state) {
case CLAPPER_PLAYER_STATE_STOPPED:
case CLAPPER_PLAYER_STATE_PAUSED:
g_value_set_string (to_value, PLAY_ICON_NAME);
break;
case CLAPPER_PLAYER_STATE_PLAYING:
g_value_set_string (to_value, PAUSE_ICON_NAME);
break;
default:
return FALSE; // no change
}
return TRUE;
}
/**
* clapper_gtk_toggle_play_button_new:
*
* Creates a new #ClapperGtkTogglePlayButton instance.
*
* Returns: a new toggle play button #GtkWidget.
*/
GtkWidget *
clapper_gtk_toggle_play_button_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_TOGGLE_PLAY_BUTTON, NULL);
}
static void
clapper_gtk_toggle_play_button_init (ClapperGtkTogglePlayButton *self)
{
gtk_button_set_icon_name (GTK_BUTTON (self), PLAY_ICON_NAME);
gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "video.toggle-play");
}
static void
clapper_gtk_toggle_play_button_map (GtkWidget *widget)
{
ClapperGtkTogglePlayButton *self = CLAPPER_GTK_TOGGLE_PLAY_BUTTON_CAST (widget);
ClapperPlayer *player;
if ((player = clapper_gtk_get_player_from_ancestor (widget))) {
self->state_binding = g_object_bind_property_full (player, "state",
self, "icon-name", G_BINDING_SYNC_CREATE,
(GBindingTransformFunc) _transform_state_func,
NULL, self, NULL);
}
GTK_WIDGET_CLASS (parent_class)->map (widget);
}
static void
clapper_gtk_toggle_play_button_unmap (GtkWidget *widget)
{
ClapperGtkTogglePlayButton *self = CLAPPER_GTK_TOGGLE_PLAY_BUTTON_CAST (widget);
g_clear_pointer (&self->state_binding, g_binding_unbind);
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
static void
clapper_gtk_toggle_play_button_class_init (ClapperGtkTogglePlayButtonClass *klass)
{
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtktoggleplaybutton", 0,
"Clapper GTK Toggle Play Button");
widget_class->map = clapper_gtk_toggle_play_button_map;
widget_class->unmap = clapper_gtk_toggle_play_button_unmap;
}

View File

@@ -0,0 +1,39 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_TOGGLE_PLAY_BUTTON (clapper_gtk_toggle_play_button_get_type())
#define CLAPPER_GTK_TOGGLE_PLAY_BUTTON_CAST(obj) ((ClapperGtkTogglePlayButton *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkTogglePlayButton, clapper_gtk_toggle_play_button, CLAPPER_GTK, TOGGLE_PLAY_BUTTON, GtkButton)
GtkWidget * clapper_gtk_toggle_play_button_new (void);
G_END_DECLS

View File

@@ -0,0 +1,35 @@
/* 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.
*/
#pragma once
#include "clapper-gtk-utils.h"
G_BEGIN_DECLS
G_GNUC_INTERNAL
void clapper_gtk_init_translations (void);
G_GNUC_INTERNAL
const gchar * clapper_gtk_get_icon_name_for_volume (gfloat volume);
G_GNUC_INTERNAL
const gchar * clapper_gtk_get_icon_name_for_speed (gfloat speed);
G_END_DECLS

View File

@@ -0,0 +1,96 @@
/* 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.
*/
#include "config.h"
#include <glib/gi18n-lib.h>
#include "clapper-gtk-utils-private.h"
#include "clapper-gtk-video.h"
static gboolean initialized = FALSE;
/**
* clapper_gtk_get_player_from_ancestor:
* @widget: a #GtkWidget
*
* Get [class@Clapper.Player] used by [class@ClapperGtk.Video] ancestor of @widget.
*
* This utility is a convenience wrapper for calling [method@Gtk.Widget.get_ancestor]
* of type `CLAPPER_GTK_TYPE_VIDEO` and [method@ClapperGtk.Video.get_player] with
* additional %NULL checking and type casting.
*
* This is meant to be used mainly for custom widget development as an easy access to the
* underlying parent [class@Clapper.Player] object. If you want to get the player from
* [class@ClapperGtk.Video] widget itself, use [method@ClapperGtk.Video.get_player] instead.
*
* Rememeber that this function will return %NULL when widget does not have
* a [class@ClapperGtk.Video] ancestor in widget hierarchy (widget is not yet placed).
*
* Returns: (transfer none) (nullable): a #ClapperPlayer from ancestor of a @widget.
*/
ClapperPlayer *
clapper_gtk_get_player_from_ancestor (GtkWidget *widget)
{
GtkWidget *parent;
ClapperPlayer *player = NULL;
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
if ((parent = gtk_widget_get_ancestor (widget, CLAPPER_GTK_TYPE_VIDEO)))
player = clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (parent));
return player;
}
void
clapper_gtk_init_translations (void)
{
if (initialized)
return;
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
initialized = TRUE;
}
const gchar *
clapper_gtk_get_icon_name_for_volume (gfloat volume)
{
return (volume <= 0.0f)
? "audio-volume-muted-symbolic"
: (volume <= 0.3f)
? "audio-volume-low-symbolic"
: (volume <= 0.7f)
? "audio-volume-medium-symbolic"
: (volume <= 1.0f)
? "audio-volume-high-symbolic"
: "audio-volume-overamplified-symbolic";
}
const gchar *
clapper_gtk_get_icon_name_for_speed (gfloat speed)
{
return (speed < 1.0f)
? "power-profile-power-saver-symbolic"
: (speed == 1.0f)
? "power-profile-balanced-symbolic"
: "power-profile-performance-symbolic";
}

View File

@@ -0,0 +1,34 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <gtk/gtk.h>
#include <clapper/clapper.h>
G_BEGIN_DECLS
ClapperPlayer * clapper_gtk_get_player_from_ancestor (GtkWidget *widget);
G_END_DECLS

View File

@@ -0,0 +1,76 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
/**
* CLAPPER_GTK_MAJOR_VERSION:
*
* ClapperGtk major version component
*/
#define CLAPPER_GTK_MAJOR_VERSION (@CLAPPER_GTK_MAJOR_VERSION@)
/**
* CLAPPER_GTK_MINOR_VERSION:
*
* ClapperGtk minor version component
*/
#define CLAPPER_GTK_MINOR_VERSION (@CLAPPER_GTK_MINOR_VERSION@)
/**
* CLAPPER_GTK_MICRO_VERSION:
*
* ClapperGtk micro version component
*/
#define CLAPPER_GTK_MICRO_VERSION (@CLAPPER_GTK_MICRO_VERSION@)
/**
* CLAPPER_GTK_VERSION:
*
* ClapperGtk version
*/
#define CLAPPER_GTK_VERSION (@CLAPPER_GTK_VERSION@)
/**
* CLAPPER_GTK_VERSION_S:
*
* ClapperGtk version, encoded as a string
*/
#define CLAPPER_GTK_VERSION_S "@CLAPPER_GTK_VERSION@"
#define CLAPPER_GTK_ENCODE_VERSION(major,minor,micro) \
((major) << 24 | (minor) << 16 | (micro) << 8)
/**
* CLAPPER_GTK_VERSION_HEX:
*
* ClapperGtk version, encoded as an hexadecimal number, useful for integer comparisons.
*/
#define CLAPPER_GTK_VERSION_HEX \
(CLAPPER_GTK_ENCODE_VERSION (CLAPPER_GTK_MAJOR_VERSION, CLAPPER_GTK_MINOR_VERSION, CLAPPER_GTK_MICRO_VERSION))
#define CLAPPER_GTK_CHECK_VERSION(major, minor, micro) \
(CLAPPER_GTK_MAJOR_VERSION > (major) || \
(CLAPPER_GTK_MAJOR_VERSION == (major) && CLAPPER_GTK_MINOR_VERSION > (minor)) || \
(CLAPPER_GTK_MAJOR_VERSION == (major) && CLAPPER_GTK_MINOR_VERSION == (minor) && \
CLAPPER_GTK_MICRO_VERSION >= (micro)))

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
/* 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.
*/
#pragma once
#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION)
#error "Only <clapper-gtk/clapper-gtk.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper/clapper.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_VIDEO (clapper_gtk_video_get_type())
#define CLAPPER_GTK_VIDEO_CAST(obj) ((ClapperGtkVideo *)(obj))
G_DECLARE_FINAL_TYPE (ClapperGtkVideo, clapper_gtk_video, CLAPPER_GTK, VIDEO, GtkWidget)
GtkWidget * clapper_gtk_video_new (void);
void clapper_gtk_video_add_overlay (ClapperGtkVideo *video, GtkWidget *widget);
void clapper_gtk_video_add_fading_overlay (ClapperGtkVideo *video, GtkWidget *widget);
ClapperPlayer * clapper_gtk_video_get_player (ClapperGtkVideo *video);
void clapper_gtk_video_set_fade_delay (ClapperGtkVideo *video, guint delay);
guint clapper_gtk_video_get_fade_delay (ClapperGtkVideo *video);
void clapper_gtk_video_set_touch_fade_delay (ClapperGtkVideo *video, guint delay);
guint clapper_gtk_video_get_touch_fade_delay (ClapperGtkVideo *video);
void clapper_gtk_video_set_auto_inhibit (ClapperGtkVideo *video, gboolean inhibit);
gboolean clapper_gtk_video_get_auto_inhibit (ClapperGtkVideo *video);
gboolean clapper_gtk_video_get_inhibited (ClapperGtkVideo *video);
G_END_DECLS

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/com/github/rafostar/Clapper/clapper-gtk">
<file preprocess="xml-stripblanks">ui/clapper-gtk-billboard.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-gtk-extra-menu-button.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-gtk-seek-bar.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-gtk-simple-controls.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-gtk-status.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-gtk-stream-list-item.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-gtk-title-header.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-gtk-video.ui</file>
<file compressed="true">css/styles.css</file>
</gresource>
</gresources>

View File

@@ -0,0 +1,42 @@
/* 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.
*/
#pragma once
#define __CLAPPER_GTK_INSIDE__
#include <clapper-gtk/clapper-gtk-enums.h>
#include <clapper-gtk/clapper-gtk-version.h>
#include <clapper-gtk/clapper-gtk-billboard.h>
#include <clapper-gtk/clapper-gtk-container.h>
#include <clapper-gtk/clapper-gtk-extra-menu-button.h>
#include <clapper-gtk/clapper-gtk-lead-container.h>
#include <clapper-gtk/clapper-gtk-next-item-button.h>
#include <clapper-gtk/clapper-gtk-previous-item-button.h>
#include <clapper-gtk/clapper-gtk-seek-bar.h>
#include <clapper-gtk/clapper-gtk-simple-controls.h>
#include <clapper-gtk/clapper-gtk-title-header.h>
#include <clapper-gtk/clapper-gtk-title-label.h>
#include <clapper-gtk/clapper-gtk-toggle-fullscreen-button.h>
#include <clapper-gtk/clapper-gtk-toggle-play-button.h>
#include <clapper-gtk/clapper-gtk-utils.h>
#include <clapper-gtk/clapper-gtk-video.h>
#undef __CLAPPER_GTK_INSIDE__

View File

@@ -0,0 +1,153 @@
/* Adwaita OSD background color is unacceptable:
* https://gitlab.gnome.org/GNOME/libadwaita/-/issues/454 */
clapper-gtk-video menubutton.osd,
clapper-gtk-video button.osd,
clapper-gtk-video box.osd,
clapper-gtk-video clapper-gtk-title-header.osd,
clapper-gtk-video .osd popover contents,
clapper-gtk-video .osd popover arrow {
background-color: rgba(38,38,38,0.8);
}
clapper-gtk-video button.osd:hover,
clapper-gtk-video button.osd:checked {
background-color: rgba(63,63,63,0.8);
}
clapper-gtk-video button.osd:active {
background-color: rgba(82,82,82,0.8);
}
clapper-gtk-video box.osd listview.osd {
background-color: transparent;
}
clapper-gtk-status box {
padding-left: 6px;
padding-right: 6px;
}
clapper-gtk-status box image {
-gtk-icon-size: 128px;
-gtk-icon-filter: opacity(0.6);
}
clapper-gtk-status.adapted box image {
-gtk-icon-size: 96px;
}
clapper-gtk-billboard .sidebox {
padding: 12px;
border-radius: 9999px;
}
clapper-gtk-billboard .sidebox:dir(ltr) {
margin-left: 6px;
}
clapper-gtk-billboard .sidebox:dir(rtl) {
margin-right: 6px;
}
clapper-gtk-billboard .sidebox .progressbox {
margin-bottom: 6px;
}
clapper-gtk-billboard .sidebox .progressbox progressbar {
min-height: 90px;
}
clapper-gtk-billboard .sidebox .progressbox.overamp progress {
background: #c01c28;
}
clapper-gtk-billboard .sidebox .progresslabel:dir(ltr) {
margin-left: 6px;
}
clapper-gtk-billboard .sidebox .progresslabel:dir(rtl) {
margin-right: 6px;
}
clapper-gtk-billboard .messagebox {
margin: 6px;
padding: 12px;
min-width: 92px;
}
clapper-gtk-billboard .messagebox image {
-gtk-icon-size: 48px;
}
menubutton.circular,
dropdown.circular button.toggle {
border-radius: 9999px;
}
.rounded {
border-radius: 19px;
}
clapper-gtk-title-header {
margin: 6px;
min-height: 38px;
}
clapper-gtk-title-header label {
margin-left: 12px;
margin-right: 12px;
}
clapper-gtk-simple-controls .centerbox {
margin-bottom: 2px;
}
clapper-gtk-simple-controls .mainbox {
margin-left: 6px;
margin-right: 6px;
margin-bottom: 4px;
}
clapper-gtk-simple-controls .mainbox button {
margin: 2px;
}
clapper-gtk-simple-controls .mainbox scale {
margin-top: 2px;
margin-bottom: 2px;
}
clapper-gtk-simple-controls .mainbox popover button {
margin: 0px;
}
clapper-gtk-simple-controls .fullscreenbutton {
margin-bottom: 6px;
}
clapper-gtk-simple-controls .fullscreenbutton:dir(ltr) {
margin-right: 6px;
}
clapper-gtk-simple-controls .fullscreenbutton:dir(rtl) {
margin-left: 6px;
}
clapper-gtk-seek-bar scale trough highlight {
min-height: 6px;
}
clapper-gtk-seek-bar label {
margin-left: 2px;
margin-right: 2px;
}
clapper-gtk-extra-menu-button popover .spinsidebutton {
min-width: 28px;
min-height: 28px;
}
clapper-gtk-extra-menu-button popover .spinsidebutton:dir(ltr) {
margin-right: 2px;
}
clapper-gtk-extra-menu-button popover .spinsidebutton:dir(rtl) {
margin-left: 2px;
}
clapper-gtk-extra-menu-button popover spinbutton {
border-radius: 9999px;
}
clapper-gtk-extra-menu-button popover spinbutton button.up:dir(ltr) {
border-radius: 0px 9999px 9999px 0px;
}
clapper-gtk-extra-menu-button popover spinbutton button.up:dir(rtl) {
border-radius: 9999px 0px 0px 9999px;
}
clapper-gtk-extra-menu-button popover spinbutton button.up:dir(ltr) {
padding-right: 6px;
}
clapper-gtk-extra-menu-button popover spinbutton button.up:dir(rtl) {
padding-left: 6px;
}
clapper-gtk-menu-scrolled-window listview row {
padding-left: 6px;
padding-right: 6px;
}
clapper-gtk-menu-scrolled-window listview row checkbutton label {
margin-left: 2px;
margin-right: 2px;
}

View File

@@ -0,0 +1,221 @@
clappergtk_dep = dependency('', required: false)
clappergtk_option = get_option('clapper-gtk')
clappergtk_api_name = meson.project_name() + '-gtk' + clapper_version_suffix
clappergtk_resource_prefix = '/com/github/rafostar/Clapper/clapper-gtk'
build_clappergtk = false
if clappergtk_option.disabled()
subdir_done()
endif
clappergtk_deps = [
clapper_dep,
gst_dep,
gst_clapper_sink_dep,
gtk4_dep,
glib_dep,
gobject_dep,
libm,
]
foreach dep : clappergtk_deps
if not dep.found()
if clappergtk_option.enabled()
error('clapper-gtk option was enabled, but required dependencies were not found')
endif
subdir_done()
endif
endforeach
subdir('po')
config_h = configuration_data()
config_h.set_quoted('GETTEXT_PACKAGE', meson.project_name() + '-app')
config_h.set_quoted('LOCALEDIR', join_paths (prefix, localedir))
config_h.set_quoted('CLAPPER_GTK_RESOURCE_PREFIX', clappergtk_resource_prefix)
configure_file(
output: 'config.h',
configuration: config_h,
)
version_conf = configuration_data()
version_conf.set(
'CLAPPER_GTK_VERSION',
meson.project_version(),
)
version_conf.set(
'CLAPPER_GTK_MAJOR_VERSION',
version_array[0].to_int(),
)
version_conf.set(
'CLAPPER_GTK_MINOR_VERSION',
version_array[1].to_int(),
)
version_conf.set(
'CLAPPER_GTK_MICRO_VERSION',
version_array[2].to_int(),
)
clappergtk_version_header = configure_file(
input: 'clapper-gtk-version.h.in',
output: 'clapper-gtk-version.h',
configuration: version_conf,
)
clappergtk_resources = gnome.compile_resources(
'clapper-gtk-resources',
'clapper-gtk.gresources.xml',
c_name: 'clapper_gtk',
)
# Include the generated headers
clappergtk_conf_inc = [
include_directories('.'),
include_directories('..'),
]
clappergtk_headers = [
'clapper-gtk.h',
'clapper-gtk-enums.h',
'clapper-gtk-billboard.h',
'clapper-gtk-container.h',
'clapper-gtk-extra-menu-button.h',
'clapper-gtk-lead-container.h',
'clapper-gtk-next-item-button.h',
'clapper-gtk-previous-item-button.h',
'clapper-gtk-seek-bar.h',
'clapper-gtk-simple-controls.h',
'clapper-gtk-title-header.h',
'clapper-gtk-title-label.h',
'clapper-gtk-toggle-fullscreen-button.h',
'clapper-gtk-toggle-play-button.h',
'clapper-gtk-utils.h',
'clapper-gtk-video.h',
clappergtk_version_header,
]
clappergtk_sources = [
'clapper-gtk-billboard.c',
'clapper-gtk-buffering-animation.c',
'clapper-gtk-buffering-paintable.c',
'clapper-gtk-container.c',
'clapper-gtk-extra-menu-button.c',
'clapper-gtk-lead-container.c',
'clapper-gtk-limited-layout.c',
'clapper-gtk-next-item-button.c',
'clapper-gtk-previous-item-button.c',
'clapper-gtk-seek-bar.c',
'clapper-gtk-simple-controls.c',
'clapper-gtk-status.c',
'clapper-gtk-stream-check-button.c',
'clapper-gtk-title-header.c',
'clapper-gtk-title-label.c',
'clapper-gtk-toggle-fullscreen-button.c',
'clapper-gtk-toggle-play-button.c',
'clapper-gtk-utils.c',
'clapper-gtk-video.c',
clappergtk_resources,
]
clappergtk_c_args = [
'-DG_LOG_DOMAIN="ClapperGtk"',
'-DCLAPPER_GTK_COMPILATION',
'-DGST_USE_UNSTABLE_API',
]
clappergtk_headers_dir = join_paths(includedir, clapper_api_name, 'clapper-gtk')
clappergtk_enums = gnome.mkenums_simple(
'clapper-gtk-enum-types',
sources: clappergtk_headers,
identifier_prefix: 'ClapperGtk',
symbol_prefix: 'clapper_gtk',
install_header: true,
install_dir: clappergtk_headers_dir,
)
clappergtk_lib = library(
clappergtk_api_name,
clappergtk_sources + clappergtk_enums,
dependencies: clappergtk_deps,
include_directories: clappergtk_conf_inc,
c_args: clappergtk_c_args,
version: clapper_version,
install: true,
)
install_headers(clappergtk_headers,
install_dir: clappergtk_headers_dir,
)
build_clappergtk = true
if build_gir
clappergtk_gir = gnome.generate_gir(clappergtk_lib,
sources: [
clappergtk_sources,
clappergtk_headers,
clappergtk_enums,
],
extra_args: [
gir_init_section,
'--quiet',
'--warn-all',
'-DCLAPPER_GTK_COMPILATION',
'-DGST_USE_UNSTABLE_API',
],
nsversion: version_array[0] + '.0',
namespace: 'ClapperGtk',
identifier_prefix: 'ClapperGtk',
symbol_prefix: 'clapper_gtk',
export_packages: clappergtk_api_name,
install: true,
includes: [
clapper_gir[0],
'Gst-1.0',
'Gtk-4.0',
'GLib-2.0',
'GObject-2.0',
],
header: join_paths(meson.project_name() + '-gtk', 'clapper-gtk.h'),
)
endif
if build_vapi
if not build_gir
if get_option('vapi').enabled()
error('Cannot build "vapi" without "introspection"')
endif
else
gnome.generate_vapi(clappergtk_api_name,
sources: clappergtk_gir[0],
packages: clapper_pkg_reqs + [
clapper_vapi,
'gtk4',
],
install: true,
)
endif
endif
pkgconfig.generate(clappergtk_lib,
subdirs: [clappergtk_api_name],
filebase: clappergtk_api_name,
name: meson.project_name() + '-gtk',
version: meson.project_version(),
description: 'Clapper GTK integration library',
requires: [
clapper_lib,
'gstreamer-1.0',
'gtk4',
'glib-2.0',
'gobject-2.0',
],
)
clappergtk_dep = declare_dependency(
link_with: clappergtk_lib,
include_directories: clappergtk_conf_inc,
dependencies: clappergtk_deps,
sources: [
clappergtk_version_header,
clappergtk_enums[1],
],
)

View File

@@ -0,0 +1 @@
ar ast ca cs de es eu fa fi fr he hr hu it ja lt nl pl pt pt_BR ru sk sv tr zh_CN

View File

@@ -0,0 +1,5 @@
src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui
src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c
src/lib/clapper-gtk/clapper-gtk-stream-check-button.c
src/lib/clapper-gtk/clapper-gtk-title-label.c

View File

@@ -0,0 +1,65 @@
# Arabic translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: ar\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,65 @@
# Asturian translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: ast\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Catalan translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Czech translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# German translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Spanish translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,65 @@
# Basque translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: eu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,65 @@
# Persian translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: fa\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Finnish translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# French translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Hebrew translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: he\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,67 @@
# Croatian translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: hr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Hungarian translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Italian translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Japanese translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: ja\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,67 @@
# Lithuanian translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: lt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"(n%100<10 || n%100>=20) ? 1 : 2);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1 @@
i18n.gettext(meson.project_name() + '-gtk', preset: 'glib')

View File

@@ -0,0 +1,66 @@
# Dutch translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,67 @@
# Polish translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Portuguese translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: pt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,67 @@
# Portuguese translations for clapper-gtk package
# Tradu<64><75>es em portugu<67>s brasileiro para o pacote clapper-gtk.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,67 @@
# Russian translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Slovak translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: sk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Swedish translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Turkish translations for clapper-gtk package.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,66 @@
# Chinese translations for clapper-gtk package
# clapper-gtk <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ķ<EFBFBD><C4B7><EFBFBD>.
# Copyright (C) 2024 THE clapper-gtk'S COPYRIGHT HOLDER
# This file is distributed under the same license as the clapper-gtk package.
# Automatically generated, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: clapper-gtk\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-05 20:50+0200\n"
"PO-Revision-Date: 2024-03-07 20:49+0100\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Enabled"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""

View File

@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="clapper-gtk">
<template class="ClapperGtkBillboard" parent="ClapperGtkContainer">
<property name="halign">fill</property>
<property name="valign">center</property>
<property name="height-target">422</property>
<property name="adaptive-height">420</property>
<property name="visible">false</property>
<signal name="adapt" handler="adapt_cb"/>
<child>
<object class="GtkCenterBox">
<property name="orientation">horizontal</property>
<property name="halign">fill</property>
<property name="valign">center</property>
<property name="hexpand">true</property>
<child type="start">
<object class="GtkRevealer" id="side_revealer">
<property name="halign">start</property>
<property name="valign">center</property>
<property name="transition-type">crossfade</property>
<property name="transition-duration">500</property>
<property name="visible">false</property>
<signal name="notify::child-revealed" handler="revealer_revealed_cb"/>
<child>
<object class="ClapperGtkLeadContainer">
<property name="blocked-actions">toggle-play</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="halign">start</property>
<style>
<class name="osd"/>
<class name="sidebox"/>
</style>
<child>
<object class="GtkRevealer" id="progress_revealer">
<property name="halign">fill</property>
<property name="valign">end</property>
<property name="transition-type">slide-up</property>
<property name="transition-duration">200</property>
<property name="reveal-child">true</property>
<child>
<object class="GtkBox" id="progress_box">
<property name="orientation">vertical</property>
<property name="halign">center</property>
<property name="valign">fill</property>
<property name="spacing">4</property>
<style>
<class name="progressbox"/>
</style>
<child>
<object class="GtkProgressBar" id="top_progress">
<property name="orientation">vertical</property>
<property name="halign">center</property>
<property name="valign">fill</property>
<property name="vexpand">true</property>
<property name="inverted">true</property>
</object>
</child>
<child>
<object class="GtkProgressBar" id="bottom_progress">
<property name="orientation">vertical</property>
<property name="halign">center</property>
<property name="valign">fill</property>
<property name="vexpand">true</property>
<property name="inverted">true</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">horizontal</property>
<property name="halign">start</property>
<property name="valign">center</property>
<child>
<object class="GtkImage" id="progress_image">
<property name="halign">center</property>
<property name="valign">center</property>
</object>
</child>
<child>
<object class="GtkRevealer">
<property name="halign">start</property>
<property name="valign">fill</property>
<property name="transition-type">slide-right</property>
<property name="transition-duration" bind-source="progress_revealer" bind-property="transition-duration" bind-flags="sync-create"/>
<property name="reveal-child" bind-source="progress_revealer" bind-property="reveal-child" bind-flags="invert-boolean">false</property>
<child>
<object class="GtkLabel" id="progress_label">
<property name="valign">center</property>
<property name="label">100%</property>
<style>
<class name="numeric"/>
<class name="progresslabel"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child type="center">
<object class="GtkRevealer" id="message_revealer">
<property name="halign">center</property>
<property name="valign">center</property>
<property name="transition-type">crossfade</property>
<property name="transition-duration">500</property>
<signal name="notify::child-revealed" handler="revealer_revealed_cb"/>
<child>
<object class="ClapperGtkContainer">
<property name="width-target">280</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="spacing">8</property>
<style>
<class name="osd"/>
<class name="rounded"/>
<class name="messagebox"/>
</style>
<child>
<object class="GtkImage" id="message_image">
<property name="halign">center</property>
<property name="valign">center</property>
</object>
</child>
<child>
<object class="GtkLabel" id="message_label">
<property name="halign">center</property>
<property name="valign">center</property>
<property name="justify">center</property>
<property name="wrap">true</property>
<property name="wrap-mode">word-char</property>
<property name="natural-wrap-mode">word</property>
<style>
<class name="heading"/>
<class name="numeric"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@@ -0,0 +1,254 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="clapper-gtk">
<menu id="extra-menu">
<section>
<item>
<attribute name="custom">top-box</attribute>
</item>
<submenu>
<attribute name="label" translatable="yes">Video</attribute>
<section>
<item>
<attribute name="custom">video-streams</attribute>
</item>
</section>
</submenu>
<submenu>
<attribute name="label" translatable="yes">Audio</attribute>
<section>
<item>
<attribute name="custom">audio-streams</attribute>
</item>
</section>
</submenu>
<submenu>
<attribute name="label" translatable="yes">Subtitles</attribute>
<section>
<item>
<attribute name="label" translatable="yes">Enabled</attribute>
<attribute name="action">clappergtk.subtitle-stream-enabled</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Open…</attribute>
<attribute name="action">clappergtk.open-subtitle-stream</attribute>
<attribute name="hidden-when">action-disabled</attribute>
</item>
</section>
<section>
<item>
<attribute name="custom">subtitle-streams</attribute>
</item>
</section>
</submenu>
</section>
</menu>
<template class="ClapperGtkExtraMenuButton" parent="GtkWidget">
<child>
<object class="GtkMenuButton" id="menu_button">
<property name="direction">up</property>
<property name="icon-name">view-more-symbolic</property>
<property name="popover">
<object class="GtkPopoverMenu">
<property name="menu-model">extra-menu</property>
<signal name="map" handler="popover_map_cb"/>
<signal name="unmap" handler="popover_unmap_cb"/>
<signal name="notify::visible-submenu" handler="visible_submenu_changed_cb"/>
<child type="top-box">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="halign">center</property>
<property name="spacing">4</property>
<child>
<object class="GtkBox" id="volume_box">
<property name="orientation">horizontal</property>
<property name="halign">fill</property>
<property name="valign">center</property>
<child>
<object class="GtkButton" id="volume_button">
<property name="halign">center</property>
<property name="valign">center</property>
<property name="icon-name">audio-volume-high-symbolic</property>
<property name="action-name">video.toggle-mute</property>
<property name="tooltip-text" translatable="yes">Mute</property>
<style>
<class name="circular"/>
<class name="flat"/>
<class name="spinsidebutton"/>
</style>
</object>
</child>
<child>
<object class="GtkSpinButton" id="volume_spin">
<property name="halign">fill</property>
<property name="valign">center</property>
<property name="width-chars">5</property>
<property name="adjustment">
<object class="GtkAdjustment">
<property name="lower">0.0</property>
<property name="upper">2.0</property>
<property name="value">1.0</property>
<property name="page-increment">0</property>
<property name="step-increment">0.02</property>
</object>
</property>
<signal name="input" handler="volume_spin_input_cb"/>
<signal name="output" handler="volume_spin_output_cb"/>
<signal name="value-changed" handler="volume_spin_changed_cb"/>
<style>
<class name="numeric"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox" id="speed_box">
<property name="orientation">horizontal</property>
<property name="halign">fill</property>
<property name="valign">center</property>
<child>
<object class="GtkButton" id="speed_button">
<property name="halign">center</property>
<property name="valign">center</property>
<property name="icon-name">power-profile-balanced-symbolic</property>
<property name="action-name">video.set-speed</property>
<property name="action-target">1.0</property>
<property name="tooltip-text" translatable="yes">Reset</property>
<style>
<class name="circular"/>
<class name="flat"/>
<class name="spinsidebutton"/>
</style>
</object>
</child>
<child>
<object class="GtkSpinButton" id="speed_spin">
<property name="halign">fill</property>
<property name="valign">center</property>
<property name="width-chars">5</property>
<property name="adjustment">
<object class="GtkAdjustment">
<property name="lower">0.05</property>
<property name="upper">2.0</property>
<property name="value">1.0</property>
<property name="page-increment">0</property>
<property name="step-increment">0.05</property>
</object>
</property>
<signal name="input" handler="speed_spin_input_cb"/>
<signal name="output" handler="speed_spin_output_cb"/>
<signal name="value-changed" handler="speed_spin_changed_cb"/>
<style>
<class name="numeric"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkSeparator" id="top_separator">
<property name="orientation">horizontal</property>
</object>
</child>
</object>
</child>
<child type="video-streams">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkSeparator">
<property name="orientation">horizontal</property>
</object>
</child>
<child>
<object class="GtkScrolledWindow" id="video_sw">
<property name="css-name">clapper-gtk-menu-scrolled-window</property>
<property name="hscrollbar-policy">automatic</property>
<property name="vscrollbar-policy">automatic</property>
<property name="propagate-natural-width">false</property>
<property name="propagate-natural-height">true</property>
<property name="max-content-width">320</property>
<property name="max-content-height">192</property>
<child>
<object class="GtkListView" id="video_list_view">
<property name="orientation">vertical</property>
<property name="enable-rubberband">false</property>
<property name="single-click-activate">false</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="resource">/com/github/rafostar/Clapper/clapper-gtk/ui/clapper-gtk-stream-list-item.ui</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child type="audio-streams">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkSeparator">
<property name="orientation">horizontal</property>
</object>
</child>
<child>
<object class="GtkScrolledWindow" id="audio_sw">
<property name="css-name">clapper-gtk-menu-scrolled-window</property>
<property name="hscrollbar-policy">automatic</property>
<property name="vscrollbar-policy">automatic</property>
<property name="propagate-natural-width">false</property>
<property name="propagate-natural-height">true</property>
<property name="max-content-width">320</property>
<property name="max-content-height">192</property>
<child>
<object class="GtkListView" id="audio_list_view">
<property name="orientation">vertical</property>
<property name="enable-rubberband">false</property>
<property name="single-click-activate">false</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="resource">/com/github/rafostar/Clapper/clapper-gtk/ui/clapper-gtk-stream-list-item.ui</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child type="subtitle-streams">
<object class="GtkScrolledWindow" id="subtitle_sw">
<property name="css-name">clapper-gtk-menu-scrolled-window</property>
<property name="hscrollbar-policy">automatic</property>
<property name="vscrollbar-policy">automatic</property>
<property name="propagate-natural-width">false</property>
<property name="propagate-natural-height">true</property>
<property name="max-content-width">320</property>
<property name="max-content-height">192</property>
<child>
<object class="GtkListView" id="subtitle_list_view">
<property name="orientation">vertical</property>
<property name="enable-rubberband">false</property>
<property name="single-click-activate">false</property>
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="resource">/com/github/rafostar/Clapper/clapper-gtk/ui/clapper-gtk-stream-list-item.ui</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child>
</template>
</interface>

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="clapper-gtk">
<template class="ClapperGtkSeekBar" parent="GtkWidget">
<property name="hexpand">true</property>
<child>
<object class="GtkRevealer" id="position_revealer">
<property name="halign">end</property>
<property name="transition-type">slide-left</property>
<property name="transition-duration">800</property>
<child>
<object class="GtkLabel" id="position_label">
<property name="valign">center</property>
<property name="label">00:00</property>
<style>
<class name="numeric"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScale" id="scale">
<property name="orientation">horizontal</property>
<property name="halign">fill</property>
<property name="valign">center</property>
<property name="hexpand">true</property>
<property name="focusable">false</property>
<property name="draw-value">false</property>
<property name="adjustment">position_adjustment</property>
<signal name="value-changed" handler="scale_value_changed_cb"/>
<signal name="notify::css-classes" handler="scale_css_classes_changed_cb"/>
</object>
</child>
<child>
<object class="GtkPopover" id="popover">
<property name="position">top</property>
<property name="autohide">false</property>
<child>
<object class="GtkLabel" id="popover_label">
<property name="valign">center</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkRevealer" id="duration_revealer">
<property name="halign">start</property>
<property name="transition-type">slide-right</property>
<property name="transition-duration">800</property>
<property name="reveal-child" bind-source="position_revealer" bind-property="reveal-child" bind-flags="sync-create"/>
<child>
<object class="GtkLabel" id="duration_label">
<property name="valign">center</property>
<property name="label">00:00</property>
<style>
<class name="numeric"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkEventControllerMotion">
<signal name="motion" handler="motion_cb"/>
<signal name="leave" handler="motion_leave_cb"/>
</object>
</child>
<child>
<object class="GtkGestureClick">
<property name="propagation-phase">capture</property>
<property name="touch-only">true</property>
<signal name="released" handler="touch_released_cb"/>
</object>
</child>
</template>
<object class="GtkAdjustment" id="position_adjustment">
<property name="lower">0</property>
<property name="upper">0</property>
<property name="value">0</property>
<property name="page-increment">0</property>
<property name="step-increment">8</property>
</object>
</interface>

View File

@@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="clapper-gtk">
<template class="ClapperGtkSimpleControls" parent="ClapperGtkContainer">
<property name="halign">center</property>
<property name="valign">end</property>
<property name="adaptive-width">560</property>
<signal name="adapt" handler="initial_adapt_cb"/>
<child>
<object class="ClapperGtkContainer">
<property name="adaptive-width">400</property>
<signal name="adapt" handler="full_adapt_cb"/>
<child>
<object class="GtkCenterBox">
<property name="orientation">horizontal</property>
<property name="halign">center</property>
<property name="valign">end</property>
<style>
<class name="centerbox"/>
</style>
<child type="center">
<object class="ClapperGtkContainer">
<property name="width-target">944</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="halign">fill</property>
<property name="valign">center</property>
<child>
<object class="GtkRevealer" id="fullscreen_top_revealer">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="transition-type">crossfade</property>
<property name="transition-duration">200</property>
<property name="reveal-child" bind-source="controls_slide_revealer" bind-property="child-revealed" bind-flags="invert-boolean">false</property>
<property name="visible" bind-source="controls_slide_revealer" bind-property="reveal-child" bind-flags="invert-boolean">false</property>
<child>
<object class="ClapperGtkToggleFullscreenButton">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="visible" bind-source="ClapperGtkSimpleControls" bind-property="fullscreenable" bind-flags="sync-create"/>
<style>
<class name="osd"/>
<class name="flat"/>
<class name="circular"/>
<class name="fullscreenbutton"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="ClapperGtkLeadContainer">
<property name="blocked-actions">toggle-play|seek-request</property>
<child>
<object class="GtkBox">
<property name="orientation">horizontal</property>
<property name="halign">fill</property>
<property name="valign">center</property>
<style>
<class name="osd"/>
<class name="rounded"/>
<class name="mainbox"/>
</style>
<child>
<object class="ClapperGtkTogglePlayButton">
<property name="halign">center</property>
<property name="valign">center</property>
<style>
<class name="flat"/>
<class name="circular"/>
</style>
</object>
</child>
<child>
<object class="ClapperGtkSeekBar" id="seek_bar">
<property name="halign">fill</property>
<property name="valign">center</property>
<property name="seek-method" bind-source="ClapperGtkSimpleControls" bind-property="seek-method" bind-flags="bidirectional"/>
</object>
</child>
<child>
<object class="ClapperGtkExtraMenuButton" id="extra_menu_button">
<property name="halign">center</property>
<property name="valign">center</property>
<style>
<class name="flat"/>
<class name="circular"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child type="end">
<object class="GtkRevealer" id="controls_slide_revealer">
<property name="halign">end</property>
<property name="valign">end</property>
<property name="transition-type">slide-left</property>
<property name="transition-duration">400</property>
<property name="reveal-child" bind-source="fullscreen_top_revealer" bind-property="child-revealed" bind-flags="invert-boolean">true</property>
<property name="reveal-child" bind-source="fullscreen_bottom_revealer" bind-property="child-revealed" bind-flags="default"/>
<signal name="notify::child-revealed" handler="controls_revealed_cb"/>
<child>
<object class="GtkRevealer" id="fullscreen_bottom_revealer">
<property name="halign">center</property>
<property name="valign">end</property>
<property name="transition-type">crossfade</property>
<property name="transition-duration">200</property>
<property name="reveal-child" bind-source="controls_slide_revealer" bind-property="child-revealed" bind-flags="default">true</property>
<child>
<object class="ClapperGtkToggleFullscreenButton">
<property name="halign">center</property>
<property name="valign">end</property>
<property name="visible" bind-source="ClapperGtkSimpleControls" bind-property="fullscreenable" bind-flags="sync-create"/>
<style>
<class name="osd"/>
<class name="flat"/>
<class name="circular"/>
<class name="fullscreenbutton"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="clapper-gtk">
<template class="ClapperGtkStatus" parent="ClapperGtkContainer">
<property name="width-target">562</property>
<property name="height-target">402</property>
<property name="adaptive-width">560</property>
<property name="adaptive-height">400</property>
<signal name="adapt" handler="adapt_cb"/>
<child>
<object class="GtkBox" id="status_box">
<property name="orientation">vertical</property>
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkImage" id="image">
<property name="halign">center</property>
<property name="valign">center</property>
</object>
</child>
<child>
<object class="GtkLabel" id="title_label">
<property name="halign">center</property>
<property name="valign">center</property>
<property name="justify">center</property>
<property name="wrap">true</property>
<style>
<class name="title-1"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel" id="description_label">
<property name="halign">center</property>
<property name="valign">center</property>
<property name="justify">center</property>
<property name="wrap">true</property>
<style>
<class name="body"/>
</style>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="clapper-gtk">
<template class="GtkListItem">
<property name="child">
<object class="ClapperGtkStreamCheckButton">
<binding name="stream">
<lookup name="item">GtkListItem</lookup>
</binding>
<binding name="active">
<lookup name="selected">GtkListItem</lookup>
</binding>
</object>
</property>
</template>
</interface>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="clapper-gtk">
<template class="ClapperGtkTitleHeader" parent="ClapperGtkLeadContainer">
<property name="halign">center</property>
<property name="valign">start</property>
<property name="blocked-actions">toggle-play|seek-request</property>
<property name="width-target">640</property>
<style>
<class name="osd"/>
<class name="rounded"/>
</style>
<child>
<object class="ClapperGtkTitleLabel" id="label">
<property name="halign">center</property>
<property name="valign">center</property>
<style>
<class name="heading"/>
</style>
</object>
</child>
</template>
</interface>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="clapper-gtk">
<template class="ClapperGtkVideo" parent="GtkWidget">
<child type="overlay">
<object class="ClapperGtkStatus" id="status">
<property name="halign">center</property>
<property name="valign">center</property>
<property name="visible">false</property>
</object>
</child>
<child type="overlay">
<object class="ClapperGtkBufferingAnimation" id="buffering_animation">
<property name="halign">center</property>
<property name="valign">center</property>
<property name="visible">false</property>
</object>
</child>
<child>
<object class="GtkGestureClick" id="touch_gesture">
<property name="propagation-phase">capture</property>
<property name="touch-only">true</property>
<signal name="pressed" handler="touch_pressed_cb"/>
<signal name="released" handler="touch_released_cb"/>
</object>
</child>
<child>
<object class="GtkGestureClick" id="click_gesture">
<property name="button">1</property>
<signal name="pressed" handler="left_click_pressed_cb"/>
<signal name="released" handler="left_click_released_cb"/>
<signal name="stopped" handler="left_click_stopped_cb"/>
</object>
</child>
<child>
<object class="GtkEventControllerMotion">
<signal name="enter" handler="motion_enter_cb"/>
<signal name="motion" handler="motion_cb"/>
<signal name="leave" handler="motion_leave_cb"/>
</object>
</child>
<child>
<object class="GtkDropControllerMotion">
<signal name="motion" handler="drop_motion_cb"/>
<signal name="leave" handler="drop_motion_leave_cb"/>
</object>
</child>
</template>
</interface>

View File

@@ -12,3 +12,4 @@ gir_init_section = '--add-init-section=extern void gst_init(gint*,gchar**);' + \
subdir('gst')
subdir('clapper')
subdir('clapper-gtk')