1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/gtk/gtk_chrome_shrinkable_hbox.h"
15 PROP_HIDE_CHILD_DIRECTLY
18 struct SizeAllocateData
{
19 GtkChromeShrinkableHBox
* box
;
20 GtkAllocation
* allocation
;
21 GtkTextDirection direction
;
25 // Maximum child width when |homogeneous| is TRUE.
26 int homogeneous_child_width
;
29 void CountVisibleChildren(GtkWidget
* child
, gpointer userdata
) {
30 if (gtk_widget_get_visible(child
))
31 ++(*reinterpret_cast<int*>(userdata
));
34 void SumChildrenWidthRequisition(GtkWidget
* child
, gpointer userdata
) {
35 if (gtk_widget_get_visible(child
)) {
37 gtk_widget_get_child_requisition(child
, &req
);
38 (*reinterpret_cast<int*>(userdata
)) += std::max(req
.width
, 0);
42 void ChildSizeAllocate(GtkWidget
* child
, gpointer userdata
) {
43 if (!gtk_widget_get_visible(child
))
46 SizeAllocateData
* data
= reinterpret_cast<SizeAllocateData
*>(userdata
);
47 GtkAllocation child_allocation
;
48 gtk_widget_get_allocation(child
, &child_allocation
);
50 if (data
->homogeneous
) {
51 // Make sure the child is not overlapped with others' boundary.
52 if (child_allocation
.width
> data
->homogeneous_child_width
) {
54 (child_allocation
.width
- data
->homogeneous_child_width
) / 2;
55 child_allocation
.width
= data
->homogeneous_child_width
;
59 GtkPackType pack_type
;
60 gtk_box_query_child_packing(GTK_BOX(data
->box
), child
, NULL
, NULL
,
61 &padding
, &pack_type
);
63 if ((data
->direction
== GTK_TEXT_DIR_RTL
&& pack_type
== GTK_PACK_START
) ||
64 (data
->direction
!= GTK_TEXT_DIR_RTL
&& pack_type
== GTK_PACK_END
)) {
65 // All children are right aligned, so make sure the child won't overflow
66 // its parent's left edge.
67 int overflow
= (data
->allocation
->x
+ data
->border_width
+ padding
-
70 child_allocation
.width
-= overflow
;
71 child_allocation
.x
+= overflow
;
74 // All children are left aligned, so make sure the child won't overflow
75 // its parent's right edge.
76 int overflow
= (child_allocation
.x
+ child_allocation
.width
+ padding
-
77 (data
->allocation
->x
+ data
->allocation
->width
- data
->border_width
));
79 child_allocation
.width
-= overflow
;
83 GtkAllocation current_allocation
;
84 gtk_widget_get_allocation(child
, ¤t_allocation
);
86 if (child_allocation
.width
!= current_allocation
.width
) {
87 if (data
->box
->hide_child_directly
|| child_allocation
.width
<= 1)
88 gtk_widget_hide(child
);
90 gtk_widget_size_allocate(child
, &child_allocation
);
98 static void gtk_chrome_shrinkable_hbox_set_property(GObject
* object
,
102 static void gtk_chrome_shrinkable_hbox_get_property(GObject
* object
,
106 static void gtk_chrome_shrinkable_hbox_size_allocate(GtkWidget
* widget
,
107 GtkAllocation
* allocation
);
109 G_DEFINE_TYPE(GtkChromeShrinkableHBox
, gtk_chrome_shrinkable_hbox
,
112 static void gtk_chrome_shrinkable_hbox_class_init(
113 GtkChromeShrinkableHBoxClass
*klass
) {
114 GObjectClass
* object_class
= G_OBJECT_CLASS(klass
);
115 GtkWidgetClass
* widget_class
= GTK_WIDGET_CLASS(klass
);
117 object_class
->set_property
= gtk_chrome_shrinkable_hbox_set_property
;
118 object_class
->get_property
= gtk_chrome_shrinkable_hbox_get_property
;
120 widget_class
->size_allocate
= gtk_chrome_shrinkable_hbox_size_allocate
;
122 g_object_class_install_property(object_class
, PROP_HIDE_CHILD_DIRECTLY
,
123 g_param_spec_boolean("hide-child-directly",
124 "Hide child directly",
125 "Whether the children should be hid directly, "
126 "if there is no enough space in its parent",
128 static_cast<GParamFlags
>(
129 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
)));
132 static void gtk_chrome_shrinkable_hbox_init(GtkChromeShrinkableHBox
* box
) {
133 box
->hide_child_directly
= FALSE
;
134 box
->children_width_requisition
= 0;
137 static void gtk_chrome_shrinkable_hbox_set_property(GObject
* object
,
141 GtkChromeShrinkableHBox
* box
= GTK_CHROME_SHRINKABLE_HBOX(object
);
144 case PROP_HIDE_CHILD_DIRECTLY
:
145 gtk_chrome_shrinkable_hbox_set_hide_child_directly(
146 box
, g_value_get_boolean(value
));
149 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
154 static void gtk_chrome_shrinkable_hbox_get_property(GObject
* object
,
158 GtkChromeShrinkableHBox
* box
= GTK_CHROME_SHRINKABLE_HBOX(object
);
161 case PROP_HIDE_CHILD_DIRECTLY
:
162 g_value_set_boolean(value
, box
->hide_child_directly
);
165 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
170 static void gtk_chrome_shrinkable_hbox_size_allocate(
171 GtkWidget
* widget
, GtkAllocation
* allocation
) {
172 GtkChromeShrinkableHBox
* box
= GTK_CHROME_SHRINKABLE_HBOX(widget
);
173 gint children_width_requisition
= 0;
174 gtk_container_foreach(GTK_CONTAINER(widget
), SumChildrenWidthRequisition
,
175 &children_width_requisition
);
177 GtkAllocation widget_allocation
;
178 gtk_widget_get_allocation(widget
, &widget_allocation
);
180 // If we are allocated to more width or some children are removed or shrunk,
181 // then we need to show all invisible children before calling parent class's
182 // size_allocate method, because the new width may be enough to show those
184 if (widget_allocation
.width
< allocation
->width
||
185 box
->children_width_requisition
> children_width_requisition
) {
186 gtk_container_foreach(GTK_CONTAINER(widget
),
187 reinterpret_cast<GtkCallback
>(gtk_widget_show
), NULL
);
189 // If there were any invisible children, showing them will trigger another
190 // allocate. But we still need to go through the size allocate process
191 // in this iteration, otherwise before the next allocate iteration, the
192 // children may be redrawn on the screen with incorrect size allocation.
195 // Let the parent class do size allocation first. After that all children will
196 // be allocated with reasonable position and size according to their size
198 (GTK_WIDGET_CLASS(gtk_chrome_shrinkable_hbox_parent_class
)->size_allocate
)
199 (widget
, allocation
);
201 gint visible_children_count
=
202 gtk_chrome_shrinkable_hbox_get_visible_child_count(
203 GTK_CHROME_SHRINKABLE_HBOX(widget
));
205 box
->children_width_requisition
= 0;
206 if (visible_children_count
== 0)
209 SizeAllocateData data
;
210 data
.box
= GTK_CHROME_SHRINKABLE_HBOX(widget
);
211 data
.allocation
= allocation
;
212 data
.direction
= gtk_widget_get_direction(widget
);
213 data
.homogeneous
= gtk_box_get_homogeneous(GTK_BOX(widget
));
214 data
.border_width
= gtk_container_get_border_width(GTK_CONTAINER(widget
));
215 data
.homogeneous_child_width
=
216 (allocation
->width
- data
.border_width
* 2 -
217 (visible_children_count
- 1) * gtk_box_get_spacing(GTK_BOX(widget
))) /
218 visible_children_count
;
220 // Shrink or hide children if necessary.
221 gtk_container_foreach(GTK_CONTAINER(widget
), ChildSizeAllocate
, &data
);
223 // Record current width requisition of visible children, so we can know if
224 // it's necessary to show invisible children next time.
225 gtk_container_foreach(GTK_CONTAINER(widget
), SumChildrenWidthRequisition
,
226 &box
->children_width_requisition
);
229 GtkWidget
* gtk_chrome_shrinkable_hbox_new(gboolean hide_child_directly
,
230 gboolean homogeneous
,
232 return GTK_WIDGET(g_object_new(GTK_TYPE_CHROME_SHRINKABLE_HBOX
,
233 "hide-child-directly", hide_child_directly
,
234 "homogeneous", homogeneous
,
239 void gtk_chrome_shrinkable_hbox_set_hide_child_directly(
240 GtkChromeShrinkableHBox
* box
, gboolean hide_child_directly
) {
241 g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box
));
243 if (hide_child_directly
!= box
->hide_child_directly
) {
244 box
->hide_child_directly
= hide_child_directly
;
245 g_object_notify(G_OBJECT(box
), "hide-child-directly");
246 gtk_widget_queue_resize(GTK_WIDGET(box
));
250 gboolean
gtk_chrome_shrinkable_hbox_get_hide_child_directly(
251 GtkChromeShrinkableHBox
* box
) {
252 g_return_val_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box
), FALSE
);
254 return box
->hide_child_directly
;
257 void gtk_chrome_shrinkable_hbox_pack_start(GtkChromeShrinkableHBox
* box
,
260 g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box
));
261 g_return_if_fail(GTK_IS_WIDGET(child
));
263 gtk_box_pack_start(GTK_BOX(box
), child
, FALSE
, FALSE
, 0);
266 void gtk_chrome_shrinkable_hbox_pack_end(GtkChromeShrinkableHBox
* box
,
269 g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box
));
270 g_return_if_fail(GTK_IS_WIDGET(child
));
272 gtk_box_pack_end(GTK_BOX(box
), child
, FALSE
, FALSE
, 0);
275 gint
gtk_chrome_shrinkable_hbox_get_visible_child_count(
276 GtkChromeShrinkableHBox
* box
) {
277 gint visible_children_count
= 0;
278 gtk_container_foreach(GTK_CONTAINER(box
), CountVisibleChildren
,
279 &visible_children_count
);
280 return visible_children_count
;