gtkframe.c 34.3 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2 3 4
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * 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
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
16
 */
17 18

/*
19
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20 21 22 23 24
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

25
#include "config.h"
26
#include <string.h>
Elliot Lee's avatar
Elliot Lee committed
27
#include "gtkframe.h"
28
#include "gtklabel.h"
29
#include "gtkprivate.h"
30
#include "gtktypebuiltins.h"
31
#include "gtkintl.h"
Johan Dahlin's avatar
Johan Dahlin committed
32
#include "gtkbuildable.h"
33
#include "gtkwidgetpath.h"
Matthias Clasen's avatar
Matthias Clasen committed
34 35 36 37 38 39 40
#include "gtkcssnodeprivate.h"
#include "gtkcsscustomgadgetprivate.h"
#include "gtkwidgetprivate.h"
#include "gtkcontainerprivate.h"
#include "gtkstylecontextprivate.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtklabel.h"
41

42
#include "a11y/gtkframeaccessible.h"
Elliot Lee's avatar
Elliot Lee committed
43

44 45 46 47 48
/**
 * SECTION:gtkframe
 * @Short_description: A bin with a decorative frame and optional label
 * @Title: GtkFrame
 *
49 50 51 52
 * The frame widget is a bin that surrounds its child with a decorative
 * frame and an optional label. If present, the label is drawn in a gap
 * in the top side of the frame. The position of the label can be
 * controlled with gtk_frame_set_label_align().
53
 *
54
 * # GtkFrame as GtkBuildable
Matthias Clasen's avatar
Matthias Clasen committed
55
 *
56 57 58 59
 * The GtkFrame implementation of the GtkBuildable interface supports
 * placing a child in the label position by specifying “label” as the
 * “type” attribute of a <child> element. A normal content child can
 * be specified without specifying a <child> type attribute.
Matthias Clasen's avatar
Matthias Clasen committed
60 61
 *
 * An example of a UI definition fragment with GtkFrame:
62
 * |[
63 64 65 66 67 68 69 70
 * <object class="GtkFrame">
 *   <child type="label">
 *     <object class="GtkLabel" id="frame-label"/>
 *   </child>
 *   <child>
 *     <object class="GtkEntry" id="frame-content"/>
 *   </child>
 * </object>
71
 * ]|
72 73 74
 *
 * # CSS nodes
 *
Matthias Clasen's avatar
Matthias Clasen committed
75 76
 * |[<!-- language="plain" -->
 * frame 
Daniel Boles's avatar
Daniel Boles committed
77
 * ├── border[.flat]
Matthias Clasen's avatar
Matthias Clasen committed
78 79 80 81
 * ├── <label widget>
 * ╰── <child>
 * ]|
 *
Daniel Boles's avatar
Daniel Boles committed
82 83 84
 * GtkFrame has a main CSS node named “frame” and a subnode named “border”. The
 * “border” node is used to draw the visible border. You can set the appearance
 * of the border using CSS properties like “border-style” on the “border” node.
85
 *
Daniel Boles's avatar
Daniel Boles committed
86
 * The border node can be given the style class “.flat”, which is used by themes
87
 * to disable drawing of the border. To do this from code, call
Daniel Boles's avatar
Daniel Boles committed
88
 * gtk_frame_set_shadow_type() with %GTK_SHADOW_NONE to add the “.flat” class or
89
 * any other shadow type to remove it.
90 91 92
 */


93 94 95
#define LABEL_PAD 1
#define LABEL_SIDE_PAD 2

96
struct _GtkFramePrivate
97 98 99 100
{
  /* Properties */
  GtkWidget *label_widget;

Matthias Clasen's avatar
Matthias Clasen committed
101 102 103
  GtkCssGadget *gadget;
  GtkCssGadget *border_gadget;

104 105 106 107 108 109
  gint16 shadow_type;
  gfloat label_xalign;
  gfloat label_yalign;
  /* Properties */

  GtkAllocation child_allocation;
110
  GtkAllocation label_allocation;
111 112
};

113
enum {
114 115 116 117
  PROP_0,
  PROP_LABEL,
  PROP_LABEL_XALIGN,
  PROP_LABEL_YALIGN,
118
  PROP_SHADOW_TYPE,
119 120
  PROP_LABEL_WIDGET,
  LAST_PROP
121 122
};

123 124
static GParamSpec *frame_props[LAST_PROP];

Matthias Clasen's avatar
Matthias Clasen committed
125
static void gtk_frame_finalize     (GObject      *object);
126 127 128 129 130 131 132 133
static void gtk_frame_set_property (GObject      *object,
				    guint         param_id,
				    const GValue *value,
				    GParamSpec   *pspec);
static void gtk_frame_get_property (GObject     *object,
				    guint        param_id,
				    GValue      *value,
				    GParamSpec  *pspec);
Benjamin Otte's avatar
Benjamin Otte committed
134 135
static gboolean gtk_frame_draw      (GtkWidget      *widget,
				     cairo_t        *cr);
Elliot Lee's avatar
Elliot Lee committed
136 137
static void gtk_frame_size_allocate (GtkWidget      *widget,
				     GtkAllocation  *allocation);
138 139 140 141 142 143 144 145 146 147 148
static void gtk_frame_remove        (GtkContainer   *container,
				     GtkWidget      *child);
static void gtk_frame_forall        (GtkContainer   *container,
				     gboolean	     include_internals,
			             GtkCallback     callback,
			             gpointer        callback_data);

static void gtk_frame_compute_child_allocation      (GtkFrame      *frame,
						     GtkAllocation *child_allocation);
static void gtk_frame_real_compute_child_allocation (GtkFrame      *frame,
						     GtkAllocation *child_allocation);
Elliot Lee's avatar
Elliot Lee committed
149

Johan Dahlin's avatar
Johan Dahlin committed
150 151
/* GtkBuildable */
static void gtk_frame_buildable_init                (GtkBuildableIface *iface);
152
static void gtk_frame_buildable_add_child           (GtkBuildable *buildable,
Johan Dahlin's avatar
Johan Dahlin committed
153 154 155 156
						     GtkBuilder   *builder,
						     GObject      *child,
						     const gchar  *type);

157
static void gtk_frame_get_preferred_width           (GtkWidget           *widget,
158 159
                                                     gint                *minimum_size,
						     gint                *natural_size);
160
static void gtk_frame_get_preferred_height          (GtkWidget           *widget,
161 162
						     gint                *minimum_size,
						     gint                *natural_size);
163
static void gtk_frame_get_preferred_height_for_width(GtkWidget           *layout,
164 165 166
						     gint                 width,
						     gint                *minimum_height,
						     gint                *natural_height);
167
static void gtk_frame_get_preferred_width_for_height(GtkWidget           *layout,
168 169 170
						     gint                 width,
						     gint                *minimum_height,
						     gint                *natural_height);
Matthias Clasen's avatar
Matthias Clasen committed
171 172 173 174 175 176 177 178 179 180 181
static void gtk_frame_state_flags_changed (GtkWidget     *widget,
                                           GtkStateFlags  previous_state);

static void     gtk_frame_measure        (GtkCssGadget        *gadget,
                                          GtkOrientation       orientation,
                                          gint                 for_size,
                                          gint                *minimum_size,
                                          gint                *natural_size,
                                          gint                *minimum_baseline,
                                          gint                *natural_baseline,
                                          gpointer             data);
182 183 184 185 186 187 188 189
static void     gtk_frame_measure_border (GtkCssGadget        *gadget,
                                          GtkOrientation       orientation,
                                          gint                 for_size,
                                          gint                *minimum_size,
                                          gint                *natural_size,
                                          gint                *minimum_baseline,
                                          gint                *natural_baseline,
                                          gpointer             data);
Matthias Clasen's avatar
Matthias Clasen committed
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
static void     gtk_frame_allocate       (GtkCssGadget        *gadget,
                                          const GtkAllocation *allocation,
                                          int                  baseline,
                                          GtkAllocation       *out_clip,
                                          gpointer             data);
static void     gtk_frame_allocate_border (GtkCssGadget        *gadget,
                                          const GtkAllocation *allocation,
                                          int                  baseline,
                                          GtkAllocation       *out_clip,
                                          gpointer             data);
static gboolean gtk_frame_render         (GtkCssGadget        *gadget,
                                          cairo_t             *cr,
                                          int                  x,
                                          int                  y,
                                          int                  width,
                                          int                  height,
                                          gpointer             data);
207

208

Johan Dahlin's avatar
Johan Dahlin committed
209
G_DEFINE_TYPE_WITH_CODE (GtkFrame, gtk_frame, GTK_TYPE_BIN,
210
                         G_ADD_PRIVATE (GtkFrame)
Johan Dahlin's avatar
Johan Dahlin committed
211
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
212
						gtk_frame_buildable_init))
Elliot Lee's avatar
Elliot Lee committed
213 214 215 216

static void
gtk_frame_class_init (GtkFrameClass *class)
{
217
  GObjectClass *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
218
  GtkWidgetClass *widget_class;
219
  GtkContainerClass *container_class;
Elliot Lee's avatar
Elliot Lee committed
220

221
  gobject_class = (GObjectClass*) class;
222 223
  widget_class = GTK_WIDGET_CLASS (class);
  container_class = GTK_CONTAINER_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
224

Matthias Clasen's avatar
Matthias Clasen committed
225
  gobject_class->finalize = gtk_frame_finalize;
226 227 228
  gobject_class->set_property = gtk_frame_set_property;
  gobject_class->get_property = gtk_frame_get_property;

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
  frame_props[PROP_LABEL] =
      g_param_spec_string ("label",
                           P_("Label"),
                           P_("Text of the frame's label"),
                           NULL,
                           GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  frame_props[PROP_LABEL_XALIGN] =
      g_param_spec_float ("label-xalign",
                          P_("Label xalign"),
                          P_("The horizontal alignment of the label"),
                          0.0, 1.0,
                          0.0,
                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  frame_props[PROP_LABEL_YALIGN] =
      g_param_spec_float ("label-yalign",
                          P_("Label yalign"),
                          P_("The vertical alignment of the label"),
                          0.0, 1.0,
                          0.5,
                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  frame_props[PROP_SHADOW_TYPE] =
      g_param_spec_enum ("shadow-type",
                         P_("Frame shadow"),
                         P_("Appearance of the frame border"),
			GTK_TYPE_SHADOW_TYPE,
			GTK_SHADOW_ETCHED_IN,
                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  frame_props[PROP_LABEL_WIDGET] =
      g_param_spec_object ("label-widget",
                           P_("Label widget"),
                           P_("A widget to display in place of the usual frame label"),
                           GTK_TYPE_WIDGET,
                           GTK_PARAM_READWRITE);

  g_object_class_install_properties (gobject_class, LAST_PROP, frame_props);
268

269 270 271 272 273 274
  widget_class->draw                           = gtk_frame_draw;
  widget_class->size_allocate                  = gtk_frame_size_allocate;
  widget_class->get_preferred_width            = gtk_frame_get_preferred_width;
  widget_class->get_preferred_height           = gtk_frame_get_preferred_height;
  widget_class->get_preferred_height_for_width = gtk_frame_get_preferred_height_for_width;
  widget_class->get_preferred_width_for_height = gtk_frame_get_preferred_width_for_height;
Matthias Clasen's avatar
Matthias Clasen committed
275
  widget_class->state_flags_changed            = gtk_frame_state_flags_changed;
276 277 278 279

  container_class->remove = gtk_frame_remove;
  container_class->forall = gtk_frame_forall;

280 281
  gtk_container_class_handle_border_width (container_class);

282
  class->compute_child_allocation = gtk_frame_real_compute_child_allocation;
283

284
  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_FRAME_ACCESSIBLE);
285
  gtk_widget_class_set_css_name (widget_class, "frame");
Elliot Lee's avatar
Elliot Lee committed
286 287
}

Johan Dahlin's avatar
Johan Dahlin committed
288 289 290
static void
gtk_frame_buildable_init (GtkBuildableIface *iface)
{
291
  iface->add_child = gtk_frame_buildable_add_child;
Johan Dahlin's avatar
Johan Dahlin committed
292 293 294
}

static void
295 296 297 298
gtk_frame_buildable_add_child (GtkBuildable *buildable,
			       GtkBuilder   *builder,
			       GObject      *child,
			       const gchar  *type)
Johan Dahlin's avatar
Johan Dahlin committed
299 300 301 302 303 304 305 306 307
{
  if (type && strcmp (type, "label") == 0)
    gtk_frame_set_label_widget (GTK_FRAME (buildable), GTK_WIDGET (child));
  else if (!type)
    gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
  else
    GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_FRAME (buildable), type);
}

Elliot Lee's avatar
Elliot Lee committed
308 309 310
static void
gtk_frame_init (GtkFrame *frame)
{
311
  GtkFramePrivate *priv;
Matthias Clasen's avatar
Matthias Clasen committed
312
  GtkCssNode *widget_node;
313

314
  frame->priv = gtk_frame_get_instance_private (frame); 
315 316 317 318 319 320
  priv = frame->priv;

  priv->label_widget = NULL;
  priv->shadow_type = GTK_SHADOW_ETCHED_IN;
  priv->label_xalign = 0.0;
  priv->label_yalign = 0.5;
Matthias Clasen's avatar
Matthias Clasen committed
321 322 323 324 325 326 327 328 329 330 331 332 333

  widget_node = gtk_widget_get_css_node (GTK_WIDGET (frame));
  priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
                                                     GTK_WIDGET (frame),
                                                     gtk_frame_measure,
                                                     gtk_frame_allocate,
                                                     gtk_frame_render,
                                                     NULL,
                                                     NULL);
  priv->border_gadget = gtk_css_custom_gadget_new ("border",
                                                       GTK_WIDGET (frame),
                                                       priv->gadget,
                                                       NULL,
334
                                                       gtk_frame_measure_border,
Matthias Clasen's avatar
Matthias Clasen committed
335 336 337 338
                                                       gtk_frame_allocate_border,
                                                       NULL,
                                                       NULL,
                                                       NULL);
339 340

  gtk_css_gadget_set_state (priv->border_gadget, gtk_widget_get_state_flags (GTK_WIDGET (frame)));
Matthias Clasen's avatar
Matthias Clasen committed
341 342 343 344 345 346 347 348 349 350 351 352
}

static void
gtk_frame_finalize (GObject *object)
{
  GtkFrame *frame = GTK_FRAME (object);
  GtkFramePrivate *priv = frame->priv;

  g_clear_object (&priv->border_gadget);
  g_clear_object (&priv->gadget);

  G_OBJECT_CLASS (gtk_frame_parent_class)->finalize (object);
Elliot Lee's avatar
Elliot Lee committed
353 354
}

355 356 357 358 359
static void 
gtk_frame_set_property (GObject         *object,
			guint            prop_id,
			const GValue    *value,
			GParamSpec      *pspec)
360
{
361
  GtkFrame *frame = GTK_FRAME (object);
362
  GtkFramePrivate *priv = frame->priv;
363

364
  switch (prop_id)
365
    {
366 367
    case PROP_LABEL:
      gtk_frame_set_label (frame, g_value_get_string (value));
368
      break;
369
    case PROP_LABEL_XALIGN:
370
      gtk_frame_set_label_align (frame, g_value_get_float (value), 
371
				 priv->label_yalign);
Matthias Clasen's avatar
Matthias Clasen committed
372
     break;
373
    case PROP_LABEL_YALIGN:
374
      gtk_frame_set_label_align (frame, priv->label_xalign,
375
				 g_value_get_float (value));
376
      break;
377
    case PROP_SHADOW_TYPE:
378
      gtk_frame_set_shadow_type (frame, g_value_get_enum (value));
379
      break;
380 381 382 383 384
    case PROP_LABEL_WIDGET:
      gtk_frame_set_label_widget (frame, g_value_get_object (value));
      break;
    default:      
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
385
      break;
386 387 388
    }
}

389 390 391 392 393
static void 
gtk_frame_get_property (GObject         *object,
			guint            prop_id,
			GValue          *value,
			GParamSpec      *pspec)
394
{
395
  GtkFrame *frame = GTK_FRAME (object);
396
  GtkFramePrivate *priv = frame->priv;
397

398
  switch (prop_id)
399
    {
400 401
    case PROP_LABEL:
      g_value_set_string (value, gtk_frame_get_label (frame));
402
      break;
403
    case PROP_LABEL_XALIGN:
404
      g_value_set_float (value, priv->label_xalign);
405
      break;
406
    case PROP_LABEL_YALIGN:
407
      g_value_set_float (value, priv->label_yalign);
408
      break;
409
    case PROP_SHADOW_TYPE:
410
      g_value_set_enum (value, priv->shadow_type);
411 412 413
      break;
    case PROP_LABEL_WIDGET:
      g_value_set_object (value,
414 415
                          priv->label_widget ?
                          G_OBJECT (priv->label_widget) : NULL);
416 417
      break;
    default:
418
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
419 420 421 422
      break;
    }
}

423 424
/**
 * gtk_frame_new:
425
 * @label: (allow-none): the text to use as the label of the frame
426 427 428 429
 * 
 * Creates a new #GtkFrame, with optional label @label.
 * If @label is %NULL, the label is omitted.
 * 
430
 * Returns: a new #GtkFrame widget
431
 **/
Elliot Lee's avatar
Elliot Lee committed
432 433 434
GtkWidget*
gtk_frame_new (const gchar *label)
{
Manish Singh's avatar
Manish Singh committed
435
  return g_object_new (GTK_TYPE_FRAME, "label", label, NULL);
Elliot Lee's avatar
Elliot Lee committed
436 437
}

438
static void
439 440
gtk_frame_remove (GtkContainer *container,
		  GtkWidget    *child)
441
{
442
  GtkFrame *frame = GTK_FRAME (container);
443
  GtkFramePrivate *priv = frame->priv;
444

445
  if (priv->label_widget == child)
446 447
    gtk_frame_set_label_widget (frame, NULL);
  else
Matthias Clasen's avatar
Matthias Clasen committed
448
    GTK_CONTAINER_CLASS (gtk_frame_parent_class)->remove (container, child);
449
}
450

451 452 453 454 455 456 457 458
static void
gtk_frame_forall (GtkContainer *container,
		  gboolean      include_internals,
		  GtkCallback   callback,
		  gpointer      callback_data)
{
  GtkBin *bin = GTK_BIN (container);
  GtkFrame *frame = GTK_FRAME (container);
459
  GtkFramePrivate *priv = frame->priv;
Javier Jardón's avatar
Javier Jardón committed
460
  GtkWidget *child;
461

Javier Jardón's avatar
Javier Jardón committed
462 463 464
  child = gtk_bin_get_child (bin);
  if (child)
    (* callback) (child, callback_data);
465

466 467
  if (priv->label_widget)
    (* callback) (priv->label_widget, callback_data);
468 469
}

470 471 472
/**
 * gtk_frame_set_label:
 * @frame: a #GtkFrame
473 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
 * new #GtkLabel with that text and adds it as the #GtkFrame:label-widget.
477
 **/
Elliot Lee's avatar
Elliot Lee committed
478 479 480 481 482 483
void
gtk_frame_set_label (GtkFrame *frame,
		     const gchar *label)
{
  g_return_if_fail (GTK_IS_FRAME (frame));

484
  if (!label)
Elliot Lee's avatar
Elliot Lee committed
485
    {
486
      gtk_frame_set_label_widget (frame, NULL);
Elliot Lee's avatar
Elliot Lee committed
487 488 489
    }
  else
    {
490 491 492 493
      GtkWidget *child = gtk_label_new (label);
      gtk_widget_show (child);

      gtk_frame_set_label_widget (frame, child);
Elliot Lee's avatar
Elliot Lee committed
494
    }
495
}
Elliot Lee's avatar
Elliot Lee committed
496

497 498 499 500
/**
 * gtk_frame_get_label:
 * @frame: a #GtkFrame
 * 
501
 * If the frame’s label widget is a #GtkLabel, returns the
502 503 504 505
 * text in the label widget. (The frame will have a #GtkLabel
 * for the label widget if a non-%NULL argument was passed
 * to gtk_frame_new().)
 * 
506
 * Returns: (nullable): the text in the label, or %NULL if there
507
 *               was no label widget or the lable widget was not
Soren Sandmann's avatar
Soren Sandmann committed
508 509
 *               a #GtkLabel. This string is owned by GTK+ and
 *               must not be modified or freed.
510
 **/
511
const gchar *
512 513
gtk_frame_get_label (GtkFrame *frame)
{
514
  GtkFramePrivate *priv;
515

516
  g_return_val_if_fail (GTK_IS_FRAME (frame), NULL);
Elliot Lee's avatar
Elliot Lee committed
517

518 519 520 521
  priv = frame->priv;

  if (GTK_IS_LABEL (priv->label_widget))
    return gtk_label_get_text (GTK_LABEL (priv->label_widget));
522 523 524
  else
    return NULL;
}
Elliot Lee's avatar
Elliot Lee committed
525

526 527 528
/**
 * gtk_frame_set_label_widget:
 * @frame: a #GtkFrame
529
 * @label_widget: (nullable): the new label widget
530
 * 
531 532
 * Sets the #GtkFrame:label-widget for the frame. This is the widget that
 * will appear embedded in the top edge of the frame as a title.
533 534 535 536 537
 **/
void
gtk_frame_set_label_widget (GtkFrame  *frame,
			    GtkWidget *label_widget)
{
538
  GtkFramePrivate *priv;
539
  gboolean need_resize = FALSE;
540

541 542
  g_return_if_fail (GTK_IS_FRAME (frame));
  g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
543
  g_return_if_fail (label_widget == NULL || gtk_widget_get_parent (label_widget) == NULL);
544 545 546 547

  priv = frame->priv;

  if (priv->label_widget == label_widget)
548
    return;
549 550

  if (priv->label_widget)
551
    {
552 553
      need_resize = gtk_widget_get_visible (priv->label_widget);
      gtk_widget_unparent (priv->label_widget);
554 555
    }

556 557
  priv->label_widget = label_widget;

558 559
  if (label_widget)
    {
560
      priv->label_widget = label_widget;
561
      gtk_widget_set_parent (label_widget, GTK_WIDGET (frame));
562
      need_resize |= gtk_widget_get_visible (label_widget);
563
    }
564

565
  if (gtk_widget_get_visible (GTK_WIDGET (frame)) && need_resize)
566
    gtk_widget_queue_resize (GTK_WIDGET (frame));
567

568
  g_object_freeze_notify (G_OBJECT (frame));
569 570
  g_object_notify_by_pspec (G_OBJECT (frame), frame_props[PROP_LABEL_WIDGET]);
  g_object_notify_by_pspec (G_OBJECT (frame),  frame_props[PROP_LABEL]);
571
  g_object_thaw_notify (G_OBJECT (frame));
Elliot Lee's avatar
Elliot Lee committed
572 573
}

574 575 576 577 578 579 580
/**
 * gtk_frame_get_label_widget:
 * @frame: a #GtkFrame
 *
 * Retrieves the label widget for the frame. See
 * gtk_frame_set_label_widget().
 *
581 582
 * Returns: (nullable) (transfer none): the label widget, or %NULL if
 * there is none.
583 584 585 586 587 588
 **/
GtkWidget *
gtk_frame_get_label_widget (GtkFrame *frame)
{
  g_return_val_if_fail (GTK_IS_FRAME (frame), NULL);

589
  return frame->priv->label_widget;
590 591
}

592 593 594 595 596 597 598
/**
 * gtk_frame_set_label_align:
 * @frame: a #GtkFrame
 * @xalign: The position of the label along the top edge
 *   of the widget. A value of 0.0 represents left alignment;
 *   1.0 represents right alignment.
 * @yalign: The y alignment of the label. A value of 0.0 aligns under 
599
 *   the frame; 1.0 aligns above the frame. If the values are exactly
600
 *   0.0 or 1.0 the gap in the frame won’t be painted because the label
601
 *   will be completely above or below the frame.
602
 * 
603
 * Sets the alignment of the frame widget’s label. The
604 605
 * default values for a newly created frame are 0.0 and 0.5.
 **/
Elliot Lee's avatar
Elliot Lee committed
606 607 608 609 610
void
gtk_frame_set_label_align (GtkFrame *frame,
			   gfloat    xalign,
			   gfloat    yalign)
{
611
  GtkFramePrivate *priv;
612

Elliot Lee's avatar
Elliot Lee committed
613 614
  g_return_if_fail (GTK_IS_FRAME (frame));

615 616
  priv = frame->priv;

Elliot Lee's avatar
Elliot Lee committed
617 618 619
  xalign = CLAMP (xalign, 0.0, 1.0);
  yalign = CLAMP (yalign, 0.0, 1.0);

620
  g_object_freeze_notify (G_OBJECT (frame));
621
  if (xalign != priv->label_xalign)
Elliot Lee's avatar
Elliot Lee committed
622
    {
623
      priv->label_xalign = xalign;
624
      g_object_notify_by_pspec (G_OBJECT (frame), frame_props[PROP_LABEL_XALIGN]);
625
    }
Elliot Lee's avatar
Elliot Lee committed
626

627
  if (yalign != priv->label_yalign)
628
    {
629
      priv->label_yalign = yalign;
630
      g_object_notify_by_pspec (G_OBJECT (frame), frame_props[PROP_LABEL_YALIGN]);
Elliot Lee's avatar
Elliot Lee committed
631
    }
632

633
  g_object_thaw_notify (G_OBJECT (frame));
634
  gtk_widget_queue_resize (GTK_WIDGET (frame));
Elliot Lee's avatar
Elliot Lee committed
635 636
}

637 638 639
/**
 * gtk_frame_get_label_align:
 * @frame: a #GtkFrame
640
 * @xalign: (out) (allow-none): location to store X alignment of
641
 *     frame’s label, or %NULL
642
 * @yalign: (out) (allow-none): location to store X alignment of
643
 *     frame’s label, or %NULL
644
 * 
645
 * Retrieves the X and Y alignment of the frame’s label. See
646 647 648 649 650 651 652
 * gtk_frame_set_label_align().
 **/
void
gtk_frame_get_label_align (GtkFrame *frame,
		           gfloat   *xalign,
			   gfloat   *yalign)
{
653
  GtkFramePrivate *priv;
654

655 656
  g_return_if_fail (GTK_IS_FRAME (frame));

657 658
  priv = frame->priv;

659
  if (xalign)
660
    *xalign = priv->label_xalign;
661
  if (yalign)
662
    *yalign = priv->label_yalign;
663 664
}

665 666 667 668 669
/**
 * gtk_frame_set_shadow_type:
 * @frame: a #GtkFrame
 * @type: the new #GtkShadowType
 * 
670
 * Sets the #GtkFrame:shadow-type for @frame, i.e. whether it is drawn without
671 672
 * (%GTK_SHADOW_NONE) or with (other values) a visible border. Values other than
 * %GTK_SHADOW_NONE are treated identically by GtkFrame. The chosen type is
673
 * applied by removing or adding the .flat class to the CSS node named border.
674
 **/
Elliot Lee's avatar
Elliot Lee committed
675 676 677 678
void
gtk_frame_set_shadow_type (GtkFrame      *frame,
			   GtkShadowType  type)
{
679
  GtkFramePrivate *priv;
680

Elliot Lee's avatar
Elliot Lee committed
681 682
  g_return_if_fail (GTK_IS_FRAME (frame));

683 684 685
  priv = frame->priv;

  if ((GtkShadowType) priv->shadow_type != type)
Elliot Lee's avatar
Elliot Lee committed
686
    {
687
      priv->shadow_type = type;
Elliot Lee's avatar
Elliot Lee committed
688

689
      if (type == GTK_SHADOW_NONE)
690
        gtk_css_gadget_add_class (priv->border_gadget, GTK_STYLE_CLASS_FLAT);
691
      else
692
        gtk_css_gadget_remove_class (priv->border_gadget, GTK_STYLE_CLASS_FLAT);
693

694
      g_object_notify_by_pspec (G_OBJECT (frame), frame_props[PROP_SHADOW_TYPE]);
Elliot Lee's avatar
Elliot Lee committed
695 696 697
    }
}

698 699 700 701 702 703 704
/**
 * gtk_frame_get_shadow_type:
 * @frame: a #GtkFrame
 *
 * Retrieves the shadow type of the frame. See
 * gtk_frame_set_shadow_type().
 *
705
 * Returns: the current shadow type of the frame.
706 707 708 709 710 711
 **/
GtkShadowType
gtk_frame_get_shadow_type (GtkFrame *frame)
{
  g_return_val_if_fail (GTK_IS_FRAME (frame), GTK_SHADOW_ETCHED_IN);

712
  return frame->priv->shadow_type;
713 714
}

Matthias Clasen's avatar
Matthias Clasen committed
715 716 717
static gboolean
gtk_frame_draw (GtkWidget *widget,
		cairo_t   *cr)
718
{
Matthias Clasen's avatar
Matthias Clasen committed
719
  gtk_css_gadget_draw (GTK_FRAME (widget)->priv->gadget, cr);
720

Matthias Clasen's avatar
Matthias Clasen committed
721
  return FALSE;
722 723
}

Benjamin Otte's avatar
Benjamin Otte committed
724
static gboolean
Matthias Clasen's avatar
Matthias Clasen committed
725 726 727 728 729 730 731
gtk_frame_render (GtkCssGadget *gadget,
                  cairo_t      *cr,
                  int           x,
                  int           y,
                  int           width,
                  int           height,
                  gpointer      data)
Elliot Lee's avatar
Elliot Lee committed
732
{
Matthias Clasen's avatar
Matthias Clasen committed
733
  GtkWidget *widget;
734
  GtkFramePrivate *priv;
Matthias Clasen's avatar
Matthias Clasen committed
735
  gint xc, yc, w, h;
Benjamin Otte's avatar
Benjamin Otte committed
736
  GtkAllocation allocation;
Elliot Lee's avatar
Elliot Lee committed
737

Matthias Clasen's avatar
Matthias Clasen committed
738 739 740 741
  widget = gtk_css_gadget_get_owner (gadget);
  priv = GTK_FRAME (widget)->priv;

  cairo_save (cr);
Elliot Lee's avatar
Elliot Lee committed
742

Benjamin Otte's avatar
Benjamin Otte committed
743
  gtk_widget_get_allocation (widget, &allocation);
744

Matthias Clasen's avatar
Matthias Clasen committed
745 746 747 748 749 750 751 752
  /* We want to use the standard gadget drawing for the border,
   * so we clip out the label allocation in order to get the
   * frame gap.
   */
  xc = priv->label_allocation.x - allocation.x;
  yc = y;
  w = priv->label_allocation.width;
  h = priv->label_allocation.height;
753

Matthias Clasen's avatar
Matthias Clasen committed
754
  if (GTK_IS_LABEL (priv->label_widget))
755
    {
Matthias Clasen's avatar
Matthias Clasen committed
756 757 758 759 760 761 762 763 764
      PangoRectangle ink_rect;

      /* Shrink the clip area for labels, so we get unclipped
       * frames for the yalign 0.0 and 1.0 cases.
       */
      pango_layout_get_pixel_extents (gtk_label_get_layout (GTK_LABEL (priv->label_widget)), &ink_rect, NULL);
      yc += (h - ink_rect.height) / 2;
      h = ink_rect.height;
    }
765

Matthias Clasen's avatar
Matthias Clasen committed
766 767 768 769
  cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
  cairo_rectangle (cr, xc + w, yc, - w, h);
  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
  cairo_clip (cr);
770

Matthias Clasen's avatar
Matthias Clasen committed
771
  gtk_css_gadget_draw (priv->border_gadget, cr);
772

Matthias Clasen's avatar
Matthias Clasen committed
773
  cairo_restore (cr);
774

Benjamin Otte's avatar
Benjamin Otte committed
775
  GTK_WIDGET_CLASS (gtk_frame_parent_class)->draw (widget, cr);
Elliot Lee's avatar
Elliot Lee committed
776 777 778 779

  return FALSE;
}

780 781 782 783
static void
gtk_frame_size_allocate (GtkWidget     *widget,
			 GtkAllocation *allocation)
{
Matthias Clasen's avatar
Matthias Clasen committed
784
  GtkAllocation clip;
Elliot Lee's avatar
Elliot Lee committed
785

786
  gtk_widget_set_allocation (widget, allocation);
Elliot Lee's avatar
Elliot Lee committed
787

Matthias Clasen's avatar
Matthias Clasen committed
788 789 790 791
  gtk_css_gadget_allocate (GTK_FRAME (widget)->priv->gadget,
                           allocation,
                           gtk_widget_get_allocated_baseline (widget),
                           &clip);
792

Matthias Clasen's avatar
Matthias Clasen committed
793 794
  gtk_widget_set_clip (widget, &clip);
}
Javier Jardón's avatar
Javier Jardón committed
795

Matthias Clasen's avatar
Matthias Clasen committed
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
static void
gtk_frame_allocate (GtkCssGadget        *gadget,
                    const GtkAllocation *allocation,
                    int                  baseline,
                    GtkAllocation       *out_clip,
                    gpointer             data)
{
  GtkWidget *widget;
  GtkFrame *frame;
  GtkFramePrivate *priv;
  GtkAllocation new_allocation;
  GtkAllocation frame_allocation;
  gint height_extra;
  GtkAllocation clip;

  widget = gtk_css_gadget_get_owner (gadget);
  frame = GTK_FRAME (widget);
  priv = frame->priv;
814

Matthias Clasen's avatar
Matthias Clasen committed
815
  gtk_frame_compute_child_allocation (frame, &new_allocation);
816 817
  priv->child_allocation = new_allocation;

818 819
  if (priv->label_widget &&
      gtk_widget_get_visible (priv->label_widget))
Elliot Lee's avatar
Elliot Lee committed
820
    {
821
      gint nat_width, width, height;
822 823 824
      gfloat xalign;

      if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
825
	xalign = priv->label_xalign;
826
      else
827 828
	xalign = 1 - priv->label_xalign;

829
      gtk_widget_get_preferred_width (priv->label_widget, NULL, &nat_width);
Matthias Clasen's avatar
Matthias Clasen committed
830 831
      width = MIN (new_allocation.width, nat_width);
      gtk_widget_get_preferred_height_for_width (priv->label_widget, width, &height, NULL);
832

Matthias Clasen's avatar
Matthias Clasen committed
833 834 835 836
      priv->label_allocation.x = new_allocation.x + (new_allocation.width - width) * xalign;
      priv->label_allocation.y = new_allocation.y - height;
      priv->label_allocation.height = height;
      priv->label_allocation.width = width;
837

Matthias Clasen's avatar
Matthias Clasen committed
838
      gtk_widget_size_allocate (priv->label_widget, &priv->label_allocation);
839

Matthias Clasen's avatar
Matthias Clasen committed
840 841 842 843
      height_extra = (1 - priv->label_yalign) * height;
    }
  else
    height_extra = 0;
844

Matthias Clasen's avatar
Matthias Clasen committed
845 846 847 848
  frame_allocation.x = priv->child_allocation.x;
  frame_allocation.y = priv->child_allocation.y - height_extra;
  frame_allocation.width = priv->child_allocation.width;
  frame_allocation.height = priv->child_allocation.height + height_extra;
849

Matthias Clasen's avatar
Matthias Clasen committed
850 851 852 853
  gtk_css_gadget_allocate (priv->border_gadget,
                           &frame_allocation,
                           -1,
                           &clip);
854

Matthias Clasen's avatar
Matthias Clasen committed
855 856 857 858 859 860 861 862 863 864 865 866
  gtk_container_get_children_clip (GTK_CONTAINER (widget), out_clip);
  gdk_rectangle_union (out_clip, &clip, out_clip);
}

static void
gtk_frame_allocate_border (GtkCssGadget        *gadget,
                           const GtkAllocation *allocation,
                           int                  baseline,
                           GtkAllocation       *out_clip,
                           gpointer             data)
{
  GtkWidget *widget;
867
  GtkFramePrivate *priv;
Matthias Clasen's avatar
Matthias Clasen committed
868
  GtkWidget *child;
869 870
  GtkAllocation child_allocation;
  gint height_extra;
Matthias Clasen's avatar
Matthias Clasen committed
871 872

  widget = gtk_css_gadget_get_owner (gadget);
873 874 875 876
  priv = GTK_FRAME (widget)->priv;

  if (priv->label_widget &&
      gtk_widget_get_visible (priv->label_widget))
877
    height_extra = priv->label_allocation.height * (1 - priv->label_yalign);
878 879 880 881 882 883
  else
    height_extra = 0;

  child_allocation = *allocation;
  child_allocation.y += height_extra;
  child_allocation.height -= height_extra;
Matthias Clasen's avatar
Matthias Clasen committed
884 885 886

  child = gtk_bin_get_child (GTK_BIN (widget));
  if (child && gtk_widget_get_visible (child))
887
    gtk_widget_size_allocate (child, &child_allocation);
888 889

  *out_clip = *allocation;
Elliot Lee's avatar
Elliot Lee committed
890 891
}

892 893 894 895 896 897
static void
gtk_frame_compute_child_allocation (GtkFrame      *frame,
				    GtkAllocation *child_allocation)
{
  g_return_if_fail (GTK_IS_FRAME (frame));
  g_return_if_fail (child_allocation != NULL);
Elliot Lee's avatar
Elliot Lee committed
898

899 900
  GTK_FRAME_GET_CLASS (frame)->compute_child_allocation (frame, child_allocation);
}
Elliot Lee's avatar
Elliot Lee committed
901

902 903 904 905
static void
gtk_frame_real_compute_child_allocation (GtkFrame      *frame,
					 GtkAllocation *child_allocation)
{
906
  GtkFramePrivate *priv = frame->priv;
907
  GtkAllocation allocation;
908
  gint height;
Elliot Lee's avatar
Elliot Lee committed
909

Matthias Clasen's avatar
Matthias Clasen committed
910
  gtk_css_gadget_get_content_allocation (priv->gadget, &allocation, NULL);
911

912
  if (priv->label_widget)
913
    {
Matthias Clasen's avatar
Matthias Clasen committed
914
      gint nat_width, width;
915

916
      gtk_widget_get_preferred_width (priv->label_widget, NULL, &nat_width);
917
      width = MIN (allocation.width, nat_width);
Matthias Clasen's avatar
Matthias Clasen committed
918
      gtk_widget_get_preferred_height_for_width (priv->label_widget, width, &height, NULL);
Elliot Lee's avatar
Elliot Lee committed
919
    }
920
  else
Matthias Clasen's avatar
Matthias Clasen committed
921 922
    height = 0;

923 924 925 926
  child_allocation->x = allocation.x;
  child_allocation->y = allocation.y + height;
  child_allocation->width = MAX (1, allocation.width);
  child_allocation->height = MAX (1, allocation.height - height);
Elliot Lee's avatar
Elliot Lee committed
927
}
928

929
static void
Matthias Clasen's avatar
Matthias Clasen committed
930 931 932 933 934 935 936 937
gtk_frame_measure (GtkCssGadget   *gadget,
                   GtkOrientation  orientation,
                   int             for_size,
                   int            *minimum,
                   int            *natural,
                   int            *minimum_baseline,
                   int            *natural_baseline,
                   gpointer        data)
938
{
Matthias Clasen's avatar
Matthias Clasen committed
939 940 941
  GtkWidget *widget;
  GtkFrame *frame;
  GtkFramePrivate *priv;
942 943
  gint child_min, child_nat;

Matthias Clasen's avatar
Matthias Clasen committed
944 945 946 947
  widget = gtk_css_gadget_get_owner (gadget);
  frame = GTK_FRAME (widget);
  priv = frame->priv;

948 949 950 951 952 953 954 955 956 957
  gtk_css_gadget_get_preferred_size (priv->border_gadget,
                                     orientation,
                                     for_size,
                                     &child_min,
                                     &child_nat,
                                     NULL, NULL);

  *minimum = child_min;
  *natural = child_nat;

958
  if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
959 960 961
    {
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
        {
Matthias Clasen's avatar
Matthias Clasen committed
962
          gtk_widget_get_preferred_width (priv->label_widget, &child_min, &child_nat);
963 964
          *minimum = MAX (child_min, *minimum);
          *natural = MAX (child_nat, *natural);
965 966 967
        }
      else
        {
Matthias Clasen's avatar
Matthias Clasen committed
968 969
          if (for_size > 0)
            gtk_widget_get_preferred_height_for_width (priv->label_widget,
970
                                                       for_size, &child_min, &child_nat);
Matthias Clasen's avatar
Matthias Clasen committed
971 972 973
          else
            gtk_widget_get_preferred_height (priv->label_widget, &child_min, &child_nat);

974 975
          *minimum += child_min;
          *natural += child_nat;
976 977
        }
    }
978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993
}

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)
{
  GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
  GtkWidget *child;
  int child_min, child_nat;

Matthias Clasen's avatar
Matthias Clasen committed
994
  child = gtk_bin_get_child (GTK_BIN (widget));
Javier Jardón's avatar
Javier Jardón committed
995
  if (child && gtk_widget_get_visible (child))
996 997 998
    {
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
        {
Matthias Clasen's avatar
Matthias Clasen committed
999
          gtk_widget_get_preferred_width (child, &child_min, &child_nat);
1000 1001 1002
        }
      else
        {
Matthias Clasen's avatar
Matthias Clasen committed
1003
          if (for_size > 0)
1004
            gtk_widget_get_preferred_height_for_width (child, for_size, &child_min, &child_nat);
Matthias Clasen's avatar
Matthias Clasen committed
1005 1006 1007
          else
            gtk_widget_get_preferred_height (child, &child_min, &child_nat);
        }
1008

1009 1010 1011 1012 1013 1014 1015 1016
      *minimum = child_min;
      *natural = child_nat;
    }
  else
    {
      *minimum = 0;
      *natural = 0;
    }
1017 1018
}

1019

1020
static void
1021
gtk_frame_get_preferred_width (GtkWidget *widget,
Matthias Clasen's avatar
Matthias Clasen committed
1022 1023
                               gint      *minimum,
                               gint      *natural)
1024
{
Matthias Clasen's avatar
Matthias Clasen committed
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
  gtk_css_gadget_get_preferred_size (GTK_FRAME (widget)->priv->gadget,
                                     GTK_ORIENTATION_HORIZONTAL,
                                     -1,
                                     minimum, natural,
                                     NULL, NULL);
}

static void
gtk_frame_get_preferred_width_for_height (GtkWidget *widget,
                                          gint       height,
                                          gint      *minimum,
                                          gint      *natural)
{
  gtk_css_gadget_get_preferred_size (GTK_FRAME (widget)->priv->gadget,
                                     GTK_ORIENTATION_HORIZONTAL,
                                     height,
                                     minimum, natural,
                                     NULL, NULL);
1043 1044 1045
}

static void
1046
gtk_frame_get_preferred_height (GtkWidget *widget,
Matthias Clasen's avatar
Matthias Clasen committed
1047 1048
                                gint      *minimum,
                                gint      *natural)
1049
{
Matthias Clasen's avatar
Matthias Clasen committed
1050 1051 1052 1053 1054
  gtk_css_gadget_get_preferred_size (GTK_FRAME (widget)->priv->gadget,
                                     GTK_ORIENTATION_VERTICAL,
                                     -1,
                                     minimum, natural,
                                     NULL, NULL);
1055 1056
}

1057 1058

static void
1059
gtk_frame_get_preferred_height_for_width (GtkWidget *widget,
1060
                                          gint       width,
Matthias Clasen's avatar
Matthias Clasen committed
1061 1062
                                          gint      *minimum,
                                          gint      *natural)
1063
{
Matthias Clasen's avatar
Matthias Clasen committed
1064 1065 1066 1067 1068
  gtk_css_gadget_get_preferred_size (GTK_FRAME (widget)->priv->gadget,
                                     GTK_ORIENTATION_VERTICAL,
                                     width,
                                     minimum, natural,
                                     NULL, NULL);
1069 1070 1071
}

static void
Matthias Clasen's avatar
Matthias Clasen committed
1072 1073
gtk_frame_state_flags_changed (GtkWidget     *widget,
                               GtkStateFlags  previous_state)
1074
{
1075
  gtk_css_gadget_set_state (GTK_FRAME (widget)->priv->border_gadget, gtk_widget_get_state_flags (widget));
Matthias Clasen's avatar
Matthias Clasen committed
1076 1077 1078

  GTK_WIDGET_CLASS (gtk_frame_parent_class)->state_flags_changed (widget, previous_state);
}