GTK4 - how to get coordinates of a moving window - c

In previous versions of GTK, we added a necessary event mask, attached to a configure-event.
gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);
g_signal_connect(G_OBJECT(window), "configure-event",
G_CALLBACK(configure_callback), NULL);
We got the corresponding x,y coordinates from the handler.
void configure_callback(GtkWindow *window,
GdkEvent *event, gpointer data) {
int x, y;
x = event->configure.x;
y = event->configure.y;
...
}
The closest thing that resembles this is GtkEventControllerMotion, but it is
for mouse pointer, not for window move events.
How to do it in GTK4?

You can't. This is because not all window managers/compositors provide this information, for privacy and/or technical reasons.
Wayland, for example, does not provide window coordinates, because not every Wayland compositor is even a 2D rectangle - for example, kiosk compositors like cage or gamescope, or VR compositors like wxrd.
Because of this, the functionality was removed from GTK4. You will need to use an X11-specific API to get this information, but note that you would need to force your application to use X11 (over XWayland on Wayland compositors)
https://docs.gtk.org/gtk4/migrating-3to4.html#adapt-to-gtkwindow-api-changes

Related

GtkScrolledWindow Not Responding to Mouse Wheel Unless Cursor is over Scroll Bar

I have a C GTK3 application using a GtkScrolledWindow with a GtkGrid inside it. I'm happy with how everything is laid out, but the ScrolledWindow only scrolls via the mouse wheel if the cursor is over the scroll bar, or if I move the scroll bar with my cursor. The behavior I'm looking for is for the mouse wheel to always scroll the ScrolledWindow when the mouse wheel is used or at least when the cursor is over the ScrolledWindow (which is what I thought the default would be).
Here is where the ScrolledWindow is created :
scroll = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scroll), BANNER_HIGHT * 4);
(The location of these lines in the application (GitHub))
I'm wondering now if I have to pass a GtkAdjustment to the constructor but it seems like all that does is set the scrolling bounds. I'm also wondering if I could connect a mousescroll event on the main window and try to trigger it manually on the callback, but I don't see a way to do that either.
This actually seems to be a bug in GTK and/or the OS (Ubuntu), I was trying to use the multi-touch scroll gesture on my laptop's trackpad to scroll and that wasn't working. Today, I happened to have a regular mouse with an actual scroll wheel plugged in and noticed it works just fine.
UPDATE: I added the behavior I was looking for by subscribing to scroll events and adjusting the Scrolled Window manually using a GtkAdjustment after all:
bool scroll_entrires(GtkWidget * widget, GdkEvent * event, gpointer data) {
double delta_x, delta_y; // delta_y is not used
if (gdk_event_get_scroll_deltas(event, &delta_x, &delta_y)) {
GtkAdjustment * adj = gtk_scrolled_window_get_vadjustment(
GTK_SCROLLED_WINDOW(scroll));
gtk_adjustment_set_value(adj,
gtk_adjustment_get_value(adj) + 30 * delta_y);
// 30 is a number that I like for my setup but can
// be changed for other situations.
return true; // We have responded to the event
}
return false; // Let other handlers respond to this event
}
// ... (In my init code)
g_signal_connect(window, "scroll-event", G_CALLBACK(scroll_entries), NULL);

How to make multiple views in GTK application

I want to make a GTK+ application with multiple views and I don't really know how to achieve it in the best possible way. In every view i need to have some labels and buttons. Firstly I tried to make the appliaction with GtkStack, but StackSwitcher has poor customization options (its buttons are in row and stackswitcher icon's are too small, even with maximum possible size with “icon-size” property).
Connecting stack's page switching with normal buttons would solve the case, but i have no idea how to do such a thing.
My second approach was with multiple windows. I was able to make a few windows and hiding/showing them with buttons. Unfortunately the app will be working on quite bad pc (what's more pc is connected to touchscreen what make it's performance even worse) and after some tests I can say that app has some lags. The whole thing makes all windows in the beggining and then just hide them or show them (depends on what button on which window was pressed).
To sum up my question:
what is the most optimal way to make such an application? With multiple windows or with GtkStack?
If with windows how to optimize the whole thing?
if with stack how to implement switchng stack's tab on normal buttons?
I prefer GtkStack. It has awesome gtk_stack_set_visible_child_name, which let's you set visible child by it's ID. In the following snippet I use GtkListBox for switching (and I had to store a GPtrArray with child names)
static void
row_activated (GtkListBox *box,
GtkListBoxRow *row,
gpointer udata)
{
MyWid *self = udata;
MyWidPrivate *priv = self->priv;
gint row_index = gtk_list_box_row_get_index (row);
gchar *path = g_ptr_array_index (priv->paths, row_index);
gtk_stack_set_visible_child_name (priv->stack, path);
}
If you want to use GtkButton thing are even more simple:
gchar *id; // just a string, that allows you to connect buttons and tabs
GtkWidget *child, *button;
child = create_tab_for_id (id); // not a real function! You should define it yourself
gtk_stack_add_named (stack, child, id);
button = create_button_for_id (id); // also not a real function
/* Time for magic */
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (gtk_stack_set_visible_child_name),
stack);

How to send GtkWidgets to the back or front?

I wonder if there is a way to "Send to back" or "Bring to front" a GtkWidget dynamically. ( I know, it can be done by changing the creating order of GtkWidget )
example : (GTK+ 2.0, Cent OS 7.0, C)
#include <gtk/gtk.h>
int main( int argc, char *argv[] )
{
GtkWidget *window, *button1, *button2, *fixed;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
button1 = gtk_button_new_with_label("A button");
button2 = gtk_button_new_with_label("B button");
fixed = gtk_fixed_new();
gtk_fixed_put( (GtkFixed*)fixed,button2, 30, 30 );
gtk_fixed_put( (GtkFixed*)fixed,button1, 50, 50 );
gtk_container_add(GTK_CONTAINER(window), fixed);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
this program will display : (the first picture)
Are there some commands that can make the "B Button" be in front of "A Button". ( look likes this following picture --> the second picture )
No, there aren't "functions" like those in a GtkFixed.
From the Gtk 2 documentation:
Description
The GtkFixed widget is a container which can place child widgets at
fixed positions and with fixed sizes, given in pixels. GtkFixed
performs no automatic layout management.
For most applications, you should not use this container! It keeps you
from having to learn about the other GTK+ containers, but it results
in broken applications. With GtkFixed, the following things will
result in truncated text, overlapping widgets, and other display bugs:
Themes, which may change widget sizes.
Fonts other than the one you used to write the app will of course change the size of widgets containing text; keep in mind that users
may use a larger font because of difficulty reading the default, or
they may be using Windows or the framebuffer port of GTK+, where
different fonts are available.
Translation of text into other languages changes its size. Also, display of non-English text will use a different font in many cases.
In addition, the fixed widget can't properly be mirrored in
right-to-left languages such as Hebrew and Arabic. i.e. normally GTK+
will flip the interface to put labels to the right of the thing they
label, but it can't do that with GtkFixed. So your application will
not be usable in right-to-left languages.
Finally, fixed positioning makes it kind of annoying to add/remove GUI
elements, since you have to reposition all the other elements. This is
a long-term maintenance problem for your application.
If you know none of these things are an issue for your application,
and prefer the simplicity of GtkFixed, by all means use the widget.
But you should be aware of the tradeoffs.
The order by which they are added is the order they will keep in the virtual Z axis. You can control this by removing and readding them or other similar approaches.

How can I capture the word over which a right click is made in GTK?

I have a GtkTextView widget and I want to display a custom popup menu when the user right-clicks on a word. In order to display the appropriate menu, I need to know what word is located at that position.
Like the following:
I'm reading the docs but I haven't found the way yet.
Does anyone know how to do that?
I've got it working by connecting the button-press-event signal from GtkTextView to the function that creates the menu
g_signal_connect(text_view, "button-press-event", G_CALLBACK(right_click_menu), NULL);
the function handling the menu creation will ignore the event when it's not generated by the button expected (right-click)
if(event->button.button != 3)
return FALSE;
getting the word is just a matter of calling gtk_text_view_window_to_buffer_coords with the coordinates that were passed in with the GdkEvent
int x, y;
gtk_text_view_window_to_buffer_coords( GTK_TEXT_VIEW(text_view),
GTK_TEXT_WINDOW_WIDGET,
event->button.x,
event->button.y,
&x,
&y );
and asking for an iterator with
GtkTextIter iter;
gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(text_view), &iter, x, y);
then using the GtkTextIter functions (https://developer.gnome.org/gtk3/stable/GtkTextIter.html) to determine the word bounds.
GTK+ uses the Pango library for it's text layouting. The Pango API provides the function pango_layout_xy_to_index() which when given an co-ordinate inside the layout will provide you with the byte index into the text that the layout was created for.
With this information you can identify the word that the given character is inside by searching forwards/backwards for the appropriate punctuation.
If you are using GtkEntry as your widget then to translate the mouse co-ordinates from the click into layout co-ordinates then will need to use gtk_entry_get_layout_offsets() to retrieve the position of the layout relative to the widget.

capture mouse with Xlib

I want to write a simple Xlib program changing the mouse behavior (to give an example, invert vertical movement). I have a problem with capturing the events.
I would like the code to
capture changes in the controllers position (I move mouse upward, MotionEvent)
calculate new cursor position (new_x -= difference_x)
set new cursor position ( move pointer down, XWarpPointer, prevent event generation here)
The code below should capture a motion event every time the mouse is moved, but it generates the event only when the pointer moves from one window to another... How to capture all the movement events?
#include "X11/Xlib.h"
#include "stdio.h"
int main(int argc, char *argv[])
{
Display *display;
Window root_window;
XEvent event;
display = XOpenDisplay(0);
root_window = XRootWindow(display, 0);
XSelectInput(display, root_window, PointerMotionMask );
while(1) {
XNextEvent( display, &event );
switch( event.type ) {
case MotionNotify:
printf("x %d y %d\n", event.xmotion.x, event.xmotion.y );
break;
}
}
return 0;
}
Related:
X11: How do I REALLY grab the mouse pointer?
When your program receives mouse events, it receives a copy of the events; copies are also sent to other programs that are listening for those events (see XSelectInput(3)). You cannot override this without using XGrabPointer(3) to take exclusive ownership of the mouse, which will prevent other programs from receiving any mouse events. In short, you can't actually do what you are trying to do.
Note also that if a client has specified PointerMotion in its do-not-propagate mask for one of its windows, you will not receive any pointer motion events within its window (again, unless you do a grab).
If you want to change the behavior of the mouse when it is being moved, I suggest you to play with the input properties instead of trying to do the processing in your program.
xinput --list
xinput --list-props 'USB Optical Mouse'
xinput --set-prop 'USB Optical Mouse' 'Evdev Axis Inversion' 1 0
xinput --set-prop 'USB Optical Mouse' 'Evdev Axes Swap' 1
There's also the 'Coordinate Transformation Matrix' property but for some reason it's not working for me right now.
You don't need to call the xinput program yourself: you can use Xlib calls (look at xinput's source code).

Resources