ComponentBoundsConstrainer consolidated list of issues


#1

[1] For any DocumentWindow, the MenuBar size is not factored into consideration when applying a ComponentBoundsConstrainer. This means that with a minimum height on the constrainer, the content component will be smaller than the minimum height by an amount equal to the menu bar height.

[2] DocumentWindow provides no way to examine the menu bar component or its height (this is covered in this post http://rawmaterialsoftware.com/viewtopic.php?f=2&t=6355).

[3] When setUsingNativeTitleBar is false for a DocumentWindow, the content component border and title bar sizes are not factored into consideration when applying a ComponentBoundsConstrainer

[4] When setUsingNativeTitleBar is true on Windows, the native window border size is not properly factored into account, and the resizeStart() and resizeEnd() members of the attached ComponentBoundsConstrainer don’t get called. I have posted platform specific fixes for this (http://rawmaterialsoftware.com/viewtopic.php?f=3&t=6352)

[5] ResizableWindow::getContentComponentBorder() should be public, to support my “shim” below.

[6] ComponentBoundsConstrainer is missing the function getMinimumOnscreenAmounts():

    void getMinimumOnscreenAmounts (int& minimumWhenOffTheTop,
                                    int& minimumWhenOffTheLeft,
                                    int& minimumWhenOffTheBottom,
                                    int& minimumWhenOffTheRight) throw()
    {
      minimumWhenOffTheTop=minOffTop;
      minimumWhenOffTheLeft=minOffLeft;
      minimumWhenOffTheBottom=minOffBottom;
      minimumWhenOffTheRight=minOffRight;
    }

I have developed a “shim”, which is a ComponentBoundsConstrainer that sits between your constrainer and the actual constrainer placed on the Window, that fixes all of these problems. I did this because it may be considered a matter of opinion whether the constrainer should apply to the entire TopLevelWindow including adornments (as the code works currently) or if the constrainer should apply strictly to the associated content Component. For my use-case, I want precise control over the content Component, hence my need for these changes.

In order for this ComponentBoundsConstrainer to compile and work properly, you will need

  • Fixes in Win32ComponentPeer if you use native title bars
  • ResizableWindow::getContentComponentBorder() made public
  • added function ComponentBoundsConstrainer::getMinimumOnscreenAmounts ()

Here are the steps for using this class:

  • call setConstrainer() on your ResizableWindow derived class with your own constrainer
  • now call ContentComponentConstrainer::attachTo (yourResizableWindow)

It will delete itself automatically, and adjust the limits so that your content component is perfectly constrained.

class ContentComponentConstrainer
  : private ComponentBoundsConstrainer
  , private ComponentListener
{
public:
  // we can attach to anything with ResizableWindow as a base
  static void attachTo (ResizableWindow* resizableWindow)
  {
    ContentComponentConstrainer* contentConstrainer =
      new ContentComponentConstrainer (resizableWindow);
    resizableWindow->addComponentListener (contentConstrainer);
  }

private:
  ContentComponentConstrainer (ResizableWindow* resizableWindow)
   : m_resizableWindow (resizableWindow)
   , m_constrainer (0)
  {
    m_constrainer = m_resizableWindow->getConstrainer();
    jassert (m_constrainer); // must exist

    m_resizableWindow->setConstrainer (this);
    m_resizableWindow->addComponentListener (this);
  }

  void resizeStart()
  {
    m_constrainer->resizeStart();
    copyConstraints (*m_constrainer);
    adjustConstraints();
  }

  void resizeEnd()
  {
    m_constrainer->resizeEnd();
  }

  void copyConstraints (ComponentBoundsConstrainer& from)
  {
    setMinimumWidth (from.getMinimumWidth());
    setMaximumWidth (from.getMaximumWidth());
    setMinimumHeight (from.getMinimumHeight());
    setMaximumHeight (from.getMaximumHeight());
    setFixedAspectRatio (from.getFixedAspectRatio());

    int minimumWhenOffTheTop;
    int minimumWhenOffTheLeft;
    int minimumWhenOffTheBottom;
    int minimumWhenOffTheRight;

    from.getMinimumOnscreenAmounts (minimumWhenOffTheTop,
                                    minimumWhenOffTheLeft,
                                    minimumWhenOffTheBottom,
                                    minimumWhenOffTheRight);

    setMinimumOnscreenAmounts (minimumWhenOffTheTop,
                               minimumWhenOffTheLeft,
                               minimumWhenOffTheBottom,
                               minimumWhenOffTheRight);
  }

  static int addLimit (int value, int amount)
  {
    if ( value>=(0x3fffffff-amount) )
      return 0x3fffffff;
    else
      return value + amount;
  }

  // adjusts the current constraints to take into account decorations
  void adjustConstraints()
  {
    BorderSize border = m_resizableWindow->getContentComponentBorder();
    setMinimumHeight (getMinimumHeight() + border.getTop() + border.getBottom());
    setMaximumHeight (addLimit (getMaximumHeight(), border.getTop() + border.getBottom()));
    setMinimumWidth (getMinimumWidth() + border.getLeft() + border.getRight());
    setMaximumWidth (addLimit (getMaximumWidth(), border.getLeft() + border.getRight()));
  }

  void componentBeingDeleted (Component& component)
  {
    delete this;
  }

private:
  ResizableWindow* m_resizableWindow;
  ComponentBoundsConstrainer* m_constrainer;
};