1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /*
4 * Copyright (C) 2001 Havoc Pennington
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "config.h"
21
22 #include "ui/theme-private.h"
23
24 #include <gtk/gtk.h>
25 #include <math.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "core/util-private.h"
31 #include "meta/prefs.h"
32 #include "ui/frames.h"
33
34 static void scale_border (GtkBorder *border, double factor);
35
36 static MetaFrameLayout *
meta_frame_layout_new(void)37 meta_frame_layout_new (void)
38 {
39 MetaFrameLayout *layout;
40
41 layout = g_new0 (MetaFrameLayout, 1);
42
43 /* Spacing as hardcoded in GTK+:
44 * https://git.gnome.org/browse/gtk+/tree/gtk/gtkheaderbar.c?h=gtk-3-14#n53
45 */
46 layout->titlebar_spacing = 6;
47 layout->has_title = TRUE;
48 layout->title_scale = PANGO_SCALE_MEDIUM;
49 layout->icon_size = META_MINI_ICON_WIDTH;
50
51 return layout;
52 }
53
54 static void
meta_frame_layout_free(MetaFrameLayout * layout)55 meta_frame_layout_free (MetaFrameLayout *layout)
56 {
57 g_return_if_fail (layout != NULL);
58
59 g_free (layout);
60 }
61
62 static void
meta_frame_layout_get_borders(const MetaFrameLayout * layout,int text_height,MetaFrameFlags flags,MetaFrameType type,MetaFrameBorders * borders)63 meta_frame_layout_get_borders (const MetaFrameLayout *layout,
64 int text_height,
65 MetaFrameFlags flags,
66 MetaFrameType type,
67 MetaFrameBorders *borders)
68 {
69 int buttons_height, content_height, draggable_borders;
70 int scale = meta_theme_get_window_scaling_factor ();
71
72 meta_frame_borders_clear (borders);
73
74 /* For a full-screen window, we don't have any borders, visible or not. */
75 if (flags & META_FRAME_FULLSCREEN)
76 return;
77
78 g_return_if_fail (layout != NULL);
79
80 if (!layout->has_title)
81 text_height = 0;
82 else
83 text_height = layout->title_margin.top + text_height + layout->title_margin.bottom;
84
85 buttons_height = MAX ((int)layout->icon_size, layout->button_min_size.height) +
86 layout->button_margin.top + layout->button_border.top +
87 layout->button_margin.bottom + layout->button_border.bottom;
88 content_height = MAX (buttons_height, text_height);
89 content_height = MAX (content_height, layout->titlebar_min_size.height) +
90 layout->titlebar_border.top + layout->titlebar_border.bottom;
91
92 borders->visible.top = layout->frame_border.top + content_height;
93 borders->visible.left = layout->frame_border.left;
94 borders->visible.right = layout->frame_border.right;
95 borders->visible.bottom = layout->frame_border.bottom;
96
97 borders->invisible = layout->invisible_border;
98
99 draggable_borders = meta_prefs_get_draggable_border_width ();
100
101 if (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE)
102 {
103 borders->invisible.left = MAX (borders->invisible.left,
104 draggable_borders - borders->visible.left);
105 borders->invisible.right = MAX (borders->invisible.right,
106 draggable_borders - borders->visible.right);
107 }
108
109 if (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE)
110 {
111 borders->invisible.bottom = MAX (borders->invisible.bottom,
112 draggable_borders - borders->visible.bottom);
113
114 /* borders.visible.top is the height of the *title bar*. We can't do the same
115 * algorithm here, titlebars are expectedly much bigger. Just subtract a couple
116 * pixels to get a proper feel. */
117 if (type != META_FRAME_TYPE_ATTACHED)
118 borders->invisible.top = MAX (borders->invisible.top, draggable_borders - 2);
119 }
120
121 borders->total.left = borders->invisible.left + borders->visible.left;
122 borders->total.right = borders->invisible.right + borders->visible.right;
123 borders->total.bottom = borders->invisible.bottom + borders->visible.bottom;
124 borders->total.top = borders->invisible.top + borders->visible.top;
125
126 /* Scale geometry for HiDPI, see comment in meta_frame_layout_draw_with_style() */
127 scale_border (&borders->visible, scale);
128 scale_border (&borders->invisible, scale);
129 scale_border (&borders->total, scale);
130 }
131
132 int
meta_theme_get_window_scaling_factor(void)133 meta_theme_get_window_scaling_factor (void)
134 {
135 GdkScreen *screen;
136 GValue value = G_VALUE_INIT;
137
138 g_value_init (&value, G_TYPE_INT);
139
140 screen = gdk_screen_get_default ();
141 if (gdk_screen_get_setting (screen, "gdk-window-scaling-factor", &value))
142 return g_value_get_int (&value);
143 else
144 return 1;
145 }
146
147 void
meta_frame_layout_apply_scale(const MetaFrameLayout * layout,PangoFontDescription * font_desc)148 meta_frame_layout_apply_scale (const MetaFrameLayout *layout,
149 PangoFontDescription *font_desc)
150 {
151 int size = pango_font_description_get_size (font_desc);
152 double scale = layout->title_scale / meta_theme_get_window_scaling_factor ();
153 pango_font_description_set_size (font_desc, MAX (size * scale, 1));
154 }
155
156 static MetaButtonSpace*
rect_for_function(MetaFrameGeometry * fgeom,MetaFrameFlags flags,MetaButtonFunction function,MetaTheme * theme)157 rect_for_function (MetaFrameGeometry *fgeom,
158 MetaFrameFlags flags,
159 MetaButtonFunction function,
160 MetaTheme *theme)
161 {
162 switch (function)
163 {
164 case META_BUTTON_FUNCTION_MENU:
165 if (flags & META_FRAME_ALLOWS_MENU)
166 return &fgeom->menu_rect;
167 else
168 return NULL;
169 case META_BUTTON_FUNCTION_MINIMIZE:
170 if (flags & META_FRAME_ALLOWS_MINIMIZE)
171 return &fgeom->min_rect;
172 else
173 return NULL;
174 case META_BUTTON_FUNCTION_MAXIMIZE:
175 if (flags & META_FRAME_ALLOWS_MAXIMIZE)
176 return &fgeom->max_rect;
177 else
178 return NULL;
179 case META_BUTTON_FUNCTION_CLOSE:
180 if (flags & META_FRAME_ALLOWS_DELETE)
181 return &fgeom->close_rect;
182 else
183 return NULL;
184
185 case META_BUTTON_FUNCTION_LAST:
186 return NULL;
187 }
188
189 return NULL;
190 }
191
192 static gboolean
strip_button(MetaButtonSpace * func_rects[MAX_BUTTONS_PER_CORNER],int * n_rects,MetaButtonSpace * to_strip)193 strip_button (MetaButtonSpace *func_rects[MAX_BUTTONS_PER_CORNER],
194 int *n_rects,
195 MetaButtonSpace *to_strip)
196 {
197 int i;
198
199 i = 0;
200 while (i < *n_rects)
201 {
202 if (func_rects[i] == to_strip)
203 {
204 *n_rects -= 1;
205
206 /* shift the other rects back in the array */
207 while (i < *n_rects)
208 {
209 func_rects[i] = func_rects[i+1];
210
211 ++i;
212 }
213
214 func_rects[i] = NULL;
215
216 return TRUE;
217 }
218
219 ++i;
220 }
221
222 return FALSE; /* did not strip anything */
223 }
224
225 static void
get_padding_and_border(GtkStyleContext * style,GtkBorder * border)226 get_padding_and_border (GtkStyleContext *style,
227 GtkBorder *border)
228 {
229 GtkBorder tmp;
230 GtkStateFlags state = gtk_style_context_get_state (style);
231
232 gtk_style_context_get_border (style, state, border);
233 gtk_style_context_get_padding (style, state, &tmp);
234
235 border->left += tmp.left;
236 border->top += tmp.top;
237 border->right += tmp.right;
238 border->bottom += tmp.bottom;
239 }
240
241 static void
get_min_size(GtkStyleContext * style,GtkRequisition * requisition)242 get_min_size (GtkStyleContext *style,
243 GtkRequisition *requisition)
244 {
245 gtk_style_context_get (style, gtk_style_context_get_state (style),
246 "min-width", &requisition->width,
247 "min-height", &requisition->height,
248 NULL);
249 }
250
251 static void
scale_border(GtkBorder * border,double factor)252 scale_border (GtkBorder *border,
253 double factor)
254 {
255 border->left *= factor;
256 border->right *= factor;
257 border->top *= factor;
258 border->bottom *= factor;
259 }
260
261 static void
meta_frame_layout_sync_with_style(MetaFrameLayout * layout,MetaStyleInfo * style_info,MetaFrameFlags flags)262 meta_frame_layout_sync_with_style (MetaFrameLayout *layout,
263 MetaStyleInfo *style_info,
264 MetaFrameFlags flags)
265 {
266 GtkStyleContext *style;
267 GtkBorder border;
268 GtkRequisition requisition;
269 GdkRectangle clip_rect;
270 int border_radius, max_radius;
271
272 meta_style_info_set_flags (style_info, flags);
273
274 style = style_info->styles[META_STYLE_ELEMENT_FRAME];
275 get_padding_and_border (style, &layout->frame_border);
276 scale_border (&layout->frame_border, layout->title_scale);
277
278 gtk_render_background_get_clip (style, 0, 0, 0, 0, &clip_rect);
279 layout->invisible_border.left = -clip_rect.x;
280 layout->invisible_border.right = clip_rect.width + clip_rect.x;
281 layout->invisible_border.top = -clip_rect.y;
282 layout->invisible_border.bottom = clip_rect.height + clip_rect.y;
283
284 if (layout->hide_buttons)
285 layout->icon_size = 0;
286
287 if (!layout->has_title && layout->hide_buttons)
288 return; /* border-only - be done */
289
290 style = style_info->styles[META_STYLE_ELEMENT_TITLEBAR];
291 gtk_style_context_get (style, gtk_style_context_get_state (style),
292 "border-radius", &border_radius,
293 NULL);
294 /* GTK+ currently does not allow us to look up radii of individual
295 * corners; however we don't clip the client area, so with the
296 * current trend of using small/no visible frame borders, most
297 * themes should work fine with this.
298 */
299 layout->top_left_corner_rounded_radius = border_radius;
300 layout->top_right_corner_rounded_radius = border_radius;
301 max_radius = MIN (layout->frame_border.bottom, layout->frame_border.left);
302 layout->bottom_left_corner_rounded_radius = MAX (border_radius, max_radius);
303 max_radius = MIN (layout->frame_border.bottom, layout->frame_border.right);
304 layout->bottom_right_corner_rounded_radius = MAX (border_radius, max_radius);
305
306 get_min_size (style, &layout->titlebar_min_size);
307 get_padding_and_border (style, &layout->titlebar_border);
308 scale_border (&layout->titlebar_border, layout->title_scale);
309
310 style = style_info->styles[META_STYLE_ELEMENT_TITLE];
311 gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
312 &layout->title_margin);
313 scale_border (&layout->title_margin, layout->title_scale);
314
315 style = style_info->styles[META_STYLE_ELEMENT_BUTTON];
316 get_min_size (style, &layout->button_min_size);
317 get_padding_and_border (style, &layout->button_border);
318 scale_border (&layout->button_border, layout->title_scale);
319 gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
320 &layout->button_margin);
321 scale_border (&layout->button_margin, layout->title_scale);
322
323 style = style_info->styles[META_STYLE_ELEMENT_IMAGE];
324 get_min_size (style, &requisition);
325 get_padding_and_border (style, &border);
326 scale_border (&border, layout->title_scale);
327
328 layout->button_border.left += border.left;
329 layout->button_border.right += border.right;
330 layout->button_border.top += border.top;
331 layout->button_border.bottom += border.bottom;
332
333 gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
334 &border);
335 layout->button_border.left += border.left;
336 layout->button_border.right += border.right;
337 layout->button_border.top += border.top;
338 layout->button_border.bottom += border.bottom;
339
340 layout->button_min_size.width = MAX(layout->button_min_size.width,
341 requisition.width);
342 layout->button_min_size.height = MAX(layout->button_min_size.height,
343 requisition.height);
344 }
345
346 static void
meta_frame_layout_calc_geometry(MetaFrameLayout * layout,MetaStyleInfo * style_info,int text_height,MetaFrameFlags flags,int client_width,int client_height,const MetaButtonLayout * button_layout,MetaFrameType type,MetaFrameGeometry * fgeom,MetaTheme * theme)347 meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
348 MetaStyleInfo *style_info,
349 int text_height,
350 MetaFrameFlags flags,
351 int client_width,
352 int client_height,
353 const MetaButtonLayout *button_layout,
354 MetaFrameType type,
355 MetaFrameGeometry *fgeom,
356 MetaTheme *theme)
357 {
358 int i, n_left, n_right, n_left_spacers, n_right_spacers;
359 int x;
360 int button_y;
361 int title_right_edge;
362 int width, height;
363 int content_width, content_height;
364 int button_width, button_height;
365 int min_size_for_rounding;
366 int scale = meta_theme_get_window_scaling_factor ();
367
368 /* the left/right rects in order; the max # of rects
369 * is the number of button functions
370 */
371 MetaButtonSpace *left_func_rects[MAX_BUTTONS_PER_CORNER];
372 MetaButtonSpace *right_func_rects[MAX_BUTTONS_PER_CORNER];
373 gboolean left_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
374 gboolean right_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
375
376 MetaFrameBorders borders;
377
378 meta_frame_layout_sync_with_style (layout, style_info, flags);
379
380 meta_frame_layout_get_borders (layout, text_height,
381 flags, type,
382 &borders);
383
384 fgeom->borders = borders;
385
386 /* Scale geometry for HiDPI, see comment in meta_frame_layout_draw_with_style() */
387 fgeom->content_border = layout->frame_border;
388 fgeom->content_border.left += layout->titlebar_border.left * scale;
389 fgeom->content_border.right += layout->titlebar_border.right * scale;
390 fgeom->content_border.top += layout->titlebar_border.top * scale;
391 fgeom->content_border.bottom += layout->titlebar_border.bottom * scale;
392
393 width = client_width + borders.total.left + borders.total.right;
394
395 height = borders.total.top + borders.total.bottom;
396 if (!(flags & META_FRAME_SHADED))
397 height += client_height;
398
399 fgeom->width = width;
400 fgeom->height = height;
401
402 content_width = width -
403 (fgeom->content_border.left + borders.invisible.left) -
404 (fgeom->content_border.right + borders.invisible.right);
405 content_height = borders.visible.top - fgeom->content_border.top - fgeom->content_border.bottom;
406
407 button_width = MAX ((int)layout->icon_size, layout->button_min_size.width) +
408 layout->button_border.left + layout->button_border.right;
409 button_height = MAX ((int)layout->icon_size, layout->button_min_size.height) +
410 layout->button_border.top + layout->button_border.bottom;
411 button_width *= scale;
412 button_height *= scale;
413
414 /* FIXME all this code sort of pretends that duplicate buttons
415 * with the same function are allowed, but that breaks the
416 * code in frames.c, so isn't really allowed right now.
417 * Would need left_close_rect, right_close_rect, etc.
418 */
419
420 /* Init all button rects to 0, lame hack */
421 memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0',
422 LENGTH_OF_BUTTON_RECTS);
423
424 n_left = 0;
425 n_right = 0;
426 n_left_spacers = 0;
427 n_right_spacers = 0;
428
429 if (!layout->hide_buttons)
430 {
431 /* Try to fill in rects */
432 for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
433 {
434 left_func_rects[n_left] = rect_for_function (fgeom, flags,
435 button_layout->left_buttons[i],
436 theme);
437 if (left_func_rects[n_left] != NULL)
438 {
439 left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i];
440 if (button_layout->left_buttons_has_spacer[i])
441 ++n_left_spacers;
442
443 ++n_left;
444 }
445 }
446
447 for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
448 {
449 right_func_rects[n_right] = rect_for_function (fgeom, flags,
450 button_layout->right_buttons[i],
451 theme);
452 if (right_func_rects[n_right] != NULL)
453 {
454 right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i];
455 if (button_layout->right_buttons_has_spacer[i])
456 ++n_right_spacers;
457
458 ++n_right;
459 }
460 }
461 }
462
463 /* Be sure buttons fit */
464 while (n_left > 0 || n_right > 0)
465 {
466 int space_used_by_buttons;
467
468 space_used_by_buttons = 0;
469
470 space_used_by_buttons += layout->button_margin.left * scale * n_left;
471 space_used_by_buttons += button_width * n_left;
472 space_used_by_buttons += layout->button_margin.right * scale * n_left;
473 space_used_by_buttons += (button_width * 0.75) * n_left_spacers;
474 space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_left - 1, 0);
475
476 space_used_by_buttons += layout->button_margin.left * scale * n_right;
477 space_used_by_buttons += button_width * n_right;
478 space_used_by_buttons += layout->button_margin.right * scale * n_right;
479 space_used_by_buttons += (button_width * 0.75) * n_right_spacers;
480 space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_right - 1, 0);
481
482 if (space_used_by_buttons <= content_width)
483 break; /* Everything fits, bail out */
484
485 /* First try to remove separators */
486 if (n_left_spacers > 0)
487 {
488 left_buttons_has_spacer[--n_left_spacers] = FALSE;
489 continue;
490 }
491 else if (n_right_spacers > 0)
492 {
493 right_buttons_has_spacer[--n_right_spacers] = FALSE;
494 continue;
495 }
496
497 /* Otherwise we need to shave out a button. Shave
498 * min, max, close, then menu (menu is most useful);
499 * prefer the default button locations.
500 */
501 if (strip_button (left_func_rects, &n_left, &fgeom->min_rect))
502 continue;
503 else if (strip_button (right_func_rects, &n_right, &fgeom->min_rect))
504 continue;
505 else if (strip_button (left_func_rects, &n_left, &fgeom->max_rect))
506 continue;
507 else if (strip_button (right_func_rects, &n_right, &fgeom->max_rect))
508 continue;
509 else if (strip_button (left_func_rects, &n_left, &fgeom->close_rect))
510 continue;
511 else if (strip_button (right_func_rects, &n_right, &fgeom->close_rect))
512 continue;
513 else if (strip_button (right_func_rects, &n_right, &fgeom->menu_rect))
514 continue;
515 else if (strip_button (left_func_rects, &n_left, &fgeom->menu_rect))
516 continue;
517 else
518 {
519 meta_bug ("Could not find a button to strip. n_left = %d n_right = %d",
520 n_left, n_right);
521 }
522 }
523
524 /* Save the button layout */
525 fgeom->button_layout = *button_layout;
526 fgeom->n_left_buttons = n_left;
527 fgeom->n_right_buttons = n_right;
528
529 /* center buttons vertically */
530 button_y = fgeom->content_border.top + borders.invisible.top +
531 (content_height - button_height) / 2;
532
533 /* right edge of farthest-right button */
534 x = width - fgeom->content_border.right - borders.invisible.right;
535
536 i = n_right - 1;
537 while (i >= 0)
538 {
539 MetaButtonSpace *rect;
540
541 if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */
542 break;
543
544 x -= layout->button_margin.right * scale;
545
546 rect = right_func_rects[i];
547 rect->visible.x = x - button_width;
548 if (right_buttons_has_spacer[i])
549 rect->visible.x -= (button_width * 0.75);
550
551 rect->visible.y = button_y;
552 rect->visible.width = button_width;
553 rect->visible.height = button_height;
554
555 if (flags & META_FRAME_MAXIMIZED ||
556 flags & META_FRAME_TILED_LEFT ||
557 flags & META_FRAME_TILED_RIGHT)
558 {
559 rect->clickable.x = rect->visible.x;
560 rect->clickable.y = 0;
561 rect->clickable.width = rect->visible.width;
562 rect->clickable.height = button_height + button_y;
563
564 if (i == n_right - 1)
565 rect->clickable.width += fgeom->content_border.right;
566
567 }
568 else
569 memmove (&(rect->clickable), &(rect->visible), sizeof (rect->clickable));
570
571 x = rect->visible.x - layout->button_margin.left * scale;
572
573 if (i > 0)
574 x -= layout->titlebar_spacing * scale;
575
576 --i;
577 }
578
579 /* save right edge of titlebar for later use */
580 title_right_edge = x;
581
582 /* Now x changes to be position from the left and we go through
583 * the left-side buttons
584 */
585 x = fgeom->content_border.left + borders.invisible.left;
586 for (i = 0; i < n_left; i++)
587 {
588 MetaButtonSpace *rect;
589
590 x += layout->button_margin.left * scale;
591
592 rect = left_func_rects[i];
593
594 rect->visible.x = x;
595 rect->visible.y = button_y;
596 rect->visible.width = button_width;
597 rect->visible.height = button_height;
598
599 if (flags & META_FRAME_MAXIMIZED)
600 {
601 if (i==0)
602 {
603 rect->clickable.x = 0;
604 rect->clickable.width = button_width + x;
605 }
606 else
607 {
608 rect->clickable.x = rect->visible.x;
609 rect->clickable.width = button_width;
610 }
611
612 rect->clickable.y = 0;
613 rect->clickable.height = button_height + button_y;
614 }
615 else
616 memmove (&(rect->clickable), &(rect->visible), sizeof (rect->clickable));
617
618 x = rect->visible.x + rect->visible.width + layout->button_margin.right * scale;
619 if (i < n_left - 1)
620 x += layout->titlebar_spacing * scale;
621 if (left_buttons_has_spacer[i])
622 x += (button_width * 0.75);
623 }
624
625 /* Center vertically in the available content area */
626 fgeom->title_rect.x = x;
627 fgeom->title_rect.y = fgeom->content_border.top + borders.invisible.top +
628 (content_height - text_height) / 2;
629 fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x;
630 fgeom->title_rect.height = text_height;
631
632 /* Nuke title if it won't fit */
633 if (fgeom->title_rect.width < 0 ||
634 fgeom->title_rect.height < 0)
635 {
636 fgeom->title_rect.width = 0;
637 fgeom->title_rect.height = 0;
638 }
639
640 if (flags & META_FRAME_SHADED)
641 min_size_for_rounding = 0;
642 else
643 min_size_for_rounding = 5 * scale;
644
645 fgeom->top_left_corner_rounded_radius = 0;
646 fgeom->top_right_corner_rounded_radius = 0;
647 fgeom->bottom_left_corner_rounded_radius = 0;
648 fgeom->bottom_right_corner_rounded_radius = 0;
649
650 if (borders.visible.top + borders.visible.left >= min_size_for_rounding)
651 fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius * scale;
652 if (borders.visible.top + borders.visible.right >= min_size_for_rounding)
653 fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius * scale;
654
655 if (borders.visible.bottom + borders.visible.left >= min_size_for_rounding)
656 fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius * scale;
657 if (borders.visible.bottom + borders.visible.right >= min_size_for_rounding)
658 fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius * scale;
659 }
660
661 static void
get_button_rect(MetaButtonType type,const MetaFrameGeometry * fgeom,GdkRectangle * rect)662 get_button_rect (MetaButtonType type,
663 const MetaFrameGeometry *fgeom,
664 GdkRectangle *rect)
665 {
666 switch (type)
667 {
668 case META_BUTTON_TYPE_CLOSE:
669 *rect = fgeom->close_rect.visible;
670 break;
671
672 case META_BUTTON_TYPE_MAXIMIZE:
673 *rect = fgeom->max_rect.visible;
674 break;
675
676 case META_BUTTON_TYPE_MINIMIZE:
677 *rect = fgeom->min_rect.visible;
678 break;
679
680 case META_BUTTON_TYPE_MENU:
681 *rect = fgeom->menu_rect.visible;
682 break;
683
684 default:
685 case META_BUTTON_TYPE_LAST:
686 g_assert_not_reached ();
687 break;
688 }
689 }
690
691 static const char *
get_class_from_button_type(MetaButtonType type)692 get_class_from_button_type (MetaButtonType type)
693 {
694 switch (type)
695 {
696 case META_BUTTON_TYPE_CLOSE:
697 return "close";
698 case META_BUTTON_TYPE_MAXIMIZE:
699 return "maximize";
700 case META_BUTTON_TYPE_MINIMIZE:
701 return "minimize";
702 default:
703 return NULL;
704 }
705 }
706
707 static void
meta_frame_layout_draw_with_style(MetaFrameLayout * layout,MetaStyleInfo * style_info,cairo_t * cr,const MetaFrameGeometry * fgeom,PangoLayout * title_layout,MetaFrameFlags flags,MetaButtonState button_states[META_BUTTON_TYPE_LAST],cairo_surface_t * mini_icon)708 meta_frame_layout_draw_with_style (MetaFrameLayout *layout,
709 MetaStyleInfo *style_info,
710 cairo_t *cr,
711 const MetaFrameGeometry *fgeom,
712 PangoLayout *title_layout,
713 MetaFrameFlags flags,
714 MetaButtonState button_states[META_BUTTON_TYPE_LAST],
715 cairo_surface_t *mini_icon)
716 {
717 GtkStyleContext *style;
718 GtkStateFlags state;
719 MetaButtonType button_type;
720 GdkRectangle visible_rect;
721 GdkRectangle titlebar_rect;
722 GdkRectangle button_rect;
723 const MetaFrameBorders *borders;
724 cairo_surface_t *frame_surface;
725 double xscale, yscale;
726 int scale;
727
728 /* We opt out of GTK+/Clutter's HiDPI handling, so we have to do the scaling
729 * ourselves; the nitty-gritty is a bit confusing, so here is an overview:
730 * - the values in MetaFrameLayout are always as they appear in the theme,
731 * i.e. unscaled
732 * - calculated values (borders, MetaFrameGeometry) include the scale - as
733 * the geometry is comprised of scaled decorations and the client size
734 * which we must not scale, we don't have another option
735 * - for drawing, we scale the canvas to have GTK+ render elements (borders,
736 * radii, ...) at the correct scale - as a result, we have to "unscale"
737 * the geometry again to not apply the scaling twice
738 * - As per commit e36b629c GTK expects the device scale to be set and match
739 * the final scaling or the surface caching won't take this in account
740 * breaking -gtk-scaled items.
741 */
742 scale = meta_theme_get_window_scaling_factor ();
743 frame_surface = cairo_get_target (cr);
744 cairo_surface_get_device_scale (frame_surface, &xscale, &yscale);
745 cairo_surface_set_device_scale (frame_surface, scale, scale);
746
747 borders = &fgeom->borders;
748
749 visible_rect.x = borders->invisible.left / scale;
750 visible_rect.y = borders->invisible.top / scale;
751 visible_rect.width = (fgeom->width - borders->invisible.left - borders->invisible.right) / scale;
752 visible_rect.height = (fgeom->height - borders->invisible.top - borders->invisible.bottom) / scale;
753
754 meta_style_info_set_flags (style_info, flags);
755
756 style = style_info->styles[META_STYLE_ELEMENT_FRAME];
757 gtk_render_background (style, cr,
758 visible_rect.x, visible_rect.y,
759 visible_rect.width, visible_rect.height);
760 gtk_render_frame (style, cr,
761 visible_rect.x, visible_rect.y,
762 visible_rect.width, visible_rect.height);
763
764 titlebar_rect.x = visible_rect.x;
765 titlebar_rect.y = visible_rect.y;
766 titlebar_rect.width = visible_rect.width;
767 titlebar_rect.height = borders->visible.top / scale;
768
769 style = style_info->styles[META_STYLE_ELEMENT_TITLEBAR];
770 gtk_render_background (style, cr,
771 titlebar_rect.x, titlebar_rect.y,
772 titlebar_rect.width, titlebar_rect.height);
773 gtk_render_frame (style, cr,
774 titlebar_rect.x, titlebar_rect.y,
775 titlebar_rect.width, titlebar_rect.height);
776
777 if (layout->has_title && title_layout)
778 {
779 PangoRectangle logical;
780 int text_width, x, y;
781
782 pango_layout_set_width (title_layout, -1);
783 pango_layout_get_pixel_extents (title_layout, NULL, &logical);
784
785 text_width = MIN(fgeom->title_rect.width / scale, logical.width);
786
787 if (text_width < logical.width)
788 pango_layout_set_width (title_layout, PANGO_SCALE * text_width);
789
790 /* Center within the frame if possible */
791 x = titlebar_rect.x + (titlebar_rect.width - text_width) / 2;
792 y = titlebar_rect.y + (titlebar_rect.height - logical.height) / 2;
793
794 if (x < fgeom->title_rect.x / scale)
795 x = fgeom->title_rect.x / scale;
796 else if (x + text_width > (fgeom->title_rect.x + fgeom->title_rect.width) / scale)
797 x = (fgeom->title_rect.x + fgeom->title_rect.width) / scale - text_width;
798
799 style = style_info->styles[META_STYLE_ELEMENT_TITLE];
800 gtk_render_layout (style, cr, x, y, title_layout);
801 }
802
803 style = style_info->styles[META_STYLE_ELEMENT_BUTTON];
804 state = gtk_style_context_get_state (style);
805 for (button_type = META_BUTTON_TYPE_CLOSE; button_type < META_BUTTON_TYPE_LAST; button_type++)
806 {
807 const char *button_class = get_class_from_button_type (button_type);
808 if (button_class)
809 gtk_style_context_add_class (style, button_class);
810
811 get_button_rect (button_type, fgeom, &button_rect);
812
813 button_rect.x /= scale;
814 button_rect.y /= scale;
815 button_rect.width /= scale;
816 button_rect.height /= scale;
817
818 if (button_states[button_type] == META_BUTTON_STATE_PRELIGHT)
819 gtk_style_context_set_state (style, state | GTK_STATE_PRELIGHT);
820 else if (button_states[button_type] == META_BUTTON_STATE_PRESSED)
821 gtk_style_context_set_state (style, state | GTK_STATE_ACTIVE);
822 else
823 gtk_style_context_set_state (style, state);
824
825 cairo_save (cr);
826
827 if (button_rect.width > 0 && button_rect.height > 0)
828 {
829 cairo_surface_t *surface = NULL;
830 const char *icon_name = NULL;
831
832 gtk_render_background (style, cr,
833 button_rect.x, button_rect.y,
834 button_rect.width, button_rect.height);
835 gtk_render_frame (style, cr,
836 button_rect.x, button_rect.y,
837 button_rect.width, button_rect.height);
838
839 switch (button_type)
840 {
841 case META_BUTTON_TYPE_CLOSE:
842 icon_name = "window-close-symbolic";
843 break;
844 case META_BUTTON_TYPE_MAXIMIZE:
845 if (flags & META_FRAME_MAXIMIZED)
846 icon_name = "window-restore-symbolic";
847 else
848 icon_name = "window-maximize-symbolic";
849 break;
850 case META_BUTTON_TYPE_MINIMIZE:
851 icon_name = "window-minimize-symbolic";
852 break;
853 case META_BUTTON_TYPE_MENU:
854 icon_name = "open-menu-symbolic";
855 break;
856 default:
857 icon_name = NULL;
858 break;
859 }
860
861 if (icon_name)
862 {
863 GtkIconTheme *theme = gtk_icon_theme_get_default ();
864 g_autoptr (GtkIconInfo) info = NULL;
865 g_autoptr (GdkPixbuf) pixbuf = NULL;
866
867 info = gtk_icon_theme_lookup_icon_for_scale (theme, icon_name,
868 layout->icon_size, scale, 0);
869 pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, NULL, NULL);
870 surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
871 }
872
873 if (surface)
874 {
875 float width, height;
876 int x, y;
877
878 width = cairo_image_surface_get_width (surface) / scale;
879 height = cairo_image_surface_get_height (surface) / scale;
880 x = button_rect.x + (button_rect.width - layout->icon_size) / 2;
881 y = button_rect.y + (button_rect.height - layout->icon_size) / 2;
882
883 cairo_translate (cr, x, y);
884 cairo_scale (cr,
885 layout->icon_size / width,
886 layout->icon_size / height);
887 cairo_set_source_surface (cr, surface, 0, 0);
888 cairo_paint (cr);
889
890 cairo_surface_destroy (surface);
891 }
892 }
893 cairo_restore (cr);
894 if (button_class)
895 gtk_style_context_remove_class (style, button_class);
896 gtk_style_context_set_state (style, state);
897 }
898
899 cairo_surface_set_device_scale (frame_surface, xscale, yscale);
900 }
901
902 /**
903 * meta_theme_get_default: (skip)
904 *
905 */
906 MetaTheme*
meta_theme_get_default(void)907 meta_theme_get_default (void)
908 {
909 static MetaTheme *theme = NULL;
910 int frame_type;
911
912 if (theme)
913 return theme;
914
915 theme = meta_theme_new ();
916
917 for (frame_type = 0; frame_type < META_FRAME_TYPE_LAST; frame_type++)
918 {
919 MetaFrameLayout *layout = meta_frame_layout_new ();
920
921 switch (frame_type)
922 {
923 case META_FRAME_TYPE_NORMAL:
924 case META_FRAME_TYPE_DIALOG:
925 case META_FRAME_TYPE_MODAL_DIALOG:
926 case META_FRAME_TYPE_ATTACHED:
927 break;
928 case META_FRAME_TYPE_MENU:
929 case META_FRAME_TYPE_UTILITY:
930 layout->title_scale = PANGO_SCALE_SMALL;
931 break;
932 case META_FRAME_TYPE_BORDER:
933 layout->has_title = FALSE;
934 layout->hide_buttons = TRUE;
935 break;
936 default:
937 g_assert_not_reached ();
938 }
939
940 theme->layouts[frame_type] = layout;
941 }
942 return theme;
943 }
944
945 /**
946 * meta_theme_new: (skip)
947 *
948 */
949 MetaTheme*
meta_theme_new(void)950 meta_theme_new (void)
951 {
952 return g_new0 (MetaTheme, 1);
953 }
954
955
956 void
meta_theme_free(MetaTheme * theme)957 meta_theme_free (MetaTheme *theme)
958 {
959 int i;
960
961 g_return_if_fail (theme != NULL);
962
963 for (i = 0; i < META_FRAME_TYPE_LAST; i++)
964 if (theme->layouts[i])
965 meta_frame_layout_free (theme->layouts[i]);
966
967 g_free (theme);
968 }
969
970 MetaFrameLayout*
meta_theme_get_frame_layout(MetaTheme * theme,MetaFrameType type)971 meta_theme_get_frame_layout (MetaTheme *theme,
972 MetaFrameType type)
973 {
974 g_return_val_if_fail (type < META_FRAME_TYPE_LAST, NULL);
975
976 return theme->layouts[type];
977 }
978
979 static GtkStyleContext *
create_style_context(GType widget_type,GtkStyleContext * parent_style,GtkCssProvider * provider,const char * object_name,const char * first_class,...)980 create_style_context (GType widget_type,
981 GtkStyleContext *parent_style,
982 GtkCssProvider *provider,
983 const char *object_name,
984 const char *first_class,
985 ...)
986 {
987 GtkStyleContext *style;
988 GtkStateFlags state;
989 GtkWidgetPath *path;
990 const char *name;
991 va_list ap;
992
993 style = gtk_style_context_new ();
994 gtk_style_context_set_scale (style, meta_theme_get_window_scaling_factor ());
995 gtk_style_context_set_parent (style, parent_style);
996
997 if (parent_style)
998 path = gtk_widget_path_copy (gtk_style_context_get_path (parent_style));
999 else
1000 path = gtk_widget_path_new ();
1001
1002 gtk_widget_path_append_type (path, widget_type);
1003
1004 if (object_name)
1005 gtk_widget_path_iter_set_object_name (path, -1, object_name);
1006
1007 state = gtk_style_context_get_state (style);
1008 if (meta_get_locale_direction() == META_LOCALE_DIRECTION_RTL)
1009 {
1010 state |= GTK_STATE_FLAG_DIR_RTL;
1011 state &= ~GTK_STATE_FLAG_DIR_LTR;
1012 }
1013 else
1014 {
1015 state |= GTK_STATE_FLAG_DIR_LTR;
1016 state &= ~GTK_STATE_FLAG_DIR_RTL;
1017 }
1018 gtk_style_context_set_state (style, state);
1019
1020 va_start (ap, first_class);
1021 for (name = first_class; name; name = va_arg (ap, const char *))
1022 gtk_widget_path_iter_add_class (path, -1, name);
1023 va_end (ap);
1024
1025 gtk_style_context_set_path (style, path);
1026 gtk_widget_path_unref (path);
1027
1028 gtk_style_context_add_provider (style, GTK_STYLE_PROVIDER (provider),
1029 GTK_STYLE_PROVIDER_PRIORITY_SETTINGS);
1030
1031 return style;
1032 }
1033
1034 static inline GtkCssProvider *
get_css_provider_for_theme_name(const gchar * theme_name,const gchar * variant)1035 get_css_provider_for_theme_name (const gchar *theme_name,
1036 const gchar *variant)
1037 {
1038 static GtkCssProvider *default_provider = NULL;
1039
1040 if (!theme_name || *theme_name == '\0')
1041 {
1042 if (G_UNLIKELY (default_provider == NULL))
1043 default_provider = gtk_css_provider_new ();
1044
1045 return default_provider;
1046 }
1047
1048 return gtk_css_provider_get_named (theme_name, variant);
1049 }
1050
1051 MetaStyleInfo *
meta_theme_create_style_info(GdkScreen * screen,const gchar * variant)1052 meta_theme_create_style_info (GdkScreen *screen,
1053 const gchar *variant)
1054 {
1055 MetaStyleInfo *style_info;
1056 GtkCssProvider *provider;
1057 char *theme_name;
1058
1059 g_object_get (gtk_settings_get_for_screen (screen),
1060 "gtk-theme-name", &theme_name,
1061 NULL);
1062
1063 provider = get_css_provider_for_theme_name (theme_name, variant);
1064 g_free (theme_name);
1065
1066 style_info = g_new0 (MetaStyleInfo, 1);
1067 style_info->refcount = 1;
1068
1069 style_info->styles[META_STYLE_ELEMENT_WINDOW] =
1070 create_style_context (META_TYPE_FRAMES,
1071 NULL,
1072 provider,
1073 "window",
1074 GTK_STYLE_CLASS_BACKGROUND,
1075 "ssd",
1076 NULL);
1077 style_info->styles[META_STYLE_ELEMENT_FRAME] =
1078 create_style_context (META_TYPE_FRAMES,
1079 style_info->styles[META_STYLE_ELEMENT_WINDOW],
1080 provider,
1081 "decoration",
1082 NULL);
1083 style_info->styles[META_STYLE_ELEMENT_TITLEBAR] =
1084 create_style_context (GTK_TYPE_HEADER_BAR,
1085 style_info->styles[META_STYLE_ELEMENT_FRAME],
1086 provider,
1087 "headerbar",
1088 GTK_STYLE_CLASS_TITLEBAR,
1089 GTK_STYLE_CLASS_HORIZONTAL,
1090 "default-decoration",
1091 NULL);
1092 style_info->styles[META_STYLE_ELEMENT_TITLE] =
1093 create_style_context (GTK_TYPE_LABEL,
1094 style_info->styles[META_STYLE_ELEMENT_TITLEBAR],
1095 provider,
1096 "label",
1097 GTK_STYLE_CLASS_TITLE,
1098 NULL);
1099 style_info->styles[META_STYLE_ELEMENT_BUTTON] =
1100 create_style_context (GTK_TYPE_BUTTON,
1101 style_info->styles[META_STYLE_ELEMENT_TITLEBAR],
1102 provider,
1103 "button",
1104 "titlebutton",
1105 NULL);
1106 style_info->styles[META_STYLE_ELEMENT_IMAGE] =
1107 create_style_context (GTK_TYPE_IMAGE,
1108 style_info->styles[META_STYLE_ELEMENT_BUTTON],
1109 provider,
1110 "image",
1111 NULL);
1112 return style_info;
1113 }
1114
1115 MetaStyleInfo *
meta_style_info_ref(MetaStyleInfo * style_info)1116 meta_style_info_ref (MetaStyleInfo *style_info)
1117 {
1118 g_return_val_if_fail (style_info != NULL, NULL);
1119 g_return_val_if_fail (style_info->refcount > 0, NULL);
1120
1121 g_atomic_int_inc ((volatile int *)&style_info->refcount);
1122 return style_info;
1123 }
1124
1125 void
meta_style_info_unref(MetaStyleInfo * style_info)1126 meta_style_info_unref (MetaStyleInfo *style_info)
1127 {
1128 g_return_if_fail (style_info != NULL);
1129 g_return_if_fail (style_info->refcount > 0);
1130
1131 if (g_atomic_int_dec_and_test ((volatile int *)&style_info->refcount))
1132 {
1133 int i;
1134 for (i = 0; i < META_STYLE_ELEMENT_LAST; i++)
1135 g_object_unref (style_info->styles[i]);
1136 g_free (style_info);
1137 }
1138 }
1139
1140 static void
add_toplevel_class(GtkStyleContext * style,const char * class_name)1141 add_toplevel_class (GtkStyleContext *style,
1142 const char *class_name)
1143 {
1144 if (gtk_style_context_get_parent (style))
1145 {
1146 GtkWidgetPath *path;
1147
1148 path = gtk_widget_path_copy (gtk_style_context_get_path (style));
1149 gtk_widget_path_iter_add_class (path, 0, class_name);
1150 gtk_style_context_set_path (style, path);
1151 gtk_widget_path_unref (path);
1152 }
1153 else
1154 gtk_style_context_add_class (style, class_name);
1155 }
1156
1157 static void
remove_toplevel_class(GtkStyleContext * style,const char * class_name)1158 remove_toplevel_class (GtkStyleContext *style,
1159 const char *class_name)
1160 {
1161 if (gtk_style_context_get_parent (style))
1162 {
1163 GtkWidgetPath *path;
1164
1165 path = gtk_widget_path_copy (gtk_style_context_get_path (style));
1166 gtk_widget_path_iter_remove_class (path, 0, class_name);
1167 gtk_style_context_set_path (style, path);
1168 gtk_widget_path_unref (path);
1169 }
1170 else
1171 gtk_style_context_remove_class (style, class_name);
1172 }
1173
1174 void
meta_style_info_set_flags(MetaStyleInfo * style_info,MetaFrameFlags flags)1175 meta_style_info_set_flags (MetaStyleInfo *style_info,
1176 MetaFrameFlags flags)
1177 {
1178 GtkStyleContext *style;
1179 const char *class_name = NULL;
1180 gboolean backdrop;
1181 GtkStateFlags state;
1182 int i;
1183
1184 backdrop = !(flags & META_FRAME_HAS_FOCUS);
1185
1186 if (flags & META_FRAME_MAXIMIZED)
1187 class_name = "maximized";
1188 else if (flags & META_FRAME_TILED_LEFT ||
1189 flags & META_FRAME_TILED_RIGHT)
1190 class_name = "tiled";
1191
1192 for (i = 0; i < META_STYLE_ELEMENT_LAST; i++)
1193 {
1194 style = style_info->styles[i];
1195
1196 state = gtk_style_context_get_state (style);
1197 if (backdrop)
1198 gtk_style_context_set_state (style, state | GTK_STATE_FLAG_BACKDROP);
1199 else
1200 gtk_style_context_set_state (style, state & ~GTK_STATE_FLAG_BACKDROP);
1201
1202 remove_toplevel_class (style, "maximized");
1203 remove_toplevel_class (style, "tiled");
1204
1205 if (class_name)
1206 add_toplevel_class (style, class_name);
1207 }
1208 }
1209
1210 PangoFontDescription*
meta_style_info_create_font_desc(MetaStyleInfo * style_info)1211 meta_style_info_create_font_desc (MetaStyleInfo *style_info)
1212 {
1213 PangoFontDescription *font_desc;
1214 const PangoFontDescription *override = meta_prefs_get_titlebar_font ();
1215 GtkStyleContext *context = style_info->styles[META_STYLE_ELEMENT_TITLE];
1216
1217 gtk_style_context_get (context,
1218 gtk_style_context_get_state (context),
1219 "font", &font_desc, NULL);
1220
1221 if (override)
1222 pango_font_description_merge (font_desc, override, TRUE);
1223
1224 return font_desc;
1225 }
1226
1227 void
meta_theme_draw_frame(MetaTheme * theme,MetaStyleInfo * style_info,cairo_t * cr,MetaFrameType type,MetaFrameFlags flags,int client_width,int client_height,PangoLayout * title_layout,int text_height,const MetaButtonLayout * button_layout,MetaButtonState button_states[META_BUTTON_TYPE_LAST],cairo_surface_t * mini_icon)1228 meta_theme_draw_frame (MetaTheme *theme,
1229 MetaStyleInfo *style_info,
1230 cairo_t *cr,
1231 MetaFrameType type,
1232 MetaFrameFlags flags,
1233 int client_width,
1234 int client_height,
1235 PangoLayout *title_layout,
1236 int text_height,
1237 const MetaButtonLayout *button_layout,
1238 MetaButtonState button_states[META_BUTTON_TYPE_LAST],
1239 cairo_surface_t *mini_icon)
1240 {
1241 MetaFrameGeometry fgeom;
1242 MetaFrameLayout *layout;
1243
1244 g_return_if_fail (type < META_FRAME_TYPE_LAST);
1245
1246 layout = theme->layouts[type];
1247
1248 /* Parser is not supposed to allow this currently */
1249 if (layout == NULL)
1250 return;
1251
1252 meta_frame_layout_calc_geometry (layout,
1253 style_info,
1254 text_height,
1255 flags,
1256 client_width, client_height,
1257 button_layout,
1258 type,
1259 &fgeom,
1260 theme);
1261
1262 meta_frame_layout_draw_with_style (layout,
1263 style_info,
1264 cr,
1265 &fgeom,
1266 title_layout,
1267 flags,
1268 button_states,
1269 mini_icon);
1270 }
1271
1272 void
meta_theme_get_frame_borders(MetaTheme * theme,MetaStyleInfo * style_info,MetaFrameType type,int text_height,MetaFrameFlags flags,MetaFrameBorders * borders)1273 meta_theme_get_frame_borders (MetaTheme *theme,
1274 MetaStyleInfo *style_info,
1275 MetaFrameType type,
1276 int text_height,
1277 MetaFrameFlags flags,
1278 MetaFrameBorders *borders)
1279 {
1280 MetaFrameLayout *layout;
1281
1282 g_return_if_fail (type < META_FRAME_TYPE_LAST);
1283
1284 layout = theme->layouts[type];
1285
1286 meta_frame_borders_clear (borders);
1287
1288 /* Parser is not supposed to allow this currently */
1289 if (layout == NULL)
1290 return;
1291
1292 meta_frame_layout_sync_with_style (layout, style_info, flags);
1293
1294 meta_frame_layout_get_borders (layout,
1295 text_height,
1296 flags, type,
1297 borders);
1298 }
1299
1300 void
meta_theme_calc_geometry(MetaTheme * theme,MetaStyleInfo * style_info,MetaFrameType type,int text_height,MetaFrameFlags flags,int client_width,int client_height,const MetaButtonLayout * button_layout,MetaFrameGeometry * fgeom)1301 meta_theme_calc_geometry (MetaTheme *theme,
1302 MetaStyleInfo *style_info,
1303 MetaFrameType type,
1304 int text_height,
1305 MetaFrameFlags flags,
1306 int client_width,
1307 int client_height,
1308 const MetaButtonLayout *button_layout,
1309 MetaFrameGeometry *fgeom)
1310 {
1311 MetaFrameLayout *layout;
1312
1313 g_return_if_fail (type < META_FRAME_TYPE_LAST);
1314
1315 layout = theme->layouts[type];
1316
1317 /* Parser is not supposed to allow this currently */
1318 if (layout == NULL)
1319 return;
1320
1321 meta_frame_layout_calc_geometry (layout,
1322 style_info,
1323 text_height,
1324 flags,
1325 client_width, client_height,
1326 button_layout,
1327 type,
1328 fgeom,
1329 theme);
1330 }
1331
1332 /**
1333 * meta_pango_font_desc_get_text_height:
1334 * @font_desc: the font
1335 * @context: the context of the font
1336 *
1337 * Returns the height of the letters in a particular font.
1338 *
1339 * Returns: the height of the letters
1340 */
1341 int
meta_pango_font_desc_get_text_height(const PangoFontDescription * font_desc,PangoContext * context)1342 meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc,
1343 PangoContext *context)
1344 {
1345 PangoFontMetrics *metrics;
1346 PangoLanguage *lang;
1347 int retval;
1348
1349 lang = pango_context_get_language (context);
1350 metrics = pango_context_get_metrics (context, font_desc, lang);
1351
1352 retval = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
1353 pango_font_metrics_get_descent (metrics));
1354
1355 pango_font_metrics_unref (metrics);
1356
1357 return retval;
1358 }
1359
1360 /**
1361 * meta_frame_type_to_string:
1362 * @type: a #MetaFrameType
1363 *
1364 * Converts a frame type enum value to the name string that would
1365 * appear in the theme definition file.
1366 *
1367 * Return value: the string value
1368 */
1369 const char*
meta_frame_type_to_string(MetaFrameType type)1370 meta_frame_type_to_string (MetaFrameType type)
1371 {
1372 switch (type)
1373 {
1374 case META_FRAME_TYPE_NORMAL:
1375 return "normal";
1376 case META_FRAME_TYPE_DIALOG:
1377 return "dialog";
1378 case META_FRAME_TYPE_MODAL_DIALOG:
1379 return "modal_dialog";
1380 case META_FRAME_TYPE_UTILITY:
1381 return "utility";
1382 case META_FRAME_TYPE_MENU:
1383 return "menu";
1384 case META_FRAME_TYPE_BORDER:
1385 return "border";
1386 case META_FRAME_TYPE_ATTACHED:
1387 return "attached";
1388 #if 0
1389 case META_FRAME_TYPE_TOOLBAR:
1390 return "toolbar";
1391 #endif
1392 case META_FRAME_TYPE_LAST:
1393 break;
1394 }
1395
1396 return "<unknown>";
1397 }
1398