Skip to content

Instantly share code, notes, and snippets.

@KurtJacobson
Last active November 16, 2024 22:04
Show Gist options
  • Select an option

  • Save KurtJacobson/57679e5036dc78e6a7a3ba5e0155dad1 to your computer and use it in GitHub Desktop.

Select an option

Save KurtJacobson/57679e5036dc78e6a7a3ba5e0155dad1 to your computer and use it in GitHub Desktop.
Re-positioning Gtk widget with a mouse drag in C and Python
#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 = w->parent;
// offset == distance of parent widget from edge of screen ...
gdk_window_get_position(p->window, &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(p->allocation.width - w->allocation.width, Sensitivity);
maxy = RoundDownToMultiple(p->allocation.height - w->allocation.height, 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);
gtk_signal_connect(GTK_OBJECT(b), "button_press_event", (GtkSignalFunc)button_press_event, NULL);
gtk_signal_connect(GTK_OBJECT(b), "motion_notify_event", (GtkSignalFunc)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;
}
#!/usr/bin/env python
import os
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
from gi.repository import GObject
# higher values make movement more performant
# lower values make movement smoother
SENSITIVITY = 1
EvMask = Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON1_MOTION_MASK
offsetx = 0
offsety = 0
px = 0
py = 0
maxx = 0
maxy = 0
def Min(a, b):
if b < a:
return b
return a
def Max(a, b):
if b > a:
return b
return a
def RoundDownToMultiple(i, m):
return i/m*m
def RoundToNearestMultiple(i, m):
if i % m > m / 2:
return (i/m+1)*m
return i/m*m
def button_press_event(w, event):
if event.button == 1:
p = w.get_parent()
# offset == distance of parent widget from edge of screen ...
global offsetx, offsety
offsetx, offsety = p.get_window().get_position()
# plus distance from pointer to edge of widget
offsetx += event.x
offsety += 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.
global maxx, maxy
maxx = RoundDownToMultiple(p.get_allocation().width - w.get_allocation().width, SENSITIVITY)
maxy = RoundDownToMultiple(p.get_allocation().height - w.get_allocation().height, SENSITIVITY)
def motion_notify_event(widget, event):
# x_root,x_root relative to screen
# x,y relative to parent (fixed widget)
# px,py stores previous values of x,y
global px, py
global offsetx, offsety
# get starting values for x,y
x = event.x_root - offsetx
y = 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 or y != py:
px = x
py = y
fixed.move(widget, x, y)
def make_button(text):
b = Gtk.Button.new_with_label(text)
b.set_events(EvMask)
b.connect("button_press_event", button_press_event)
b.connect("motion_notify_event", motion_notify_event)
b.show()
return b
# top level window
window = Gtk.Window()
window.connect("destroy", Gtk.main_quit)
# fixed container
fixed = Gtk.Fixed()
window.add(fixed)
# buttons
fixed.put(make_button("A button"), 50, 50)
fixed.put(make_button("Another button"), 250, 100)
window.show_all()
Gtk.main()
@KurtJacobson
Copy link
Copy Markdown
Author

Here are the erratic position values returned by 'motion-notify-event':

erratic

And a video comparing the two examples:
https://youtu.be/mLjwxWCYP2Y

@KurtJacobson
Copy link
Copy Markdown
Author

KurtJacobson commented Aug 18, 2017

This problem was solved by using the event position relative to the screen root rather than to the window by changing

    # get starting values for x,y
    x = event.x - offsetx
    y = event.y - offsety

to

    # get starting values for x,y
    x = event.x_root - offsetx
    y = event.y_root - offsety

@bretantonio
Copy link
Copy Markdown

How do I do this with C++

@Fenshu28
Copy link
Copy Markdown

I get these errors, why is it?
Captura desde 2022-10-27 17-48-43

@Fenshu28
Copy link
Copy Markdown

I get these errors, why is it? Captura desde 2022-10-27 17-48-43

I already corrected the errors, they were due to ambiguities, the updated code for GTK 3 in C:
https://gist.github.com/Fenshu28/cb91acffd95d5a9cee6da2e5e61da5ae

@crab2313
Copy link
Copy Markdown

I recently encountered this problem and write a GTK4 version of this.

https://gist.github.com/crab2313/9b1087677388e4d9156b5668b1794349

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment