How to use images in GTK Stack Switcher using C - 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).

Related

How to align a GtkWidget label in the center of its GtkWidget layout in C with GTK3?

I have the following code, and I'm struggling to align the label in the center
of its parent (layout), but nothing has worked for me:
int main(int argc, char ** argv){
GtkWidget *window;
GtkWidget *layout;
GtkWidget *label;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(window), 1024, 600);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
layout = gtk_layout_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER (window), layout);
label = gtk_label_new(NULL);
gtk_label_set_text(GTK_LABEL(label), "SOME TEXT");
gtk_layout_put(GTK_LAYOUT(layout), label, 0, 0);
// Here I'm trying to align my label to the center,
// but it doesn't work with any of the three functions
gtk_widget_set_halign(label, GTK_ALIGN_CENTER);
//gtk_label_set_xalign(GTK_LABEL(label), GTK_ALIGN_CENTER);
//gtk_misc_set_alignment(GTK_MISC(label), 0.5f, 0.5f);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
I have also tryied some other functions that I could find in the GENOME documentation and other SO questions but they are mostly deprecated, or also doesn't work. At this point, as I'm working with GTK3, the recommended way seems to be with:
gtk_widget_set_halign(label, GTK_ALIGN_CENTER);
But it is not working. The label is still at the position I put it with:
gtk_layout_put(GTK_LAYOUT(layout), label, 512, 300);
The code compiles without any errors or warnings.
GtkLayout, like its relative GtkFixed, is kind of a last-resort container that you use when none of the other containers will work for your purpose. It just puts widgets where you specify them. So, the horizontal alignment APIs will not work. With GtkLayout, you have to calculate the alignment yourself.
I would recommend using GtkGrid or one of the other, more full-featured containers.

GTK2+ error: GTK_IS_CONTAINER & GTK_IS_WIDGET failed

It's my first post here, I made this account because I'm kind of stumped.
I'm trying to practise passing structures as arguments to callbacks, and for that purpose I created a simple program with a button in a window. In the first iteration, pressing the button with label "Button 1" will change it into a different button with label "Button 2", while in the second the change is done by hovering over the buttons.
Below is the code for the first iteration
#include <gtk/gtk.h>
typedef struct {
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *window;
} example;
void callback_func (GtkWidget *ignored, example *test) {
GtkWidget *window=test->window;
GtkWidget *changebutton1=test->button1;
GtkWidget *changebutton2=test->button2;
gtk_container_remove(GTK_CONTAINER(window),changebutton1);
gtk_container_add(GTK_CONTAINER(window),changebutton2);
gtk_widget_show_all(window);
}
void callback_func2 (GtkWidget *ignored, example *test) {
GtkWidget *window=test->window;
GtkWidget *changebutton1=test->button1;
GtkWidget *changebutton2=test->button2;
gtk_container_remove(GTK_CONTAINER(window),changebutton2);
gtk_container_add(GTK_CONTAINER(window),changebutton1);
gtk_widget_show_all(window);
}
int main(int argc, char *argv[]) {
example test;
gtk_init(&argc,&argv);
GtkWidget *window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget *changebutton1=gtk_button_new_with_label("Button 1");
GtkWidget *changebutton2=gtk_button_new_with_label("Button 2");
test.window=window;
test.button1=changebutton1;
test.button2=changebutton2;
g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
gtk_container_add(GTK_CONTAINER(window),changebutton1);
g_signal_connect (G_OBJECT (changebutton1), "clicked", G_CALLBACK (callback_func), (gpointer*)&test);
g_signal_connect (G_OBJECT (changebutton2),"clicked",G_CALLBACK(callback_func2),(gpointer*)&test);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
When running the above code, by clicking the button once, the buttons switch properly, but when I click it again (now calling callback_func2 instead of callback_func, which are essentially identical save for the buttons switching place) I get this:
(gtktesting.exe:92024): Gtk-CRITICAL **: gtk_container_add: assertion `GTK_IS_WIDGET (widget)' failed
If I replace the "clicked" events with "enter_notify_event" and "leave_notify_event", the first change fails as well, now yielding more errors.
Anybody know what's going on?
Also, a bonus question. I pretty much copied and pasted the base of this code from another post, since I had been trying to write it by myself unsuccessfully and I wanted to test if it works at all. I noticed that the second argument in the callbacks is "example *test". Can anybody explain the "example" type? It's only the name of the stucture, and I've no idea what it's doing there.
tl;dr
Try:
test.window = g_object_ref(window);
test.button1 = g_object_ref(changebutton1);
test.button2 = g_object_ref(changebutton2);
Full explaination
Objects (i.e. subclasses of GObject) in GTK+ are reference counted. That means, every object (e.g. Widget) has "reference count" - number of pointers that point to it. When number reaches 0 - object is deallocated. Objects are created with reference count of 1. As C does not have smart pointers C++ does nor anything similar, reference counting has to be done manually. User has to call g_object_ref in order to retain a reference (I use words "pointer" and "reference" interchangeably), and g_object_unref when user is done with a reference. That ensures that no object is destroyed while being in use.
GtkWidget is special, as it begins life with "floating" reference. That means that first time widget is referenced its reference count does not increase - its "floating" reference is "sunk". After that it behaves like any other GObject.
When you create your buttons, they are created with reference count 1 ("floating"). When they are added to their container, their references are still 1 (but "sunk"). This means, buttons are owned by the container that they're added to.
Now, when you remove button from container:
gtk_container_remove(GTK_CONTAINER(window),changebutton1);
reference count for changebutton1 is decreased, drops to 0, which forces object destruction, and test.button1 is now dangling pointer.
To overcome this, use g_object_ref anytime you want to store pointer to GObject. That way you express "test participates in ownership of changebutton1" (or, "test is interested in keeping changebutton1 alive).
When you are done with window, button1 and button2, call g_object_unref on them.
Bonus question
Also, a bonus question. I pretty much copied and pasted the base of
this code from another post, since I had been trying to write it by
myself unsuccessfully and I wanted to test if it works at all. I
noticed that the second argument in the callbacks is "example *test".
Can anybody explain the "example" type? It's only the name of the
stucture, and I've no idea what it's doing there.
example is defined here:
typedef struct {
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *window;
} example; // declares type "example"
GObject signal system is designed in a sucha way that it allows passing arbitrary pointer as a last argument to callback, so a programmer can pass extra information from place where signal connection is created (g_signal_connect), to a callback.

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.

C Gtk Issue: Adding a VBox to the Main Window

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

GTK: set default button for dialog

It seems that GtkDialog automatically sets the focus on the left-most button (which is "Cancel" in my case).
I want to change this default focus to another button, but I cannot go the route of "gtk_dialog_set_default_response" because I have packed the buttons manually into the dialogs action area.
Then, while searching the API doc up and down, I realized that GtkDialog is a descendent of GtkWindow, and thus tried "gtk_window_set_default", which at first gave me some sort of "assertion `gtk_widget_get_can_default (default_widget)' failed" warning. To comply, I used "gtk_widget_set_can_default" on the button, and the warning disappeared.. BUT: the focus is still being set on the "Cancel" button.
Is there really no way other than having to use "gtk_dialog_add_action_widget"?
Just use gtk_widget_grab_focus on the widget which you want to have focus on. The widget has to be focusable, which is true by default in case of a button. Here is a sample code for your reference:
#include <gtk/gtk.h>
/* Uncomment the below macro to see the default focus */
//#define DEFAULT_FOCUS
int main(void)
{
gtk_init (NULL, NULL);
#ifdef DIALOG_WITH_BUTTONS
GtkWidget * dialog = gtk_dialog_new_with_buttons ("Dialog",
NULL,
GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
GTK_STOCK_OK,
GTK_RESPONSE_OK,
NULL);
#ifndef DEFAULT_FOCUS
gtk_widget_grab_focus(gtk_dialog_get_widget_for_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK));
#endif
#else
GtkWidget *dialog = gtk_dialog_new();
gtk_window_set_title(GTK_WINDOW(dialog), "Dialog");
GtkWidget *action_area = gtk_dialog_get_action_area(GTK_DIALOG(dialog));
GtkWidget *ok_button = gtk_button_new_with_label("OK");
GtkWidget *cancel_button = gtk_button_new_with_label("Cancel");
gtk_container_add(GTK_CONTAINER(action_area), cancel_button);
gtk_container_add(GTK_CONTAINER(action_area), ok_button);
gtk_widget_show_all(dialog);
#ifndef DEFAULT_FOCUS
gtk_widget_grab_focus(ok_button);
#endif
#endif
g_signal_connect(dialog, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_main();
return 0;
}
Hope this helps!

Resources