Commit 2ef5844b authored by Paolo Borelli's avatar Paolo Borelli

Introduce gtk_render_insertion_cursor

The new function provides an API that takes the PangoLayout and index
as input params, this way it handles strong and weak cursors internally
factoring out all code duplicated in the widgets that need to render
cursors.

https://bugzilla.gnome.org/show_bug.cgi?id=640317
parent 3c684222
...@@ -5738,6 +5738,7 @@ gtk_render_slider ...@@ -5738,6 +5738,7 @@ gtk_render_slider
gtk_render_activity gtk_render_activity
gtk_render_icon_pixbuf gtk_render_icon_pixbuf
gtk_render_icon gtk_render_icon
gtk_render_insertion_cursor
<SUBSECTION Standard> <SUBSECTION Standard>
GTK_TYPE_STYLE_CONTEXT GTK_TYPE_STYLE_CONTEXT
......
...@@ -2274,6 +2274,7 @@ gtk_render_frame_gap ...@@ -2274,6 +2274,7 @@ gtk_render_frame_gap
gtk_render_handle gtk_render_handle
gtk_render_icon gtk_render_icon
gtk_render_icon_pixbuf gtk_render_icon_pixbuf
gtk_render_insertion_cursor
gtk_render_layout gtk_render_layout
gtk_render_line gtk_render_line
gtk_render_option gtk_render_option
......
...@@ -5848,29 +5848,20 @@ gtk_entry_draw_cursor (GtkEntry *entry, ...@@ -5848,29 +5848,20 @@ gtk_entry_draw_cursor (GtkEntry *entry,
{ {
GtkEntryPrivate *priv = entry->priv; GtkEntryPrivate *priv = entry->priv;
GtkWidget *widget = GTK_WIDGET (entry); GtkWidget *widget = GTK_WIDGET (entry);
GdkKeymap *keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget)); GtkStyleContext *context;
PangoDirection keymap_direction = gdk_keymap_get_direction (keymap);
GdkRectangle cursor_location;
gboolean split_cursor;
PangoRectangle cursor_rect; PangoRectangle cursor_rect;
GtkBorder inner_border;
gint xoffset;
gint text_area_height;
gint cursor_index; gint cursor_index;
gboolean block; gboolean block;
gboolean block_at_line_end; gboolean block_at_line_end;
PangoLayout *layout; PangoLayout *layout;
const char *text; const char *text;
_gtk_entry_effective_inner_border (entry, &inner_border); context = gtk_widget_get_style_context (widget);
xoffset = inner_border.left - priv->scroll_offset;
text_area_height = gdk_window_get_height (priv->text_area);
layout = gtk_entry_ensure_layout (entry, TRUE); layout = gtk_entry_ensure_layout (entry, TRUE);
text = pango_layout_get_text (layout); text = pango_layout_get_text (layout);
cursor_index = g_utf8_offset_to_pointer (text, priv->current_pos + priv->preedit_cursor) - text; cursor_index = g_utf8_offset_to_pointer (text, priv->current_pos + priv->preedit_cursor) - text;
if (!priv->overwrite_mode) if (!priv->overwrite_mode)
block = FALSE; block = FALSE;
else else
...@@ -5879,58 +5870,16 @@ gtk_entry_draw_cursor (GtkEntry *entry, ...@@ -5879,58 +5870,16 @@ gtk_entry_draw_cursor (GtkEntry *entry,
if (!block) if (!block)
{ {
gint strong_x, weak_x; GtkBorder inner_border;
GtkTextDirection dir1, dir2;
gint x1 = 0;
gint x2 = 0;
gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x);
g_object_get (gtk_widget_get_settings (widget),
"gtk-split-cursor", &split_cursor,
NULL);
dir1 = (priv->resolved_dir == PANGO_DIRECTION_LTR) ? GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
dir2 = GTK_TEXT_DIR_NONE;
if (split_cursor)
{
x1 = strong_x;
if (weak_x != strong_x)
{
dir2 = (priv->resolved_dir == PANGO_DIRECTION_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
x2 = weak_x;
}
}
else
{
if (keymap_direction == priv->resolved_dir)
x1 = strong_x;
else
x1 = weak_x;
}
cursor_location.x = xoffset + x1;
cursor_location.y = inner_border.top;
cursor_location.width = 0;
cursor_location.height = text_area_height - inner_border.top - inner_border.bottom;
gtk_draw_insertion_cursor (widget, cr, _gtk_entry_effective_inner_border (entry, &inner_border);
&cursor_location, TRUE, dir1,
dir2 != GTK_TEXT_DIR_NONE);
if (dir2 != GTK_TEXT_DIR_NONE) gtk_render_insertion_cursor (context, cr,
{ inner_border.left - priv->scroll_offset, inner_border.top,
cursor_location.x = xoffset + x2; layout, cursor_index, priv->resolved_dir);
gtk_draw_insertion_cursor (widget, cr,
&cursor_location, FALSE, dir2,
TRUE);
}
} }
else /* overwrite_mode */ else /* overwrite_mode */
{ {
GtkStyleContext *context;
GdkRGBA cursor_color; GdkRGBA cursor_color;
GdkRectangle rect; GdkRectangle rect;
gint x, y; gint x, y;
...@@ -5944,8 +5893,6 @@ gtk_entry_draw_cursor (GtkEntry *entry, ...@@ -5944,8 +5893,6 @@ gtk_entry_draw_cursor (GtkEntry *entry,
rect.width = PANGO_PIXELS (cursor_rect.width); rect.width = PANGO_PIXELS (cursor_rect.width);
rect.height = PANGO_PIXELS (cursor_rect.height); rect.height = PANGO_PIXELS (cursor_rect.height);
context = gtk_widget_get_style_context (widget);
_gtk_style_context_get_cursor_color (context, &cursor_color, NULL); _gtk_style_context_get_cursor_color (context, &cursor_color, NULL);
gdk_cairo_set_source_rgba (cr, &cursor_color); gdk_cairo_set_source_rgba (cr, &cursor_color);
gdk_cairo_rectangle (cr, &rect); gdk_cairo_rectangle (cr, &rect);
......
...@@ -3978,84 +3978,6 @@ get_cursor_direction (GtkLabel *label) ...@@ -3978,84 +3978,6 @@ get_cursor_direction (GtkLabel *label)
return PANGO_DIRECTION_LTR; return PANGO_DIRECTION_LTR;
} }
static void
gtk_label_draw_cursor (GtkLabel *label, cairo_t *cr, gint xoffset, gint yoffset)
{
GtkLabelPrivate *priv = label->priv;
GtkWidget *widget;
if (priv->select_info == NULL)
return;
widget = GTK_WIDGET (label);
if (gtk_widget_is_drawable (widget))
{
PangoDirection keymap_direction;
PangoDirection cursor_direction;
PangoRectangle strong_pos, weak_pos;
GtkTextDirection dir1, dir2;
gboolean split_cursor;
PangoRectangle *cursor1 = NULL;
PangoRectangle *cursor2 = NULL;
GdkRectangle cursor_location;
keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget)));
cursor_direction = get_cursor_direction (label);
gtk_label_ensure_layout (label);
pango_layout_get_cursor_pos (priv->layout, priv->select_info->selection_end,
&strong_pos, &weak_pos);
g_object_get (gtk_widget_get_settings (widget),
"gtk-split-cursor", &split_cursor,
NULL);
dir1 = (cursor_direction == PANGO_DIRECTION_LTR) ? GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
dir2 = GTK_TEXT_DIR_NONE;
if (split_cursor)
{
cursor1 = &strong_pos;
if (strong_pos.x != weak_pos.x || strong_pos.y != weak_pos.y)
{
dir2 = (cursor_direction == PANGO_DIRECTION_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
cursor2 = &weak_pos;
}
}
else
{
if (keymap_direction == cursor_direction)
cursor1 = &strong_pos;
else
cursor1 = &weak_pos;
}
cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
cursor_location.width = 0;
cursor_location.height = PANGO_PIXELS (cursor1->height);
gtk_draw_insertion_cursor (widget, cr,
&cursor_location, TRUE, dir1,
dir2 != GTK_TEXT_DIR_NONE);
if (dir2 != GTK_TEXT_DIR_NONE)
{
cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
cursor_location.width = 0;
cursor_location.height = PANGO_PIXELS (cursor2->height);
gtk_draw_insertion_cursor (widget, cr,
&cursor_location, FALSE, dir2,
TRUE);
}
}
}
static GtkLabelLink * static GtkLabelLink *
gtk_label_get_focus_link (GtkLabel *label) gtk_label_get_focus_link (GtkLabel *label)
{ {
...@@ -4167,8 +4089,18 @@ gtk_label_draw (GtkWidget *widget, ...@@ -4167,8 +4089,18 @@ gtk_label_draw (GtkWidget *widget,
GdkColor *link_color; GdkColor *link_color;
GdkColor *visited_link_color; GdkColor *visited_link_color;
if (info->selectable && gtk_widget_has_focus (widget)) if (info->selectable &&
gtk_label_draw_cursor (label, cr, x, y); gtk_widget_has_focus (widget) &&
gtk_widget_is_drawable (widget))
{
PangoDirection cursor_direction;
cursor_direction = get_cursor_direction (label);
gtk_render_insertion_cursor (context, cr,
x, y,
priv->layout, priv->select_info->selection_end,
cursor_direction);
}
focus_link = gtk_label_get_focus_link (label); focus_link = gtk_label_get_focus_link (label);
active_link = info->active_link; active_link = info->active_link;
......
...@@ -4350,45 +4350,24 @@ gtk_render_icon (GtkStyleContext *context, ...@@ -4350,45 +4350,24 @@ gtk_render_icon (GtkStyleContext *context,
cairo_restore (cr); cairo_restore (cr);
} }
/** static void
* gtk_draw_insertion_cursor: draw_insertion_cursor (GtkStyleContext *context,
* @widget: a #GtkWidget cairo_t *cr,
* @cr: cairo context to draw to gdouble x,
* @location: location where to draw the cursor (@location->width is ignored) gdouble y,
* @is_primary: if the cursor should be the primary cursor color. gdouble height,
* @direction: whether the cursor is left-to-right or gboolean is_primary,
* right-to-left. Should never be #GTK_TEXT_DIR_NONE PangoDirection direction,
* @draw_arrow: %TRUE to draw a directional arrow on the gboolean draw_arrow)
* cursor. Should be %FALSE unless the cursor is split.
*
* Draws a text caret on @cr at @location. This is not a style function
* but merely a convenience function for drawing the standard cursor shape.
*
* Since: 3.0
*/
void
gtk_draw_insertion_cursor (GtkWidget *widget,
cairo_t *cr,
const GdkRectangle *location,
gboolean is_primary,
GtkTextDirection direction,
gboolean draw_arrow)
{ {
gint stem_width;
gint arrow_width;
gint x, y;
gfloat cursor_aspect_ratio;
gint offset;
GtkStyleContext *context;
GdkRGBA primary_color; GdkRGBA primary_color;
GdkRGBA secondary_color; GdkRGBA secondary_color;
gfloat cursor_aspect_ratio;
gint stem_width;
gint offset;
g_return_if_fail (GTK_IS_WIDGET (widget)); cairo_save (cr);
g_return_if_fail (cr != NULL);
g_return_if_fail (location != NULL);
g_return_if_fail (direction != GTK_TEXT_DIR_NONE);
context = gtk_widget_get_style_context (widget);
_gtk_style_context_get_cursor_color (context, &primary_color, &secondary_color); _gtk_style_context_get_cursor_color (context, &primary_color, &secondary_color);
gdk_cairo_set_source_rgba (cr, is_primary ? &primary_color : &secondary_color); gdk_cairo_set_source_rgba (cr, is_primary ? &primary_color : &secondary_color);
...@@ -4401,43 +4380,176 @@ gtk_draw_insertion_cursor (GtkWidget *widget, ...@@ -4401,43 +4380,176 @@ gtk_draw_insertion_cursor (GtkWidget *widget,
"cursor-aspect-ratio", &cursor_aspect_ratio, "cursor-aspect-ratio", &cursor_aspect_ratio,
NULL); NULL);
stem_width = location->height * cursor_aspect_ratio + 1; stem_width = height * cursor_aspect_ratio + 1;
arrow_width = stem_width + 1;
/* put (stem_width % 2) on the proper side of the cursor */ /* put (stem_width % 2) on the proper side of the cursor */
if (direction == GTK_TEXT_DIR_LTR) if (direction == PANGO_DIRECTION_LTR)
offset = stem_width / 2; offset = stem_width / 2;
else else
offset = stem_width - stem_width / 2; offset = stem_width - stem_width / 2;
cairo_rectangle (cr, cairo_rectangle (cr, x - offset, y, stem_width, height);
location->x - offset, location->y,
stem_width, location->height);
cairo_fill (cr); cairo_fill (cr);
if (draw_arrow) if (draw_arrow)
{ {
if (direction == GTK_TEXT_DIR_RTL) gint arrow_width;
gint ax, ay;
arrow_width = stem_width + 1;
if (direction == PANGO_DIRECTION_RTL)
{ {
x = location->x - offset - 1; ax = x - offset - 1;
y = location->y + location->height - arrow_width * 2 - arrow_width + 1; ay = y + height - arrow_width * 2 - arrow_width + 1;
cairo_move_to (cr, x, y + 1); cairo_move_to (cr, ax, ay + 1);
cairo_line_to (cr, x - arrow_width, y + arrow_width); cairo_line_to (cr, ax - arrow_width, ay + arrow_width);
cairo_line_to (cr, x, y + 2 * arrow_width); cairo_line_to (cr, ax, ay + 2 * arrow_width);
cairo_fill (cr); cairo_fill (cr);
} }
else if (direction == GTK_TEXT_DIR_LTR) else if (direction == PANGO_DIRECTION_LTR)
{ {
x = location->x + stem_width - offset; ax = x + stem_width - offset;
y = location->y + location->height - arrow_width * 2 - arrow_width + 1; ay = y + height - arrow_width * 2 - arrow_width + 1;
cairo_move_to (cr, x, y + 1); cairo_move_to (cr, ax, ay + 1);
cairo_line_to (cr, x + arrow_width, y + arrow_width); cairo_line_to (cr, ax + arrow_width, ay + arrow_width);
cairo_line_to (cr, x, y + 2 * arrow_width); cairo_line_to (cr, ax, ay + 2 * arrow_width);
cairo_fill (cr); cairo_fill (cr);
} }
else
g_assert_not_reached();
} }
cairo_restore (cr);
}
/**
* gtk_render_insertion_cursor:
* @context: a #GtkStyleContext
* @cr: a #cairo_t
* @x: X origin
* @y: Y origin
* @layout: the #PangoLayout of the text
* @index: the index in the #PangoLayout
* @direction: the #PangoDirection of the text
*
* Draws a text caret on @cr at the specified index of @layout.
*
* Since: 3.4
**/
void
gtk_render_insertion_cursor (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
PangoLayout *layout,
int index,
PangoDirection direction)
{
GtkStyleContextPrivate *priv;
gboolean split_cursor;
PangoRectangle strong_pos, weak_pos;
PangoRectangle *cursor1, *cursor2;
PangoDirection keymap_direction;
PangoDirection direction2;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
g_return_if_fail (PANGO_IS_LAYOUT (layout));
g_return_if_fail (index >= 0);
priv = context->priv;
g_object_get (gtk_settings_get_for_screen (priv->screen),
"gtk-split-cursor", &split_cursor,
NULL);
keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gdk_screen_get_display (priv->screen)));
pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
direction2 = PANGO_DIRECTION_NEUTRAL;
if (split_cursor)
{
cursor1 = &strong_pos;
if (strong_pos.x != weak_pos.x || strong_pos.y != weak_pos.y)
{
direction2 = (direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
cursor2 = &weak_pos;
}
}
else
{
if (keymap_direction == direction)
cursor1 = &strong_pos;
else
cursor1 = &weak_pos;
}
draw_insertion_cursor (context,
cr,
x + PANGO_PIXELS (cursor1->x),
y + PANGO_PIXELS (cursor1->y),
PANGO_PIXELS (cursor1->height),
TRUE,
direction,
direction2 != PANGO_DIRECTION_NEUTRAL);
if (direction2 != PANGO_DIRECTION_NEUTRAL)
{
draw_insertion_cursor (context,
cr,
x + PANGO_PIXELS (cursor2->x),
y + PANGO_PIXELS (cursor2->y),
PANGO_PIXELS (cursor2->height),
FALSE,
direction2,
TRUE);
}
}
/**
* gtk_draw_insertion_cursor:
* @widget: a #GtkWidget
* @cr: cairo context to draw to
* @location: location where to draw the cursor (@location->width is ignored)
* @is_primary: if the cursor should be the primary cursor color.
* @direction: whether the cursor is left-to-right or
* right-to-left. Should never be #GTK_TEXT_DIR_NONE
* @draw_arrow: %TRUE to draw a directional arrow on the
* cursor. Should be %FALSE unless the cursor is split.
*
* Draws a text caret on @cr at @location. This is not a style function
* but merely a convenience function for drawing the standard cursor shape.
*
* Since: 3.0
*/
void
gtk_draw_insertion_cursor (GtkWidget *widget,
cairo_t *cr,
const GdkRectangle *location,
gboolean is_primary,
GtkTextDirection direction,
gboolean draw_arrow)
{
GtkStyleContext *context;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (cr != NULL);
g_return_if_fail (location != NULL);
g_return_if_fail (direction != GTK_TEXT_DIR_NONE);
context = gtk_widget_get_style_context (widget);
draw_insertion_cursor (context, cr,
location->x, location->y, location->height,
is_primary,
(direction == GTK_TEXT_DIR_RTL) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR,
draw_arrow);
} }
static AtkAttributeSet * static AtkAttributeSet *
......
...@@ -922,6 +922,14 @@ void gtk_render_icon (GtkStyleContext *context, ...@@ -922,6 +922,14 @@ void gtk_render_icon (GtkStyleContext *context,
GdkPixbuf *pixbuf, GdkPixbuf *pixbuf,
gdouble x, gdouble x,
gdouble y); gdouble y);
void gtk_render_insertion_cursor
(GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
PangoLayout *layout,
int index,
PangoDirection direction);
void gtk_draw_insertion_cursor (GtkWidget *widget, void gtk_draw_insertion_cursor (GtkWidget *widget,
cairo_t *cr, cairo_t *cr,
......
...@@ -821,6 +821,7 @@ gtk_text_layout_draw (GtkTextLayout *layout, ...@@ -821,6 +821,7 @@ gtk_text_layout_draw (GtkTextLayout *layout,
cairo_t *cr, cairo_t *cr,
GList **widgets) GList **widgets)
{ {
GtkStyleContext *context;
gint offset_y; gint offset_y;
GtkTextRenderer *text_renderer; GtkTextRenderer *text_renderer;
GtkTextIter selection_start, selection_end; GtkTextIter selection_start, selection_end;
...@@ -838,6 +839,8 @@ gtk_text_layout_draw (GtkTextLayout *layout, ...@@ -838,6 +839,8 @@ gtk_text_layout_draw (GtkTextLayout *layout,
if (!gdk_cairo_get_clip_rectangle (cr, &clip)) if (!gdk_cairo_get_clip_rectangle (cr, &clip))
return; return;
context = gtk_widget_get_style_context (widget);
line_list = gtk_text_layout_get_lines (layout, clip.y, clip.y + clip.height, &offset_y); line_list = gtk_text_layout_get_lines (layout, clip.y, clip.y + clip.height, &offset_y);
if (line_list == NULL) if (line_list == NULL)
...@@ -911,63 +914,17 @@ gtk_text_layout_draw (GtkTextLayout *layout, ...@@ -911,63 +914,17 @@ gtk_text_layout_draw (GtkTextLayout *layout,
for (i = 0; i < line_display->cursors->len; i++) for (i = 0; i < line_display->cursors->len; i++)
{ {
int index; int index;
PangoRectangle strong_pos, weak_pos; PangoDirection dir;
PangoRectangle *cursor1, *cursor2;
gboolean split_cursor;
GtkTextDirection dir1, dir2;
GdkRectangle cursor_location;
index = g_array_index(line_display->cursors, int, i); index = g_array_index(line_display->cursors, int, i);
pango_layout_get_cursor_pos (line_display->layout, index, &strong_pos, &weak_pos); dir = (line_display->direction == GTK_TEXT_DIR_RTL) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
gtk_render_insertion_cursor (context, cr,
dir1 = line_display->direction; line_display->x_offset, line_display->top_margin,
dir2 = GTK_TEXT_DIR_NONE; line_display->layout, index, dir);
g_object_get (gtk_widget_get_settings (widget),
"gtk-split-cursor", &split_cursor,
NULL);
if (split_cursor)
{
cursor1 = &strong_pos;
if (strong_pos.x != weak_pos.x || strong_pos.y != weak_pos.y)
{
dir2 = (line_display->direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
cursor2 = &weak_pos;
}
}
else
{
if (layout->keyboard_direction == line_display->direction)
cursor1 = &strong_pos;
else
cursor1 = &weak_pos;
}
cursor_location.x = line_display->x_offset + PANGO_PIXELS (cursor1->x);
cursor_location.y = line_display->top_margin + PANGO_PIXELS (cursor1->y);
cursor_location.width = 0;
cursor_location.height = PANGO_PIXELS (cursor1->height);
gtk_draw_insertion_cursor (widget, cr,
&cursor_location, TRUE, dir1,
dir2 != GTK_TEXT_DIR_NONE);
if (dir2 != GTK_TEXT_DIR_NONE)
{
cursor_location.x = line_display->x_offset + PANGO_PIXELS (cursor2->x);
cursor_location.y = line_display->top_margin + PANGO_PIXELS (cursor2->y);
cursor_location.width = 0;
cursor_location.height = PANGO_PIXELS (cursor2->height);
gtk_draw_insertion_cursor (widget, cr,
&cursor_location, FALSE, dir2,
TRUE);
}
} }
} }
} /* line_display->height > 0 */ } /* line_display->height > 0 */
cairo_translate (cr, 0, line_display->height); cairo_translate (cr, 0, line_display->height);
gtk_text_layout_free_line_display (layout, line_display); gtk_text_layout_free_line_display (layout, line_display);
......
...@@ -8622,7 +8622,7 @@ text_window_invalidate_cursors (GtkTextWindow *win) ...@@ -8622,7 +8622,7 @@ text_window_invalidate_cursors (GtkTextWindow *win)
gtk_text_layout_get_cursor_locations (priv->layout, &iter, gtk_text_layout_get_cursor_locations (priv->layout, &iter,
&strong, &weak); &strong, &weak);
/* cursor width calculation as in gtkstyle.c:draw_insertion_cursor(), /* cursor width calculation as in gtkstylecontext.c:draw_insertion_cursor(),
* ignoring the text direction be exposing both sides of the cursor * ignoring the text direction be exposing both sides of the cursor
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment