From c8f75f2c0b4f4cb3770f19499dbb26270aa6d5d3 Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Sat, 9 Apr 2022 09:40:55 +0300 Subject: [PATCH 40/40] gtk4: Implement mouse button down gestures for mapcanvas. See osdn #44305 Signed-off-by: Marko Lindqvist --- client/gui-gtk-4.0/editgui.c | 18 +-- client/gui-gtk-4.0/editgui.h | 4 +- client/gui-gtk-4.0/gui_main.c | 22 +++- client/gui-gtk-4.0/mapctrl.c | 231 +++++++++++++++++++--------------- client/gui-gtk-4.0/mapctrl.h | 7 +- 5 files changed, 166 insertions(+), 116 deletions(-) diff --git a/client/gui-gtk-4.0/editgui.c b/client/gui-gtk-4.0/editgui.c index 233849e4ef..93dfd8b437 100644 --- a/client/gui-gtk-4.0/editgui.c +++ b/client/gui-gtk-4.0/editgui.c @@ -942,21 +942,15 @@ static int convert_mouse_button(int gdk_mouse_button) /************************************************************************//** Pass on the gdk mouse event to the editor's handler. ****************************************************************************/ -gboolean handle_edit_mouse_button_press(GdkEvent *ev) +gboolean handle_edit_mouse_button_press(GtkGestureClick *gesture, + int editor_mouse_button, + double x, double y) { - gdouble e_x, e_y; - guint button; GdkModifierType state; - if (gdk_event_get_event_type(ev) != GDK_BUTTON_PRESS) { - return TRUE; - } - - gdk_event_get_position(ev, &e_x, &e_y); - button = gdk_button_event_get_button(ev); - state = gdk_event_get_modifier_state(ev); - editor_mouse_button_press(e_x, e_y, - convert_mouse_button(button), + state = gtk_event_controller_get_current_event_state( + GTK_EVENT_CONTROLLER(gesture)); + editor_mouse_button_press(x, y, editor_mouse_button, convert_modifiers(state)); return TRUE; diff --git a/client/gui-gtk-4.0/editgui.h b/client/gui-gtk-4.0/editgui.h index 8c58b0be67..4987f0f1c6 100644 --- a/client/gui-gtk-4.0/editgui.h +++ b/client/gui-gtk-4.0/editgui.h @@ -36,7 +36,9 @@ struct editbar { GtkWidget *player_pov_combobox; }; -gboolean handle_edit_mouse_button_press(GdkEvent *ev); +gboolean handle_edit_mouse_button_press(GtkGestureClick *gesture, + int editor_mouse_button, + double x, double y); gboolean handle_edit_mouse_button_release(GdkEvent *ev); gboolean handle_edit_mouse_move(GdkEvent *ev); gboolean handle_edit_key_press(GdkEvent *ev); diff --git a/client/gui-gtk-4.0/gui_main.c b/client/gui-gtk-4.0/gui_main.c index a12c675116..5f5c111771 100644 --- a/client/gui-gtk-4.0/gui_main.c +++ b/client/gui-gtk-4.0/gui_main.c @@ -1120,6 +1120,8 @@ static void setup_widgets(void) int top_row = 0; int grid_col = 0; int grid2_col = 0; + GtkGesture *mc_gesture; + GtkEventController *mc_controller; message_buffer = gtk_text_buffer_new(NULL); @@ -1505,6 +1507,23 @@ static void setup_widgets(void) gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(map_canvas), map_canvas_draw, NULL, NULL); + mc_controller = GTK_EVENT_CONTROLLER(gtk_gesture_click_new()); + g_signal_connect(mc_controller, "pressed", + G_CALLBACK(left_butt_down_mapcanvas), NULL); + gtk_widget_add_controller(map_canvas, mc_controller); + mc_gesture = gtk_gesture_click_new(); + gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(mc_gesture), 3); + mc_controller = GTK_EVENT_CONTROLLER(mc_gesture); + g_signal_connect(mc_controller, "pressed", + G_CALLBACK(right_butt_down_mapcanvas), NULL); + gtk_widget_add_controller(map_canvas, mc_controller); + mc_gesture = gtk_gesture_click_new(); + gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(mc_gesture), 2); + mc_controller = GTK_EVENT_CONTROLLER(mc_gesture); + g_signal_connect(mc_controller, "pressed", + G_CALLBACK(middle_butt_down_mapcanvas), NULL); + gtk_widget_add_controller(map_canvas, mc_controller); + g_signal_connect(map_canvas, "resize", G_CALLBACK(map_canvas_resize), NULL); @@ -1514,9 +1533,6 @@ static void setup_widgets(void) g_signal_connect(toplevel, "enter_notify_event", G_CALLBACK(leave_mapcanvas), NULL); - g_signal_connect(map_canvas, "button_press_event", - G_CALLBACK(butt_down_mapcanvas), NULL); - g_signal_connect(map_canvas, "button_release_event", G_CALLBACK(butt_release_mapcanvas), NULL); diff --git a/client/gui-gtk-4.0/mapctrl.c b/client/gui-gtk-4.0/mapctrl.c index 5c034e154b..a4917666d9 100644 --- a/client/gui-gtk-4.0/mapctrl.c +++ b/client/gui-gtk-4.0/mapctrl.c @@ -64,14 +64,12 @@ extern gint cur_x, cur_y; Popup a label with information about the tile, unit, city, when the user used the middle mouse button on the map. **************************************************************************/ -static void popit(GdkEvent *ev, struct tile *ptile) +static void popit(struct tile *ptile) { GtkWidget *p; struct unit *punit; if (TILE_UNKNOWN != client_tile_get_known(ptile)) { - gdouble e_x, e_y; - p = gtk_window_new(); gtk_widget_set_margin_start(p, 4); gtk_widget_set_margin_end(p, 4); @@ -93,9 +91,6 @@ static void popit(GdkEvent *ev, struct tile *ptile) g_signal_connect(p, "destroy", G_CALLBACK(popupinfo_popdown_callback), NULL); - - gdk_event_get_position(ev, &e_x, &e_y); - gtk_widget_show(p); } } @@ -181,19 +176,74 @@ gboolean butt_release_mapcanvas(GtkWidget *w, GdkEvent *ev, gpointer data) } /**********************************************************************//** - Handle all mouse button press on canvas. + Handle all left mouse button presses on canvas. + Future feature: User-configurable mouse clicks. +**************************************************************************/ +gboolean left_butt_down_mapcanvas(GtkGestureClick *gesture, int n_press, + double x, double y) +{ + struct tile *ptile = NULL; + GdkModifierType state; + + if (editor_is_active()) { + return handle_edit_mouse_button_press(gesture, MOUSE_BUTTON_LEFT, + x, y); + } + + if (!can_client_change_view()) { + return TRUE; + } + + gtk_widget_grab_focus(map_canvas); + ptile = canvas_pos_to_tile(x, y); + + state = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(gesture)); + + /* + + LMB : Adjust workers. */ + if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK)) { + adjust_workers_button_pressed(x, y); + } else if (state & GDK_CONTROL_MASK) { + /* + LMB : Quickselect a sea unit. */ + action_button_pressed(x, y, SELECT_SEA); + } else if (ptile && (state & GDK_SHIFT_MASK)) { + /* + LMB: Append focus unit. */ + action_button_pressed(x, y, SELECT_APPEND); + } else if (ptile && (state & GDK_ALT_MASK)) { + /* + LMB: popit (same as middle-click) */ + /* FIXME: we need a general mechanism for letting freeciv work with + * 1- or 2-button mice. */ + popit(ptile); + } else if (tiles_hilited_cities) { + /* LMB in Area Selection mode. */ + if (ptile) { + toggle_tile_hilite(ptile); + } + } else if (rally_set_tile(ptile)) { + /* Nothing here, rally_set_tile() already did what we wanted */ + } else if (infra_placement_mode()) { + infra_placement_set_tile(ptile); + } else { + /* Plain LMB click. */ + action_button_pressed(x, y, SELECT_POPUP); + } + + return TRUE; +} + +/**********************************************************************//** + Handle all right mouse button presses on canvas. Future feature: User-configurable mouse clicks. **************************************************************************/ -gboolean butt_down_mapcanvas(GtkWidget *w, GdkEvent *ev, gpointer data) +gboolean right_butt_down_mapcanvas(GtkGestureClick *gesture, int n_press, + double x, double y) { struct city *pcity = NULL; struct tile *ptile = NULL; - gdouble e_x, e_y; - guint button; GdkModifierType state; if (editor_is_active()) { - return handle_edit_mouse_button_press(ev); + return handle_edit_mouse_button_press(gesture, MOUSE_BUTTON_RIGHT, + x, y); } if (!can_client_change_view()) { @@ -201,101 +251,84 @@ gboolean butt_down_mapcanvas(GtkWidget *w, GdkEvent *ev, gpointer data) } gtk_widget_grab_focus(map_canvas); - gdk_event_get_position(ev, &e_x, &e_y); - ptile = canvas_pos_to_tile(e_x, e_y); + ptile = canvas_pos_to_tile(x, y); pcity = ptile ? tile_city(ptile) : NULL; - button = gdk_button_event_get_button(ev); - state = gdk_event_get_modifier_state(ev); - - switch (button) { - - case 1: /* LEFT mouse button */ - - /* + + LMB : Adjust workers. */ - if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK)) { - adjust_workers_button_pressed(e_x, e_y); - } else if (state & GDK_CONTROL_MASK) { - /* + LMB : Quickselect a sea unit. */ - action_button_pressed(e_x, e_y, SELECT_SEA); - } else if (ptile && (state & GDK_SHIFT_MASK)) { - /* + LMB: Append focus unit. */ - action_button_pressed(e_x, e_y, SELECT_APPEND); - } else if (ptile && (state & GDK_ALT_MASK)) { - /* + LMB: popit (same as middle-click) */ - /* FIXME: we need a general mechanism for letting freeciv work with - * 1- or 2-button mice. */ - popit(ev, ptile); - } else if (tiles_hilited_cities) { - /* LMB in Area Selection mode. */ - if (ptile) { - toggle_tile_hilite(ptile); - } - } else if (rally_set_tile(ptile)) { - /* Nothing here, rally_set_tile() already did what we wanted */ - } else if (infra_placement_mode()) { - infra_placement_set_tile(ptile); - } else { - /* Plain LMB click. */ - action_button_pressed(e_x, e_y, SELECT_POPUP); + state = gtk_event_controller_get_current_event_state( + GTK_EVENT_CONTROLLER(gesture)); + + /* + + RMB : insert city or tile chat link. */ + /* + + + RMB : insert unit chat link. */ + if (ptile && (state & GDK_ALT_MASK) + && (state & GDK_CONTROL_MASK)) { + inputline_make_chat_link(ptile, (state & GDK_SHIFT_MASK) != 0); + } else if ((state & GDK_SHIFT_MASK) && (state & GDK_ALT_MASK)) { + /* + + RMB : Show/hide workers. */ + key_city_overlay(x, y); + } else if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK) + && pcity != NULL) { + /* + RMB: Paste Production. */ + clipboard_paste_production(pcity); + cancel_tile_hiliting(); + } else if (state & GDK_SHIFT_MASK + && clipboard_copy_production(ptile)) { + /* + RMB on city/unit: Copy Production. */ + /* If nothing to copy, fall through to rectangle selection. */ + + /* Already done the copy */ + } else if (state & GDK_CONTROL_MASK) { + /* + RMB : Quickselect a land unit. */ + action_button_pressed(x, y, SELECT_LAND); + } else { + /* Plain RMB click. Area selection. */ + /* A foolproof user will depress button on canvas, + * release it on another widget, and return to canvas + * to find rectangle still active. + */ + if (rectangle_active) { + release_right_button(x, y, + (state & GDK_SHIFT_MASK) != 0); + return TRUE; } - break; + if (hover_state == HOVER_NONE) { + anchor_selection_rectangle(x, y); + rbutton_down = TRUE; /* causes rectangle updates */ + } + } - case 2: /* MIDDLE mouse button */ + return TRUE; +} - /* + MMB: Wake up sentries. */ - if (state & GDK_CONTROL_MASK) { - wakeup_button_pressed(e_x, e_y); - } else if (ptile) { - /* Plain Middle click. */ - popit(ev, ptile); - } - break; +/**********************************************************************//** + Handle all middle mouse button presses on canvas. + Future feature: User-configurable mouse clicks. +**************************************************************************/ +gboolean middle_butt_down_mapcanvas(GtkGestureClick *gesture, int n_press, + double x, double y) +{ + struct tile *ptile = NULL; + GdkModifierType state; - case 3: /* RIGHT mouse button */ - - /* + + RMB : insert city or tile chat link. */ - /* + + + RMB : insert unit chat link. */ - if (ptile && (state & GDK_ALT_MASK) - && (state & GDK_CONTROL_MASK)) { - inputline_make_chat_link(ptile, (state & GDK_SHIFT_MASK) != 0); - } else if ((state & GDK_SHIFT_MASK) && (state & GDK_ALT_MASK)) { - /* + + RMB : Show/hide workers. */ - key_city_overlay(e_x, e_y); - } else if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK) - && pcity != NULL) { - /* + RMB: Paste Production. */ - clipboard_paste_production(pcity); - cancel_tile_hiliting(); - } else if (state & GDK_SHIFT_MASK - && clipboard_copy_production(ptile)) { - /* + RMB on city/unit: Copy Production. */ - /* If nothing to copy, fall through to rectangle selection. */ - - /* Already done the copy */ - } else if (state & GDK_CONTROL_MASK) { - /* + RMB : Quickselect a land unit. */ - action_button_pressed(e_x, e_y, SELECT_LAND); - } else { - /* Plain RMB click. Area selection. */ - /* A foolproof user will depress button on canvas, - * release it on another widget, and return to canvas - * to find rectangle still active. - */ - if (rectangle_active) { - release_right_button(e_x, e_y, - (state & GDK_SHIFT_MASK) != 0); - return TRUE; - } - if (hover_state == HOVER_NONE) { - anchor_selection_rectangle(e_x, e_y); - rbutton_down = TRUE; /* causes rectangle updates */ - } - } - break; + if (editor_is_active()) { + return handle_edit_mouse_button_press(gesture, MOUSE_BUTTON_MIDDLE, + x, y); + } - default: - break; + if (!can_client_change_view()) { + return TRUE; + } + + gtk_widget_grab_focus(map_canvas); + ptile = canvas_pos_to_tile(x, y); + + state = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(gesture)); + + /* + MMB: Wake up sentries. */ + if (state & GDK_CONTROL_MASK) { + wakeup_button_pressed(x, y); + } else if (ptile) { + /* Plain Middle click. */ + popit(ptile); } return TRUE; diff --git a/client/gui-gtk-4.0/mapctrl.h b/client/gui-gtk-4.0/mapctrl.h index 73f235cf74..92f547c6e6 100644 --- a/client/gui-gtk-4.0/mapctrl.h +++ b/client/gui-gtk-4.0/mapctrl.h @@ -22,7 +22,12 @@ #include "mapctrl_g.h" gboolean butt_release_mapcanvas(GtkWidget *w, GdkEvent *ev, gpointer data); -gboolean butt_down_mapcanvas(GtkWidget *w, GdkEvent *ev, gpointer data); +gboolean left_butt_down_mapcanvas(GtkGestureClick *gesture, int n_press, + double x, double y); +gboolean right_butt_down_mapcanvas(GtkGestureClick *gesture, int n_press, + double x, double y); +gboolean middle_butt_down_mapcanvas(GtkGestureClick *gesture, int n_press, + double x, double y); gboolean butt_down_overviewcanvas(GtkWidget *w, GdkEvent *ev, gpointer data); gboolean move_mapcanvas(GtkWidget *w, GdkEvent *ev, gpointer data); gboolean leave_mapcanvas(GtkWidget *widget, GdkEvent *ev); -- 2.35.1