mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-29 15:22:11 +02:00
The Free Software Foundation is now remote-only, and no longer has a street address. Updated license notice text is from https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html#SEC4.
414 lines
12 KiB
C
414 lines
12 KiB
C
/* 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, see
|
|
* <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* 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
|
|
* @child: a #GtkWidget
|
|
*
|
|
* 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");
|
|
}
|