summaryrefslogtreecommitdiff
path: root/gtk/gtkcsscustomgadget.c
blob: 60a1881ba9b4c32e735b6e0041a5030497fc25bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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
93
94
95
96
97
98
99
100
101
102
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
173
174
175
176
177
178
179
180
181
182
183
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
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/*
 * Copyright © 2015 Red Hat Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: Benjamin Otte <otte@gnome.org>
 */

#include "config.h"

#include "gtkcsscustomgadgetprivate.h"

#include "gtkcssnodeprivate.h"

/*
 * GtkCssCustomGadget is a subclass that lets widgets customize size
 * requests, size allocation and drawing of gadgets. The gadget is passed
 * to the callbacks as the first argument, and you can use gtk_css_gadget_get_owner()
 * to obtain the widget. Note that the widgets style context is not saved,
 * so if you want to query style properties or call gtk_render functions which
 * take the style context as an argument, you should use
 * gtk_style_context_save_to_node to make the gadget's CSS node take effect.
 *
 * The callbacks are
 *
 * GtkCssPreferredSizeFunc:
 * @gadget: the #GtkCssCustomGadget
 * @orientation: whether a width (ie horizontal) or height (ie vertical) is requested
 * @for_size: the available size in the opposite direction, or -1
 * @minimum: return location for the minimum size
 * @natural: return location for the natural size
 * @minimum_baseline: (nullable): return location for the baseline at minimum size
 * @natural_baseline: (nullable): return location for the baseline at natural size
 * @data: data provided when registering the callback
 *
 * The GtkCssPreferredSizeFunc is called to determine the content size in
 * gtk_css_gadget_get_preferred_size(). @for_size is a content size (ie excluding
 * CSS padding, border and margin) and the returned @minimum, @natural,
 * @minimum_baseline, @natural_baseline should be content sizes excluding CSS
 * padding, border and margin as well.
 *
 * Typically, GtkCssPreferredSizeFunc will query the size of sub-gadgets and
 * child widgets that are placed relative to the gadget and determine its own
 * needed size from the results. If the gadget has no sub-gadgets or child
 * widgets that it needs to place, then a GtkCssPreferredSizeFunc is only
 * needed if you want to enforce a minimum size independent of CSS min-width
 * and min-height (e.g. if size-related style properties need to be supported
 * for compatibility).
 *
 * GtkCssAllocateFunc:
 * @gadget: the #GtkCssCustomGadget
 * @allocation: the allocation
 * @baseline: the baseline
 * @out_clip: (out): return location for the content clip
 * @data: data provided when registering the callback
 *
 * The GtkCssAllocateFunc is called to allocate the gadgets content in
 * gtk_css_gadget_allocate(). @allocation and @baseline are content sizes
 * (ie excluding CSS padding, border and margin).
 *
 * Typically, GtkCssAllocateFunc will allocate sub-gadgets and child widgets
 * that are placed relative to the gadget, and merge their clips into the
 * value returned as @out_clip. For clip handling in the main gadget of
 * containers, gtk_container_get_children_clip() can be useful. Gadgets that
 * don't have sub-gadgets of child widgets don't need a GtkCssAllocateFunc
 * (although it is still required to call gtk_css_gadget_allocate() on them).
 *
 * Note that @out_clip *must* be set to meaningful values. If in doubt,
 * just set it to the allocation.
 *
 * GtkCssDrawFunc:
 * @gadget: the #GtkCssCustomGadget
 * @cr: the cairo context to draw on
 * @x: the x origin of the content area
 * @y: the y origin of the content area
 * @width: the width of the content area
 * @height: the height of the content area
 * @data: data provided when registering the callback
 *
 * The GtkCssDrawFunc is called to draw the gadgets content in
 * gtk_css_gadget_draw(). It gets passed an untransformed cairo context
 * and the coordinates of the area to draw the content in.
 *
 * Typically, GtkCssDrawFunc will draw sub-gadgets and child widgets
 * that are placed relative to the gadget, as well as custom content
 * such as icons, checkmarks, arrows or text.
 */

typedef struct _GtkCssCustomGadgetPrivate GtkCssCustomGadgetPrivate;
struct _GtkCssCustomGadgetPrivate {
  GtkCssPreferredSizeFunc          preferred_size_func;
  GtkCssAllocateFunc               allocate_func;
  GtkCssDrawFunc                   draw_func;
  gpointer                         data;
  GDestroyNotify                   destroy_func;
};

G_DEFINE_TYPE_WITH_CODE (GtkCssCustomGadget, gtk_css_custom_gadget, GTK_TYPE_CSS_GADGET,
                         G_ADD_PRIVATE (GtkCssCustomGadget))

static void
gtk_css_custom_gadget_get_preferred_size (GtkCssGadget   *gadget,
                                          GtkOrientation  orientation,
                                          gint            for_size,
                                          gint           *minimum,
                                          gint           *natural,
                                          gint           *minimum_baseline,
                                          gint           *natural_baseline)
{
  GtkCssCustomGadgetPrivate *priv = gtk_css_custom_gadget_get_instance_private (GTK_CSS_CUSTOM_GADGET (gadget));

  if (priv->preferred_size_func)
    return priv->preferred_size_func (gadget, orientation, for_size, 
                                      minimum, natural,
                                      minimum_baseline, natural_baseline,
                                      priv->data);
  else
    return GTK_CSS_GADGET_CLASS (gtk_css_custom_gadget_parent_class)->get_preferred_size (gadget, orientation, for_size, 
                                                                                          minimum, natural,
                                                                                          minimum_baseline, natural_baseline);
}

static void
gtk_css_custom_gadget_allocate (GtkCssGadget        *gadget,
                                const GtkAllocation *allocation,
                                int                  baseline,
                                GtkAllocation       *out_clip)
{
  GtkCssCustomGadgetPrivate *priv = gtk_css_custom_gadget_get_instance_private (GTK_CSS_CUSTOM_GADGET (gadget));

  if (priv->allocate_func)
    return priv->allocate_func (gadget, allocation, baseline, out_clip, priv->data);
  else
    return GTK_CSS_GADGET_CLASS (gtk_css_custom_gadget_parent_class)->allocate (gadget, allocation, baseline, out_clip);
}

static gboolean
gtk_css_custom_gadget_draw (GtkCssGadget *gadget,
                            cairo_t      *cr,
                            int           x,
                            int           y,
                            int           width,
                            int           height)
{
  GtkCssCustomGadgetPrivate *priv = gtk_css_custom_gadget_get_instance_private (GTK_CSS_CUSTOM_GADGET (gadget));

  if (priv->draw_func)
    return priv->draw_func (gadget, cr, x, y, width, height, priv->data);
  else
    return GTK_CSS_GADGET_CLASS (gtk_css_custom_gadget_parent_class)->draw (gadget, cr, x, y, width, height);
}

static void
gtk_css_custom_gadget_finalize (GObject *object)
{
  GtkCssCustomGadgetPrivate *priv = gtk_css_custom_gadget_get_instance_private (GTK_CSS_CUSTOM_GADGET (object));

  if (priv->destroy_func)
    priv->destroy_func (priv->data);

  G_OBJECT_CLASS (gtk_css_custom_gadget_parent_class)->finalize (object);
}

static void
gtk_css_custom_gadget_class_init (GtkCssCustomGadgetClass *klass)
{
  GtkCssGadgetClass *gadget_class = GTK_CSS_GADGET_CLASS (klass);
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize = gtk_css_custom_gadget_finalize;

  gadget_class->get_preferred_size = gtk_css_custom_gadget_get_preferred_size;
  gadget_class->allocate = gtk_css_custom_gadget_allocate;
  gadget_class->draw = gtk_css_custom_gadget_draw;
}

static void
gtk_css_custom_gadget_init (GtkCssCustomGadget *custom_gadget)
{

}

/**
 * gtk_css_custom_gadget_new_for_node:
 * @node: the #GtkCssNode to use for the gadget
 * @owner: the widget that the gadget belongs to
 * @preferred_size_func: (nullable): the GtkCssPreferredSizeFunc to use
 * @allocate_func: (nullable): the GtkCssAllocateFunc to use
 * @draw_func: (nullable): the GtkCssDrawFunc to use
 * @data: (nullable): user data to pass to the callbacks
 * @destroy_func: (nullable): destroy notify for @data
 *
 * Creates a #GtkCssCustomGadget for an existing CSS node.
 * This function is typically used in the widgets init function
 * to create the main gadget for the widgets main CSS node (which
 * is obtained with gtk_widget_get_css_node()), as well as other
 * permanent sub-gadgets. Sub-gadgets that only exist sometimes
 * (e.g. depending on widget properties) should be created and
 * destroyed as needed. All gadgets should be destroyed in the
 * finalize (or dispose) vfunc.
 *
 * Returns: (transfer full): the new gadget
 */
GtkCssGadget *
gtk_css_custom_gadget_new_for_node (GtkCssNode                 *node,
                                    GtkWidget                  *owner,
                                    GtkCssPreferredSizeFunc     preferred_size_func,
                                    GtkCssAllocateFunc          allocate_func,
                                    GtkCssDrawFunc              draw_func,
                                    gpointer                    data,
                                    GDestroyNotify              destroy_func)
{
  GtkCssCustomGadgetPrivate *priv;
  GtkCssGadget *result;

  result = g_object_new (GTK_TYPE_CSS_CUSTOM_GADGET,
                         "node", node,
                         "owner", owner,
                         NULL);

  priv = gtk_css_custom_gadget_get_instance_private (GTK_CSS_CUSTOM_GADGET (result));

  priv->preferred_size_func = preferred_size_func;
  priv->allocate_func = allocate_func;
  priv->draw_func = draw_func;
  priv->data = data;
  priv->destroy_func = destroy_func;

  return result;
}

/**
 * gtk_css_custom_gadget_new:
 * @name: the name for the CSS node
 * @owner: the widget that the gadget belongs to
 * @parent: the gadget that has the parent CSS node
 * @next_sibling: the gadget that has the sibling CSS node
 * @preferred_size_func: (nullable): the GtkCssPreferredSizeFunc to use
 * @allocate_func: (nullable): the GtkCssAllocateFunc to use
 * @draw_func: (nullable): the GtkCssDrawFunc to use
 * @data: (nullable): user data to pass to the callbacks
 * @destroy_func: (nullable): destroy notify for @data
 *
 * Creates a #GtkCssCustomGadget with a new CSS node which gets
 * placed below the @parent's and before the @next_sibling's CSS node.
 *
 * Returns: (transfer full): the new gadget
 */
GtkCssGadget *
gtk_css_custom_gadget_new (const char                 *name,
                           GtkWidget                  *owner,
                           GtkCssGadget               *parent,
                           GtkCssGadget               *next_sibling,
                           GtkCssPreferredSizeFunc     preferred_size_func,
                           GtkCssAllocateFunc          allocate_func,
                           GtkCssDrawFunc              draw_func,
                           gpointer                    data,
                           GDestroyNotify              destroy_func)
{
  GtkCssNode *node;
  GtkCssGadget *result;

  node = gtk_css_node_new ();
  gtk_css_node_set_name (node, g_intern_string (name));
  if (parent)
    gtk_css_node_insert_before (gtk_css_gadget_get_node (parent),
                                node,
                                next_sibling ? gtk_css_gadget_get_node (next_sibling) : NULL);

  result = gtk_css_custom_gadget_new_for_node (node,
                                               owner,
                                               preferred_size_func,
                                               allocate_func,
                                               draw_func,
                                               data,
                                               destroy_func);

  g_object_unref (node);

  return result;
}