GTK and scrolling text view - c

This is what I have so far
GtkWidget* createConsoleBox()
{
GtkWidget* textArea = gtk_text_view_new();
GtkWidget* scrollbar = gtk_vscrollbar_new(gtk_text_view_get_vadjustment(GTK_TEXT_VIEW(textArea)));
GtkWidget* textEntry = gtk_entry_new();
GtkWidget* console = gtk_table_new(3, 2, FALSE);
gtk_table_attach_defaults(GTK_TABLE(console), textArea, 0, 1, 0, 1);
gtk_table_attach_defaults(GTK_TABLE(console), scrollbar, 1, 2, 0, 1);
gtk_table_attach_defaults(GTK_TABLE(console), textEntry, 0, 2, 1, 2);
return console;
}
I want the text view to be scrollable as the text begins to fill the box, but the box keeps on expanding to accommodate more text. How to do I limit the size of the text view and create a scrollable text view.
Thanks in advance :-)

I'm afraid you've misunderstood how scrollbars work in GTK; usually you don't create a scrollbar directly, but you place the widget you would like to scroll in a GtkScrolledWindow. This creates scrollbars automatically and connects them to the widget inside the scrolled window; in your case, the text view.
Here's what your createConsoleBox() function should look like:
GtkWidget* createConsoleBox()
{
GtkWidget* textArea = gtk_text_view_new();
GtkWidget* scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
GtkWidget* textEntry = gtk_entry_new();
GtkWidget* console = gtk_table_new(3, 1, FALSE);
gtk_container_add(GTK_CONTAINER(scrolledwindow), textArea);
gtk_table_attach_defaults(GTK_TABLE(console), scrolledwindow, 0, 1, 0, 1);
gtk_table_attach_defaults(GTK_TABLE(console), textEntry, 0, 1, 1, 2);
return console;
}

What you experience is the result of the widget asking more space to its parent container.
Unless the parent container has some rules forbidding the expansion, it will give as much space as child widget asks.
A common way to avoid this is to set a given size for the child widget with gtk_widget_set_size_request(), followed by some way to make sure the parent can give shrink or grow, depending on the parent properties.
This sample code show one way to accomplish this.
#include <gtk/gtk.h>
GtkWidget* createConsoleBox()
{
GtkWidget* textArea = gtk_text_view_new();
GtkWidget* scrollbar= gtk_vscrollbar_new(gtk_text_view_get_vadjustment(GTK_TEXT_VIEW(textArea)));
GtkWidget* textEntry = gtk_entry_new();
GtkWidget* console = gtk_table_new(3, 2, FALSE);
gtk_table_attach_defaults(GTK_TABLE(console), textArea, 0, 1, 0, 1);
gtk_table_attach_defaults(GTK_TABLE(console), scrollbar, 1, 2, 0, 1);
gtk_table_attach_defaults(GTK_TABLE(console), textEntry, 0, 2, 1, 2);
//This code sets the preferred size for the widget, so it does not ask for extra space
gtk_widget_set_size_request(textArea, 320, 240);
return console;
}
int main(int argc,char* argv[]){
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Simple Sample");
gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);// does not matter this size
gtk_container_add(GTK_CONTAINER(window), createConsoleBox());
gtk_widget_show_all(window);
gtk_window_set_resizable(GTK_WINDOW(window),FALSE);//because of this
gtk_main();
return 0;
}
gtk_window_set_resizable() is meant to make the window un-resizeable by the user (the App can still resize it), but has the extra propertie of tighting up the window to the size of its child widget. Each GtkContainer has it way of setting up expansion, tighness, etc. Is only matter of experimentation to find the right one for your needs.
If the window resizable property had been set to TRUE, the textarea would still have the given size for it, the container would just put a lot of extra space between the individual widgets inside the GtkTable.

Related

How to prevent GtkWindow growing?

This is my codes:
#include<gtk/gtk.h>
int main(int argc,char**argv)
{
GtkWidget* window, *button, *grid;
gtk_init(&argc,&argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(window),100,100);
g_signal_connect(G_OBJECT(window),"destroy",G_CALLBACK(gtk_main_quit),NULL);
//gtk_container_set_border_width(GTK_CONTAINER(window),10);
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
grid = gtk_grid_new();
button = gtk_button_new_with_label("1");
gtk_grid_attach(GTK_GRID(grid), button, 0,0, 100, 1);
gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE);
gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE);
button = gtk_button_new_with_label("1");
gtk_grid_attach(GTK_GRID(grid), button,0, 1, 10, 1);
gtk_container_add(GTK_CONTAINER(window), grid);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
the result of this codes is :
gtk_grid_attach(GTK_GRID(grid), button,0, 1, 100, 1)
When I change gtk_grid_attach(GTK_GRID(grid), button,0, 1, 100, 1) to gtk_grid_attach(GTK_GRID(grid), button,0, 1, 200, 1)
Then the result of this code is:
gtk_grid_attach(GTK_GRID(grid), button,0, 1, 200, 1)
Why does the width of window increase? I find that the size of window may be varied with the function gtk_grid_attach(). So how to solve it?
Why does the width of window increase?
You have used
gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE);
gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE);
Which means that each cell in the grid will have equal width and height, which will be the same as the largest widget embedded. So If you have more cells, the grid will grow further and thus the window width increases
I find that the size of window may be varied with the function gtk_grid_attach(). So how to solve it?
I don't know what's your usecase is. But you could unset the homogeneous properties and/or use a real grid width and height when attaching children.

GTK3 - radiobutton: callback is called twice every time toggle the button

I'm trying to use gtk3 radiobutton following the turorial bellow. The problem is I noticed the callback funcion is being called twice everytime I click in the button.
My question is why and how can I change this for the callback to be called one time when I click in a radio button?
https://developer.gnome.org/gnome-devel-demos/stable/radiobutton.c.html.en
#include <gtk/gtk.h>
static void foo(GtkWidget *widget, gpointer data)
{
char *id_radio = (char*)data;
g_print("%s\n", id_radio);
}
int main(int argc, char *argv[])
{
gtk_init(&argc, & argv);
GtkWidget *radio_1, *radio_2, *radio_3, *vbox, *window;
radio_1 = gtk_radio_button_new_with_label(NULL, "Radio 01");
radio_2 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(radio_1), "Radio 02");
radio_3 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(radio_1), "Radio 03");
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(GTK_TOGGLE_BUTTON(radio_1), "toggled", G_CALLBACK(foo), (gpointer)"1");
g_signal_connect(GTK_TOGGLE_BUTTON(radio_2), "toggled", G_CALLBACK(foo), (gpointer)"2");
g_signal_connect(GTK_TOGGLE_BUTTON(radio_3), "toggled", G_CALLBACK(foo), (gpointer)"3");
gtk_box_pack_start(GTK_BOX(vbox), radio_1, 1, 1, 0);
gtk_box_pack_start(GTK_BOX(vbox), radio_2, 1, 1, 0);
gtk_box_pack_start(GTK_BOX(vbox), radio_3, 1, 1, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The "toggled" signal which is inherited from a toggle button, triggers both on deactivation and activation. when you select a radio button, the previously selected button gets deactivated (the first callback), and the newly pressed one activates (the second callback).
If you want to filter out just the activations, get the toggle button's state inside the callback with gtk_toggle_button_get_active ().
As per documentation from:
https://developer.gnome.org/gtk3/stable/GtkToggleButton.html#gtk-toggle-button-get-active

How to center a dialog window on the main window in GTK?

I want to print small warning messages. To achieve that, I'm creating a new window and I want it to be centered on the main window. So far I've tried setting its parent with gtk_widget_set_parent, using GTK_WINDOW_POPUP and GTK_WINDOW_TOPLEVEL but nothing worked.
How to center the window on the main window?
Here's the code for the function:
static void pop_warning(char *title, char *text)
{
GtkWidget *warning_window;
GtkWidget *box;
GtkWidget *label;
GtkWidget *button;
warning_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(warning_window), title);
g_signal_connect(GTK_WINDOW(warning_window), "destroy", G_CALLBACK(gtk_widget_destroy), NULL);
gtk_container_set_border_width(GTK_CONTAINER(warning_window), 20);
gtk_window_set_resizable(GTK_WINDOW(warning_window), FALSE);
//window is the main window
gtk_widget_set_parent(warning_window, window);
gtk_window_set_position(GTK_WINDOW(warning_window), GTK_WIN_POS_CENTER_ON_PARENT);
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
gtk_container_add(GTK_CONTAINER(warning_window), box);
label = gtk_label_new(text);
gtk_box_pack_start(GTK_BOX(box), label, TRUE, FALSE, 0);
button = gtk_button_new_with_label("Ok");
g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_widget_destroy), warning_window);
gtk_box_pack_start(GTK_BOX(box), button, TRUE, FALSE, 0);
gtk_widget_show_all(warning_window);
}
gtk_window_set_position(GTK_WINDOW(warning_window), GTK_WIN_POS_CENTER_ON_PARENT);
only works if you first call
gtk_window_set_transient_for(GtkWindow *window, GtkWindow *parent);
as mentioned explicitly in the documentation.
You could also try to use GTK_WIN_POS_CENTER_ALWAYS that has not the above mentioned constrain - at least not according the docs.

Auto scrolling GtkScrolledWindow with GtkTextView wrapped in GtkBox

When I have GtkTextView in GtkScrolledWindow, it scrolls automatically when user appends new line at the bottom of the widget. When I put GtkTextView in GtkBox and then in GtkScrolledWindow I doesn't work. I need to put box between GtkTextView and scrollbar and I can't do that without placing whole thing in another box. Is there any way to preserve autoscrolling behaviour when using GtkBox inside GtkScrolledWindow?
There is code that ilustrates my problem:
#include <gtk/gtk.h>
int main(int argc, char *argv[]) {
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(window),
gdk_screen_width()*0.5, gdk_screen_height()*0.5);
GtkWidget *main = gtk_box_new(0, 0);
gtk_container_add(GTK_CONTAINER(window), main);
/*/////////////////////////////////////////////////*/
/* IMPORTANT PART */
/* FIRST CASE (this one works correctly) */
GtkWidget *scrolled_window_first = gtk_scrolled_window_new(NULL, NULL);
GtkWidget *text_view_first = gtk_text_view_new();
gtk_container_add(GTK_CONTAINER(scrolled_window_first), text_view_first);
gtk_box_pack_start(GTK_BOX(main), scrolled_window_first, 1, 1, 0);
/* SECOND CASE (there is no auto scroll which I need) */
GtkWidget *scrolled_window_second = gtk_scrolled_window_new(NULL, NULL);
GtkWidget *text_view_second = gtk_text_view_new();
GtkWidget *box_from_second_example = gtk_box_new(0, 0);
GtkWidget *example_box_before_scroller = gtk_box_new(0,0);
GtkWidget *example_label = gtk_label_new("I need this box badly!");
gtk_box_pack_start(GTK_BOX(box_from_second_example), text_view_second, 1, 1, 0);
gtk_container_add(GTK_CONTAINER(scrolled_window_second), box_from_second_example);
gtk_container_add(GTK_CONTAINER(box_from_second_example), example_box_before_scroller);
gtk_box_pack_start(GTK_BOX(main), scrolled_window_second, 1, 1, 0);
/* END IMPORTANT PART */
/*/////////////////////////////////////////////////*/
gtk_container_add(GTK_CONTAINER(example_box_before_scroller), example_label);
g_signal_connect(GTK_WINDOW(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
The problem is GtkTextView implements GtkScrollable but GtkBox does not. This is not a minor issue: if you want to go the GtkBox way you should put it inside a GtkViewport and add the scrollability stuff... quite some code that involves deep understanding of how the whole thing scrolls.
Anyway if you are lazy enough you could also note GtkTextview is a GtkContainer. In other words you could add widgets around the GtkTextview without the need to incomodate GtkBox. Not exactly what you were looking for but maybe good enough for your purposes:
#include <gtk/gtk.h>
gint main(gint argc, gchar **argv)
{
GtkWidget *window, *scrolled_window, *text_view, *label;
gtk_init(&argc, &argv);
text_view = gtk_text_view_new();
gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view)),
"1\n\n\n2\n\n\n3\n\n\n4\n\n\n5\n\n\n6\n\n\n7\n\n\n8", -1);
/* Use this to set the size you want to reserve on the right */
gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(text_view),
GTK_TEXT_WINDOW_RIGHT,
130);
/* Add whatever you want instead of a GtkLabel */
gtk_text_view_add_child_in_window(GTK_TEXT_VIEW(text_view),
gtk_label_new("You badly need this"),
GTK_TEXT_WINDOW_RIGHT,
0, 0);
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(scrolled_window), text_view);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(window), 320, 240);
gtk_container_add(GTK_CONTAINER(window), scrolled_window);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
Did you try to modify the GtkAdjustments the textview creates by default (GtkScrolledWindow implements the GtkScrollable afaik).
GtkAdjustment * gtk_scrollable_get_hadjustment (GtkScrollable *scrollable);
I think the best approach would be to detect insertions to the GtkTextBuffer (get the views buffer via
GtkTextBuffer * gtk_text_view_get_buffer (GtkTextView *text_view);
and hook a callback (which tests for \n) to its insert-text g_signal and use
void gtk_adjustment_set_value (GtkAdjustment *adjustment,
gdouble value);
to modify the scrolling height.
Note that this is untested and a simpler method might be around the corner.

Trying to populate a GtkComboBox with model in C

I'm really new to C and GTK+ so my problem might be painfully obvious. I have tried to follow examples and tutorials found all over the net.
I want a combo box with three values, the middle one being the default. I can successfully set it up using Glade but I have decided to rewrite everything in C. The combobox is drawn but it is empty/blank. I don't know what I am doing wrong.
...
GtkTreeIter iter;
GtkListStore *liststore;
GtkWidget *combo;
liststore = gtk_list_store_new(1, G_TYPE_STRING);
gtk_list_store_insert_with_values (liststore, &iter, 0, 0, "Don't install.", -1);
gtk_list_store_insert_with_values (liststore, &iter, 1, 0, "This user only.", -1);
gtk_list_store_insert_with_values (liststore, &iter, 2, 0, "All users.", -1);
combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(liststore));
gtk_combo_box_set_active (GTK_COMBO_BOX(combo), 1);
...
gtk_grid_attach (GTK_GRID(grid), combo, 2, 4, 1, 1);
...
In your original code you correctly bound the data model to the combo box but you did not specified how the model should be presented (that is the view part of the whole model-view concept). This is what the GtkCellLayout is supposed to provide.
To give you an idea on why this added complexity is useful, here is an example that shows how to use the model to have custom background (this is bad UX and, depending on your theme, the background color can be totally ignored). I think the most difficult thing is avoiding memory leaks, so I added some comment on this regard:
#include <gtk/gtk.h>
int main(int argc, char **argv)
{
GtkWidget *window;
GtkListStore *liststore;
GtkWidget *combo;
GtkCellRenderer *column;
gtk_init(&argc, &argv);
liststore = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
gtk_list_store_insert_with_values(liststore, NULL, -1,
0, "red",
1, "Don't install.",
-1);
gtk_list_store_insert_with_values(liststore, NULL, -1,
0, "green",
1, "This user only.",
-1);
gtk_list_store_insert_with_values(liststore, NULL, -1,
0, "yellow",
1, "All users.",
-1);
combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(liststore));
/* liststore is now owned by combo, so the initial reference can
* be dropped */
g_object_unref(liststore);
column = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), column, TRUE);
/* column does not need to be g_object_unref()ed because it
* is GInitiallyUnowned and the floating reference has been
* passed to combo by the gtk_cell_layout_pack_start() call. */
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), column,
"cell-background", 0,
"text", 1,
NULL);
gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 1);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_add(GTK_CONTAINER(window), combo);
/* Also combo is GInitiallyUnowned and it is now owned
by window after the gtk_container_add() call. */
gtk_widget_show_all(window);
gtk_main();
return 0;
}
But if you intend to use only strings in your combo box, the code can be stripped down by leveraging GtkComboBoxText:
#include <gtk/gtk.h>
int main(int argc, char **argv)
{
GtkWidget *window, *combo;
gtk_init(&argc, &argv);
combo = gtk_combo_box_text_new();
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo), NULL, "Don't install.");
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo), NULL, "This user only.");
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo), NULL, "All users");
gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 1);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_add(GTK_CONTAINER(window), combo);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
I think I found the solution.
I had to insert a cell renderer into the combo box. I added this code between creating the combobox and setting the default active value.
GtkCellRenderer *combocell = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(combo), combocell, TRUE );
gtk_cell_layout_set_attributes( GTK_CELL_LAYOUT(combo), combocell, "text", 0, NULL );
I still don't understand this and I have no idea what attributes are suppose to go with GtkCellLayout or how they work. Any supplemental reading would greatly be appreciated.

Resources