C Gtk Issue: Adding a VBox to the Main Window - c

I'm writing a C game using the GTK library for the interface. The idea is to have two main containers: one for holding the buttons and other widgets for letting the user choose settings, and the other for displaying and moving the images during the actual gameplay. I'm using a VBox to hold the menu widgets, and a Fixed container to hold the game sprites. I have here all of the lines of code relating to the VBox and Fixed containers in the main() method:
GtkWidget* vbox;
GtkWidget* fixed;
...
int main(int argc, char** argv) {
// (The rest of this code block is in the main method)
...
// Make a vertical box for the menu widgets.
vbox = gtk_vbox_new(TRUE, 0);
// Add the menu widgets to the vbox.
gtk_box_pack_start(GTK_BOX(vbox), label1, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), button1, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), button2, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, TRUE, 0);
// Make a new fixed container, which allows its children
// widgets to be moved dynamically.
fixed = gtk_fixed_new();
// Add the game widgets to the fixed container.
gtk_fixed_put(GTK_FIXED(fixed), player1, x, y);
gtk_fixed_put(GTK_FIXED(fixed), player2, x, y + 40);
gtk_fixed_put(GTK_FIXED(fixed), ball, x + 80, y);
gtk_fixed_put(GTK_FIXED(fixed), wall, x + 120, y);
// Add the fixed container to the window.
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show(vbox);
gtk_widget_show(fixed);
In one of my buttons' actions, I have the following, to initiate gameplay:
gtk_container_remove(GTK_CONTAINER(window), vbox);
gtk_container_add(GTK_CONTAINER(window), fixed);
Which works without any issue. However, after a certain condition is met, I have another function called, which does the opposite:
gtk_container_remove(GTK_CONTAINER(window), fixed);
gtk_container_add(GTK_CONTAINER(window), vbox);
This causes the following issue:
(a.out:11762): Gtk-CRITICAL **: IA__gtk_container_add: assertion `GTK_IS_WIDGET (widget)' failed
I have looked at the widget hierarchy, and verified that VBox falls under the category of GtkWidget. The above error did not occur when I initially added the VBox to the window in the main() method. Is there something I'm not aware of when adding this container to the window after gtk_main() has been called?

The problem is taht GtkWidgets are reference counted but you don't own any reference. This is usual for GtkWidgets, because as long as the widget is visible the window system holds one reference to them. But as long as you remove the widget from its container, the reference count drops to zero and it is destroyed.
The first time it works because there is a floating reference, that exist just after the widget is first created.
Your easiest solution is just to hold a reference to your two moving widgets: call g_object_ref_sink just after creating them to convert the floating reference to a real one. But don't forget to call g_object_unref when you are done with them!
Other option would be to just increment/decrement the counter while you are moving them around, but you still need to sink the reference to fixed to work as expected:
g_object_ref_sink(fixed);
Then:
g_object_ref(vbox);
gtk_container_remove(GTK_CONTAINER(window), vbox);
gtk_container_add(GTK_CONTAINER(window), fixed);
g_object_unref(fixed);
And:
g_object_ref(fixed);
gtk_container_remove(GTK_CONTAINER(window), fixed);
gtk_container_add(GTK_CONTAINER(window), vbox);
g_object_unref(vbox);

Related

How to use images in GTK Stack Switcher using C

I'm making application in gtk using C. I have a GtkStack with GtkStackSwitcher and I don't know how to set images/icons to buttons in stack switcher. I had similar problem with application in gtkmm and C++ but I was able to find required function in the documentation. This time, after searching the documentation for GtkStack, GtkStackSwitcher and GtkContainer, I didn't find anything useful in GtkStack and GtkStackSwitcher. In GtkContainer there is function gtk_container_child_set_property (). It may be the function I'm looking for but I have no idea how to put an icon-name into GValue and if it's possible.
To sum up - can I set icon to GtkStackSwitcher's button with mentioned functions or using any other method?
Edit:
Maybe it's possible to achieve this with css? Setting background-image for GtkStack and GtkStackSwticher doesn't work but setting background_image for buttons works. Works very bad but works. The image doesn't fit the button and button doesn't resize to be the image size (If i set button new from pixbuf the button does resize). So is it possible with css or is it a dead end?
From the GtkStack documentation, at Child Properties, you can see the property "icon-name":
The “icon-name” child property
“icon-name” gchar *
The icon name of the child page.
Flags: Read / Write
Default value: NULL
As you pointed out, we can use gtk_container_child_set_property on the GtkStack (a GtkContainer) and set the icon. The problem is that the stack uses the icon or the title, not both.
Here is a simple example in C code:
#include <gtk/gtk.h>
int main (int argc, char** argv) {
GtkBox *box;
GtkStack *stack;
GtkLabel *label1;
GtkLabel *label2;
GtkWindow *window;
GtkStackSwitcher *switcher;
GValue iconval1 = G_VALUE_INIT;
GValue iconval2 = G_VALUE_INIT;
gtk_init (&argc, &argv);
g_value_init (&iconval1, G_TYPE_STRING);
g_value_init (&iconval2, G_TYPE_STRING);
window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 6));
stack = GTK_STACK(gtk_stack_new ());
switcher = GTK_STACK_SWITCHER(gtk_stack_switcher_new ());
label1 = GTK_LABEL(gtk_label_new("Stack Page 1"));
label2 = GTK_LABEL(gtk_label_new("Stack Page 2"));
gtk_stack_add_titled(stack, GTK_WIDGET(label1), "Page 1", "Page 1");
gtk_stack_add_titled(stack, GTK_WIDGET(label2), "Page 2", "Page 2");
gtk_widget_set_halign (GTK_WIDGET(switcher), GTK_ALIGN_CENTER);
g_value_set_string(&iconval1, "zoom-in-symbolic.symbolic");
g_value_set_string(&iconval2, "zoom-out-symbolic.symbolic");
gtk_container_child_set_property(GTK_CONTAINER(stack), GTK_WIDGET(label1), "icon-name", &iconval1);
gtk_container_child_set_property(GTK_CONTAINER(stack), GTK_WIDGET(label2), "icon-name", &iconval2);
gtk_stack_switcher_set_stack (switcher, stack);
gtk_box_pack_start (box, GTK_WIDGET(switcher), FALSE, FALSE, 6);
gtk_box_pack_start (box, GTK_WIDGET(stack), TRUE, TRUE, 6);
gtk_container_add (GTK_CONTAINER(window), GTK_WIDGET(box));
g_signal_connect(G_OBJECT(window), "destroy", gtk_main_quit, NULL);
gtk_widget_show_all (GTK_WIDGET(window));
gtk_main ();
return 0;
}
Compile it with:
gcc -o test main.c `pkg-config --cflags --libs gtk+-3.0`
and the result should be:
EDIT:
As requested in the comments:
Can you tell me also how to change icon sizes of stack switcher icons?
I see that stack switcher has property "icon-size"...
GtkStackSwitcher has the property "icon-size" but it was introduced in Gtk+ 3.20. So, in order to use this property there is this requirement.
To set a property to which Gtk+ does not provide a setter/getter you should use g_object_set (or set_full).
Using the code above:
...
switcher = GTK_STACK_SWITCHER(gtk_stack_switcher_new ());
g_object_set(G_OBJECT(switcher), "icon-size", GTK_ICON_SIZE_LARGE_TOOLBAR, NULL);
label1 = GTK_LABEL(gtk_label_new("Stack Page 1"));
...
The property is a gint value so you can try out some values and verify the size. There is also a enumerated type containing default sizes for icons, it's GtkIconSize. In the example i've used GTK_ICON_SIZE_LARGE_TOOLBAR (24px).

saving gtk window position

I'm trying to save the gtk window position(absolute) to restore it wehn I open the applicaiton again
here's my code so far:
gint x,y;
gtk_window_get_position(main_window,&x,&y);
printf("current position is:\nx: %i\ny:%i\n",x,y);
this code runs when the application exits, I always get:
current position is:
x: 0
y:0
What am I doing wrong.
gtk_window_get_position usually does a best guess but you cannot rely on it because
the X Window System does not specify a way to obtain the geometry of
the decorations placed on a window by the window manager.
(from gtk_window_get_position reference)
To see the function in action, try something like below:
#include <gtk/gtk.h>
int main(int argv, char* argc[])
{
GtkWidget *window, *button;
gint x, y;
gtk_init(&argv, &argc);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Test Window");
button = gtk_button_new_with_label("Close");
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(gtk_main_quit), (gpointer)NULL);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_window_get_position(GTK_WINDOW(window), &x, &y);
printf("current position is:\nx: %i\ny:%i\n", x, y);
gtk_window_set_position(GTK_WINDOW(window),
GTK_WIN_POS_CENTER_ALWAYS);
gtk_window_get_position(GTK_WINDOW(window), &x, &y);
printf("new position is:\nx: %i\ny:%i\n", x, y);
gtk_widget_show_all(window);
gtk_main();
}
Edit
If you wish the window to appear at a specific location, you could try something like:
gtk_window_move(GTK_WINDOW(window), 420, 180);
However the above function should be placed after
gtk_widget_show_all(window);
because
most window managers ignore requests for initial window positions
(instead using a user-defined placement algorithm) and honor requests
after the window has already been shown.
(from gtk_window_move reference)
I've just implemented this feature using pygobject; it's not C but it could still be useful to have a look at it.
You can find the code here.
I've used GNOME Builder's default template for a python GNOME application, so it should be super-easy to replicate if you set your project with it.

How can I suspend the GtkDrawingArea draw callbacks while the window is being resized?

I am developping a small GTK+ program in which I placed a GtkDrawingArea. I use it in order to draw a rather specific kind of data representation graph, and the result is quite satisfying.
The problem is: the "graph" has a lot of data to process, and the draw signal's callback is called quite often. Most importantly, it is called every time the window (GtkWindow/GtkContainer) is resized by a few pixels. In order to avoid slowing down the application too much, I'd like to "suspend" the draw callbacks while the window is being resized. We could imagine that the whole area would be covered with a gray rectangle in the meantime, or something similar...
gboolean draw_callback(GtkWidget* widget, cairo_t* cr, gpointer data){
/* A lot of drawing with Cairo
* This is called WAY too often. */
}
int main(int argc, char* argv[]){
GtkBuilder* builder;
GtkWidget *window;
GtkWidget *draw_area;
gtk_init(&argc, &argv);
builder = gtk_builder_new_from_file("myapp.ui");
window = GTK_WIDGET(gtk_builder_get_object(builder, "main_window"));
draw_area = GTK_WIDGET(gtk_builder_get_object(builder, "draw_area"));
g_signal_connect(draw_area, "draw", G_CALLBACK(draw_callback), NULL);
gtk_widget_show_all(GTK_WIDGET(window));
g_object_unref(builder);
gtk_main();
return 0;
}
Note: the callbacks must still be executed once the window is resized (some coordinates need to be recomputed when that happens). I'm just trying to avoid it while the window is being resized.
My first idea was to connect a callback to the check-resize event, in which a boolean could be set and unset, as the window is grabbed and released (while resizing) :
gboolean resizing = false;
void resize_callback(GtkContainer* container, gpointer data){
/* Set "resizing"...
* Is the window being grabbed? Released? */
}
gboolean draw_callback(GtkWidget* widget, cairo_t* cr, gpointer data){
if(resizing){
/* Draw a gray overlay or something if necessary... */
return true;
}
/* Draw the actual stuff here... */
}
int main(int argc, char* argv[]){
GtkWidget *window;
GtkWidget *draw_area;
// ...
g_signal_connect(window, "check-resize", G_CALLBACK(resize_callback), NULL);
g_signal_connect(draw_area, "draw", G_CALLBACK(draw_callback), NULL);
// ...
}
However this event doesn't really suit me because it is not triggered when the window is being grabbed/released (only when its size actually changes).
Is there a way to be notified when the window is being grabbed and released (for resizing) ? Or is there a better way to suspend/simplify the calls to draw_callback when the window is being resized?
Consider blocking the draw callback while the mouse button is down. Save the callback id:
draw_callback_id = g_signal_connect(draw_area, "draw",
G_CALLBACK(draw_callback), NULL);
When the button-pressed signal is detected, do
g_signal_handler_block(draw_area, draw_callback_id);
And, of course, after the button-release-event:
g_signal_handler_block(draw_area, draw_callback_id);
You could then manually trigger a redraw event. To optimize, you can use the gtk_widget_queue_draw_region() call, which only redraws the specified rectangle.
Another possibility (though I haven't tried this) could be drawing only the window borders while resizing or moving. The Window manager (XFCE) has this option, but I haven't seen how to do it from inside GTK.

Can't make a Widget non-resizable

I want to maximize my Widget and then make it non-resizable. I can maximize the Widget with:
gtk_window_maximize(GTK_WINDOW(window));
But when I try to make it non-resizable with:
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
The window lost its maximized state; it returned to its original size.
Why? How can a Widget be maximized and made non-resizable?
Disclaimer: This answer is proposed as a workaround only, and does not actually solve the problem.
After following #ptomato's link, this is the closest thing I got:
int main(int argc, char *argv[]) {
gtk_init(&argc, &argv);
GtkWidget *win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(win);
GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(win));
GdkRectangle rect;
gdk_screen_get_monitor_workarea(screen, 0, &rect);
gtk_window_move(GTK_WINDOW(win), rect.x, rect.y);
gtk_widget_set_size_request(win, rect.width, rect.height);
gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
gtk_main();
return 0;
}
However, this is still not perfect, and differs from a maximised window in the following ways (tested using the default Ubuntu 14.04 theme):
The width and height of the window is very slightly larger (by a few pixels) than the monitor size minus the title bar and the launch bar.
In addition to the system title bar the window also has its own title bar.
You can move the window around, while you shouldn't be able to if it is maximised.

How do I make the gtk code work?

vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
...
frame = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), frame);
...
The above code will generate the warning below:
Gtk-WARNING **: Attempting to add a
widget with type GtkFixed to a
GtkWindow, but as a GtkBin subclass a
GtkWindow can only contain one widget
at a time; it already contains a
widget of type GtkVBox
Which results in frame is not shown in the window.
How can I make both vbox and frame show?
Put them both in a surrounding vbox (if you want to stack them vertically, that is):
parentVbox = gtk_vbox_new(FALSE, 0);
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(parentVbox), vbox, TRUE, TRUE, 0);
...
frame = gtk_fixed_new();
gtk_box_pack_start(GTK_BOX(parentVbox), frame, TRUE, 0);
...
gtk_container_add(GTK_CONTAINER(window), parentVbox);
The error you are having is because every widget can only contain one widget (you want vbox and frame to be contained by window), even though the widget contained may be a complex one with several other widgets contained.
To put a widget inside a box you have to use gtk_box_pack_start ().
Gnome Reference Manual link:
http://library.gnome.org/devel/gtk/stable/GtkBox.html#gtk-box-pack-start
vbox = gtk_vbox_new(FALSE, 0);
frame = gtk_fixed_new();
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); //the frame goes inside vbox
...
gtk_container_add(GTK_CONTAINER(window), vbox); //the vbox is contained by window

Resources