mirror of
https://github.com/brl/mutter.git
synced 2024-11-26 01:50:42 -05:00
313e3e771c
Unlike g_* logging utilities, the meta_* counterparts behave like odd printf() functions. Lets change that so they fit better into how logging is done everywhere else. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1466
1398 lines
45 KiB
C
1398 lines
45 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/*
|
|
* Copyright (C) 2001 Havoc Pennington
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "ui/theme-private.h"
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <math.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "core/util-private.h"
|
|
#include "meta/prefs.h"
|
|
#include "ui/frames.h"
|
|
|
|
static void scale_border (GtkBorder *border, double factor);
|
|
|
|
static MetaFrameLayout *
|
|
meta_frame_layout_new (void)
|
|
{
|
|
MetaFrameLayout *layout;
|
|
|
|
layout = g_new0 (MetaFrameLayout, 1);
|
|
|
|
/* Spacing as hardcoded in GTK+:
|
|
* https://git.gnome.org/browse/gtk+/tree/gtk/gtkheaderbar.c?h=gtk-3-14#n53
|
|
*/
|
|
layout->titlebar_spacing = 6;
|
|
layout->has_title = TRUE;
|
|
layout->title_scale = PANGO_SCALE_MEDIUM;
|
|
layout->icon_size = META_MINI_ICON_WIDTH;
|
|
|
|
return layout;
|
|
}
|
|
|
|
static void
|
|
meta_frame_layout_free (MetaFrameLayout *layout)
|
|
{
|
|
g_return_if_fail (layout != NULL);
|
|
|
|
g_free (layout);
|
|
}
|
|
|
|
static void
|
|
meta_frame_layout_get_borders (const MetaFrameLayout *layout,
|
|
int text_height,
|
|
MetaFrameFlags flags,
|
|
MetaFrameType type,
|
|
MetaFrameBorders *borders)
|
|
{
|
|
int buttons_height, content_height, draggable_borders;
|
|
int scale = meta_theme_get_window_scaling_factor ();
|
|
|
|
meta_frame_borders_clear (borders);
|
|
|
|
/* For a full-screen window, we don't have any borders, visible or not. */
|
|
if (flags & META_FRAME_FULLSCREEN)
|
|
return;
|
|
|
|
g_return_if_fail (layout != NULL);
|
|
|
|
if (!layout->has_title)
|
|
text_height = 0;
|
|
else
|
|
text_height = layout->title_margin.top + text_height + layout->title_margin.bottom;
|
|
|
|
buttons_height = MAX ((int)layout->icon_size, layout->button_min_size.height) +
|
|
layout->button_margin.top + layout->button_border.top +
|
|
layout->button_margin.bottom + layout->button_border.bottom;
|
|
content_height = MAX (buttons_height, text_height);
|
|
content_height = MAX (content_height, layout->titlebar_min_size.height) +
|
|
layout->titlebar_border.top + layout->titlebar_border.bottom;
|
|
|
|
borders->visible.top = layout->frame_border.top + content_height;
|
|
borders->visible.left = layout->frame_border.left;
|
|
borders->visible.right = layout->frame_border.right;
|
|
borders->visible.bottom = layout->frame_border.bottom;
|
|
|
|
borders->invisible = layout->invisible_border;
|
|
|
|
draggable_borders = meta_prefs_get_draggable_border_width ();
|
|
|
|
if (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE)
|
|
{
|
|
borders->invisible.left = MAX (borders->invisible.left,
|
|
draggable_borders - borders->visible.left);
|
|
borders->invisible.right = MAX (borders->invisible.right,
|
|
draggable_borders - borders->visible.right);
|
|
}
|
|
|
|
if (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE)
|
|
{
|
|
borders->invisible.bottom = MAX (borders->invisible.bottom,
|
|
draggable_borders - borders->visible.bottom);
|
|
|
|
/* borders.visible.top is the height of the *title bar*. We can't do the same
|
|
* algorithm here, titlebars are expectedly much bigger. Just subtract a couple
|
|
* pixels to get a proper feel. */
|
|
if (type != META_FRAME_TYPE_ATTACHED)
|
|
borders->invisible.top = MAX (borders->invisible.top, draggable_borders - 2);
|
|
}
|
|
|
|
borders->total.left = borders->invisible.left + borders->visible.left;
|
|
borders->total.right = borders->invisible.right + borders->visible.right;
|
|
borders->total.bottom = borders->invisible.bottom + borders->visible.bottom;
|
|
borders->total.top = borders->invisible.top + borders->visible.top;
|
|
|
|
/* Scale geometry for HiDPI, see comment in meta_frame_layout_draw_with_style() */
|
|
scale_border (&borders->visible, scale);
|
|
scale_border (&borders->invisible, scale);
|
|
scale_border (&borders->total, scale);
|
|
}
|
|
|
|
int
|
|
meta_theme_get_window_scaling_factor (void)
|
|
{
|
|
GdkScreen *screen;
|
|
GValue value = G_VALUE_INIT;
|
|
|
|
g_value_init (&value, G_TYPE_INT);
|
|
|
|
screen = gdk_screen_get_default ();
|
|
if (gdk_screen_get_setting (screen, "gdk-window-scaling-factor", &value))
|
|
return g_value_get_int (&value);
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
meta_frame_layout_apply_scale (const MetaFrameLayout *layout,
|
|
PangoFontDescription *font_desc)
|
|
{
|
|
int size = pango_font_description_get_size (font_desc);
|
|
double scale = layout->title_scale / meta_theme_get_window_scaling_factor ();
|
|
pango_font_description_set_size (font_desc, MAX (size * scale, 1));
|
|
}
|
|
|
|
static MetaButtonSpace*
|
|
rect_for_function (MetaFrameGeometry *fgeom,
|
|
MetaFrameFlags flags,
|
|
MetaButtonFunction function,
|
|
MetaTheme *theme)
|
|
{
|
|
switch (function)
|
|
{
|
|
case META_BUTTON_FUNCTION_MENU:
|
|
if (flags & META_FRAME_ALLOWS_MENU)
|
|
return &fgeom->menu_rect;
|
|
else
|
|
return NULL;
|
|
case META_BUTTON_FUNCTION_MINIMIZE:
|
|
if (flags & META_FRAME_ALLOWS_MINIMIZE)
|
|
return &fgeom->min_rect;
|
|
else
|
|
return NULL;
|
|
case META_BUTTON_FUNCTION_MAXIMIZE:
|
|
if (flags & META_FRAME_ALLOWS_MAXIMIZE)
|
|
return &fgeom->max_rect;
|
|
else
|
|
return NULL;
|
|
case META_BUTTON_FUNCTION_CLOSE:
|
|
if (flags & META_FRAME_ALLOWS_DELETE)
|
|
return &fgeom->close_rect;
|
|
else
|
|
return NULL;
|
|
|
|
case META_BUTTON_FUNCTION_LAST:
|
|
return NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
strip_button (MetaButtonSpace *func_rects[MAX_BUTTONS_PER_CORNER],
|
|
int *n_rects,
|
|
MetaButtonSpace *to_strip)
|
|
{
|
|
int i;
|
|
|
|
i = 0;
|
|
while (i < *n_rects)
|
|
{
|
|
if (func_rects[i] == to_strip)
|
|
{
|
|
*n_rects -= 1;
|
|
|
|
/* shift the other rects back in the array */
|
|
while (i < *n_rects)
|
|
{
|
|
func_rects[i] = func_rects[i+1];
|
|
|
|
++i;
|
|
}
|
|
|
|
func_rects[i] = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
return FALSE; /* did not strip anything */
|
|
}
|
|
|
|
static void
|
|
get_padding_and_border (GtkStyleContext *style,
|
|
GtkBorder *border)
|
|
{
|
|
GtkBorder tmp;
|
|
GtkStateFlags state = gtk_style_context_get_state (style);
|
|
|
|
gtk_style_context_get_border (style, state, border);
|
|
gtk_style_context_get_padding (style, state, &tmp);
|
|
|
|
border->left += tmp.left;
|
|
border->top += tmp.top;
|
|
border->right += tmp.right;
|
|
border->bottom += tmp.bottom;
|
|
}
|
|
|
|
static void
|
|
get_min_size (GtkStyleContext *style,
|
|
GtkRequisition *requisition)
|
|
{
|
|
gtk_style_context_get (style, gtk_style_context_get_state (style),
|
|
"min-width", &requisition->width,
|
|
"min-height", &requisition->height,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
scale_border (GtkBorder *border,
|
|
double factor)
|
|
{
|
|
border->left *= factor;
|
|
border->right *= factor;
|
|
border->top *= factor;
|
|
border->bottom *= factor;
|
|
}
|
|
|
|
static void
|
|
meta_frame_layout_sync_with_style (MetaFrameLayout *layout,
|
|
MetaStyleInfo *style_info,
|
|
MetaFrameFlags flags)
|
|
{
|
|
GtkStyleContext *style;
|
|
GtkBorder border;
|
|
GtkRequisition requisition;
|
|
GdkRectangle clip_rect;
|
|
int border_radius, max_radius;
|
|
|
|
meta_style_info_set_flags (style_info, flags);
|
|
|
|
style = style_info->styles[META_STYLE_ELEMENT_FRAME];
|
|
get_padding_and_border (style, &layout->frame_border);
|
|
scale_border (&layout->frame_border, layout->title_scale);
|
|
|
|
gtk_render_background_get_clip (style, 0, 0, 0, 0, &clip_rect);
|
|
layout->invisible_border.left = -clip_rect.x;
|
|
layout->invisible_border.right = clip_rect.width + clip_rect.x;
|
|
layout->invisible_border.top = -clip_rect.y;
|
|
layout->invisible_border.bottom = clip_rect.height + clip_rect.y;
|
|
|
|
if (layout->hide_buttons)
|
|
layout->icon_size = 0;
|
|
|
|
if (!layout->has_title && layout->hide_buttons)
|
|
return; /* border-only - be done */
|
|
|
|
style = style_info->styles[META_STYLE_ELEMENT_TITLEBAR];
|
|
gtk_style_context_get (style, gtk_style_context_get_state (style),
|
|
"border-radius", &border_radius,
|
|
NULL);
|
|
/* GTK+ currently does not allow us to look up radii of individual
|
|
* corners; however we don't clip the client area, so with the
|
|
* current trend of using small/no visible frame borders, most
|
|
* themes should work fine with this.
|
|
*/
|
|
layout->top_left_corner_rounded_radius = border_radius;
|
|
layout->top_right_corner_rounded_radius = border_radius;
|
|
max_radius = MIN (layout->frame_border.bottom, layout->frame_border.left);
|
|
layout->bottom_left_corner_rounded_radius = MAX (border_radius, max_radius);
|
|
max_radius = MIN (layout->frame_border.bottom, layout->frame_border.right);
|
|
layout->bottom_right_corner_rounded_radius = MAX (border_radius, max_radius);
|
|
|
|
get_min_size (style, &layout->titlebar_min_size);
|
|
get_padding_and_border (style, &layout->titlebar_border);
|
|
scale_border (&layout->titlebar_border, layout->title_scale);
|
|
|
|
style = style_info->styles[META_STYLE_ELEMENT_TITLE];
|
|
gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
|
|
&layout->title_margin);
|
|
scale_border (&layout->title_margin, layout->title_scale);
|
|
|
|
style = style_info->styles[META_STYLE_ELEMENT_BUTTON];
|
|
get_min_size (style, &layout->button_min_size);
|
|
get_padding_and_border (style, &layout->button_border);
|
|
scale_border (&layout->button_border, layout->title_scale);
|
|
gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
|
|
&layout->button_margin);
|
|
scale_border (&layout->button_margin, layout->title_scale);
|
|
|
|
style = style_info->styles[META_STYLE_ELEMENT_IMAGE];
|
|
get_min_size (style, &requisition);
|
|
get_padding_and_border (style, &border);
|
|
scale_border (&border, layout->title_scale);
|
|
|
|
layout->button_border.left += border.left;
|
|
layout->button_border.right += border.right;
|
|
layout->button_border.top += border.top;
|
|
layout->button_border.bottom += border.bottom;
|
|
|
|
gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
|
|
&border);
|
|
layout->button_border.left += border.left;
|
|
layout->button_border.right += border.right;
|
|
layout->button_border.top += border.top;
|
|
layout->button_border.bottom += border.bottom;
|
|
|
|
layout->button_min_size.width = MAX(layout->button_min_size.width,
|
|
requisition.width);
|
|
layout->button_min_size.height = MAX(layout->button_min_size.height,
|
|
requisition.height);
|
|
}
|
|
|
|
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)
|
|
{
|
|
int i, n_left, n_right, n_left_spacers, n_right_spacers;
|
|
int x;
|
|
int button_y;
|
|
int title_right_edge;
|
|
int width, height;
|
|
int content_width, content_height;
|
|
int button_width, button_height;
|
|
int min_size_for_rounding;
|
|
int scale = meta_theme_get_window_scaling_factor ();
|
|
|
|
/* the left/right rects in order; the max # of rects
|
|
* is the number of button functions
|
|
*/
|
|
MetaButtonSpace *left_func_rects[MAX_BUTTONS_PER_CORNER];
|
|
MetaButtonSpace *right_func_rects[MAX_BUTTONS_PER_CORNER];
|
|
gboolean left_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
|
|
gboolean right_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
|
|
|
|
MetaFrameBorders borders;
|
|
|
|
meta_frame_layout_sync_with_style (layout, style_info, flags);
|
|
|
|
meta_frame_layout_get_borders (layout, text_height,
|
|
flags, type,
|
|
&borders);
|
|
|
|
fgeom->borders = borders;
|
|
|
|
/* Scale geometry for HiDPI, see comment in meta_frame_layout_draw_with_style() */
|
|
fgeom->content_border = layout->frame_border;
|
|
fgeom->content_border.left += layout->titlebar_border.left * scale;
|
|
fgeom->content_border.right += layout->titlebar_border.right * scale;
|
|
fgeom->content_border.top += layout->titlebar_border.top * scale;
|
|
fgeom->content_border.bottom += layout->titlebar_border.bottom * scale;
|
|
|
|
width = client_width + borders.total.left + borders.total.right;
|
|
|
|
height = borders.total.top + borders.total.bottom;
|
|
if (!(flags & META_FRAME_SHADED))
|
|
height += client_height;
|
|
|
|
fgeom->width = width;
|
|
fgeom->height = height;
|
|
|
|
content_width = width -
|
|
(fgeom->content_border.left + borders.invisible.left) -
|
|
(fgeom->content_border.right + borders.invisible.right);
|
|
content_height = borders.visible.top - fgeom->content_border.top - fgeom->content_border.bottom;
|
|
|
|
button_width = MAX ((int)layout->icon_size, layout->button_min_size.width) +
|
|
layout->button_border.left + layout->button_border.right;
|
|
button_height = MAX ((int)layout->icon_size, layout->button_min_size.height) +
|
|
layout->button_border.top + layout->button_border.bottom;
|
|
button_width *= scale;
|
|
button_height *= scale;
|
|
|
|
/* FIXME all this code sort of pretends that duplicate buttons
|
|
* with the same function are allowed, but that breaks the
|
|
* code in frames.c, so isn't really allowed right now.
|
|
* Would need left_close_rect, right_close_rect, etc.
|
|
*/
|
|
|
|
/* Init all button rects to 0, lame hack */
|
|
memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0',
|
|
LENGTH_OF_BUTTON_RECTS);
|
|
|
|
n_left = 0;
|
|
n_right = 0;
|
|
n_left_spacers = 0;
|
|
n_right_spacers = 0;
|
|
|
|
if (!layout->hide_buttons)
|
|
{
|
|
/* Try to fill in rects */
|
|
for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
|
|
{
|
|
left_func_rects[n_left] = rect_for_function (fgeom, flags,
|
|
button_layout->left_buttons[i],
|
|
theme);
|
|
if (left_func_rects[n_left] != NULL)
|
|
{
|
|
left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i];
|
|
if (button_layout->left_buttons_has_spacer[i])
|
|
++n_left_spacers;
|
|
|
|
++n_left;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
|
|
{
|
|
right_func_rects[n_right] = rect_for_function (fgeom, flags,
|
|
button_layout->right_buttons[i],
|
|
theme);
|
|
if (right_func_rects[n_right] != NULL)
|
|
{
|
|
right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i];
|
|
if (button_layout->right_buttons_has_spacer[i])
|
|
++n_right_spacers;
|
|
|
|
++n_right;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Be sure buttons fit */
|
|
while (n_left > 0 || n_right > 0)
|
|
{
|
|
int space_used_by_buttons;
|
|
|
|
space_used_by_buttons = 0;
|
|
|
|
space_used_by_buttons += layout->button_margin.left * scale * n_left;
|
|
space_used_by_buttons += button_width * n_left;
|
|
space_used_by_buttons += layout->button_margin.right * scale * n_left;
|
|
space_used_by_buttons += (button_width * 0.75) * n_left_spacers;
|
|
space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_left - 1, 0);
|
|
|
|
space_used_by_buttons += layout->button_margin.left * scale * n_right;
|
|
space_used_by_buttons += button_width * n_right;
|
|
space_used_by_buttons += layout->button_margin.right * scale * n_right;
|
|
space_used_by_buttons += (button_width * 0.75) * n_right_spacers;
|
|
space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_right - 1, 0);
|
|
|
|
if (space_used_by_buttons <= content_width)
|
|
break; /* Everything fits, bail out */
|
|
|
|
/* First try to remove separators */
|
|
if (n_left_spacers > 0)
|
|
{
|
|
left_buttons_has_spacer[--n_left_spacers] = FALSE;
|
|
continue;
|
|
}
|
|
else if (n_right_spacers > 0)
|
|
{
|
|
right_buttons_has_spacer[--n_right_spacers] = FALSE;
|
|
continue;
|
|
}
|
|
|
|
/* Otherwise we need to shave out a button. Shave
|
|
* min, max, close, then menu (menu is most useful);
|
|
* prefer the default button locations.
|
|
*/
|
|
if (strip_button (left_func_rects, &n_left, &fgeom->min_rect))
|
|
continue;
|
|
else if (strip_button (right_func_rects, &n_right, &fgeom->min_rect))
|
|
continue;
|
|
else if (strip_button (left_func_rects, &n_left, &fgeom->max_rect))
|
|
continue;
|
|
else if (strip_button (right_func_rects, &n_right, &fgeom->max_rect))
|
|
continue;
|
|
else if (strip_button (left_func_rects, &n_left, &fgeom->close_rect))
|
|
continue;
|
|
else if (strip_button (right_func_rects, &n_right, &fgeom->close_rect))
|
|
continue;
|
|
else if (strip_button (right_func_rects, &n_right, &fgeom->menu_rect))
|
|
continue;
|
|
else if (strip_button (left_func_rects, &n_left, &fgeom->menu_rect))
|
|
continue;
|
|
else
|
|
{
|
|
meta_bug ("Could not find a button to strip. n_left = %d n_right = %d",
|
|
n_left, n_right);
|
|
}
|
|
}
|
|
|
|
/* Save the button layout */
|
|
fgeom->button_layout = *button_layout;
|
|
fgeom->n_left_buttons = n_left;
|
|
fgeom->n_right_buttons = n_right;
|
|
|
|
/* center buttons vertically */
|
|
button_y = fgeom->content_border.top + borders.invisible.top +
|
|
(content_height - button_height) / 2;
|
|
|
|
/* right edge of farthest-right button */
|
|
x = width - fgeom->content_border.right - borders.invisible.right;
|
|
|
|
i = n_right - 1;
|
|
while (i >= 0)
|
|
{
|
|
MetaButtonSpace *rect;
|
|
|
|
if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */
|
|
break;
|
|
|
|
x -= layout->button_margin.right * scale;
|
|
|
|
rect = right_func_rects[i];
|
|
rect->visible.x = x - button_width;
|
|
if (right_buttons_has_spacer[i])
|
|
rect->visible.x -= (button_width * 0.75);
|
|
|
|
rect->visible.y = button_y;
|
|
rect->visible.width = button_width;
|
|
rect->visible.height = button_height;
|
|
|
|
if (flags & META_FRAME_MAXIMIZED ||
|
|
flags & META_FRAME_TILED_LEFT ||
|
|
flags & META_FRAME_TILED_RIGHT)
|
|
{
|
|
rect->clickable.x = rect->visible.x;
|
|
rect->clickable.y = 0;
|
|
rect->clickable.width = rect->visible.width;
|
|
rect->clickable.height = button_height + button_y;
|
|
|
|
if (i == n_right - 1)
|
|
rect->clickable.width += fgeom->content_border.right;
|
|
|
|
}
|
|
else
|
|
memmove (&(rect->clickable), &(rect->visible), sizeof (rect->clickable));
|
|
|
|
x = rect->visible.x - layout->button_margin.left * scale;
|
|
|
|
if (i > 0)
|
|
x -= layout->titlebar_spacing * scale;
|
|
|
|
--i;
|
|
}
|
|
|
|
/* save right edge of titlebar for later use */
|
|
title_right_edge = x;
|
|
|
|
/* Now x changes to be position from the left and we go through
|
|
* the left-side buttons
|
|
*/
|
|
x = fgeom->content_border.left + borders.invisible.left;
|
|
for (i = 0; i < n_left; i++)
|
|
{
|
|
MetaButtonSpace *rect;
|
|
|
|
x += layout->button_margin.left * scale;
|
|
|
|
rect = left_func_rects[i];
|
|
|
|
rect->visible.x = x;
|
|
rect->visible.y = button_y;
|
|
rect->visible.width = button_width;
|
|
rect->visible.height = button_height;
|
|
|
|
if (flags & META_FRAME_MAXIMIZED)
|
|
{
|
|
if (i==0)
|
|
{
|
|
rect->clickable.x = 0;
|
|
rect->clickable.width = button_width + x;
|
|
}
|
|
else
|
|
{
|
|
rect->clickable.x = rect->visible.x;
|
|
rect->clickable.width = button_width;
|
|
}
|
|
|
|
rect->clickable.y = 0;
|
|
rect->clickable.height = button_height + button_y;
|
|
}
|
|
else
|
|
memmove (&(rect->clickable), &(rect->visible), sizeof (rect->clickable));
|
|
|
|
x = rect->visible.x + rect->visible.width + layout->button_margin.right * scale;
|
|
if (i < n_left - 1)
|
|
x += layout->titlebar_spacing * scale;
|
|
if (left_buttons_has_spacer[i])
|
|
x += (button_width * 0.75);
|
|
}
|
|
|
|
/* Center vertically in the available content area */
|
|
fgeom->title_rect.x = x;
|
|
fgeom->title_rect.y = fgeom->content_border.top + borders.invisible.top +
|
|
(content_height - text_height) / 2;
|
|
fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x;
|
|
fgeom->title_rect.height = text_height;
|
|
|
|
/* Nuke title if it won't fit */
|
|
if (fgeom->title_rect.width < 0 ||
|
|
fgeom->title_rect.height < 0)
|
|
{
|
|
fgeom->title_rect.width = 0;
|
|
fgeom->title_rect.height = 0;
|
|
}
|
|
|
|
if (flags & META_FRAME_SHADED)
|
|
min_size_for_rounding = 0;
|
|
else
|
|
min_size_for_rounding = 5 * scale;
|
|
|
|
fgeom->top_left_corner_rounded_radius = 0;
|
|
fgeom->top_right_corner_rounded_radius = 0;
|
|
fgeom->bottom_left_corner_rounded_radius = 0;
|
|
fgeom->bottom_right_corner_rounded_radius = 0;
|
|
|
|
if (borders.visible.top + borders.visible.left >= min_size_for_rounding)
|
|
fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius * scale;
|
|
if (borders.visible.top + borders.visible.right >= min_size_for_rounding)
|
|
fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius * scale;
|
|
|
|
if (borders.visible.bottom + borders.visible.left >= min_size_for_rounding)
|
|
fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius * scale;
|
|
if (borders.visible.bottom + borders.visible.right >= min_size_for_rounding)
|
|
fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius * scale;
|
|
}
|
|
|
|
static void
|
|
get_button_rect (MetaButtonType type,
|
|
const MetaFrameGeometry *fgeom,
|
|
GdkRectangle *rect)
|
|
{
|
|
switch (type)
|
|
{
|
|
case META_BUTTON_TYPE_CLOSE:
|
|
*rect = fgeom->close_rect.visible;
|
|
break;
|
|
|
|
case META_BUTTON_TYPE_MAXIMIZE:
|
|
*rect = fgeom->max_rect.visible;
|
|
break;
|
|
|
|
case META_BUTTON_TYPE_MINIMIZE:
|
|
*rect = fgeom->min_rect.visible;
|
|
break;
|
|
|
|
case META_BUTTON_TYPE_MENU:
|
|
*rect = fgeom->menu_rect.visible;
|
|
break;
|
|
|
|
default:
|
|
case META_BUTTON_TYPE_LAST:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
get_class_from_button_type (MetaButtonType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case META_BUTTON_TYPE_CLOSE:
|
|
return "close";
|
|
case META_BUTTON_TYPE_MAXIMIZE:
|
|
return "maximize";
|
|
case META_BUTTON_TYPE_MINIMIZE:
|
|
return "minimize";
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
GtkStyleContext *style;
|
|
GtkStateFlags state;
|
|
MetaButtonType button_type;
|
|
GdkRectangle visible_rect;
|
|
GdkRectangle titlebar_rect;
|
|
GdkRectangle button_rect;
|
|
const MetaFrameBorders *borders;
|
|
cairo_surface_t *frame_surface;
|
|
double xscale, yscale;
|
|
int scale;
|
|
|
|
/* We opt out of GTK+/Clutter's HiDPI handling, so we have to do the scaling
|
|
* ourselves; the nitty-gritty is a bit confusing, so here is an overview:
|
|
* - the values in MetaFrameLayout are always as they appear in the theme,
|
|
* i.e. unscaled
|
|
* - calculated values (borders, MetaFrameGeometry) include the scale - as
|
|
* the geometry is comprised of scaled decorations and the client size
|
|
* which we must not scale, we don't have another option
|
|
* - for drawing, we scale the canvas to have GTK+ render elements (borders,
|
|
* radii, ...) at the correct scale - as a result, we have to "unscale"
|
|
* the geometry again to not apply the scaling twice
|
|
* - As per commit e36b629c GTK expects the device scale to be set and match
|
|
* the final scaling or the surface caching won't take this in account
|
|
* breaking -gtk-scaled items.
|
|
*/
|
|
scale = meta_theme_get_window_scaling_factor ();
|
|
frame_surface = cairo_get_target (cr);
|
|
cairo_surface_get_device_scale (frame_surface, &xscale, &yscale);
|
|
cairo_surface_set_device_scale (frame_surface, scale, scale);
|
|
|
|
borders = &fgeom->borders;
|
|
|
|
visible_rect.x = borders->invisible.left / scale;
|
|
visible_rect.y = borders->invisible.top / scale;
|
|
visible_rect.width = (fgeom->width - borders->invisible.left - borders->invisible.right) / scale;
|
|
visible_rect.height = (fgeom->height - borders->invisible.top - borders->invisible.bottom) / scale;
|
|
|
|
meta_style_info_set_flags (style_info, flags);
|
|
|
|
style = style_info->styles[META_STYLE_ELEMENT_FRAME];
|
|
gtk_render_background (style, cr,
|
|
visible_rect.x, visible_rect.y,
|
|
visible_rect.width, visible_rect.height);
|
|
gtk_render_frame (style, cr,
|
|
visible_rect.x, visible_rect.y,
|
|
visible_rect.width, visible_rect.height);
|
|
|
|
titlebar_rect.x = visible_rect.x;
|
|
titlebar_rect.y = visible_rect.y;
|
|
titlebar_rect.width = visible_rect.width;
|
|
titlebar_rect.height = borders->visible.top / scale;
|
|
|
|
style = style_info->styles[META_STYLE_ELEMENT_TITLEBAR];
|
|
gtk_render_background (style, cr,
|
|
titlebar_rect.x, titlebar_rect.y,
|
|
titlebar_rect.width, titlebar_rect.height);
|
|
gtk_render_frame (style, cr,
|
|
titlebar_rect.x, titlebar_rect.y,
|
|
titlebar_rect.width, titlebar_rect.height);
|
|
|
|
if (layout->has_title && title_layout)
|
|
{
|
|
PangoRectangle logical;
|
|
int text_width, x, y;
|
|
|
|
pango_layout_set_width (title_layout, -1);
|
|
pango_layout_get_pixel_extents (title_layout, NULL, &logical);
|
|
|
|
text_width = MIN(fgeom->title_rect.width / scale, logical.width);
|
|
|
|
if (text_width < logical.width)
|
|
pango_layout_set_width (title_layout, PANGO_SCALE * text_width);
|
|
|
|
/* Center within the frame if possible */
|
|
x = titlebar_rect.x + (titlebar_rect.width - text_width) / 2;
|
|
y = titlebar_rect.y + (titlebar_rect.height - logical.height) / 2;
|
|
|
|
if (x < fgeom->title_rect.x / scale)
|
|
x = fgeom->title_rect.x / scale;
|
|
else if (x + text_width > (fgeom->title_rect.x + fgeom->title_rect.width) / scale)
|
|
x = (fgeom->title_rect.x + fgeom->title_rect.width) / scale - text_width;
|
|
|
|
style = style_info->styles[META_STYLE_ELEMENT_TITLE];
|
|
gtk_render_layout (style, cr, x, y, title_layout);
|
|
}
|
|
|
|
style = style_info->styles[META_STYLE_ELEMENT_BUTTON];
|
|
state = gtk_style_context_get_state (style);
|
|
for (button_type = META_BUTTON_TYPE_CLOSE; button_type < META_BUTTON_TYPE_LAST; button_type++)
|
|
{
|
|
const char *button_class = get_class_from_button_type (button_type);
|
|
if (button_class)
|
|
gtk_style_context_add_class (style, button_class);
|
|
|
|
get_button_rect (button_type, fgeom, &button_rect);
|
|
|
|
button_rect.x /= scale;
|
|
button_rect.y /= scale;
|
|
button_rect.width /= scale;
|
|
button_rect.height /= scale;
|
|
|
|
if (button_states[button_type] == META_BUTTON_STATE_PRELIGHT)
|
|
gtk_style_context_set_state (style, state | GTK_STATE_PRELIGHT);
|
|
else if (button_states[button_type] == META_BUTTON_STATE_PRESSED)
|
|
gtk_style_context_set_state (style, state | GTK_STATE_ACTIVE);
|
|
else
|
|
gtk_style_context_set_state (style, state);
|
|
|
|
cairo_save (cr);
|
|
|
|
if (button_rect.width > 0 && button_rect.height > 0)
|
|
{
|
|
cairo_surface_t *surface = NULL;
|
|
const char *icon_name = NULL;
|
|
|
|
gtk_render_background (style, cr,
|
|
button_rect.x, button_rect.y,
|
|
button_rect.width, button_rect.height);
|
|
gtk_render_frame (style, cr,
|
|
button_rect.x, button_rect.y,
|
|
button_rect.width, button_rect.height);
|
|
|
|
switch (button_type)
|
|
{
|
|
case META_BUTTON_TYPE_CLOSE:
|
|
icon_name = "window-close-symbolic";
|
|
break;
|
|
case META_BUTTON_TYPE_MAXIMIZE:
|
|
if (flags & META_FRAME_MAXIMIZED)
|
|
icon_name = "window-restore-symbolic";
|
|
else
|
|
icon_name = "window-maximize-symbolic";
|
|
break;
|
|
case META_BUTTON_TYPE_MINIMIZE:
|
|
icon_name = "window-minimize-symbolic";
|
|
break;
|
|
case META_BUTTON_TYPE_MENU:
|
|
icon_name = "open-menu-symbolic";
|
|
break;
|
|
default:
|
|
icon_name = NULL;
|
|
break;
|
|
}
|
|
|
|
if (icon_name)
|
|
{
|
|
GtkIconTheme *theme = gtk_icon_theme_get_default ();
|
|
g_autoptr (GtkIconInfo) info = NULL;
|
|
g_autoptr (GdkPixbuf) pixbuf = NULL;
|
|
|
|
info = gtk_icon_theme_lookup_icon_for_scale (theme, icon_name,
|
|
layout->icon_size, scale, 0);
|
|
pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, NULL, NULL);
|
|
surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
|
|
}
|
|
|
|
if (surface)
|
|
{
|
|
float width, height;
|
|
int x, y;
|
|
|
|
width = cairo_image_surface_get_width (surface) / scale;
|
|
height = cairo_image_surface_get_height (surface) / scale;
|
|
x = button_rect.x + (button_rect.width - layout->icon_size) / 2;
|
|
y = button_rect.y + (button_rect.height - layout->icon_size) / 2;
|
|
|
|
cairo_translate (cr, x, y);
|
|
cairo_scale (cr,
|
|
layout->icon_size / width,
|
|
layout->icon_size / height);
|
|
cairo_set_source_surface (cr, surface, 0, 0);
|
|
cairo_paint (cr);
|
|
|
|
cairo_surface_destroy (surface);
|
|
}
|
|
}
|
|
cairo_restore (cr);
|
|
if (button_class)
|
|
gtk_style_context_remove_class (style, button_class);
|
|
gtk_style_context_set_state (style, state);
|
|
}
|
|
|
|
cairo_surface_set_device_scale (frame_surface, xscale, yscale);
|
|
}
|
|
|
|
/**
|
|
* meta_theme_get_default: (skip)
|
|
*
|
|
*/
|
|
MetaTheme*
|
|
meta_theme_get_default (void)
|
|
{
|
|
static MetaTheme *theme = NULL;
|
|
int frame_type;
|
|
|
|
if (theme)
|
|
return theme;
|
|
|
|
theme = meta_theme_new ();
|
|
|
|
for (frame_type = 0; frame_type < META_FRAME_TYPE_LAST; frame_type++)
|
|
{
|
|
MetaFrameLayout *layout = meta_frame_layout_new ();
|
|
|
|
switch (frame_type)
|
|
{
|
|
case META_FRAME_TYPE_NORMAL:
|
|
case META_FRAME_TYPE_DIALOG:
|
|
case META_FRAME_TYPE_MODAL_DIALOG:
|
|
case META_FRAME_TYPE_ATTACHED:
|
|
break;
|
|
case META_FRAME_TYPE_MENU:
|
|
case META_FRAME_TYPE_UTILITY:
|
|
layout->title_scale = PANGO_SCALE_SMALL;
|
|
break;
|
|
case META_FRAME_TYPE_BORDER:
|
|
layout->has_title = FALSE;
|
|
layout->hide_buttons = TRUE;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
theme->layouts[frame_type] = layout;
|
|
}
|
|
return theme;
|
|
}
|
|
|
|
/**
|
|
* meta_theme_new: (skip)
|
|
*
|
|
*/
|
|
MetaTheme*
|
|
meta_theme_new (void)
|
|
{
|
|
return g_new0 (MetaTheme, 1);
|
|
}
|
|
|
|
|
|
void
|
|
meta_theme_free (MetaTheme *theme)
|
|
{
|
|
int i;
|
|
|
|
g_return_if_fail (theme != NULL);
|
|
|
|
for (i = 0; i < META_FRAME_TYPE_LAST; i++)
|
|
if (theme->layouts[i])
|
|
meta_frame_layout_free (theme->layouts[i]);
|
|
|
|
g_free (theme);
|
|
}
|
|
|
|
MetaFrameLayout*
|
|
meta_theme_get_frame_layout (MetaTheme *theme,
|
|
MetaFrameType type)
|
|
{
|
|
g_return_val_if_fail (type < META_FRAME_TYPE_LAST, NULL);
|
|
|
|
return theme->layouts[type];
|
|
}
|
|
|
|
static GtkStyleContext *
|
|
create_style_context (GType widget_type,
|
|
GtkStyleContext *parent_style,
|
|
GtkCssProvider *provider,
|
|
const char *object_name,
|
|
const char *first_class,
|
|
...)
|
|
{
|
|
GtkStyleContext *style;
|
|
GtkStateFlags state;
|
|
GtkWidgetPath *path;
|
|
const char *name;
|
|
va_list ap;
|
|
|
|
style = gtk_style_context_new ();
|
|
gtk_style_context_set_scale (style, meta_theme_get_window_scaling_factor ());
|
|
gtk_style_context_set_parent (style, parent_style);
|
|
|
|
if (parent_style)
|
|
path = gtk_widget_path_copy (gtk_style_context_get_path (parent_style));
|
|
else
|
|
path = gtk_widget_path_new ();
|
|
|
|
gtk_widget_path_append_type (path, widget_type);
|
|
|
|
if (object_name)
|
|
gtk_widget_path_iter_set_object_name (path, -1, object_name);
|
|
|
|
state = gtk_style_context_get_state (style);
|
|
if (meta_get_locale_direction() == META_LOCALE_DIRECTION_RTL)
|
|
{
|
|
state |= GTK_STATE_FLAG_DIR_RTL;
|
|
state &= ~GTK_STATE_FLAG_DIR_LTR;
|
|
}
|
|
else
|
|
{
|
|
state |= GTK_STATE_FLAG_DIR_LTR;
|
|
state &= ~GTK_STATE_FLAG_DIR_RTL;
|
|
}
|
|
gtk_style_context_set_state (style, state);
|
|
|
|
va_start (ap, first_class);
|
|
for (name = first_class; name; name = va_arg (ap, const char *))
|
|
gtk_widget_path_iter_add_class (path, -1, name);
|
|
va_end (ap);
|
|
|
|
gtk_style_context_set_path (style, path);
|
|
gtk_widget_path_unref (path);
|
|
|
|
gtk_style_context_add_provider (style, GTK_STYLE_PROVIDER (provider),
|
|
GTK_STYLE_PROVIDER_PRIORITY_SETTINGS);
|
|
|
|
return style;
|
|
}
|
|
|
|
static inline GtkCssProvider *
|
|
get_css_provider_for_theme_name (const gchar *theme_name,
|
|
const gchar *variant)
|
|
{
|
|
static GtkCssProvider *default_provider = NULL;
|
|
|
|
if (!theme_name || *theme_name == '\0')
|
|
{
|
|
if (G_UNLIKELY (default_provider == NULL))
|
|
default_provider = gtk_css_provider_new ();
|
|
|
|
return default_provider;
|
|
}
|
|
|
|
return gtk_css_provider_get_named (theme_name, variant);
|
|
}
|
|
|
|
MetaStyleInfo *
|
|
meta_theme_create_style_info (GdkScreen *screen,
|
|
const gchar *variant)
|
|
{
|
|
MetaStyleInfo *style_info;
|
|
GtkCssProvider *provider;
|
|
char *theme_name;
|
|
|
|
g_object_get (gtk_settings_get_for_screen (screen),
|
|
"gtk-theme-name", &theme_name,
|
|
NULL);
|
|
|
|
provider = get_css_provider_for_theme_name (theme_name, variant);
|
|
g_free (theme_name);
|
|
|
|
style_info = g_new0 (MetaStyleInfo, 1);
|
|
style_info->refcount = 1;
|
|
|
|
style_info->styles[META_STYLE_ELEMENT_WINDOW] =
|
|
create_style_context (META_TYPE_FRAMES,
|
|
NULL,
|
|
provider,
|
|
"window",
|
|
GTK_STYLE_CLASS_BACKGROUND,
|
|
"ssd",
|
|
NULL);
|
|
style_info->styles[META_STYLE_ELEMENT_FRAME] =
|
|
create_style_context (META_TYPE_FRAMES,
|
|
style_info->styles[META_STYLE_ELEMENT_WINDOW],
|
|
provider,
|
|
"decoration",
|
|
NULL);
|
|
style_info->styles[META_STYLE_ELEMENT_TITLEBAR] =
|
|
create_style_context (GTK_TYPE_HEADER_BAR,
|
|
style_info->styles[META_STYLE_ELEMENT_FRAME],
|
|
provider,
|
|
"headerbar",
|
|
GTK_STYLE_CLASS_TITLEBAR,
|
|
GTK_STYLE_CLASS_HORIZONTAL,
|
|
"default-decoration",
|
|
NULL);
|
|
style_info->styles[META_STYLE_ELEMENT_TITLE] =
|
|
create_style_context (GTK_TYPE_LABEL,
|
|
style_info->styles[META_STYLE_ELEMENT_TITLEBAR],
|
|
provider,
|
|
"label",
|
|
GTK_STYLE_CLASS_TITLE,
|
|
NULL);
|
|
style_info->styles[META_STYLE_ELEMENT_BUTTON] =
|
|
create_style_context (GTK_TYPE_BUTTON,
|
|
style_info->styles[META_STYLE_ELEMENT_TITLEBAR],
|
|
provider,
|
|
"button",
|
|
"titlebutton",
|
|
NULL);
|
|
style_info->styles[META_STYLE_ELEMENT_IMAGE] =
|
|
create_style_context (GTK_TYPE_IMAGE,
|
|
style_info->styles[META_STYLE_ELEMENT_BUTTON],
|
|
provider,
|
|
"image",
|
|
NULL);
|
|
return style_info;
|
|
}
|
|
|
|
MetaStyleInfo *
|
|
meta_style_info_ref (MetaStyleInfo *style_info)
|
|
{
|
|
g_return_val_if_fail (style_info != NULL, NULL);
|
|
g_return_val_if_fail (style_info->refcount > 0, NULL);
|
|
|
|
g_atomic_int_inc ((volatile int *)&style_info->refcount);
|
|
return style_info;
|
|
}
|
|
|
|
void
|
|
meta_style_info_unref (MetaStyleInfo *style_info)
|
|
{
|
|
g_return_if_fail (style_info != NULL);
|
|
g_return_if_fail (style_info->refcount > 0);
|
|
|
|
if (g_atomic_int_dec_and_test ((volatile int *)&style_info->refcount))
|
|
{
|
|
int i;
|
|
for (i = 0; i < META_STYLE_ELEMENT_LAST; i++)
|
|
g_object_unref (style_info->styles[i]);
|
|
g_free (style_info);
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_toplevel_class (GtkStyleContext *style,
|
|
const char *class_name)
|
|
{
|
|
if (gtk_style_context_get_parent (style))
|
|
{
|
|
GtkWidgetPath *path;
|
|
|
|
path = gtk_widget_path_copy (gtk_style_context_get_path (style));
|
|
gtk_widget_path_iter_add_class (path, 0, class_name);
|
|
gtk_style_context_set_path (style, path);
|
|
gtk_widget_path_unref (path);
|
|
}
|
|
else
|
|
gtk_style_context_add_class (style, class_name);
|
|
}
|
|
|
|
static void
|
|
remove_toplevel_class (GtkStyleContext *style,
|
|
const char *class_name)
|
|
{
|
|
if (gtk_style_context_get_parent (style))
|
|
{
|
|
GtkWidgetPath *path;
|
|
|
|
path = gtk_widget_path_copy (gtk_style_context_get_path (style));
|
|
gtk_widget_path_iter_remove_class (path, 0, class_name);
|
|
gtk_style_context_set_path (style, path);
|
|
gtk_widget_path_unref (path);
|
|
}
|
|
else
|
|
gtk_style_context_remove_class (style, class_name);
|
|
}
|
|
|
|
void
|
|
meta_style_info_set_flags (MetaStyleInfo *style_info,
|
|
MetaFrameFlags flags)
|
|
{
|
|
GtkStyleContext *style;
|
|
const char *class_name = NULL;
|
|
gboolean backdrop;
|
|
GtkStateFlags state;
|
|
int i;
|
|
|
|
backdrop = !(flags & META_FRAME_HAS_FOCUS);
|
|
|
|
if (flags & META_FRAME_MAXIMIZED)
|
|
class_name = "maximized";
|
|
else if (flags & META_FRAME_TILED_LEFT ||
|
|
flags & META_FRAME_TILED_RIGHT)
|
|
class_name = "tiled";
|
|
|
|
for (i = 0; i < META_STYLE_ELEMENT_LAST; i++)
|
|
{
|
|
style = style_info->styles[i];
|
|
|
|
state = gtk_style_context_get_state (style);
|
|
if (backdrop)
|
|
gtk_style_context_set_state (style, state | GTK_STATE_FLAG_BACKDROP);
|
|
else
|
|
gtk_style_context_set_state (style, state & ~GTK_STATE_FLAG_BACKDROP);
|
|
|
|
remove_toplevel_class (style, "maximized");
|
|
remove_toplevel_class (style, "tiled");
|
|
|
|
if (class_name)
|
|
add_toplevel_class (style, class_name);
|
|
}
|
|
}
|
|
|
|
PangoFontDescription*
|
|
meta_style_info_create_font_desc (MetaStyleInfo *style_info)
|
|
{
|
|
PangoFontDescription *font_desc;
|
|
const PangoFontDescription *override = meta_prefs_get_titlebar_font ();
|
|
GtkStyleContext *context = style_info->styles[META_STYLE_ELEMENT_TITLE];
|
|
|
|
gtk_style_context_get (context,
|
|
gtk_style_context_get_state (context),
|
|
"font", &font_desc, NULL);
|
|
|
|
if (override)
|
|
pango_font_description_merge (font_desc, override, TRUE);
|
|
|
|
return font_desc;
|
|
}
|
|
|
|
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)
|
|
{
|
|
MetaFrameGeometry fgeom;
|
|
MetaFrameLayout *layout;
|
|
|
|
g_return_if_fail (type < META_FRAME_TYPE_LAST);
|
|
|
|
layout = theme->layouts[type];
|
|
|
|
/* Parser is not supposed to allow this currently */
|
|
if (layout == NULL)
|
|
return;
|
|
|
|
meta_frame_layout_calc_geometry (layout,
|
|
style_info,
|
|
text_height,
|
|
flags,
|
|
client_width, client_height,
|
|
button_layout,
|
|
type,
|
|
&fgeom,
|
|
theme);
|
|
|
|
meta_frame_layout_draw_with_style (layout,
|
|
style_info,
|
|
cr,
|
|
&fgeom,
|
|
title_layout,
|
|
flags,
|
|
button_states,
|
|
mini_icon);
|
|
}
|
|
|
|
void
|
|
meta_theme_get_frame_borders (MetaTheme *theme,
|
|
MetaStyleInfo *style_info,
|
|
MetaFrameType type,
|
|
int text_height,
|
|
MetaFrameFlags flags,
|
|
MetaFrameBorders *borders)
|
|
{
|
|
MetaFrameLayout *layout;
|
|
|
|
g_return_if_fail (type < META_FRAME_TYPE_LAST);
|
|
|
|
layout = theme->layouts[type];
|
|
|
|
meta_frame_borders_clear (borders);
|
|
|
|
/* Parser is not supposed to allow this currently */
|
|
if (layout == NULL)
|
|
return;
|
|
|
|
meta_frame_layout_sync_with_style (layout, style_info, flags);
|
|
|
|
meta_frame_layout_get_borders (layout,
|
|
text_height,
|
|
flags, type,
|
|
borders);
|
|
}
|
|
|
|
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)
|
|
{
|
|
MetaFrameLayout *layout;
|
|
|
|
g_return_if_fail (type < META_FRAME_TYPE_LAST);
|
|
|
|
layout = theme->layouts[type];
|
|
|
|
/* Parser is not supposed to allow this currently */
|
|
if (layout == NULL)
|
|
return;
|
|
|
|
meta_frame_layout_calc_geometry (layout,
|
|
style_info,
|
|
text_height,
|
|
flags,
|
|
client_width, client_height,
|
|
button_layout,
|
|
type,
|
|
fgeom,
|
|
theme);
|
|
}
|
|
|
|
/**
|
|
* meta_pango_font_desc_get_text_height:
|
|
* @font_desc: the font
|
|
* @context: the context of the font
|
|
*
|
|
* Returns the height of the letters in a particular font.
|
|
*
|
|
* Returns: the height of the letters
|
|
*/
|
|
int
|
|
meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc,
|
|
PangoContext *context)
|
|
{
|
|
PangoFontMetrics *metrics;
|
|
PangoLanguage *lang;
|
|
int retval;
|
|
|
|
lang = pango_context_get_language (context);
|
|
metrics = pango_context_get_metrics (context, font_desc, lang);
|
|
|
|
retval = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
|
|
pango_font_metrics_get_descent (metrics));
|
|
|
|
pango_font_metrics_unref (metrics);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* meta_frame_type_to_string:
|
|
* @type: a #MetaFrameType
|
|
*
|
|
* Converts a frame type enum value to the name string that would
|
|
* appear in the theme definition file.
|
|
*
|
|
* Return value: the string value
|
|
*/
|
|
const char*
|
|
meta_frame_type_to_string (MetaFrameType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case META_FRAME_TYPE_NORMAL:
|
|
return "normal";
|
|
case META_FRAME_TYPE_DIALOG:
|
|
return "dialog";
|
|
case META_FRAME_TYPE_MODAL_DIALOG:
|
|
return "modal_dialog";
|
|
case META_FRAME_TYPE_UTILITY:
|
|
return "utility";
|
|
case META_FRAME_TYPE_MENU:
|
|
return "menu";
|
|
case META_FRAME_TYPE_BORDER:
|
|
return "border";
|
|
case META_FRAME_TYPE_ATTACHED:
|
|
return "attached";
|
|
#if 0
|
|
case META_FRAME_TYPE_TOOLBAR:
|
|
return "toolbar";
|
|
#endif
|
|
case META_FRAME_TYPE_LAST:
|
|
break;
|
|
}
|
|
|
|
return "<unknown>";
|
|
}
|