Compare commits
10 Commits
message-wi
...
wip/matthi
Author | SHA1 | Date | |
---|---|---|---|
|
fde02b8dc1 | ||
|
1839bc6b51 | ||
|
fb004d1c64 | ||
|
972b11ab40 | ||
|
d0c332c5e4 | ||
|
00a6fdf62a | ||
|
289f73ff22 | ||
|
9788f05a84 | ||
|
64ab7aee8d | ||
|
5ca7ff34ae |
@@ -99,6 +99,13 @@
|
||||
<xi:include href="xml/gtkfixed.xml" />
|
||||
</chapter>
|
||||
|
||||
<chapter id="LayoutManagers">
|
||||
<title>Layout Managers</title>
|
||||
<xi:include href="xml/gtklayoutmanager.xml" />
|
||||
<xi:include href="xml/gtklayoutchild.xml" />
|
||||
<xi:include href="xml/gtkboxlayout.xml" />
|
||||
</chapter>
|
||||
|
||||
<chapter id="DisplayWidgets">
|
||||
<title>Display Widgets</title>
|
||||
<xi:include href="xml/gtklabel.xml" />
|
||||
|
@@ -4535,6 +4535,8 @@ gtk_widget_get_first_child
|
||||
gtk_widget_get_last_child
|
||||
gtk_widget_insert_before
|
||||
gtk_widget_insert_after
|
||||
gtk_widget_set_layout_manager
|
||||
gtk_widget_get_layout_manager
|
||||
|
||||
<SUBSECTION>
|
||||
gtk_widget_get_path
|
||||
@@ -7089,3 +7091,51 @@ gtk_media_stream_error_valist
|
||||
GTK_TYPE_MEDIA_STREAM
|
||||
gtk_media_stream_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtklayoutmanager</FILE>
|
||||
GtkLayoutManager
|
||||
GtkLayoutManagerClass
|
||||
|
||||
gtk_layout_manager_measure
|
||||
gtk_layout_manager_allocate
|
||||
gtk_layout_manager_get_request_mode
|
||||
gtk_layout_manager_get_widget
|
||||
gtk_layout_manager_get_layout_child
|
||||
gtk_layout_manager_layout_changed
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GTK_TYPE_LAYOUT_MANAGER
|
||||
gtk_layout_manager_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtklayoutchild</FILE>
|
||||
GtkLayoutChild
|
||||
GtkLayoutChildClass
|
||||
|
||||
gtk_layout_child_get_layout_manager
|
||||
gtk_layout_child_get_child_widget
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GTK_TYPE_LAYOUT_CHILD
|
||||
gtk_layout_child_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkboxlayout</FILE>
|
||||
GtkBoxLayout
|
||||
GtkBoxLayoutClass
|
||||
|
||||
gtk_box_layout_new
|
||||
gtk_box_layout_set_homogeneous
|
||||
gtk_box_layout_get_homogeneous
|
||||
gtk_box_layout_set_spacing
|
||||
gtk_box_layout_get_spacing
|
||||
gtk_box_layout_set_baseline_position
|
||||
gtk_box_layout_get_baseline_position
|
||||
|
||||
<SUBSECTION Standard>
|
||||
GTK_TYPE_BOX_LAYOUT
|
||||
gtk_box_layout_get_type
|
||||
</SECTION>
|
||||
|
@@ -48,6 +48,7 @@
|
||||
#include <gtk/gtkbin.h>
|
||||
#include <gtk/gtkbindings.h>
|
||||
#include <gtk/gtkborder.h>
|
||||
#include <gtk/gtkboxlayout.h>
|
||||
#include <gtk/gtkbox.h>
|
||||
#include <gtk/gtkbuildable.h>
|
||||
#include <gtk/gtkbuilder.h>
|
||||
@@ -136,6 +137,8 @@
|
||||
#include <gtk/gtkinvisible.h>
|
||||
#include <gtk/gtklabel.h>
|
||||
#include <gtk/gtklayout.h>
|
||||
#include <gtk/gtklayoutmanager.h>
|
||||
#include <gtk/gtklayoutchild.h>
|
||||
#include <gtk/gtklevelbar.h>
|
||||
#include <gtk/gtklinkbutton.h>
|
||||
#include <gtk/gtklistbox.h>
|
||||
|
621
gtk/gtkbox.c
621
gtk/gtkbox.c
@@ -58,6 +58,7 @@
|
||||
|
||||
#include "gtkbox.h"
|
||||
#include "gtkboxprivate.h"
|
||||
#include "gtkboxlayout.h"
|
||||
#include "gtkcsspositionvalueprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkorientable.h"
|
||||
@@ -93,11 +94,6 @@ typedef struct
|
||||
|
||||
static GParamSpec *props[LAST_PROP] = { NULL, };
|
||||
|
||||
static void gtk_box_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline);
|
||||
|
||||
static void gtk_box_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
@@ -117,13 +113,6 @@ static GType gtk_box_child_type (GtkContainer *container);
|
||||
static GtkWidgetPath * gtk_box_get_path_for_child
|
||||
(GtkContainer *container,
|
||||
GtkWidget *child);
|
||||
static void gtk_box_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
|
||||
G_ADD_PRIVATE (GtkBox)
|
||||
@@ -139,9 +128,6 @@ gtk_box_class_init (GtkBoxClass *class)
|
||||
object_class->set_property = gtk_box_set_property;
|
||||
object_class->get_property = gtk_box_get_property;
|
||||
|
||||
widget_class->size_allocate = gtk_box_size_allocate;
|
||||
widget_class->measure = gtk_box_measure;
|
||||
|
||||
container_class->add = gtk_box_add;
|
||||
container_class->remove = gtk_box_remove;
|
||||
container_class->forall = gtk_box_forall;
|
||||
@@ -188,6 +174,7 @@ gtk_box_set_property (GObject *object,
|
||||
{
|
||||
GtkBox *box = GTK_BOX (object);
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkLayoutManager *box_layout = gtk_widget_get_layout_manager (GTK_WIDGET (box));
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
@@ -197,20 +184,21 @@ gtk_box_set_property (GObject *object,
|
||||
if (priv->orientation != orientation)
|
||||
{
|
||||
priv->orientation = orientation;
|
||||
gtk_orientable_set_orientation (GTK_ORIENTABLE (box_layout),
|
||||
priv->orientation);
|
||||
_gtk_orientable_set_style_classes (GTK_ORIENTABLE (box));
|
||||
gtk_widget_queue_resize (GTK_WIDGET (box));
|
||||
g_object_notify (object, "orientation");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_SPACING:
|
||||
gtk_box_set_spacing (box, g_value_get_int (value));
|
||||
gtk_box_layout_set_spacing (GTK_BOX_LAYOUT (box_layout), g_value_get_int (value));
|
||||
break;
|
||||
case PROP_BASELINE_POSITION:
|
||||
gtk_box_set_baseline_position (box, g_value_get_enum (value));
|
||||
gtk_box_layout_set_baseline_position (GTK_BOX_LAYOUT (box_layout), g_value_get_enum (value));
|
||||
break;
|
||||
case PROP_HOMOGENEOUS:
|
||||
gtk_box_set_homogeneous (box, g_value_get_boolean (value));
|
||||
gtk_box_layout_set_homogeneous (GTK_BOX_LAYOUT (box_layout), g_value_get_boolean (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
@@ -226,6 +214,7 @@ gtk_box_get_property (GObject *object,
|
||||
{
|
||||
GtkBox *box = GTK_BOX (object);
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkBoxLayout *box_layout = GTK_BOX_LAYOUT (gtk_widget_get_layout_manager (GTK_WIDGET (box)));
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
@@ -233,13 +222,13 @@ gtk_box_get_property (GObject *object,
|
||||
g_value_set_enum (value, priv->orientation);
|
||||
break;
|
||||
case PROP_SPACING:
|
||||
g_value_set_int (value, priv->spacing);
|
||||
g_value_set_int (value, gtk_box_layout_get_spacing (box_layout));
|
||||
break;
|
||||
case PROP_BASELINE_POSITION:
|
||||
g_value_set_enum (value, priv->baseline_pos);
|
||||
g_value_set_enum (value, gtk_box_layout_get_baseline_position (box_layout));
|
||||
break;
|
||||
case PROP_HOMOGENEOUS:
|
||||
g_value_set_boolean (value, priv->homogeneous);
|
||||
g_value_set_boolean (value, gtk_box_layout_get_homogeneous (box_layout));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
@@ -247,285 +236,6 @@ gtk_box_get_property (GObject *object,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
count_expand_children (GtkBox *box,
|
||||
gint *visible_children,
|
||||
gint *expand_children)
|
||||
{
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkWidget *child;
|
||||
|
||||
*visible_children = *expand_children = 0;
|
||||
|
||||
for (child = _gtk_widget_get_first_child (GTK_WIDGET (box));
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
if (_gtk_widget_get_visible (child))
|
||||
{
|
||||
*visible_children += 1;
|
||||
|
||||
if (gtk_widget_compute_expand (child, priv->orientation))
|
||||
*expand_children += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
get_spacing (GtkBox *box)
|
||||
{
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkCssValue *border_spacing;
|
||||
gint css_spacing;
|
||||
|
||||
border_spacing = _gtk_style_context_peek_property (gtk_widget_get_style_context (GTK_WIDGET (box)), GTK_CSS_PROPERTY_BORDER_SPACING);
|
||||
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
css_spacing = _gtk_css_position_value_get_x (border_spacing, 100);
|
||||
else
|
||||
css_spacing = _gtk_css_position_value_get_y (border_spacing, 100);
|
||||
|
||||
return css_spacing + priv->spacing;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_box_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkBox *box = GTK_BOX (widget);
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkWidget *child;
|
||||
gint nvis_children;
|
||||
gint nexpand_children;
|
||||
GtkTextDirection direction;
|
||||
GtkAllocation child_allocation;
|
||||
GtkRequestedSize *sizes;
|
||||
gint child_minimum_baseline, child_natural_baseline;
|
||||
gint minimum_above, natural_above;
|
||||
gint minimum_below, natural_below;
|
||||
gboolean have_baseline;
|
||||
gint extra_space;
|
||||
gint children_minimum_size = 0;
|
||||
gint size_given_to_child;
|
||||
gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
|
||||
gint x = 0, y = 0, i;
|
||||
gint child_size;
|
||||
gint spacing;
|
||||
|
||||
|
||||
count_expand_children (box, &nvis_children, &nexpand_children);
|
||||
|
||||
/* If there is no visible child, simply return. */
|
||||
if (nvis_children <= 0)
|
||||
return;
|
||||
|
||||
direction = _gtk_widget_get_direction (widget);
|
||||
sizes = g_newa (GtkRequestedSize, nvis_children);
|
||||
spacing = get_spacing (box);
|
||||
|
||||
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
extra_space = width - (nvis_children - 1) * spacing;
|
||||
else
|
||||
extra_space = height - (nvis_children - 1) * spacing;
|
||||
|
||||
have_baseline = FALSE;
|
||||
minimum_above = natural_above = 0;
|
||||
minimum_below = natural_below = 0;
|
||||
|
||||
/* Retrieve desired size for visible children. */
|
||||
for (i = 0, child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
if (!_gtk_widget_get_visible (child))
|
||||
continue;
|
||||
|
||||
gtk_widget_measure (child,
|
||||
priv->orientation,
|
||||
priv->orientation == GTK_ORIENTATION_HORIZONTAL ? height : width,
|
||||
&sizes[i].minimum_size, &sizes[i].natural_size,
|
||||
NULL, NULL);
|
||||
|
||||
children_minimum_size += sizes[i].minimum_size;
|
||||
|
||||
sizes[i].data = child;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (priv->homogeneous)
|
||||
{
|
||||
/* We still need to run the above loop to populate the minimum sizes for
|
||||
* children that aren't going to fill.
|
||||
*/
|
||||
|
||||
size_given_to_child = extra_space / nvis_children;
|
||||
n_extra_widgets = extra_space % nvis_children;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Bring children up to size first */
|
||||
extra_space -= children_minimum_size;
|
||||
extra_space = MAX (0, extra_space);
|
||||
extra_space = gtk_distribute_natural_allocation (extra_space, nvis_children, sizes);
|
||||
|
||||
/* Calculate space which hasn't distributed yet,
|
||||
* and is available for expanding children.
|
||||
*/
|
||||
if (nexpand_children > 0)
|
||||
{
|
||||
size_given_to_child = extra_space / nexpand_children;
|
||||
n_extra_widgets = extra_space % nexpand_children;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_given_to_child = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate child sizes. */
|
||||
for (i = 0, child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
/* If widget is not visible, skip it. */
|
||||
if (!_gtk_widget_get_visible (child))
|
||||
continue;
|
||||
|
||||
/* Assign the child's size. */
|
||||
if (priv->homogeneous)
|
||||
{
|
||||
child_size = size_given_to_child;
|
||||
|
||||
if (n_extra_widgets > 0)
|
||||
{
|
||||
child_size++;
|
||||
n_extra_widgets--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
child_size = sizes[i].minimum_size;
|
||||
|
||||
if (gtk_widget_compute_expand (child, priv->orientation))
|
||||
{
|
||||
child_size += size_given_to_child;
|
||||
|
||||
if (n_extra_widgets > 0)
|
||||
{
|
||||
child_size++;
|
||||
n_extra_widgets--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sizes[i].natural_size = child_size;
|
||||
|
||||
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
|
||||
gtk_widget_get_valign (child) == GTK_ALIGN_BASELINE)
|
||||
{
|
||||
int child_allocation_width;
|
||||
int child_minimum_height, child_natural_height;
|
||||
|
||||
child_allocation_width = child_size;
|
||||
|
||||
child_minimum_baseline = -1;
|
||||
child_natural_baseline = -1;
|
||||
gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL,
|
||||
child_allocation_width,
|
||||
&child_minimum_height, &child_natural_height,
|
||||
&child_minimum_baseline, &child_natural_baseline);
|
||||
|
||||
if (child_minimum_baseline >= 0)
|
||||
{
|
||||
have_baseline = TRUE;
|
||||
minimum_below = MAX (minimum_below, child_minimum_height - child_minimum_baseline);
|
||||
natural_below = MAX (natural_below, child_natural_height - child_natural_baseline);
|
||||
minimum_above = MAX (minimum_above, child_minimum_baseline);
|
||||
natural_above = MAX (natural_above, child_natural_baseline);
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
|
||||
baseline = -1;
|
||||
|
||||
/* we only calculate our own baseline if we don't get one passed from the parent
|
||||
* and any of the child widgets explicitly request one */
|
||||
if (baseline == -1 && have_baseline)
|
||||
{
|
||||
/* TODO: This is purely based on the minimum baseline, when things fit we should
|
||||
use the natural one? */
|
||||
|
||||
switch (priv->baseline_pos)
|
||||
{
|
||||
case GTK_BASELINE_POSITION_TOP:
|
||||
baseline = minimum_above;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_CENTER:
|
||||
baseline = minimum_above + (height - (minimum_above + minimum_below)) / 2;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_BOTTOM:
|
||||
baseline = height - minimum_below;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate child positions. */
|
||||
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
child_allocation.y = 0;
|
||||
child_allocation.height = height;
|
||||
x = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
child_allocation.x = 0;
|
||||
child_allocation.width = width;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
for (i = 0, child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
/* If widget is not visible, skip it. */
|
||||
if (!_gtk_widget_get_visible (child))
|
||||
continue;
|
||||
|
||||
child_size = sizes[i].natural_size;
|
||||
|
||||
/* Assign the child's position. */
|
||||
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
child_allocation.width = child_size;
|
||||
child_allocation.x = x;
|
||||
|
||||
x += child_size + spacing;
|
||||
|
||||
if (direction == GTK_TEXT_DIR_RTL)
|
||||
child_allocation.x = width - child_allocation.x - child_allocation.width;
|
||||
|
||||
}
|
||||
else /* (priv->orientation == GTK_ORIENTATION_VERTICAL) */
|
||||
{
|
||||
child_allocation.height = child_size;
|
||||
child_allocation.y = y;
|
||||
|
||||
y += child_size + spacing;
|
||||
}
|
||||
|
||||
gtk_widget_size_allocate (child, &child_allocation, baseline);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_box_child_type (GtkContainer *container)
|
||||
{
|
||||
@@ -631,263 +341,17 @@ gtk_box_get_path_for_child (GtkContainer *container,
|
||||
return path;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
|
||||
int for_size,
|
||||
gint *minimum_size,
|
||||
gint *natural_size,
|
||||
gint *minimum_baseline,
|
||||
gint *natural_baseline)
|
||||
{
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkWidget *widget = GTK_WIDGET (box);
|
||||
GtkWidget *child;
|
||||
gint nvis_children;
|
||||
gint nexpand_children;
|
||||
gint computed_minimum = 0, computed_natural = 0;
|
||||
gint computed_minimum_above = 0, computed_natural_above = 0;
|
||||
gint computed_minimum_below = 0, computed_natural_below = 0;
|
||||
gint computed_minimum_baseline = -1, computed_natural_baseline = -1;
|
||||
GtkRequestedSize *sizes;
|
||||
gint extra_space, size_given_to_child, i;
|
||||
gint children_minimum_size = 0;
|
||||
gint child_size, child_minimum, child_natural;
|
||||
gint child_minimum_baseline, child_natural_baseline;
|
||||
gint n_extra_widgets = 0;
|
||||
gint spacing;
|
||||
gboolean have_baseline;
|
||||
|
||||
count_expand_children (box, &nvis_children, &nexpand_children);
|
||||
|
||||
if (nvis_children <= 0)
|
||||
return;
|
||||
|
||||
spacing = get_spacing (box);
|
||||
sizes = g_newa (GtkRequestedSize, nvis_children);
|
||||
extra_space = MAX (0, for_size - (nvis_children - 1) * spacing);
|
||||
|
||||
/* Retrieve desired size for visible children */
|
||||
for (i = 0, child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
if (_gtk_widget_get_visible (child))
|
||||
{
|
||||
gtk_widget_measure (child,
|
||||
priv->orientation,
|
||||
-1,
|
||||
&sizes[i].minimum_size, &sizes[i].natural_size,
|
||||
NULL, NULL);
|
||||
|
||||
children_minimum_size += sizes[i].minimum_size;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->homogeneous)
|
||||
{
|
||||
/* We still need to run the above loop to populate the minimum sizes for
|
||||
* children that aren't going to fill.
|
||||
*/
|
||||
|
||||
size_given_to_child = extra_space / nvis_children;
|
||||
n_extra_widgets = extra_space % nvis_children;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Bring children up to size first */
|
||||
extra_space -= children_minimum_size;
|
||||
extra_space = MAX (0, extra_space);
|
||||
extra_space = gtk_distribute_natural_allocation (extra_space, nvis_children, sizes);
|
||||
|
||||
/* Calculate space which hasn't distributed yet,
|
||||
* and is available for expanding children.
|
||||
*/
|
||||
if (nexpand_children > 0)
|
||||
{
|
||||
size_given_to_child = extra_space / nexpand_children;
|
||||
n_extra_widgets = extra_space % nexpand_children;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_given_to_child = 0;
|
||||
}
|
||||
}
|
||||
|
||||
have_baseline = FALSE;
|
||||
for (i = 0, child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
/* If widget is not visible, skip it. */
|
||||
if (!_gtk_widget_get_visible (child))
|
||||
continue;
|
||||
|
||||
/* Assign the child's size. */
|
||||
if (priv->homogeneous)
|
||||
{
|
||||
child_size = size_given_to_child;
|
||||
|
||||
if (n_extra_widgets > 0)
|
||||
{
|
||||
child_size++;
|
||||
n_extra_widgets--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
child_size = sizes[i].minimum_size;
|
||||
|
||||
if (gtk_widget_compute_expand (child, priv->orientation))
|
||||
{
|
||||
child_size += size_given_to_child;
|
||||
|
||||
if (n_extra_widgets > 0)
|
||||
{
|
||||
child_size++;
|
||||
n_extra_widgets--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
child_minimum_baseline = child_natural_baseline = -1;
|
||||
/* Assign the child's position. */
|
||||
gtk_widget_measure (child,
|
||||
OPPOSITE_ORIENTATION (priv->orientation),
|
||||
child_size,
|
||||
&child_minimum, &child_natural,
|
||||
&child_minimum_baseline, &child_natural_baseline);
|
||||
|
||||
if (child_minimum_baseline >= 0)
|
||||
{
|
||||
have_baseline = TRUE;
|
||||
computed_minimum_below = MAX (computed_minimum_below, child_minimum - child_minimum_baseline);
|
||||
computed_natural_below = MAX (computed_natural_below, child_natural - child_natural_baseline);
|
||||
computed_minimum_above = MAX (computed_minimum_above, child_minimum_baseline);
|
||||
computed_natural_above = MAX (computed_natural_above, child_natural_baseline);
|
||||
}
|
||||
else
|
||||
{
|
||||
computed_minimum = MAX (computed_minimum, child_minimum);
|
||||
computed_natural = MAX (computed_natural, child_natural);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (have_baseline)
|
||||
{
|
||||
computed_minimum = MAX (computed_minimum, computed_minimum_below + computed_minimum_above);
|
||||
computed_natural = MAX (computed_natural, computed_natural_below + computed_natural_above);
|
||||
switch (priv->baseline_pos)
|
||||
{
|
||||
case GTK_BASELINE_POSITION_TOP:
|
||||
computed_minimum_baseline = computed_minimum_above;
|
||||
computed_natural_baseline = computed_natural_above;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_CENTER:
|
||||
computed_minimum_baseline = computed_minimum_above + MAX((computed_minimum - (computed_minimum_above + computed_minimum_below)) / 2, 0);
|
||||
computed_natural_baseline = computed_natural_above + MAX((computed_natural - (computed_natural_above + computed_natural_below)) / 2, 0);
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_BOTTOM:
|
||||
computed_minimum_baseline = computed_minimum - computed_minimum_below;
|
||||
computed_natural_baseline = computed_natural - computed_natural_below;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*minimum_size = computed_minimum;
|
||||
*natural_size = MAX (computed_natural, computed_natural_below + computed_natural_above);
|
||||
|
||||
*minimum_baseline = computed_minimum_baseline;
|
||||
*natural_baseline = computed_natural_baseline;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_box_compute_size_for_orientation (GtkBox *box,
|
||||
int for_size,
|
||||
int *minimum_size,
|
||||
int *natural_size)
|
||||
{
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkWidget *child;
|
||||
const int spacing = get_spacing (box);
|
||||
int nvis_children = 0;
|
||||
int required_size = 0, required_natural = 0;
|
||||
int largest_child = 0, largest_natural = 0;
|
||||
|
||||
for (child = _gtk_widget_get_first_child (GTK_WIDGET (box));
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
if (_gtk_widget_get_visible (child))
|
||||
{
|
||||
int child_size, child_natural;
|
||||
|
||||
gtk_widget_measure (child,
|
||||
priv->orientation,
|
||||
for_size,
|
||||
&child_size, &child_natural,
|
||||
NULL, NULL);
|
||||
|
||||
largest_child = MAX (largest_child, child_size);
|
||||
largest_natural = MAX (largest_natural, child_natural);
|
||||
|
||||
required_size += child_size;
|
||||
required_natural += child_natural;
|
||||
|
||||
nvis_children += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (nvis_children > 0)
|
||||
{
|
||||
if (priv->homogeneous)
|
||||
{
|
||||
required_size = largest_child * nvis_children;
|
||||
required_natural = largest_natural * nvis_children;
|
||||
}
|
||||
|
||||
required_size += (nvis_children - 1) * spacing;
|
||||
required_natural += (nvis_children - 1) * spacing;
|
||||
}
|
||||
|
||||
*minimum_size = required_size;
|
||||
*natural_size = required_natural;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gtk_box_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkBox *box = GTK_BOX (widget);
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
|
||||
if (priv->orientation != orientation)
|
||||
gtk_box_compute_size_for_opposing_orientation (box, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
else
|
||||
gtk_box_compute_size_for_orientation (box, for_size, minimum, natural);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_box_init (GtkBox *box)
|
||||
{
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkLayoutManager *box_layout = gtk_box_layout_new (GTK_ORIENTATION_HORIZONTAL);
|
||||
|
||||
gtk_widget_set_has_surface (GTK_WIDGET (box), FALSE);
|
||||
|
||||
priv->orientation = GTK_ORIENTATION_HORIZONTAL;
|
||||
priv->homogeneous = FALSE;
|
||||
priv->spacing = 0;
|
||||
priv->baseline_pos = GTK_BASELINE_POSITION_CENTER;
|
||||
gtk_widget_set_layout_manager (GTK_WIDGET (box), box_layout);
|
||||
|
||||
priv->orientation = GTK_ORIENTATION_HORIZONTAL;
|
||||
_gtk_orientable_set_style_classes (GTK_ORIENTABLE (box));
|
||||
}
|
||||
|
||||
@@ -906,7 +370,7 @@ gtk_box_new (GtkOrientation orientation,
|
||||
{
|
||||
return g_object_new (GTK_TYPE_BOX,
|
||||
"orientation", orientation,
|
||||
"spacing", spacing,
|
||||
"spacing", spacing,
|
||||
NULL);
|
||||
}
|
||||
|
||||
@@ -924,18 +388,14 @@ void
|
||||
gtk_box_set_homogeneous (GtkBox *box,
|
||||
gboolean homogeneous)
|
||||
{
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkLayoutManager *box_layout;
|
||||
|
||||
g_return_if_fail (GTK_IS_BOX (box));
|
||||
|
||||
homogeneous = homogeneous != FALSE;
|
||||
box_layout = gtk_widget_get_layout_manager (GTK_WIDGET (box));
|
||||
gtk_box_layout_set_homogeneous (GTK_BOX_LAYOUT (box_layout), homogeneous);
|
||||
|
||||
if (priv->homogeneous != homogeneous)
|
||||
{
|
||||
priv->homogeneous = homogeneous;
|
||||
g_object_notify_by_pspec (G_OBJECT (box), props[PROP_HOMOGENEOUS]);
|
||||
gtk_widget_queue_resize (GTK_WIDGET (box));
|
||||
}
|
||||
g_object_notify_by_pspec (G_OBJECT (box), props[PROP_HOMOGENEOUS]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -950,11 +410,13 @@ gtk_box_set_homogeneous (GtkBox *box,
|
||||
gboolean
|
||||
gtk_box_get_homogeneous (GtkBox *box)
|
||||
{
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkLayoutManager *box_layout;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
|
||||
|
||||
return priv->homogeneous;
|
||||
box_layout = gtk_widget_get_layout_manager (GTK_WIDGET (box));
|
||||
|
||||
return gtk_box_layout_get_homogeneous (GTK_BOX_LAYOUT (box_layout));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -969,18 +431,14 @@ void
|
||||
gtk_box_set_spacing (GtkBox *box,
|
||||
gint spacing)
|
||||
{
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkLayoutManager *box_layout;
|
||||
|
||||
g_return_if_fail (GTK_IS_BOX (box));
|
||||
|
||||
if (priv->spacing != spacing)
|
||||
{
|
||||
priv->spacing = spacing;
|
||||
box_layout = gtk_widget_get_layout_manager (GTK_WIDGET (box));
|
||||
gtk_box_layout_set_spacing (GTK_BOX_LAYOUT (box_layout), spacing);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (box), props[PROP_SPACING]);
|
||||
|
||||
gtk_widget_queue_resize (GTK_WIDGET (box));
|
||||
}
|
||||
g_object_notify_by_pspec (G_OBJECT (box), props[PROP_SPACING]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -994,10 +452,13 @@ gtk_box_set_spacing (GtkBox *box,
|
||||
gint
|
||||
gtk_box_get_spacing (GtkBox *box)
|
||||
{
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkLayoutManager *box_layout;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BOX (box), 0);
|
||||
|
||||
return priv->spacing;
|
||||
box_layout = gtk_widget_get_layout_manager (GTK_WIDGET (box));
|
||||
|
||||
return gtk_box_layout_get_spacing (GTK_BOX_LAYOUT (box_layout));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1016,18 +477,14 @@ void
|
||||
gtk_box_set_baseline_position (GtkBox *box,
|
||||
GtkBaselinePosition position)
|
||||
{
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkLayoutManager *box_layout;
|
||||
|
||||
g_return_if_fail (GTK_IS_BOX (box));
|
||||
|
||||
if (priv->baseline_pos != position)
|
||||
{
|
||||
priv->baseline_pos = position;
|
||||
box_layout = gtk_widget_get_layout_manager (GTK_WIDGET (box));
|
||||
gtk_box_layout_set_baseline_position (GTK_BOX_LAYOUT (box_layout), position);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (box), props[PROP_BASELINE_POSITION]);
|
||||
|
||||
gtk_widget_queue_resize (GTK_WIDGET (box));
|
||||
}
|
||||
g_object_notify_by_pspec (G_OBJECT (box), props[PROP_BASELINE_POSITION]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1041,11 +498,13 @@ gtk_box_set_baseline_position (GtkBox *box,
|
||||
GtkBaselinePosition
|
||||
gtk_box_get_baseline_position (GtkBox *box)
|
||||
{
|
||||
GtkBoxPrivate *priv = gtk_box_get_instance_private (box);
|
||||
GtkLayoutManager *box_layout;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BOX (box), GTK_BASELINE_POSITION_CENTER);
|
||||
|
||||
return priv->baseline_pos;
|
||||
box_layout = gtk_widget_get_layout_manager (GTK_WIDGET (box));
|
||||
|
||||
return gtk_box_layout_get_baseline_position (GTK_BOX_LAYOUT (box_layout));
|
||||
}
|
||||
|
||||
static void
|
||||
|
871
gtk/gtkboxlayout.c
Normal file
871
gtk/gtkboxlayout.c
Normal file
@@ -0,0 +1,871 @@
|
||||
/* gtkboxlayout.c: Box layout manager
|
||||
*
|
||||
* Copyright 2019 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 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkboxlayout.h"
|
||||
|
||||
#include "gtkcsspositionvalueprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkorientable.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtksizerequest.h"
|
||||
#include "gtkstylecontextprivate.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkboxlayout
|
||||
* @Title: GtkBoxLayout
|
||||
* @Short_description: Layout manager for placing all children in a single row or column
|
||||
*
|
||||
* A GtkBoxLayout is a layout manager that arranges the children of any
|
||||
* widget using it into a single row or column, depending on the value
|
||||
* of its #GtkOrientable:orientation property. Within the other dimension
|
||||
* all children all allocated the same size. The GtkBoxLayout will respect
|
||||
* the #GtkWidget:halign and #GtkWidget:valign properties of each child
|
||||
* widget.
|
||||
*
|
||||
* If you want all children to be assigned the same size, you can use
|
||||
* the #GtkBoxLayout:homogeneous property.
|
||||
*
|
||||
* If you want to specify the amount of space placed between each child,
|
||||
* you can use the #GtkBoxLayout:spacing property.
|
||||
*/
|
||||
|
||||
struct _GtkBoxLayout
|
||||
{
|
||||
GtkLayoutManager parent_instance;
|
||||
|
||||
gboolean homogeneous;
|
||||
guint spacing;
|
||||
GtkOrientation orientation;
|
||||
GtkBaselinePosition baseline_position;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkBoxLayout, gtk_box_layout, GTK_TYPE_LAYOUT_MANAGER,
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
|
||||
|
||||
enum {
|
||||
PROP_HOMOGENEOUS = 1,
|
||||
PROP_SPACING,
|
||||
PROP_BASELINE_POSITION,
|
||||
|
||||
/* From GtkOrientable */
|
||||
PROP_ORIENTATION,
|
||||
|
||||
N_PROPS = PROP_ORIENTATION
|
||||
};
|
||||
|
||||
static GParamSpec *box_layout_props[N_PROPS];
|
||||
|
||||
static void
|
||||
gtk_box_layout_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkBoxLayout *self = GTK_BOX_LAYOUT (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_HOMOGENEOUS:
|
||||
gtk_box_layout_set_homogeneous (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_SPACING:
|
||||
gtk_box_layout_set_spacing (self, g_value_get_int (value));
|
||||
break;
|
||||
|
||||
case PROP_BASELINE_POSITION:
|
||||
gtk_box_layout_set_baseline_position (self, g_value_get_enum (value));
|
||||
break;
|
||||
|
||||
/* We cannot call gtk_orientable_set_orientation(), because
|
||||
* that will just call g_object_set() on this property
|
||||
*/
|
||||
case PROP_ORIENTATION:
|
||||
{
|
||||
GtkOrientation new_orientation = g_value_get_enum (value);
|
||||
|
||||
if (self->orientation != new_orientation)
|
||||
{
|
||||
self->orientation = new_orientation;
|
||||
|
||||
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (self));
|
||||
g_object_notify (gobject, "orientation");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_box_layout_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkBoxLayout *box_layout = GTK_BOX_LAYOUT (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_HOMOGENEOUS:
|
||||
g_value_set_boolean (value, box_layout->homogeneous);
|
||||
break;
|
||||
|
||||
case PROP_SPACING:
|
||||
g_value_set_int (value, box_layout->spacing);
|
||||
break;
|
||||
|
||||
case PROP_BASELINE_POSITION:
|
||||
g_value_set_enum (value, box_layout->baseline_position);
|
||||
break;
|
||||
|
||||
case PROP_ORIENTATION:
|
||||
g_value_set_enum (value, box_layout->orientation);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
count_expand_children (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
gint *visible_children,
|
||||
gint *expand_children)
|
||||
{
|
||||
GtkWidget *child;
|
||||
|
||||
*visible_children = *expand_children = 0;
|
||||
|
||||
for (child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
if (_gtk_widget_get_visible (child))
|
||||
{
|
||||
*visible_children += 1;
|
||||
|
||||
if (gtk_widget_compute_expand (child, orientation))
|
||||
*expand_children += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
get_spacing (GtkBoxLayout *self,
|
||||
GtkStyleContext *style_context)
|
||||
{
|
||||
GtkCssValue *border_spacing;
|
||||
gint css_spacing;
|
||||
|
||||
border_spacing = _gtk_style_context_peek_property (style_context, GTK_CSS_PROPERTY_BORDER_SPACING);
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
css_spacing = _gtk_css_position_value_get_x (border_spacing, 100);
|
||||
else
|
||||
css_spacing = _gtk_css_position_value_get_y (border_spacing, 100);
|
||||
|
||||
return css_spacing + self->spacing;
|
||||
}
|
||||
|
||||
static GtkSizeRequestMode
|
||||
gtk_box_layout_get_request_mode (GtkLayoutManager *layout_manager,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkBoxLayout *self = GTK_BOX_LAYOUT (layout_manager);
|
||||
|
||||
return self->orientation == GTK_ORIENTATION_HORIZONTAL
|
||||
? GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT
|
||||
: GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_box_layout_compute_size (GtkBoxLayout *self,
|
||||
GtkWidget *widget,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkWidget *child;
|
||||
int n_visible_children = 0;
|
||||
int required_min = 0, required_nat = 0;
|
||||
int largest_min = 0, largest_nat = 0;
|
||||
int spacing = get_spacing (self, _gtk_widget_get_style_context (widget));
|
||||
|
||||
for (child = gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
int child_min = 0;
|
||||
int child_nat = 0;
|
||||
|
||||
if (!_gtk_widget_get_visible (child))
|
||||
continue;
|
||||
|
||||
gtk_widget_measure (child, self->orientation,
|
||||
for_size,
|
||||
&child_min, &child_nat,
|
||||
NULL, NULL);
|
||||
|
||||
largest_min = MAX (largest_min, child_min);
|
||||
largest_nat = MAX (largest_nat, child_nat);
|
||||
|
||||
required_min += child_min;
|
||||
required_nat += child_nat;
|
||||
|
||||
n_visible_children += 1;
|
||||
}
|
||||
|
||||
if (n_visible_children > 0)
|
||||
{
|
||||
if (self->homogeneous)
|
||||
{
|
||||
required_min = largest_min * n_visible_children;
|
||||
required_nat = largest_nat * n_visible_children;
|
||||
}
|
||||
|
||||
required_min += (n_visible_children - 1) * spacing;
|
||||
required_nat += (n_visible_children - 1) * spacing;
|
||||
}
|
||||
|
||||
if (minimum != NULL)
|
||||
*minimum = required_min;
|
||||
if (natural != NULL)
|
||||
*natural = required_nat;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_box_layout_compute_opposite_size (GtkBoxLayout *self,
|
||||
GtkWidget *widget,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *min_baseline,
|
||||
int *nat_baseline)
|
||||
{
|
||||
GtkWidget *child;
|
||||
int nvis_children;
|
||||
int nexpand_children;
|
||||
int computed_minimum = 0, computed_natural = 0;
|
||||
int computed_minimum_above = 0, computed_natural_above = 0;
|
||||
int computed_minimum_below = 0, computed_natural_below = 0;
|
||||
int computed_minimum_baseline = -1, computed_natural_baseline = -1;
|
||||
GtkRequestedSize *sizes;
|
||||
int extra_space, size_given_to_child, i;
|
||||
int children_minimum_size = 0;
|
||||
int child_size, child_minimum, child_natural;
|
||||
int child_minimum_baseline, child_natural_baseline;
|
||||
int n_extra_widgets = 0;
|
||||
int spacing;
|
||||
gboolean have_baseline;
|
||||
|
||||
count_expand_children (widget, self->orientation, &nvis_children, &nexpand_children);
|
||||
|
||||
if (nvis_children <= 0)
|
||||
return;
|
||||
|
||||
spacing = get_spacing (self, _gtk_widget_get_style_context (widget));
|
||||
sizes = g_newa (GtkRequestedSize, nvis_children);
|
||||
extra_space = MAX (0, for_size - (nvis_children - 1) * spacing);
|
||||
|
||||
/* Retrieve desired size for visible children */
|
||||
for (i = 0, child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
if (_gtk_widget_get_visible (child))
|
||||
{
|
||||
gtk_widget_measure (child,
|
||||
self->orientation,
|
||||
-1,
|
||||
&sizes[i].minimum_size, &sizes[i].natural_size,
|
||||
NULL, NULL);
|
||||
|
||||
children_minimum_size += sizes[i].minimum_size;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->homogeneous)
|
||||
{
|
||||
/* We still need to run the above loop to populate the minimum sizes for
|
||||
* children that aren't going to fill.
|
||||
*/
|
||||
|
||||
size_given_to_child = extra_space / nvis_children;
|
||||
n_extra_widgets = extra_space % nvis_children;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Bring children up to size first */
|
||||
extra_space -= children_minimum_size;
|
||||
extra_space = MAX (0, extra_space);
|
||||
extra_space = gtk_distribute_natural_allocation (extra_space, nvis_children, sizes);
|
||||
|
||||
/* Calculate space which hasn't distributed yet,
|
||||
* and is available for expanding children.
|
||||
*/
|
||||
if (nexpand_children > 0)
|
||||
{
|
||||
size_given_to_child = extra_space / nexpand_children;
|
||||
n_extra_widgets = extra_space % nexpand_children;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_given_to_child = 0;
|
||||
}
|
||||
}
|
||||
|
||||
have_baseline = FALSE;
|
||||
for (i = 0, child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
/* If widget is not visible, skip it. */
|
||||
if (!_gtk_widget_get_visible (child))
|
||||
continue;
|
||||
|
||||
/* Assign the child's size. */
|
||||
if (self->homogeneous)
|
||||
{
|
||||
child_size = size_given_to_child;
|
||||
|
||||
if (n_extra_widgets > 0)
|
||||
{
|
||||
child_size++;
|
||||
n_extra_widgets--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
child_size = sizes[i].minimum_size;
|
||||
|
||||
if (gtk_widget_compute_expand (child, self->orientation))
|
||||
{
|
||||
child_size += size_given_to_child;
|
||||
|
||||
if (n_extra_widgets > 0)
|
||||
{
|
||||
child_size++;
|
||||
n_extra_widgets--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
child_minimum_baseline = child_natural_baseline = -1;
|
||||
/* Assign the child's position. */
|
||||
gtk_widget_measure (child,
|
||||
OPPOSITE_ORIENTATION (self->orientation),
|
||||
child_size,
|
||||
&child_minimum, &child_natural,
|
||||
&child_minimum_baseline, &child_natural_baseline);
|
||||
|
||||
if (child_minimum_baseline >= 0)
|
||||
{
|
||||
have_baseline = TRUE;
|
||||
computed_minimum_below = MAX (computed_minimum_below, child_minimum - child_minimum_baseline);
|
||||
computed_natural_below = MAX (computed_natural_below, child_natural - child_natural_baseline);
|
||||
computed_minimum_above = MAX (computed_minimum_above, child_minimum_baseline);
|
||||
computed_natural_above = MAX (computed_natural_above, child_natural_baseline);
|
||||
}
|
||||
else
|
||||
{
|
||||
computed_minimum = MAX (computed_minimum, child_minimum);
|
||||
computed_natural = MAX (computed_natural, child_natural);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (have_baseline)
|
||||
{
|
||||
computed_minimum = MAX (computed_minimum, computed_minimum_below + computed_minimum_above);
|
||||
computed_natural = MAX (computed_natural, computed_natural_below + computed_natural_above);
|
||||
switch (self->baseline_position)
|
||||
{
|
||||
case GTK_BASELINE_POSITION_TOP:
|
||||
computed_minimum_baseline = computed_minimum_above;
|
||||
computed_natural_baseline = computed_natural_above;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_CENTER:
|
||||
computed_minimum_baseline = computed_minimum_above + MAX((computed_minimum - (computed_minimum_above + computed_minimum_below)) / 2, 0);
|
||||
computed_natural_baseline = computed_natural_above + MAX((computed_natural - (computed_natural_above + computed_natural_below)) / 2, 0);
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_BOTTOM:
|
||||
computed_minimum_baseline = computed_minimum - computed_minimum_below;
|
||||
computed_natural_baseline = computed_natural - computed_natural_below;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (minimum != NULL)
|
||||
*minimum = computed_minimum;
|
||||
if (natural != NULL)
|
||||
*natural = MAX (computed_natural, computed_natural_below + computed_natural_above);
|
||||
|
||||
if (min_baseline != NULL)
|
||||
*min_baseline = computed_minimum_baseline;
|
||||
if (nat_baseline != NULL)
|
||||
*nat_baseline = computed_natural_baseline;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_box_layout_measure (GtkLayoutManager *layout_manager,
|
||||
GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *min_baseline,
|
||||
int *nat_baseline)
|
||||
{
|
||||
GtkBoxLayout *self = GTK_BOX_LAYOUT (layout_manager);
|
||||
|
||||
if (self->orientation != orientation)
|
||||
{
|
||||
gtk_box_layout_compute_opposite_size (self, widget, for_size,
|
||||
minimum, natural,
|
||||
min_baseline, nat_baseline);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_box_layout_compute_size (self, widget, for_size,
|
||||
minimum, natural);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_box_layout_allocate (GtkLayoutManager *layout_manager,
|
||||
GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkBoxLayout *self = GTK_BOX_LAYOUT (layout_manager);
|
||||
GtkWidget *child;
|
||||
gint nvis_children;
|
||||
gint nexpand_children;
|
||||
GtkTextDirection direction;
|
||||
GtkAllocation child_allocation;
|
||||
GtkRequestedSize *sizes;
|
||||
gint child_minimum_baseline, child_natural_baseline;
|
||||
gint minimum_above, natural_above;
|
||||
gint minimum_below, natural_below;
|
||||
gboolean have_baseline;
|
||||
gint extra_space;
|
||||
gint children_minimum_size = 0;
|
||||
gint size_given_to_child;
|
||||
gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */
|
||||
gint x = 0, y = 0, i;
|
||||
gint child_size;
|
||||
gint spacing;
|
||||
|
||||
count_expand_children (widget, self->orientation, &nvis_children, &nexpand_children);
|
||||
|
||||
/* If there is no visible child, simply return. */
|
||||
if (nvis_children <= 0)
|
||||
return;
|
||||
|
||||
direction = _gtk_widget_get_direction (widget);
|
||||
sizes = g_newa (GtkRequestedSize, nvis_children);
|
||||
spacing = get_spacing (self, _gtk_widget_get_style_context (widget));
|
||||
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
extra_space = width - (nvis_children - 1) * spacing;
|
||||
else
|
||||
extra_space = height - (nvis_children - 1) * spacing;
|
||||
|
||||
have_baseline = FALSE;
|
||||
minimum_above = natural_above = 0;
|
||||
minimum_below = natural_below = 0;
|
||||
|
||||
/* Retrieve desired size for visible children. */
|
||||
for (i = 0, child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
if (!_gtk_widget_get_visible (child))
|
||||
continue;
|
||||
|
||||
gtk_widget_measure (child,
|
||||
self->orientation,
|
||||
self->orientation == GTK_ORIENTATION_HORIZONTAL ? height : width,
|
||||
&sizes[i].minimum_size, &sizes[i].natural_size,
|
||||
NULL, NULL);
|
||||
|
||||
children_minimum_size += sizes[i].minimum_size;
|
||||
|
||||
sizes[i].data = child;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (self->homogeneous)
|
||||
{
|
||||
/* We still need to run the above loop to populate the minimum sizes for
|
||||
* children that aren't going to fill.
|
||||
*/
|
||||
|
||||
size_given_to_child = extra_space / nvis_children;
|
||||
n_extra_widgets = extra_space % nvis_children;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Bring children up to size first */
|
||||
extra_space -= children_minimum_size;
|
||||
extra_space = MAX (0, extra_space);
|
||||
extra_space = gtk_distribute_natural_allocation (extra_space, nvis_children, sizes);
|
||||
|
||||
/* Calculate space which hasn't distributed yet,
|
||||
* and is available for expanding children.
|
||||
*/
|
||||
if (nexpand_children > 0)
|
||||
{
|
||||
size_given_to_child = extra_space / nexpand_children;
|
||||
n_extra_widgets = extra_space % nexpand_children;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_given_to_child = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate child sizes. */
|
||||
for (i = 0, child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
/* If widget is not visible, skip it. */
|
||||
if (!_gtk_widget_get_visible (child))
|
||||
continue;
|
||||
|
||||
/* Assign the child's size. */
|
||||
if (self->homogeneous)
|
||||
{
|
||||
child_size = size_given_to_child;
|
||||
|
||||
if (n_extra_widgets > 0)
|
||||
{
|
||||
child_size++;
|
||||
n_extra_widgets--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
child_size = sizes[i].minimum_size;
|
||||
|
||||
if (gtk_widget_compute_expand (child, self->orientation))
|
||||
{
|
||||
child_size += size_given_to_child;
|
||||
|
||||
if (n_extra_widgets > 0)
|
||||
{
|
||||
child_size++;
|
||||
n_extra_widgets--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sizes[i].natural_size = child_size;
|
||||
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL &&
|
||||
gtk_widget_get_valign (child) == GTK_ALIGN_BASELINE)
|
||||
{
|
||||
int child_allocation_width;
|
||||
int child_minimum_height, child_natural_height;
|
||||
|
||||
child_allocation_width = child_size;
|
||||
|
||||
child_minimum_baseline = -1;
|
||||
child_natural_baseline = -1;
|
||||
gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL,
|
||||
child_allocation_width,
|
||||
&child_minimum_height, &child_natural_height,
|
||||
&child_minimum_baseline, &child_natural_baseline);
|
||||
|
||||
if (child_minimum_baseline >= 0)
|
||||
{
|
||||
have_baseline = TRUE;
|
||||
minimum_below = MAX (minimum_below, child_minimum_height - child_minimum_baseline);
|
||||
natural_below = MAX (natural_below, child_natural_height - child_natural_baseline);
|
||||
minimum_above = MAX (minimum_above, child_minimum_baseline);
|
||||
natural_above = MAX (natural_above, child_natural_baseline);
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (self->orientation == GTK_ORIENTATION_VERTICAL)
|
||||
baseline = -1;
|
||||
|
||||
/* we only calculate our own baseline if we don't get one passed from the parent
|
||||
* and any of the child widgets explicitly request one */
|
||||
if (baseline == -1 && have_baseline)
|
||||
{
|
||||
/* TODO: This is purely based on the minimum baseline, when things fit we should
|
||||
use the natural one? */
|
||||
|
||||
switch (self->baseline_position)
|
||||
{
|
||||
case GTK_BASELINE_POSITION_TOP:
|
||||
baseline = minimum_above;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_CENTER:
|
||||
baseline = minimum_above + (height - (minimum_above + minimum_below)) / 2;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_BOTTOM:
|
||||
baseline = height - minimum_below;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate child positions. */
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
child_allocation.y = 0;
|
||||
child_allocation.height = height;
|
||||
x = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
child_allocation.x = 0;
|
||||
child_allocation.width = width;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
for (i = 0, child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
/* If widget is not visible, skip it. */
|
||||
if (!_gtk_widget_get_visible (child))
|
||||
continue;
|
||||
|
||||
child_size = sizes[i].natural_size;
|
||||
|
||||
/* Assign the child's position. */
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
child_allocation.width = child_size;
|
||||
child_allocation.x = x;
|
||||
|
||||
x += child_size + spacing;
|
||||
|
||||
if (direction == GTK_TEXT_DIR_RTL)
|
||||
child_allocation.x = width - child_allocation.x - child_allocation.width;
|
||||
|
||||
}
|
||||
else /* (self->orientation == GTK_ORIENTATION_VERTICAL) */
|
||||
{
|
||||
child_allocation.height = child_size;
|
||||
child_allocation.y = y;
|
||||
|
||||
y += child_size + spacing;
|
||||
}
|
||||
|
||||
gtk_widget_size_allocate (child, &child_allocation, baseline);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_box_layout_class_init (GtkBoxLayoutClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GtkLayoutManagerClass *layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = gtk_box_layout_set_property;
|
||||
gobject_class->get_property = gtk_box_layout_get_property;
|
||||
|
||||
layout_manager_class->get_request_mode = gtk_box_layout_get_request_mode;
|
||||
layout_manager_class->measure = gtk_box_layout_measure;
|
||||
layout_manager_class->allocate = gtk_box_layout_allocate;
|
||||
|
||||
/**
|
||||
* GtkBoxLayout:homogeneous:
|
||||
*
|
||||
* Whether the box layout should distribute the available space
|
||||
* homogeneously among the children of the widget using it as a
|
||||
* layout manager.
|
||||
*/
|
||||
box_layout_props[PROP_HOMOGENEOUS] =
|
||||
g_param_spec_boolean ("homogeneous",
|
||||
P_("Homogeneous"),
|
||||
P_("Distribute space homogeneously"),
|
||||
FALSE,
|
||||
GTK_PARAM_READWRITE |
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkBoxLayout:spacing:
|
||||
*
|
||||
* The space between each child of the widget using the box
|
||||
* layout as its layout manager.
|
||||
*/
|
||||
box_layout_props[PROP_SPACING] =
|
||||
g_param_spec_int ("spacing",
|
||||
P_("Spacing"),
|
||||
P_("Spacing between widgets"),
|
||||
0, G_MAXINT, 0,
|
||||
GTK_PARAM_READWRITE |
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkBoxLayout:baseline-position:
|
||||
*
|
||||
* The position of the allocated baseline within the extra space
|
||||
* allocated to each child of the widget using a box layout
|
||||
* manager.
|
||||
*
|
||||
* This property is only relevant for horizontal layouts containing
|
||||
* at least one child with a baseline alignment.
|
||||
*/
|
||||
box_layout_props[PROP_BASELINE_POSITION] =
|
||||
g_param_spec_enum ("baseline-position",
|
||||
P_("Baseline position"),
|
||||
P_("The position of the baseline aligned widgets if extra space is available"),
|
||||
GTK_TYPE_BASELINE_POSITION,
|
||||
GTK_BASELINE_POSITION_CENTER,
|
||||
GTK_PARAM_READWRITE |
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, box_layout_props);
|
||||
g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_box_layout_init (GtkBoxLayout *self)
|
||||
{
|
||||
self->homogeneous = FALSE;
|
||||
self->spacing = 0;
|
||||
self->orientation = GTK_ORIENTATION_HORIZONTAL;
|
||||
self->baseline_position = GTK_BASELINE_POSITION_CENTER;
|
||||
}
|
||||
|
||||
GtkLayoutManager *
|
||||
gtk_box_layout_new (GtkOrientation orientation)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_BOX_LAYOUT,
|
||||
"orientation", orientation,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_box_layout_set_homogeneous (GtkBoxLayout *box_layout,
|
||||
gboolean homogeneous)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_BOX_LAYOUT (box_layout));
|
||||
|
||||
homogeneous = !!homogeneous;
|
||||
if (box_layout->homogeneous == homogeneous)
|
||||
return;
|
||||
|
||||
box_layout->homogeneous = homogeneous;
|
||||
|
||||
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (box_layout));
|
||||
g_object_notify_by_pspec (G_OBJECT (box_layout), box_layout_props[PROP_HOMOGENEOUS]);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_box_layout_get_homogeneous (GtkBoxLayout *box_layout)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BOX_LAYOUT (box_layout), FALSE);
|
||||
|
||||
return box_layout->homogeneous;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_box_layout_set_spacing (GtkBoxLayout *box_layout,
|
||||
guint spacing)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_BOX_LAYOUT (box_layout));
|
||||
|
||||
if (box_layout->spacing == spacing)
|
||||
return;
|
||||
|
||||
box_layout->spacing = spacing;
|
||||
|
||||
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (box_layout));
|
||||
g_object_notify_by_pspec (G_OBJECT (box_layout), box_layout_props[PROP_SPACING]);
|
||||
}
|
||||
|
||||
guint
|
||||
gtk_box_layout_get_spacing (GtkBoxLayout *box_layout)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BOX_LAYOUT (box_layout), 0);
|
||||
|
||||
return box_layout->spacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_box_layout_set_baseline_position:
|
||||
* @box_layout: a #GtkBoxLayout
|
||||
* @position: a #GtkBaselinePosition
|
||||
*
|
||||
* Sets the baseline position of a box layout.
|
||||
*
|
||||
* The baseline position affects only horizontal boxes with at least one
|
||||
* baseline aligned child. If there is more vertical space available than
|
||||
* requested, and the baseline is not allocated by the parent then the
|
||||
* given @position is used to allocate the baseline within the extra
|
||||
* space available.
|
||||
*/
|
||||
void
|
||||
gtk_box_layout_set_baseline_position (GtkBoxLayout *box_layout,
|
||||
GtkBaselinePosition position)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_BOX_LAYOUT (box_layout));
|
||||
|
||||
if (box_layout->baseline_position != position)
|
||||
{
|
||||
box_layout->baseline_position = position;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (box_layout), box_layout_props[PROP_BASELINE_POSITION]);
|
||||
|
||||
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (box_layout));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_box_layout_get_baseline_position:
|
||||
* @box_layout: a #GtkBoxLayout
|
||||
*
|
||||
* Gets the value set by gtk_box_layout_set_baseline_position().
|
||||
*
|
||||
* Returns: the baseline position
|
||||
*/
|
||||
GtkBaselinePosition
|
||||
gtk_box_layout_get_baseline_position (GtkBoxLayout *box_layout)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BOX_LAYOUT (box_layout), GTK_BASELINE_POSITION_CENTER);
|
||||
|
||||
return box_layout->baseline_position;
|
||||
}
|
54
gtk/gtkboxlayout.h
Normal file
54
gtk/gtkboxlayout.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* gtkboxlayout.h: Box layout manager
|
||||
*
|
||||
* Copyright 2019 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 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkenums.h>
|
||||
#include <gtk/gtklayoutmanager.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_BOX_LAYOUT (gtk_box_layout_get_type())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkBoxLayout, gtk_box_layout, GTK, BOX_LAYOUT, GtkLayoutManager)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkLayoutManager * gtk_box_layout_new (GtkOrientation orientation);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_box_layout_set_homogeneous (GtkBoxLayout *box_layout,
|
||||
gboolean homogeneous);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_box_layout_get_homogeneous (GtkBoxLayout *box_layout);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_box_layout_set_spacing (GtkBoxLayout *box_layout,
|
||||
guint spacing);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_box_layout_get_spacing (GtkBoxLayout *box_layout);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_box_layout_set_baseline_position (GtkBoxLayout *box_layout,
|
||||
GtkBaselinePosition position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBaselinePosition gtk_box_layout_get_baseline_position (GtkBoxLayout *box_layout);
|
||||
|
||||
G_END_DECLS
|
@@ -54,6 +54,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "gtkcenterbox.h"
|
||||
#include "gtkcenterlayout.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkorientable.h"
|
||||
@@ -73,7 +74,6 @@ struct _GtkCenterBox
|
||||
GtkWidget *end_widget;
|
||||
|
||||
GtkOrientation orientation;
|
||||
GtkBaselinePosition baseline_pos;
|
||||
};
|
||||
|
||||
struct _GtkCenterBoxClass
|
||||
@@ -120,446 +120,6 @@ gtk_center_box_buildable_init (GtkBuildableIface *iface)
|
||||
iface->add_child = gtk_center_box_buildable_add_child;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
get_expand (GtkWidget *widget,
|
||||
GtkOrientation orientation)
|
||||
{
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
return gtk_widget_get_hexpand (widget);
|
||||
else
|
||||
return gtk_widget_get_vexpand (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_box_distribute (GtkCenterBox *self,
|
||||
gint for_size,
|
||||
gint size,
|
||||
GtkRequestedSize *sizes)
|
||||
{
|
||||
int center_size = 0;
|
||||
int start_size = 0;
|
||||
int end_size = 0;
|
||||
gboolean center_expand = FALSE;
|
||||
gboolean start_expand = FALSE;
|
||||
gboolean end_expand = FALSE;
|
||||
int avail;
|
||||
|
||||
sizes[0].minimum_size = sizes[0].natural_size = 0;
|
||||
sizes[1].minimum_size = sizes[1].natural_size = 0;
|
||||
sizes[2].minimum_size = sizes[2].natural_size = 0;
|
||||
|
||||
if (self->start_widget)
|
||||
gtk_widget_measure (self->start_widget,
|
||||
self->orientation,
|
||||
for_size,
|
||||
&sizes[0].minimum_size, &sizes[0].natural_size,
|
||||
NULL, NULL);
|
||||
|
||||
if (self->center_widget)
|
||||
gtk_widget_measure (self->center_widget,
|
||||
self->orientation,
|
||||
for_size,
|
||||
&sizes[1].minimum_size, &sizes[1].natural_size,
|
||||
NULL, NULL);
|
||||
|
||||
|
||||
if (self->end_widget)
|
||||
gtk_widget_measure (self->end_widget,
|
||||
self->orientation,
|
||||
for_size,
|
||||
&sizes[2].minimum_size, &sizes[2].natural_size,
|
||||
NULL, NULL);
|
||||
|
||||
if (self->center_widget)
|
||||
{
|
||||
center_size = CLAMP (size - (sizes[0].minimum_size + sizes[2].minimum_size), sizes[1].minimum_size, sizes[1].natural_size);
|
||||
center_expand = get_expand (self->center_widget, self->orientation);
|
||||
}
|
||||
|
||||
if (self->start_widget)
|
||||
{
|
||||
avail = MIN ((size - center_size) / 2, size - (center_size + sizes[2].minimum_size));
|
||||
start_size = CLAMP (avail, sizes[0].minimum_size, sizes[0].natural_size);
|
||||
start_expand = get_expand (self->start_widget, self->orientation);
|
||||
}
|
||||
|
||||
if (self->end_widget)
|
||||
{
|
||||
avail = MIN ((size - center_size) / 2, size - (center_size + sizes[0].minimum_size));
|
||||
end_size = CLAMP (avail, sizes[2].minimum_size, sizes[2].natural_size);
|
||||
end_expand = get_expand (self->end_widget, self->orientation);
|
||||
}
|
||||
|
||||
if (self->center_widget)
|
||||
{
|
||||
int center_pos;
|
||||
|
||||
center_pos = (size / 2) - (center_size / 2);
|
||||
|
||||
/* Push in from start/end */
|
||||
if (start_size > center_pos)
|
||||
center_pos = start_size;
|
||||
else if (size - end_size < center_pos + center_size)
|
||||
center_pos = size - center_size - end_size;
|
||||
else if (center_expand)
|
||||
{
|
||||
center_size = size - 2 * MAX (start_size, end_size);
|
||||
center_pos = (size / 2) - (center_size / 2);
|
||||
}
|
||||
|
||||
if (start_expand)
|
||||
start_size = center_pos;
|
||||
|
||||
if (end_expand)
|
||||
end_size = size - (center_pos + center_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
avail = size - (start_size + end_size);
|
||||
if (start_expand && end_expand)
|
||||
{
|
||||
start_size += avail / 2;
|
||||
end_size += avail / 2;
|
||||
}
|
||||
else if (start_expand)
|
||||
{
|
||||
start_size += avail;
|
||||
}
|
||||
else if (end_expand)
|
||||
{
|
||||
end_size += avail;
|
||||
}
|
||||
}
|
||||
|
||||
sizes[0].minimum_size = start_size;
|
||||
sizes[1].minimum_size = center_size;
|
||||
sizes[2].minimum_size = end_size;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_box_measure_orientation (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkCenterBox *self = GTK_CENTER_BOX (widget);
|
||||
int min_baseline, nat_baseline;
|
||||
int start_min = 0;
|
||||
int start_nat = 0;
|
||||
int center_min = 0;
|
||||
int center_nat = 0;
|
||||
int end_min = 0;
|
||||
int end_nat = 0;
|
||||
|
||||
if (self->start_widget)
|
||||
gtk_widget_measure (self->start_widget,
|
||||
orientation,
|
||||
for_size,
|
||||
&start_min, &start_nat,
|
||||
&min_baseline, &nat_baseline);
|
||||
|
||||
if (self->center_widget)
|
||||
gtk_widget_measure (self->center_widget,
|
||||
orientation,
|
||||
for_size,
|
||||
¢er_min, ¢er_nat,
|
||||
&min_baseline, &nat_baseline);
|
||||
|
||||
if (self->end_widget)
|
||||
gtk_widget_measure (self->end_widget,
|
||||
orientation,
|
||||
for_size,
|
||||
&end_min, &end_nat,
|
||||
&min_baseline, &nat_baseline);
|
||||
|
||||
*minimum = start_min + center_min + end_min;
|
||||
*natural = center_nat + 2 * MAX (start_nat, end_nat);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_box_measure_opposite (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkCenterBox *self = GTK_CENTER_BOX (widget);
|
||||
int child_min, child_nat;
|
||||
int child_min_baseline, child_nat_baseline;
|
||||
int total_min, above_min, below_min;
|
||||
int total_nat, above_nat, below_nat;
|
||||
GtkWidget *child[3];
|
||||
GtkRequestedSize sizes[3];
|
||||
int i;
|
||||
|
||||
child[0] = self->start_widget;
|
||||
child[1] = self->center_widget;
|
||||
child[2] = self->end_widget;
|
||||
|
||||
if (for_size >= 0)
|
||||
gtk_center_box_distribute (self, -1, for_size, sizes);
|
||||
|
||||
above_min = below_min = above_nat = below_nat = -1;
|
||||
total_min = total_nat = 0;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
if (child[i] == NULL)
|
||||
continue;
|
||||
|
||||
gtk_widget_measure (child[i],
|
||||
orientation,
|
||||
for_size >= 0 ? sizes[i].minimum_size : -1,
|
||||
&child_min, &child_nat,
|
||||
&child_min_baseline, &child_nat_baseline);
|
||||
|
||||
if (child_min_baseline >= 0)
|
||||
{
|
||||
below_min = MAX (below_min, child_min - child_min_baseline);
|
||||
above_min = MAX (above_min, child_min_baseline);
|
||||
below_nat = MAX (below_nat, child_nat - child_nat_baseline);
|
||||
above_nat = MAX (above_nat, child_nat_baseline);
|
||||
}
|
||||
else
|
||||
{
|
||||
total_min = MAX (total_min, child_min);
|
||||
total_nat = MAX (total_nat, child_nat);
|
||||
}
|
||||
}
|
||||
|
||||
if (above_min >= 0)
|
||||
{
|
||||
int min_baseline = -1;
|
||||
int nat_baseline = -1;
|
||||
|
||||
total_min = MAX (total_min, above_min + below_min);
|
||||
total_nat = MAX (total_nat, above_nat + below_nat);
|
||||
|
||||
switch (self->baseline_pos)
|
||||
{
|
||||
case GTK_BASELINE_POSITION_TOP:
|
||||
min_baseline = above_min;
|
||||
nat_baseline = above_nat;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_CENTER:
|
||||
min_baseline = above_min + (total_min - (above_min + below_min)) / 2;
|
||||
nat_baseline = above_nat + (total_nat - (above_nat + below_nat)) / 2;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_BOTTOM:
|
||||
min_baseline = total_min - below_min;
|
||||
nat_baseline = total_nat - below_nat;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (minimum_baseline)
|
||||
*minimum_baseline = min_baseline;
|
||||
if (natural_baseline)
|
||||
*natural_baseline = nat_baseline;
|
||||
}
|
||||
|
||||
*minimum = total_min;
|
||||
*natural = total_nat;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_box_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkCenterBox *self = GTK_CENTER_BOX (widget);
|
||||
|
||||
if (self->orientation == orientation)
|
||||
gtk_center_box_measure_orientation (widget, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
else
|
||||
gtk_center_box_measure_opposite (widget, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_box_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkCenterBox *self = GTK_CENTER_BOX (widget);
|
||||
GtkAllocation child_allocation;
|
||||
GtkWidget *child[3];
|
||||
int child_size[3];
|
||||
int child_pos[3];
|
||||
GtkRequestedSize sizes[3];
|
||||
int size;
|
||||
int for_size;
|
||||
int i;
|
||||
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
size = width;
|
||||
for_size = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
size = height;
|
||||
for_size = width;
|
||||
baseline = -1;
|
||||
}
|
||||
|
||||
/* Allocate child sizes */
|
||||
|
||||
gtk_center_box_distribute (self, for_size, size, sizes);
|
||||
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL &&
|
||||
gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
|
||||
{
|
||||
child[0] = self->end_widget;
|
||||
child[1] = self->center_widget;
|
||||
child[2] = self->start_widget;
|
||||
child_size[0] = sizes[2].minimum_size;
|
||||
child_size[1] = sizes[1].minimum_size;
|
||||
child_size[2] = sizes[0].minimum_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
child[0] = self->start_widget;
|
||||
child[1] = self->center_widget;
|
||||
child[2] = self->end_widget;
|
||||
child_size[0] = sizes[0].minimum_size;
|
||||
child_size[1] = sizes[1].minimum_size;
|
||||
child_size[2] = sizes[2].minimum_size;
|
||||
}
|
||||
|
||||
/* Determine baseline */
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL &&
|
||||
baseline == -1)
|
||||
{
|
||||
int min_above, nat_above;
|
||||
int min_below, nat_below;
|
||||
gboolean have_baseline;
|
||||
|
||||
have_baseline = FALSE;
|
||||
min_above = nat_above = 0;
|
||||
min_below = nat_below = 0;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
if (child[i] && gtk_widget_get_valign (child[i]) == GTK_ALIGN_BASELINE)
|
||||
{
|
||||
int child_min_height, child_nat_height;
|
||||
int child_min_baseline, child_nat_baseline;
|
||||
|
||||
child_min_baseline = child_nat_baseline = -1;
|
||||
|
||||
gtk_widget_measure (child[i], GTK_ORIENTATION_VERTICAL,
|
||||
child_size[i],
|
||||
&child_min_height, &child_nat_height,
|
||||
&child_min_baseline, &child_nat_baseline);
|
||||
|
||||
if (child_min_baseline >= 0)
|
||||
{
|
||||
have_baseline = TRUE;
|
||||
min_below = MAX (min_below, child_min_height - child_min_baseline);
|
||||
nat_below = MAX (nat_below, child_nat_height - child_nat_baseline);
|
||||
min_above = MAX (min_above, child_min_baseline);
|
||||
nat_above = MAX (nat_above, child_nat_baseline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (have_baseline)
|
||||
{
|
||||
/* TODO: This is purely based on the minimum baseline.
|
||||
* When things fit we should use the natural one
|
||||
*/
|
||||
switch (self->baseline_pos)
|
||||
{
|
||||
default:
|
||||
case GTK_BASELINE_POSITION_TOP:
|
||||
baseline = min_above;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_CENTER:
|
||||
baseline = min_above + (height - (min_above + min_below)) / 2;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_BOTTOM:
|
||||
baseline = height - min_below;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate child positions */
|
||||
|
||||
child_pos[0] = 0;
|
||||
child_pos[1] = (size / 2) - (child_size[1] / 2);
|
||||
child_pos[2] = size - child_size[2];
|
||||
|
||||
if (child[1])
|
||||
{
|
||||
/* Push in from start/end */
|
||||
if (child_size[0] > child_pos[1])
|
||||
child_pos[1] = child_size[0];
|
||||
else if (size - child_size[2] < child_pos[1] + child_size[1])
|
||||
child_pos[1] = size - child_size[1] - child_size[2];
|
||||
}
|
||||
|
||||
child_allocation = (GtkAllocation) { 0, 0, width, height };
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
if (child[i] == NULL)
|
||||
continue;
|
||||
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
child_allocation.x = child_pos[i];
|
||||
child_allocation.y = 0;
|
||||
child_allocation.width = child_size[i];
|
||||
child_allocation.height = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
child_allocation.x = 0;
|
||||
child_allocation.y = child_pos[i];
|
||||
child_allocation.width = width;
|
||||
child_allocation.height = child_size[i];
|
||||
}
|
||||
|
||||
gtk_widget_size_allocate (child[i], &child_allocation, baseline);
|
||||
}
|
||||
}
|
||||
|
||||
static GtkSizeRequestMode
|
||||
gtk_center_box_get_request_mode (GtkWidget *widget)
|
||||
{
|
||||
GtkCenterBox *self = GTK_CENTER_BOX (widget);
|
||||
gint count[3] = { 0, 0, 0 };
|
||||
|
||||
if (self->start_widget)
|
||||
count[gtk_widget_get_request_mode (self->start_widget)]++;
|
||||
|
||||
if (self->center_widget)
|
||||
count[gtk_widget_get_request_mode (self->center_widget)]++;
|
||||
|
||||
if (self->end_widget)
|
||||
count[gtk_widget_get_request_mode (self->end_widget)]++;
|
||||
|
||||
if (!count[GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH] &&
|
||||
!count[GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT])
|
||||
return GTK_SIZE_REQUEST_CONSTANT_SIZE;
|
||||
else
|
||||
return count[GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT] > count[GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH]
|
||||
? GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT
|
||||
: GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_box_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
@@ -567,11 +127,12 @@ gtk_center_box_set_property (GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkCenterBox *self = GTK_CENTER_BOX (object);
|
||||
GtkLayoutManager *layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_BASELINE_POSITION:
|
||||
gtk_center_box_set_baseline_position (self, g_value_get_enum (value));
|
||||
gtk_center_layout_set_baseline_position (GTK_CENTER_LAYOUT (layout), g_value_get_enum (value));
|
||||
break;
|
||||
|
||||
case PROP_ORIENTATION:
|
||||
@@ -600,11 +161,12 @@ gtk_center_box_get_property (GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkCenterBox *self = GTK_CENTER_BOX (object);
|
||||
GtkLayoutManager *layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_BASELINE_POSITION:
|
||||
g_value_set_enum (value, self->baseline_pos);
|
||||
g_value_set_enum (value, gtk_center_layout_get_baseline_position (GTK_CENTER_LAYOUT (layout)));
|
||||
break;
|
||||
|
||||
case PROP_ORIENTATION:
|
||||
@@ -639,10 +201,6 @@ gtk_center_box_class_init (GtkCenterBoxClass *klass)
|
||||
object_class->get_property = gtk_center_box_get_property;
|
||||
object_class->dispose = gtk_center_box_dispose;
|
||||
|
||||
widget_class->measure = gtk_center_box_measure;
|
||||
widget_class->size_allocate = gtk_center_box_size_allocate;
|
||||
widget_class->get_request_mode = gtk_center_box_get_request_mode;
|
||||
|
||||
g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
|
||||
|
||||
g_object_class_install_property (object_class, PROP_BASELINE_POSITION,
|
||||
@@ -661,14 +219,18 @@ gtk_center_box_class_init (GtkCenterBoxClass *klass)
|
||||
static void
|
||||
gtk_center_box_init (GtkCenterBox *self)
|
||||
{
|
||||
GtkLayoutManager *layout = gtk_center_layout_new (GTK_ORIENTATION_HORIZONTAL);
|
||||
|
||||
gtk_widget_set_has_surface (GTK_WIDGET (self), FALSE);
|
||||
|
||||
gtk_widget_set_layout_manager (GTK_WIDGET (self), layout);
|
||||
|
||||
self->start_widget = NULL;
|
||||
self->center_widget = NULL;
|
||||
self->end_widget = NULL;
|
||||
|
||||
self->orientation = GTK_ORIENTATION_HORIZONTAL;
|
||||
self->baseline_pos = GTK_BASELINE_POSITION_CENTER;
|
||||
_gtk_orientable_set_style_classes (GTK_ORIENTABLE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -700,7 +262,11 @@ gtk_center_box_set_start_widget (GtkCenterBox *self,
|
||||
|
||||
self->start_widget = child;
|
||||
if (child)
|
||||
gtk_widget_insert_after (child, GTK_WIDGET (self), NULL);
|
||||
{
|
||||
GtkLayoutManager *manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
|
||||
gtk_widget_insert_after (child, GTK_WIDGET (self), NULL);
|
||||
gtk_center_layout_set_start_widget (GTK_CENTER_LAYOUT (manager), child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -719,7 +285,11 @@ gtk_center_box_set_center_widget (GtkCenterBox *self,
|
||||
|
||||
self->center_widget = child;
|
||||
if (child)
|
||||
gtk_widget_insert_after (child, GTK_WIDGET (self), self->start_widget);
|
||||
{
|
||||
GtkLayoutManager *manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
|
||||
gtk_widget_insert_after (child, GTK_WIDGET (self), self->start_widget);
|
||||
gtk_center_layout_set_center_widget (GTK_CENTER_LAYOUT (manager), child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -738,7 +308,11 @@ gtk_center_box_set_end_widget (GtkCenterBox *self,
|
||||
|
||||
self->end_widget = child;
|
||||
if (child)
|
||||
gtk_widget_insert_before (child, GTK_WIDGET (self), NULL);
|
||||
{
|
||||
GtkLayoutManager *manager = gtk_widget_get_layout_manager (GTK_WIDGET (self));
|
||||
gtk_widget_insert_before (child, GTK_WIDGET (self), NULL);
|
||||
gtk_center_layout_set_end_widget (GTK_CENTER_LAYOUT (manager), child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -800,14 +374,14 @@ void
|
||||
gtk_center_box_set_baseline_position (GtkCenterBox *self,
|
||||
GtkBaselinePosition position)
|
||||
{
|
||||
GtkLayoutManager *layout;
|
||||
|
||||
g_return_if_fail (GTK_IS_CENTER_BOX (self));
|
||||
|
||||
if (self->baseline_pos != position)
|
||||
{
|
||||
self->baseline_pos = position;
|
||||
g_object_notify (G_OBJECT (self), "baseline-position");
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
}
|
||||
layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
|
||||
gtk_center_layout_set_baseline_position (GTK_CENTER_LAYOUT (layout), position);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "baseline-position");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -821,8 +395,12 @@ gtk_center_box_set_baseline_position (GtkCenterBox *self,
|
||||
GtkBaselinePosition
|
||||
gtk_center_box_get_baseline_position (GtkCenterBox *self)
|
||||
{
|
||||
GtkLayoutManager *layout;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_CENTER_BOX (self), GTK_BASELINE_POSITION_CENTER);
|
||||
|
||||
return self->baseline_pos;
|
||||
layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
|
||||
|
||||
return gtk_center_layout_get_baseline_position (GTK_CENTER_LAYOUT (layout));
|
||||
}
|
||||
|
||||
|
699
gtk/gtkcenterlayout.c
Normal file
699
gtk/gtkcenterlayout.c
Normal file
@@ -0,0 +1,699 @@
|
||||
/* gtkcenterlayout.c: Center layout manager
|
||||
*
|
||||
* Copyright 2019 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 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcenterlayout.h"
|
||||
|
||||
#include "gtkcsspositionvalueprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkorientable.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtksizerequest.h"
|
||||
#include "gtkstylecontextprivate.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcenterlayout
|
||||
* @Title: GtkCenterLayout
|
||||
* @Short_description: Layout manager for placing all children in a single row or column
|
||||
*
|
||||
* A GtkCenterLayout is a layout manager that arranges the children of any
|
||||
* widget using it into a single row or column, depending on the value
|
||||
* of its #GtkOrientable:orientation property. Within the other dimension
|
||||
* all children all allocated the same size. The GtkCenterLayout will respect
|
||||
* the #GtkWidget:halign and #GtkWidget:valign properties of each child
|
||||
* widget.
|
||||
*
|
||||
* If you want all children to be assigned the same size, you can use
|
||||
* the #GtkCenterLayout:homogeneous property.
|
||||
*
|
||||
* If you want to specify the amount of space placed between each child,
|
||||
* you can use the #GtkCenterLayout:spacing property.
|
||||
*/
|
||||
|
||||
struct _GtkCenterLayout
|
||||
{
|
||||
GtkLayoutManager parent_instance;
|
||||
|
||||
GtkOrientation orientation;
|
||||
GtkBaselinePosition baseline_position;
|
||||
|
||||
GtkWidget *start_widget;
|
||||
GtkWidget *center_widget;
|
||||
GtkWidget *end_widget;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkCenterLayout, gtk_center_layout, GTK_TYPE_LAYOUT_MANAGER,
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
|
||||
|
||||
enum {
|
||||
PROP_BASELINE_POSITION = 1,
|
||||
|
||||
/* From GtkOrientable */
|
||||
PROP_ORIENTATION,
|
||||
|
||||
N_PROPS = PROP_ORIENTATION
|
||||
};
|
||||
|
||||
static GParamSpec *center_layout_props[N_PROPS];
|
||||
|
||||
static void
|
||||
gtk_center_layout_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkCenterLayout *self = GTK_CENTER_LAYOUT (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_BASELINE_POSITION:
|
||||
gtk_center_layout_set_baseline_position (self, g_value_get_enum (value));
|
||||
break;
|
||||
|
||||
/* We cannot call gtk_orientable_set_orientation(), because
|
||||
* that will just call g_object_set() on this property
|
||||
*/
|
||||
case PROP_ORIENTATION:
|
||||
{
|
||||
GtkOrientation new_orientation = g_value_get_enum (value);
|
||||
|
||||
if (self->orientation != new_orientation)
|
||||
{
|
||||
self->orientation = new_orientation;
|
||||
|
||||
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (self));
|
||||
g_object_notify (gobject, "orientation");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_layout_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkCenterLayout *center_layout = GTK_CENTER_LAYOUT (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_BASELINE_POSITION:
|
||||
g_value_set_enum (value, center_layout->baseline_position);
|
||||
break;
|
||||
|
||||
case PROP_ORIENTATION:
|
||||
g_value_set_enum (value, center_layout->orientation);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GtkSizeRequestMode
|
||||
gtk_center_layout_get_request_mode (GtkLayoutManager *layout_manager,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkCenterLayout *self = GTK_CENTER_LAYOUT (layout_manager);
|
||||
gint count[3] = { 0, 0, 0 };
|
||||
|
||||
if (self->start_widget)
|
||||
count[gtk_widget_get_request_mode (self->start_widget)]++;
|
||||
|
||||
if (self->center_widget)
|
||||
count[gtk_widget_get_request_mode (self->center_widget)]++;
|
||||
|
||||
if (self->end_widget)
|
||||
count[gtk_widget_get_request_mode (self->end_widget)]++;
|
||||
|
||||
if (!count[GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH] &&
|
||||
!count[GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT])
|
||||
return GTK_SIZE_REQUEST_CONSTANT_SIZE;
|
||||
else
|
||||
return count[GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT] > count[GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH]
|
||||
? GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT
|
||||
: GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
get_expand (GtkWidget *widget,
|
||||
GtkOrientation orientation)
|
||||
{
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
return gtk_widget_get_hexpand (widget);
|
||||
else
|
||||
return gtk_widget_get_vexpand (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_layout_distribute (GtkCenterLayout *self,
|
||||
gint for_size,
|
||||
gint size,
|
||||
GtkRequestedSize *sizes)
|
||||
{
|
||||
int center_size = 0;
|
||||
int start_size = 0;
|
||||
int end_size = 0;
|
||||
gboolean center_expand = FALSE;
|
||||
gboolean start_expand = FALSE;
|
||||
gboolean end_expand = FALSE;
|
||||
int avail;
|
||||
|
||||
sizes[0].minimum_size = sizes[0].natural_size = 0;
|
||||
sizes[1].minimum_size = sizes[1].natural_size = 0;
|
||||
sizes[2].minimum_size = sizes[2].natural_size = 0;
|
||||
|
||||
if (self->start_widget)
|
||||
gtk_widget_measure (self->start_widget,
|
||||
self->orientation,
|
||||
for_size,
|
||||
&sizes[0].minimum_size, &sizes[0].natural_size,
|
||||
NULL, NULL);
|
||||
|
||||
if (self->center_widget)
|
||||
gtk_widget_measure (self->center_widget,
|
||||
self->orientation,
|
||||
for_size,
|
||||
&sizes[1].minimum_size, &sizes[1].natural_size,
|
||||
NULL, NULL);
|
||||
|
||||
|
||||
if (self->end_widget)
|
||||
gtk_widget_measure (self->end_widget,
|
||||
self->orientation,
|
||||
for_size,
|
||||
&sizes[2].minimum_size, &sizes[2].natural_size,
|
||||
NULL, NULL);
|
||||
|
||||
if (self->center_widget)
|
||||
{
|
||||
center_size = CLAMP (size - (sizes[0].minimum_size + sizes[2].minimum_size), sizes[1].minimum_size, sizes[1].natural_size);
|
||||
center_expand = get_expand (self->center_widget, self->orientation);
|
||||
}
|
||||
|
||||
if (self->start_widget)
|
||||
{
|
||||
avail = MIN ((size - center_size) / 2, size - (center_size + sizes[2].minimum_size));
|
||||
start_size = CLAMP (avail, sizes[0].minimum_size, sizes[0].natural_size);
|
||||
start_expand = get_expand (self->start_widget, self->orientation);
|
||||
}
|
||||
|
||||
if (self->end_widget)
|
||||
{
|
||||
avail = MIN ((size - center_size) / 2, size - (center_size + sizes[0].minimum_size));
|
||||
end_size = CLAMP (avail, sizes[2].minimum_size, sizes[2].natural_size);
|
||||
end_expand = get_expand (self->end_widget, self->orientation);
|
||||
}
|
||||
|
||||
if (self->center_widget)
|
||||
{
|
||||
int center_pos;
|
||||
|
||||
center_pos = (size / 2) - (center_size / 2);
|
||||
|
||||
/* Push in from start/end */
|
||||
if (start_size > center_pos)
|
||||
center_pos = start_size;
|
||||
else if (size - end_size < center_pos + center_size)
|
||||
center_pos = size - center_size - end_size;
|
||||
else if (center_expand)
|
||||
{
|
||||
center_size = size - 2 * MAX (start_size, end_size);
|
||||
center_pos = (size / 2) - (center_size / 2);
|
||||
}
|
||||
|
||||
if (start_expand)
|
||||
start_size = center_pos;
|
||||
|
||||
if (end_expand)
|
||||
end_size = size - (center_pos + center_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
avail = size - (start_size + end_size);
|
||||
if (start_expand && end_expand)
|
||||
{
|
||||
start_size += avail / 2;
|
||||
end_size += avail / 2;
|
||||
}
|
||||
else if (start_expand)
|
||||
{
|
||||
start_size += avail;
|
||||
}
|
||||
else if (end_expand)
|
||||
{
|
||||
end_size += avail;
|
||||
}
|
||||
}
|
||||
|
||||
sizes[0].minimum_size = start_size;
|
||||
sizes[1].minimum_size = center_size;
|
||||
sizes[2].minimum_size = end_size;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_layout_compute_size (GtkCenterLayout *self,
|
||||
GtkWidget *widget,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
int min_baseline, nat_baseline;
|
||||
int start_min = 0;
|
||||
int start_nat = 0;
|
||||
int center_min = 0;
|
||||
int center_nat = 0;
|
||||
int end_min = 0;
|
||||
int end_nat = 0;
|
||||
|
||||
if (self->start_widget)
|
||||
gtk_widget_measure (self->start_widget,
|
||||
self->orientation,
|
||||
for_size,
|
||||
&start_min, &start_nat,
|
||||
&min_baseline, &nat_baseline);
|
||||
|
||||
if (self->center_widget)
|
||||
gtk_widget_measure (self->center_widget,
|
||||
self->orientation,
|
||||
for_size,
|
||||
¢er_min, ¢er_nat,
|
||||
&min_baseline, &nat_baseline);
|
||||
|
||||
if (self->end_widget)
|
||||
gtk_widget_measure (self->end_widget,
|
||||
self->orientation,
|
||||
for_size,
|
||||
&end_min, &end_nat,
|
||||
&min_baseline, &nat_baseline);
|
||||
|
||||
*minimum = start_min + center_min + end_min;
|
||||
*natural = center_nat + 2 * MAX (start_nat, end_nat);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_layout_compute_opposite_size (GtkCenterLayout *self,
|
||||
GtkWidget *widget,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
int child_min, child_nat;
|
||||
int child_min_baseline, child_nat_baseline;
|
||||
int total_min, above_min, below_min;
|
||||
int total_nat, above_nat, below_nat;
|
||||
GtkWidget *child[3];
|
||||
GtkRequestedSize sizes[3];
|
||||
int i;
|
||||
|
||||
child[0] = self->start_widget;
|
||||
child[1] = self->center_widget;
|
||||
child[2] = self->end_widget;
|
||||
|
||||
if (for_size >= 0)
|
||||
gtk_center_layout_distribute (self, -1, for_size, sizes);
|
||||
|
||||
above_min = below_min = above_nat = below_nat = -1;
|
||||
total_min = total_nat = 0;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
if (child[i] == NULL)
|
||||
continue;
|
||||
|
||||
gtk_widget_measure (child[i],
|
||||
OPPOSITE_ORIENTATION (self->orientation),
|
||||
for_size >= 0 ? sizes[i].minimum_size : -1,
|
||||
&child_min, &child_nat,
|
||||
&child_min_baseline, &child_nat_baseline);
|
||||
|
||||
if (child_min_baseline >= 0)
|
||||
{
|
||||
below_min = MAX (below_min, child_min - child_min_baseline);
|
||||
above_min = MAX (above_min, child_min_baseline);
|
||||
below_nat = MAX (below_nat, child_nat - child_nat_baseline);
|
||||
above_nat = MAX (above_nat, child_nat_baseline);
|
||||
}
|
||||
else
|
||||
{
|
||||
total_min = MAX (total_min, child_min);
|
||||
total_nat = MAX (total_nat, child_nat);
|
||||
}
|
||||
}
|
||||
|
||||
if (above_min >= 0)
|
||||
{
|
||||
int min_baseline = -1;
|
||||
int nat_baseline = -1;
|
||||
|
||||
total_min = MAX (total_min, above_min + below_min);
|
||||
total_nat = MAX (total_nat, above_nat + below_nat);
|
||||
|
||||
switch (self->baseline_position)
|
||||
{
|
||||
case GTK_BASELINE_POSITION_TOP:
|
||||
min_baseline = above_min;
|
||||
nat_baseline = above_nat;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_CENTER:
|
||||
min_baseline = above_min + (total_min - (above_min + below_min)) / 2;
|
||||
nat_baseline = above_nat + (total_nat - (above_nat + below_nat)) / 2;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_BOTTOM:
|
||||
min_baseline = total_min - below_min;
|
||||
nat_baseline = total_nat - below_nat;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (minimum_baseline)
|
||||
*minimum_baseline = min_baseline;
|
||||
if (natural_baseline)
|
||||
*natural_baseline = nat_baseline;
|
||||
}
|
||||
|
||||
*minimum = total_min;
|
||||
*natural = total_nat;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_layout_measure (GtkLayoutManager *layout_manager,
|
||||
GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *min_baseline,
|
||||
int *nat_baseline)
|
||||
{
|
||||
GtkCenterLayout *self = GTK_CENTER_LAYOUT (layout_manager);
|
||||
|
||||
if (self->orientation != orientation)
|
||||
{
|
||||
gtk_center_layout_compute_opposite_size (self, widget, for_size,
|
||||
minimum, natural,
|
||||
min_baseline, nat_baseline);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_center_layout_compute_size (self, widget, for_size,
|
||||
minimum, natural);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_layout_allocate (GtkLayoutManager *layout_manager,
|
||||
GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkCenterLayout *self = GTK_CENTER_LAYOUT (layout_manager);
|
||||
GtkAllocation child_allocation;
|
||||
GtkWidget *child[3];
|
||||
int child_size[3];
|
||||
int child_pos[3];
|
||||
GtkRequestedSize sizes[3];
|
||||
int size;
|
||||
int for_size;
|
||||
int i;
|
||||
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
size = width;
|
||||
for_size = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
size = height;
|
||||
for_size = width;
|
||||
baseline = -1;
|
||||
}
|
||||
|
||||
/* Allocate child sizes */
|
||||
|
||||
gtk_center_layout_distribute (self, for_size, size, sizes);
|
||||
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL &&
|
||||
gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
|
||||
{
|
||||
child[0] = self->end_widget;
|
||||
child[1] = self->center_widget;
|
||||
child[2] = self->start_widget;
|
||||
child_size[0] = sizes[2].minimum_size;
|
||||
child_size[1] = sizes[1].minimum_size;
|
||||
child_size[2] = sizes[0].minimum_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
child[0] = self->start_widget;
|
||||
child[1] = self->center_widget;
|
||||
child[2] = self->end_widget;
|
||||
child_size[0] = sizes[0].minimum_size;
|
||||
child_size[1] = sizes[1].minimum_size;
|
||||
child_size[2] = sizes[2].minimum_size;
|
||||
}
|
||||
|
||||
/* Determine baseline */
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL &&
|
||||
baseline == -1)
|
||||
{
|
||||
int min_above, nat_above;
|
||||
int min_below, nat_below;
|
||||
gboolean have_baseline;
|
||||
|
||||
have_baseline = FALSE;
|
||||
min_above = nat_above = 0;
|
||||
min_below = nat_below = 0;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
if (child[i] && gtk_widget_get_valign (child[i]) == GTK_ALIGN_BASELINE)
|
||||
{
|
||||
int child_min_height, child_nat_height;
|
||||
int child_min_baseline, child_nat_baseline;
|
||||
|
||||
child_min_baseline = child_nat_baseline = -1;
|
||||
|
||||
gtk_widget_measure (child[i], GTK_ORIENTATION_VERTICAL,
|
||||
child_size[i],
|
||||
&child_min_height, &child_nat_height,
|
||||
&child_min_baseline, &child_nat_baseline);
|
||||
|
||||
if (child_min_baseline >= 0)
|
||||
{
|
||||
have_baseline = TRUE;
|
||||
min_below = MAX (min_below, child_min_height - child_min_baseline);
|
||||
nat_below = MAX (nat_below, child_nat_height - child_nat_baseline);
|
||||
min_above = MAX (min_above, child_min_baseline);
|
||||
nat_above = MAX (nat_above, child_nat_baseline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (have_baseline)
|
||||
{
|
||||
/* TODO: This is purely based on the minimum baseline.
|
||||
* When things fit we should use the natural one
|
||||
*/
|
||||
switch (self->baseline_position)
|
||||
{
|
||||
default:
|
||||
case GTK_BASELINE_POSITION_TOP:
|
||||
baseline = min_above;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_CENTER:
|
||||
baseline = min_above + (height - (min_above + min_below)) / 2;
|
||||
break;
|
||||
case GTK_BASELINE_POSITION_BOTTOM:
|
||||
baseline = height - min_below;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate child positions */
|
||||
|
||||
child_pos[0] = 0;
|
||||
child_pos[1] = (size / 2) - (child_size[1] / 2);
|
||||
child_pos[2] = size - child_size[2];
|
||||
|
||||
if (child[1])
|
||||
{
|
||||
/* Push in from start/end */
|
||||
if (child_size[0] > child_pos[1])
|
||||
child_pos[1] = child_size[0];
|
||||
else if (size - child_size[2] < child_pos[1] + child_size[1])
|
||||
child_pos[1] = size - child_size[1] - child_size[2];
|
||||
}
|
||||
|
||||
child_allocation = (GtkAllocation) { 0, 0, width, height };
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
if (child[i] == NULL)
|
||||
continue;
|
||||
|
||||
if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
child_allocation.x = child_pos[i];
|
||||
child_allocation.y = 0;
|
||||
child_allocation.width = child_size[i];
|
||||
child_allocation.height = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
child_allocation.x = 0;
|
||||
child_allocation.y = child_pos[i];
|
||||
child_allocation.width = width;
|
||||
child_allocation.height = child_size[i];
|
||||
}
|
||||
|
||||
gtk_widget_size_allocate (child[i], &child_allocation, baseline);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_layout_class_init (GtkCenterLayoutClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GtkLayoutManagerClass *layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = gtk_center_layout_set_property;
|
||||
gobject_class->get_property = gtk_center_layout_get_property;
|
||||
|
||||
layout_manager_class->get_request_mode = gtk_center_layout_get_request_mode;
|
||||
layout_manager_class->measure = gtk_center_layout_measure;
|
||||
layout_manager_class->allocate = gtk_center_layout_allocate;
|
||||
|
||||
/**
|
||||
* GtkCenterLayout:baseline-position:
|
||||
*
|
||||
* The position of the allocated baseline within the extra space
|
||||
* allocated to each child of the widget using a center layout
|
||||
* manager.
|
||||
*
|
||||
* This property is only relevant for horizontal layouts containing
|
||||
* at least one child with a baseline alignment.
|
||||
*/
|
||||
center_layout_props[PROP_BASELINE_POSITION] =
|
||||
g_param_spec_enum ("baseline-position",
|
||||
P_("Baseline position"),
|
||||
P_("The position of the baseline aligned widgets if extra space is available"),
|
||||
GTK_TYPE_BASELINE_POSITION,
|
||||
GTK_BASELINE_POSITION_CENTER,
|
||||
GTK_PARAM_READWRITE |
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, center_layout_props);
|
||||
g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_center_layout_init (GtkCenterLayout *self)
|
||||
{
|
||||
self->orientation = GTK_ORIENTATION_HORIZONTAL;
|
||||
self->baseline_position = GTK_BASELINE_POSITION_CENTER;
|
||||
}
|
||||
|
||||
GtkLayoutManager *
|
||||
gtk_center_layout_new (GtkOrientation orientation)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_CENTER_LAYOUT,
|
||||
"orientation", orientation,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_center_layout_set_baseline_position:
|
||||
* @center_layout: a #GtkCenterLayout
|
||||
* @position: a #GtkBaselinePosition
|
||||
*
|
||||
* Sets the baseline position of a center layout.
|
||||
*
|
||||
* The baseline position affects only horizontal centeres with at least one
|
||||
* baseline aligned child. If there is more vertical space available than
|
||||
* requested, and the baseline is not allocated by the parent then the
|
||||
* given @position is used to allocate the baseline within the extra
|
||||
* space available.
|
||||
*/
|
||||
void
|
||||
gtk_center_layout_set_baseline_position (GtkCenterLayout *center_layout,
|
||||
GtkBaselinePosition position)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_CENTER_LAYOUT (center_layout));
|
||||
|
||||
if (center_layout->baseline_position != position)
|
||||
{
|
||||
center_layout->baseline_position = position;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (center_layout), center_layout_props[PROP_BASELINE_POSITION]);
|
||||
|
||||
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (center_layout));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_center_layout_get_baseline_position:
|
||||
* @center_layout: a #GtkCenterLayout
|
||||
*
|
||||
* Gets the value set by gtk_center_layout_set_baseline_position().
|
||||
*
|
||||
* Returns: the baseline position
|
||||
*/
|
||||
GtkBaselinePosition
|
||||
gtk_center_layout_get_baseline_position (GtkCenterLayout *center_layout)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CENTER_LAYOUT (center_layout), GTK_BASELINE_POSITION_CENTER);
|
||||
|
||||
return center_layout->baseline_position;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_center_layout_set_start_widget (GtkCenterLayout *center_layout,
|
||||
GtkWidget *child)
|
||||
{
|
||||
center_layout->start_widget = child;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_center_layout_set_center_widget (GtkCenterLayout *center_layout,
|
||||
GtkWidget *child)
|
||||
{
|
||||
center_layout->center_widget = child;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_center_layout_set_end_widget (GtkCenterLayout *center_layout,
|
||||
GtkWidget *child)
|
||||
{
|
||||
center_layout->end_widget = child;
|
||||
}
|
54
gtk/gtkcenterlayout.h
Normal file
54
gtk/gtkcenterlayout.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* gtkcenterlayout.h: Center layout manager
|
||||
*
|
||||
* Copyright 2019 Red Hat, Inc.
|
||||
*
|
||||
* 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 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkenums.h>
|
||||
#include <gtk/gtklayoutmanager.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CENTER_LAYOUT (gtk_center_layout_get_type())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkCenterLayout, gtk_center_layout, GTK, CENTER_LAYOUT, GtkLayoutManager)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkLayoutManager * gtk_center_layout_new (GtkOrientation orientation);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_center_layout_set_baseline_position (GtkCenterLayout *center_layout,
|
||||
GtkBaselinePosition position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBaselinePosition gtk_center_layout_get_baseline_position (GtkCenterLayout *center_layout);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_center_layout_set_start_widget (GtkCenterLayout *center_layout,
|
||||
GtkWidget *child);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_center_layout_set_center_widget (GtkCenterLayout *center_layout,
|
||||
GtkWidget *child);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_center_layout_set_end_widget (GtkCenterLayout *center_layout,
|
||||
GtkWidget *child);
|
||||
|
||||
G_END_DECLS
|
@@ -57,7 +57,7 @@
|
||||
*/
|
||||
|
||||
|
||||
typedef struct _GtkLayoutChild GtkLayoutChild;
|
||||
typedef struct _LayoutChild LayoutChild;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -77,7 +77,7 @@ typedef struct
|
||||
GList *children;
|
||||
} GtkLayoutPrivate;
|
||||
|
||||
struct _GtkLayoutChild {
|
||||
struct _LayoutChild {
|
||||
GtkWidget *widget;
|
||||
gint x;
|
||||
gint y;
|
||||
@@ -298,7 +298,7 @@ gtk_layout_set_vadjustment (GtkLayout *layout,
|
||||
g_object_notify (G_OBJECT (layout), "vadjustment");
|
||||
}
|
||||
|
||||
static GtkLayoutChild*
|
||||
static LayoutChild *
|
||||
get_child (GtkLayout *layout,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
@@ -308,7 +308,7 @@ get_child (GtkLayout *layout,
|
||||
children = priv->children;
|
||||
while (children)
|
||||
{
|
||||
GtkLayoutChild *child;
|
||||
LayoutChild *child;
|
||||
|
||||
child = children->data;
|
||||
children = children->next;
|
||||
@@ -338,12 +338,12 @@ gtk_layout_put (GtkLayout *layout,
|
||||
gint y)
|
||||
{
|
||||
GtkLayoutPrivate *priv = gtk_layout_get_instance_private (layout);
|
||||
GtkLayoutChild *child;
|
||||
LayoutChild *child;
|
||||
|
||||
g_return_if_fail (GTK_IS_LAYOUT (layout));
|
||||
g_return_if_fail (GTK_IS_WIDGET (child_widget));
|
||||
|
||||
child = g_new (GtkLayoutChild, 1);
|
||||
child = g_new (LayoutChild, 1);
|
||||
|
||||
child->widget = child_widget;
|
||||
child->x = x;
|
||||
@@ -362,7 +362,7 @@ gtk_layout_move_internal (GtkLayout *layout,
|
||||
gboolean change_y,
|
||||
gint y)
|
||||
{
|
||||
GtkLayoutChild *child;
|
||||
LayoutChild *child;
|
||||
|
||||
child = get_child (layout, widget);
|
||||
|
||||
@@ -660,7 +660,7 @@ gtk_layout_get_child_property (GtkContainer *container,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkLayoutChild *layout_child;
|
||||
LayoutChild *layout_child;
|
||||
|
||||
layout_child = get_child (GTK_LAYOUT (container), child);
|
||||
|
||||
@@ -729,7 +729,7 @@ gtk_layout_size_allocate (GtkWidget *widget,
|
||||
|
||||
while (tmp_list)
|
||||
{
|
||||
GtkLayoutChild *child = tmp_list->data;
|
||||
LayoutChild *child = tmp_list->data;
|
||||
GtkAllocation allocation;
|
||||
GtkRequisition requisition;
|
||||
|
||||
@@ -765,7 +765,7 @@ gtk_layout_remove (GtkContainer *container,
|
||||
GtkLayout *layout = GTK_LAYOUT (container);
|
||||
GtkLayoutPrivate *priv = gtk_layout_get_instance_private (layout);
|
||||
GList *tmp_list;
|
||||
GtkLayoutChild *child = NULL;
|
||||
LayoutChild *child = NULL;
|
||||
|
||||
tmp_list = priv->children;
|
||||
while (tmp_list)
|
||||
@@ -793,7 +793,7 @@ gtk_layout_forall (GtkContainer *container,
|
||||
{
|
||||
GtkLayout *layout = GTK_LAYOUT (container);
|
||||
GtkLayoutPrivate *priv = gtk_layout_get_instance_private (layout);
|
||||
GtkLayoutChild *child;
|
||||
LayoutChild *child;
|
||||
GList *tmp_list;
|
||||
|
||||
tmp_list = priv->children;
|
||||
|
189
gtk/gtklayoutchild.c
Normal file
189
gtk/gtklayoutchild.c
Normal file
@@ -0,0 +1,189 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gtklayoutchild.h"
|
||||
|
||||
#include "gtklayoutmanager.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtklayoutchild
|
||||
* @Title: GtkLayoutChild
|
||||
* @Short_description: An object containing layout properties
|
||||
*
|
||||
* #GtkLayoutChild is the base class for objects that are meant to hold
|
||||
* layout properties. If a #GtkLayoutManager has per-child properties,
|
||||
* like their packing type, or the horizontal and vertical span, or the
|
||||
* icon name, then the layout manager should use a #GtkLayoutChild
|
||||
* implementation to store those properties.
|
||||
*
|
||||
* A #GtkLayoutChild instance is only ever valid while a widget is part
|
||||
* of a layout.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
GtkLayoutManager *manager;
|
||||
GtkWidget *widget;
|
||||
} GtkLayoutChildPrivate;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkLayoutChild, gtk_layout_child, G_TYPE_OBJECT)
|
||||
|
||||
enum {
|
||||
PROP_LAYOUT_MANAGER = 1,
|
||||
PROP_CHILD_WIDGET,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *layout_child_properties[N_PROPS];
|
||||
|
||||
static void
|
||||
gtk_layout_child_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkLayoutChild *layout_child = GTK_LAYOUT_CHILD (gobject);
|
||||
GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_LAYOUT_MANAGER:
|
||||
priv->manager = g_value_get_object (value);
|
||||
break;
|
||||
|
||||
case PROP_CHILD_WIDGET:
|
||||
priv->widget = g_value_get_object (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_layout_child_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkLayoutChild *layout_child = GTK_LAYOUT_CHILD (gobject);
|
||||
GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_LAYOUT_MANAGER:
|
||||
g_value_set_object (value, priv->manager);
|
||||
break;
|
||||
|
||||
case PROP_CHILD_WIDGET:
|
||||
g_value_set_object (value, priv->widget);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_layout_child_constructed (GObject *gobject)
|
||||
{
|
||||
GtkLayoutChild *layout_child = GTK_LAYOUT_CHILD (gobject);
|
||||
GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
|
||||
|
||||
G_OBJECT_CLASS (gtk_layout_child_parent_class)->constructed (gobject);
|
||||
|
||||
if (priv->manager == NULL)
|
||||
{
|
||||
g_critical ("The layout child of type %s does not have "
|
||||
"the GtkLayoutChild:layout-manager property set",
|
||||
G_OBJECT_TYPE_NAME (gobject));
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->widget == NULL)
|
||||
{
|
||||
g_critical ("The layout child of type %s does not have "
|
||||
"the GtkLayoutChild:child-widget property set",
|
||||
G_OBJECT_TYPE_NAME (gobject));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_layout_child_class_init (GtkLayoutChildClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = gtk_layout_child_set_property;
|
||||
gobject_class->get_property = gtk_layout_child_get_property;
|
||||
gobject_class->constructed = gtk_layout_child_constructed;
|
||||
|
||||
/**
|
||||
* GtkLayoutChild:layout-manager:
|
||||
*
|
||||
* The layout manager that created the #GtkLayoutChild instance.
|
||||
*/
|
||||
layout_child_properties[PROP_LAYOUT_MANAGER] =
|
||||
g_param_spec_object ("layout-manager",
|
||||
"Layout Manager",
|
||||
"The layout manager that created this object",
|
||||
GTK_TYPE_LAYOUT_MANAGER,
|
||||
GTK_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY);
|
||||
/**
|
||||
* GtkLayoutChild:child-widget:
|
||||
*
|
||||
* The widget that is associated to the #GtkLayoutChild instance.
|
||||
*/
|
||||
layout_child_properties[PROP_CHILD_WIDGET] =
|
||||
g_param_spec_object ("child-widget",
|
||||
"Child Widget",
|
||||
"The child widget that is associated to this object",
|
||||
GTK_TYPE_WIDGET,
|
||||
GTK_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, layout_child_properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_layout_child_init (GtkLayoutChild *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_layout_child_get_layout_manager:
|
||||
* @layout_child: a #GtkLayoutChild
|
||||
*
|
||||
* Retrieves the #GtkLayoutManager instance that created the
|
||||
* given @layout_child.
|
||||
*
|
||||
* Returns: (transfer none): a #GtkLayoutManager
|
||||
*/
|
||||
GtkLayoutManager *
|
||||
gtk_layout_child_get_layout_manager (GtkLayoutChild *layout_child)
|
||||
{
|
||||
GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LAYOUT_CHILD (layout_child), NULL);
|
||||
|
||||
return priv->manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_layout_child_get_child_widget:
|
||||
* @layout_child: a #GtkLayoutChild
|
||||
*
|
||||
* Retrieves the #GtkWidget associated to the given @layout_child.
|
||||
*
|
||||
* Returns: (transfer none): a #GtkWidget
|
||||
*/
|
||||
GtkWidget *
|
||||
gtk_layout_child_get_child_widget (GtkLayoutChild *layout_child)
|
||||
{
|
||||
GtkLayoutChildPrivate *priv = gtk_layout_child_get_instance_private (layout_child);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LAYOUT_CHILD (layout_child), NULL);
|
||||
|
||||
return priv->widget;
|
||||
}
|
27
gtk/gtklayoutchild.h
Normal file
27
gtk/gtklayoutchild.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LAYOUT_CHILD (gtk_layout_child_get_type())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_DERIVABLE_TYPE (GtkLayoutChild, gtk_layout_child, GTK, LAYOUT_CHILD, GObject)
|
||||
|
||||
struct _GtkLayoutChildClass
|
||||
{
|
||||
/*< private >*/
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkLayoutManager * gtk_layout_child_get_layout_manager (GtkLayoutChild *layout_child);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_layout_child_get_child_widget (GtkLayoutChild *layout_child);
|
||||
|
||||
G_END_DECLS
|
357
gtk/gtklayoutmanager.c
Normal file
357
gtk/gtklayoutmanager.c
Normal file
@@ -0,0 +1,357 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:gtklayoutmanager
|
||||
* @Title: GtkLayoutManager
|
||||
* @Short_description: Base class for layout manager
|
||||
*
|
||||
* Layout managers are delegate classes that handle the preferred size
|
||||
* and the allocation of a container widget.
|
||||
*
|
||||
* You typically subclass #GtkLayoutManager if you want to implement a
|
||||
* layout policy for the children of a widget, without necessarily
|
||||
* implementing the @GtkWidgetClass.measure() and @GtkWidgetClass.size_allocate()
|
||||
* virtual functions directly.
|
||||
*
|
||||
* 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 gtk_widget_set_layout_manager().
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtklayoutmanagerprivate.h"
|
||||
#include "gtklayoutchild.h"
|
||||
#include "gtkwidget.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;
|
||||
} GtkLayoutManagerPrivate;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkLayoutManager, gtk_layout_manager, G_TYPE_OBJECT)
|
||||
|
||||
static GQuark quark_layout_child;
|
||||
|
||||
static GtkSizeRequestMode
|
||||
gtk_layout_manager_real_get_request_mode (GtkLayoutManager *manager,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, get_request_mode);
|
||||
|
||||
return GTK_SIZE_REQUEST_CONSTANT_SIZE;
|
||||
}
|
||||
|
||||
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 void
|
||||
gtk_layout_manager_class_init (GtkLayoutManagerClass *klass)
|
||||
{
|
||||
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;
|
||||
|
||||
quark_layout_child = g_quark_from_static_string ("-GtkLayoutManager-layout-child");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 [GtkWidget's geometry management section][geometry-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;
|
||||
|
||||
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,
|
||||
minimum, natural,
|
||||
minimum_baseline, natural_baseline);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* This function 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));
|
||||
|
||||
klass = GTK_LAYOUT_MANAGER_GET_CLASS (manager);
|
||||
|
||||
klass->allocate (manager, widget, width, height, baseline);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_layout_manager_get_request_mode:
|
||||
* @manager: a #GtkLayoutManager
|
||||
* @widget: the #GtkWidget using @manager
|
||||
*
|
||||
* Retrieves the request mode of @manager.
|
||||
*
|
||||
* Returns: a #GtkSizeRequestMode
|
||||
*/
|
||||
GtkSizeRequestMode
|
||||
gtk_layout_manager_get_request_mode (GtkLayoutManager *manager,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkLayoutManagerClass *klass;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LAYOUT_MANAGER (manager), GTK_SIZE_REQUEST_CONSTANT_SIZE);
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), GTK_SIZE_REQUEST_CONSTANT_SIZE);
|
||||
|
||||
klass = GTK_LAYOUT_MANAGER_GET_CLASS (manager);
|
||||
|
||||
return klass->get_request_mode (manager, 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_layout_manager_get_layout_child:
|
||||
* @manager: a #GtkLayoutManager
|
||||
* @widget: a #GtkWidget
|
||||
*
|
||||
* Retrieves a #GtkLayoutChild instance for the #GtkLayoutManager, creating
|
||||
* one if necessary
|
||||
*
|
||||
* The #GtkLayoutChild instance is owned by the #GtkLayoutManager, and is
|
||||
* guaranteed to exist as long as @widget 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 *widget)
|
||||
{
|
||||
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 (widget), NULL);
|
||||
|
||||
parent = gtk_widget_get_parent (widget);
|
||||
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 (widget), widget,
|
||||
G_OBJECT_TYPE_NAME (manager), manager);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (GTK_LAYOUT_MANAGER_GET_CLASS (manager)->create_layout_child == NULL)
|
||||
{
|
||||
g_critical ("The layout manager of type %s %p does not create "
|
||||
"GtkLayoutChild instances",
|
||||
G_OBJECT_TYPE_NAME (manager), manager);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We store the LayoutChild into the Widget, so that the LayoutChild
|
||||
* instance goes away once the Widget goes away
|
||||
*/
|
||||
res = g_object_get_qdata (G_OBJECT (widget), quark_layout_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, widget);
|
||||
g_assert (res != NULL);
|
||||
g_assert (g_type_is_a (G_OBJECT_TYPE (res), GTK_TYPE_LAYOUT_CHILD));
|
||||
|
||||
g_object_set_qdata_full (G_OBJECT (widget), quark_layout_child,
|
||||
res,
|
||||
g_object_unref);
|
||||
|
||||
return res;
|
||||
}
|
106
gtk/gtklayoutmanager.h
Normal file
106
gtk/gtklayoutmanager.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/* gtklayoutmanager.h: 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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include <gtk/gtklayoutchild.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LAYOUT_MANAGER (gtk_layout_manager_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_DERIVABLE_TYPE (GtkLayoutManager, gtk_layout_manager, GTK, LAYOUT_MANAGER, GObject)
|
||||
|
||||
/**
|
||||
* GtkLayoutManagerClass:
|
||||
* @get_request_mode: a virtual function, used to return the preferred
|
||||
* request mode for the layout manager; for instance, "width for height"
|
||||
* or "height for width"; see #GtkSizeRequestMode
|
||||
* @measure: a virtual function, used to measure the minimum and preferred
|
||||
* sizes of the widget using the layout manager for a given orientation
|
||||
* @allocate: a virtual function, used to allocate the size of the widget
|
||||
* using the layout manager
|
||||
*
|
||||
* The `GtkLayoutManagerClass` structure contains only private data, and
|
||||
* should only be accessed through the provided API, or when subclassing
|
||||
* #GtkLayoutManager.
|
||||
*/
|
||||
struct _GtkLayoutManagerClass
|
||||
{
|
||||
/*< private >*/
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< public >*/
|
||||
GtkSizeRequestMode (* get_request_mode) (GtkLayoutManager *manager,
|
||||
GtkWidget *widget);
|
||||
|
||||
void (* measure) (GtkLayoutManager *manager,
|
||||
GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline);
|
||||
|
||||
void (* allocate) (GtkLayoutManager *manager,
|
||||
GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline);
|
||||
|
||||
GtkLayoutChild * (* create_layout_child) (GtkLayoutManager *manager,
|
||||
GtkWidget *widget);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _padding[16];
|
||||
};
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_layout_manager_measure (GtkLayoutManager *manager,
|
||||
GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_layout_manager_allocate (GtkLayoutManager *manager,
|
||||
GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSizeRequestMode gtk_layout_manager_get_request_mode (GtkLayoutManager *manager,
|
||||
GtkWidget *widget);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_layout_manager_get_widget (GtkLayoutManager *manager);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_layout_manager_layout_changed (GtkLayoutManager *manager);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkLayoutChild * gtk_layout_manager_get_layout_child (GtkLayoutManager *manager,
|
||||
GtkWidget *widget);
|
||||
|
||||
G_END_DECLS
|
10
gtk/gtklayoutmanagerprivate.h
Normal file
10
gtk/gtklayoutmanagerprivate.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "gtklayoutmanager.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gtk_layout_manager_set_widget (GtkLayoutManager *manager,
|
||||
GtkWidget *widget);
|
||||
|
||||
G_END_DECLS
|
@@ -31,6 +31,7 @@
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkcssnumbervalueprivate.h"
|
||||
#include "gtklayoutmanagerprivate.h"
|
||||
|
||||
|
||||
#ifdef G_ENABLE_CONSISTENCY_CHECKS
|
||||
@@ -195,45 +196,87 @@ gtk_widget_query_size_for_orientation (GtkWidget *widget,
|
||||
css_min_for_size = get_number_ceil (style, GTK_CSS_PROPERTY_MIN_WIDTH);
|
||||
}
|
||||
|
||||
if (for_size < 0)
|
||||
GtkLayoutManager *layout_manager = gtk_widget_get_layout_manager (widget);
|
||||
|
||||
if (layout_manager != NULL)
|
||||
{
|
||||
push_recursion_check (widget, orientation);
|
||||
widget_class->measure (widget, orientation, -1,
|
||||
&reported_min_size, &reported_nat_size,
|
||||
&min_baseline, &nat_baseline);
|
||||
pop_recursion_check (widget, orientation);
|
||||
if (for_size < 0)
|
||||
{
|
||||
gtk_layout_manager_measure (layout_manager, widget,
|
||||
orientation, -1,
|
||||
&reported_min_size, &reported_nat_size,
|
||||
&min_baseline, &nat_baseline);
|
||||
}
|
||||
else
|
||||
{
|
||||
int adjusted_for_size;
|
||||
int minimum_for_size = 0;
|
||||
int natural_for_size = 0;
|
||||
int dummy = 0;
|
||||
|
||||
/* Pull the minimum for_size from the cache as it's needed to adjust
|
||||
* the proposed 'for_size' */
|
||||
gtk_layout_manager_measure (layout_manager, widget,
|
||||
OPPOSITE_ORIENTATION (orientation), -1,
|
||||
&minimum_for_size, &natural_for_size,
|
||||
NULL, NULL);
|
||||
|
||||
if (for_size < MAX (minimum_for_size, css_min_for_size))
|
||||
for_size = MAX (minimum_for_size, css_min_for_size);
|
||||
|
||||
adjusted_for_size = for_size;
|
||||
gtk_widget_adjust_size_allocation (widget, OPPOSITE_ORIENTATION (orientation),
|
||||
&for_size, &natural_for_size,
|
||||
&dummy, &adjusted_for_size);
|
||||
adjusted_for_size -= css_extra_for_size;
|
||||
|
||||
gtk_layout_manager_measure (layout_manager, widget,
|
||||
orientation, adjusted_for_size,
|
||||
&reported_min_size, &reported_nat_size,
|
||||
&min_baseline, &nat_baseline);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int adjusted_for_size;
|
||||
int minimum_for_size = 0;
|
||||
int natural_for_size = 0;
|
||||
int dummy = 0;
|
||||
if (for_size < 0)
|
||||
{
|
||||
push_recursion_check (widget, orientation);
|
||||
widget_class->measure (widget, orientation, -1,
|
||||
&reported_min_size, &reported_nat_size,
|
||||
&min_baseline, &nat_baseline);
|
||||
pop_recursion_check (widget, orientation);
|
||||
}
|
||||
else
|
||||
{
|
||||
int adjusted_for_size;
|
||||
int minimum_for_size = 0;
|
||||
int natural_for_size = 0;
|
||||
int dummy = 0;
|
||||
|
||||
/* Pull the minimum for_size from the cache as it's needed to adjust
|
||||
* the proposed 'for_size' */
|
||||
gtk_widget_measure (widget, OPPOSITE_ORIENTATION (orientation), -1,
|
||||
&minimum_for_size, &natural_for_size, NULL, NULL);
|
||||
/* Pull the minimum for_size from the cache as it's needed to adjust
|
||||
* the proposed 'for_size' */
|
||||
gtk_widget_measure (widget, OPPOSITE_ORIENTATION (orientation), -1,
|
||||
&minimum_for_size, &natural_for_size, NULL, NULL);
|
||||
|
||||
/* TODO: Warn if the given for_size is too small? */
|
||||
if (for_size < MAX (minimum_for_size, css_min_for_size))
|
||||
for_size = MAX (minimum_for_size, css_min_for_size);
|
||||
/* TODO: Warn if the given for_size is too small? */
|
||||
if (for_size < MAX (minimum_for_size, css_min_for_size))
|
||||
for_size = MAX (minimum_for_size, css_min_for_size);
|
||||
|
||||
adjusted_for_size = for_size;
|
||||
gtk_widget_adjust_size_allocation (widget, OPPOSITE_ORIENTATION (orientation),
|
||||
&for_size, &natural_for_size,
|
||||
&dummy, &adjusted_for_size);
|
||||
adjusted_for_size = for_size;
|
||||
gtk_widget_adjust_size_allocation (widget, OPPOSITE_ORIENTATION (orientation),
|
||||
&for_size, &natural_for_size,
|
||||
&dummy, &adjusted_for_size);
|
||||
|
||||
adjusted_for_size -= css_extra_for_size;
|
||||
|
||||
push_recursion_check (widget, orientation);
|
||||
widget_class->measure (widget,
|
||||
orientation,
|
||||
adjusted_for_size,
|
||||
&reported_min_size, &reported_nat_size,
|
||||
&min_baseline, &nat_baseline);
|
||||
pop_recursion_check (widget, orientation);
|
||||
adjusted_for_size -= css_extra_for_size;
|
||||
|
||||
push_recursion_check (widget, orientation);
|
||||
widget_class->measure (widget,
|
||||
orientation,
|
||||
adjusted_for_size,
|
||||
&reported_min_size, &reported_nat_size,
|
||||
&min_baseline, &nat_baseline);
|
||||
pop_recursion_check (widget, orientation);
|
||||
}
|
||||
}
|
||||
|
||||
min_size = MAX (0, MAX (reported_min_size, css_min_size)) + css_extra_size;
|
||||
@@ -512,7 +555,13 @@ gtk_widget_get_request_mode (GtkWidget *widget)
|
||||
|
||||
if (G_UNLIKELY (!cache->request_mode_valid))
|
||||
{
|
||||
cache->request_mode = GTK_WIDGET_GET_CLASS (widget)->get_request_mode (widget);
|
||||
GtkLayoutManager *layout_manager = gtk_widget_get_layout_manager (widget);
|
||||
|
||||
if (layout_manager != NULL)
|
||||
cache->request_mode = gtk_layout_manager_get_request_mode (layout_manager, widget);
|
||||
else
|
||||
cache->request_mode = GTK_WIDGET_GET_CLASS (widget)->get_request_mode (widget);
|
||||
|
||||
cache->request_mode_valid = TRUE;
|
||||
}
|
||||
|
||||
|
@@ -38,6 +38,7 @@ typedef struct _GtkBuilder GtkBuilder;
|
||||
typedef struct _GtkClipboard GtkClipboard;
|
||||
typedef struct _GtkEventController GtkEventController;
|
||||
typedef struct _GtkGesture GtkGesture;
|
||||
typedef struct _GtkLayoutManager GtkLayoutManager;
|
||||
typedef struct _GtkRequisition GtkRequisition;
|
||||
typedef struct _GtkSelectionData GtkSelectionData;
|
||||
typedef struct _GtkSettings GtkSettings;
|
||||
|
101
gtk/gtkwidget.c
101
gtk/gtkwidget.c
@@ -47,6 +47,7 @@
|
||||
#include "gtkgestureswipe.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkinvisible.h"
|
||||
#include "gtklayoutmanagerprivate.h"
|
||||
#include "gtkmarshalers.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkmenu.h"
|
||||
@@ -535,6 +536,7 @@ enum {
|
||||
PROP_EXPAND,
|
||||
PROP_SCALE_FACTOR,
|
||||
PROP_CSS_NAME,
|
||||
PROP_LAYOUT_MANAGER,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
|
||||
@@ -1328,6 +1330,19 @@ gtk_widget_class_init (GtkWidgetClass *klass)
|
||||
NULL,
|
||||
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
/**
|
||||
* GtkWidget:layout-manager:
|
||||
*
|
||||
* The #GtkLayoutManager instance to use to compute the preferred size
|
||||
* of the widget, and allocate its children.
|
||||
*/
|
||||
widget_props[PROP_LAYOUT_MANAGER] =
|
||||
g_param_spec_object ("layout-manager",
|
||||
P_("Layout Manager"),
|
||||
P_("The layout manager used to layout children of the widget"),
|
||||
GTK_TYPE_LAYOUT_MANAGER,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, widget_props);
|
||||
|
||||
/**
|
||||
@@ -2322,6 +2337,9 @@ gtk_widget_set_property (GObject *object,
|
||||
else
|
||||
gtk_css_node_set_name (priv->cssnode, GTK_WIDGET_GET_CLASS (widget)->priv->css_name);
|
||||
break;
|
||||
case PROP_LAYOUT_MANAGER:
|
||||
gtk_widget_set_layout_manager (widget, g_value_get_object (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -2462,6 +2480,9 @@ gtk_widget_get_property (GObject *object,
|
||||
case PROP_CSS_NAME:
|
||||
g_value_set_string (value, gtk_css_node_get_name (priv->cssnode));
|
||||
break;
|
||||
case PROP_LAYOUT_MANAGER:
|
||||
g_value_set_object (value, gtk_widget_get_layout_manager (widget));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -4254,16 +4275,26 @@ gtk_widget_size_allocate (GtkWidget *widget,
|
||||
if (baseline >= 0)
|
||||
baseline -= margin.top + border.top + padding.top;
|
||||
|
||||
if (g_signal_has_handler_pending (widget, widget_signals[SIZE_ALLOCATE], 0, FALSE))
|
||||
g_signal_emit (widget, widget_signals[SIZE_ALLOCATE], 0,
|
||||
real_allocation.width,
|
||||
real_allocation.height,
|
||||
baseline);
|
||||
if (priv->layout_manager != NULL)
|
||||
{
|
||||
gtk_layout_manager_allocate (priv->layout_manager, widget,
|
||||
real_allocation.width,
|
||||
real_allocation.height,
|
||||
baseline);
|
||||
}
|
||||
else
|
||||
GTK_WIDGET_GET_CLASS (widget)->size_allocate (widget,
|
||||
real_allocation.width,
|
||||
real_allocation.height,
|
||||
baseline);
|
||||
{
|
||||
if (g_signal_has_handler_pending (widget, widget_signals[SIZE_ALLOCATE], 0, FALSE))
|
||||
g_signal_emit (widget, widget_signals[SIZE_ALLOCATE], 0,
|
||||
real_allocation.width,
|
||||
real_allocation.height,
|
||||
baseline);
|
||||
else
|
||||
GTK_WIDGET_GET_CLASS (widget)->size_allocate (widget,
|
||||
real_allocation.width,
|
||||
real_allocation.height,
|
||||
baseline);
|
||||
}
|
||||
|
||||
/* Size allocation is god... after consulting god, no further requests or allocations are needed */
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
@@ -8146,6 +8177,9 @@ gtk_widget_dispose (GObject *object)
|
||||
while (priv->paintables)
|
||||
gtk_widget_paintable_set_widget (priv->paintables->data, NULL);
|
||||
|
||||
gtk_widget_set_layout_manager (widget, NULL);
|
||||
g_clear_object (&priv->layout_manager);
|
||||
|
||||
priv->visible = FALSE;
|
||||
if (_gtk_widget_get_realized (widget))
|
||||
gtk_widget_unrealize (widget);
|
||||
@@ -13586,3 +13620,52 @@ gtk_widget_get_height (GtkWidget *widget)
|
||||
border.top - border.bottom -
|
||||
padding.top - padding.bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_set_layout_manager:
|
||||
* @widget: a #GtkWidget
|
||||
* @layout_manager: (nullable) (transfer full): a #GtkLayoutManager
|
||||
*
|
||||
* Sets the layout manager delegate instance that provides an implementation
|
||||
* for measuring and allocating the children of @widget.
|
||||
*
|
||||
* The @widget acquires a reference to the given @layout_manager.
|
||||
*/
|
||||
void
|
||||
gtk_widget_set_layout_manager (GtkWidget *widget,
|
||||
GtkLayoutManager *layout_manager)
|
||||
{
|
||||
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
|
||||
|
||||
g_return_if_fail (GTK_IS_WIDGET (widget));
|
||||
g_return_if_fail (layout_manager == NULL || GTK_IS_LAYOUT_MANAGER (layout_manager));
|
||||
g_return_if_fail (layout_manager == NULL || gtk_layout_manager_get_widget (layout_manager) == NULL);
|
||||
|
||||
if (g_set_object (&priv->layout_manager, layout_manager))
|
||||
{
|
||||
if (priv->layout_manager != NULL)
|
||||
gtk_layout_manager_set_widget (priv->layout_manager, widget);
|
||||
|
||||
gtk_widget_queue_resize (widget);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_LAYOUT_MANAGER]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_widget_get_layout_manager:
|
||||
* @widget: a #GtkWidget
|
||||
*
|
||||
* Retrieves the layout manager set using gtk_widget_set_layout_manager().
|
||||
*
|
||||
* Returns: (transfer none) (nullable): a #GtkLayoutManager
|
||||
*/
|
||||
GtkLayoutManager *
|
||||
gtk_widget_get_layout_manager (GtkWidget *widget)
|
||||
{
|
||||
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
|
||||
|
||||
return priv->layout_manager;
|
||||
}
|
||||
|
@@ -424,6 +424,12 @@ void gtk_widget_get_preferred_size (GtkWidget *w
|
||||
GtkRequisition *minimum_size,
|
||||
GtkRequisition *natural_size);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_widget_set_layout_manager (GtkWidget *widget,
|
||||
GtkLayoutManager *layout_manager);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkLayoutManager * gtk_widget_get_layout_manager (GtkWidget *widget);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_widget_add_accelerator (GtkWidget *widget,
|
||||
const gchar *accel_signal,
|
||||
|
@@ -149,6 +149,9 @@ struct _GtkWidgetPrivate
|
||||
/* The render node we draw or %NULL if not yet created.*/
|
||||
GskRenderNode *render_node;
|
||||
|
||||
/* The layout manager, or %NULL */
|
||||
GtkLayoutManager *layout_manager;
|
||||
|
||||
GSList *paintables;
|
||||
|
||||
/* The widget's surface or its parent surface if it does
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 201 B After Width: | Height: | Size: 250 B |
62
gtk/icons/scalable/status/switch-off-symbolic.svg
Normal file
62
gtk/icons/scalable/status/switch-off-symbolic.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
sodipodi:docname="switch-off-symbolic.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="11.858493"
|
||||
inkscape:cx="-1.0931113"
|
||||
inkscape:cy="14.095717"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid815" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-292.76666)">
|
||||
<path
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 7.9785156,295.73828 c -2.7589327,0 -5.015625,2.25865 -5.015625,5.01758 0,2.75893 2.2566923,5.01758 5.015625,5.01758 2.7589324,0 5.0175784,-2.25865 5.0175784,-5.01758 0,-2.75893 -2.258646,-5.01758 -5.0175784,-5.01758 z m 0,2 c 1.6780526,0 3.0175784,1.33953 3.0175784,3.01758 0,1.67805 -1.3395258,3.01758 -3.0175784,3.01758 -1.6780525,0 -3.015625,-1.33953 -3.015625,-3.01758 0,-1.67805 1.3375725,-3.01758 3.015625,-3.01758 z"
|
||||
id="path838"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
@@ -171,6 +171,7 @@ gtk_public_sources = files([
|
||||
'gtkbin.c',
|
||||
'gtkbindings.c',
|
||||
'gtkborder.c',
|
||||
'gtkboxlayout.c',
|
||||
'gtkbox.c',
|
||||
'gtkbuildable.c',
|
||||
'gtkbuilder.c',
|
||||
@@ -192,6 +193,7 @@ gtk_public_sources = files([
|
||||
'gtkcellrenderertoggle.c',
|
||||
'gtkcellview.c',
|
||||
'gtkcenterbox.c',
|
||||
'gtkcenterlayout.c',
|
||||
'gtkcheckbutton.c',
|
||||
'gtkcheckmenuitem.c',
|
||||
'gtkcolorbutton.c',
|
||||
@@ -263,6 +265,8 @@ gtk_public_sources = files([
|
||||
'gtkinvisible.c',
|
||||
'gtklabel.c',
|
||||
'gtklayout.c',
|
||||
'gtklayoutchild.c',
|
||||
'gtklayoutmanager.c',
|
||||
'gtklevelbar.c',
|
||||
'gtklinkbutton.c',
|
||||
'gtklistbox.c',
|
||||
@@ -510,6 +514,8 @@ gtk_public_headers = files([
|
||||
'gtkinvisible.h',
|
||||
'gtklabel.h',
|
||||
'gtklayout.h',
|
||||
'gtklayoutchild.h',
|
||||
'gtklayoutmanager.h',
|
||||
'gtklevelbar.h',
|
||||
'gtklinkbutton.h',
|
||||
'gtklistbox.h',
|
||||
|
Reference in New Issue
Block a user