diff options
Diffstat (limited to 'clutter/clutter/clutter-text.c')
-rw-r--r-- | clutter/clutter/clutter-text.c | 365 |
1 files changed, 288 insertions, 77 deletions
diff --git a/clutter/clutter/clutter-text.c b/clutter/clutter/clutter-text.c index 1642c79f6..b6f054863 100644 --- a/clutter/clutter/clutter-text.c +++ b/clutter/clutter/clutter-text.c @@ -144,15 +144,17 @@ struct _ClutterTextPrivate */ gint x_pos; - /* the x position of the PangoLayout when in - * single line mode, to scroll the contents of the + /* the x position of the PangoLayout (in both physical and logical pixels) + * when in single line mode, to scroll the contents of the * text actor */ gint text_x; + gint text_logical_x; - /* the y position of the PangoLayout, fixed to 0 by - * default for now */ + /* the y position of the PangoLayout (in both physical and logical pixels), + * fixed to 0 by default for now */ gint text_y; + gint text_logical_y; /* Where to draw the cursor */ ClutterRect cursor_rect; @@ -185,6 +187,9 @@ struct _ClutterTextPrivate ClutterInputContentHintFlags input_hints; ClutterInputContentPurpose input_purpose; + /* Signal handler for when the :resource-scale changes */ + guint resource_scale_changed_id; + /* bitfields */ guint alignment : 2; guint wrap : 1; @@ -290,6 +295,33 @@ G_DECLARE_FINAL_TYPE (ClutterTextInputFocus, clutter_text_input_focus, G_DEFINE_TYPE (ClutterTextInputFocus, clutter_text_input_focus, CLUTTER_TYPE_INPUT_FOCUS) +/* Utilities pango to (logical) pixels functions */ +static float +pixels_to_pango (float px) +{ + return ceilf (px * (float) PANGO_SCALE); +} + +static float +logical_pixels_to_pango (float px, + float scale) +{ + return pixels_to_pango (px * scale); +} + +static float +pango_to_pixels (float size) +{ + return ceilf (size / (float) PANGO_SCALE); +} + +static float +pango_to_logical_pixels (float size, + float scale) +{ + return pango_to_pixels (size / scale); +} + static void clutter_text_input_focus_request_surrounding (ClutterInputFocus *focus) { @@ -551,6 +583,41 @@ clutter_text_get_display_text (ClutterText *self) } static void +ensure_effective_pango_scale_attribute (ClutterText *self) +{ + float resource_scale; + ClutterTextPrivate *priv = self->priv; + + if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale) || + resource_scale == 1.0) + return; + + if (priv->effective_attrs != NULL) + { + PangoAttrIterator *iter; + PangoAttribute *scale_attrib; + PangoAttrList *old_attributes; + + old_attributes = priv->effective_attrs; + priv->effective_attrs = pango_attr_list_copy (priv->effective_attrs); + pango_attr_list_unref (old_attributes); + + iter = pango_attr_list_get_iterator (priv->effective_attrs); + scale_attrib = pango_attr_iterator_get (iter, PANGO_ATTR_SCALE); + + if (scale_attrib != NULL) + resource_scale *= ((PangoAttrFloat *) scale_attrib)->value; + + pango_attr_iterator_destroy (iter); + } + else + priv->effective_attrs = pango_attr_list_new (); + + pango_attr_list_change (priv->effective_attrs, + pango_attr_scale_new (resource_scale)); +} + +static void set_effective_pango_attributes (ClutterText *self, PangoAttrList *attributes) { @@ -568,6 +635,8 @@ set_effective_pango_attributes (ClutterText *self, { g_clear_pointer (&priv->effective_attrs, pango_attr_list_unref); } + + ensure_effective_pango_scale_attribute (self); } static inline void @@ -836,6 +905,18 @@ clutter_text_direction_changed_cb (GObject *gobject, /* no need to queue a relayout: set_text_direction() will do that for us */ } +static void +clutter_text_resource_scale_changed_cb (GObject *gobject, + GParamSpec *pspec) +{ + ClutterText *self = CLUTTER_TEXT (gobject); + ClutterTextPrivate *priv = self->priv; + + g_clear_pointer (&priv->effective_attrs, pango_attr_list_unref); + clutter_text_dirty_cache (self); + clutter_actor_queue_relayout (CLUTTER_ACTOR (gobject)); +} + /* * clutter_text_create_layout: * @text: a #ClutterText @@ -903,7 +984,7 @@ clutter_text_create_layout (ClutterText *text, !((priv->editable && priv->single_line_mode) || (priv->ellipsize == PANGO_ELLIPSIZE_NONE && !priv->wrap)))) { - width = allocation_width * 1024 + 0.5f; + width = pixels_to_pango (allocation_width); } /* Pango only uses height if ellipsization is enabled, so don't set @@ -920,7 +1001,7 @@ clutter_text_create_layout (ClutterText *text, priv->ellipsize != PANGO_ELLIPSIZE_NONE && !priv->single_line_mode) { - height = allocation_height * 1024 + 0.5f; + height = pixels_to_pango (allocation_height); } /* Search for a cached layout with the same width and keep @@ -1017,6 +1098,37 @@ clutter_text_create_layout (ClutterText *text, return oldest_cache->layout; } +static PangoLayout * +create_text_layout_with_scale (ClutterText *text, + gfloat allocation_width, + gfloat allocation_height, + gfloat scale) +{ + if (allocation_width > 0) + allocation_width = roundf (allocation_width * scale); + + if (allocation_height > 0) + allocation_height = roundf (allocation_height * scale); + + return clutter_text_create_layout (text, allocation_width, allocation_height); +} + +static PangoLayout * +maybe_create_text_layout_with_resource_scale (ClutterText *text, + gfloat allocation_width, + gfloat allocation_height) +{ + float resource_scale; + + if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (text), &resource_scale)) + return NULL; + + return create_text_layout_with_scale (text, + allocation_width, + allocation_height, + resource_scale); +} + /** * clutter_text_coords_to_position: * @self: a #ClutterText @@ -1037,14 +1149,18 @@ clutter_text_coords_to_position (ClutterText *self, gint index_; gint px, py; gint trailing; + gfloat resource_scale; g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); + if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale)) + return 0; + /* Take any offset due to scrolling into account, and normalize * the coordinates to PangoScale units */ - px = (x - self->priv->text_x) * PANGO_SCALE; - py = (y - self->priv->text_y) * PANGO_SCALE; + px = logical_pixels_to_pango (x - self->priv->text_logical_x, resource_scale); + py = logical_pixels_to_pango (y - self->priv->text_logical_y, resource_scale); pango_layout_xy_to_index (clutter_text_get_layout (self), px, py, @@ -1053,26 +1169,12 @@ clutter_text_coords_to_position (ClutterText *self, return index_ + trailing; } -/** - * clutter_text_position_to_coords: - * @self: a #ClutterText - * @position: position in characters - * @x: (out): return location for the X coordinate, or %NULL - * @y: (out): return location for the Y coordinate, or %NULL - * @line_height: (out): return location for the line height, or %NULL - * - * Retrieves the coordinates of the given @position. - * - * Return value: %TRUE if the conversion was successful - * - * Since: 1.0 - */ -gboolean -clutter_text_position_to_coords (ClutterText *self, - gint position, - gfloat *x, - gfloat *y, - gfloat *line_height) +static gboolean +clutter_text_position_to_coords_internal (ClutterText *self, + gint position, + gfloat *x, + gfloat *y, + gfloat *line_height) { ClutterTextPrivate *priv; PangoRectangle rect; @@ -1138,7 +1240,7 @@ clutter_text_position_to_coords (ClutterText *self, if (x) { - *x = (gfloat) rect.x / 1024.0f; + *x = pango_to_pixels (rect.x); /* Take any offset due to scrolling into account */ if (priv->single_line_mode) @@ -1146,14 +1248,58 @@ clutter_text_position_to_coords (ClutterText *self, } if (y) - *y = (gfloat) rect.y / 1024.0f; + *y = pango_to_pixels (rect.y); if (line_height) - *line_height = (gfloat) rect.height / 1024.0f; + *line_height = pango_to_pixels (rect.height); return TRUE; } +/** + * clutter_text_position_to_coords: + * @self: a #ClutterText + * @position: position in characters + * @x: (out): return location for the X coordinate, or %NULL + * @y: (out): return location for the Y coordinate, or %NULL + * @line_height: (out): return location for the line height, or %NULL + * + * Retrieves the coordinates of the given @position. + * + * Return value: %TRUE if the conversion was successful + * + * Since: 1.0 + */ +gboolean +clutter_text_position_to_coords (ClutterText *self, + gint position, + gfloat *x, + gfloat *y, + gfloat *line_height) +{ + gfloat resource_scale; + gboolean ret; + + g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); + + if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale)) + return FALSE; + + ret = clutter_text_position_to_coords_internal (self, position, + x, y, line_height); + + if (x) + *x /= resource_scale; + + if (y) + *y /= resource_scale; + + if (line_height) + *line_height /= resource_scale; + + return ret; +} + static inline void update_cursor_location (ClutterText *self) { @@ -1171,7 +1317,8 @@ update_cursor_location (ClutterText *self) } static inline void -clutter_text_ensure_cursor_position (ClutterText *self) +clutter_text_ensure_cursor_position (ClutterText *self, + float scale) { ClutterTextPrivate *priv = self->priv; gfloat x, y, cursor_height; @@ -1194,15 +1341,15 @@ clutter_text_ensure_cursor_position (ClutterText *self) priv->preedit_set ? priv->preedit_cursor_pos : 0); x = y = cursor_height = 0; - clutter_text_position_to_coords (self, position, - &x, &y, - &cursor_height); + clutter_text_position_to_coords_internal (self, position, + &x, &y, + &cursor_height); clutter_rect_init (&cursor_rect, x, - y + CURSOR_Y_PADDING, - priv->cursor_size, - cursor_height - 2 * CURSOR_Y_PADDING); + y + CURSOR_Y_PADDING * scale, + priv->cursor_size * scale, + cursor_height - 2 * CURSOR_Y_PADDING * scale); if (!clutter_rect_equals (&priv->cursor_rect, &cursor_rect)) { @@ -1625,6 +1772,12 @@ clutter_text_dispose (GObject *gobject) priv->direction_changed_id = 0; } + if (priv->resource_scale_changed_id) + { + g_signal_handler_disconnect (self, priv->resource_scale_changed_id); + priv->resource_scale_changed_id = 0; + } + if (priv->settings_changed_id) { g_signal_handler_disconnect (clutter_get_default_backend (), @@ -1677,6 +1830,7 @@ typedef void (* ClutterTextSelectionFunc) (ClutterText *text, static void clutter_text_foreach_selection_rectangle (ClutterText *self, + float scale, ClutterTextSelectionFunc func, gpointer user_data) { @@ -1728,9 +1882,9 @@ clutter_text_foreach_selection_rectangle (ClutterText *self, &n_ranges); pango_layout_line_x_to_index (line, 0, &index_, NULL); - clutter_text_position_to_coords (self, - bytes_to_offset (utf8, index_), - NULL, &y, &height); + clutter_text_position_to_coords_internal (self, + bytes_to_offset (utf8, index_), + NULL, &y, &height); box.y1 = y; box.y2 = y + height; @@ -1740,18 +1894,18 @@ clutter_text_foreach_selection_rectangle (ClutterText *self, gfloat range_x; gfloat range_width; - range_x = ranges[i * 2] / PANGO_SCALE; + range_x = pango_to_pixels (ranges[i * 2]); /* Account for any scrolling in single line mode */ if (priv->single_line_mode) range_x += priv->text_x; - range_width = ((gfloat) ranges[i * 2 + 1] - (gfloat) ranges[i * 2]) - / PANGO_SCALE; - + range_width = pango_to_pixels (ranges[i * 2 + 1] - ranges[i * 2]); box.x1 = range_x; - box.x2 = ceilf (range_x + range_width + .5f); + box.x2 = ceilf (range_x + range_width); + + clutter_actor_box_scale (&box, scale); func (self, &box, user_data); } @@ -1770,6 +1924,14 @@ add_selection_rectangle_to_path (ClutterText *text, cogl_path_rectangle (user_data, box->x1, box->y1, box->x2, box->y2); } +static void +clutter_text_foreach_selection_rectangle_prescaled (ClutterText *self, + ClutterTextSelectionFunc func, + gpointer user_data) +{ + clutter_text_foreach_selection_rectangle (self, 1.0f, func, user_data); +} + /* Draws the selected text, its background, and the cursor */ static void selection_paint (ClutterText *self, @@ -1825,9 +1987,9 @@ selection_paint (ClutterText *self, else color = &priv->text_color; - clutter_text_foreach_selection_rectangle (self, - add_selection_rectangle_to_path, - selection_path); + clutter_text_foreach_selection_rectangle_prescaled (self, + add_selection_rectangle_to_path, + selection_path); cogl_path_fill (selection_path); @@ -2416,6 +2578,7 @@ clutter_text_paint (ClutterActor *self) guint n_chars; float alloc_width; float alloc_height; + float resource_scale; fb = cogl_get_draw_framebuffer (); @@ -2425,8 +2588,6 @@ clutter_text_paint (ClutterActor *self) n_chars = clutter_text_buffer_get_length (get_buffer (text)); clutter_actor_get_allocation_box (self, &alloc); - alloc_width = alloc.x2 - alloc.x1; - alloc_height = alloc.y2 - alloc.y1; if (G_UNLIKELY (default_color_pipeline == NULL)) { @@ -2459,7 +2620,8 @@ clutter_text_paint (ClutterActor *self) cogl_framebuffer_draw_rectangle (fb, color_pipeline, 0, 0, - alloc_width, alloc_height); + clutter_actor_box_get_width (&alloc), + clutter_actor_box_get_height (&alloc)); cogl_object_unref (color_pipeline); } @@ -2472,6 +2634,12 @@ clutter_text_paint (ClutterActor *self) !clutter_text_should_draw_cursor (text)) return; + if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale)) + return; + + clutter_actor_box_scale (&alloc, resource_scale); + clutter_actor_box_get_size (&alloc, &alloc_width, &alloc_height); + if (priv->editable && priv->single_line_mode) layout = clutter_text_create_layout (text, -1, -1); else @@ -2503,8 +2671,15 @@ clutter_text_paint (ClutterActor *self) } } + if (resource_scale != 1.0f) + { + float paint_scale = 1.0f / resource_scale; + cogl_framebuffer_push_matrix (fb); + cogl_framebuffer_scale (fb, paint_scale, paint_scale, 1.0f); + } + if (clutter_text_should_draw_cursor (text)) - clutter_text_ensure_cursor_position (text); + clutter_text_ensure_cursor_position (text, resource_scale); if (priv->editable && priv->single_line_mode) { @@ -2518,7 +2693,7 @@ clutter_text_paint (ClutterActor *self) clip_set = TRUE; actor_width = alloc_width - 2 * TEXT_PADDING; - text_width = logical_rect.width / PANGO_SCALE; + text_width = pango_to_pixels (logical_rect.width); rtl = priv->resolved_direction == PANGO_DIRECTION_RTL; @@ -2575,8 +2750,10 @@ clutter_text_paint (ClutterActor *self) { priv->text_x = text_x; priv->text_y = text_y; + priv->text_logical_x = roundf ((float) text_x / resource_scale); + priv->text_logical_y = roundf ((float) text_y / resource_scale); - clutter_text_ensure_cursor_position (text); + clutter_text_ensure_cursor_position (text, resource_scale); } real_opacity = clutter_actor_get_paint_opacity (self) @@ -2595,6 +2772,9 @@ clutter_text_paint (ClutterActor *self) selection_paint (text, fb); + if (resource_scale != 1.0f) + cogl_framebuffer_pop_matrix (fb); + if (clip_set) cogl_framebuffer_pop_clip (fb); } @@ -2624,26 +2804,32 @@ add_selection_to_paint_volume (ClutterText *text, static void clutter_text_get_paint_volume_for_cursor (ClutterText *text, + float resource_scale, ClutterPaintVolume *volume) { ClutterTextPrivate *priv = text->priv; ClutterVertex origin; - clutter_text_ensure_cursor_position (text); + clutter_text_ensure_cursor_position (text, resource_scale); if (priv->position == priv->selection_bound) { - origin.x = priv->cursor_rect.origin.x; - origin.y = priv->cursor_rect.origin.y; + float width, height; + + width = priv->cursor_rect.size.width / resource_scale; + height = priv->cursor_rect.size.height / resource_scale; + origin.x = priv->cursor_rect.origin.x / resource_scale; + origin.y = priv->cursor_rect.origin.y / resource_scale; origin.z = 0; clutter_paint_volume_set_origin (volume, &origin); - clutter_paint_volume_set_width (volume, priv->cursor_rect.size.width); - clutter_paint_volume_set_height (volume, priv->cursor_rect.size.height); + clutter_paint_volume_set_width (volume, width); + clutter_paint_volume_set_height (volume, height); } else { clutter_text_foreach_selection_rectangle (text, + 1.0f / resource_scale, add_selection_to_paint_volume, volume); } @@ -2666,6 +2852,7 @@ clutter_text_get_paint_volume (ClutterActor *self, PangoLayout *layout; PangoRectangle ink_rect; ClutterVertex origin; + float resource_scale; /* If the text is single line editable then it gets clipped to the allocation anyway so we can just use that */ @@ -2680,19 +2867,24 @@ clutter_text_get_paint_volume (ClutterActor *self, if (!clutter_actor_has_allocation (self)) return FALSE; + if (!clutter_actor_get_resource_scale (self, &resource_scale)) + return FALSE; + _clutter_paint_volume_init_static (&priv->paint_volume, self); layout = clutter_text_get_layout (text); pango_layout_get_extents (layout, &ink_rect, NULL); - origin.x = ink_rect.x / (float) PANGO_SCALE; - origin.y = ink_rect.y / (float) PANGO_SCALE; + origin.x = pango_to_logical_pixels (ink_rect.x, resource_scale); + origin.y = pango_to_logical_pixels (ink_rect.y, resource_scale); origin.z = 0; clutter_paint_volume_set_origin (&priv->paint_volume, &origin); clutter_paint_volume_set_width (&priv->paint_volume, - ink_rect.width / (float) PANGO_SCALE); + pango_to_logical_pixels (ink_rect.width, + resource_scale)); clutter_paint_volume_set_height (&priv->paint_volume, - ink_rect.height / (float) PANGO_SCALE); + pango_to_logical_pixels (ink_rect.height, + resource_scale)); /* If the cursor is visible then that will likely be drawn outside of the ink rectangle so we should merge that in */ @@ -2702,7 +2894,8 @@ clutter_text_get_paint_volume (ClutterActor *self, _clutter_paint_volume_init_static (&cursor_paint_volume, self); - clutter_text_get_paint_volume_for_cursor (text, &cursor_paint_volume); + clutter_text_get_paint_volume_for_cursor (text, resource_scale, + &cursor_paint_volume); clutter_paint_volume_union (&priv->paint_volume, &cursor_paint_volume); @@ -2730,9 +2923,12 @@ clutter_text_get_preferred_width (ClutterActor *self, PangoLayout *layout; gint logical_width; gfloat layout_width; + gfloat resource_scale; - layout = clutter_text_create_layout (text, -1, -1); + if (!clutter_actor_get_resource_scale (self, &resource_scale)) + resource_scale = 1; + layout = clutter_text_create_layout (text, -1, -1); pango_layout_get_extents (layout, NULL, &logical_rect); /* the X coordinate of the logical rectangle might be non-zero @@ -2742,7 +2938,7 @@ clutter_text_get_preferred_width (ClutterActor *self, logical_width = logical_rect.x + logical_rect.width; layout_width = logical_width > 0 - ? ceilf (logical_width / 1024.0f) + ? pango_to_logical_pixels (logical_width, resource_scale) : 1; if (min_width_p) @@ -2784,12 +2980,16 @@ clutter_text_get_preferred_height (ClutterActor *self, PangoRectangle logical_rect = { 0, }; gint logical_height; gfloat layout_height; + gfloat resource_scale; + + if (!clutter_actor_get_resource_scale (self, &resource_scale)) + resource_scale = 1; if (priv->single_line_mode) for_width = -1; - layout = clutter_text_create_layout (CLUTTER_TEXT (self), - for_width, -1); + layout = create_text_layout_with_scale (CLUTTER_TEXT (self), + for_width, -1, resource_scale); pango_layout_get_extents (layout, NULL, &logical_rect); @@ -2798,7 +2998,7 @@ clutter_text_get_preferred_height (ClutterActor *self, * the height accordingly */ logical_height = logical_rect.y + logical_rect.height; - layout_height = ceilf (logical_height / 1024.0f); + layout_height = pango_to_logical_pixels (logical_height, resource_scale); if (min_height_p) { @@ -2814,7 +3014,8 @@ clutter_text_get_preferred_height (ClutterActor *self, pango_layout_line_get_extents (line, NULL, &logical_rect); logical_height = logical_rect.y + logical_rect.height; - line_height = ceilf (logical_height / 1024.0f); + line_height = pango_to_logical_pixels (logical_height, + resource_scale); *min_height_p = line_height; } @@ -2845,9 +3046,9 @@ clutter_text_allocate (ClutterActor *self, if (text->priv->editable && text->priv->single_line_mode) clutter_text_create_layout (text, -1, -1); else - clutter_text_create_layout (text, - box->x2 - box->x1, - box->y2 - box->y1); + maybe_create_text_layout_with_resource_scale (text, + box->x2 - box->x1, + box->y2 - box->y1); parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class); parent_class->allocate (self, box, flags); @@ -4418,6 +4619,11 @@ clutter_text_init (ClutterText *self) NULL); priv->input_focus = clutter_text_input_focus_new (self); + + priv->resource_scale_changed_id = + g_signal_connect (self, "notify::resource-scale", + G_CALLBACK (clutter_text_resource_scale_changed_cb), + NULL); } /** @@ -5528,6 +5734,7 @@ clutter_text_set_markup (ClutterText *self, PangoLayout * clutter_text_get_layout (ClutterText *self) { + PangoLayout *layout; gfloat width, height; g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); @@ -5536,8 +5743,12 @@ clutter_text_get_layout (ClutterText *self) return clutter_text_create_layout (self, -1, -1); clutter_actor_get_size (CLUTTER_ACTOR (self), &width, &height); + layout = maybe_create_text_layout_with_resource_scale (self, width, height); - return clutter_text_create_layout (self, width, height); + if (!layout) + layout = clutter_text_create_layout (self, width, height); + + return layout; } /** @@ -6541,10 +6752,10 @@ clutter_text_get_layout_offsets (ClutterText *self, priv = self->priv; if (x != NULL) - *x = priv->text_x; + *x = priv->text_logical_x; if (y != NULL) - *y = priv->text_y; + *y = priv->text_logical_y; } /** |