How to make multiple views in GTK application - c

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);

Related

GTK4 - how to get coordinates of a moving window

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

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.

GTK3 Set GtkButton size

I have a very simple code, wich create a GtkWindow and place in it a GtkButton.
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request(_window, 800, 450);
gtk_window_set_decorated(GTK_WINDOW (_window), FALSE);
gtk_window_set_position(GTK_WINDOW (_window),GTK_WIN_POS_CENTER_ALWAYS);
gtk_window_set_resizable(GTK_WINDOW (_window), FALSE);
_startbutton = gtk_button_new_with_label("myLabel");
gtk_container_add(GTK_CONTAINER(_window), _startbutton);
gtk_widget_show_all(_window);
Yet, this doesn't work as expected because the button fills the whole window.
I tried to find a way to change the button size, but all the methods that i found use some methods that are deprecated...
Can someone explain to me the way to do this ?
Because the GtkButton is the only control in the GtkWindow, it will be given the entire area of the GtkWindow to fill. If you want to do anything more complicated, you will need to use layout containers like GtkBox and GtkGrid to explicitly lay out the button, usually in relation to other controls that you will also have in the window.
Once you do lay out your controls, you can use expansion and alignment to control how the button makes use of its allotted space.

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.

How to make a Gtk+ widget inside a GtkScrolledWindow to expand when packed into a GtkPane?

I have a program which shows two GtkTreeViews packed inside a GtkPaned (sscce: here):
gtk_paned_add1(GTK_PANED(paned), tree_view1);
gtk_paned_add2(GTK_PANED(paned), tree_view2);
The result is the following:
However, the tables can become bigger, so I added then to GtkScrolledWindows (sscce: here):
GtkWidget *scrolled_window1 = gtk_scrolled_window_new(NULL, NULL),
*scrolled_window2 = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(scrolled_window1), tree_view1);
gtk_container_add(GTK_CONTAINER(scrolled_window2), tree_view2);
gtk_paned_add1(GTK_PANED(paned), scrolled_window1);
gtk_paned_add2(GTK_PANED(paned), scrolled_window2);
However, now the window collapses itself to the point it is almost a thin trance, as in the screenshot below:
If I maximize the window, the first column does not appear (although I can manually expand it):
So, what is the best method of getting the appearance of the first screenshot wen using GtkScrolledWindows in this scenario? Also, could I define the size of the pane columns in relation to one another (for example, 30% for the first one, 70% for the second one?
I've been using this pattern (I'd be happy if someone posted a better answer):
GtkWidget* treeView_, pane1_;
// [Removed code to create and show the widget heirarchy.]
GtkRequisition sizeReq;
gtk_widget_size_request(treeView_, &sizeReq); // get tree's preferred size
gtk_paned_set_position(GTK_PANED(pane1_), sizeReq.width);
This gets pretty close, but you'll probably have a horizontal scrollbar with a few hidden pixels. Since our application remembers the user's adjustments to the pane positions, it only needs to look "good enough" on initial layout.
Also, there are requirements for gtk_widget_size_request() returning something meaningful. In my case, I've invoked a gtk_widget_show_all() on the hierarchy before retrieving the size request.
The solution I adopted was to actually set the size of the scrolled windows (sscce):
gtk_widget_set_size_request(scrolled_window1, 200, 600);
gtk_widget_set_size_request(scrolled_window2, 600, 600);
The result was, as one would expect, a window with 800x600:
I am not satisfied: this approach relies on arbitrary sizes and it seems too "manual". Nonetheless, I want to use my software so I will adopt it for now.

Resources