Steffen have a look at this little example program
#include "juce.h"
//==============================================================================
/**
Smoothly repositions and resizes child Components without rounding errors,
according to caller provided stretching parameters.
Usage:
1) Derive your Component from ResizableLayout
2) Give your Component a well defined default size
3) Add child Components and position them according to how
they should appear at the default.
4) For each child Component, also call addToLayout() and specify what
portion of the owner's growth or reduction should apply to each corner.
5) At the end of your constructor, call activateLayout() to turn it on
6) If you later manually reposition a control (for example, using a
ComponentDragger) call updateLayoutFor() on the control that moved,
or if you moved all the controls call updateLayout().
*/
class ResizableLayout : private ComponentListener
{
public:
enum
{
anchorUnit=100
};
enum Style
{
styleStretch,
styleFixedAspect
};
static const Point<int> anchorNone;
static const Point<int> anchorTopLeft;
static const Point<int> anchorTopCenter;
static const Point<int> anchorTopRight;
static const Point<int> anchorMidLeft;
static const Point<int> anchorMidCenter;
static const Point<int> anchorMidRight;
static const Point<int> anchorBottomLeft;
static const Point<int> anchorBottomCenter;
static const Point<int> anchorBottomRight;
public:
ResizableLayout (Component* owner);
~ResizableLayout ();
// Add a Component to the Layout.
// topLeft and bottomRight are the percentages that the top left and bottom right of
// the Component should move by, when the layout is resized.
// So if you wanted to have the control take on the full width of the parent, and
// half the height, you would use bottomRight.x=100, bottomRight.y=50. or
// use the constant anchorMidRight
void addToLayout (
Component *component,
const Point<int> &topLeft,
const Point<int> &bottomRight=anchorNone,
Style style = styleStretch );
// Remove a Component from the Layout.
void removeFromLayout (Component* component);
// Activate (or deactivate) the Layout. The Layout is initially inactive,
// to prevent spurious recalculation while a Component and its children are being
// constructed (causing resized() messages). Activating the Layout for the
// first time will cause an Update().
void activateLayout (bool bActive=true);
// Update the state information for all items. This is used on the first Activate(),
// and can also be used if multiple controls are moved or resized from elsewhere.
void updateLayout ();
// Call this to manually update the state information for a single control
// after it has been moved or resized from elsewhere.
void updateLayoutFor (Component *component);
private:
struct Rect
{
Rect() {}
Rect( int top0, int left0, int bottom0, int right0 ) { top=top0; left=left0; bottom=bottom0; right=right0; }
Rect( const Rectangle<int> &r ) { top=int(r.getY()); left=int(r.getX()); bottom=int(r.getBottom()); right=int(r.getRight()); }
operator Rectangle<int>() const { return Rectangle<int>( left, top, Width(), Height() ); }
int Height( void ) const { return bottom-top; }
int Width( void ) const { return right-left; }
void Inset( int dx, int dy ) { top+=dy; left+=dx; bottom-=dy; right-=dx; }
int top;
int left;
int bottom;
int right;
};
struct Anchor
{
Style style;
Component* component;
Point<int> topLeft;
Point<int> bottomRight;
Anchor (Component* component=0);
bool operator== (const Anchor& rhs) const;
bool operator>= (const Anchor& rhs) const;
};
struct State
{
Component* component;
double aspect;
Rect margin;
State (Component* component=0);
bool operator== (const State& rhs) const;
bool operator>= (const State& rhs) const;
};
void addStateFor (const Anchor& anchor);
void recalculateLayout ();
void componentMovedOrResized (Component& component,
bool wasMoved,
bool wasResized);
void componentBeingDeleted (Component& component);
private:
Component* m_owner;
SortedSet<Anchor> m_anchors;
SortedSet<State> m_states;
bool m_bFirstTime;
bool m_bActive;
};
const Point<int> ResizableLayout::anchorNone ( -1, -1 );
const Point<int> ResizableLayout::anchorTopLeft ( 0, 0 );
const Point<int> ResizableLayout::anchorTopCenter ( anchorUnit/2, 0 );
const Point<int> ResizableLayout::anchorTopRight ( anchorUnit, 0 );
const Point<int> ResizableLayout::anchorMidLeft ( 0, anchorUnit/2 );
const Point<int> ResizableLayout::anchorMidCenter ( anchorUnit/2, anchorUnit/2 );
const Point<int> ResizableLayout::anchorMidRight ( anchorUnit, anchorUnit/2 );
const Point<int> ResizableLayout::anchorBottomLeft ( 0, anchorUnit );
const Point<int> ResizableLayout::anchorBottomCenter( anchorUnit/2, anchorUnit );
const Point<int> ResizableLayout::anchorBottomRight ( anchorUnit, anchorUnit );
ResizableLayout::Anchor::Anchor (Component* component_)
: component (component_)
{
jassert (component);
}
bool ResizableLayout::Anchor::operator== (const Anchor& rhs) const
{ return component == rhs.component; }
bool ResizableLayout::Anchor::operator>= (const Anchor& rhs) const
{ return component >= rhs.component; }
ResizableLayout::State::State (Component* component_)
: component (component_)
{
jassert (component);
}
bool ResizableLayout::State::operator== (const State& rhs) const
{ return component == rhs.component; }
bool ResizableLayout::State::operator>= (const State& rhs) const
{ return component >= rhs.component; }
//----
ResizableLayout::ResizableLayout (Component* owner)
: m_owner (owner)
{
m_bFirstTime = true;
m_bActive = false;
m_owner->addComponentListener (this);
}
ResizableLayout::~ResizableLayout()
{
}
void ResizableLayout::addToLayout (Component* component,
const Point<int> &topLeft,
const Point<int> &bottomRight,
Style style )
{
jassert (topLeft!=anchorNone);
Anchor anchor (component);
anchor.style = style;
anchor.topLeft = topLeft;
anchor.bottomRight = bottomRight;
m_anchors.add (anchor);
//component->addComponentListener (this);
}
void ResizableLayout::removeFromLayout (Component* component)
{
m_anchors.removeValue (component);
m_states.removeValue (component);
}
void ResizableLayout::activateLayout( bool bActive )
{
if( m_bActive!=bActive )
{
if( bActive && m_bFirstTime )
{
updateLayout();
m_bFirstTime=false;
}
m_bActive=bActive;
}
}
void ResizableLayout::updateLayout ()
{
m_states.clearQuick();
for( int i=0; i<m_anchors.size(); i++ )
addStateFor (m_anchors[i]);
}
void ResizableLayout::updateLayoutFor (Component *component)
{
m_states.removeValue (component);
addStateFor (m_anchors[m_anchors.indexOf (Anchor(component))]);
}
void ResizableLayout::addStateFor (const Anchor& anchor)
{
Rect rBounds = anchor.component->getBounds();
// owner size
Point<int> ptSize( int(m_owner->getWidth()), int(m_owner->getHeight()) );
State state (anchor.component);
// secret sauce
state.margin.top = rBounds.top - (ptSize.getY() * anchor.topLeft.getY()) / anchorUnit;
state.margin.left = rBounds.left - (ptSize.getX() * anchor.topLeft.getX()) / anchorUnit;
state.margin.bottom = rBounds.bottom - (ptSize.getY() * anchor.bottomRight.getY()) / anchorUnit;
state.margin.right = rBounds.right - (ptSize.getX() * anchor.bottomRight.getX()) / anchorUnit;
state.aspect = double (rBounds.Width()) / rBounds.Height();
m_states.add (state);
}
// Recalculate the position and size of all the controls
// in the layout, based on the owner Component size.
void ResizableLayout::recalculateLayout()
{
if( m_bActive )
{
Rect rParent = m_owner->getBounds();
for( int i=0; i<m_states.size(); i++ )
{
Anchor anchor = m_anchors[i];
State state = m_states[i];
jassert (anchor.component == state.component);
Rect rBounds;
// secret sauce
rBounds.top = state.margin.top + (rParent.Height() * anchor.topLeft.getY()) / anchorUnit;
rBounds.left = state.margin.left + (rParent.Width() * anchor.topLeft.getX()) / anchorUnit;
if( anchor.bottomRight != anchorNone )
{
rBounds.bottom= state.margin.bottom + (rParent.Height() * anchor.bottomRight.getY()) / anchorUnit;
rBounds.right = state.margin.right + (rParent.Width() * anchor.bottomRight.getX()) / anchorUnit;
}
else
{
rBounds.bottom = rBounds.top + anchor.component->getHeight();
rBounds.right = rBounds.left + anchor.component->getWidth();
}
if (anchor.style == styleStretch)
{
anchor.component->setBounds (rBounds);
}
else if (anchor.style==styleFixedAspect)
{
Rect rItem;
double aspect = double (rBounds.Width()) / rBounds.Height();
if( aspect > state.aspect )
{
rItem.top = rBounds.top;
rItem.bottom = rBounds.bottom;
int width = int (state.aspect * rItem.Height());
rItem.left = rBounds.left + (rBounds.Width()-width)/2;
rItem.right = rItem.left + width;
}
else
{
rItem.left = rBounds.left;
rItem.right = rBounds.right;
int height = int (1. / state.aspect * rItem.Width());
rItem.top = rBounds.top + (rBounds.Height() - height) / 2;
rItem.bottom = rItem.top + height;
}
anchor.component->setBounds( rItem );
}
}
}
}
void ResizableLayout::componentMovedOrResized (Component& component,
bool wasMoved,
bool wasResized)
{
if( &component == m_owner )
{
if (wasResized)
{
recalculateLayout ();
}
}
}
void ResizableLayout::componentBeingDeleted (Component& component)
{
m_anchors.removeValue (&component);
m_states.removeValue (&component);
}
struct MainPanel : Component, ResizableLayout
{
MainPanel() : ResizableLayout (this)
{
// create the initial layout
// must have a defined initial size
setSize(512, 384);
TextButton* b;
b = new TextButton ("Juce");
b->setBounds (32, 32, 512-64, 32);
addToLayout (b, anchorTopLeft, anchorTopRight);
addAndMakeVisible (b);
b = new TextButton ("One");
b->setBounds (32, 96, 128, 32);
addToLayout (b, Point<int>(0,0), Point<int>(33,0));
addAndMakeVisible (b);
b = new TextButton ("Two");
b->setBounds (32+128+32, 96, 128, 32);
addToLayout (b, Point<int>(33,0), Point<int>(66,0));
addAndMakeVisible (b);
b = new TextButton ("Three");
b->setBounds (32+128+32+128+32, 96, 128, 32);
addToLayout (b, Point<int>(66,0), Point<int>(100,0));
addAndMakeVisible (b);
b = new TextButton ("BR");
b->setBounds (512-96, 384-64, 64, 32);
addToLayout (b, anchorBottomRight);
addAndMakeVisible (b);
b = new TextButton ("Aspect");
b->setBounds (32, 128+32, 96, 96);
addToLayout (b, anchorTopLeft, anchorBottomRight, styleFixedAspect);
addAndMakeVisible (b);
// turn it on
activateLayout ();
}
~MainPanel() { deleteAllChildren(); }
void paint (Graphics& g)
{
g.setColour (Colours::grey);
g.fillAll();
}
};
struct MainWindow : DocumentWindow
{
MainWindow()
: DocumentWindow (JUCE_T("Test")
, Colours::black
, DocumentWindow::allButtons
, true )
{
MainPanel* p = new MainPanel;
setResizable (true, false);
setContentComponent (p, true, true);
centreWithSize (getWidth(), getHeight());
setVisible( true );
}
~MainWindow() {}
void closeButtonPressed() { JUCEApplication::quit(); }
};
struct MainApp : JUCEApplication
{
MainApp() : mainWindow(0) { s_app=this; }
~MainApp() { s_app=0; }
static MainApp& GetInstance() { return *s_app; }
const String getApplicationName() { return JUCE_T("JuceTest"); }
const String getApplicationVersion() { return JUCE_T("0.1.0"); }
bool moreThanOneInstanceAllowed() { return true; }
void anotherInstanceStarted (const String& commandLine) {}
void initialise (const String& commandLine)
{
mainWindow = new MainWindow;
}
void shutdown()
{
delete mainWindow;
}
static MainApp* s_app;
MainWindow* mainWindow;
};
MainApp* MainApp::s_app = 0;
START_JUCE_APPLICATION (MainApp)