Quelle gtklayoutmanager.c
Sprache: C
/* gtklayoutmanager.c: Layout manager base class
* Copyright 2018 The GNOME Foundation
*
* 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 < http : //www.gnu.org/licenses/>.
*
* Author : Emmanuele Bassi
*/
/**
* GtkLayoutManager :
*
* Handles the preferred size and allocation for children of a widget .
*
* You typically subclass ` GtkLayoutManager ` if you want to implement a
* layout policy for the children of a widget , or if you want to determine
* the size of a widget depending on its contents .
*
* Each ` GtkWidget ` can only have a ` GtkLayoutManager ` instance associated
* to it at any given time ; it is possible , though , to replace the layout
* manager instance using [ method @ Gtk . Widget . set_layout_manager ] .
*
* # # Layout properties
*
* A layout manager can expose properties for controlling the layout of
* each child , by creating an object type derived from [ class @ Gtk . LayoutChild ]
* and installing the properties on it as normal ` GObject ` properties .
*
* Each ` GtkLayoutChild ` instance storing the layout properties for a
* specific child is created through the [ method @ Gtk . LayoutManager . get_layout_child ]
* method ; a ` GtkLayoutManager ` controls the creation of its ` GtkLayoutChild `
* instances by overriding the GtkLayoutManagerClass . create_layout_child ( )
* virtual function . The typical implementation should look like :
*
* ` ` ` c
* static GtkLayoutChild *
* create_layout_child ( GtkLayoutManager * manager ,
* GtkWidget * container ,
* GtkWidget * child )
* {
* return g_object_new ( your_layout_child_get_type ( ) ,
* " layout - manager " , manager ,
* " child - widget " , child ,
* NULL ) ;
* }
* ` ` `
*
* The [ property @ Gtk . LayoutChild : layout - manager ] and
* [ property @ Gtk . LayoutChild : child - widget ] properties
* on the newly created ` GtkLayoutChild ` instance are mandatory . The
* ` GtkLayoutManager ` will cache the newly created ` GtkLayoutChild ` instance
* until the widget is removed from its parent , or the parent removes the
* layout manager .
*
* Each ` GtkLayoutManager ` instance creating a ` GtkLayoutChild ` should use
* [ method @ Gtk . LayoutManager . get_layout_child ] every time it needs to query
* the layout properties ; each ` GtkLayoutChild ` instance should call
* [ method @ Gtk . LayoutManager . layout_changed ] every time a property is
* updated , in order to queue a new size measuring and allocation .
*/
#include "config.h"
#include "gtklayoutmanagerprivate.h"
#include "gtklayoutchild.h"
#include "gtkwidgetprivate.h"
#include "gtknative.h"
#include "gtkpopover.h"
#include "gtktexthandleprivate.h"
#include "gtktooltipwindowprivate.h"
#ifdef G_ENABLE_DEBUG
#define LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED(m,method) G_STMT_START { \
GObject *_obj = G_OBJECT (m); \
g_warning ("Layout managers of type %s do not implement " \
"the GtkLayoutManager::%s method" , \
G_OBJECT_TYPE_NAME (_obj), \
#method ); } G_STMT_END
#else
#define LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED(m,method)
#endif
typedef struct {
GtkWidget *widget;
GtkRoot *root;
/* HashTable<Widget, LayoutChild> */
GHashTable *layout_children;
} GtkLayoutManagerPrivate;
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkLayoutManager, gtk_layout_manager, G_TYPE_OBJECT)
static void
gtk_layout_manager_real_root (GtkLayoutManager *manager)
{
}
static void
gtk_layout_manager_real_unroot (GtkLayoutManager *manager)
{
}
static GtkSizeRequestMode
gtk_layout_manager_real_get_request_mode (GtkLayoutManager *manager,
GtkWidget *widget)
{
int hfw = 0 , wfh = 0 ;
GtkWidget *child;
for (child = _gtk_widget_get_first_child (widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
GtkSizeRequestMode res;
if (!gtk_widget_should_layout (child))
continue ;
res = gtk_widget_get_request_mode (child);
switch (res)
{
case GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH:
hfw += 1 ;
break ;
case GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT:
wfh += 1 ;
break ;
case GTK_SIZE_REQUEST_CONSTANT_SIZE:
default :
break ;
}
}
if (hfw == 0 && wfh == 0 )
return GTK_SIZE_REQUEST_CONSTANT_SIZE;
return hfw >= wfh ? GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH
: GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
}
static void
gtk_layout_manager_real_measure (GtkLayoutManager *manager,
GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *baseline_minimum,
int *baseline_natural)
{
LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, measure);
if (minimum != NULL)
*minimum = 0 ;
if (natural != NULL)
*natural = 0 ;
if (baseline_minimum != NULL)
*baseline_minimum = 0 ;
if (baseline_natural != NULL)
*baseline_natural = 0 ;
}
static void
gtk_layout_manager_real_allocate (GtkLayoutManager *manager,
GtkWidget *widget,
int width,
int height,
int baseline)
{
LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, allocate);
}
static GtkLayoutChild *
gtk_layout_manager_real_create_layout_child (GtkLayoutManager *manager,
GtkWidget *widget,
GtkWidget *child)
{
GtkLayoutManagerClass *manager_class = GTK_LAYOUT_MANAGER_GET_CLASS (manager);
if (manager_class->layout_child_type == G_TYPE_INVALID)
{
LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, create_layout_child);
return NULL;
}
return g_object_new (manager_class->layout_child_type,
"layout-manager" , manager,
"child-widget" , child,
NULL);
}
static void
gtk_layout_manager_finalize (GObject *gobject)
{
GtkLayoutManager *self = GTK_LAYOUT_MANAGER (gobject);
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (self);
g_clear_pointer (&priv->layout_children, g_hash_table_unref);
G_OBJECT_CLASS (gtk_layout_manager_parent_class)->finalize (gobject);
}
static void
gtk_layout_manager_class_init (GtkLayoutManagerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = gtk_layout_manager_finalize;
klass->get_request_mode = gtk_layout_manager_real_get_request_mode;
klass->measure = gtk_layout_manager_real_measure;
klass->allocate = gtk_layout_manager_real_allocate;
klass->create_layout_child = gtk_layout_manager_real_create_layout_child;
klass->root = gtk_layout_manager_real_root;
klass->unroot = gtk_layout_manager_real_unroot;
}
static void
gtk_layout_manager_init (GtkLayoutManager *self)
{
}
/*< private >
* gtk_layout_manager_set_widget :
* @ layout_manager : a ` GtkLayoutManager `
* @ widget : ( nullable ) : a ` GtkWidget `
*
* Sets a back pointer from @ widget to @ layout_manager .
*/
void
gtk_layout_manager_set_widget (GtkLayoutManager *layout_manager,
GtkWidget *widget)
{
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (layout_manager);
if (widget != NULL && priv->widget != NULL)
{
g_critical ("The layout manager %p of type %s is already in use "
"by widget %p '%s', and cannot be used by widget %p '%s'" ,
layout_manager, G_OBJECT_TYPE_NAME (layout_manager),
priv->widget, gtk_widget_get_name (priv->widget),
widget, gtk_widget_get_name (widget));
return ;
}
priv->widget = widget;
if (widget != NULL)
gtk_layout_manager_set_root (layout_manager, gtk_widget_get_root (widget));
}
/*< private >
* gtk_layout_manager_set_root :
* @ layout_manager : a i ` GtkLayoutManager `
* @ root : ( nullable ) : a ` GtkWidget ` implementing ` GtkRoot `
*
* Sets a back pointer from @ root to @ layout_manager .
*
* This function is called by ` GtkWidget ` when getting rooted and unrooted ,
* and will call ` GtkLayoutManagerClass . root ( ) ` or ` GtkLayoutManagerClass . unroot ( ) `
* depending on whether @ root is a ` GtkWidget ` or % NULL .
*/
void
gtk_layout_manager_set_root (GtkLayoutManager *layout_manager,
GtkRoot *root)
{
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (layout_manager);
GtkRoot *old_root = priv->root;
priv->root = root;
if (old_root != root)
{
if (priv->root != NULL)
GTK_LAYOUT_MANAGER_GET_CLASS (layout_manager)->root (layout_manager);
else
GTK_LAYOUT_MANAGER_GET_CLASS (layout_manager)->unroot (layout_manager);
}
}
/**
* gtk_layout_manager_measure :
* @ manager : a ` GtkLayoutManager `
* @ widget : the ` GtkWidget ` using @ manager
* @ orientation : the orientation to measure
* @ for_size : Size for the opposite of @ orientation ; for instance , if
* the @ orientation is % GTK_ORIENTATION_HORIZONTAL , this is the height
* of the widget ; if the @ orientation is % GTK_ORIENTATION_VERTICAL , this
* is the width of the widget . This allows to measure the height for the
* given width , and the width for the given height . Use - 1 if the size
* is not known
* @ minimum : ( out ) ( optional ) : the minimum size for the given size and
* orientation
* @ natural : ( out ) ( optional ) : the natural , or preferred size for the
* given size and orientation
* @ minimum_baseline : ( out ) ( optional ) : the baseline position for the
* minimum size
* @ natural_baseline : ( out ) ( optional ) : the baseline position for the
* natural size
*
* Measures the size of the @ widget using @ manager , for the
* given @ orientation and size .
*
* See the [ class @ Gtk . Widget ] documentation on layout management for
* more details .
*/
void
gtk_layout_manager_measure (GtkLayoutManager *manager,
GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkLayoutManagerClass *klass;
int min_size = 0 ;
int nat_size = 0 ;
int min_baseline = -1 ;
int nat_baseline = -1 ;
g_return_if_fail (GTK_IS_LAYOUT_MANAGER (manager));
g_return_if_fail (GTK_IS_WIDGET (widget));
klass = GTK_LAYOUT_MANAGER_GET_CLASS (manager);
klass->measure (manager, widget, orientation,
for_size,
&min_size, &nat_size,
&min_baseline, &nat_baseline);
if (minimum)
*minimum = min_size;
if (natural)
*natural = nat_size;
if (minimum_baseline)
*minimum_baseline = min_baseline;
if (natural_baseline)
*natural_baseline = nat_baseline;
}
static void
allocate_native_children (GtkWidget *widget)
{
GtkWidget *child;
for (child = _gtk_widget_get_first_child (widget);
child != NULL;
child = _gtk_widget_get_next_sibling (child))
{
if (GTK_IS_POPOVER (child))
gtk_popover_present (GTK_POPOVER (child));
else if (GTK_IS_TEXT_HANDLE (child))
gtk_text_handle_present (GTK_TEXT_HANDLE (child));
else if (GTK_IS_TOOLTIP_WINDOW (child))
gtk_tooltip_window_present (GTK_TOOLTIP_WINDOW (child));
else if (GTK_IS_NATIVE (child))
g_warning ("Unable to present a to the layout manager unknown auxiliary child surface widget type %s" ,
G_OBJECT_TYPE_NAME (child));
}
}
/**
* gtk_layout_manager_allocate :
* @ manager : a ` GtkLayoutManager `
* @ widget : the ` GtkWidget ` using @ manager
* @ width : the new width of the @ widget
* @ height : the new height of the @ widget
* @ baseline : the baseline position of the @ widget , or - 1
*
* Assigns the given @ width , @ height , and @ baseline to
* a @ widget , and computes the position and sizes of the children of
* the @ widget using the layout management policy of @ manager .
*/
void
gtk_layout_manager_allocate (GtkLayoutManager *manager,
GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkLayoutManagerClass *klass;
g_return_if_fail (GTK_IS_LAYOUT_MANAGER (manager));
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (baseline >= -1 );
allocate_native_children (widget);
klass = GTK_LAYOUT_MANAGER_GET_CLASS (manager);
klass->allocate (manager, widget, width, height, baseline);
}
/**
* gtk_layout_manager_get_request_mode :
* @ manager : a ` GtkLayoutManager `
*
* Retrieves the request mode of @ manager .
*
* Returns : a ` GtkSizeRequestMode `
*/
GtkSizeRequestMode
gtk_layout_manager_get_request_mode (GtkLayoutManager *manager)
{
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (manager);
GtkLayoutManagerClass *klass;
g_return_val_if_fail (GTK_IS_LAYOUT_MANAGER (manager), GTK_SIZE_REQUEST_CONSTANT_SIZE);
g_return_val_if_fail (priv->widget != NULL, GTK_SIZE_REQUEST_CONSTANT_SIZE);
klass = GTK_LAYOUT_MANAGER_GET_CLASS (manager);
return klass->get_request_mode (manager, priv->widget);
}
/**
* gtk_layout_manager_get_widget :
* @ manager : a ` GtkLayoutManager `
*
* Retrieves the ` GtkWidget ` using the given ` GtkLayoutManager ` .
*
* Returns : ( transfer none ) ( nullable ) : a ` GtkWidget `
*/
GtkWidget *
gtk_layout_manager_get_widget (GtkLayoutManager *manager)
{
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (manager);
g_return_val_if_fail (GTK_IS_LAYOUT_MANAGER (manager), NULL);
return priv->widget;
}
/**
* gtk_layout_manager_layout_changed :
* @ manager : a ` GtkLayoutManager `
*
* Queues a resize on the ` GtkWidget ` using @ manager , if any .
*
* This function should be called by subclasses of ` GtkLayoutManager `
* in response to changes to their layout management policies .
*/
void
gtk_layout_manager_layout_changed (GtkLayoutManager *manager)
{
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (manager);
g_return_if_fail (GTK_IS_LAYOUT_MANAGER (manager));
if (priv->widget != NULL)
gtk_widget_queue_resize (priv->widget);
}
/*< private >
* gtk_layout_manager_remove_layout_child :
* @ manager : a ` GtkLayoutManager `
* @ widget : a ` GtkWidget `
*
* Removes the ` GtkLayoutChild ` associated with @ widget from the
* given ` GtkLayoutManager ` , if any is set .
*/
void
gtk_layout_manager_remove_layout_child (GtkLayoutManager *manager,
GtkWidget *widget)
{
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (manager);
if (priv->layout_children != NULL)
{
g_hash_table_remove (priv->layout_children, widget);
if (g_hash_table_size (priv->layout_children) == 0 )
g_clear_pointer (&priv->layout_children, g_hash_table_unref);
}
}
/**
* gtk_layout_manager_get_layout_child :
* @ manager : a ` GtkLayoutManager `
* @ child : a ` GtkWidget `
*
* Retrieves a ` GtkLayoutChild ` instance for the ` GtkLayoutManager ` ,
* creating one if necessary .
*
* The @ child widget must be a child of the widget using @ manager .
*
* The ` GtkLayoutChild ` instance is owned by the ` GtkLayoutManager ` ,
* and is guaranteed to exist as long as @ child is a child of the
* ` GtkWidget ` using the given ` GtkLayoutManager ` .
*
* Returns : ( transfer none ) : a ` GtkLayoutChild `
*/
GtkLayoutChild *
gtk_layout_manager_get_layout_child (GtkLayoutManager *manager,
GtkWidget *child)
{
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (manager);
GtkLayoutChild *res;
GtkWidget *parent;
g_return_val_if_fail (GTK_IS_LAYOUT_MANAGER (manager), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
parent = _gtk_widget_get_parent (child);
g_return_val_if_fail (parent != NULL, NULL);
if (priv->widget != parent)
{
g_critical ("The parent %s %p of the widget %s %p does not "
"use the given layout manager of type %s %p" ,
gtk_widget_get_name (parent), parent,
gtk_widget_get_name (child), child,
G_OBJECT_TYPE_NAME (manager), manager);
return NULL;
}
if (priv->layout_children == NULL)
{
priv->layout_children = g_hash_table_new_full (NULL, NULL,
NULL,
(GDestroyNotify) g_object_unref);
}
res = g_hash_table_lookup (priv->layout_children, child);
if (res != NULL)
{
/* If the LayoutChild instance is stale, and refers to another
* layout manager , then we simply ask the LayoutManager to
* replace it , as it means the layout manager for the parent
* widget was replaced
*/
if (gtk_layout_child_get_layout_manager (res) == manager)
return res;
}
res = GTK_LAYOUT_MANAGER_GET_CLASS (manager)->create_layout_child (manager, parent, child);
if (res == NULL)
{
g_critical ("The layout manager of type %s %p does not create "
"GtkLayoutChild instances" ,
G_OBJECT_TYPE_NAME (manager), manager);
return NULL;
}
g_assert (g_type_is_a (G_OBJECT_TYPE (res), GTK_TYPE_LAYOUT_CHILD));
g_hash_table_insert (priv->layout_children, child, res);
return res;
}
Messung V0.5 in Prozent C=98 H=95 G=96
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland
2026-07-09