diff options
| author | Carlos Garcia Campos <[email protected]> | 2013-06-13 19:08:44 +0200 | 
|---|---|---|
| committer | raveit65 <[email protected]> | 2017-09-06 18:25:34 +0200 | 
| commit | ac82f591013915acd09a345847c337589ac901ce (patch) | |
| tree | a397755b264044bed2caeb6bc5ecfae7fd55a558 | |
| parent | b5b335319cf0768142e0e3ae8af6df984bf2a01b (diff) | |
| download | atril-ac82f591013915acd09a345847c337589ac901ce.tar.bz2 atril-ac82f591013915acd09a345847c337589ac901ce.tar.xz  | |
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
taken from:
https://git.gnome.org/browse/evince/commit/?h=gnome-3-10&id=e6f7250
| -rw-r--r-- | libview/ev-view-private.h | 3 | ||||
| -rw-r--r-- | libview/ev-view.c | 336 | 
2 files changed, 255 insertions, 84 deletions
diff --git a/libview/ev-view-private.h b/libview/ev-view-private.h index c5180397..c286efbe 100644 --- a/libview/ev-view-private.h +++ b/libview/ev-view-private.h @@ -217,6 +217,9 @@ struct _EvView {  	/* Caret navigation */  	gboolean caret_enabled;  	gint     cursor_offset; +	gboolean cursor_visible; +	guint    cursor_blink_timeout_id; +	guint    cursor_blink_time;  	/* Gestures */  	GtkGesture *pan_gesture; diff --git a/libview/ev-view.c b/libview/ev-view.c index ed66a51b..89ef5196 100644 --- a/libview/ev-view.c +++ b/libview/ev-view.c @@ -3231,6 +3231,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) @@ -3239,6 +3442,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));  	}  } @@ -3694,85 +3898,6 @@ ev_view_realize (GtkWidget *widget)  }  static void -draw_focus (EvView       *view, -            cairo_t      *cr, -            gint          page, -            GdkRectangle *clip) -{ -	GtkWidget   *widget = GTK_WIDGET (view); -	GdkRectangle rect; -	GdkRectangle intersect; - -	if (view->focused_element_page != page) -		return; - -	if (!ev_view_get_focused_area (view, &rect)) -		return; - -	if (gdk_rectangle_intersect (&rect, clip, &intersect)) { -		gtk_render_focus (gtk_widget_get_style_context (widget), -	                          cr, -	                          intersect.x, -	                          intersect.y, -	                          intersect.width, -	                          intersect.height); -	} -} - -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)  { @@ -3838,6 +3963,32 @@ draw_caret_cursor (EvView  *view,  	render_cursor (GTK_WIDGET (view), cr, &view_rect);  } +static void +draw_focus (EvView       *view, +            cairo_t      *cr, +            gint          page, +            GdkRectangle *clip) +{ +	GtkWidget   *widget = GTK_WIDGET (view); +	GdkRectangle rect; +	GdkRectangle intersect; + +	if (view->focused_element_page != page) +		return; + +	if (!ev_view_get_focused_area (view, &rect)) +		return; + +	if (gdk_rectangle_intersect (&rect, clip, &intersect)) { +		gtk_render_focus (gtk_widget_get_style_context (widget), +	                          cr, +	                          intersect.x, +	                          intersect.y, +	                          intersect.width, +	                          intersect.height); +	} +} +  static gboolean  ev_view_draw (GtkWidget      *widget,  	      cairo_t *cr) @@ -3879,7 +4030,7 @@ ev_view_draw (GtkWidget      *widget,  		draw_one_page (view, i, cr, &page_area, &border, area, &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); @@ -4921,6 +5072,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) @@ -4950,6 +5103,8 @@ caret_key_press_event (EvView      *view,  		return FALSE;  	} +	ev_view_pend_cursor_blink (view); +  	return TRUE;  } @@ -4985,8 +5140,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; @@ -4996,8 +5155,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; @@ -5388,6 +5551,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; +	} +  	if (view->loading_timeout) {  		g_source_remove (view->loading_timeout);  		view->loading_timeout = 0;  | 
