]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/frame.c
simply the render interface by not requiring the setting of all the areas. only take...
[mikachu/openbox.git] / openbox / frame.c
1 #include "frame.h"
2 #include "openbox.h"
3 #include "extensions.h"
4 #include "framerender.h"
5 #include "render/theme.h"
6
7 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
8 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
9                          ButtonPressMask | ButtonReleaseMask)
10 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
11                            ButtonMotionMask | ExposureMask)
12
13 static void layout_title(Frame *self);
14
15 void frame_startup()
16 {
17 }
18
19 void frame_shutdown()
20 {
21 }
22
23 static Window createWindow(Window parent, unsigned long mask,
24                            XSetWindowAttributes *attrib)
25 {
26     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
27                          render_depth, InputOutput, render_visual,
28                          mask, attrib);
29                        
30 }
31
32 Frame *frame_new()
33 {
34     XSetWindowAttributes attrib;
35     unsigned long mask;
36     Frame *self;
37
38     self = g_new(Frame, 1);
39
40     self->visible = FALSE;
41
42     /* create all of the decor windows */
43     mask = CWOverrideRedirect | CWEventMask;
44     attrib.event_mask = FRAME_EVENTMASK;
45     attrib.override_redirect = TRUE;
46     self->window = createWindow(ob_root, mask, &attrib);
47
48     mask = 0;
49     self->plate = createWindow(self->window, mask, &attrib);
50
51     mask = CWEventMask;
52     attrib.event_mask = ELEMENT_EVENTMASK;
53     self->title = createWindow(self->window, mask, &attrib);
54     self->label = createWindow(self->title, mask, &attrib);
55     self->max = createWindow(self->title, mask, &attrib);
56     self->close = createWindow(self->title, mask, &attrib);
57     self->desk = createWindow(self->title, mask, &attrib);
58     self->shade = createWindow(self->title, mask, &attrib);
59     self->icon = createWindow(self->title, mask, &attrib);
60     self->iconify = createWindow(self->title, mask, &attrib);
61     self->handle = createWindow(self->window, mask, &attrib);
62     mask |= CWCursor;
63     attrib.cursor = ob_cursors.bl;
64     self->lgrip = createWindow(self->handle, mask, &attrib);
65     attrib.cursor = ob_cursors.br;
66     self->rgrip = createWindow(self->handle, mask, &attrib);
67
68     self->focused = FALSE;
69
70     /* the other stuff is shown based on decor settings */
71     XMapWindow(ob_display, self->plate);
72     XMapWindow(ob_display, self->lgrip);
73     XMapWindow(ob_display, self->rgrip);
74     XMapWindow(ob_display, self->label);
75
76     /* set colors/appearance/sizes for stuff that doesn't change */
77     XSetWindowBorder(ob_display, self->window, theme_b_color->pixel);
78     XSetWindowBorder(ob_display, self->label, theme_b_color->pixel);
79     XSetWindowBorder(ob_display, self->rgrip, theme_b_color->pixel);
80     XSetWindowBorder(ob_display, self->lgrip, theme_b_color->pixel);
81
82     XResizeWindow(ob_display, self->max, theme_button_size, theme_button_size);
83     XResizeWindow(ob_display, self->iconify,
84                   theme_button_size, theme_button_size);
85     XResizeWindow(ob_display, self->icon,
86                   theme_button_size + 2, theme_button_size + 2);
87     XResizeWindow(ob_display, self->close,
88                   theme_button_size, theme_button_size);
89     XResizeWindow(ob_display, self->desk,
90                   theme_button_size, theme_button_size);
91     XResizeWindow(ob_display, self->shade,
92                   theme_button_size, theme_button_size);
93     XResizeWindow(ob_display, self->lgrip,
94                   theme_grip_width, theme_handle_height);
95     XResizeWindow(ob_display, self->rgrip,
96                   theme_grip_width, theme_handle_height);
97
98     /* set up the dynamic appearances */
99     self->a_unfocused_title = appearance_copy(theme_a_unfocused_title);
100     self->a_focused_title = appearance_copy(theme_a_focused_title);
101     self->a_unfocused_label = appearance_copy(theme_a_unfocused_label);
102     self->a_focused_label = appearance_copy(theme_a_focused_label);
103     self->a_unfocused_handle = appearance_copy(theme_a_unfocused_handle);
104     self->a_focused_handle = appearance_copy(theme_a_focused_handle);
105     self->a_icon = appearance_copy(theme_a_icon);
106
107     self->max_press = self->close_press = self->desk_press = 
108         self->iconify_press = self->shade_press = FALSE;
109
110     return (Frame*)self;
111 }
112
113 static void frame_free(Frame *self)
114 {
115     appearance_free(self->a_unfocused_title); 
116     appearance_free(self->a_focused_title);
117     appearance_free(self->a_unfocused_label);
118     appearance_free(self->a_focused_label);
119     appearance_free(self->a_unfocused_handle);
120     appearance_free(self->a_focused_handle);
121     appearance_free(self->a_icon);
122
123     XDestroyWindow(ob_display, self->window);
124
125     g_free(self);
126 }
127
128 void frame_show(Frame *self)
129 {
130     if (!self->visible) {
131         self->visible = TRUE;
132         XMapWindow(ob_display, self->window);
133     }
134 }
135
136 void frame_hide(Frame *self)
137 {
138     if (self->visible) {
139         self->visible = FALSE;
140         self->client->ignore_unmaps++;
141         XUnmapWindow(ob_display, self->window);
142     }
143 }
144
145 void frame_adjust_shape(Frame *self)
146 {
147 #ifdef SHAPE
148     int num;
149     XRectangle xrect[2];
150
151     if (!self->client->shaped) {
152         /* clear the shape on the frame window */
153         XShapeCombineMask(ob_display, self->window, ShapeBounding,
154                           self->innersize.left,
155                           self->innersize.top,
156                           None, ShapeSet);
157     } else {
158         /* make the frame's shape match the clients */
159         XShapeCombineShape(ob_display, self->window, ShapeBounding,
160                            self->innersize.left,
161                            self->innersize.top,
162                            self->client->window,
163                            ShapeBounding, ShapeSet);
164
165         num = 0;
166         if (self->client->decorations & Decor_Titlebar) {
167             xrect[0].x = -theme_bevel;
168             xrect[0].y = -theme_bevel;
169             xrect[0].width = self->width + self->bwidth * 2;
170             xrect[0].height = theme_title_height +
171                 self->bwidth * 2;
172             ++num;
173         }
174
175         if (self->client->decorations & Decor_Handle) {
176             xrect[1].x = -theme_bevel;
177             xrect[1].y = FRAME_HANDLE_Y(self);
178             xrect[1].width = self->width + self->bwidth * 2;
179             xrect[1].height = theme_handle_height +
180                 self->bwidth * 2;
181             ++num;
182         }
183
184         XShapeCombineRectangles(ob_display, self->window,
185                                 ShapeBounding, 0, 0, xrect, num,
186                                 ShapeUnion, Unsorted);
187     }
188 #endif
189 }
190
191 void frame_adjust_area(Frame *self, gboolean moved, gboolean resized)
192 {
193     if (resized) {
194         if (self->client->decorations & Decor_Border) {
195             self->bwidth = theme_bwidth;
196             self->cbwidth = theme_cbwidth;
197         } else {
198             self->bwidth = self->cbwidth = 0;
199         }
200         STRUT_SET(self->innersize, self->cbwidth, self->cbwidth,
201                   self->cbwidth, self->cbwidth);
202         self->width = self->client->area.width + self->cbwidth * 2;
203         g_assert(self->width > 0);
204
205         /* set border widths */
206         XSetWindowBorderWidth(ob_display, self->plate,  self->cbwidth);
207         XSetWindowBorderWidth(ob_display, self->window, self->bwidth);
208         XSetWindowBorderWidth(ob_display, self->title,  self->bwidth);
209         XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
210         XSetWindowBorderWidth(ob_display, self->lgrip,  self->bwidth);
211         XSetWindowBorderWidth(ob_display, self->rgrip,  self->bwidth);
212   
213         /* position/size and map/unmap all the windows */
214
215         /* they all default off, they're turned on in layout_title */
216         self->icon_x = -1;
217         self->desk_x = -1;
218         self->shade_x = -1;
219         self->iconify_x = -1;
220         self->label_x = -1;
221         self->max_x = -1;
222         self->close_x = -1;
223
224         if (self->client->decorations & Decor_Titlebar) {
225             XMoveResizeWindow(ob_display, self->title,
226                               -self->bwidth, -self->bwidth,
227                               self->width, theme_title_height);
228             self->innersize.top += theme_title_height + self->bwidth;
229             XMapWindow(ob_display, self->title);
230
231             /* layout the title bar elements */
232             layout_title(self);
233         } else
234             XUnmapWindow(ob_display, self->title);
235
236         if (self->client->decorations & Decor_Handle) {
237             XMoveResizeWindow(ob_display, self->handle,
238                               -self->bwidth, FRAME_HANDLE_Y(self),
239                               self->width, theme_handle_height);
240             XMoveWindow(ob_display, self->lgrip,
241                         -self->bwidth, -self->bwidth);
242             XMoveWindow(ob_display, self->rgrip,
243                         -self->bwidth + self->width -
244                         theme_grip_width, -self->bwidth);
245             self->innersize.bottom += theme_handle_height +
246                 self->bwidth;
247             XMapWindow(ob_display, self->handle);
248
249             /* XXX make a subwindow with these dimentions?
250                theme_grip_width + self->bwidth, 0,
251                self->width - (theme_grip_width + self->bwidth) * 2,
252                theme_handle_height);
253             */
254         } else
255             XUnmapWindow(ob_display, self->handle);
256     }
257
258     if (resized) {
259         /* move and resize the plate */
260         XMoveResizeWindow(ob_display, self->plate,
261                           self->innersize.left - self->cbwidth,
262                           self->innersize.top - self->cbwidth,
263                           self->client->area.width,
264                           self->client->area.height);
265         /* when the client has StaticGravity, it likes to move around. */
266         XMoveWindow(ob_display, self->client->window, 0, 0);
267     }
268
269     if (resized) {
270         STRUT_SET(self->size,
271                   self->innersize.left + self->bwidth,
272                   self->innersize.top + self->bwidth,
273                   self->innersize.right + self->bwidth,
274                   self->innersize.bottom + self->bwidth);
275     }
276
277     /* shading can change without being moved or resized */
278     RECT_SET_SIZE(self->area,
279                   self->client->area.width +
280                   self->size.left + self->size.right,
281                   (self->client->shaded ? theme_title_height + self->bwidth*2:
282                    self->client->area.height +
283                    self->size.top + self->size.bottom));
284
285     if (moved) {
286         /* find the new coordinates, done after setting the frame.size, for
287            frame_client_gravity. */
288         self->area.x = self->client->area.x;
289         self->area.y = self->client->area.y;
290         frame_client_gravity((Frame*)self,
291                              &self->area.x, &self->area.y);
292     }
293
294     /* move and resize the top level frame.
295        shading can change without being moved or resized */
296     XMoveResizeWindow(ob_display, self->window,
297                       self->area.x, self->area.y,
298                       self->width,
299                       self->area.height - self->bwidth * 2);
300
301     if (resized) {
302         framerender_frame(self);
303
304         frame_adjust_shape(self);
305     }
306 }
307
308 void frame_adjust_state(Frame *self)
309 {
310     framerender_frame(self);
311 }
312
313 void frame_adjust_focus(Frame *self, gboolean hilite)
314 {
315     self->focused = hilite;
316     framerender_frame(self);
317 }
318
319 void frame_adjust_title(Frame *self)
320 {
321     framerender_frame(self);
322 }
323
324 void frame_adjust_icon(Frame *self)
325 {
326     framerender_frame(self);
327 }
328
329 void frame_grab_client(Frame *self, Client *client)
330 {
331     self->client = client;
332
333     /* reparent the client to the frame */
334     XReparentWindow(ob_display, client->window, self->plate, 0, 0);
335     /*
336       When reparenting the client window, it is usually not mapped yet, since
337       this occurs from a MapRequest. However, in the case where Openbox is
338       starting up, the window is already mapped, so we'll see unmap events for
339       it. There are 2 unmap events generated that we see, one with the 'event'
340       member set the root window, and one set to the client, but both get
341       handled and need to be ignored.
342     */
343     if (ob_state == State_Starting)
344         client->ignore_unmaps += 2;
345
346     /* select the event mask on the client's parent (to receive config/map
347        req's) the ButtonPress is to catch clicks on the client border */
348     XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
349
350     /* map the client so it maps when the frame does */
351     XMapWindow(ob_display, client->window);
352
353     frame_adjust_area(self, TRUE, TRUE);
354
355     /* set all the windows for the frame in the window_map */
356     g_hash_table_insert(window_map, &self->window, client);
357     g_hash_table_insert(window_map, &self->plate, client);
358     g_hash_table_insert(window_map, &self->title, client);
359     g_hash_table_insert(window_map, &self->label, client);
360     g_hash_table_insert(window_map, &self->max, client);
361     g_hash_table_insert(window_map, &self->close, client);
362     g_hash_table_insert(window_map, &self->desk, client);
363     g_hash_table_insert(window_map, &self->shade, client);
364     g_hash_table_insert(window_map, &self->icon, client);
365     g_hash_table_insert(window_map, &self->iconify, client);
366     g_hash_table_insert(window_map, &self->handle, client);
367     g_hash_table_insert(window_map, &self->lgrip, client);
368     g_hash_table_insert(window_map, &self->rgrip, client);
369 }
370
371 void frame_release_client(Frame *self, Client *client)
372 {
373     XEvent ev;
374
375     g_assert(self->client == client);
376
377     /* check if the app has already reparented its window away */
378     if (XCheckTypedWindowEvent(ob_display, client->window,
379                                ReparentNotify, &ev)) {
380         XPutBackEvent(ob_display, &ev);
381
382         /* re-map the window since the unmanaging process unmaps it */
383
384         /* XXX ... um no it doesnt it unmaps its parent, the window itself
385            retains its mapped state, no?! XXX
386            XMapWindow(ob_display, client->window); */
387     } else {
388         /* according to the ICCCM - if the client doesn't reparent itself,
389            then we will reparent the window to root for them */
390         XReparentWindow(ob_display, client->window, ob_root,
391                         client->area.x,
392                         client->area.y);
393     }
394
395     /* remove all the windows for the frame from the window_map */
396     g_hash_table_remove(window_map, &self->window);
397     g_hash_table_remove(window_map, &self->plate);
398     g_hash_table_remove(window_map, &self->title);
399     g_hash_table_remove(window_map, &self->label);
400     g_hash_table_remove(window_map, &self->max);
401     g_hash_table_remove(window_map, &self->close);
402     g_hash_table_remove(window_map, &self->desk);
403     g_hash_table_remove(window_map, &self->shade);
404     g_hash_table_remove(window_map, &self->icon);
405     g_hash_table_remove(window_map, &self->iconify);
406     g_hash_table_remove(window_map, &self->handle);
407     g_hash_table_remove(window_map, &self->lgrip);
408     g_hash_table_remove(window_map, &self->rgrip);
409
410     frame_free(self);
411 }
412
413 static void layout_title(Frame *self)
414 {
415     char *lc;
416     int x;
417     gboolean n, d, i, l, m, c, s;
418
419     n = d = i = l = m = c = s = FALSE;
420
421     /* figure out whats being shown, and the width of the label */
422     self->label_width = self->width - (theme_bevel + 1) * 2;
423     for (lc = theme_title_layout; *lc != '\0'; ++lc) {
424         switch (*lc) {
425         case 'N':
426             if (!(self->client->decorations & Decor_Icon)) break;
427             if (n) { *lc = ' '; break; } /* rm duplicates */
428             n = TRUE;
429             self->label_width -= theme_button_size + 2 + theme_bevel + 1;
430             break;
431         case 'D':
432             if (!(self->client->decorations & Decor_AllDesktops)) break;
433             if (d) { *lc = ' '; break; } /* rm duplicates */
434             d = TRUE;
435             self->label_width -= theme_button_size + theme_bevel + 1;
436             break;
437         case 'S':
438             if (!(self->client->decorations & Decor_Shade)) break;
439             if (s) { *lc = ' '; break; } /* rm duplicates */
440             s = TRUE;
441             self->label_width -= theme_button_size + theme_bevel + 1;
442             break;
443         case 'I':
444             if (!(self->client->decorations & Decor_Iconify)) break;
445             if (i) { *lc = ' '; break; } /* rm duplicates */
446             i = TRUE;
447             self->label_width -= theme_button_size + theme_bevel + 1;
448             break;
449         case 'L':
450             if (l) { *lc = ' '; break; } /* rm duplicates */
451             l = TRUE;
452             break;
453         case 'M':
454             if (!(self->client->decorations & Decor_Maximize)) break;
455             if (m) { *lc = ' '; break; } /* rm duplicates */
456             m = TRUE;
457             self->label_width -= theme_button_size + theme_bevel + 1;
458             break;
459         case 'C':
460             if (!(self->client->decorations & Decor_Close)) break;
461             if (c) { *lc = ' '; break; } /* rm duplicates */
462             c = TRUE;
463             self->label_width -= theme_button_size + theme_bevel + 1;
464             break;
465         }
466     }
467     if (self->label_width < 1) self->label_width = 1;
468
469     XResizeWindow(ob_display, self->label, self->label_width,
470                   theme_label_height);
471   
472     if (!n) XUnmapWindow(ob_display, self->icon);
473     if (!d) XUnmapWindow(ob_display, self->desk);
474     if (!s) XUnmapWindow(ob_display, self->shade);
475     if (!i) XUnmapWindow(ob_display, self->iconify);
476     if (!l) XUnmapWindow(ob_display, self->label);
477     if (!m) XUnmapWindow(ob_display, self->max);
478     if (!c) XUnmapWindow(ob_display, self->close);
479
480     x = theme_bevel + 1;
481     for (lc = theme_title_layout; *lc != '\0'; ++lc) {
482         switch (*lc) {
483         case 'N':
484             if (!n) break;
485             self->icon_x = x;
486             XMapWindow(ob_display, self->icon);
487             XMoveWindow(ob_display, self->icon, x, theme_bevel);
488             x += theme_button_size + 2 + theme_bevel + 1;
489             break;
490         case 'D':
491             if (!d) break;
492             self->desk_x = x;
493             XMapWindow(ob_display, self->desk);
494             XMoveWindow(ob_display, self->desk, x, theme_bevel + 1);
495             x += theme_button_size + theme_bevel + 1;
496             break;
497         case 'S':
498             if (!s) break;
499             self->shade_x = x;
500             XMapWindow(ob_display, self->shade);
501             XMoveWindow(ob_display, self->shade, x, theme_bevel + 1);
502             x += theme_button_size + theme_bevel + 1;
503             break;
504         case 'I':
505             if (!i) break;
506             self->iconify_x = x;
507             XMapWindow(ob_display, self->iconify);
508             XMoveWindow(ob_display, self->iconify, x, theme_bevel + 1);
509             x += theme_button_size + theme_bevel + 1;
510             break;
511         case 'L':
512             if (!l) break;
513             self->label_x = x;
514             XMapWindow(ob_display, self->label);
515             XMoveWindow(ob_display, self->label, x, theme_bevel);
516             x += self->label_width + theme_bevel + 1;
517             break;
518         case 'M':
519             if (!m) break;
520             self->max_x = x;
521             XMapWindow(ob_display, self->max);
522             XMoveWindow(ob_display, self->max, x, theme_bevel + 1);
523             x += theme_button_size + theme_bevel + 1;
524             break;
525         case 'C':
526             if (!c) break;
527             self->close_x = x;
528             XMapWindow(ob_display, self->close);
529             XMoveWindow(ob_display, self->close, x, theme_bevel + 1);
530             x += theme_button_size + theme_bevel + 1;
531             break;
532         }
533     }
534 }
535
536 Context frame_context_from_string(char *name)
537 {
538     if (!g_ascii_strcasecmp("root", name))
539         return Context_Root;
540     else if (!g_ascii_strcasecmp("client", name))
541         return Context_Client;
542     else if (!g_ascii_strcasecmp("titlebar", name))
543         return Context_Titlebar;
544     else if (!g_ascii_strcasecmp("handle", name))
545         return Context_Handle;
546     else if (!g_ascii_strcasecmp("frame", name))
547         return Context_Frame;
548     else if (!g_ascii_strcasecmp("blcorner", name))
549         return Context_BLCorner;
550     else if (!g_ascii_strcasecmp("tlcorner", name))
551         return Context_TLCorner;
552     else if (!g_ascii_strcasecmp("brcorner", name))
553         return Context_BRCorner;
554     else if (!g_ascii_strcasecmp("trcorner", name))
555         return Context_TRCorner;
556     else if (!g_ascii_strcasecmp("maximize", name))
557         return Context_Maximize;
558     else if (!g_ascii_strcasecmp("alldesktops", name))
559         return Context_AllDesktops;
560     else if (!g_ascii_strcasecmp("shade", name))
561         return Context_Shade;
562     else if (!g_ascii_strcasecmp("iconify", name))
563         return Context_Iconify;
564     else if (!g_ascii_strcasecmp("icon", name))
565         return Context_Icon;
566     else if (!g_ascii_strcasecmp("close", name))
567         return Context_Close;
568     return Context_None;
569 }
570
571 Context frame_context(Client *client, Window win)
572 {
573     Frame *self;
574
575     if (win == ob_root) return Context_Root;
576     if (client == NULL) return Context_None;
577     if (win == client->window) return Context_Client;
578
579     self = client->frame;
580     if (win == self->window) return Context_Frame;
581     if (win == self->plate)  return Context_Client;
582     if (win == self->title)  return Context_Titlebar;
583     if (win == self->label)  return Context_Titlebar;
584     if (win == self->handle) return Context_Handle;
585     if (win == self->lgrip)  return Context_BLCorner;
586     if (win == self->rgrip)  return Context_BRCorner;
587     if (win == self->max)    return Context_Maximize;
588     if (win == self->iconify)return Context_Iconify;
589     if (win == self->close)  return Context_Close;
590     if (win == self->icon)   return Context_Icon;
591     if (win == self->desk)   return Context_AllDesktops;
592     if (win == self->shade)  return Context_Shade;
593
594     return Context_None;
595 }
596
597 void frame_client_gravity(Frame *self, int *x, int *y)
598 {
599     /* horizontal */
600     switch (self->client->gravity) {
601     default:
602     case NorthWestGravity:
603     case SouthWestGravity:
604     case WestGravity:
605         break;
606
607     case NorthGravity:
608     case SouthGravity:
609     case CenterGravity:
610         *x -= (self->size.left + self->size.right) / 2;
611         break;
612
613     case NorthEastGravity:
614     case SouthEastGravity:
615     case EastGravity:
616         *x -= self->size.left + self->size.right;
617         break;
618
619     case ForgetGravity:
620     case StaticGravity:
621         *x -= self->size.left;
622         break;
623     }
624
625     /* vertical */
626     switch (self->client->gravity) {
627     default:
628     case NorthWestGravity:
629     case NorthEastGravity:
630     case NorthGravity:
631         break;
632
633     case CenterGravity:
634     case EastGravity:
635     case WestGravity:
636         *y -= (self->size.top + self->size.bottom) / 2;
637         break;
638
639     case SouthWestGravity:
640     case SouthEastGravity:
641     case SouthGravity:
642         *y -= self->size.top + self->size.bottom;
643         break;
644
645     case ForgetGravity:
646     case StaticGravity:
647         *y -= self->size.top;
648         break;
649     }
650 }
651
652 void frame_frame_gravity(Frame *self, int *x, int *y)
653 {
654     /* horizontal */
655     switch (self->client->gravity) {
656     default:
657     case NorthWestGravity:
658     case WestGravity:
659     case SouthWestGravity:
660         break;
661     case NorthGravity:
662     case CenterGravity:
663     case SouthGravity:
664         *x += (self->size.left + self->size.right) / 2;
665         break;
666     case NorthEastGravity:
667     case EastGravity:
668     case SouthEastGravity:
669         *x += self->size.left + self->size.right;
670         break;
671     case StaticGravity:
672     case ForgetGravity:
673         *x += self->size.left;
674         break;
675     }
676
677     /* vertical */
678     switch (self->client->gravity) {
679     default:
680     case NorthWestGravity:
681     case WestGravity:
682     case SouthWestGravity:
683         break;
684     case NorthGravity:
685     case CenterGravity:
686     case SouthGravity:
687         *y += (self->size.top + self->size.bottom) / 2;
688         break;
689     case NorthEastGravity:
690     case EastGravity:
691     case SouthEastGravity:
692         *y += self->size.top + self->size.bottom;
693         break;
694     case StaticGravity:
695     case ForgetGravity:
696         *y += self->size.top;
697         break;
698     }
699 }