hdy-title-bar.c 10.8 KB
Newer Older
Adrien Plazas's avatar
Adrien Plazas committed
1
2
3
/*
 * Copyright (C) 2018 Purism SPC
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
Adrien Plazas's avatar
Adrien Plazas committed
5
6
 */

Bastien Nocera's avatar
Bastien Nocera committed
7
#include "config.h"
Adrien Plazas's avatar
Adrien Plazas committed
8
9
#include "hdy-title-bar.h"

Bastien Nocera's avatar
Bastien Nocera committed
10
#include <glib/gi18n-lib.h>
Adrien Plazas's avatar
Adrien Plazas committed
11

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
12
13
G_GNUC_BEGIN_IGNORE_DEPRECATIONS

Adrien Plazas's avatar
Adrien Plazas committed
14
/**
15
 * HdyTitleBar:
Adrien Plazas's avatar
Adrien Plazas committed
16
 *
17
 * A simple title bar container.
Adrien Plazas's avatar
Adrien Plazas committed
18
 *
19
20
21
22
 * `HdyTitleBar` is meant to be used as the top-level widget of your window's
 * title bar. It will be drawn with the same style as a [class@Gtk.HeaderBar]
 * but it won't force a widget layout on you: you can put whatever widget you
 * want in it, including a [class@Gtk.HeaderBar].
23
 *
24
25
 * `HdyTitleBar` becomes really useful when you want to animate header bars,
 * like an adaptive application using [class@Leaflet] would do.
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
26
 *
27
28
 * `HdyTitleBar` has been deprecated, header bars can be animated without it
 * when placed inside [class@Window] or [class@ApplicationWindow].
29
 *
30
31
32
33
34
 * ## CSS nodes
 *
 * `HdyTitleBar` has a single CSS node with name `headerbar`.
 *
 * Since: 1.0
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
35
36
 *
 * Deprecated: 1.4
Adrien Plazas's avatar
Adrien Plazas committed
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 */

enum {
  PROP_0,
  PROP_SELECTION_MODE,
  LAST_PROP,
};

struct _HdyTitleBar
{
  GtkBin parent_instance;

  gboolean selection_mode;
};

G_DEFINE_TYPE (HdyTitleBar, hdy_title_bar, GTK_TYPE_BIN)

static GParamSpec *props[LAST_PROP];

/**
57
58
59
 * hdy_title_bar_set_selection_mode:  (attributes org.gtk.Method.set_property=selection-mode)
 * @self: a title bar
 * @selection_mode: `TRUE` to enable the selection mode
Adrien Plazas's avatar
Adrien Plazas committed
60
61
 *
 * Sets whether @self is in selection mode.
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
62
 *
63
64
 * Since: 1.0
 *
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
65
 * Deprecated: 1.4
Adrien Plazas's avatar
Adrien Plazas committed
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
 */
void
hdy_title_bar_set_selection_mode (HdyTitleBar *self,
                                  gboolean     selection_mode)
{
  GtkStyleContext *context;

  g_return_if_fail (HDY_IS_TITLE_BAR (self));

  selection_mode = !!selection_mode;

  context = gtk_widget_get_style_context (GTK_WIDGET (self));

  if (self->selection_mode == selection_mode)
    return;

  self->selection_mode = selection_mode;

  if (selection_mode)
    gtk_style_context_add_class (context, "selection-mode");
  else
    gtk_style_context_remove_class (context, "selection-mode");

  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SELECTION_MODE]);
}

/**
93
94
 * hdy_title_bar_get_selection_mode:  (attributes org.gtk.Method.get_property=selection-mode)
 * @self: a title bar
Adrien Plazas's avatar
Adrien Plazas committed
95
 *
Adrien Plazas's avatar
Adrien Plazas committed
96
 * Returns whether whether @self is in selection mode.
Adrien Plazas's avatar
Adrien Plazas committed
97
 *
98
99
100
 * Returns: `TRUE` if the title bar is in selection mode
 *
 * Since: 1.0
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
101
102
 *
 * Deprecated: 1.4
Adrien Plazas's avatar
Adrien Plazas committed
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
 */
gboolean
hdy_title_bar_get_selection_mode (HdyTitleBar *self)
{
  g_return_val_if_fail (HDY_IS_TITLE_BAR (self), FALSE);

  return self->selection_mode;
}

static void
style_updated_cb (HdyTitleBar *self)
{
  GtkStyleContext *context;
  gboolean selection_mode;

  g_assert (HDY_IS_TITLE_BAR (self));

  context = gtk_widget_get_style_context (GTK_WIDGET (self));
  selection_mode = gtk_style_context_has_class (context, "selection-mode");

  if (self->selection_mode == selection_mode)
    return;

  self->selection_mode = selection_mode;

  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SELECTION_MODE]);
}

static void
hdy_title_bar_get_property (GObject    *object,
                            guint       prop_id,
                            GValue     *value,
                            GParamSpec *pspec)
{
  HdyTitleBar *self = HDY_TITLE_BAR (object);

  switch (prop_id) {
  case PROP_SELECTION_MODE:
    g_value_set_boolean (value, hdy_title_bar_get_selection_mode (self));
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  }
}

static void
hdy_title_bar_set_property (GObject      *object,
                            guint         prop_id,
                            const GValue *value,
                            GParamSpec   *pspec)
{
  HdyTitleBar *self = HDY_TITLE_BAR (object);

  switch (prop_id) {
  case PROP_SELECTION_MODE:
    hdy_title_bar_set_selection_mode (self, g_value_get_boolean (value));
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  }
}

static gboolean
hdy_title_bar_draw (GtkWidget *widget,
                    cairo_t   *cr)
{
  GtkStyleContext *context;

  context = gtk_widget_get_style_context (widget);
  /* GtkWidget draws nothing by default so we have to render the background
Adrien Plazas's avatar
Adrien Plazas committed
173
   * explicitly for HdyTitleBar to render the typical titlebar background.
Adrien Plazas's avatar
Adrien Plazas committed
174
175
176
177
178
179
180
181
182
183
   */
  gtk_render_background (context,
                         cr,
                         0, 0,
                         gtk_widget_get_allocated_width (widget),
                         gtk_widget_get_allocated_height (widget));

  return GTK_WIDGET_CLASS (hdy_title_bar_parent_class)->draw (widget, cr);
}

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/* This private method is prefixed by the class name because it will be a
 * virtual method in GTK 4.
 */
static void
hdy_title_bar_measure (GtkWidget      *widget,
                       GtkOrientation  orientation,
                       gint            for_size,
                       gint           *minimum,
                       gint           *natural,
                       gint           *minimum_baseline,
                       gint           *natural_baseline)
{
  GtkWidget *child;
  gint parent_min, parent_nat;
  gint css_width, css_height, css_min;

  child = gtk_bin_get_child (GTK_BIN (widget));

  gtk_style_context_get (gtk_widget_get_style_context (widget),
                         gtk_widget_get_state_flags (widget),
                         "min-width", &css_width,
                         "min-height", &css_height,
                         NULL);

  if (orientation == GTK_ORIENTATION_HORIZONTAL)
    css_min = css_width;
  else
    css_min = css_height;

  if (child)
    if (orientation == GTK_ORIENTATION_HORIZONTAL)
      if (for_size != 1)
        gtk_widget_get_preferred_width_for_height (child,
                                                   MAX (for_size, css_height),
                                                   &parent_min, &parent_nat);
      else
        gtk_widget_get_preferred_width (child, &parent_min, &parent_nat);
    else
      if (for_size != 1)
        gtk_widget_get_preferred_height_for_width (child,
                                                   MAX (for_size, css_width),
                                                   &parent_min, &parent_nat);
      else
        gtk_widget_get_preferred_height (child, &parent_min, &parent_nat);
  else {
    parent_min = 0;
    parent_nat = 0;
  }

  if (minimum)
234
    *minimum = MAX (parent_min, css_min);
235

236
  if (natural)
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
    *natural = MAX (parent_nat, css_min);

  if (minimum_baseline)
    *minimum_baseline = -1;

  if (natural_baseline)
    *natural_baseline = -1;
}

static void
hdy_title_bar_get_preferred_width (GtkWidget *widget,
                                   gint      *minimum,
                                   gint      *natural)
{
  hdy_title_bar_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1,
                         minimum, natural, NULL, NULL);
}

static void
hdy_title_bar_get_preferred_width_for_height (GtkWidget *widget,
                                              gint       height,
                                              gint      *minimum,
                                              gint      *natural)
{
  hdy_title_bar_measure (widget, GTK_ORIENTATION_HORIZONTAL, height,
                         minimum, natural, NULL, NULL);
}

static void
hdy_title_bar_get_preferred_height (GtkWidget *widget,
                                    gint      *minimum,
                                    gint      *natural)
{
  hdy_title_bar_measure (widget, GTK_ORIENTATION_VERTICAL, -1,
                         minimum, natural, NULL, NULL);
}

static void
hdy_title_bar_get_preferred_height_for_width (GtkWidget *widget,
                                              gint       width,
                                              gint      *minimum,
                                              gint      *natural)
{
  hdy_title_bar_measure (widget, GTK_ORIENTATION_VERTICAL, width,
                         minimum, natural, NULL, NULL);
}

284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
static void
hdy_title_bar_size_allocate (GtkWidget     *widget,
                             GtkAllocation *allocation)
{
  GtkAllocation clip;

  gtk_render_background_get_clip (gtk_widget_get_style_context (widget),
                                  allocation->x,
                                  allocation->y,
                                  allocation->width,
                                  allocation->height,
                                  &clip);

  GTK_WIDGET_CLASS (hdy_title_bar_parent_class)->size_allocate (widget, allocation);
  gtk_widget_set_clip (widget, &clip);
}

Adrien Plazas's avatar
Adrien Plazas committed
301
302
303
304
305
306
307
308
309
310
311
static void
hdy_title_bar_class_init (HdyTitleBarClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);

  object_class->get_property = hdy_title_bar_get_property;
  object_class->set_property = hdy_title_bar_set_property;

  widget_class->draw = hdy_title_bar_draw;
312
313
314
315
  widget_class->get_preferred_width = hdy_title_bar_get_preferred_width;
  widget_class->get_preferred_width_for_height = hdy_title_bar_get_preferred_width_for_height;
  widget_class->get_preferred_height = hdy_title_bar_get_preferred_height;
  widget_class->get_preferred_height_for_width = hdy_title_bar_get_preferred_height_for_width;
316
  widget_class->size_allocate = hdy_title_bar_size_allocate;
Adrien Plazas's avatar
Adrien Plazas committed
317
318

  /**
319
320
321
   * HdyTitleBar:selection-mode: (attributes org.gtk.Property.get=hdy_title_bar_set_selection_mode org.gtk.Property.set=hdy_title_bar_set_selection_mode)
   *
   * Whether or not the title bar is in selection mode.
Adrien Plazas's avatar
Adrien Plazas committed
322
   *
323
   * Since: 1.0
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
324
325
   *
   * Deprecated: 1.4
Adrien Plazas's avatar
Adrien Plazas committed
326
327
328
329
330
331
   */
  props[PROP_SELECTION_MODE] =
      g_param_spec_boolean ("selection-mode",
                            _("Selection mode"),
                            _("Whether or not the title bar is in selection mode"),
                            FALSE,
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
332
                            G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_DEPRECATED);
Adrien Plazas's avatar
Adrien Plazas committed
333
334
335

  g_object_class_install_properties (object_class, LAST_PROP, props);

Adrien Plazas's avatar
Adrien Plazas committed
336
  gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_TITLE_BAR);
Adrien Plazas's avatar
Adrien Plazas committed
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
  /* Adwaita states it expects a headerbar to be the top-level titlebar widget,
   * so style-wise HdyTitleBar pretends to be one as its role is to be the
   * top-level titlebar widget.
   */
  gtk_widget_class_set_css_name (widget_class, "headerbar");
  gtk_container_class_handle_border_width (container_class);
}

static void
hdy_title_bar_init (HdyTitleBar *self)
{
  GtkStyleContext *context;

  context = gtk_widget_get_style_context (GTK_WIDGET (self));
  /* Ensure the widget has the titlebar style class. */
  gtk_style_context_add_class (context, "titlebar");

  g_signal_connect (self, "style-updated", G_CALLBACK (style_updated_cb), NULL);
}

/**
 * hdy_title_bar_new:
 *
360
361
362
 * Creates a new `HdyTitleBar`.
 *
 * Returns: a new `HdyTitleBar`
Adrien Plazas's avatar
Adrien Plazas committed
363
 *
364
 * Since: 1.0
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
365
366
 *
 * Deprecated: 1.4
Adrien Plazas's avatar
Adrien Plazas committed
367
 */
Ujjwal Kumar's avatar
Ujjwal Kumar committed
368
GtkWidget *
Adrien Plazas's avatar
Adrien Plazas committed
369
370
371
372
hdy_title_bar_new (void)
{
  return g_object_new (HDY_TYPE_TITLE_BAR, NULL);
}
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
373
374

G_GNUC_END_IGNORE_DEPRECATIONS