[evince] libview: Make caret cursor blink
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evince] libview: Make caret cursor blink
- Date: Thu, 13 Jun 2013 17:13:04 +0000 (UTC)
commit e6f7250f8bf12a3f5fffaa50a9e306c7dde8075d
Author: Carlos Garcia Campos <carlosgc gnome org>
Date: Thu Jun 13 19:08:44 2013 +0200
libview: Make caret cursor blink
Based on GtkEntry and GtkTextView implementation, the caret cursor
blinks when the view is active and caret navigation is enabled. It stops
blinking after a while if there's no user interaction. It uses
GtkSettings:gtk-cursor-blink-time and
GtkSettings:gtk-cursor-blink-timeout.
https://bugzilla.gnome.org/show_bug.cgi?id=702076
libview/ev-view-private.h | 3 +
libview/ev-view.c | 284 +++++++++++++++++++++++++++++++++++---------
2 files changed, 229 insertions(+), 58 deletions(-)
---
diff --git a/libview/ev-view-private.h b/libview/ev-view-private.h
index fda40a9..135e9cf 100644
--- a/libview/ev-view-private.h
+++ b/libview/ev-view-private.h
@@ -211,6 +211,9 @@ struct _EvView {
/* Caret navigation */
gboolean caret_enabled;
gint cursor_offset;
+ gboolean cursor_visible;
+ guint cursor_blink_timeout_id;
+ guint cursor_blink_time;
};
struct _EvViewClass {
diff --git a/libview/ev-view.c b/libview/ev-view.c
index 826ba4d..5760cc3 100644
--- a/libview/ev-view.c
+++ b/libview/ev-view.c
@@ -3113,6 +3113,209 @@ ev_view_synctex_backward_search (EvView *view,
}
/* Caret navigation */
+#define CURSOR_ON_MULTIPLIER 2
+#define CURSOR_OFF_MULTIPLIER 1
+#define CURSOR_PEND_MULTIPLIER 3
+#define CURSOR_DIVIDER 3
+
+static gboolean
+cursor_should_blink (EvView *view)
+{
+ if (view->caret_enabled &&
+ view->rotation == 0 &&
+ gtk_widget_has_focus (GTK_WIDGET (view))) {
+ GtkSettings *settings;
+ gboolean blink;
+
+ settings = gtk_widget_get_settings (GTK_WIDGET (view));
+ g_object_get (settings, "gtk-cursor-blink", &blink, NULL);
+
+ return blink;
+ }
+
+ return FALSE;
+}
+
+static gint
+get_cursor_blink_time (EvView *view)
+{
+ GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (view));
+ gint time;
+
+ g_object_get (settings, "gtk-cursor-blink-time", &time, NULL);
+
+ return time;
+}
+
+static gint
+get_cursor_blink_timeout_id (EvView *view)
+{
+ GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (view));
+ gint timeout;
+
+ g_object_get (settings, "gtk-cursor-blink-timeout", &timeout, NULL);
+
+ return timeout;
+}
+
+static gboolean
+get_caret_cursor_rect_from_offset (EvView *view,
+ gint offset,
+ gint page,
+ GdkRectangle *rect)
+{
+ EvRectangle *areas = NULL;
+ EvRectangle *doc_rect;
+ guint n_areas = 0;
+
+ if (!view->caret_enabled)
+ return FALSE;
+
+ /* Disable caret navigation on rotated pages */
+ if (view->rotation != 0)
+ return FALSE;
+
+ if (!view->page_cache)
+ return FALSE;
+
+ ev_page_cache_get_text_layout (view->page_cache, page, &areas, &n_areas);
+ if (!areas)
+ return FALSE;
+
+ if (offset > n_areas)
+ return FALSE;
+
+ doc_rect = areas + offset;
+ if (offset == n_areas ||
+ ((doc_rect->x1 == doc_rect->x2 || doc_rect->y1 == doc_rect->y2) && offset > 0)) {
+ EvRectangle *prev;
+ EvRectangle last_rect;
+
+ /* Special characters like \n have an empty bounding box
+ * and the end of a page doesn't have any bounding box,
+ * use the size of the previous area.
+ */
+ prev = areas + offset - 1;
+ last_rect.x1 = prev->x2;
+ last_rect.y1 = prev->y1;
+ last_rect.x2 = prev->x2 + (prev->x2 - prev->x1);
+ last_rect.y2 = prev->y2;
+
+ _ev_view_transform_doc_rect_to_view_rect (view, page, &last_rect, rect);
+
+ return TRUE;
+ }
+
+ _ev_view_transform_doc_rect_to_view_rect (view, page, doc_rect, rect);
+
+ return TRUE;
+}
+
+static void
+show_cursor (EvView *view)
+{
+ GtkWidget *widget;
+ GdkRectangle view_rect;
+
+ if (view->cursor_visible)
+ return;
+
+ widget = GTK_WIDGET (view);
+ view->cursor_visible = TRUE;
+ if (gtk_widget_has_focus (widget)
+ && get_caret_cursor_rect_from_offset (view, view->cursor_offset, view->current_page, &view_rect))
{
+ view_rect.x = view_rect.x - view->scroll_x;
+ view_rect.y = view_rect.y - view->scroll_y;
+ gtk_widget_queue_draw_area (widget, view_rect.x, view_rect.y, view_rect.width,
view_rect.height);
+ }
+}
+
+static void
+hide_cursor (EvView *view)
+{
+ GtkWidget *widget;
+ GdkRectangle view_rect;
+
+ if (!view->cursor_visible)
+ return;
+
+ widget = GTK_WIDGET (view);
+ view->cursor_visible = FALSE;
+ if (gtk_widget_has_focus (widget)
+ && get_caret_cursor_rect_from_offset (view, view->cursor_offset, view->current_page, &view_rect))
{
+ view_rect.x = view_rect.x - view->scroll_x;
+ view_rect.y = view_rect.y - view->scroll_y;
+ gtk_widget_queue_draw_area (widget, view_rect.x, view_rect.y, view_rect.width,
view_rect.height);
+ }
+}
+
+static gboolean
+blink_cb (EvView *view)
+{
+ gint blink_timeout;
+ guint blink_time;
+
+ blink_timeout = get_cursor_blink_timeout_id (view);
+ if (view->cursor_blink_time > 1000 * blink_timeout && blink_timeout < G_MAXINT / 1000) {
+ /* We've blinked enough without the user doing anything, stop blinking */
+ show_cursor (view);
+ view->cursor_blink_timeout_id = 0;
+
+ return FALSE;
+ }
+
+ blink_time = get_cursor_blink_time (view);
+ if (view->cursor_visible) {
+ hide_cursor (view);
+ blink_time *= CURSOR_OFF_MULTIPLIER;
+ } else {
+ show_cursor (view);
+ view->cursor_blink_time += blink_time;
+ blink_time *= CURSOR_ON_MULTIPLIER;
+ }
+
+ view->cursor_blink_timeout_id = gdk_threads_add_timeout (blink_time / CURSOR_DIVIDER,
(GSourceFunc)blink_cb, view);
+
+ return FALSE;
+}
+
+static void
+ev_view_check_cursor_blink (EvView *view)
+{
+ if (cursor_should_blink (view)) {
+ if (view->cursor_blink_timeout_id == 0) {
+ show_cursor (view);
+ view->cursor_blink_timeout_id = gdk_threads_add_timeout (get_cursor_blink_time (view)
* CURSOR_ON_MULTIPLIER / CURSOR_DIVIDER,
+ (GSourceFunc)blink_cb, view);
+ }
+
+ return;
+ }
+
+ if (view->cursor_blink_timeout_id > 0) {
+ g_source_remove (view->cursor_blink_timeout_id);
+ view->cursor_blink_timeout_id = 0;
+ }
+
+ view->cursor_visible = TRUE;
+ view->cursor_blink_time = 0;
+}
+
+static void
+ev_view_pend_cursor_blink (EvView *view)
+{
+ if (!cursor_should_blink (view))
+ return;
+
+ if (view->cursor_blink_timeout_id > 0)
+ g_source_remove (view->cursor_blink_timeout_id);
+
+ show_cursor (view);
+ view->cursor_blink_timeout_id = gdk_threads_add_timeout (get_cursor_blink_time (view) *
CURSOR_PEND_MULTIPLIER / CURSOR_DIVIDER,
+ (GSourceFunc)blink_cb, view);
+}
+
+
void
ev_view_set_caret_navigation_enabled (EvView *view,
gboolean enabled)
@@ -3121,6 +3324,7 @@ ev_view_set_caret_navigation_enabled (EvView *view,
if (view->caret_enabled != enabled) {
view->caret_enabled = enabled;
+ ev_view_check_cursor_blink (view);
gtk_widget_queue_draw (GTK_WIDGET (view));
}
}
@@ -3549,59 +3753,6 @@ ev_view_realize (GtkWidget *widget)
window);
}
-static gboolean
-get_caret_cursor_rect_from_offset (EvView *view,
- gint offset,
- gint page,
- GdkRectangle *rect)
-{
- EvRectangle *areas = NULL;
- EvRectangle *doc_rect;
- guint n_areas = 0;
-
- if (!view->caret_enabled)
- return FALSE;
-
- /* Disable caret navigation on rotated pages */
- if (view->rotation != 0)
- return FALSE;
-
- if (!view->page_cache)
- return FALSE;
-
- ev_page_cache_get_text_layout (view->page_cache, page, &areas, &n_areas);
- if (!areas)
- return FALSE;
-
- if (offset > n_areas)
- return FALSE;
-
- doc_rect = areas + offset;
- if (offset == n_areas ||
- ((doc_rect->x1 == doc_rect->x2 || doc_rect->y1 == doc_rect->y2) && offset > 0)) {
- EvRectangle *prev;
- EvRectangle last_rect;
-
- /* Special characters like \n have an empty bounding box
- * and the end of a page doesn't have any bounding box,
- * use the size of the previous area.
- */
- prev = areas + offset - 1;
- last_rect.x1 = prev->x2;
- last_rect.y1 = prev->y1;
- last_rect.x2 = prev->x2 + (prev->x2 - prev->x1);
- last_rect.y2 = prev->y2;
-
- _ev_view_transform_doc_rect_to_view_rect (view, page, &last_rect, rect);
-
- return TRUE;
- }
-
- _ev_view_transform_doc_rect_to_view_rect (view, page, doc_rect, rect);
-
- return TRUE;
-}
-
static void
get_cursor_color (GtkStyleContext *context,
GdkRGBA *color)
@@ -3701,7 +3852,7 @@ ev_view_draw (GtkWidget *widget,
draw_one_page (view, i, cr, &page_area, &border, &clip_rect, &page_ready);
- if (page_ready && view->caret_enabled && view->current_page == i)
+ if (page_ready && view->caret_enabled && view->current_page == i && view->cursor_visible)
draw_caret_cursor (view, cr);
if (page_ready && view->find_pages && view->highlight_find_results)
highlight_find_results (view, cr, i);
@@ -4689,6 +4840,8 @@ caret_key_press_event (EvView *view,
if (view->rotation != 0)
return FALSE;
+ view->cursor_blink_time = 0;
+
switch (event->keyval) {
case GDK_KEY_Left:
if (event->state & GDK_CONTROL_MASK)
@@ -4718,6 +4871,8 @@ caret_key_press_event (EvView *view,
return FALSE;
}
+ ev_view_pend_cursor_blink (view);
+
return TRUE;
}
@@ -4753,8 +4908,12 @@ static gint
ev_view_focus_in (GtkWidget *widget,
GdkEventFocus *event)
{
- if (EV_VIEW (widget)->pixbuf_cache)
- ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
+ EvView *view = EV_VIEW (widget);
+
+ if (view->pixbuf_cache)
+ ev_pixbuf_cache_style_changed (view->pixbuf_cache);
+
+ ev_view_check_cursor_blink (view);
gtk_widget_queue_draw (widget);
return FALSE;
@@ -4764,8 +4923,12 @@ static gint
ev_view_focus_out (GtkWidget *widget,
GdkEventFocus *event)
{
- if (EV_VIEW (widget)->pixbuf_cache)
- ev_pixbuf_cache_style_changed (EV_VIEW (widget)->pixbuf_cache);
+ EvView *view = EV_VIEW (widget);
+
+ if (view->pixbuf_cache)
+ ev_pixbuf_cache_style_changed (view->pixbuf_cache);
+
+ ev_view_check_cursor_blink (view);
gtk_widget_queue_draw (widget);
return FALSE;
@@ -5105,6 +5268,11 @@ ev_view_dispose (GObject *object)
view->drag_info.release_timeout_id = 0;
}
+ if (view->cursor_blink_timeout_id) {
+ g_source_remove (view->cursor_blink_timeout_id);
+ view->cursor_blink_timeout_id = 0;
+ }
+
gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (view), NULL);
gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (view), NULL);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]