(GTK4) Any way to connect to a window resize signal?

Hi there

I wanted to make my app responsive/adaptive by hiding and showing widgets as the window’s width changes

Some Adw widgets react to window resize and Nautilus looks like it’s doing something app-specific, but I can’t find anything on how.

A lot of applications use libadwaita to implement adaptive layouts.

See Adw – 1: Adaptive Layouts

Unfortunately I can’t help with the signal as I’m not that deep into GTK. But there must be a way as it works for libadwaita.

There used to be a signal but it was removed in GTK4, see “Adapt to GtkWidget’s size allocation changes” in the migration guide.

Libadwaita implements custom widgets overriding the size_allocate vfunc, if making a custom widget or container or layout manager then that is the way to handle it.

2 Likes

Nice, thanks !

The migration guide mentions listening to default_width, which does exactly what I wanted initially.

I’m using a custom widget though, and I wanted to try overriding size_allocate.
Overriding as shown in the gtk-rs book breaks the app. I ended up adding self.parent_size_allocate() guessing it’s like Java’s super(). It doesn’t crash now. Am I using it right ?

impl WidgetImpl for MyWindow {
    fn size_allocate(&self, width: i32, height: i32, baseline: i32) {
        println!("new width {}", width);
        self.parent_size_allocate(width, height, baseline)
    }
}

Yes, the parent_ method needs to be used most of the time when overriding methods in those traits. It is confusing because some methods like clicked in the book example don’t need it, but we don’t really have a way to differentiate that in Rust. If you want, you can file an issue/PR about improving the example to explain that better…

Uh… well now I’m confused

I’m not familiar with GTK. To my (freshly acquired) understanding, size_allocate() is used to determine where children widgets should be displayed.

So it should be called on every widget with children, once at “creation” and every time a dimension changes, right?

When I override my GtkWindow, it goes crazy everytime I dare to hover a scrollbar.
On a Box, it just does nothing. Not even the initial call on creation.

Do I need to do something to make non-GtkWindow widgets use it ?

IIRC the documentation around this is not great, but yes, size_allocate is called by containers to “assign” a size to its children. A container will call that method on its children every time it performs layout again. If your custom container has children then you will want calculate their sizes there, then call .size_allocate() on each of the children to give them a position. (But only if those children return should_layout() == true)

Also you will probably need to implement measure and maybe request_mode to perform the initial size negotiation, see Height-for-width Geometry Management for how that works. IMO if you are really curious about the details, the source for some of the adaptive containers in libadwaita is a good reference for what to do.

About Window and Box, there are actually two ways to implement the vfunc. One is to override it in the widget, the other is to add a layout manager. The layout manager takes precedence, so e.g. Box uses BoxLayout so it will always use the allocate implementation from that layout manager. Window does not have a layout manager but it is already a container, so probably you should not use that as a parent class if implementing a custom layout because anything you do can conflict with it. What you want to do is either implement a custom container widget (or implement a custom layout manager and add it to the widget) and then set it as the only child using window.set_child().

Alright, so this is interesting and I’ll probably want to get into all this stuff later

But the investment is getting disproportionate to the tiny button I’m trying to display

I’m going with the property notification for now.
I’ll answer to myself for future reference. If I’m missing something, tell me and I’ll edit the answer.

So I was going to answer to myself and close

But then I made an error : testing.

The properties notification doesn’t fire when tiling (maximize, Super+LeftArrow…), then fires twice when leaving tiling, once at tiling size and once at new size. I can listen to is-maximized but it doesn’t solve the side-tiling.

I guess it’s a bug and it’s meant to fire on tiling. Should I open a ticket ?

Anyways, I’m just giving up on responsive. It looks like GTK isn’t meant for it and I don’t want to fight against the library. Seeing what libadwaita was doing, I thought it would be casual.

IMO you can open a ticket, possibly that should be a feature request for another flag.

Writing your own containers is a more advanced usage of GTK so I think it should not be done unless necessary. You may want to make sure your use case is not covered by AdwSqueezer.

I guess it’s a bug and it’s meant to fire on tiling.

I would say the opposite: If there is a bug, it’s that the properties are briefly set to the tiled size when restoring the window.

The two properties are only really relevant when showing a window. However updating them to the (floating) size makes saving window state more convenient. That would break if the size was also updated on maximize or when tiled.

I wanted to make my app responsive/adaptive by hiding and showing widgets as the window’s width changes

Even if really none of the existing widgets in libadwaita suit your use case and you must implement a custom widget, tracking the top-level size doesn’t look like the best approach.

Instead, I’d try something along those lines:

  1. in measure, ignore any optional elements for the minimum size
  2. in size_allocate, include the optional elements if the widget was given enough additional space

You may want to make sure your use case is not covered by AdwSqueezer.

I mean… I can overlay it, give it two invisible children, then use the notification from visible-child.

For new devs this may actually the best solution available
It does the thing, and I guess I understand it ? :confused:

Yeah - I have no clue what you’re talking about

I’ve learned GTK basics via the gtk-rs book and want to make a quick app with basic responsive.
I come from a HTML/JS background so I expected a simple way to do custom responsive, like a signal or a dedicated method.
If I need to learn the “lower level” of GTK, as cool as it sounds, it’s not worth the investment for now.


If it’s worth a feature request and tracking the top-level size is uncool, maybe a Widget.resized() callback?

I don’t know. I think having a newbie-friendly way for responsive may be a good thing.
People like me are going to see nice responsive UIs like Nautilus and think it’s something officially endorsed by GTK.

Edit : did I already say that ? I totally already said that.

Nautilus uses libadwaita, which is precisely what provides adaptive layouts.

You decided to skip all of that, and went straight to the lowest level possible—and now you’re complaining that it’s complicated. Yes, it is complicated: you need to understand how size negotiation works in GTK. That’s why libraries like libadwaita exist in the first place.

I did not “decide” to go low level. As I said I thought it would be high level. Neither am I complaining.

I learned it’s not for me, now I am interested in knowing if it may get easier. If it can’t, that’s fine.

I don’t know if I broke an etiquette or something but please avoid putting people in front of their strawmanned intentions, that’s how you get Twitter going. You’ve told me stuff that was already said and now I feel obliged to answer accusations.

(Talking about etiquette, I’m not sure what I’m meant to do with this thread. Should I mark it as answered with a “no” ?)

Now that I think about it, this does not need a feature request, you can check tiling status by monitoring the state property on the gdk toplevel surface. A resize signal only really makes sense for simple widgets without any complex sizing requirements. GtkDrawingArea fits that use case, so it already has that signal.

If this is a custom widget that contains other widgets (not just a window trying to change its layout), then you must learn the way layout in GTK works and also implement measure (or implement a layout manager). Every GTK widget takes part in a size negotiation to determine the final layout. Aside from the libadwaita approach of providing premade responsive containers, there is no other realistic newbie-friendly way to do responsive.

As an aside, note that on the web, using window.onresize is not really a great method to do this for individual components either because they could be sized/positioned with CSS or have their size modified with Javascript, and the window event will fail to indicate when that happens.

1 Like

There is no dedicated signal, but you can use two separate property notifications.

// When the window is maximised or tiled
window.surface().connect_notify(Some("state"), |surface, _new_state_param| {
    do_something(surface.width())
});
// When the user manually drags the border of the window
window.connect_default_width_notify(|window| {
    do_something(window.width())
});

Note:
The easier way is to use premade widgets. You may get more flexibility than you’d expect by using property notifications on them.
If you are making a custom adaptive widget, the proper way is to override size_allocate() method. It gets a bit lower level though. Maybe this comment can get you started.

Yeah, I’m not aware of any way to do per-element responsive in HTML
AFAIK librairies have to use cheesy CSS selectors to allow configuring at which window size a widget should react

Probably because, well, it seems it is not high-level friendly

Thanks for the help ^^

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.