So my goal was to have a read-only GtkTextView widgets in which text can be arranged with layouts like grids, flowbox, vbox, or hbox layouts. Imagine my app to be a textual dashboard where end-users can decide how to place each dashboard widget, following the principles of tiling window managers. I did not have much luck achieving this with GtkTextViews.
So I tried a different approach of adding GtkLabels to GtkGrid and GtkFlowBox widgets all with the “selectable” property enabled. So far so good. However the default behavior for GtkGrid or GtkFlowBox seems to always highlight all of the text in the first selectable GtkLabel added to whatever container I am using. I would instead like for nothing to be selected until it is explicitly selected by the end-user.
The attached image shows what the window looks like when it is first made visible and before any user interaction — the upper-left GtkLabel is highlighted. I would like for this to not happen until the end-user drags over the widget with the mouse, but I am not sure how to go about it.
A different but related question: I would also like if the user drags beyond the boundary of a GtkLabel for the selection to continue to neighboring GtkLabel widgets, sort-of like how it works in a web browser. Is there an easy way to achieve this in Gtk without writing lots of custom drag event handlers for all the the widgets I construct in the UI?
Thanks for your reply! Yes, I would like whole labels. I will try your recommendation soon and post my results.
I am curious, is it there an easy way to configure the FlowBox to select individual characters across multiple lables in a FlowBox in the same way that pieces of text can be selected in a TextView?
I’m not sure Gtk allows that, for text only one single selection is allowed at the same time, if I remember correctly (I think it’s related to the selection being copied in a special clipboard)
i.e. selecting characters in another label will clear the characters selection of the 1st label.
So here is another thing I tried that didn’t work:
On the GtkGrid object I set my own set-focus-child callback which selects no children. I made sure the callback returned TRUE to prevent propagation of the event to any container widgets above this one.
Like I said, this did not solve the problem, although I can prove that my custom focus callback is triggered because the printf statement does produce a log message in my test program.
I also tried calling gtk_container_set_focus_child(my_container, NULL) from within the callback but that triggers the callback again and causes an infinite loop.
So, back to reading the documentation in the hopes of finding some clue that might solve my problem.
@gwillems
Your timing is impeccable! I just now (probably as you were writing this comment) pushed a new branch to my project which has exactly the code I am working on which has this issue.
Unfortunately, the code is actually not written in C, it is written in Guile Scheme using GObject Introspection to auto-generate the C bindings (via the Guile GI framework). I have been writing the C equivalent into my comments here to make it easier to understand my problem at a higher level. I am sure the problem has nothing to do with the language bindings or Guile.
To recreate the issue in C: create an application window, create a GtkGrid container, add four GtkLabel elements to each of the four grid spaces, set the following properties on each label:
The fact that the labels are “selectable” is what seems to be causing the problem, or rather, that the default behavior of a GtkContainer is to select the first selectable element added to it. This is fine, but I just need to know how to override that default behavior to make it so nothing is selected when the widget is first mapped to the window. I don’t know if you would do this by setting a property or setting a signal handler, or which one to set to achieve the result I need.
If you are comfortable with Guile and can get Guile GI installed you can run the program from within the Guile REPL using the following commands:
@gwillems Here is another thing just now I tried, and it kind-of works.
I set the properties of all of the GtkLabels in the grid like so: can-focus: FALSE, selectable: TRUE. This does solve the problem, but unfortunately I lose the ability to use the “tab” key to switch between the GtkLabel elements in the focus chain.
I suppose I can live with this as a solution, but I would really prefer to have both can-focus: TRUE and selectable: TRUE and none of the GtkLabel elements selected when the GtkGrid container is first mapped, and a GtkLabel only being selected after a mouse click. That would be ideal.
Inside the application window create a GtkViewport and set can-default: FALSE, has-default: FALSE.
Inside the viewport create a 2x2 GtkGrid and set can-default: FALSE, has-default: FALSE.
Inside the grid create a GtkLabel in each of the GtkGrid spaces setting can-default: FALSE, has-default: FALSE, selectable: TRUE, can-focus: TRUE.
So apparently can-default and has-default has nothing to do with the behavior of GtkContainer always selecting the first selectable element added to it.
Here is another thing I just now tried which does not work:
Along with all of the above properties set, I also set a signal handler on the GtkGrid to handle the add signal, and in the add signal handler I executed the gtk_container_set_focus_child(my_grid, NULL); command.
After this change, the first GtkLabel is still selected when the test app runs.
The attached image shows what the window looks like when it is first made visible and before any user interaction — the upper-left GtkLabel is highlighted. I would like for this to not happen until the end-user drags over the widget with the mouse, but I am not sure how to go about it.
I am just posting here again to let everyone know that I am still working on this issue, I just haven’t had time to work on it recently. I could probably write an example C program where I can reproduce this issue, but it shouldn’t really be necessary. This is really just a matter of me simply not knowing which signal handler to override, or which property to set.
If anyone knows of a signal handler or property I can change to solve the problem, to prevent the first element added to the container from being focused, please let me know. I am guessing it is maybe something to do with the set-focus-child signal handler, but like my previous post says, simply sending that signal directly to the container didn’t solve this problem. So I am still hoping someone can advise me on what to do.
Hey, this thread is a bit difficult to follow due to listing different containers that handle things in various ways but I think I know what your issue is.
To get some things out of the way, flowbox selection is a different issue that depends on your application logic and the selection mode you need, this is somewhat unrelated to the gtk label being highlighted. I also don’t think trying to remove the focus is the right way to go about it, the focus has to be somewhere after all.
I believe your issue is actually Gtk.Settings:gtk-label-select-on-focus, which selects the whole label when you focus on a selectable label. Setting it to false should fix your issue. For completeness, here’s a vala demo
public class ExampleApp : Gtk.Application {
public ExampleApp () {
Object (application_id: "com.example.App");
}
public override void activate () {
var win = new Gtk.ApplicationWindow (this);
var gtk_settings = Gtk.Settings.get_default ();
if (gtk_settings != null) gtk_settings.gtk_label_select_on_focus = false;
var grid = new Gtk.Grid () {column_spacing = row_spacing = margin_top = margin_bottom = margin_start = margin_end = 24};
grid.attach (new Gtk.Label ("upper left") { selectable = true }, 0, 0);
grid.attach (new Gtk.Label ("upper right") { selectable = true }, 1, 0);
grid.attach (new Gtk.Label ("lower left") { selectable = true }, 0, 1);
grid.attach (new Gtk.Label ("lower right") { selectable = true }, 1, 1);
win.child = grid;
win.present ();
}
public static int main (string[] args) {
var app = new ExampleApp ();
return app.run (args);
}
}
@GeopJr hey, this did indeed solve my problem, thanks for your help!
Just to let anyone else know if they have a similar problem: I am using GObject introspection to establish a foreign function interface to Gtk from a scripting language.
The example given by @GeopJr is written in Vala, so using a different language may not work in precisely the same way. I found that in order to make this work, I had to call Gtk.Settings.get_default() from within an event handler for the activate event connected to the GtkApplication object for my application, otherwise trying to obtain the default settings returned NULL.
Inside of the activate event handler, I did exactly what @GeopJr described, and it worked.