Skip to content

Instantly share code, notes, and snippets.

@Fenshu28
Created October 27, 2022 23:20
Show Gist options
  • Save Fenshu28/cb91acffd95d5a9cee6da2e5e61da5ae to your computer and use it in GitHub Desktop.
Save Fenshu28/cb91acffd95d5a9cee6da2e5e61da5ae to your computer and use it in GitHub Desktop.
Drggable widget in GTK 3 with C
#include <gtk/gtk.h>
// Source:
// http://www.linuxforums.org/forum/programming-scripting/117713-gtk-moving-widget-mouse-post906490.html#post906490
// higher values make movement more performant
// lower values make movement smoother
const gint Sensitivity = 1;
const gint EvMask = GDK_BUTTON_PRESS_MASK | GDK_BUTTON1_MOTION_MASK;
GtkWidget *fixed;
int offsetx, offsety, px, py, maxx, maxy;
inline static int Min(const int a, const int b) { return b < a ? b : a; }
inline static int Max(const int a, const int b) { return b > a ? b : a; }
inline static int RoundDownToMultiple(const int i, const int m)
{
return i/m*m;
}
inline static int RoundToNearestMultiple(const int i, const int m)
{
if (i % m > (double)m / 2.0d)
return (i/m+1)*m;
return i/m*m;
}
static void destroy( GtkWidget *widget, gpointer data ) { gtk_main_quit (); }
static gboolean button_press_event( GtkWidget *w, GdkEventButton *event )
{
if (event->button == 1) {
GtkWidget* p = gtk_widget_get_parent(w);
// offset == distance of parent widget from edge of screen ...
gdk_window_get_position(gtk_widget_get_window(p), &offsetx, &offsety);
// plus distance from pointer to edge of widget
offsetx += (int)event->x;
offsety += (int)event->y;
// maxx, maxy both relative to the parent
// note that we're rounding down now so that these max values don't get
// rounded upward later and push the widget off the edge of its parent.
maxx = RoundDownToMultiple(gtk_widget_get_allocated_width(p) - gtk_widget_get_allocated_width(w), Sensitivity);
maxy = RoundDownToMultiple(gtk_widget_get_allocated_height(p) - gtk_widget_get_allocated_height(w), Sensitivity);
}
return TRUE;
}
static gboolean motion_notify_event( GtkWidget *widget, GdkEventMotion *event )
{
// x_root,x_root relative to screen
// x,y relative to parent (fixed widget)
// px,py stores previous values of x,y
// get starting values for x,y
int x = (int)event->x_root - offsetx;
int y = (int)event->y_root - offsety;
// make sure the potential coordinates x,y:
// 1) will not push any part of the widget outside of its parent container
// 2) is a multiple of Sensitivity
x = RoundToNearestMultiple(Max(Min(x, maxx), 0), Sensitivity);
y = RoundToNearestMultiple(Max(Min(y, maxy), 0), Sensitivity);
if (x != px || y != py) {
px = x;
py = y;
gtk_fixed_move(GTK_FIXED(fixed), widget, x, y);
}
return TRUE;
}
GtkWidget* make_button(const gchar* const text)
{
GtkWidget* b = gtk_button_new_with_label(text);
gtk_widget_add_events(b, EvMask);
g_signal_connect(b, "button_press_event", G_CALLBACK(button_press_event), NULL);
g_signal_connect(b, "motion_notify_event",G_CALLBACK(motion_notify_event), NULL);
gtk_widget_show(b);
return b;
}
int main(int argc, char *argv[] )
{
gtk_init (&argc, &argv);
// top level window
GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width (GTK_CONTAINER (window), 0);
g_signal_connect (G_OBJECT (window), "destroy",G_CALLBACK (destroy), NULL);
// fixed container
fixed=gtk_fixed_new();
gtk_container_add((GtkContainer*)window,(GtkWidget*)fixed);
gtk_widget_show (fixed);
// buttons
gtk_fixed_put(GTK_FIXED(fixed), make_button("A button"), 50, 50);
gtk_fixed_put(GTK_FIXED(fixed), make_button("Another button"), 250, 100);
gtk_widget_show (window);
gtk_main ();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment