3 #include "extensions.h"
4 #include "framerender.h"
5 #include "render/theme.h"
7 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
8 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
9 ButtonPressMask | ButtonReleaseMask)
10 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
11 ButtonMotionMask | ExposureMask)
13 static void layout_title(Frame *self);
17 RECT_SET(theme_a_focused_pressed_desk->area, 0, 0,
18 theme_button_size, theme_button_size);
19 RECT_SET(theme_a_focused_pressed_set_desk->area, 0, 0,
20 theme_button_size, theme_button_size);
21 RECT_SET(theme_a_focused_unpressed_desk->area, 0, 0,
22 theme_button_size, theme_button_size);
23 RECT_SET(theme_a_unfocused_pressed_desk->area, 0, 0,
24 theme_button_size, theme_button_size);
25 RECT_SET(theme_a_unfocused_pressed_set_desk->area, 0, 0,
26 theme_button_size, theme_button_size);
27 RECT_SET(theme_a_unfocused_unpressed_desk->area, 0, 0,
28 theme_button_size, theme_button_size);
29 RECT_SET(theme_a_focused_pressed_shade->area, 0, 0,
30 theme_button_size, theme_button_size);
31 RECT_SET(theme_a_focused_pressed_set_shade->area, 0, 0,
32 theme_button_size, theme_button_size);
33 RECT_SET(theme_a_focused_unpressed_shade->area, 0, 0,
34 theme_button_size, theme_button_size);
35 RECT_SET(theme_a_unfocused_pressed_shade->area, 0, 0,
36 theme_button_size, theme_button_size);
37 RECT_SET(theme_a_unfocused_pressed_set_shade->area, 0, 0,
38 theme_button_size, theme_button_size);
39 RECT_SET(theme_a_unfocused_unpressed_shade->area, 0, 0,
40 theme_button_size, theme_button_size);
41 RECT_SET(theme_a_focused_pressed_iconify->area, 0, 0,
42 theme_button_size, theme_button_size);
43 RECT_SET(theme_a_focused_unpressed_iconify->area, 0, 0,
44 theme_button_size, theme_button_size);
45 RECT_SET(theme_a_unfocused_pressed_iconify->area, 0, 0,
46 theme_button_size, theme_button_size);
47 RECT_SET(theme_a_unfocused_unpressed_iconify->area, 0, 0,
48 theme_button_size, theme_button_size);
49 RECT_SET(theme_a_unfocused_unpressed_iconify->area, 0, 0,
50 theme_button_size, theme_button_size);
51 RECT_SET(theme_a_focused_pressed_max->area, 0, 0,
52 theme_button_size, theme_button_size);
53 RECT_SET(theme_a_focused_pressed_set_max->area, 0, 0,
54 theme_button_size, theme_button_size);
55 RECT_SET(theme_a_focused_unpressed_max->area, 0, 0,
56 theme_button_size, theme_button_size);
57 RECT_SET(theme_a_unfocused_pressed_max->area, 0, 0,
58 theme_button_size, theme_button_size);
59 RECT_SET(theme_a_unfocused_pressed_set_max->area, 0, 0,
60 theme_button_size, theme_button_size);
61 RECT_SET(theme_a_unfocused_unpressed_max->area, 0, 0,
62 theme_button_size, theme_button_size);
63 RECT_SET(theme_a_focused_pressed_close->area, 0, 0,
64 theme_button_size, theme_button_size);
65 RECT_SET(theme_a_focused_unpressed_close->area, 0, 0,
66 theme_button_size, theme_button_size);
67 RECT_SET(theme_a_unfocused_pressed_close->area, 0, 0,
68 theme_button_size, theme_button_size);
69 RECT_SET(theme_a_unfocused_unpressed_close->area, 0, 0,
70 theme_button_size, theme_button_size);
72 RECT_SET(theme_a_focused_grip->area, 0, 0,
73 theme_grip_width, theme_handle_height);
74 RECT_SET(theme_a_unfocused_grip->area, 0, 0,
75 theme_grip_width, theme_handle_height);
82 static Window createWindow(Window parent, unsigned long mask,
83 XSetWindowAttributes *attrib)
85 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
86 render_depth, InputOutput, render_visual,
93 XSetWindowAttributes attrib;
97 self = g_new(Frame, 1);
99 self->visible = FALSE;
101 /* create all of the decor windows */
102 mask = CWOverrideRedirect | CWEventMask;
103 attrib.event_mask = FRAME_EVENTMASK;
104 attrib.override_redirect = TRUE;
105 self->window = createWindow(ob_root, mask, &attrib);
108 self->plate = createWindow(self->window, mask, &attrib);
111 attrib.event_mask = ELEMENT_EVENTMASK;
112 self->title = createWindow(self->window, mask, &attrib);
113 self->label = createWindow(self->title, mask, &attrib);
114 self->max = createWindow(self->title, mask, &attrib);
115 self->close = createWindow(self->title, mask, &attrib);
116 self->desk = createWindow(self->title, mask, &attrib);
117 self->shade = createWindow(self->title, mask, &attrib);
118 self->icon = createWindow(self->title, mask, &attrib);
119 self->iconify = createWindow(self->title, mask, &attrib);
120 self->handle = createWindow(self->window, mask, &attrib);
122 attrib.cursor = ob_cursors.bl;
123 self->lgrip = createWindow(self->handle, mask, &attrib);
124 attrib.cursor = ob_cursors.br;
125 self->rgrip = createWindow(self->handle, mask, &attrib);
127 self->focused = FALSE;
129 /* the other stuff is shown based on decor settings */
130 XMapWindow(ob_display, self->plate);
131 XMapWindow(ob_display, self->lgrip);
132 XMapWindow(ob_display, self->rgrip);
133 XMapWindow(ob_display, self->label);
135 /* set colors/appearance/sizes for stuff that doesn't change */
136 XSetWindowBorder(ob_display, self->window, theme_b_color->pixel);
137 XSetWindowBorder(ob_display, self->label, theme_b_color->pixel);
138 XSetWindowBorder(ob_display, self->rgrip, theme_b_color->pixel);
139 XSetWindowBorder(ob_display, self->lgrip, theme_b_color->pixel);
141 XResizeWindow(ob_display, self->max, theme_button_size, theme_button_size);
142 XResizeWindow(ob_display, self->iconify,
143 theme_button_size, theme_button_size);
144 XResizeWindow(ob_display, self->icon,
145 theme_button_size + 2, theme_button_size + 2);
146 XResizeWindow(ob_display, self->close,
147 theme_button_size, theme_button_size);
148 XResizeWindow(ob_display, self->desk,
149 theme_button_size, theme_button_size);
150 XResizeWindow(ob_display, self->shade,
151 theme_button_size, theme_button_size);
152 XResizeWindow(ob_display, self->lgrip,
153 theme_grip_width, theme_handle_height);
154 XResizeWindow(ob_display, self->rgrip,
155 theme_grip_width, theme_handle_height);
157 /* set up the dynamic appearances */
158 self->a_unfocused_title = appearance_copy(theme_a_unfocused_title);
159 self->a_focused_title = appearance_copy(theme_a_focused_title);
160 self->a_unfocused_label = appearance_copy(theme_a_unfocused_label);
161 self->a_focused_label = appearance_copy(theme_a_focused_label);
162 self->a_unfocused_handle = appearance_copy(theme_a_unfocused_handle);
163 self->a_focused_handle = appearance_copy(theme_a_focused_handle);
164 self->a_icon = appearance_copy(theme_a_icon);
166 self->max_press = self->close_press = self->desk_press =
167 self->iconify_press = self->shade_press = FALSE;
172 static void frame_free(Frame *self)
174 appearance_free(self->a_unfocused_title);
175 appearance_free(self->a_focused_title);
176 appearance_free(self->a_unfocused_label);
177 appearance_free(self->a_focused_label);
178 appearance_free(self->a_unfocused_handle);
179 appearance_free(self->a_focused_handle);
180 appearance_free(self->a_icon);
182 XDestroyWindow(ob_display, self->window);
187 void frame_show(Frame *self)
189 if (!self->visible) {
190 self->visible = TRUE;
191 XMapWindow(ob_display, self->window);
195 void frame_hide(Frame *self)
198 self->visible = FALSE;
199 self->client->ignore_unmaps++;
200 XUnmapWindow(ob_display, self->window);
204 void frame_adjust_shape(Frame *self)
210 if (!self->client->shaped) {
211 /* clear the shape on the frame window */
212 XShapeCombineMask(ob_display, self->window, ShapeBounding,
213 self->innersize.left,
217 /* make the frame's shape match the clients */
218 XShapeCombineShape(ob_display, self->window, ShapeBounding,
219 self->innersize.left,
221 self->client->window,
222 ShapeBounding, ShapeSet);
225 if (self->client->decorations & Decor_Titlebar) {
226 xrect[0].x = -theme_bevel;
227 xrect[0].y = -theme_bevel;
228 xrect[0].width = self->width + self->bwidth * 2;
229 xrect[0].height = theme_title_height +
234 if (self->client->decorations & Decor_Handle) {
235 xrect[1].x = -theme_bevel;
236 xrect[1].y = FRAME_HANDLE_Y(self);
237 xrect[1].width = self->width + self->bwidth * 2;
238 xrect[1].height = theme_handle_height +
243 XShapeCombineRectangles(ob_display, self->window,
244 ShapeBounding, 0, 0, xrect, num,
245 ShapeUnion, Unsorted);
250 void frame_adjust_area(Frame *self, gboolean moved, gboolean resized)
253 if (self->client->decorations & Decor_Border) {
254 self->bwidth = theme_bwidth;
255 self->cbwidth = theme_cbwidth;
257 self->bwidth = self->cbwidth = 0;
259 STRUT_SET(self->innersize, self->cbwidth, self->cbwidth,
260 self->cbwidth, self->cbwidth);
261 self->width = self->client->area.width + self->cbwidth * 2;
262 g_assert(self->width > 0);
264 /* set border widths */
265 XSetWindowBorderWidth(ob_display, self->plate, self->cbwidth);
266 XSetWindowBorderWidth(ob_display, self->window, self->bwidth);
267 XSetWindowBorderWidth(ob_display, self->title, self->bwidth);
268 XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
269 XSetWindowBorderWidth(ob_display, self->lgrip, self->bwidth);
270 XSetWindowBorderWidth(ob_display, self->rgrip, self->bwidth);
272 /* position/size and map/unmap all the windows */
274 /* they all default off, they're turned on in layout_title */
278 self->iconify_x = -1;
283 if (self->client->decorations & Decor_Titlebar) {
284 XMoveResizeWindow(ob_display, self->title,
285 -self->bwidth, -self->bwidth,
286 self->width, theme_title_height);
287 self->innersize.top += theme_title_height + self->bwidth;
288 XMapWindow(ob_display, self->title);
290 RECT_SET(self->a_focused_title->area, 0, 0,
291 self->width, theme_title_height);
292 RECT_SET(self->a_unfocused_title->area, 0, 0,
293 self->width, theme_title_height);
295 /* layout the title bar elements */
298 XUnmapWindow(ob_display, self->title);
300 if (self->client->decorations & Decor_Handle) {
301 XMoveResizeWindow(ob_display, self->handle,
302 -self->bwidth, FRAME_HANDLE_Y(self),
303 self->width, theme_handle_height);
304 XMoveWindow(ob_display, self->lgrip,
305 -self->bwidth, -self->bwidth);
306 XMoveWindow(ob_display, self->rgrip,
307 -self->bwidth + self->width -
308 theme_grip_width, -self->bwidth);
309 self->innersize.bottom += theme_handle_height +
311 XMapWindow(ob_display, self->handle);
313 if (theme_a_focused_grip->surface.data.planar.grad ==
314 Background_ParentRelative)
315 RECT_SET(self->a_focused_handle->area, 0, 0,
316 self->width, theme_handle_height);
318 RECT_SET(self->a_focused_handle->area,
319 theme_grip_width + self->bwidth, 0,
320 self->width - (theme_grip_width + self->bwidth) * 2,
321 theme_handle_height);
322 if (theme_a_unfocused_grip->surface.data.planar.grad ==
323 Background_ParentRelative)
324 RECT_SET(self->a_unfocused_handle->area, 0, 0,
325 self->width, theme_handle_height);
327 RECT_SET(self->a_unfocused_handle->area,
328 theme_grip_width + self->bwidth, 0,
329 self->width - (theme_grip_width + self->bwidth) * 2,
330 theme_handle_height);
333 XUnmapWindow(ob_display, self->handle);
337 /* move and resize the plate */
338 XMoveResizeWindow(ob_display, self->plate,
339 self->innersize.left - self->cbwidth,
340 self->innersize.top - self->cbwidth,
341 self->client->area.width,
342 self->client->area.height);
343 /* when the client has StaticGravity, it likes to move around. */
344 XMoveWindow(ob_display, self->client->window, 0, 0);
348 STRUT_SET(self->size,
349 self->innersize.left + self->bwidth,
350 self->innersize.top + self->bwidth,
351 self->innersize.right + self->bwidth,
352 self->innersize.bottom + self->bwidth);
355 /* shading can change without being moved or resized */
356 RECT_SET_SIZE(self->area,
357 self->client->area.width +
358 self->size.left + self->size.right,
359 (self->client->shaded ? theme_title_height + self->bwidth*2:
360 self->client->area.height +
361 self->size.top + self->size.bottom));
364 /* find the new coordinates, done after setting the frame.size, for
365 frame_client_gravity. */
366 self->area.x = self->client->area.x;
367 self->area.y = self->client->area.y;
368 frame_client_gravity((Frame*)self,
369 &self->area.x, &self->area.y);
372 /* move and resize the top level frame.
373 shading can change without being moved or resized */
374 XMoveResizeWindow(ob_display, self->window,
375 self->area.x, self->area.y,
377 self->area.height - self->bwidth * 2);
380 framerender_frame(self);
382 frame_adjust_shape(self);
386 void frame_adjust_state(Frame *self)
388 framerender_frame(self);
391 void frame_adjust_focus(Frame *self, gboolean hilite)
393 self->focused = hilite;
394 framerender_frame(self);
397 void frame_adjust_title(Frame *self)
399 framerender_frame(self);
402 void frame_adjust_icon(Frame *self)
404 framerender_frame(self);
407 void frame_grab_client(Frame *self, Client *client)
409 self->client = client;
411 /* reparent the client to the frame */
412 XReparentWindow(ob_display, client->window, self->plate, 0, 0);
414 When reparenting the client window, it is usually not mapped yet, since
415 this occurs from a MapRequest. However, in the case where Openbox is
416 starting up, the window is already mapped, so we'll see unmap events for
417 it. There are 2 unmap events generated that we see, one with the 'event'
418 member set the root window, and one set to the client, but both get
419 handled and need to be ignored.
421 if (ob_state == State_Starting)
422 client->ignore_unmaps += 2;
424 /* select the event mask on the client's parent (to receive config/map
425 req's) the ButtonPress is to catch clicks on the client border */
426 XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
428 /* map the client so it maps when the frame does */
429 XMapWindow(ob_display, client->window);
431 frame_adjust_area(self, TRUE, TRUE);
433 /* set all the windows for the frame in the client_map */
434 g_hash_table_insert(client_map, &self->window, client);
435 g_hash_table_insert(client_map, &self->plate, client);
436 g_hash_table_insert(client_map, &self->title, client);
437 g_hash_table_insert(client_map, &self->label, client);
438 g_hash_table_insert(client_map, &self->max, client);
439 g_hash_table_insert(client_map, &self->close, client);
440 g_hash_table_insert(client_map, &self->desk, client);
441 g_hash_table_insert(client_map, &self->shade, client);
442 g_hash_table_insert(client_map, &self->icon, client);
443 g_hash_table_insert(client_map, &self->iconify, client);
444 g_hash_table_insert(client_map, &self->handle, client);
445 g_hash_table_insert(client_map, &self->lgrip, client);
446 g_hash_table_insert(client_map, &self->rgrip, client);
449 void frame_release_client(Frame *self, Client *client)
453 g_assert(self->client == client);
455 /* check if the app has already reparented its window away */
456 if (XCheckTypedWindowEvent(ob_display, client->window,
457 ReparentNotify, &ev)) {
458 XPutBackEvent(ob_display, &ev);
460 /* re-map the window since the unmanaging process unmaps it */
462 /* XXX ... um no it doesnt it unmaps its parent, the window itself
463 retains its mapped state, no?! XXX
464 XMapWindow(ob_display, client->window); */
466 /* according to the ICCCM - if the client doesn't reparent itself,
467 then we will reparent the window to root for them */
468 XReparentWindow(ob_display, client->window, ob_root,
473 /* remove all the windows for the frame from the client_map */
474 g_hash_table_remove(client_map, &self->window);
475 g_hash_table_remove(client_map, &self->plate);
476 g_hash_table_remove(client_map, &self->title);
477 g_hash_table_remove(client_map, &self->label);
478 g_hash_table_remove(client_map, &self->max);
479 g_hash_table_remove(client_map, &self->close);
480 g_hash_table_remove(client_map, &self->desk);
481 g_hash_table_remove(client_map, &self->shade);
482 g_hash_table_remove(client_map, &self->icon);
483 g_hash_table_remove(client_map, &self->iconify);
484 g_hash_table_remove(client_map, &self->handle);
485 g_hash_table_remove(client_map, &self->lgrip);
486 g_hash_table_remove(client_map, &self->rgrip);
491 static void layout_title(Frame *self)
495 gboolean n, d, i, l, m, c, s;
497 n = d = i = l = m = c = s = FALSE;
499 /* figure out whats being shown, and the width of the label */
500 self->label_width = self->width - (theme_bevel + 1) * 2;
501 for (lc = theme_title_layout; *lc != '\0'; ++lc) {
504 if (!(self->client->decorations & Decor_Icon)) break;
505 if (n) { *lc = ' '; break; } /* rm duplicates */
507 self->label_width -= theme_button_size + 2 + theme_bevel + 1;
510 if (!(self->client->decorations & Decor_AllDesktops)) break;
511 if (d) { *lc = ' '; break; } /* rm duplicates */
513 self->label_width -= theme_button_size + theme_bevel + 1;
516 if (!(self->client->decorations & Decor_Shade)) break;
517 if (s) { *lc = ' '; break; } /* rm duplicates */
519 self->label_width -= theme_button_size + theme_bevel + 1;
522 if (!(self->client->decorations & Decor_Iconify)) break;
523 if (i) { *lc = ' '; break; } /* rm duplicates */
525 self->label_width -= theme_button_size + theme_bevel + 1;
528 if (l) { *lc = ' '; break; } /* rm duplicates */
532 if (!(self->client->decorations & Decor_Maximize)) break;
533 if (m) { *lc = ' '; break; } /* rm duplicates */
535 self->label_width -= theme_button_size + theme_bevel + 1;
538 if (!(self->client->decorations & Decor_Close)) break;
539 if (c) { *lc = ' '; break; } /* rm duplicates */
541 self->label_width -= theme_button_size + theme_bevel + 1;
545 if (self->label_width < 1) self->label_width = 1;
547 XResizeWindow(ob_display, self->label, self->label_width,
550 if (!n) XUnmapWindow(ob_display, self->icon);
551 if (!d) XUnmapWindow(ob_display, self->desk);
552 if (!s) XUnmapWindow(ob_display, self->shade);
553 if (!i) XUnmapWindow(ob_display, self->iconify);
554 if (!l) XUnmapWindow(ob_display, self->label);
555 if (!m) XUnmapWindow(ob_display, self->max);
556 if (!c) XUnmapWindow(ob_display, self->close);
559 for (lc = theme_title_layout; *lc != '\0'; ++lc) {
564 RECT_SET(self->a_icon->area, 0, 0,
565 theme_button_size + 2, theme_button_size + 2);
566 XMapWindow(ob_display, self->icon);
567 XMoveWindow(ob_display, self->icon, x, theme_bevel);
568 x += theme_button_size + 2 + theme_bevel + 1;
573 XMapWindow(ob_display, self->desk);
574 XMoveWindow(ob_display, self->desk, x, theme_bevel + 1);
575 x += theme_button_size + theme_bevel + 1;
580 XMapWindow(ob_display, self->shade);
581 XMoveWindow(ob_display, self->shade, x, theme_bevel + 1);
582 x += theme_button_size + theme_bevel + 1;
587 XMapWindow(ob_display, self->iconify);
588 XMoveWindow(ob_display, self->iconify, x, theme_bevel + 1);
589 x += theme_button_size + theme_bevel + 1;
594 XMapWindow(ob_display, self->label);
595 XMoveWindow(ob_display, self->label, x, theme_bevel);
596 x += self->label_width + theme_bevel + 1;
601 XMapWindow(ob_display, self->max);
602 XMoveWindow(ob_display, self->max, x, theme_bevel + 1);
603 x += theme_button_size + theme_bevel + 1;
608 XMapWindow(ob_display, self->close);
609 XMoveWindow(ob_display, self->close, x, theme_bevel + 1);
610 x += theme_button_size + theme_bevel + 1;
615 RECT_SET(self->a_focused_label->area, 0, 0,
616 self->label_width, theme_label_height);
617 RECT_SET(self->a_unfocused_label->area, 0, 0,
618 self->label_width, theme_label_height);
621 Context frame_context_from_string(char *name)
623 if (!g_ascii_strcasecmp("root", name))
625 else if (!g_ascii_strcasecmp("client", name))
626 return Context_Client;
627 else if (!g_ascii_strcasecmp("titlebar", name))
628 return Context_Titlebar;
629 else if (!g_ascii_strcasecmp("handle", name))
630 return Context_Handle;
631 else if (!g_ascii_strcasecmp("frame", name))
632 return Context_Frame;
633 else if (!g_ascii_strcasecmp("blcorner", name))
634 return Context_BLCorner;
635 else if (!g_ascii_strcasecmp("tlcorner", name))
636 return Context_TLCorner;
637 else if (!g_ascii_strcasecmp("brcorner", name))
638 return Context_BRCorner;
639 else if (!g_ascii_strcasecmp("trcorner", name))
640 return Context_TRCorner;
641 else if (!g_ascii_strcasecmp("maximize", name))
642 return Context_Maximize;
643 else if (!g_ascii_strcasecmp("alldesktops", name))
644 return Context_AllDesktops;
645 else if (!g_ascii_strcasecmp("shade", name))
646 return Context_Shade;
647 else if (!g_ascii_strcasecmp("iconify", name))
648 return Context_Iconify;
649 else if (!g_ascii_strcasecmp("icon", name))
651 else if (!g_ascii_strcasecmp("close", name))
652 return Context_Close;
656 Context frame_context(Client *client, Window win)
660 if (win == ob_root) return Context_Root;
661 if (client == NULL) return Context_None;
662 if (win == client->window) return Context_Client;
664 self = client->frame;
665 if (win == self->window) return Context_Frame;
666 if (win == self->plate) return Context_Client;
667 if (win == self->title) return Context_Titlebar;
668 if (win == self->label) return Context_Titlebar;
669 if (win == self->handle) return Context_Handle;
670 if (win == self->lgrip) return Context_BLCorner;
671 if (win == self->rgrip) return Context_BRCorner;
672 if (win == self->max) return Context_Maximize;
673 if (win == self->iconify)return Context_Iconify;
674 if (win == self->close) return Context_Close;
675 if (win == self->icon) return Context_Icon;
676 if (win == self->desk) return Context_AllDesktops;
677 if (win == self->shade) return Context_Shade;
682 void frame_client_gravity(Frame *self, int *x, int *y)
685 switch (self->client->gravity) {
687 case NorthWestGravity:
688 case SouthWestGravity:
695 *x -= (self->size.left + self->size.right) / 2;
698 case NorthEastGravity:
699 case SouthEastGravity:
701 *x -= self->size.left + self->size.right;
706 *x -= self->size.left;
711 switch (self->client->gravity) {
713 case NorthWestGravity:
714 case NorthEastGravity:
721 *y -= (self->size.top + self->size.bottom) / 2;
724 case SouthWestGravity:
725 case SouthEastGravity:
727 *y -= self->size.top + self->size.bottom;
732 *y -= self->size.top;
737 void frame_frame_gravity(Frame *self, int *x, int *y)
740 switch (self->client->gravity) {
742 case NorthWestGravity:
744 case SouthWestGravity:
749 *x += (self->size.left + self->size.right) / 2;
751 case NorthEastGravity:
753 case SouthEastGravity:
754 *x += self->size.left + self->size.right;
758 *x += self->size.left;
763 switch (self->client->gravity) {
765 case NorthWestGravity:
767 case SouthWestGravity:
772 *y += (self->size.top + self->size.bottom) / 2;
774 case NorthEastGravity:
776 case SouthEastGravity:
777 *y += self->size.top + self->size.bottom;
781 *y += self->size.top;