对拖放(D&D)的通常理解是假设,例如,一个文件的链接是从一个窗口小部件获取的,然后用鼠标将其移动到另一个窗口或窗口小部件。 接下来,我们将不讨论D&D的库功能,而是讨论我们自己在窗口内移动小部件的实现以及相关功能。 该代码比用C语言编写的带有类的具体实际应用更多。 编辑器是CodeBlocks 17.12,与第16版相比,它在Ubuntu x64上不再崩溃。
有一个小部件容器GtkFixed,它可以在特定坐标处存储其他小部件,在GTK上创建应用程序的经典概念涉及使用小部件容器GtkBox(及其他)正确拉伸窗口并填充空间。 通常,将容器拉伸到窗口的大小(以及其他窗口小部件的边界),或减小到子窗口小部件的大小。
该代码分为main.cpp,main.hpp,movable_widgets.hpp。 我没有单独选择实现文件。 main.cpp的内容非常典型:
#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)) {
一些窗口小部件是通过XML描述(函数builder_init)创建的,另一部分是通过编程方式创建的(页面-> add_widget)。 Gtk_widget_show_all函数(data-> win); 递归显示小部件及其内容所需的内容。 GTK在删除小部件(尤其是其他子小部件)时会独立清除其内容。 在执行application_shutdown回调函数时,主窗口和所有内容窗口小部件已被删除。
#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
pages字段是指向包含页面内容的类的指针的数组,在此示例中,由于仅使用了1个制表符,因此未使用。 使用限制是一个业余爱好者。 从理论上讲,可以提高性能。 在这种情况下,无需使用。
小部件本身放置在GtkEventBox类型的容器中。 他收集事件
buttonclick。 也是GtkFrame类型的可选容器,用于在单击鼠标左键时在框架中显示小部件。 容器更换操作足够快。 插入小部件的选项卡本身具有以下附件层次结构:GtkScrolledWindow-> GtkViewport-> GtkFixed。 最初,小部件的类型为GtkWidget,它通过宏转换为GtkViewport,GtkFixed类型。 我会强调
注意宏观观点
InsertedWidgetWithProperty * widget_with_property =&g_array_index(widgets,InsertedWidgetWithProperty,i);
因为在这里犯错最简单。 参数x_correction,y_correction-相对于插入的窗口小部件GtkEvent的鼠标单击的坐标。 button_not_pressed标志用于正确显示框架容器。 通过逻辑,可以理解,如果在插入的窗口小部件上单击了鼠标按钮之一,则应将其放置在框架中。 也就是说,buttonclick和buttonrelease事件没有配对,这与与光标形状的更改相关联的enter-notify-event事件和leave-notify-event事件不同。 如果x_correction,button_not_pressed参数是服务参数,也就是说,应将它们放置在私有部分中,则click_order标志用于将当前窗口小部件显示在其余窗口之上。
typedef struct { GtkWidget *restrict widget_ptr; GtkWidget *restrict eventbox; GtkWidget *restrict frame;
用于计算小部件的坐标的公式,以便在您单击小部件上的鼠标键时它不会向任何方向移动。 它涉及点击相对于小部件的坐标,
GtkFixed相对于应用程序窗口的坐标,窗口相对于屏幕的坐标。
校正因子+25使我有些困惑,但是我不知道写起来更简单。 我检查了Ubuntu 15.10、16.04、18.04版本的工作。 与0和-1进行比较,以使插入的窗口小部件不会从可滚动区域中删除。 滚动本身由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; }
其余的回调函数。 通过鼠标右键单击上下文菜单实现了单个小部件的删除。 GtkScrolledWindow删除事件已挂起删除page_body类。
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; }
谢谢您的关注。
github.com/SanyaZ7/movable_widgets_on_GtkFixed-