[gtk/wip/textview] text view: Smooth cursor blinking
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/textview] text view: Smooth cursor blinking
- Date: Sun, 21 Jul 2019 22:29:12 +0000 (UTC)
commit 57fb29e572b4e4e756babaa72556b38d1fc8926c
Author: Matthias Clasen <mclasen redhat com>
Date: Sun Jul 21 13:06:23 2019 -0700
text view: Smooth cursor blinking
Fade the text cursor in and out, instead
of abruptly turning it on and off.
gtk/gtktextlayout.c | 14 +++-
gtk/gtktextlayoutprivate.h | 3 +-
gtk/gtktextutil.c | 2 +-
gtk/gtktextview.c | 181 ++++++++++++++++++++++++++-------------------
4 files changed, 119 insertions(+), 81 deletions(-)
---
diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c
index 5564fd815f..5e2d42a07f 100644
--- a/gtk/gtktextlayout.c
+++ b/gtk/gtktextlayout.c
@@ -3795,7 +3795,8 @@ render_para (GskPangoRenderer *crenderer,
int offset_y,
GtkTextLineDisplay *line_display,
int selection_start_index,
- int selection_end_index)
+ int selection_end_index,
+ float cursor_alpha)
{
GtkStyleContext *context;
PangoLayout *layout = line_display->layout;
@@ -3972,6 +3973,7 @@ render_para (GskPangoRenderer *crenderer,
* (normally white on black) */
_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);
/* draw text under the cursor if any */
@@ -3985,6 +3987,7 @@ render_para (GskPangoRenderer *crenderer,
baseline);
gtk_snapshot_pop (crenderer->snapshot);
}
+ gtk_snapshot_pop (crenderer->snapshot);
}
}
@@ -4002,7 +4005,8 @@ void
gtk_text_layout_snapshot (GtkTextLayout *layout,
GtkWidget *widget,
GtkSnapshot *snapshot,
- const GdkRectangle *clip)
+ const GdkRectangle *clip,
+ float cursor_alpha)
{
GskPangoRenderer *crenderer;
GtkStyleContext *context;
@@ -4079,7 +4083,8 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
}
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
* and need to appear on top.
@@ -4088,6 +4093,7 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
{
int i;
+ gtk_snapshot_push_opacity (crenderer->snapshot, cursor_alpha);
for (i = 0; i < line_display->cursors->len; i++)
{
int index;
@@ -4100,6 +4106,8 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
line_display->x_offset, offset_y +
line_display->top_margin,
line_display->layout, index, dir);
}
+
+ gtk_snapshot_pop (crenderer->snapshot);
}
} /* line_display->height > 0 */
diff --git a/gtk/gtktextlayoutprivate.h b/gtk/gtktextlayoutprivate.h
index 7ba6627b04..7aff2074ff 100644
--- a/gtk/gtktextlayoutprivate.h
+++ b/gtk/gtktextlayoutprivate.h
@@ -400,7 +400,8 @@ void gtk_text_layout_spew (GtkTextLayout *layout);
void gtk_text_layout_snapshot (GtkTextLayout *layout,
GtkWidget *widget,
GtkSnapshot *snapshot,
- const GdkRectangle *clip);
+ const GdkRectangle *clip,
+ float cursor_alpha);
G_END_DECLS
diff --git a/gtk/gtktextutil.c b/gtk/gtktextutil.c
index e806c6a205..2c3c43d8bc 100644
--- a/gtk/gtktextutil.c
+++ b/gtk/gtktextutil.c
@@ -335,7 +335,7 @@ gtk_text_util_create_rich_drag_icon (GtkWidget *widget,
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 (new_buffer);
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index 73c8521455..4b30b8d653 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -209,7 +209,10 @@ struct _GtkTextViewPrivate
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 */
- guint blink_timeout;
+ guint64 blink_start_time;
+ guint blink_tick;
+ float cursor_alpha;
+
guint scroll_timeout;
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_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_translate (snapshot, &GRAPHENE_POINT_INIT (-priv->xoffset, -priv->yoffset));
@@ -5364,7 +5361,8 @@ gtk_text_view_paint (GtkWidget *widget,
priv->yoffset,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget)
- });
+ },
+ priv->cursor_alpha);
gtk_snapshot_restore (snapshot);
}
@@ -5661,15 +5659,80 @@ get_cursor_blink_timeout (GtkTextView *text_view)
* Blink!
*/
-static gint
-blink_cb (gpointer data)
+typedef struct {
+ 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;
GtkTextViewPrivate *priv;
- gboolean visible;
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;
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"
"If you handle this event, you must return\n"
"GDK_EVENT_PROPAGATE so the text view gets the event as well");
-
gtk_text_view_check_cursor_blink (text_view);
return FALSE;
@@ -5686,47 +5748,41 @@ blink_cb (gpointer data)
g_assert (priv->layout);
g_assert (cursor_visible (text_view));
- visible = gtk_text_layout_get_cursor_visible (priv->layout);
-
blink_timeout = get_cursor_blink_timeout (text_view);
- if (priv->blink_time > 1000 * blink_timeout &&
- blink_timeout < G_MAXINT/1000)
+ blink_time = get_cursor_time (text_view);
+
+ 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 */
- visible = 0;
- priv->blink_timeout = 0;
- }
- else if (visible)
- {
- 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");
+ priv->cursor_alpha = 1.0;
+ remove_blink_timeout (text_view);
+ gtk_widget_queue_draw (widget);
+
+ return G_SOURCE_REMOVE;
}
- else
+
+ phase = (now - data->start) / (float) (data->end - data->start);
+
+ priv->cursor_alpha = blink_alpha (phase);
+
+ if (now >= data->end)
{
- 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);
+ data->start = data->end;
+ data->end = data->start + blink_time * 1000;
}
- gtk_text_layout_set_cursor_visible (priv->layout, !visible);
+ gtk_widget_queue_draw (widget);
- /* Remove ourselves */
- return FALSE;
+ return G_SOURCE_CONTINUE;
}
static void
gtk_text_view_stop_cursor_blink (GtkTextView *text_view)
{
- if (text_view->priv->blink_timeout)
- {
- g_source_remove (text_view->priv->blink_timeout);
- text_view->priv->blink_timeout = 0;
- }
+ remove_blink_timeout (text_view);
}
static void
@@ -5734,52 +5790,25 @@ gtk_text_view_check_cursor_blink (GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
- if (priv->layout != NULL &&
- cursor_visible (text_view) &&
- gtk_widget_has_focus (GTK_WIDGET (text_view)))
+ if (cursor_blinks (text_view))
{
- if (cursor_blinks (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);
- }
+ if (!priv->blink_tick)
+ add_blink_timeout (text_view);
}
else
{
- gtk_text_view_stop_cursor_blink (text_view);
- gtk_text_layout_set_cursor_visible (priv->layout, FALSE);
+ if (priv->blink_tick)
+ remove_blink_timeout (text_view);
}
}
static void
gtk_text_view_pend_cursor_blink (GtkTextView *text_view)
{
- GtkTextViewPrivate *priv = text_view->priv;
-
- if (priv->layout != NULL &&
- cursor_visible (text_view) &&
- gtk_widget_has_focus (GTK_WIDGET (text_view)) &&
- cursor_blinks (text_view))
+ if (cursor_blinks (text_view))
{
- gtk_text_view_stop_cursor_blink (text_view);
- gtk_text_layout_set_cursor_visible (priv->layout, TRUE);
-
- 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");
+ remove_blink_timeout (text_view);
+ add_blink_timeout (text_view);
}
}
@@ -5788,7 +5817,7 @@ gtk_text_view_reset_blink_time (GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
- priv->blink_time = 0;
+ priv->blink_start_time = g_get_monotonic_time ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]