1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25 #include "config.h"
26 #include <string.h>
27 #include "gtkframe.h"
28 #include "gtklabel.h"
29 #include "gtkprivate.h"
30 #include "gtktypebuiltins.h"
31 #include "gtkintl.h"
32 #include "gtkbuildable.h"
33 #include "gtkwidgetpath.h"
34 #include "gtkcssnodeprivate.h"
35 #include "gtkcsscustomgadgetprivate.h"
36 #include "gtkwidgetprivate.h"
37 #include "gtkcontainerprivate.h"
38 #include "gtkstylecontextprivate.h"
39 #include "gtkcssstylepropertyprivate.h"
40 #include "gtklabel.h"
41
42 #include "a11y/gtkframeaccessible.h"
43
44 /**
45 * SECTION:gtkframe
46 * @Short_description: A bin with a decorative frame and optional label
47 * @Title: GtkFrame
48 *
49 * The frame widget is a bin that surrounds its child with a decorative
50 * frame and an optional label. If present, the label is drawn in a gap
51 * in the top side of the frame. The position of the label can be
52 * controlled with gtk_frame_set_label_align().
53 *
54 * # GtkFrame as GtkBuildable
55 *
56 * The GtkFrame implementation of the #GtkBuildable interface supports
57 * placing a child in the label position by specifying “label” as the
58 * “type” attribute of a `<child>` element. A normal content child can
59 * be specified without specifying a `<child>` type attribute.
60 *
61 * An example of a UI definition fragment with `GtkFrame`:
62 *
63 * |[<!-- language="xml" -->
64 * <object class="GtkFrame">
65 * <child type="label">
66 * <object class="GtkLabel" id="frame-label"/>
67 * </child>
68 * <child>
69 * <object class="GtkEntry" id="frame-content"/>
70 * </child>
71 * </object>
72 * ]|
73 *
74 * # CSS nodes
75 *
76 * |[<!-- language="plain" -->
77 * frame
78 * ├── border[.flat]
79 * ├── <label widget>
80 * ╰── <child>
81 * ]|
82 *
83 * GtkFrame has a main CSS node named “frame” and a subnode named “border”. The
84 * “border” node is used to draw the visible border. You can set the appearance
85 * of the border using CSS properties like “border-style” on the “border” node.
86 *
87 * The border node can be given the style class “.flat”, which is used by themes
88 * to disable drawing of the border. To do this from code, call
89 * gtk_frame_set_shadow_type() with %GTK_SHADOW_NONE to add the “.flat” class or
90 * any other shadow type to remove it.
91 */
92
93
94 #define LABEL_PAD 1
95 #define LABEL_SIDE_PAD 2
96
97 struct _GtkFramePrivate
98 {
99 /* Properties */
100 GtkWidget *label_widget;
101
102 GtkCssGadget *gadget;
103 GtkCssGadget *border_gadget;
104
105 gint16 shadow_type;
106 gfloat label_xalign;
107 gfloat label_yalign;
108 /* Properties */
109
110 GtkAllocation child_allocation;
111 GtkAllocation label_allocation;
112 };
113
114 enum {
115 PROP_0,
116 PROP_LABEL,
117 PROP_LABEL_XALIGN,
118 PROP_LABEL_YALIGN,
119 PROP_SHADOW_TYPE,
120 PROP_LABEL_WIDGET,
121 LAST_PROP
122 };
123
124 static GParamSpec *frame_props[LAST_PROP];
125
126 static void gtk_frame_finalize (GObject *object);
127 static void gtk_frame_set_property (GObject *object,
128 guint param_id,
129 const GValue *value,
130 GParamSpec *pspec);
131 static void gtk_frame_get_property (GObject *object,
132 guint param_id,
133 GValue *value,
134 GParamSpec *pspec);
135 static gboolean gtk_frame_draw (GtkWidget *widget,
136 cairo_t *cr);
137 static void gtk_frame_size_allocate (GtkWidget *widget,
138 GtkAllocation *allocation);
139 static void gtk_frame_remove (GtkContainer *container,
140 GtkWidget *child);
141 static void gtk_frame_forall (GtkContainer *container,
142 gboolean include_internals,
143 GtkCallback callback,
144 gpointer callback_data);
145
146 static void gtk_frame_compute_child_allocation (GtkFrame *frame,
147 GtkAllocation *child_allocation);
148 static void gtk_frame_real_compute_child_allocation (GtkFrame *frame,
149 GtkAllocation *child_allocation);
150
151 /* GtkBuildable */
152 static void gtk_frame_buildable_init (GtkBuildableIface *iface);
153 static void gtk_frame_buildable_add_child (GtkBuildable *buildable,
154 GtkBuilder *builder,
155 GObject *child,
156 const gchar *type);
157
158 static void gtk_frame_get_preferred_width (GtkWidget *widget,
159 gint *minimum_size,
160 gint *natural_size);
161 static void gtk_frame_get_preferred_height (GtkWidget *widget,
162 gint *minimum_size,
163 gint *natural_size);
164 static void gtk_frame_get_preferred_height_for_width(GtkWidget *layout,
165 gint width,
166 gint *minimum_height,
167 gint *natural_height);
168 static void gtk_frame_get_preferred_width_for_height(GtkWidget *layout,
169 gint width,
170 gint *minimum_height,
171 gint *natural_height);
172 static void gtk_frame_state_flags_changed (GtkWidget *widget,
173 GtkStateFlags previous_state);
174
175 static void gtk_frame_measure (GtkCssGadget *gadget,
176 GtkOrientation orientation,
177 gint for_size,
178 gint *minimum_size,
179 gint *natural_size,
180 gint *minimum_baseline,
181 gint *natural_baseline,
182 gpointer data);
183 static void gtk_frame_measure_border (GtkCssGadget *gadget,
184 GtkOrientation orientation,
185 gint for_size,
186 gint *minimum_size,
187 gint *natural_size,
188 gint *minimum_baseline,
189 gint *natural_baseline,
190 gpointer data);
191 static void gtk_frame_allocate (GtkCssGadget *gadget,
192 const GtkAllocation *allocation,
193 int baseline,
194 GtkAllocation *out_clip,
195 gpointer data);
196 static void gtk_frame_allocate_border (GtkCssGadget *gadget,
197 const GtkAllocation *allocation,
198 int baseline,
199 GtkAllocation *out_clip,
200 gpointer data);
201 static gboolean gtk_frame_render (GtkCssGadget *gadget,
202 cairo_t *cr,
203 int x,
204 int y,
205 int width,
206 int height,
207 gpointer data);
208
209
G_DEFINE_TYPE_WITH_CODE(GtkFrame,gtk_frame,GTK_TYPE_BIN,G_ADD_PRIVATE (GtkFrame)G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_frame_buildable_init))210 G_DEFINE_TYPE_WITH_CODE (GtkFrame, gtk_frame, GTK_TYPE_BIN,
211 G_ADD_PRIVATE (GtkFrame)
212 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
213 gtk_frame_buildable_init))
214
215 static void
216 gtk_frame_class_init (GtkFrameClass *class)
217 {
218 GObjectClass *gobject_class;
219 GtkWidgetClass *widget_class;
220 GtkContainerClass *container_class;
221
222 gobject_class = (GObjectClass*) class;
223 widget_class = GTK_WIDGET_CLASS (class);
224 container_class = GTK_CONTAINER_CLASS (class);
225
226 gobject_class->finalize = gtk_frame_finalize;
227 gobject_class->set_property = gtk_frame_set_property;
228 gobject_class->get_property = gtk_frame_get_property;
229
230 frame_props[PROP_LABEL] =
231 g_param_spec_string ("label",
232 P_("Label"),
233 P_("Text of the frame's label"),
234 NULL,
235 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
236
237 frame_props[PROP_LABEL_XALIGN] =
238 g_param_spec_float ("label-xalign",
239 P_("Label xalign"),
240 P_("The horizontal alignment of the label"),
241 0.0, 1.0,
242 0.0,
243 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
244
245 frame_props[PROP_LABEL_YALIGN] =
246 g_param_spec_float ("label-yalign",
247 P_("Label yalign"),
248 P_("The vertical alignment of the label"),
249 0.0, 1.0,
250 0.5,
251 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
252
253 frame_props[PROP_SHADOW_TYPE] =
254 g_param_spec_enum ("shadow-type",
255 P_("Frame shadow"),
256 P_("Appearance of the frame border"),
257 GTK_TYPE_SHADOW_TYPE,
258 GTK_SHADOW_ETCHED_IN,
259 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
260
261 frame_props[PROP_LABEL_WIDGET] =
262 g_param_spec_object ("label-widget",
263 P_("Label widget"),
264 P_("A widget to display in place of the usual frame label"),
265 GTK_TYPE_WIDGET,
266 GTK_PARAM_READWRITE);
267
268 g_object_class_install_properties (gobject_class, LAST_PROP, frame_props);
269
270 widget_class->draw = gtk_frame_draw;
271 widget_class->size_allocate = gtk_frame_size_allocate;
272 widget_class->get_preferred_width = gtk_frame_get_preferred_width;
273 widget_class->get_preferred_height = gtk_frame_get_preferred_height;
274 widget_class->get_preferred_height_for_width = gtk_frame_get_preferred_height_for_width;
275 widget_class->get_preferred_width_for_height = gtk_frame_get_preferred_width_for_height;
276 widget_class->state_flags_changed = gtk_frame_state_flags_changed;
277
278 container_class->remove = gtk_frame_remove;
279 container_class->forall = gtk_frame_forall;
280
281 gtk_container_class_handle_border_width (container_class);
282
283 class->compute_child_allocation = gtk_frame_real_compute_child_allocation;
284
285 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_FRAME_ACCESSIBLE);
286 gtk_widget_class_set_css_name (widget_class, "frame");
287 }
288
289 static void
gtk_frame_buildable_init(GtkBuildableIface * iface)290 gtk_frame_buildable_init (GtkBuildableIface *iface)
291 {
292 iface->add_child = gtk_frame_buildable_add_child;
293 }
294
295 static void
gtk_frame_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * type)296 gtk_frame_buildable_add_child (GtkBuildable *buildable,
297 GtkBuilder *builder,
298 GObject *child,
299 const gchar *type)
300 {
301 if (type && strcmp (type, "label") == 0)
302 gtk_frame_set_label_widget (GTK_FRAME (buildable), GTK_WIDGET (child));
303 else if (!type)
304 gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
305 else
306 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_FRAME (buildable), type);
307 }
308
309 static void
gtk_frame_init(GtkFrame * frame)310 gtk_frame_init (GtkFrame *frame)
311 {
312 GtkFramePrivate *priv;
313 GtkCssNode *widget_node;
314
315 frame->priv = gtk_frame_get_instance_private (frame);
316 priv = frame->priv;
317
318 priv->label_widget = NULL;
319 priv->shadow_type = GTK_SHADOW_ETCHED_IN;
320 priv->label_xalign = 0.0;
321 priv->label_yalign = 0.5;
322
323 widget_node = gtk_widget_get_css_node (GTK_WIDGET (frame));
324 priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
325 GTK_WIDGET (frame),
326 gtk_frame_measure,
327 gtk_frame_allocate,
328 gtk_frame_render,
329 NULL,
330 NULL);
331 priv->border_gadget = gtk_css_custom_gadget_new ("border",
332 GTK_WIDGET (frame),
333 priv->gadget,
334 NULL,
335 gtk_frame_measure_border,
336 gtk_frame_allocate_border,
337 NULL,
338 NULL,
339 NULL);
340
341 gtk_css_gadget_set_state (priv->border_gadget, gtk_widget_get_state_flags (GTK_WIDGET (frame)));
342 }
343
344 static void
gtk_frame_finalize(GObject * object)345 gtk_frame_finalize (GObject *object)
346 {
347 GtkFrame *frame = GTK_FRAME (object);
348 GtkFramePrivate *priv = frame->priv;
349
350 g_clear_object (&priv->border_gadget);
351 g_clear_object (&priv->gadget);
352
353 G_OBJECT_CLASS (gtk_frame_parent_class)->finalize (object);
354 }
355
356 static void
gtk_frame_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)357 gtk_frame_set_property (GObject *object,
358 guint prop_id,
359 const GValue *value,
360 GParamSpec *pspec)
361 {
362 GtkFrame *frame = GTK_FRAME (object);
363 GtkFramePrivate *priv = frame->priv;
364
365 switch (prop_id)
366 {
367 case PROP_LABEL:
368 gtk_frame_set_label (frame, g_value_get_string (value));
369 break;
370 case PROP_LABEL_XALIGN:
371 gtk_frame_set_label_align (frame, g_value_get_float (value),
372 priv->label_yalign);
373 break;
374 case PROP_LABEL_YALIGN:
375 gtk_frame_set_label_align (frame, priv->label_xalign,
376 g_value_get_float (value));
377 break;
378 case PROP_SHADOW_TYPE:
379 gtk_frame_set_shadow_type (frame, g_value_get_enum (value));
380 break;
381 case PROP_LABEL_WIDGET:
382 gtk_frame_set_label_widget (frame, g_value_get_object (value));
383 break;
384 default:
385 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
386 break;
387 }
388 }
389
390 static void
gtk_frame_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)391 gtk_frame_get_property (GObject *object,
392 guint prop_id,
393 GValue *value,
394 GParamSpec *pspec)
395 {
396 GtkFrame *frame = GTK_FRAME (object);
397 GtkFramePrivate *priv = frame->priv;
398
399 switch (prop_id)
400 {
401 case PROP_LABEL:
402 g_value_set_string (value, gtk_frame_get_label (frame));
403 break;
404 case PROP_LABEL_XALIGN:
405 g_value_set_float (value, priv->label_xalign);
406 break;
407 case PROP_LABEL_YALIGN:
408 g_value_set_float (value, priv->label_yalign);
409 break;
410 case PROP_SHADOW_TYPE:
411 g_value_set_enum (value, priv->shadow_type);
412 break;
413 case PROP_LABEL_WIDGET:
414 g_value_set_object (value,
415 priv->label_widget ?
416 G_OBJECT (priv->label_widget) : NULL);
417 break;
418 default:
419 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
420 break;
421 }
422 }
423
424 /**
425 * gtk_frame_new:
426 * @label: (allow-none): the text to use as the label of the frame
427 *
428 * Creates a new #GtkFrame, with optional label @label.
429 * If @label is %NULL, the label is omitted.
430 *
431 * Returns: a new #GtkFrame widget
432 **/
433 GtkWidget*
gtk_frame_new(const gchar * label)434 gtk_frame_new (const gchar *label)
435 {
436 return g_object_new (GTK_TYPE_FRAME, "label", label, NULL);
437 }
438
439 static void
gtk_frame_remove(GtkContainer * container,GtkWidget * child)440 gtk_frame_remove (GtkContainer *container,
441 GtkWidget *child)
442 {
443 GtkFrame *frame = GTK_FRAME (container);
444 GtkFramePrivate *priv = frame->priv;
445
446 if (priv->label_widget == child)
447 gtk_frame_set_label_widget (frame, NULL);
448 else
449 GTK_CONTAINER_CLASS (gtk_frame_parent_class)->remove (container, child);
450 }
451
452 static void
gtk_frame_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)453 gtk_frame_forall (GtkContainer *container,
454 gboolean include_internals,
455 GtkCallback callback,
456 gpointer callback_data)
457 {
458 GtkBin *bin = GTK_BIN (container);
459 GtkFrame *frame = GTK_FRAME (container);
460 GtkFramePrivate *priv = frame->priv;
461 GtkWidget *child;
462
463 child = gtk_bin_get_child (bin);
464 if (child)
465 (* callback) (child, callback_data);
466
467 if (priv->label_widget)
468 (* callback) (priv->label_widget, callback_data);
469 }
470
471 /**
472 * gtk_frame_set_label:
473 * @frame: a #GtkFrame
474 * @label: (allow-none): the text to use as the label of the frame
475 *
476 * Removes the current #GtkFrame:label-widget. If @label is not %NULL, creates a
477 * new #GtkLabel with that text and adds it as the #GtkFrame:label-widget.
478 **/
479 void
gtk_frame_set_label(GtkFrame * frame,const gchar * label)480 gtk_frame_set_label (GtkFrame *frame,
481 const gchar *label)
482 {
483 g_return_if_fail (GTK_IS_FRAME (frame));
484
485 if (!label)
486 {
487 gtk_frame_set_label_widget (frame, NULL);
488 }
489 else
490 {
491 GtkWidget *child = gtk_label_new (label);
492 gtk_widget_show (child);
493
494 gtk_frame_set_label_widget (frame, child);
495 }
496 }
497
498 /**
499 * gtk_frame_get_label:
500 * @frame: a #GtkFrame
501 *
502 * If the frame’s label widget is a #GtkLabel, returns the
503 * text in the label widget. (The frame will have a #GtkLabel
504 * for the label widget if a non-%NULL argument was passed
505 * to gtk_frame_new().)
506 *
507 * Returns: (nullable): the text in the label, or %NULL if there
508 * was no label widget or the lable widget was not
509 * a #GtkLabel. This string is owned by GTK+ and
510 * must not be modified or freed.
511 **/
512 const gchar *
gtk_frame_get_label(GtkFrame * frame)513 gtk_frame_get_label (GtkFrame *frame)
514 {
515 GtkFramePrivate *priv;
516
517 g_return_val_if_fail (GTK_IS_FRAME (frame), NULL);
518
519 priv = frame->priv;
520
521 if (GTK_IS_LABEL (priv->label_widget))
522 return gtk_label_get_text (GTK_LABEL (priv->label_widget));
523 else
524 return NULL;
525 }
526
527 /**
528 * gtk_frame_set_label_widget:
529 * @frame: a #GtkFrame
530 * @label_widget: (nullable): the new label widget
531 *
532 * Sets the #GtkFrame:label-widget for the frame. This is the widget that
533 * will appear embedded in the top edge of the frame as a title.
534 **/
535 void
gtk_frame_set_label_widget(GtkFrame * frame,GtkWidget * label_widget)536 gtk_frame_set_label_widget (GtkFrame *frame,
537 GtkWidget *label_widget)
538 {
539 GtkFramePrivate *priv;
540 gboolean need_resize = FALSE;
541
542 g_return_if_fail (GTK_IS_FRAME (frame));
543 g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
544 g_return_if_fail (label_widget == NULL || gtk_widget_get_parent (label_widget) == NULL);
545
546 priv = frame->priv;
547
548 if (priv->label_widget == label_widget)
549 return;
550
551 if (priv->label_widget)
552 {
553 need_resize = gtk_widget_get_visible (priv->label_widget);
554 gtk_widget_unparent (priv->label_widget);
555 }
556
557 priv->label_widget = label_widget;
558
559 if (label_widget)
560 {
561 priv->label_widget = label_widget;
562 gtk_widget_set_parent (label_widget, GTK_WIDGET (frame));
563 need_resize |= gtk_widget_get_visible (label_widget);
564 }
565
566 if (gtk_widget_get_visible (GTK_WIDGET (frame)) && need_resize)
567 gtk_widget_queue_resize (GTK_WIDGET (frame));
568
569 g_object_freeze_notify (G_OBJECT (frame));
570 g_object_notify_by_pspec (G_OBJECT (frame), frame_props[PROP_LABEL_WIDGET]);
571 g_object_notify_by_pspec (G_OBJECT (frame), frame_props[PROP_LABEL]);
572 g_object_thaw_notify (G_OBJECT (frame));
573 }
574
575 /**
576 * gtk_frame_get_label_widget:
577 * @frame: a #GtkFrame
578 *
579 * Retrieves the label widget for the frame. See
580 * gtk_frame_set_label_widget().
581 *
582 * Returns: (nullable) (transfer none): the label widget, or %NULL if
583 * there is none.
584 **/
585 GtkWidget *
gtk_frame_get_label_widget(GtkFrame * frame)586 gtk_frame_get_label_widget (GtkFrame *frame)
587 {
588 g_return_val_if_fail (GTK_IS_FRAME (frame), NULL);
589
590 return frame->priv->label_widget;
591 }
592
593 /**
594 * gtk_frame_set_label_align:
595 * @frame: a #GtkFrame
596 * @xalign: The position of the label along the top edge
597 * of the widget. A value of 0.0 represents left alignment;
598 * 1.0 represents right alignment.
599 * @yalign: The y alignment of the label. A value of 0.0 aligns under
600 * the frame; 1.0 aligns above the frame. If the values are exactly
601 * 0.0 or 1.0 the gap in the frame won’t be painted because the label
602 * will be completely above or below the frame.
603 *
604 * Sets the alignment of the frame widget’s label. The
605 * default values for a newly created frame are 0.0 and 0.5.
606 **/
607 void
gtk_frame_set_label_align(GtkFrame * frame,gfloat xalign,gfloat yalign)608 gtk_frame_set_label_align (GtkFrame *frame,
609 gfloat xalign,
610 gfloat yalign)
611 {
612 GtkFramePrivate *priv;
613
614 g_return_if_fail (GTK_IS_FRAME (frame));
615
616 priv = frame->priv;
617
618 xalign = CLAMP (xalign, 0.0, 1.0);
619 yalign = CLAMP (yalign, 0.0, 1.0);
620
621 g_object_freeze_notify (G_OBJECT (frame));
622 if (xalign != priv->label_xalign)
623 {
624 priv->label_xalign = xalign;
625 g_object_notify_by_pspec (G_OBJECT (frame), frame_props[PROP_LABEL_XALIGN]);
626 }
627
628 if (yalign != priv->label_yalign)
629 {
630 priv->label_yalign = yalign;
631 g_object_notify_by_pspec (G_OBJECT (frame), frame_props[PROP_LABEL_YALIGN]);
632 }
633
634 g_object_thaw_notify (G_OBJECT (frame));
635 gtk_widget_queue_resize (GTK_WIDGET (frame));
636 }
637
638 /**
639 * gtk_frame_get_label_align:
640 * @frame: a #GtkFrame
641 * @xalign: (out) (allow-none): location to store X alignment of
642 * frame’s label, or %NULL
643 * @yalign: (out) (allow-none): location to store X alignment of
644 * frame’s label, or %NULL
645 *
646 * Retrieves the X and Y alignment of the frame’s label. See
647 * gtk_frame_set_label_align().
648 **/
649 void
gtk_frame_get_label_align(GtkFrame * frame,gfloat * xalign,gfloat * yalign)650 gtk_frame_get_label_align (GtkFrame *frame,
651 gfloat *xalign,
652 gfloat *yalign)
653 {
654 GtkFramePrivate *priv;
655
656 g_return_if_fail (GTK_IS_FRAME (frame));
657
658 priv = frame->priv;
659
660 if (xalign)
661 *xalign = priv->label_xalign;
662 if (yalign)
663 *yalign = priv->label_yalign;
664 }
665
666 /**
667 * gtk_frame_set_shadow_type:
668 * @frame: a #GtkFrame
669 * @type: the new #GtkShadowType
670 *
671 * Sets the #GtkFrame:shadow-type for @frame, i.e. whether it is drawn without
672 * (%GTK_SHADOW_NONE) or with (other values) a visible border. Values other than
673 * %GTK_SHADOW_NONE are treated identically by GtkFrame. The chosen type is
674 * applied by removing or adding the .flat class to the CSS node named border.
675 **/
676 void
gtk_frame_set_shadow_type(GtkFrame * frame,GtkShadowType type)677 gtk_frame_set_shadow_type (GtkFrame *frame,
678 GtkShadowType type)
679 {
680 GtkFramePrivate *priv;
681
682 g_return_if_fail (GTK_IS_FRAME (frame));
683
684 priv = frame->priv;
685
686 if ((GtkShadowType) priv->shadow_type != type)
687 {
688 priv->shadow_type = type;
689
690 if (type == GTK_SHADOW_NONE)
691 gtk_css_gadget_add_class (priv->border_gadget, GTK_STYLE_CLASS_FLAT);
692 else
693 gtk_css_gadget_remove_class (priv->border_gadget, GTK_STYLE_CLASS_FLAT);
694
695 g_object_notify_by_pspec (G_OBJECT (frame), frame_props[PROP_SHADOW_TYPE]);
696 }
697 }
698
699 /**
700 * gtk_frame_get_shadow_type:
701 * @frame: a #GtkFrame
702 *
703 * Retrieves the shadow type of the frame. See
704 * gtk_frame_set_shadow_type().
705 *
706 * Returns: the current shadow type of the frame.
707 **/
708 GtkShadowType
gtk_frame_get_shadow_type(GtkFrame * frame)709 gtk_frame_get_shadow_type (GtkFrame *frame)
710 {
711 g_return_val_if_fail (GTK_IS_FRAME (frame), GTK_SHADOW_ETCHED_IN);
712
713 return frame->priv->shadow_type;
714 }
715
716 static gboolean
gtk_frame_draw(GtkWidget * widget,cairo_t * cr)717 gtk_frame_draw (GtkWidget *widget,
718 cairo_t *cr)
719 {
720 gtk_css_gadget_draw (GTK_FRAME (widget)->priv->gadget, cr);
721
722 return FALSE;
723 }
724
725 static gboolean
gtk_frame_render(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)726 gtk_frame_render (GtkCssGadget *gadget,
727 cairo_t *cr,
728 int x,
729 int y,
730 int width,
731 int height,
732 gpointer data)
733 {
734 GtkWidget *widget;
735 GtkFramePrivate *priv;
736 gint xc, yc, w, h;
737 GtkAllocation allocation;
738
739 widget = gtk_css_gadget_get_owner (gadget);
740 priv = GTK_FRAME (widget)->priv;
741
742 cairo_save (cr);
743
744 gtk_widget_get_allocation (widget, &allocation);
745
746 /* We want to use the standard gadget drawing for the border,
747 * so we clip out the label allocation in order to get the
748 * frame gap.
749 */
750 xc = priv->label_allocation.x - allocation.x;
751 yc = y;
752 w = priv->label_allocation.width;
753 h = priv->label_allocation.height;
754
755 if (GTK_IS_LABEL (priv->label_widget))
756 {
757 PangoRectangle ink_rect;
758
759 /* Shrink the clip area for labels, so we get unclipped
760 * frames for the yalign 0.0 and 1.0 cases.
761 */
762 pango_layout_get_pixel_extents (gtk_label_get_layout (GTK_LABEL (priv->label_widget)), &ink_rect, NULL);
763 yc += (h - ink_rect.height) / 2;
764 h = ink_rect.height;
765 }
766
767 cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
768 cairo_rectangle (cr, xc + w, yc, - w, h);
769 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
770 cairo_clip (cr);
771
772 gtk_css_gadget_draw (priv->border_gadget, cr);
773
774 cairo_restore (cr);
775
776 GTK_WIDGET_CLASS (gtk_frame_parent_class)->draw (widget, cr);
777
778 return FALSE;
779 }
780
781 static void
gtk_frame_size_allocate(GtkWidget * widget,GtkAllocation * allocation)782 gtk_frame_size_allocate (GtkWidget *widget,
783 GtkAllocation *allocation)
784 {
785 GtkAllocation clip;
786
787 gtk_widget_set_allocation (widget, allocation);
788
789 gtk_css_gadget_allocate (GTK_FRAME (widget)->priv->gadget,
790 allocation,
791 gtk_widget_get_allocated_baseline (widget),
792 &clip);
793
794 gtk_widget_set_clip (widget, &clip);
795 }
796
797 static void
gtk_frame_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer data)798 gtk_frame_allocate (GtkCssGadget *gadget,
799 const GtkAllocation *allocation,
800 int baseline,
801 GtkAllocation *out_clip,
802 gpointer data)
803 {
804 GtkWidget *widget;
805 GtkFrame *frame;
806 GtkFramePrivate *priv;
807 GtkAllocation new_allocation;
808 GtkAllocation frame_allocation;
809 gint height_extra;
810 GtkAllocation clip;
811
812 widget = gtk_css_gadget_get_owner (gadget);
813 frame = GTK_FRAME (widget);
814 priv = frame->priv;
815
816 gtk_frame_compute_child_allocation (frame, &new_allocation);
817 priv->child_allocation = new_allocation;
818
819 if (priv->label_widget &&
820 gtk_widget_get_visible (priv->label_widget))
821 {
822 gint nat_width, width, height;
823 gfloat xalign;
824
825 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
826 xalign = priv->label_xalign;
827 else
828 xalign = 1 - priv->label_xalign;
829
830 gtk_widget_get_preferred_width (priv->label_widget, NULL, &nat_width);
831 width = MIN (new_allocation.width, nat_width);
832 gtk_widget_get_preferred_height_for_width (priv->label_widget, width, &height, NULL);
833
834 priv->label_allocation.x = new_allocation.x + (new_allocation.width - width) * xalign;
835 priv->label_allocation.y = new_allocation.y - height;
836 priv->label_allocation.height = height;
837 priv->label_allocation.width = width;
838
839 gtk_widget_size_allocate (priv->label_widget, &priv->label_allocation);
840
841 height_extra = (1 - priv->label_yalign) * height;
842 }
843 else
844 height_extra = 0;
845
846 frame_allocation.x = priv->child_allocation.x;
847 frame_allocation.y = priv->child_allocation.y - height_extra;
848 frame_allocation.width = priv->child_allocation.width;
849 frame_allocation.height = priv->child_allocation.height + height_extra;
850
851 gtk_css_gadget_allocate (priv->border_gadget,
852 &frame_allocation,
853 -1,
854 &clip);
855
856 gtk_container_get_children_clip (GTK_CONTAINER (widget), out_clip);
857 gdk_rectangle_union (out_clip, &clip, out_clip);
858 }
859
860 static void
gtk_frame_allocate_border(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer data)861 gtk_frame_allocate_border (GtkCssGadget *gadget,
862 const GtkAllocation *allocation,
863 int baseline,
864 GtkAllocation *out_clip,
865 gpointer data)
866 {
867 GtkWidget *widget;
868 GtkFramePrivate *priv;
869 GtkWidget *child;
870 GtkAllocation child_allocation;
871 gint height_extra;
872
873 widget = gtk_css_gadget_get_owner (gadget);
874 priv = GTK_FRAME (widget)->priv;
875
876 if (priv->label_widget &&
877 gtk_widget_get_visible (priv->label_widget))
878 height_extra = priv->label_allocation.height * (1 - priv->label_yalign);
879 else
880 height_extra = 0;
881
882 child_allocation = *allocation;
883 child_allocation.y += height_extra;
884 child_allocation.height -= height_extra;
885
886 child = gtk_bin_get_child (GTK_BIN (widget));
887 if (child && gtk_widget_get_visible (child))
888 gtk_widget_size_allocate (child, &child_allocation);
889
890 *out_clip = *allocation;
891 }
892
893 static void
gtk_frame_compute_child_allocation(GtkFrame * frame,GtkAllocation * child_allocation)894 gtk_frame_compute_child_allocation (GtkFrame *frame,
895 GtkAllocation *child_allocation)
896 {
897 g_return_if_fail (GTK_IS_FRAME (frame));
898 g_return_if_fail (child_allocation != NULL);
899
900 GTK_FRAME_GET_CLASS (frame)->compute_child_allocation (frame, child_allocation);
901 }
902
903 static void
gtk_frame_real_compute_child_allocation(GtkFrame * frame,GtkAllocation * child_allocation)904 gtk_frame_real_compute_child_allocation (GtkFrame *frame,
905 GtkAllocation *child_allocation)
906 {
907 GtkFramePrivate *priv = frame->priv;
908 GtkAllocation allocation;
909 gint height;
910
911 gtk_css_gadget_get_content_allocation (priv->gadget, &allocation, NULL);
912
913 if (priv->label_widget)
914 {
915 gint nat_width, width;
916
917 gtk_widget_get_preferred_width (priv->label_widget, NULL, &nat_width);
918 width = MIN (allocation.width, nat_width);
919 gtk_widget_get_preferred_height_for_width (priv->label_widget, width, &height, NULL);
920 }
921 else
922 height = 0;
923
924 child_allocation->x = allocation.x;
925 child_allocation->y = allocation.y + height;
926 child_allocation->width = MAX (1, allocation.width);
927 child_allocation->height = MAX (1, allocation.height - height);
928 }
929
930 static void
gtk_frame_measure(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer data)931 gtk_frame_measure (GtkCssGadget *gadget,
932 GtkOrientation orientation,
933 int for_size,
934 int *minimum,
935 int *natural,
936 int *minimum_baseline,
937 int *natural_baseline,
938 gpointer data)
939 {
940 GtkWidget *widget;
941 GtkFrame *frame;
942 GtkFramePrivate *priv;
943 gint child_min, child_nat;
944
945 widget = gtk_css_gadget_get_owner (gadget);
946 frame = GTK_FRAME (widget);
947 priv = frame->priv;
948
949 gtk_css_gadget_get_preferred_size (priv->border_gadget,
950 orientation,
951 for_size,
952 &child_min,
953 &child_nat,
954 NULL, NULL);
955
956 *minimum = child_min;
957 *natural = child_nat;
958
959 if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
960 {
961 if (orientation == GTK_ORIENTATION_HORIZONTAL)
962 {
963 gtk_widget_get_preferred_width (priv->label_widget, &child_min, &child_nat);
964 *minimum = MAX (child_min, *minimum);
965 *natural = MAX (child_nat, *natural);
966 }
967 else
968 {
969 if (for_size > 0)
970 gtk_widget_get_preferred_height_for_width (priv->label_widget,
971 for_size, &child_min, &child_nat);
972 else
973 gtk_widget_get_preferred_height (priv->label_widget, &child_min, &child_nat);
974
975 *minimum += child_min;
976 *natural += child_nat;
977 }
978 }
979 }
980
981 static void
gtk_frame_measure_border(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer data)982 gtk_frame_measure_border (GtkCssGadget *gadget,
983 GtkOrientation orientation,
984 int for_size,
985 int *minimum,
986 int *natural,
987 int *minimum_baseline,
988 int *natural_baseline,
989 gpointer data)
990 {
991 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
992 GtkWidget *child;
993 int child_min, child_nat;
994
995 child = gtk_bin_get_child (GTK_BIN (widget));
996 if (child && gtk_widget_get_visible (child))
997 {
998 if (orientation == GTK_ORIENTATION_HORIZONTAL)
999 {
1000 gtk_widget_get_preferred_width (child, &child_min, &child_nat);
1001 }
1002 else
1003 {
1004 if (for_size > 0)
1005 gtk_widget_get_preferred_height_for_width (child, for_size, &child_min, &child_nat);
1006 else
1007 gtk_widget_get_preferred_height (child, &child_min, &child_nat);
1008 }
1009
1010 *minimum = child_min;
1011 *natural = child_nat;
1012 }
1013 else
1014 {
1015 *minimum = 0;
1016 *natural = 0;
1017 }
1018 }
1019
1020
1021 static void
gtk_frame_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)1022 gtk_frame_get_preferred_width (GtkWidget *widget,
1023 gint *minimum,
1024 gint *natural)
1025 {
1026 gtk_css_gadget_get_preferred_size (GTK_FRAME (widget)->priv->gadget,
1027 GTK_ORIENTATION_HORIZONTAL,
1028 -1,
1029 minimum, natural,
1030 NULL, NULL);
1031 }
1032
1033 static void
gtk_frame_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum,gint * natural)1034 gtk_frame_get_preferred_width_for_height (GtkWidget *widget,
1035 gint height,
1036 gint *minimum,
1037 gint *natural)
1038 {
1039 gtk_css_gadget_get_preferred_size (GTK_FRAME (widget)->priv->gadget,
1040 GTK_ORIENTATION_HORIZONTAL,
1041 height,
1042 minimum, natural,
1043 NULL, NULL);
1044 }
1045
1046 static void
gtk_frame_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)1047 gtk_frame_get_preferred_height (GtkWidget *widget,
1048 gint *minimum,
1049 gint *natural)
1050 {
1051 gtk_css_gadget_get_preferred_size (GTK_FRAME (widget)->priv->gadget,
1052 GTK_ORIENTATION_VERTICAL,
1053 -1,
1054 minimum, natural,
1055 NULL, NULL);
1056 }
1057
1058
1059 static void
gtk_frame_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum,gint * natural)1060 gtk_frame_get_preferred_height_for_width (GtkWidget *widget,
1061 gint width,
1062 gint *minimum,
1063 gint *natural)
1064 {
1065 gtk_css_gadget_get_preferred_size (GTK_FRAME (widget)->priv->gadget,
1066 GTK_ORIENTATION_VERTICAL,
1067 width,
1068 minimum, natural,
1069 NULL, NULL);
1070 }
1071
1072 static void
gtk_frame_state_flags_changed(GtkWidget * widget,GtkStateFlags previous_state)1073 gtk_frame_state_flags_changed (GtkWidget *widget,
1074 GtkStateFlags previous_state)
1075 {
1076 gtk_css_gadget_set_state (GTK_FRAME (widget)->priv->border_gadget, gtk_widget_get_state_flags (widget));
1077
1078 GTK_WIDGET_CLASS (gtk_frame_parent_class)->state_flags_changed (widget, previous_state);
1079 }
1080