1 | #include "demolayout.h" |
2 | |
3 | struct _DemoLayout |
4 | { |
5 | GtkLayoutManager parent_instance; |
6 | |
7 | float position; |
8 | int pos[16]; |
9 | }; |
10 | |
11 | struct _DemoLayoutClass |
12 | { |
13 | GtkLayoutManagerClass parent_class; |
14 | }; |
15 | |
16 | G_DEFINE_TYPE (DemoLayout, demo_layout, GTK_TYPE_LAYOUT_MANAGER) |
17 | |
18 | static void |
19 | demo_layout_measure (GtkLayoutManager *layout_manager, |
20 | GtkWidget *widget, |
21 | GtkOrientation orientation, |
22 | int for_size, |
23 | int *minimum, |
24 | int *natural, |
25 | int *minimum_baseline, |
26 | int *natural_baseline) |
27 | { |
28 | GtkWidget *child; |
29 | int minimum_size = 0; |
30 | int natural_size = 0; |
31 | |
32 | for (child = gtk_widget_get_first_child (widget); |
33 | child != NULL; |
34 | child = gtk_widget_get_next_sibling (widget: child)) |
35 | { |
36 | int child_min = 0, child_nat = 0; |
37 | |
38 | if (!gtk_widget_should_layout (widget: child)) |
39 | continue; |
40 | |
41 | gtk_widget_measure (widget: child, orientation, for_size: -1, |
42 | minimum: &child_min, natural: &child_nat, |
43 | NULL, NULL); |
44 | minimum_size = MAX (minimum_size, child_min); |
45 | natural_size = MAX (natural_size, child_nat); |
46 | } |
47 | |
48 | /* A back-of-a-napkin calculation to reserve enough |
49 | * space for arranging 16 children in a circle. |
50 | */ |
51 | *minimum = 16 * minimum_size / G_PI + minimum_size; |
52 | *natural = 16 * natural_size / G_PI + natural_size; |
53 | } |
54 | |
55 | static void |
56 | demo_layout_allocate (GtkLayoutManager *layout_manager, |
57 | GtkWidget *widget, |
58 | int width, |
59 | int height, |
60 | int baseline) |
61 | { |
62 | DemoLayout *self = DEMO_LAYOUT (ptr: layout_manager); |
63 | GtkWidget *child; |
64 | int i; |
65 | int child_width = 0; |
66 | int child_height = 0; |
67 | int x0, y0; |
68 | float r; |
69 | float t; |
70 | |
71 | t = self->position; |
72 | |
73 | for (child = gtk_widget_get_first_child (widget); |
74 | child != NULL; |
75 | child = gtk_widget_get_next_sibling (widget: child)) |
76 | { |
77 | GtkRequisition child_req; |
78 | |
79 | if (!gtk_widget_should_layout (widget: child)) |
80 | continue; |
81 | |
82 | gtk_widget_get_preferred_size (widget: child, minimum_size: &child_req, NULL); |
83 | |
84 | child_width = MAX (child_width, child_req.width); |
85 | child_height = MAX (child_height, child_req.height); |
86 | } |
87 | |
88 | /* the center of our layout */ |
89 | x0 = (width / 2); |
90 | y0 = (height / 2); |
91 | |
92 | /* the radius for our circle of children */ |
93 | r = 8 * child_width / G_PI; |
94 | |
95 | for (child = gtk_widget_get_first_child (widget), i = 0; |
96 | child != NULL; |
97 | child = gtk_widget_get_next_sibling (widget: child), i++) |
98 | { |
99 | GtkRequisition child_req; |
100 | float a = self->pos[i] * G_PI / 8; |
101 | int gx, gy; |
102 | int cx, cy; |
103 | int x, y; |
104 | |
105 | if (!gtk_widget_should_layout (widget: child)) |
106 | continue; |
107 | |
108 | gtk_widget_get_preferred_size (widget: child, minimum_size: &child_req, NULL); |
109 | |
110 | /* The grid position of child. */ |
111 | gx = x0 + (i % 4 - 2) * child_width; |
112 | gy = y0 + (i / 4 - 2) * child_height; |
113 | |
114 | /* The circle position of child. Note that we |
115 | * are adjusting the position by half the child size |
116 | * to place the center of child on a centered circle. |
117 | * This assumes that the children don't use align flags |
118 | * or uneven margins that would shift the center. |
119 | */ |
120 | cx = x0 + sin (x: a) * r - child_req.width / 2; |
121 | cy = y0 + cos (x: a) * r - child_req.height / 2; |
122 | |
123 | /* we interpolate between the two layouts according to |
124 | * the position value that has been set on the layout. |
125 | */ |
126 | x = t * cx + (1 - t) * gx; |
127 | y = t * cy + (1 - t) * gy; |
128 | |
129 | gtk_widget_size_allocate (widget: child, |
130 | allocation: &(const GtkAllocation){ x, y, child_width, child_height}, |
131 | baseline: -1); |
132 | } |
133 | } |
134 | |
135 | static GtkSizeRequestMode |
136 | demo_layout_get_request_mode (GtkLayoutManager *layout_manager, |
137 | GtkWidget *widget) |
138 | { |
139 | return GTK_SIZE_REQUEST_CONSTANT_SIZE; |
140 | } |
141 | |
142 | static void |
143 | demo_layout_class_init (DemoLayoutClass *klass) |
144 | { |
145 | GtkLayoutManagerClass *layout_class = GTK_LAYOUT_MANAGER_CLASS (ptr: klass); |
146 | |
147 | layout_class->get_request_mode = demo_layout_get_request_mode; |
148 | layout_class->measure = demo_layout_measure; |
149 | layout_class->allocate = demo_layout_allocate; |
150 | } |
151 | |
152 | static void |
153 | demo_layout_init (DemoLayout *self) |
154 | { |
155 | int i; |
156 | |
157 | for (i = 0; i < 16; i++) |
158 | self->pos[i] = i; |
159 | } |
160 | |
161 | GtkLayoutManager * |
162 | demo_layout_new (void) |
163 | { |
164 | return g_object_new (DEMO_TYPE_LAYOUT, NULL); |
165 | } |
166 | |
167 | void |
168 | demo_layout_set_position (DemoLayout *layout, |
169 | float position) |
170 | { |
171 | layout->position = position; |
172 | } |
173 | |
174 | /* Shuffle the circle positions of the children. |
175 | * Should be called when we are in the grid layout. |
176 | */ |
177 | void |
178 | demo_layout_shuffle (DemoLayout *layout) |
179 | { |
180 | int i, j, tmp; |
181 | |
182 | for (i = 0; i < 16; i++) |
183 | { |
184 | j = g_random_int_range (begin: 0, end: i + 1); |
185 | tmp = layout->pos[i]; |
186 | layout->pos[i] = layout->pos[j]; |
187 | layout->pos[j] = tmp; |
188 | } |
189 | } |
190 | |