Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Slow/delayed switching between active/inactive state with certain Gtk+ themes #204

Closed
thomholwerda opened this issue Oct 30, 2020 · 33 comments
Labels
bug Something isn't working design UI and design issues

Comments

@thomholwerda
Copy link

I've been experiencing this really odd bug where switching to and from Cawbird as the active window is slow/delayed - but only with certain Gtk+ themes. I can't find any rhyme or reason as to why only certain themes cause this behaviour. All other applications do not exhibit this behaviour. The best way to show it is a video so... I made one. With my phone, since I wasn't sure a screenrecording would actually capture it.

The theme I'm currently using is this one, and a theme I know for sure that doesn't have this issue is any of the many available Arc themes. The issue has been there forever as far I can remember, and while not a major bug, it is annoying as it also seems to cause the app to stutter a bit when switching to it.

This is on Linux Mint 20, Cinnamon, fully updated, running Cawbird 1.2.1. I think it may perhaps be related to a combination of Cinnamon's Mutter fork (Muffin) and Cawbird using CSD, but that's just a wild guess. Other CSD apps do NOT have this bug.

@IBBoard
Copy link
Owner

IBBoard commented Oct 30, 2020

Is there a short (<1s) delay between you clicking to it and everything repainting?

I'm not sure how we debug this. I don't know of anything special that we do that might cause it. And I don't know how you'd debug something that's basically all GTK. I'll have to check some of the logging options.

@thomholwerda
Copy link
Author

As you can hopefully see in the video, it doesn't seem it's a full redraw - it's the switch from the theme variant for inactive windows to the active one (and vice versa). To replicate, open e.g. a basic Gtk+ app (like Gedit or Xed) and Cawbird (with one of the offending themes set), switch between the two windows.

It could be some difference between how each theme is coded, but that still doesn't explain why only (as far as I can tell) Cawbird is affected. I've been trying to find another app that exhibits the issue, but no luck so far. I'm not a programmer, so this might be a dumb question, but perhaps Cawbird uses a rare Gtk+ element some themes stumble over?

Let me know if you need me to run a debug build or whatever, or anything else I can do to help!

@IBBoard
Copy link
Owner

IBBoard commented Oct 31, 2020

It looks like I get it here as well, and just never noticed. I'm using a modified version of Adwaita, so it shouldn't have much affect other than having been loaded from CSS files in my home directory.

Oddly, if I bring up the Settings dialog then it is slow to swap between the main window and the Settings (or between the main window and another app) but quick between the Settings dialog and the other app. Which at least narrows it down to "probably the timeline widgets".

@IBBoard IBBoard added bug Something isn't working design UI and design issues labels Oct 31, 2020
@IBBoard
Copy link
Owner

IBBoard commented Oct 31, 2020

Well, it definitely appears to be TweetListItem related (i.e. the timeline). If I remove the Twitter API calls that make us populate the timelines then everything is quick and responsive.

It's not an image problem - if I disable image loading then it still has the lag.

It's not the fact that we've got scrollwindows inside scrollwindows (to stop tweets with very long unbreakable words stretching the whole window) - if I swap that out for a Bin then it's still slow.

If I add logging in to the get_preferred_height_for_width and size_allocate functions then it appears to do a lot of requesting new sizes for each widget, but as I understand it then that's just normal GTK behaviour. And we shouldn't do anything particularly expensive most of the time.

Anyone know how to debug GTK's repainting process? 🙂

@thomholwerda
Copy link
Author

Could it be an idea to file a Gtk+ bug? If this is out of your control, that seems like the right thing to do.

@IBBoard
Copy link
Owner

IBBoard commented Oct 31, 2020

I want to rule out that we're not doing something odd that makes it self-inflicted first. There's nothing more annoying for libraries than getting bug reports that are just "I didn't use the API right". I'd probably go to the Gnome forums first.

@IBBoard
Copy link
Owner

IBBoard commented Oct 31, 2020

More debugging: Even if we only have HomeTimeline available (remove the rest from the list in MainWidget) then it has the lag. And that's with ~40 items loaded.

[Update]

More debugging:

  • If the TweetListEntry is just the tweet text then it is fast
  • If I remove the outer stack (so there's no actions or "sensitive" message to swap to) then it lags
  • If I remove the responsive layout then it still lags (so it's not our responsive layout causing issues)
  • If I remove the images (but not the media - just avatar, RT and the status images in the top-right) then it still lags

Starting to wonder whether it's the grid!

@thomholwerda
Copy link
Author

I don't have much useful stuff to add at this point, only to say that I am always surprised when something so small can lead to such a bug hunt :D.

@IBBoard
Copy link
Owner

IBBoard commented Nov 1, 2020

I think that's called "software development" 😉

@IBBoard
Copy link
Owner

IBBoard commented Nov 3, 2020

More debugging:

  • If I quickly swap out the grids for a GtkBox then a) it looks ugly and b) it still lags (also, c) I broke something with avatar loading!)
  • If I swap out the grids and just show the HomeTimeline then it's reasonably quick but not perfect
  • If I keep the grids but just show the HomeTimeline then it's not showing as much lag as I saw previously

I'm starting to think that there's a problem between the "custom widget per tweet in a list" code and the way the UI handles the multiple views. I wonder whether @CodedOre is seeing any similar behaviour with #177 and the redesign?

@CodedOre
Copy link
Contributor

CodedOre commented Nov 3, 2020

I am not at that point to redesign the Tweet design in my libhandy-port (also took a break from working on that last week).

So, can't really give an experience here.

However, I have worked with an similar thing while reworking the AboutDialog (as I made a custom adaptive one, don't really know why).
I also had there a Grid which would basically be a list in adaptive view.

What I have done was to keep the grid, but to remove the elements from the grid and readd them in different positions when the change is done.

For one grid, it did worked well, but I can't say how the performance of that method would be when used to display the tweet.

@IBBoard
Copy link
Owner

IBBoard commented Nov 3, 2020

I think this is separate from the 'responsive' design issue and moving widgets. And I only rearrange the grid if/when we cross the "responsive threshold", otherwise it just calls standard GTK code. I'd always assumed that removing and re-adding items would be slower than just adjusting a few attachment values, though!

I just grabbed a copy of your repo and tested out the main (only!) branch. It looks like you've still got the same issue, but then you're still using the same MainWidget with all the views, so it makes sense.

@CodedOre
Copy link
Contributor

CodedOre commented Nov 3, 2020

Well, like mentioned, I only implemented a similar system in the AboutDialog.

I currently looking trough the backend code to what and how I need to change it (as I need a few additional functionality for displaying a thread in the future), so I did not went to implementing more of the new UI yet.

@IBBoard
Copy link
Owner

IBBoard commented Dec 12, 2020

Finally got round to asking on the Gnome forums - https://discourse.gnome.org/t/slow-active-inactive-state-change/5015

@IBBoard
Copy link
Owner

IBBoard commented Dec 12, 2020

The official answer appears to be "we invalidate widgets; it happens".

But I'm wondering whether Cawbird's current design keeps all of the lists active, even when they're not visible, and so maybe we're invalidating more than just the current page? Maybe libhandy will improve that?

@IBBoard
Copy link
Owner

IBBoard commented Dec 26, 2020

Reading linked tickets, this one suggests that GtkStack is the thing that performs badly. Which probably explains a lot, since all of the TweetList items are a stack to provide the "right-click to show actions" behaviour.

The libhandy mockups show actions in the TweetList (which takes more space, but is more accessible) so hopefully it won't suffer from most of these issues.

IBBoard added a commit that referenced this issue Dec 26, 2020
This mainly loses the cross-fade animation, but makes it feel snappier.

We also have a minor bug where resizing while actions are visible
can't track the grid height, but who activates actions on a tweet and
*then* resizes the window? It all fixes itself after they toggle back
to the tweet.
IBBoard added a commit that referenced this issue Dec 26, 2020
This improves app focus transition times but loses transition effects.
@IBBoard
Copy link
Owner

IBBoard commented Dec 26, 2020

Those two commits seem to speed things up a bit BUT we lose the transitions. I don't think it's so important for actions, but I'm trying to keep the page transitions.

I've got something half-working with each page being in a GtkRevealer but I'm struggling to get the "tweet to tweet" transition working.

IBBoard added a commit that referenced this issue Dec 27, 2020
It isn't perfect because the views resize rather than moving
and tweet to tweet doesn't currently transition, but it's something.
IBBoard added a commit that referenced this issue Dec 27, 2020
Part of the slow-down is because GTK invalidates and recalculates ALL
of the widgets in the stack. But if we hide them then it doesn't need
to do that. So when the transition ends, hide all widgets we're not
showing.

Currently seems to behave the same, but with faster focus switching.
IBBoard added a commit that referenced this issue Dec 27, 2020
If the actions can't know the right height as we resize then the only
option is to stop showing the actions as we resize!
@IBBoard
Copy link
Owner

IBBoard commented Dec 27, 2020

@thomholwerda Can you test the new branches? I was working on bug204-slow-focus-painting and it works and is quick, but the transitions aren't right yet. But I've just pushed bug204-slow-focus-painting-alt which was a random idea I had and I think it works and does the right thing.

@IBBoard
Copy link
Owner

IBBoard commented Dec 27, 2020

I was going to reply to my original Gnome post to reference the other bug from Baedert and point out that there's a possible work-around… but it got auto-locked yesterday!

Hopefully people can still trip over this with Google. To (hopefully) increase the chances, here's my post text:

One of my users has reported that my GTK3 app is slow to transition between “active” and “inactive” states. They’ve provided a video:

https://photos.google.com/share/AF1QipMfmBcZuH_HgaAfi1cfFd33nUunBPU4AiI65lgmFn0gW-zmCYOjfStudDw1E-Hr_g/photo/AF1QipNjqPb0-G4IyH9iD8EIbnixu1qa5tWYpyNXTO3b?key=YXZwZkdKWjFGWFF4bm10ODR4ZlBYYUN0MzRRbjV3 3

The delay is between the window getting focus and the window rendering in the “active” styling. You can click on the window, it’ll come to the front, there will be a short but noticeable delay, and then it’ll render as “active”. This happens with all themes, including Adwaita, so it’s not just the user’s theme causing it. It doesn’t happen when you’ve got a dialog (e.g. Settings) open.

The main window is a set of GtkListBox widgets containing custom GtkListItems that contain a GtkGrid with text and some image widgets.

If I limit it to 1 item in the list then the lag isn’t noticeable, but I wouldn’t expect 25 items (the default amount) to cause an obvious lag. It doesn’t appear to be the media, because the lag still happens when they’re not included.

What can cause the lag? How can I debug it? Is it just an unavoidable part of having lists with custom widgets in? From the logging I’ve added so far, it doesn’t appear to be a cascade of reallocations or anything, and I can’t find a way to debug CSS rendering.

@IBBoard
Copy link
Owner

IBBoard commented Dec 28, 2020

I've done a bit more testing. Entirely removing the stack is definitely faster, but the animations are… substandard. Hiding non-visible things seems to be faster, but I've not got numbers to confirm.

IBBoard added a commit that referenced this issue Dec 29, 2020
Why inherit from a generic Widget when we can use an EventBox?
This prevents potential problems where the media button windows
don't align with the actual position in the window.
IBBoard added a commit that referenced this issue Dec 29, 2020
This is the harshest version of removing pages when we're not using
them. We only keep extra pages in the stack until we're done with them.
But the pages are still created and keep state, so they don't lose
positions etc

Also now pass the main window around so that we don't assume a
base/parent when it might not be set.

This won't work as-is with the libhandy redesign (because we need to
keep all of the main tabs as children so we populate the switcher).
IBBoard added a commit that referenced this issue Feb 13, 2021
Part of the slow-down is because GTK invalidates and recalculates ALL
of the widgets in the stack. But if we hide them then it doesn't need
to do that. So when the transition ends, hide all widgets we're not
showing.

Currently seems to behave the same, but with faster focus switching.
IBBoard added a commit that referenced this issue Feb 13, 2021
If the actions can't know the right height as we resize then the only
option is to stop showing the actions as we resize!
IBBoard added a commit that referenced this issue Feb 13, 2021
Why inherit from a generic Widget when we can use an EventBox?
This prevents potential problems where the media button windows
don't align with the actual position in the window.
IBBoard added a commit that referenced this issue Feb 13, 2021
This is the harshest version of removing pages when we're not using
them. We only keep extra pages in the stack until we're done with them.
But the pages are still created and keep state, so they don't lose
positions etc

Also now pass the main window around so that we don't assume a
base/parent when it might not be set.

This won't work as-is with the libhandy redesign (because we need to
keep all of the main tabs as children so we populate the switcher).
IBBoard added a commit that referenced this issue Feb 13, 2021
Apparently GTK is "smart" and doesn't do the transitions if it doesn't
need to. This means that it spots our "add the impostor, change to it,
and then swap back" shuffle. BUT if we always had the impostor then
it (silently) swaps to the impostor and then happily transitions.
@IBBoard
Copy link
Owner

IBBoard commented Feb 13, 2021

I've just rebased the alt-alt branch on top of Master, since it was quite out of date.

@IBBoard
Copy link
Owner

IBBoard commented Feb 14, 2021

I think there's a bug in this. I've got a custom build that includes lots of new changes including this branch. When you right-click, it jumps to the top of the timeline. I presume something that I'm doing here is breaking the vadjust handling.

IBBoard added a commit that referenced this issue Feb 16, 2021
Although we call `show()`, GTK hasn't *actually* shown it yet, so it
can't grab focus. But it tries. Which kicks us to the top of the feed!

We now `connect_after` on size allocation (which happens when
the widget is shown) to wait until we're done.
@IBBoard
Copy link
Owner

IBBoard commented Feb 16, 2021

Okay, now it should be okay for people to test. I might still put some small improvements in, though.

IBBoard added a commit that referenced this issue Feb 16, 2021
Size allocate only happens once if the action box is shown, hidden and
shown again, so we need to hook on to other events.

Always pushing focus to the TweetListEntry and then moving it to the
RT button when necessary seems to work.
IBBoard added a commit that referenced this issue Mar 6, 2021
This mainly loses the cross-fade animation, but makes it feel snappier.

We also have a minor bug where resizing while actions are visible
can't track the grid height, but who activates actions on a tweet and
*then* resizes the window? It all fixes itself after they toggle back
to the tweet.
IBBoard added a commit that referenced this issue Mar 6, 2021
Part of the slow-down is because GTK invalidates and recalculates ALL
of the widgets in the stack. But if we hide them then it doesn't need
to do that. So when the transition ends, hide all widgets we're not
showing.

Currently seems to behave the same, but with faster focus switching.
IBBoard added a commit that referenced this issue Mar 6, 2021
If the actions can't know the right height as we resize then the only
option is to stop showing the actions as we resize!
IBBoard added a commit that referenced this issue Mar 6, 2021
This is the harshest version of removing pages when we're not using
them. We only keep extra pages in the stack until we're done with them.
But the pages are still created and keep state, so they don't lose
positions etc

Also now pass the main window around so that we don't assume a
base/parent when it might not be set.

This won't work as-is with the libhandy redesign (because we need to
keep all of the main tabs as children so we populate the switcher).
IBBoard added a commit that referenced this issue Mar 6, 2021
Apparently GTK is "smart" and doesn't do the transitions if it doesn't
need to. This means that it spots our "add the impostor, change to it,
and then swap back" shuffle. BUT if we always had the impostor then
it (silently) swaps to the impostor and then happily transitions.
IBBoard added a commit that referenced this issue Mar 6, 2021
Although we call `show()`, GTK hasn't *actually* shown it yet, so it
can't grab focus. But it tries. Which kicks us to the top of the feed!

We now `connect_after` on size allocation (which happens when
the widget is shown) to wait until we're done.
IBBoard added a commit that referenced this issue Mar 6, 2021
Size allocate only happens once if the action box is shown, hidden and
shown again, so we need to hook on to other events.

Always pushing focus to the TweetListEntry and then moving it to the
RT button when necessary seems to work.
@IBBoard
Copy link
Owner

IBBoard commented Mar 11, 2021

I've been running with this as a custom patch for a little while and I've not noticed any problems, so I'll merge it.

IBBoard added a commit that referenced this issue Mar 11, 2021
This mainly loses the cross-fade animation, but makes it feel snappier.

We also have a minor bug where resizing while actions are visible
can't track the grid height, but who activates actions on a tweet and
*then* resizes the window? It all fixes itself after they toggle back
to the tweet.
IBBoard added a commit that referenced this issue Mar 11, 2021
Part of the slow-down is because GTK invalidates and recalculates ALL
of the widgets in the stack. But if we hide them then it doesn't need
to do that. So when the transition ends, hide all widgets we're not
showing.

Currently seems to behave the same, but with faster focus switching.
IBBoard added a commit that referenced this issue Mar 11, 2021
If the actions can't know the right height as we resize then the only
option is to stop showing the actions as we resize!
IBBoard added a commit that referenced this issue Mar 11, 2021
This is the harshest version of removing pages when we're not using
them. We only keep extra pages in the stack until we're done with them.
But the pages are still created and keep state, so they don't lose
positions etc

Also now pass the main window around so that we don't assume a
base/parent when it might not be set.

This won't work as-is with the libhandy redesign (because we need to
keep all of the main tabs as children so we populate the switcher).
IBBoard added a commit that referenced this issue Mar 11, 2021
Apparently GTK is "smart" and doesn't do the transitions if it doesn't
need to. This means that it spots our "add the impostor, change to it,
and then swap back" shuffle. BUT if we always had the impostor then
it (silently) swaps to the impostor and then happily transitions.
IBBoard added a commit that referenced this issue Mar 11, 2021
Although we call `show()`, GTK hasn't *actually* shown it yet, so it
can't grab focus. But it tries. Which kicks us to the top of the feed!

We now `connect_after` on size allocation (which happens when
the widget is shown) to wait until we're done.
IBBoard added a commit that referenced this issue Mar 11, 2021
Size allocate only happens once if the action box is shown, hidden and
shown again, so we need to hook on to other events.

Always pushing focus to the TweetListEntry and then moving it to the
RT button when necessary seems to work.
@IBBoard
Copy link
Owner

IBBoard commented Mar 11, 2021

Fixed by #335

@ValdikSS
Copy link

@IBBoard, unfortunately I can't say this is fixed for me in 1.5.
It may be worse earlier, but with GTK3 HighContrast theme, it redraws the window at 4 fps when it is focused or unfocused.
The issue is "backdrop" effect. Everything is very smooth with "Clearlooks-Phenix" theme which does not have it.

cawbird-2022-06-19_05.36.36.mp4

@IBBoard
Copy link
Owner

IBBoard commented Jun 19, 2022

There's not really much more that we can do. We don't add backdrop effects, they're part of the system theme. We've already stripped back the unnecessary widgets (pages are removed when they're not the focussed one). I can't think of any other changes that we can make.

GTK4 apparently has better handling of lists of lots of widgets, so it will have to be something that is resolved with the redesign.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working design UI and design issues
Projects
None yet
Development

No branches or pull requests

4 participants