Merge branch 'feature/selection' into 'develop'

Proper text selection upon mouse drag (Closes pwmt/zathura#26).

Closes #26

See merge request pwmt/zathura!59
This commit is contained in:
Sebastian Ramacher 2022-11-27 15:17:54 +01:00
commit a99249e799
5 changed files with 112 additions and 25 deletions

View file

@ -14,8 +14,8 @@ version_array = version.split('.')
# * If any of the exported datastructures have changed in a incompatible way
# bump SOMAJOR and set SOMINOR to 0.
# * If a function has been added bump SOMINOR.
plugin_api_version = '3'
plugin_abi_version = '4'
plugin_api_version = '4'
plugin_abi_version = '5'
conf_data = configuration_data()
conf_data.set('ZVMAJOR', version_array[0])

View file

@ -38,6 +38,12 @@ typedef struct zathura_page_widget_private_s {
gboolean draw; /**< Draw search results */
} search;
struct {
girara_list_t *list; /**< List of selection rectangles that should be drawn */
zathura_rectangle_t bounds; /** Bounds of highlighted selection */
gboolean draw; /** Used to clear previous selection */
} selection;
struct {
girara_list_t* list; /**< List of images on the page */
gboolean retrieved; /**< True if we already tried to retrieve the list of images */
@ -617,13 +623,14 @@ zathura_page_widget_draw(GtkWidget* widget, cairo_t* cairo)
++idx;
);
}
/* draw selection */
if (priv->mouse.selection.y2 != -1 && priv->mouse.selection.x2 != -1) {
if (priv->selection.list != NULL && priv->selection.draw == true) {
const GdkRGBA color = priv->zathura->ui.colors.highlight_color;
cairo_set_source_rgba(cairo, color.red, color.green, color.blue, transparency);
cairo_rectangle(cairo, priv->mouse.selection.x1, priv->mouse.selection.y1,
(priv->mouse.selection.x2 - priv->mouse.selection.x1), (priv->mouse.selection.y2 - priv->mouse.selection.y1));
GIRARA_LIST_FOREACH_BODY(priv->selection.list, zathura_rectangle_t*, rect,
zathura_rectangle_t rectangle = recalc_rectangle(priv->page, *rect);
cairo_rectangle(cairo, rectangle.x1, rectangle.y1, rectangle.x2 - rectangle.x1, rectangle.y2 - rectangle.y1);
cairo_fill(cairo);
);
}
} else {
/* set background color */
@ -881,6 +888,11 @@ cb_zathura_page_widget_button_press_event(GtkWidget* widget, GdkEventButton* but
return true;
}
if (priv->selection.list != NULL) {
priv->selection.draw = false;
redraw_rect(page, &priv->selection.bounds);
}
if (button->button == GDK_BUTTON_PRIMARY) { /* left click */
if (button->type == GDK_BUTTON_PRESS) {
/* start the selection */
@ -891,6 +903,7 @@ cb_zathura_page_widget_button_press_event(GtkWidget* widget, GdkEventButton* but
priv->mouse.selection.y1 = button->y;
priv->mouse.selection.x2 = button->x;
priv->mouse.selection.y2 = button->y;
} else if (button->type == GDK_2BUTTON_PRESS || button->type == GDK_3BUTTON_PRESS) {
/* abort the selection */
priv->mouse.selection_basepoint.x = -1;
@ -962,8 +975,6 @@ cb_zathura_page_widget_button_release_event(GtkWidget* widget, GdkEventButton* b
);
}
} else {
redraw_rect(ZATHURA_PAGE(widget), &priv->mouse.selection);
zathura_rectangle_t tmp = priv->mouse.selection;
tmp.x1 /= scale;
@ -999,6 +1010,9 @@ cb_zathura_page_widget_motion_notify(GtkWidget* widget, GdkEventMotion* event)
ZathuraPage* page = ZATHURA_PAGE(widget);
ZathuraPagePrivate* priv = zathura_page_widget_get_instance_private(page);
zathura_document_t* document = zathura_page_get_document(priv->page);
const double scale = zathura_document_get_scale(document);
if ((event->state & GDK_BUTTON1_MASK) == 0) {
if (priv->links.retrieved == false) {
priv->links.list = zathura_page_links_get(priv->page, NULL);
@ -1029,24 +1043,51 @@ cb_zathura_page_widget_motion_notify(GtkWidget* widget, GdkEventMotion* event)
return false;
}
zathura_rectangle_t tmp = priv->mouse.selection;
if (event->x < priv->mouse.selection_basepoint.x) {
tmp.x1 = event->x;
tmp.x2 = priv->mouse.selection_basepoint.x;
} else {
tmp.x2 = event->x;
tmp.x1 = priv->mouse.selection_basepoint.x;
}
if (event->y < priv->mouse.selection_basepoint.y) {
tmp.y1 = event->y;
tmp.y2 = priv->mouse.selection_basepoint.y;
} else {
tmp.y1 = priv->mouse.selection_basepoint.y;
tmp.y2 = event->y;
const zathura_rectangle_t tmp = {
priv->mouse.selection_basepoint.x,
priv->mouse.selection_basepoint.y,
event->x,
event->y
};
if (priv->selection.list != NULL) {
girara_list_free(priv->selection.list);
}
redraw_rect(ZATHURA_PAGE(widget), &priv->mouse.selection);
redraw_rect(ZATHURA_PAGE(widget), &tmp);
zathura_rectangle_t scaled_mouse_selection = tmp;
scaled_mouse_selection.x1 /= scale;
scaled_mouse_selection.x2 /= scale;
scaled_mouse_selection.y1 /= scale;
scaled_mouse_selection.y2 /= scale;
const unsigned int page_width = gtk_widget_get_allocated_width(widget);
float y1, y2;
if (tmp.y1 < tmp.y2) {
y1 = tmp.y1;
y2 = tmp.y2;
} else {
y1 = tmp.y2;
y2 = tmp.y1;
}
zathura_rectangle_t redraw_bounds = {0, y1, page_width, y2};
priv->selection.list = zathura_page_get_selection(priv->page, scaled_mouse_selection, NULL);
priv->selection.list = zathura_page_get_selection(priv->page, scaled_mouse_selection, NULL);
if (priv->selection.list != NULL && girara_list_size(priv->selection.list) != 0) {
GIRARA_LIST_FOREACH_BODY(priv->selection.list, zathura_rectangle_t*, rect,
redraw_bounds.y1 = fmin(rect->y1 * scale, redraw_bounds.y1);
redraw_bounds.y2 = fmax(rect->y2 * scale, redraw_bounds.y2);
);
priv->selection.draw = false;
redraw_rect(page, &priv->selection.bounds);
priv->selection.draw = true;
redraw_rect(page, &redraw_bounds);
priv->selection.bounds = redraw_bounds;
}
priv->mouse.selection = tmp;
return false;
@ -1059,6 +1100,10 @@ cb_zathura_page_widget_leave_notify(GtkWidget* widget, GdkEventCrossing* UNUSED(
ZathuraPage* page = ZATHURA_PAGE(widget);
ZathuraPagePrivate* priv = zathura_page_widget_get_instance_private(page);
if (priv->selection.list != NULL) {
priv->selection.draw = false;
redraw_rect(page, &priv->selection.bounds);
}
if (priv->mouse.over_link == true) {
g_signal_emit(ZATHURA_PAGE(widget), signals[LEAVE_LINK], 0);
priv->mouse.over_link = false;

View file

@ -344,6 +344,28 @@ zathura_page_get_text(zathura_page_t* page, zathura_rectangle_t rectangle, zathu
return functions->page_get_text(page, page->data, rectangle, error);
}
girara_list_t*
zathura_page_get_selection(zathura_page_t* page, zathura_rectangle_t rectangle, zathura_error_t* error)
{
if (page == NULL || page->document == NULL ) {
if (error) {
*error = ZATHURA_ERROR_INVALID_ARGUMENTS;
}
return NULL;
}
zathura_plugin_t* plugin = zathura_document_get_plugin(page->document);
const zathura_plugin_functions_t* functions = zathura_plugin_get_functions(plugin);
if (functions->page_get_selection == NULL) {
if (error) {
*error = ZATHURA_ERROR_NOT_IMPLEMENTED;
}
return NULL;
}
return functions->page_get_selection(page, page->data, rectangle, error);
}
zathura_error_t
zathura_page_render(zathura_page_t* page, cairo_t* cairo, bool printing)
{

View file

@ -192,6 +192,16 @@ ZATHURA_PLUGIN_API cairo_surface_t* zathura_page_image_get_cairo(zathura_page_t*
*/
ZATHURA_PLUGIN_API char* zathura_page_get_text(zathura_page_t* page, zathura_rectangle_t rectangle, zathura_error_t* error);
/**
* Get rectangles from selection
* @param page Page
* @param rectangle Selection
* @param error Set to an error value (see \ref zathura_error_t) if an error
* occurred
* @return List of rectangles or NULL if an error occurred
*/
ZATHURA_PLUGIN_API girara_list_t* zathura_page_get_selection(zathura_page_t* page, zathura_rectangle_t rectangle, zathura_error_t* error);
/**
* Render page
*

View file

@ -88,6 +88,11 @@ typedef cairo_surface_t* (*zathura_plugin_page_image_get_cairo_t)(zathura_page_t
*/
typedef char* (*zathura_plugin_page_get_text_t)(zathura_page_t* page, void* data, zathura_rectangle_t rectangle, zathura_error_t* error);
/**
* Get rectangles from selection
*/
typedef girara_list_t* (*zathura_plugin_page_get_selection_t)(zathura_page_t* page, void* data, zathura_rectangle_t rectangle, zathura_error_t* error);
/**
* Renders the page
*/
@ -181,6 +186,11 @@ struct zathura_plugin_functions_s
*/
zathura_plugin_page_get_text_t page_get_text;
/**
* Get text for selection
*/
zathura_plugin_page_get_selection_t page_get_selection;
/**
* Renders the page
*/