theme: Scale window decorations on HiDPI displays

As we opt out of GTK+/Clutter's HiDPI handling, we need to apply the
window scaling factor manually to decorations, both the geometry and
when drawing.

https://bugzilla.gnome.org/show_bug.cgi?id=744354
This commit is contained in:
Florian Müllner 2014-10-02 01:05:18 +02:00
parent 3a2920d4bc
commit 57c1078ee7
3 changed files with 84 additions and 30 deletions

View File

@ -1389,6 +1389,7 @@ meta_ui_frame_get_mask (MetaUIFrame *frame,
MetaFrameBorders borders;
MetaFrameFlags flags;
MetaRectangle frame_rect;
int scale = meta_theme_get_window_scaling_factor ();
meta_window_get_frame_rect (frame->meta_window, &frame_rect);
@ -1396,9 +1397,13 @@ meta_ui_frame_get_mask (MetaUIFrame *frame,
meta_style_info_set_flags (frame->style_info, flags);
meta_ui_frame_get_borders (frame, &borders);
/* See comment in meta_frame_layout_draw_with_style() for details on HiDPI handling */
cairo_scale (cr, scale, scale);
gtk_render_background (frame->style_info->styles[META_STYLE_ELEMENT_FRAME], cr,
borders.invisible.left, borders.invisible.top,
frame_rect.width, frame_rect.height);
borders.invisible.left / scale,
borders.invisible.top / scale,
frame_rect.width / scale, frame_rect.height / scale);
}
/* XXX -- this is disgusting. Find a better approach here.

View File

@ -276,5 +276,6 @@ void meta_theme_calc_geometry (MetaTheme *theme,
int meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc,
PangoContext *context);
int meta_theme_get_window_scaling_factor (void);
#endif /* META_THEME_PRIVATE_H */

View File

@ -30,6 +30,8 @@
#define DEBUG_FILL_STRUCT(s) memset ((s), 0xef, sizeof (*(s)))
static void scale_border (GtkBorder *border, double factor);
static MetaFrameLayout *
meta_frame_layout_new (void)
{
@ -65,6 +67,7 @@ meta_frame_layout_get_borders (const MetaFrameLayout *layout,
MetaFrameBorders *borders)
{
int buttons_height, content_height, draggable_borders;
int scale = meta_theme_get_window_scaling_factor ();
meta_frame_borders_clear (borders);
@ -110,6 +113,26 @@ meta_frame_layout_get_borders (const MetaFrameLayout *layout,
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 ()
{
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
@ -117,8 +140,8 @@ meta_frame_layout_apply_scale (const MetaFrameLayout *layout,
PangoFontDescription *font_desc)
{
int size = pango_font_description_get_size (font_desc);
pango_font_description_set_size (font_desc,
MAX (size * layout->title_scale, 1));
double scale = layout->title_scale / meta_theme_get_window_scaling_factor ();
pango_font_description_set_size (font_desc, MAX (size * scale, 1));
}
static MetaButtonSpace*
@ -306,6 +329,7 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
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
@ -325,11 +349,12 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
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;
fgeom->content_border.right += layout->titlebar_border.right;
fgeom->content_border.top += layout->titlebar_border.top;
fgeom->content_border.bottom += layout->titlebar_border.bottom;
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;
@ -349,6 +374,8 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
layout->button_border.left + layout->button_border.right;
button_height = layout->icon_size +
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
@ -408,11 +435,11 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
space_used_by_buttons += button_width * n_left;
space_used_by_buttons += (button_width * 0.75) * n_left_spacers;
space_used_by_buttons += layout->titlebar_spacing * MAX (n_left - 1, 0);
space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_left - 1, 0);
space_used_by_buttons += button_width * n_right;
space_used_by_buttons += (button_width * 0.75) * n_right_spacers;
space_used_by_buttons += layout->titlebar_spacing * MAX (n_right - 1, 0);
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 */
@ -564,7 +591,7 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
x = rect->visible.x + rect->visible.width;
if (i < n_left - 1)
x += layout->titlebar_spacing;
x += layout->titlebar_spacing * scale;
if (left_buttons_has_spacer[i])
x += (button_width * 0.75);
}
@ -587,7 +614,7 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
if (flags & META_FRAME_SHADED)
min_size_for_rounding = 0;
else
min_size_for_rounding = 5;
min_size_for_rounding = 5 * scale;
fgeom->top_left_corner_rounded_radius = 0;
fgeom->top_right_corner_rounded_radius = 0;
@ -595,14 +622,14 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
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;
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;
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;
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;
fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius * scale;
}
static void
@ -696,13 +723,27 @@ meta_frame_layout_draw_with_style (MetaFrameLayout *layout,
GdkRectangle titlebar_rect;
GdkRectangle button_rect;
const MetaFrameBorders *borders;
int scale = meta_theme_get_window_scaling_factor ();
/* 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
*/
cairo_scale (cr, scale, scale);
borders = &fgeom->borders;
visible_rect.x = borders->invisible.left;
visible_rect.y = borders->invisible.top;
visible_rect.width = fgeom->width - borders->invisible.left - borders->invisible.right;
visible_rect.height = fgeom->height - borders->invisible.top - borders->invisible.bottom;
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);
@ -717,7 +758,7 @@ meta_frame_layout_draw_with_style (MetaFrameLayout *layout,
titlebar_rect.x = visible_rect.x;
titlebar_rect.y = visible_rect.y;
titlebar_rect.width = visible_rect.width;
titlebar_rect.height = borders->visible.top;
titlebar_rect.height = borders->visible.top / scale;
style = style_info->styles[META_STYLE_ELEMENT_TITLEBAR];
gtk_render_background (style, cr,
@ -735,7 +776,7 @@ meta_frame_layout_draw_with_style (MetaFrameLayout *layout,
pango_layout_set_width (title_layout, -1);
pango_layout_get_pixel_extents (title_layout, NULL, &logical);
text_width = MIN(fgeom->title_rect.width, logical.width);
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);
@ -744,10 +785,10 @@ meta_frame_layout_draw_with_style (MetaFrameLayout *layout,
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)
x = fgeom->title_rect.x;
else if (x + text_width > fgeom->title_rect.x + fgeom->title_rect.width)
x = fgeom->title_rect.x + fgeom->title_rect.width - text_width;
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);
@ -763,6 +804,11 @@ meta_frame_layout_draw_with_style (MetaFrameLayout *layout,
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)
@ -815,9 +861,10 @@ meta_frame_layout_draw_with_style (MetaFrameLayout *layout,
GtkIconInfo *info;
GdkPixbuf *pixbuf;
info = gtk_icon_theme_lookup_icon (theme, icon_name, layout->icon_size, 0);
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, 1, NULL);
surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
}
if (surface)
@ -825,8 +872,8 @@ meta_frame_layout_draw_with_style (MetaFrameLayout *layout,
float width, height;
int x, y;
width = cairo_image_surface_get_width (surface);
height = cairo_image_surface_get_height (surface);
width = cairo_image_surface_get_width (surface) / scale;
height = cairo_image_surface_get_height (surface) / scale;
x = button_rect.x + (button_rect.width - width) / 2;
y = button_rect.y + (button_rect.height - height) / 2;
@ -939,6 +986,7 @@ create_style_context (GType widget_type,
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)