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/**
26 * GtkFixed:
27 *
28 * `GtkFixed` places its child widgets at fixed positions and with fixed sizes.
29 *
30 * `GtkFixed` performs no automatic layout management.
31 *
32 * For most applications, you should not use this container! It keeps
33 * you from having to learn about the other GTK containers, but it
34 * results in broken applications. With `GtkFixed`, the following
35 * things will result in truncated text, overlapping widgets, and
36 * other display bugs:
37 *
38 * - Themes, which may change widget sizes.
39 *
40 * - Fonts other than the one you used to write the app will of course
41 * change the size of widgets containing text; keep in mind that
42 * users may use a larger font because of difficulty reading the
43 * default, or they may be using a different OS that provides different fonts.
44 *
45 * - Translation of text into other languages changes its size. Also,
46 * display of non-English text will use a different font in many
47 * cases.
48 *
49 * In addition, `GtkFixed` does not pay attention to text direction and
50 * thus may produce unwanted results if your app is run under right-to-left
51 * languages such as Hebrew or Arabic. That is: normally GTK will order
52 * containers appropriately for the text direction, e.g. to put labels to
53 * the right of the thing they label when using an RTL language, but it can’t
54 * do that with `GtkFixed`. So if you need to reorder widgets depending on
55 * the text direction, you would need to manually detect it and adjust child
56 * positions accordingly.
57 *
58 * Finally, fixed positioning makes it kind of annoying to add/remove
59 * UI elements, since you have to reposition all the other elements. This
60 * is a long-term maintenance problem for your application.
61 *
62 * If you know none of these things are an issue for your application,
63 * and prefer the simplicity of `GtkFixed`, by all means use the
64 * widget. But you should be aware of the tradeoffs.
65 */
66
67#include "config.h"
68
69#include "gtkfixed.h"
70
71#include "gtkfixedlayout.h"
72#include "gtkintl.h"
73#include "gtkprivate.h"
74#include "gtkwidgetprivate.h"
75#include "gtkbuildable.h"
76
77typedef struct {
78 GtkLayoutManager *layout;
79} GtkFixedPrivate;
80
81static void gtk_fixed_buildable_iface_init (GtkBuildableIface *iface);
82
83G_DEFINE_TYPE_WITH_CODE (GtkFixed, gtk_fixed, GTK_TYPE_WIDGET,
84 G_ADD_PRIVATE (GtkFixed)
85 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
86 gtk_fixed_buildable_iface_init))
87
88static void
89gtk_fixed_compute_expand (GtkWidget *widget,
90 gboolean *hexpand_p,
91 gboolean *vexpand_p)
92{
93 GtkWidget *w;
94 gboolean hexpand = FALSE;
95 gboolean vexpand = FALSE;
96
97 for (w = gtk_widget_get_first_child (widget);
98 w != NULL;
99 w = gtk_widget_get_next_sibling (widget: w))
100 {
101 hexpand = hexpand || gtk_widget_compute_expand (widget: w, orientation: GTK_ORIENTATION_HORIZONTAL);
102 vexpand = vexpand || gtk_widget_compute_expand (widget: w, orientation: GTK_ORIENTATION_VERTICAL);
103 }
104
105 *hexpand_p = hexpand;
106 *vexpand_p = vexpand;
107}
108
109static GtkSizeRequestMode
110gtk_fixed_get_request_mode (GtkWidget *widget)
111{
112 GtkWidget *w;
113 int wfh = 0, hfw = 0;
114
115 for (w = gtk_widget_get_first_child (widget);
116 w != NULL;
117 w = gtk_widget_get_next_sibling (widget: w))
118 {
119 GtkSizeRequestMode mode = gtk_widget_get_request_mode (widget: w);
120
121 switch (mode)
122 {
123 case GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH:
124 hfw ++;
125 break;
126 case GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT:
127 wfh ++;
128 break;
129 case GTK_SIZE_REQUEST_CONSTANT_SIZE:
130 default:
131 break;
132 }
133 }
134
135 if (hfw == 0 && wfh == 0)
136 return GTK_SIZE_REQUEST_CONSTANT_SIZE;
137 else
138 return wfh > hfw ?
139 GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT :
140 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
141}
142
143static void
144gtk_fixed_dispose (GObject *object)
145{
146 GtkWidget *child;
147
148 while ((child = gtk_widget_get_first_child (GTK_WIDGET (object))))
149 gtk_fixed_remove (GTK_FIXED (object), widget: child);
150
151 G_OBJECT_CLASS (gtk_fixed_parent_class)->dispose (object);
152}
153
154static void
155gtk_fixed_class_init (GtkFixedClass *klass)
156{
157 GObjectClass *object_class = G_OBJECT_CLASS (klass);
158 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
159
160 object_class->dispose = gtk_fixed_dispose;
161
162 widget_class->compute_expand = gtk_fixed_compute_expand;
163 widget_class->get_request_mode = gtk_fixed_get_request_mode;
164
165 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_FIXED_LAYOUT);
166}
167
168static GtkBuildableIface *parent_buildable_iface;
169
170static void
171gtk_fixed_buildable_add_child (GtkBuildable *buildable,
172 GtkBuilder *builder,
173 GObject *child,
174 const char *type)
175{
176 if (GTK_IS_WIDGET (child))
177 gtk_fixed_put (GTK_FIXED (buildable), GTK_WIDGET (child), x: 0, y: 0);
178 else
179 parent_buildable_iface->add_child (buildable, builder, child, type);
180}
181
182static void
183gtk_fixed_buildable_iface_init (GtkBuildableIface *iface)
184{
185 parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface);
186
187 iface->add_child = gtk_fixed_buildable_add_child;
188}
189
190static void
191gtk_fixed_init (GtkFixed *self)
192{
193 GtkFixedPrivate *priv = gtk_fixed_get_instance_private (self);
194
195 gtk_widget_set_overflow (GTK_WIDGET (self), overflow: GTK_OVERFLOW_HIDDEN);
196
197 priv->layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
198}
199
200/**
201 * gtk_fixed_new:
202 *
203 * Creates a new `GtkFixed`.
204 *
205 * Returns: a new `GtkFixed`.
206 */
207GtkWidget*
208gtk_fixed_new (void)
209{
210 return g_object_new (GTK_TYPE_FIXED, NULL);
211}
212
213/**
214 * gtk_fixed_put:
215 * @fixed: a `GtkFixed`
216 * @widget: the widget to add
217 * @x: the horizontal position to place the widget at
218 * @y: the vertical position to place the widget at
219 *
220 * Adds a widget to a `GtkFixed` at the given position.
221 */
222void
223gtk_fixed_put (GtkFixed *fixed,
224 GtkWidget *widget,
225 double x,
226 double y)
227{
228 GtkFixedPrivate *priv = gtk_fixed_get_instance_private (self: fixed);
229 GtkFixedLayoutChild *child_info;
230 GskTransform *transform = NULL;
231
232 g_return_if_fail (GTK_IS_FIXED (fixed));
233 g_return_if_fail (GTK_IS_WIDGET (widget));
234 g_return_if_fail (_gtk_widget_get_parent (widget) == NULL);
235
236 gtk_widget_set_parent (widget, GTK_WIDGET (fixed));
237
238 child_info = GTK_FIXED_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout, child: widget));
239
240 transform = gsk_transform_translate (next: transform, point: &GRAPHENE_POINT_INIT (x, y));
241 gtk_fixed_layout_child_set_transform (child: child_info, transform);
242 gsk_transform_unref (self: transform);
243}
244
245/**
246 * gtk_fixed_get_child_position:
247 * @fixed: a `GtkFixed`
248 * @widget: a child of @fixed
249 * @x: (out): the horizontal position of the @widget
250 * @y: (out): the vertical position of the @widget
251 *
252 * Retrieves the translation transformation of the
253 * given child `GtkWidget` in the `GtkFixed`.
254 *
255 * See also: [method@Gtk.Fixed.get_child_transform].
256 */
257void
258gtk_fixed_get_child_position (GtkFixed *fixed,
259 GtkWidget *widget,
260 double *x,
261 double *y)
262{
263 g_return_if_fail (GTK_IS_FIXED (fixed));
264 g_return_if_fail (GTK_IS_WIDGET (widget));
265 g_return_if_fail (x != NULL);
266 g_return_if_fail (y != NULL);
267 g_return_if_fail (gtk_widget_get_parent (widget) == GTK_WIDGET (fixed));
268
269 gtk_widget_translate_coordinates (src_widget: widget, GTK_WIDGET (fixed), src_x: 0, src_y: 0, dest_x: x, dest_y: y);
270}
271
272/**
273 * gtk_fixed_set_child_transform:
274 * @fixed: a `GtkFixed`
275 * @widget: a `GtkWidget`, child of @fixed
276 * @transform: (nullable): the transformation assigned to @widget
277 * to reset @widget's transform
278 *
279 * Sets the transformation for @widget.
280 *
281 * This is a convenience function that retrieves the
282 * [class@Gtk.FixedLayoutChild] instance associated to
283 * @widget and calls [method@Gtk.FixedLayoutChild.set_transform].
284 */
285void
286gtk_fixed_set_child_transform (GtkFixed *fixed,
287 GtkWidget *widget,
288 GskTransform *transform)
289{
290 GtkFixedPrivate *priv = gtk_fixed_get_instance_private (self: fixed);
291 GtkFixedLayoutChild *child_info;
292
293 g_return_if_fail (GTK_IS_FIXED (fixed));
294 g_return_if_fail (GTK_IS_WIDGET (widget));
295 g_return_if_fail (gtk_widget_get_parent (widget) == GTK_WIDGET (fixed));
296
297 child_info = GTK_FIXED_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout, child: widget));
298 gtk_fixed_layout_child_set_transform (child: child_info, transform);
299}
300
301/**
302 * gtk_fixed_get_child_transform:
303 * @fixed: a `GtkFixed`
304 * @widget: a `GtkWidget`, child of @fixed
305 *
306 * Retrieves the transformation for @widget set using
307 * gtk_fixed_set_child_transform().
308 *
309 * Returns: (transfer none) (nullable): a `GskTransform`
310 */
311GskTransform *
312gtk_fixed_get_child_transform (GtkFixed *fixed,
313 GtkWidget *widget)
314{
315 GtkFixedPrivate *priv = gtk_fixed_get_instance_private (self: fixed);
316 GtkFixedLayoutChild *child_info;
317
318 g_return_val_if_fail (GTK_IS_FIXED (fixed), NULL);
319 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
320 g_return_val_if_fail (gtk_widget_get_parent (widget) == GTK_WIDGET (fixed), NULL);
321
322 child_info = GTK_FIXED_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout, child: widget));
323 return gtk_fixed_layout_child_get_transform (child: child_info);
324}
325
326/**
327 * gtk_fixed_move:
328 * @fixed: a `GtkFixed`
329 * @widget: the child widget
330 * @x: the horizontal position to move the widget to
331 * @y: the vertical position to move the widget to
332 *
333 * Sets a translation transformation to the given @x and @y
334 * coordinates to the child @widget of the `GtkFixed`.
335 */
336void
337gtk_fixed_move (GtkFixed *fixed,
338 GtkWidget *widget,
339 double x,
340 double y)
341{
342 GtkFixedPrivate *priv = gtk_fixed_get_instance_private (self: fixed);
343 GtkFixedLayoutChild *child_info;
344 GskTransform *transform = NULL;
345
346 g_return_if_fail (GTK_IS_FIXED (fixed));
347 g_return_if_fail (GTK_IS_WIDGET (widget));
348 g_return_if_fail (gtk_widget_get_parent (widget) == GTK_WIDGET (fixed));
349
350 child_info = GTK_FIXED_LAYOUT_CHILD (ptr: gtk_layout_manager_get_layout_child (manager: priv->layout, child: widget));
351
352 transform = gsk_transform_translate (next: transform, point: &GRAPHENE_POINT_INIT (x, y));
353 gtk_fixed_layout_child_set_transform (child: child_info, transform);
354 gsk_transform_unref (self: transform);
355}
356
357/**
358 * gtk_fixed_remove:
359 * @fixed: a `GtkFixed`
360 * @widget: the child widget to remove
361 *
362 * Removes a child from @fixed.
363 */
364void
365gtk_fixed_remove (GtkFixed *fixed,
366 GtkWidget *widget)
367{
368 g_return_if_fail (GTK_IS_FIXED (fixed));
369 g_return_if_fail (GTK_IS_WIDGET (widget));
370 g_return_if_fail (gtk_widget_get_parent (widget) == GTK_WIDGET (fixed));
371
372 gtk_widget_unparent (widget);
373}
374

source code of gtk/gtk/gtkfixed.c