O entendimento usual de Arrastar e Soltar (D&D) pressupõe que, por exemplo, um link para um arquivo seja retirado de um widget e movido com o mouse para outra janela ou widget. Em seguida, falaremos não sobre as funções da biblioteca de D&D, mas sobre nossa própria implementação de mover o widget dentro da janela e as funcionalidades relacionadas. O código é mais, por exemplo, que aplicação prática concreta, escrita em estilo C com classes. O editor é o CodeBlocks 17.12, que parou de funcionar no Ubuntu x64 em comparação com a 16ª versão.
Há um contêiner de widget GtkFixed, que pode armazenar outros widgets em determinadas coordenadas, o conceito clássico de criação de aplicativos no GTK envolve o uso de contêineres de widget GtkBox (e outros) para esticar corretamente a janela e preencher o espaço. O contêiner é estendido para o tamanho da janela (e para as bordas de outros widgets) ou reduzido para o tamanho do widget filho como regra.
O código é dividido em main.cpp, main.hpp, movable_widgets.hpp. Não selecionei o arquivo de implementação separadamente. O conteúdo do main.cpp é 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)) {
Alguns widgets são criados a partir de uma descrição XML (função builder_init), outra parte programaticamente (página-> add_widget). Função Gtk_widget_show_all (dados-> vitória); necessário para a exibição recursiva de widgets e seu conteúdo. O GTK limpa independentemente o conteúdo dos widgets quando eles são excluídos, em particular outros widgets filhos. Quando a função de retorno de chamada application_shutdown é executada, a janela principal e todos os widgets de conteúdo já foram excluídos.
#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
O campo de páginas é uma matriz de ponteiros para classes com o conteúdo das páginas; este exemplo não é usado, pois 1 guia é usada apenas. Usar restringir é um amador. Teoricamente, dá um certo aumento no desempenho. Nesse caso, não há necessidade de uso.
O próprio widget é colocado em um contêiner do tipo GtkEventBox. Ele coleciona eventos
buttonclick. Também um contêiner opcional do tipo GtkFrame para exibir um widget em um quadro ao clicar no botão esquerdo do mouse. A operação de troca de contêiner é rápida o suficiente. A própria guia, onde os widgets são inseridos, possui a seguinte hierarquia de anexos: GtkScrolledWindow-> GtkViewport-> GtkFixed. Inicialmente, os widgets são do tipo GtkWidget, que é convertido por macros nos tipos GtkViewport, GtkFixed. Eu enfatizaria
atenção à visão macro
InsertedWidgetWithProperty * widget_with_property = & g_array_index (widgets, InsertedWidgetWithProperty, i);
já que é mais simples cometer um erro aqui. Parâmetros x_correction, y_correction - coordenadas do clique do mouse em relação ao widget inserido GtkEvent. O sinalizador button_not_pressed é usado para exibir corretamente o contêiner do quadro. Por lógica, entende-se que, se um dos botões do mouse for clicado no widget inserido, ele deverá ser colocado em um quadro. Ou seja, os eventos buttonclick e buttonrelease não são pareados, ao contrário dos eventos enter-notify-event e leave-notify-event, que estão associados a uma alteração na forma do cursor. Se os parâmetros x_correction, button_not_pressed forem parâmetros de serviço, ou seja, eles devem ser colocados na seção privada, o sinalizador click_order será usado para exibir o widget atual sobre o resto.
typedef struct { GtkWidget *restrict widget_ptr; GtkWidget *restrict eventbox; GtkWidget *restrict frame;
A fórmula para calcular as coordenadas do widget para que ele não se mova em nenhuma direção quando você clica no botão do mouse. Envolve as coordenadas do clique em relação ao widget,
Coordenadas GtkFixed em relação à janela do aplicativo, coordenadas da janela em relação à tela.
O fator de correção +25 me confunde um pouco, mas não sei como é mais simples escrever. Eu verifiquei o trabalho nas versões do Ubuntu 15.10, 16.04, 18.04. A comparação com 0 e -1 é realizada para que o widget inserido não seja removido da área de rolagem. A rolagem em si é fornecida pelo 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; }
O restante das funções de retorno de chamada. Implementou a remoção de widgets individuais através do menu de contexto com o botão direito do mouse. A remoção da classe page_body está suspensa no evento de exclusão 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; }
Obrigado pela atenção.
github.com/SanyaZ7/movable_widgets_on_GtkFixed-