Widgets accionados por mouse. Arrastra y suelta dentro de la ventana

La comprensi贸n habitual de arrastrar y soltar (D&D) supone que, por ejemplo, un enlace a un archivo se toma de un widget y se mueve con el mouse a otra ventana o widget. A continuaci贸n, hablaremos no sobre las funciones de biblioteca de D&D, sino sobre nuestra propia implementaci贸n de mover el widget dentro de la ventana y la funcionalidad relacionada. El c贸digo es m谩s, por ejemplo, que una aplicaci贸n pr谩ctica concreta, escrita en estilo C con clases. El editor es CodeBlocks 17.12, que dej贸 de fallar en Ubuntu x64 en comparaci贸n con la versi贸n 16.

imagen

Hay un contenedor de widgets GtkFixed, que puede almacenar otros widgets en ciertas coordenadas, el concepto cl谩sico de crear aplicaciones en GTK implica el uso de contenedores de widgets GtkBox (y otros) para estirar correctamente la ventana y llenar el espacio. El contenedor se estira al tama帽o de la ventana (y a los bordes de otros widgets), o se reduce al tama帽o del widget secundario como una regla.

El c贸digo se divide en main.cpp, main.hpp, movable_widgets.hpp. No seleccion茅 el archivo de implementaci贸n por separado. El contenido de main.cpp es bastante t铆pico:

#include "main.hpp" #include "movable_widgets.hpp" void builder_init(gpointer user_data) { appdata *data=(appdata*) user_data; GError *error = NULL; GtkBuilder *builder = gtk_builder_new(); if (!gtk_builder_add_from_file (builder, "window.glade", &error)) { //     g_critical ("   : %s", error->message); g_error_free (error); } data->win=GTK_WIDGET(gtk_builder_get_object(builder, "window1")); data->notebook=GTK_NOTEBOOK(gtk_builder_get_object(builder, "notebook1")); gtk_notebook_remove_page(data->notebook,0); ///    gtk_builder_connect_signals (builder,data); g_clear_object(&builder); } void application_activate(GtkApplication *application, gpointer user_data) { appdata *data=(appdata*) user_data; builder_init(data); gtk_widget_set_size_request(data->win,320,240); gtk_application_add_window(data->app,GTK_WINDOW(data->win)); page_body *page=new page_body(data, G_OBJECT(data->notebook)); const gchar *text ="<span foreground=\"blue\" size=\"x-large\">Blue text</span>" ; GtkWidget *label = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL (label), text); GtkWidget *image=gtk_image_new_from_file("opennet2.gif"); GtkWidget *image2=gtk_image_new_from_file("n_temp.png"); page->add_widget(label,label_t,10,10); page->add_widget(image,image_t,20,20); page->add_widget(image2,image_t,40,40); gtk_widget_show_all(data->win); } void application_shutdown(const GtkApplication *application, gpointer user_data) {} int main (int argc, char *argv[]) { appdata data; gtk_init (&argc, &argv); gint res; data.app = gtk_application_new("gtk3.org", G_APPLICATION_FLAGS_NONE); g_signal_connect(data.app, "activate", G_CALLBACK(application_activate), &data); g_signal_connect(data.app, "shutdown", G_CALLBACK(application_shutdown), &data); res = g_application_run(G_APPLICATION(data.app), 0, NULL); return 0; } 

Algunos widgets se crean a partir de una descripci贸n XML (funci贸n builder_init), otra parte mediante programaci贸n (p谩gina-> add_widget). Funci贸n Gtk_widget_show_all (datos-> win); necesarios para la visualizaci贸n recursiva de widgets y sus contenidos. GTK borra independientemente el contenido de los widgets cuando se eliminan, en particular otros widgets secundarios. Para cuando se ejecuta la funci贸n de devoluci贸n de llamada application_shutdown, la ventana principal y todos los widgets de contenido ya se han eliminado.

 #ifndef MAIN_H #define MAIN_H #include <gtk/gtk.h> #include <stdbool.h> #include <stdlib.h> #define restrict __restrict__ class appdata { public: char *glade_name=(char*)"window.glade"; GtkApplication *restrict app; GtkWidget *restrict win; GtkNotebook *restrict notebook; GArray *restrict pages; }; #endif 

El campo de p谩ginas es una matriz de punteros a clases con el contenido de las p谩ginas, en este ejemplo no se usa, ya que solo se usa 1 pesta帽a. Usar restrict es un aficionado. Te贸ricamente, da un cierto aumento en el rendimiento. En este caso, no hay necesidad de uso.

El widget insertado se coloca en un contenedor del tipo GtkEventBox. El colecciona eventos
clic en el bot贸n Tambi茅n un contenedor opcional del tipo GtkFrame para mostrar un widget en un marco al hacer clic con el bot贸n izquierdo del mouse. La operaci贸n de cambio de contenedor es lo suficientemente r谩pida. La pesta帽a en s铆, donde se insertan los widgets, tiene la siguiente jerarqu铆a de archivos adjuntos: GtkScrolledWindow-> GtkViewport-> GtkFixed. Inicialmente, los widgets son del tipo GtkWidget, que las macros convierten a los tipos GtkViewport, GtkFixed. Yo enfatizar铆a
atenci贸n a la vista macro
InsertedWidgetWithProperty * widget_with_property = & g_array_index (widgets, InsertedWidgetWithProperty, i);
ya que es m谩s f谩cil cometer un error aqu铆. Par谩metros x_correction, y_correction: coordenadas del clic del mouse en relaci贸n con el widget insertado GtkEvent. La bandera button_not_pressed se usa para mostrar correctamente el contenedor del marco. Por l贸gica, se entiende que si se hace clic en uno de los botones del mouse en el widget insertado, entonces debe colocarse en un marco. Es decir, los eventos de hacer clic en el bot贸n y soltar el bot贸n no est谩n emparejados, a diferencia de los eventos de ingresar-notificar-evento y dejar-notificar-evento, que est谩n asociados con un cambio en la forma del cursor. Si los par谩metros x_correction, button_not_pressed son par谩metros de servicio, es decir, deben colocarse en la secci贸n privada, el indicador click_order se usa para mostrar el widget actual encima del resto.

 typedef struct { GtkWidget *restrict widget_ptr; GtkWidget *restrict eventbox; GtkWidget *restrict frame; //   GtkWidget *restrict pmenu; widget_type type; bool button_not_pressed; } InsertedWidgetWithProperty; class page_body { public: GtkWidget *restrict scrolledwindow; GtkWidget *restrict viewport; GtkWidget *restrict fixed; GArray *restrict widgets=g_array_new(FALSE, TRUE, sizeof(InsertedWidgetWithProperty)); GtkAdjustment *restrict h_adj; GtkAdjustment *restrict v_adj; int num_of_current_widget=0; double x_correction=0; double y_correction=0; GtkWidget *restrict window; ///      int widget_count=0; bool click_order=FALSE; //TRUE -  page_body(appdata *data, GObject *container) { window=data->win; h_adj=gtk_adjustment_new(0.0,4.0,900.0,1.0,5.0,10.0); v_adj=gtk_adjustment_new(0.0,4.0,900.0,1.0,5.0,10.0); scrolledwindow=gtk_scrolled_window_new(h_adj, v_adj); viewport=gtk_viewport_new(h_adj, v_adj); fixed=gtk_fixed_new(); gtk_container_add(GTK_CONTAINER(scrolledwindow),GTK_WIDGET(viewport)); gtk_container_add(GTK_CONTAINER(viewport),GTK_WIDGET(fixed)); if(GTK_IS_NOTEBOOK(container)) { gtk_notebook_append_page ((GtkNotebook*)container,scrolledwindow,NULL); } else if(GTK_IS_WIDGET(container)) { gtk_container_add(GTK_CONTAINER(container),scrolledwindow); } g_signal_connect(fixed,"motion-notify-event",G_CALLBACK(fixed_motion_notify), this); g_signal_connect(scrolledwindow,"destroy",G_CALLBACK(scrolled_window_destroy_cb), this); } ~page_body() { int i=widgets->len; if(widget_count>0) { for(i; i>=0; i--) { InsertedWidgetWithProperty *widget_with_property; widget_with_property=&g_array_index(widgets,InsertedWidgetWithProperty,i); } } g_array_free(widgets,TRUE); } void add_widget(GtkWidget *widget, widget_type type, int x, int y) { ++widget_count; InsertedWidgetWithProperty *widget_with_property=(InsertedWidgetWithProperty*) g_malloc0(sizeof(InsertedWidgetWithProperty)); widget_with_property->eventbox=gtk_event_box_new(); widget_with_property->type=type; widget_with_property->widget_ptr=widget; gtk_container_add(GTK_CONTAINER(widget_with_property->eventbox),widget); gtk_fixed_put(GTK_FIXED(fixed),widget_with_property->eventbox,x,y); widget_with_property->pmenu=gtk_menu_new(); GtkWidget *menu_items = gtk_menu_item_new_with_label (""); gtk_widget_show(menu_items); gtk_menu_shell_append (GTK_MENU_SHELL (widget_with_property->pmenu), menu_items); g_signal_connect(widget_with_property->eventbox,"button-press-event",G_CALLBACK(eventbox_press_cb),this); g_signal_connect(widget_with_property->eventbox,"button-release-event",G_CALLBACK(eventbox_release_cb),this); g_signal_connect(menu_items,"activate",G_CALLBACK(menu_delete_activate),this); g_signal_connect(widget_with_property->eventbox,"leave-notify-event",G_CALLBACK(eventbox_leave_cb),this); g_signal_connect(widget_with_property->eventbox,"enter-notify-event",G_CALLBACK(eventbox_enter_cb),this); gtk_widget_set_events(widget_with_property->eventbox,GDK_LEAVE_NOTIFY_MASK|GDK_ENTER_NOTIFY_MASK|GDK_STRUCTURE_MASK); g_array_append_val(widgets, *widget_with_property); } inline void change_cursor(char *cursor_name) { GdkDisplay *display; GdkCursor *cursor; display = gtk_widget_get_display (window); if(cursor_name) cursor = gdk_cursor_new_from_name (display, cursor_name); else cursor = gdk_cursor_new_from_name (display, "default"); GdkWindow *gdkwindow=gtk_widget_get_window (window); gdk_window_set_cursor (gdkwindow, cursor); } inline void delete_widget(int i) { InsertedWidgetWithProperty *widget_with_property= &g_array_index(this->widgets,InsertedWidgetWithProperty,i); GtkWidget *eventbox=widget_with_property->eventbox; g_object_ref(eventbox); gtk_container_remove(GTK_CONTAINER(this->fixed),eventbox); if(widget_with_property->frame!=NULL) { gtk_widget_destroy(widget_with_property->frame); } gtk_widget_destroy(widget_with_property->eventbox); this->widgets=g_array_remove_index_fast(this->widgets,i); --this->widget_count; } }; 

La f贸rmula para calcular las coordenadas del widget para que no se mueva en ninguna direcci贸n cuando hace clic con el bot贸n del mouse sobre 茅l. Implica las coordenadas del clic en relaci贸n con el widget,
Coordenadas GtkFixed relativas a la ventana de la aplicaci贸n, coordenadas de la ventana relativas a la pantalla.
El factor de correcci贸n +25 me confunde un poco, pero no s茅 qu茅 tan f谩cil de escribir. Verifiqu茅 el trabajo en las versiones de Ubuntu 15.10, 16.04, 18.04. La comparaci贸n con 0 y -1 se lleva a cabo para que el widget insertado no se elimine del 谩rea desplazable. El desplazamiento en s铆 lo proporciona el widget GtkScrolledWindow.

 gboolean fixed_motion_notify (GtkWidget *widget, GdkEvent *event, gpointer user_data) { page_body *page=(page_body*) user_data; int x_win, y_win, x_fixed, y_fixed; gtk_window_get_position(GTK_WINDOW(page->window),&x_win,&y_win); gtk_widget_translate_coordinates(page->window,page->fixed,x_win,y_win,&x_fixed,&y_fixed); double correction_y=(-y_fixed+y_win)*2+25; double correction_x=(-x_fixed+x_win); double x_corr=page->x_correction; double y_corr=page->y_correction; int position_x=event->motion.x_root-x_corr-x_win-correction_x; int position_y=event->motion.y_root-y_corr-y_fixed-correction_y; InsertedWidgetWithProperty *widget_with_property=&g_array_index(page->widgets,InsertedWidgetWithProperty,page->num_of_current_widget); GtkWidget *fixed=page->fixed; GtkWidget *eventbox=widget_with_property->eventbox; if(position_x<-1) position_x=0; if(position_y<-1) position_y=0; gtk_fixed_move(GTK_FIXED(fixed), eventbox, position_x, position_y); return FALSE; } 

El resto de las funciones de devoluci贸n de llamada. Se implement贸 la eliminaci贸n de widgets individuales a trav茅s del men煤 contextual con el bot贸n derecho del mouse. La eliminaci贸n de la clase page_body se cuelga del evento de eliminaci贸n GtkScrolledWindow.

 void scrolled_window_destroy_cb (GtkWidget *object, gpointer user_data) { page_body *page=(page_body*) user_data; delete page; } void menu_delete_activate (GtkMenuItem *menuitem, gpointer user_data) { page_body *page=(page_body*) user_data; page->delete_widget(page->num_of_current_widget); } gboolean eventbox_leave_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data) { page_body *page=(page_body*) user_data; page->change_cursor(NULL); } gboolean eventbox_enter_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data) { page_body *page=(page_body*) user_data; page->change_cursor("pointer"); } gboolean eventbox_press_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data) { page_body *page=(page_body*) user_data; page->x_correction=event->button.x; page->y_correction=event->button.y; int i=0; InsertedWidgetWithProperty *widget_compare; for(i; i<=page->widgets->len; i++) { widget_compare=(InsertedWidgetWithProperty*) page->widgets->data+i; if(widget==widget_compare->eventbox) { page->num_of_current_widget=i; break; } } if(widget_compare->button_not_pressed==FALSE) { GtkWidget *eventbox=widget_compare->eventbox; if(page->click_order) { int x, y; gtk_widget_translate_coordinates(page->fixed, eventbox,0,0,&x, &y); gtk_container_remove(GTK_CONTAINER(page->fixed),eventbox); gtk_fixed_put(GTK_FIXED(page->fixed),eventbox,-x,-y); } g_object_ref(widget_compare->widget_ptr); gtk_container_remove(GTK_CONTAINER(eventbox),widget_compare->widget_ptr); if(widget_compare->frame==NULL) widget_compare->frame=gtk_frame_new(NULL); gtk_container_add(GTK_CONTAINER(widget_compare->frame),widget_compare->widget_ptr); gtk_container_add(GTK_CONTAINER(eventbox),widget_compare->frame); gtk_widget_show_all(eventbox); widget_compare->button_not_pressed=TRUE; } ///   const gint RIGHT_CLICK = 3; if (event->type == GDK_BUTTON_PRESS) { GdkEventButton *bevent = (GdkEventButton *) event; if (bevent->button == RIGHT_CLICK) { gtk_menu_popup(GTK_MENU(widget_compare->pmenu), NULL, NULL, NULL, NULL, bevent->button, bevent->time); } } return FALSE; } gboolean eventbox_release_cb (GtkWidget *eventbox, GdkEvent *event, gpointer user_data) { page_body *page=(page_body*) user_data; InsertedWidgetWithProperty *widget_with_property= &g_array_index(page->widgets,InsertedWidgetWithProperty,page->num_of_current_widget); ///      ,    if(widget_with_property->button_not_pressed==TRUE) { widget_with_property->frame=(GtkWidget*) g_object_ref(widget_with_property->frame); widget_with_property->widget_ptr=(GtkWidget*) g_object_ref(widget_with_property->widget_ptr); GtkWidget *frame=widget_with_property->frame; GtkWidget *widget=widget_with_property->widget_ptr; gtk_container_remove(GTK_CONTAINER(eventbox), frame); gtk_container_remove(GTK_CONTAINER(frame), widget); gtk_container_add(GTK_CONTAINER(eventbox), widget); widget_with_property->button_not_pressed=FALSE; } } 

Gracias por su atencion

github.com/SanyaZ7/movable_widgets_on_GtkFixed-

Source: https://habr.com/ru/post/473716/


All Articles