Resized() stops working after rotating two times

#1

Now I encounter the following problem:

I am correctly determining my screen orientation and resizing my components accordingly.
I have overriden the resized() method of my DocumentWindow (the main window). It also works pretty good. So if I go from portrait to landscape, everything works as intended. The problem now is, that if I go back to portrait mode, nothing happens. The resized() method is never called when going from landscape to portrait mode. But not only that, from now on the resized() method of my TopLevel DocumentWindow is completely ignored when rotating and not even called when switching to landscape again (have a breakpoint there). So weird.

0 Likes

#2

Can you reproduce that with any of the JUCE demo projects?

0 Likes

#3

Is there any android specific demo I’m missing or do you mean a general demo project?

0 Likes

#4

The DemoRunner, or some of the tutorial code.

An empty project generated from the Projucer would be enlightening too. I’m just trying to narrow the problem down a bit before diving in.

Does it work on iOS? What device and version of Android?

0 Likes

#5

I’m gonna try it right now

0 Likes

#6

Hmmm… I created a simple app with an additional resized() function in Main.cpp and it somehow works as intended. Although I should note that all my components are wrappen inside a Viewport for scrolling support… so I’m gonna try and add a Viewport and see where this leads me…

0 Likes

#7

Damn. What a troll. It’s all working fine. But in my app it’s not and I have no idea why!
Especially since it is not that complicated. Well gonna have to go through it step by step… The funny thing is, the app is indeed rotated, but the resized function is not triggered…

0 Likes

#8

I finally got the approval of my client to look into this issue further. The following is the case:

If I leave the resized() function of my main DocumentWindow empty and set a breakpoint there (after my app is loaded, as it calls resized() many times during startup), everything works as expected - every time I rotate my device, the breakpoint suspends the application.

If I now add normal stuff to the resized() body, like for example

int i = 5;
int j = 7;
int minInt = std::min(5, 7);
int maxInt = std::max(5, 7);

same thing happens. But of course it is not my intention to calculate useless numbers.

Some background information. I have a Viewport named viewport as the main component of the DocumentWindow, which itself holds a standard Component named windowManager that manages the content of my app. When rotating the device, I need to resize viewport (swap width and height, so newWidth = oldHeight and newHeight = oldWidth) as well as windowManager.
I tried that and everything worked perfectly when going from portrait to landscape. But when I tried turning it back to landscape, resized() of my main window is never called again as are the resized() methods of the child components.

void resized()
{
    if (viewport != nullptr && windowManager != nullptr)
    {
        int minDim = min(displayArea.getWidth(), displayArea.getHeight());
        int maxDim = max(displayArea.getWidth(), displayArea.getHeight());

        if (AdditionalFuncs::isUpright())
        {
            viewport->setSize(minDim, maxDim);
        }
        else
        {
            viewport->setSize(maxDim, minDim);
        }

        windowManager->setSize(viewport->getWidth() - (viewport->isVerticalScrollBarShown() ? viewport->getScrollBarThickness() : 0),
                               viewport->getHeight() - (viewport->isHorizontalScrollBarShown() ? viewport->getScrollBarThickness() : 0) + 200);
    }
}

First I thought it had something to do with kind of a feedback loop, because only when I introduced the setSize calls, the resized() method of the main window gets called multiple times, instead of one time, per rotation. Still after trying to “rotate back”, resized() gets never called again.
Because I assumed that it had something to do with the multiple calls to the main window resized() method, I changed all of this to

void resized()
{
    if (viewport != nullptr && windowManager != nullptr && !lockResize)
    {
        lockResize = true;

        int minDim = min(displayArea.getWidth(), displayArea.getHeight());
        int maxDim = max(displayArea.getWidth(), displayArea.getHeight());

        if (AdditionalFuncs::isUpright())
        {
            viewport->setSize(minDim, maxDim);
        }
        else
        {
            viewport->setSize(maxDim, minDim);
        }

        windowManager->setSize(viewport->getWidth() - (viewport->isVerticalScrollBarShown() ? viewport->getScrollBarThickness() : 0), viewport->getHeight() - (viewport->isHorizontalScrollBarShown() ? viewport->getScrollBarThickness() : 0) + 200);

        Timer::callAfterDelay(500, [this] () { this->lockResize = false; });
    }
}

I have a member variable bool lockResize which defaults to false, so it only calls my specific stuff once per rotation, but doesn’t block any other needed operation. And voila my stuff is only called once… But… rotating back still messes things up, so resized() is never called again.

I have no idea where to look or what to do as it seems to be very specific. But I don’t see why it shouldn’t work…

To ensure that my code is not the problem, I have stripped it down as far as possible. As I said, if I remove everything inside resized(), said method gets called like one would expect…
You can find the stripped down version under https://gist.github.com/DustVoice/44fbc972f6d0a67717fb057ae9ee82ec. I would love some assistance on this matter.

0 Likes

#9

@t0m As you suggested, everything works fine in the demo project but fails too if I try to change the size of the child component within resized(). I would also be happy if someone could point me to some “native” code that calls this resized method so I can maybe debug it myself

0 Likes

#10

add a breakpoint inside your resized(), and go back through the stack trace to see where it’s called from the native classes?

0 Likes

#11

Gonna do just that as a next step. But It should be something in the general JUCE code and not in the platform specific code, because someone in another thread mentioned that it happened to him on iOS too

0 Likes

#12

Either way, it would help you to debug it.
And since only iOS and Android support rotating at all, my guess tends the other way round.

But like I said, “guessing”. Lets be engineers and use science, or to put it into a meme:
Use the source, Luke!
:wink:

0 Likes

#13

I totally agree with you. Also I will now have time to do just that.

0 Likes

#14

Great, good luck! I would help, but I have currently no active mobile project.

0 Likes

#15

It’s alright. My client is ok with it and I am so deep down the rabbit hole that I can also just find the bottom and contribute to my favorite framework

0 Likes

#16

EUREKA! I have found it!
It turns out, ComponentPeer doesn’t think that my component was moved or resized, because line 308 of ComponentPeer.cpp:

const bool wasMoved   = (oldBounds.getPosition() != newBounds.getPosition());
const bool wasResized = (oldBounds.getWidth() != newBounds.getWidth() || oldBounds.getHeight() != newBounds.getHeight());

if (wasMoved || wasResized)

evaluates wasMoved and wasResized to false! I will now have to check, why it thinks my old and new bounds are the same!

0 Likes

#17

I now debugged that if I rotate my device from portrait to landscape, ComponentPeer::handleMovedOrResized() is called multiple times (5 to be exact) where the component is moved and resized to fit the new orientation:

  1. moved 24 pixels
  2. resized to the new display size
  3. somehow height gets smaller by 10 pixels
  4. moved somewhere stupid
  5. perfect position and size applied

I don’t know why it does that 5 times, but at least it works!
If I now rotate it from landscape to portrait, ComponentPeer::handleMovedOrResized() gets called exactly 1 time and this time the new and old bounds are exactly the same, so the “resize” is never handled!

It is also very interesting to mention that within the first rotation (in step 2 described above), the height of newBounds is the width of oldBounds and its width is the height of oldBounds! From now on, both newBounds and oldBounds only represent the “landscape” size, so width > height! This means that newBounds is actually never adapted to the new screen size!

0 Likes

#18

By the way:
The problem not only occurs when starting in portrait and then switching from portrait to landscape and then trying to switch back to portrait,

but also when starting in landscape and then switching from landscape to portrait and then trying to switch back to landscape!

For me the problem clearly is located in line 305 of ComponentPeer.cpp:

auto newBounds = Component::ComponentHelpers::rawPeerPositionToLocal (component, getBounds());

which is the calculation of the new bounds of the component! If this would correctly represent the new screen size, every side effect would disappear!

0 Likes

#19

:sob: Can I please die?!
I’m utterly stupid!

I hope this helps other people experiencing the same problem to not make this mistake too!

I have made my Viewport the main component of my DocumentWindow by doing setContentOwned(viewport.get(), true); and then tried to adapt the size of my viewport inside the overriden resized() method of said DocumentWindow!
This is just stupid, because with the second argument of setContentOwned you define wether you want the owned component to be automatically resized upon resize of the DocumentWindow.
Now any blind person can see where the issue is: Trying to resize the owned component (in my case a Viewport) two times!

The moment I changed setContentOwned(viewport.get(), true); to setContentOwned(viewport.get(), false); everything works as expected!
Don’t be as stupid as me …

0 Likes