We’re using a transform to scale our MainComponent in a DocumentWindow-based app, so that it maintains the same aspect ratio within the bounds of the window (and the window then scales around that content). That works fine, except we need a menu in the window for PC. And we do not want to transform the menu along with everything else. The menu (PC only) should change its width to match the content width, but not be part of the MainComponent whose UI content gets scaled as the window is resized.
How and where can I attach a MenuBarComponent to my Windows app such that it behaves like any normal Windows app, keeping the menu above the content and within the window frame, and not scaling it when the content causes the MainWindow to resize?
I tried adding the menu to the MainWindow (DocumentWindow), but then I hit an assert that you should not add components to a ResizableWindow (the MainWindow/DocumentWindow). But if I add it to the MainComponent, then the menu gets scaled right along with the other content when resizing.
How can I resolve this catch-22?
add a new component in between that contains the menu and your current MainComponent ?
Ouch. Was hoping to avoid that, but I guess that’s the only option. Unfortunate that the Windows menu isn’t just something that is already handled by the DocumentWindow class, without having to have code wrapped in #if JUCE_MAC, with significantly different ways of creating and managing the app’s menu.
Doesn’t quite work if I do that. Here’s what I tried:
I added a MainComponentWrapper between my MainWindow and my MainDocument. Then I set up the transform to take place in that wrapper, applying it to my MainComponent. That works fine. The problem is that I cannot move the Constrainer that I call setFixedAspectRatio setup into my wrapper, because it’s not a ResizableWindow. And I cannot set a fixed aspect ratio for the MainWindow, because the new MainComponentWrapper it contains now has a fixed menu bar at the top, and the MainComponent below that (vertically) which is what needs to maintain its 960x560 aspect ratio.
Seems like a catch-22 to me.
You need to have a custom constrainer for that.
class CustomConstrainer : public juce::ComponentBoundsConstrainer
{
public:
CustomConstrainer(int originalWidth_, int originalHeight_, int fixedHeight_) : originalWidth(originalWidth_), originalHeight(originalHeight_), fixedHeight(fixedHeight_) { }
void checkBounds (juce::Rectangle<int> &bounds, const juce::Rectangle<int> &old, const juce::Rectangle<int> &limits, bool isStretchingTop, bool isStretchingLeft, bool isStretchingBottom, bool isStretchingRight) override
{
using juce::roundToInt;
using juce::jlimit;
int minW = originalWidth;
int maxW = 10000;
int minH = originalHeight + fixedHeight;
int maxH = 10000;
// See DecoratorConstrainer from JUCE
const auto requestedBounds = bounds;
juce::ComponentBoundsConstrainer::checkBounds(bounds,
old,
limits,
isStretchingTop,
isStretchingLeft,
isStretchingBottom,
isStretchingRight);
bounds = bounds.withPosition(requestedBounds.getPosition());
float aspectRatio = originalWidth / (float)originalHeight;
// constrain the aspect ratio if one has been specified..
if (aspectRatio > 0.0)
{
bool adjustWidth;
if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
{
adjustWidth = true;
}
else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
{
adjustWidth = false;
}
else
{
const double oldRatio = (old.getHeight() > 0) ? std::abs (old.getWidth() / (double) (old.getHeight() - fixedHeight)) : 0.0;
const double newRatio = std::abs (bounds.getWidth() / (double) (bounds.getHeight() - fixedHeight));
adjustWidth = (oldRatio > newRatio);
}
if (adjustWidth)
{
bounds.setWidth(roundToInt ((bounds.getHeight() - fixedHeight) * aspectRatio));
if (bounds.getWidth() > maxW || bounds.getWidth() < minW)
{
bounds.setWidth (jlimit (minW, maxW, bounds.getWidth()));
bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio) + fixedHeight);
}
}
else
{
bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio) + fixedHeight);
if (bounds.getHeight() > maxH || bounds.getHeight() < minH)
{
bounds.setHeight (jlimit (minH, maxH, bounds.getHeight()));
bounds.setWidth (roundToInt ((bounds.getHeight()-fixedHeight) * aspectRatio));
}
}
if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
{
bounds.setX (old.getX() + (old.getWidth() - bounds.getWidth()) / 2);
}
else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
{
bounds.setY (old.getY() + (old.getHeight() - bounds.getHeight()) / 2);
}
else
{
if (isStretchingLeft)
bounds.setX (old.getRight() - bounds.getWidth());
if (isStretchingTop)
bounds.setY (old.getBottom() - bounds.getHeight());
}
}
}
int originalWidth;
int originalHeight;
int fixedHeight;
};
1 Like