forked from AuroraMiddleware/gtk
text view: Smooth cursor blinking
Fade the text cursor in and out, instead of abruptly turning it on and off.
This commit is contained in:
parent
4ff9163c47
commit
064ad42432
@ -3795,7 +3795,8 @@ render_para (GskPangoRenderer *crenderer,
|
|||||||
int offset_y,
|
int offset_y,
|
||||||
GtkTextLineDisplay *line_display,
|
GtkTextLineDisplay *line_display,
|
||||||
int selection_start_index,
|
int selection_start_index,
|
||||||
int selection_end_index)
|
int selection_end_index,
|
||||||
|
float cursor_alpha)
|
||||||
{
|
{
|
||||||
GtkStyleContext *context;
|
GtkStyleContext *context;
|
||||||
PangoLayout *layout = line_display->layout;
|
PangoLayout *layout = line_display->layout;
|
||||||
@ -3972,6 +3973,7 @@ render_para (GskPangoRenderer *crenderer,
|
|||||||
* (normally white on black) */
|
* (normally white on black) */
|
||||||
_gtk_style_context_get_cursor_color (context, &cursor_color, NULL);
|
_gtk_style_context_get_cursor_color (context, &cursor_color, NULL);
|
||||||
|
|
||||||
|
gtk_snapshot_push_opacity (crenderer->snapshot, cursor_alpha);
|
||||||
gtk_snapshot_append_color (crenderer->snapshot, &cursor_color, &bounds);
|
gtk_snapshot_append_color (crenderer->snapshot, &cursor_color, &bounds);
|
||||||
|
|
||||||
/* draw text under the cursor if any */
|
/* draw text under the cursor if any */
|
||||||
@ -3985,6 +3987,7 @@ render_para (GskPangoRenderer *crenderer,
|
|||||||
baseline);
|
baseline);
|
||||||
gtk_snapshot_pop (crenderer->snapshot);
|
gtk_snapshot_pop (crenderer->snapshot);
|
||||||
}
|
}
|
||||||
|
gtk_snapshot_pop (crenderer->snapshot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4002,7 +4005,8 @@ void
|
|||||||
gtk_text_layout_snapshot (GtkTextLayout *layout,
|
gtk_text_layout_snapshot (GtkTextLayout *layout,
|
||||||
GtkWidget *widget,
|
GtkWidget *widget,
|
||||||
GtkSnapshot *snapshot,
|
GtkSnapshot *snapshot,
|
||||||
const GdkRectangle *clip)
|
const GdkRectangle *clip,
|
||||||
|
float cursor_alpha)
|
||||||
{
|
{
|
||||||
GskPangoRenderer *crenderer;
|
GskPangoRenderer *crenderer;
|
||||||
GtkStyleContext *context;
|
GtkStyleContext *context;
|
||||||
@ -4085,7 +4089,8 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
|
|||||||
}
|
}
|
||||||
|
|
||||||
render_para (crenderer, offset_y, line_display,
|
render_para (crenderer, offset_y, line_display,
|
||||||
selection_start_index, selection_end_index);
|
selection_start_index, selection_end_index,
|
||||||
|
cursor_alpha);
|
||||||
|
|
||||||
/* We paint the cursors last, because they overlap another chunk
|
/* We paint the cursors last, because they overlap another chunk
|
||||||
* and need to appear on top.
|
* and need to appear on top.
|
||||||
@ -4094,6 +4099,7 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
gtk_snapshot_push_opacity (crenderer->snapshot, cursor_alpha);
|
||||||
for (i = 0; i < line_display->cursors->len; i++)
|
for (i = 0; i < line_display->cursors->len; i++)
|
||||||
{
|
{
|
||||||
int index;
|
int index;
|
||||||
@ -4106,6 +4112,8 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
|
|||||||
line_display->x_offset, offset_y + line_display->top_margin,
|
line_display->x_offset, offset_y + line_display->top_margin,
|
||||||
line_display->layout, index, dir);
|
line_display->layout, index, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gtk_snapshot_pop (crenderer->snapshot);
|
||||||
}
|
}
|
||||||
} /* line_display->height > 0 */
|
} /* line_display->height > 0 */
|
||||||
|
|
||||||
|
@ -400,7 +400,8 @@ void gtk_text_layout_spew (GtkTextLayout *layout);
|
|||||||
void gtk_text_layout_snapshot (GtkTextLayout *layout,
|
void gtk_text_layout_snapshot (GtkTextLayout *layout,
|
||||||
GtkWidget *widget,
|
GtkWidget *widget,
|
||||||
GtkSnapshot *snapshot,
|
GtkSnapshot *snapshot,
|
||||||
const GdkRectangle *clip);
|
const GdkRectangle *clip,
|
||||||
|
float cursor_alpha);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
@ -335,7 +335,7 @@ gtk_text_util_create_rich_drag_icon (GtkWidget *widget,
|
|||||||
|
|
||||||
snapshot = gtk_snapshot_new ();
|
snapshot = gtk_snapshot_new ();
|
||||||
|
|
||||||
gtk_text_layout_snapshot (layout, widget, snapshot, &(GdkRectangle) { 0, 0, layout_width, layout_height });
|
gtk_text_layout_snapshot (layout, widget, snapshot, &(GdkRectangle) { 0, 0, layout_width, layout_height }, 1.0);
|
||||||
|
|
||||||
g_object_unref (layout);
|
g_object_unref (layout);
|
||||||
g_object_unref (new_buffer);
|
g_object_unref (new_buffer);
|
||||||
|
@ -209,7 +209,10 @@ struct _GtkTextViewPrivate
|
|||||||
GtkTextMark *first_para_mark; /* Mark at the beginning of the first onscreen paragraph */
|
GtkTextMark *first_para_mark; /* Mark at the beginning of the first onscreen paragraph */
|
||||||
gint first_para_pixels; /* Offset of top of screen in the first onscreen paragraph */
|
gint first_para_pixels; /* Offset of top of screen in the first onscreen paragraph */
|
||||||
|
|
||||||
guint blink_timeout;
|
guint64 blink_start_time;
|
||||||
|
guint blink_tick;
|
||||||
|
float cursor_alpha;
|
||||||
|
|
||||||
guint scroll_timeout;
|
guint scroll_timeout;
|
||||||
|
|
||||||
guint first_validate_idle; /* Idle to revalidate onscreen portion, runs before resize */
|
guint first_validate_idle; /* Idle to revalidate onscreen portion, runs before resize */
|
||||||
@ -5346,12 +5349,6 @@ gtk_text_view_paint (GtkWidget *widget,
|
|||||||
g_warning (G_STRLOC ": somehow some text lines were modified or scrolling occurred since the last validation of lines on the screen - may be a text widget bug.");
|
g_warning (G_STRLOC ": somehow some text lines were modified or scrolling occurred since the last validation of lines on the screen - may be a text widget bug.");
|
||||||
g_assert_not_reached ();
|
g_assert_not_reached ();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
printf ("painting %d,%d %d x %d\n",
|
|
||||||
area->x, area->y,
|
|
||||||
area->width, area->height);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gtk_snapshot_save (snapshot);
|
gtk_snapshot_save (snapshot);
|
||||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (-priv->xoffset, -priv->yoffset));
|
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (-priv->xoffset, -priv->yoffset));
|
||||||
@ -5364,7 +5361,8 @@ gtk_text_view_paint (GtkWidget *widget,
|
|||||||
priv->yoffset,
|
priv->yoffset,
|
||||||
gtk_widget_get_width (widget),
|
gtk_widget_get_width (widget),
|
||||||
gtk_widget_get_height (widget)
|
gtk_widget_get_height (widget)
|
||||||
});
|
},
|
||||||
|
priv->cursor_alpha);
|
||||||
|
|
||||||
gtk_snapshot_restore (snapshot);
|
gtk_snapshot_restore (snapshot);
|
||||||
}
|
}
|
||||||
@ -5661,15 +5659,80 @@ get_cursor_blink_timeout (GtkTextView *text_view)
|
|||||||
* Blink!
|
* Blink!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static gint
|
typedef struct {
|
||||||
blink_cb (gpointer data)
|
guint64 start;
|
||||||
|
guint64 end;
|
||||||
|
} BlinkData;
|
||||||
|
|
||||||
|
static gboolean blink_cb (GtkWidget *widget,
|
||||||
|
GdkFrameClock *clock,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_blink_timeout (GtkTextView *self)
|
||||||
|
{
|
||||||
|
GtkTextViewPrivate *priv = self->priv;
|
||||||
|
BlinkData *data;
|
||||||
|
int blink_time;
|
||||||
|
|
||||||
|
priv->blink_start_time = g_get_monotonic_time ();
|
||||||
|
priv->cursor_alpha = 1.0;
|
||||||
|
|
||||||
|
blink_time = get_cursor_time (self);
|
||||||
|
|
||||||
|
data = g_new (BlinkData, 1);
|
||||||
|
data->start = priv->blink_start_time;
|
||||||
|
data->end = data->start + blink_time * 1000;
|
||||||
|
|
||||||
|
priv->blink_tick = gtk_widget_add_tick_callback (GTK_WIDGET (self),
|
||||||
|
blink_cb,
|
||||||
|
data,
|
||||||
|
g_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
remove_blink_timeout (GtkTextView *self)
|
||||||
|
{
|
||||||
|
GtkTextViewPrivate *priv = self->priv;
|
||||||
|
|
||||||
|
if (priv->blink_tick)
|
||||||
|
{
|
||||||
|
gtk_widget_remove_tick_callback (GTK_WIDGET (self), priv->blink_tick);
|
||||||
|
priv->blink_tick = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static float
|
||||||
|
blink_alpha (float phase)
|
||||||
|
{
|
||||||
|
/* keep it simple, and split the blink cycle evenly
|
||||||
|
* into visible, fading out, invisible, fading in
|
||||||
|
*/
|
||||||
|
if (phase < 0.25)
|
||||||
|
return 1;
|
||||||
|
else if (phase < 0.5)
|
||||||
|
return 1 - 4 * (phase - 0.25);
|
||||||
|
else if (phase < 0.75)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 4 * (phase - 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
blink_cb (GtkWidget *widget,
|
||||||
|
GdkFrameClock *clock,
|
||||||
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
GtkTextView *text_view;
|
GtkTextView *text_view;
|
||||||
GtkTextViewPrivate *priv;
|
GtkTextViewPrivate *priv;
|
||||||
gboolean visible;
|
|
||||||
gint blink_timeout;
|
gint blink_timeout;
|
||||||
|
gint blink_time;
|
||||||
|
guint64 now;
|
||||||
|
float phase;
|
||||||
|
BlinkData *data = user_data;
|
||||||
|
|
||||||
text_view = GTK_TEXT_VIEW (data);
|
text_view = GTK_TEXT_VIEW (widget);
|
||||||
priv = text_view->priv;
|
priv = text_view->priv;
|
||||||
|
|
||||||
if (!gtk_widget_has_focus (GTK_WIDGET (text_view)))
|
if (!gtk_widget_has_focus (GTK_WIDGET (text_view)))
|
||||||
@ -5677,7 +5740,6 @@ blink_cb (gpointer data)
|
|||||||
g_warning ("GtkTextView - did not receive a focus-out.\n"
|
g_warning ("GtkTextView - did not receive a focus-out.\n"
|
||||||
"If you handle this event, you must return\n"
|
"If you handle this event, you must return\n"
|
||||||
"GDK_EVENT_PROPAGATE so the text view gets the event as well");
|
"GDK_EVENT_PROPAGATE so the text view gets the event as well");
|
||||||
|
|
||||||
gtk_text_view_check_cursor_blink (text_view);
|
gtk_text_view_check_cursor_blink (text_view);
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -5686,47 +5748,41 @@ blink_cb (gpointer data)
|
|||||||
g_assert (priv->layout);
|
g_assert (priv->layout);
|
||||||
g_assert (cursor_visible (text_view));
|
g_assert (cursor_visible (text_view));
|
||||||
|
|
||||||
visible = gtk_text_layout_get_cursor_visible (priv->layout);
|
|
||||||
|
|
||||||
blink_timeout = get_cursor_blink_timeout (text_view);
|
blink_timeout = get_cursor_blink_timeout (text_view);
|
||||||
if (priv->blink_time > 1000 * blink_timeout &&
|
blink_time = get_cursor_time (text_view);
|
||||||
blink_timeout < G_MAXINT/1000)
|
|
||||||
|
now = g_get_monotonic_time ();
|
||||||
|
|
||||||
|
if (now > priv->blink_start_time + blink_timeout * 1000000)
|
||||||
{
|
{
|
||||||
/* we've blinked enough without the user doing anything, stop blinking */
|
/* we've blinked enough without the user doing anything, stop blinking */
|
||||||
visible = 0;
|
priv->cursor_alpha = 1.0;
|
||||||
priv->blink_timeout = 0;
|
remove_blink_timeout (text_view);
|
||||||
}
|
gtk_widget_queue_draw (widget);
|
||||||
else if (visible)
|
|
||||||
{
|
return G_SOURCE_REMOVE;
|
||||||
priv->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_OFF_MULTIPLIER / CURSOR_DIVIDER,
|
|
||||||
blink_cb,
|
|
||||||
text_view);
|
|
||||||
g_source_set_name_by_id (priv->blink_timeout, "[gtk] blink_cb");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
priv->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_ON_MULTIPLIER / CURSOR_DIVIDER,
|
|
||||||
blink_cb,
|
|
||||||
text_view);
|
|
||||||
g_source_set_name_by_id (priv->blink_timeout, "[gtk] blink_cb");
|
|
||||||
priv->blink_time += get_cursor_time (text_view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_text_layout_set_cursor_visible (priv->layout, !visible);
|
phase = (now - data->start) / (float) (data->end - data->start);
|
||||||
|
|
||||||
/* Remove ourselves */
|
priv->cursor_alpha = blink_alpha (phase);
|
||||||
return FALSE;
|
|
||||||
|
if (now >= data->end)
|
||||||
|
{
|
||||||
|
data->start = data->end;
|
||||||
|
data->end = data->start + blink_time * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_widget_queue_draw (widget);
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_text_view_stop_cursor_blink (GtkTextView *text_view)
|
gtk_text_view_stop_cursor_blink (GtkTextView *text_view)
|
||||||
{
|
{
|
||||||
if (text_view->priv->blink_timeout)
|
remove_blink_timeout (text_view);
|
||||||
{
|
|
||||||
g_source_remove (text_view->priv->blink_timeout);
|
|
||||||
text_view->priv->blink_timeout = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -5734,52 +5790,25 @@ gtk_text_view_check_cursor_blink (GtkTextView *text_view)
|
|||||||
{
|
{
|
||||||
GtkTextViewPrivate *priv = text_view->priv;
|
GtkTextViewPrivate *priv = text_view->priv;
|
||||||
|
|
||||||
if (priv->layout != NULL &&
|
if (cursor_blinks (text_view))
|
||||||
cursor_visible (text_view) &&
|
|
||||||
gtk_widget_has_focus (GTK_WIDGET (text_view)))
|
|
||||||
{
|
{
|
||||||
if (cursor_blinks (text_view))
|
if (!priv->blink_tick)
|
||||||
{
|
add_blink_timeout (text_view);
|
||||||
if (priv->blink_timeout == 0)
|
|
||||||
{
|
|
||||||
gtk_text_layout_set_cursor_visible (priv->layout, TRUE);
|
|
||||||
|
|
||||||
priv->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_OFF_MULTIPLIER / CURSOR_DIVIDER,
|
|
||||||
blink_cb,
|
|
||||||
text_view);
|
|
||||||
g_source_set_name_by_id (priv->blink_timeout, "[gtk] blink_cb");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gtk_text_view_stop_cursor_blink (text_view);
|
|
||||||
gtk_text_layout_set_cursor_visible (priv->layout, TRUE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gtk_text_view_stop_cursor_blink (text_view);
|
if (priv->blink_tick)
|
||||||
gtk_text_layout_set_cursor_visible (priv->layout, FALSE);
|
remove_blink_timeout (text_view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_text_view_pend_cursor_blink (GtkTextView *text_view)
|
gtk_text_view_pend_cursor_blink (GtkTextView *text_view)
|
||||||
{
|
{
|
||||||
GtkTextViewPrivate *priv = text_view->priv;
|
if (cursor_blinks (text_view))
|
||||||
|
|
||||||
if (priv->layout != NULL &&
|
|
||||||
cursor_visible (text_view) &&
|
|
||||||
gtk_widget_has_focus (GTK_WIDGET (text_view)) &&
|
|
||||||
cursor_blinks (text_view))
|
|
||||||
{
|
{
|
||||||
gtk_text_view_stop_cursor_blink (text_view);
|
remove_blink_timeout (text_view);
|
||||||
gtk_text_layout_set_cursor_visible (priv->layout, TRUE);
|
add_blink_timeout (text_view);
|
||||||
|
|
||||||
priv->blink_timeout = g_timeout_add (get_cursor_time (text_view) * CURSOR_PEND_MULTIPLIER / CURSOR_DIVIDER,
|
|
||||||
blink_cb,
|
|
||||||
text_view);
|
|
||||||
g_source_set_name_by_id (priv->blink_timeout, "[gtk] blink_cb");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5788,7 +5817,7 @@ gtk_text_view_reset_blink_time (GtkTextView *text_view)
|
|||||||
{
|
{
|
||||||
GtkTextViewPrivate *priv = text_view->priv;
|
GtkTextViewPrivate *priv = text_view->priv;
|
||||||
|
|
||||||
priv->blink_time = 0;
|
priv->blink_start_time = g_get_monotonic_time ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user