1026 lines
34 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/common/layout.cpp
// Purpose: Constraint layout system classes
// Author: Julian Smart
// Modified by:
// Created: 04/01/98
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// =============================================================================
// declarations
// =============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_CONSTRAINTS
#include "wx/layout.h"
#ifndef WX_PRECOMP
#include "wx/window.h"
#include "wx/utils.h"
#include "wx/dialog.h"
#include "wx/msgdlg.h"
#include "wx/intl.h"
#endif
IMPLEMENT_DYNAMIC_CLASS(wxIndividualLayoutConstraint, wxObject)
IMPLEMENT_DYNAMIC_CLASS(wxLayoutConstraints, wxObject)
inline void wxGetAsIs(wxWindowBase* win, int* w, int* h)
{
#if 1
// The old way. Works for me.
win->GetSize(w, h);
#endif
#if 0
// Vadim's change. Breaks wxPython's LayoutAnchors
win->GetBestSize(w, h);
#endif
#if 0
// Proposed compromise. Doesn't work.
int sw, sh, bw, bh;
win->GetSize(&sw, &sh);
win->GetBestSize(&bw, &bh);
if (w)
*w = wxMax(sw, bw);
if (h)
*h = wxMax(sh, bh);
#endif
}
wxIndividualLayoutConstraint::wxIndividualLayoutConstraint()
{
myEdge = wxTop;
relationship = wxUnconstrained;
margin = 0;
value = 0;
percent = 0;
otherEdge = wxTop;
done = false;
otherWin = NULL;
}
void wxIndividualLayoutConstraint::Set(wxRelationship rel, wxWindowBase *otherW, wxEdge otherE, int val, int marg)
{
if (rel == wxSameAs)
{
// If Set is called by the user with wxSameAs then call SameAs to do
// it since it will actually use wxPercent instead.
SameAs(otherW, otherE, marg);
return;
}
relationship = rel;
otherWin = otherW;
otherEdge = otherE;
if ( rel == wxPercentOf )
{
percent = val;
}
else
{
value = val;
}
margin = marg;
}
void wxIndividualLayoutConstraint::LeftOf(wxWindowBase *sibling, int marg)
{
Set(wxLeftOf, sibling, wxLeft, 0, marg);
}
void wxIndividualLayoutConstraint::RightOf(wxWindowBase *sibling, int marg)
{
Set(wxRightOf, sibling, wxRight, 0, marg);
}
void wxIndividualLayoutConstraint::Above(wxWindowBase *sibling, int marg)
{
Set(wxAbove, sibling, wxTop, 0, marg);
}
void wxIndividualLayoutConstraint::Below(wxWindowBase *sibling, int marg)
{
Set(wxBelow, sibling, wxBottom, 0, marg);
}
//
// 'Same edge' alignment
//
void wxIndividualLayoutConstraint::SameAs(wxWindowBase *otherW, wxEdge edge, int marg)
{
Set(wxPercentOf, otherW, edge, 100, marg);
}
// The edge is a percentage of the other window's edge
void wxIndividualLayoutConstraint::PercentOf(wxWindowBase *otherW, wxEdge wh, int per)
{
Set(wxPercentOf, otherW, wh, per);
}
//
// Edge has absolute value
//
void wxIndividualLayoutConstraint::Absolute(int val)
{
value = val;
relationship = wxAbsolute;
}
// Reset constraint if it mentions otherWin
bool wxIndividualLayoutConstraint::ResetIfWin(wxWindowBase *otherW)
{
if (otherW == otherWin)
{
myEdge = wxTop;
relationship = wxAsIs;
margin = 0;
value = 0;
percent = 0;
otherEdge = wxTop;
otherWin = NULL;
return true;
}
return false;
}
// Try to satisfy constraint
bool wxIndividualLayoutConstraint::SatisfyConstraint(wxLayoutConstraints *constraints, wxWindowBase *win)
{
if (relationship == wxAbsolute)
{
done = true;
return true;
}
switch (myEdge)
{
case wxLeft:
{
switch (relationship)
{
case wxLeftOf:
{
// We can know this edge if: otherWin is win's
// parent, or otherWin has a satisfied constraint,
// or otherWin has no constraint.
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos - margin;
done = true;
return true;
}
else
return false;
}
case wxRightOf:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos + margin;
done = true;
return true;
}
else
return false;
}
case wxPercentOf:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = (int)(edgePos*(((float)percent)*0.01) + margin);
done = true;
return true;
}
else
return false;
}
case wxUnconstrained:
{
// We know the left-hand edge position if we know
// the right-hand edge and we know the width; OR if
// we know the centre and the width.
if (constraints->right.GetDone() && constraints->width.GetDone())
{
value = (constraints->right.GetValue() - constraints->width.GetValue() + margin);
done = true;
return true;
}
else if (constraints->centreX.GetDone() && constraints->width.GetDone())
{
value = (int)(constraints->centreX.GetValue() - (constraints->width.GetValue()/2) + margin);
done = true;
return true;
}
else
return false;
}
case wxAsIs:
{
int y;
win->GetPosition(&value, &y);
done = true;
return true;
}
default:
break;
}
break;
}
case wxRight:
{
switch (relationship)
{
case wxLeftOf:
{
// We can know this edge if: otherWin is win's
// parent, or otherWin has a satisfied constraint,
// or otherWin has no constraint.
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos - margin;
done = true;
return true;
}
else
return false;
}
case wxRightOf:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos + margin;
done = true;
return true;
}
else
return false;
}
case wxPercentOf:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = (int)(edgePos*(((float)percent)*0.01) - margin);
done = true;
return true;
}
else
return false;
}
case wxUnconstrained:
{
// We know the right-hand edge position if we know the
// left-hand edge and we know the width, OR if we know the
// centre edge and the width.
if (constraints->left.GetDone() && constraints->width.GetDone())
{
value = (constraints->left.GetValue() + constraints->width.GetValue() - margin);
done = true;
return true;
}
else if (constraints->centreX.GetDone() && constraints->width.GetDone())
{
value = (int)(constraints->centreX.GetValue() + (constraints->width.GetValue()/2) - margin);
done = true;
return true;
}
else
return false;
}
case wxAsIs:
{
int x, y;
int w, h;
wxGetAsIs(win, &w, &h);
win->GetPosition(&x, &y);
value = x + w;
done = true;
return true;
}
default:
break;
}
break;
}
case wxTop:
{
switch (relationship)
{
case wxAbove:
{
// We can know this edge if: otherWin is win's
// parent, or otherWin has a satisfied constraint,
// or otherWin has no constraint.
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos - margin;
done = true;
return true;
}
else
return false;
}
case wxBelow:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos + margin;
done = true;
return true;
}
else
return false;
}
case wxPercentOf:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = (int)(edgePos*(((float)percent)*0.01) + margin);
done = true;
return true;
}
else
return false;
}
case wxUnconstrained:
{
// We know the top edge position if we know the bottom edge
// and we know the height; OR if we know the centre edge and
// the height.
if (constraints->bottom.GetDone() && constraints->height.GetDone())
{
value = (constraints->bottom.GetValue() - constraints->height.GetValue() + margin);
done = true;
return true;
}
else if (constraints->centreY.GetDone() && constraints->height.GetDone())
{
value = (constraints->centreY.GetValue() - (constraints->height.GetValue()/2) + margin);
done = true;
return true;
}
else
return false;
}
case wxAsIs:
{
int x;
win->GetPosition(&x, &value);
done = true;
return true;
}
default:
break;
}
break;
}
case wxBottom:
{
switch (relationship)
{
case wxAbove:
{
// We can know this edge if: otherWin is win's parent,
// or otherWin has a satisfied constraint, or
// otherWin has no constraint.
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos + margin;
done = true;
return true;
}
else
return false;
}
case wxBelow:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos - margin;
done = true;
return true;
}
else
return false;
}
case wxPercentOf:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = (int)(edgePos*(((float)percent)*0.01) - margin);
done = true;
return true;
}
else
return false;
}
case wxUnconstrained:
{
// We know the bottom edge position if we know the top edge
// and we know the height; OR if we know the centre edge and
// the height.
if (constraints->top.GetDone() && constraints->height.GetDone())
{
value = (constraints->top.GetValue() + constraints->height.GetValue() - margin);
done = true;
return true;
}
else if (constraints->centreY.GetDone() && constraints->height.GetDone())
{
value = (constraints->centreY.GetValue() + (constraints->height.GetValue()/2) - margin);
done = true;
return true;
}
else
return false;
}
case wxAsIs:
{
int x, y;
int w, h;
wxGetAsIs(win, &w, &h);
win->GetPosition(&x, &y);
value = h + y;
done = true;
return true;
}
default:
break;
}
break;
}
case wxCentreX:
{
switch (relationship)
{
case wxLeftOf:
{
// We can know this edge if: otherWin is win's parent, or
// otherWin has a satisfied constraint, or otherWin has no
// constraint.
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos - margin;
done = true;
return true;
}
else
return false;
}
case wxRightOf:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos + margin;
done = true;
return true;
}
else
return false;
}
case wxPercentOf:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = (int)(edgePos*(((float)percent)*0.01) + margin);
done = true;
return true;
}
else
return false;
}
case wxUnconstrained:
{
// We know the centre position if we know
// the left-hand edge and we know the width, OR
// the right-hand edge and the width
if (constraints->left.GetDone() && constraints->width.GetDone())
{
value = (int)(constraints->left.GetValue() + (constraints->width.GetValue()/2) + margin);
done = true;
return true;
}
else if (constraints->right.GetDone() && constraints->width.GetDone())
{
value = (int)(constraints->left.GetValue() - (constraints->width.GetValue()/2) + margin);
done = true;
return true;
}
else
return false;
}
default:
break;
}
break;
}
case wxCentreY:
{
switch (relationship)
{
case wxAbove:
{
// We can know this edge if: otherWin is win's parent,
// or otherWin has a satisfied constraint, or otherWin
// has no constraint.
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos - margin;
done = true;
return true;
}
else
return false;
}
case wxBelow:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = edgePos + margin;
done = true;
return true;
}
else
return false;
}
case wxPercentOf:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = (int)(edgePos*(((float)percent)*0.01) + margin);
done = true;
return true;
}
else
return false;
}
case wxUnconstrained:
{
// We know the centre position if we know
// the top edge and we know the height, OR
// the bottom edge and the height.
if (constraints->bottom.GetDone() && constraints->height.GetDone())
{
value = (int)(constraints->bottom.GetValue() - (constraints->height.GetValue()/2) + margin);
done = true;
return true;
}
else if (constraints->top.GetDone() && constraints->height.GetDone())
{
value = (int)(constraints->top.GetValue() + (constraints->height.GetValue()/2) + margin);
done = true;
return true;
}
else
return false;
}
default:
break;
}
break;
}
case wxWidth:
{
switch (relationship)
{
case wxPercentOf:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = (int)(edgePos*(((float)percent)*0.01));
done = true;
return true;
}
else
return false;
}
case wxAsIs:
{
if (win)
{
int h;
wxGetAsIs(win, &value, &h);
done = true;
return true;
}
else return false;
}
case wxUnconstrained:
{
// We know the width if we know the left edge and the right edge, OR
// if we know the left edge and the centre, OR
// if we know the right edge and the centre
if (constraints->left.GetDone() && constraints->right.GetDone())
{
value = constraints->right.GetValue() - constraints->left.GetValue();
done = true;
return true;
}
else if (constraints->centreX.GetDone() && constraints->left.GetDone())
{
value = (int)(2*(constraints->centreX.GetValue() - constraints->left.GetValue()));
done = true;
return true;
}
else if (constraints->centreX.GetDone() && constraints->right.GetDone())
{
value = (int)(2*(constraints->right.GetValue() - constraints->centreX.GetValue()));
done = true;
return true;
}
else
return false;
}
default:
break;
}
break;
}
case wxHeight:
{
switch (relationship)
{
case wxPercentOf:
{
int edgePos = GetEdge(otherEdge, win, otherWin);
if (edgePos != -1)
{
value = (int)(edgePos*(((float)percent)*0.01));
done = true;
return true;
}
else
return false;
}
case wxAsIs:
{
if (win)
{
int w;
wxGetAsIs(win, &w, &value);
done = true;
return true;
}
else return false;
}
case wxUnconstrained:
{
// We know the height if we know the top edge and the bottom edge, OR
// if we know the top edge and the centre, OR
// if we know the bottom edge and the centre
if (constraints->top.GetDone() && constraints->bottom.GetDone())
{
value = constraints->bottom.GetValue() - constraints->top.GetValue();
done = true;
return true;
}
else if (constraints->top.GetDone() && constraints->centreY.GetDone())
{
value = (int)(2*(constraints->centreY.GetValue() - constraints->top.GetValue()));
done = true;
return true;
}
else if (constraints->bottom.GetDone() && constraints->centreY.GetDone())
{
value = (int)(2*(constraints->bottom.GetValue() - constraints->centreY.GetValue()));
done = true;
return true;
}
else
return false;
}
default:
break;
}
break;
}
default:
break;
}
return false;
}
// Get the value of this edge or dimension, or if this is not determinable, -1.
int wxIndividualLayoutConstraint::GetEdge(wxEdge which,
wxWindowBase *thisWin,
wxWindowBase *other) const
{
// If the edge or dimension belongs to the parent, then we know the
// dimension is obtainable immediately. E.g. a wxExpandSizer may contain a
// button (but the button's true parent is a panel, not the sizer)
if (other->GetChildren().Find((wxWindow*)thisWin))
{
switch (which)
{
case wxLeft:
{
return 0;
}
case wxTop:
{
return 0;
}
case wxRight:
{
int w, h;
other->GetClientSizeConstraint(&w, &h);
return w;
}
case wxBottom:
{
int w, h;
other->GetClientSizeConstraint(&w, &h);
return h;
}
case wxWidth:
{
int w, h;
other->GetClientSizeConstraint(&w, &h);
return w;
}
case wxHeight:
{
int w, h;
other->GetClientSizeConstraint(&w, &h);
return h;
}
case wxCentreX:
case wxCentreY:
{
int w, h;
other->GetClientSizeConstraint(&w, &h);
if (which == wxCentreX)
return (int)(w/2);
else
return (int)(h/2);
}
default:
return -1;
}
}
switch (which)
{
case wxLeft:
{
wxLayoutConstraints *constr = other->GetConstraints();
// If no constraints, it means the window is not dependent
// on anything, and therefore we know its value immediately
if (constr)
{
if (constr->left.GetDone())
return constr->left.GetValue();
else
return -1;
}
else
{
int x, y;
other->GetPosition(&x, &y);
return x;
}
}
case wxTop:
{
wxLayoutConstraints *constr = other->GetConstraints();
// If no constraints, it means the window is not dependent
// on anything, and therefore we know its value immediately
if (constr)
{
if (constr->top.GetDone())
return constr->top.GetValue();
else
return -1;
}
else
{
int x, y;
other->GetPosition(&x, &y);
return y;
}
}
case wxRight:
{
wxLayoutConstraints *constr = other->GetConstraints();
// If no constraints, it means the window is not dependent
// on anything, and therefore we know its value immediately
if (constr)
{
if (constr->right.GetDone())
return constr->right.GetValue();
else
return -1;
}
else
{
int x, y, w, h;
other->GetPosition(&x, &y);
other->GetSize(&w, &h);
return (int)(x + w);
}
}
case wxBottom:
{
wxLayoutConstraints *constr = other->GetConstraints();
// If no constraints, it means the window is not dependent
// on anything, and therefore we know its value immediately
if (constr)
{
if (constr->bottom.GetDone())
return constr->bottom.GetValue();
else
return -1;
}
else
{
int x, y, w, h;
other->GetPosition(&x, &y);
other->GetSize(&w, &h);
return (int)(y + h);
}
}
case wxWidth:
{
wxLayoutConstraints *constr = other->GetConstraints();
// If no constraints, it means the window is not dependent
// on anything, and therefore we know its value immediately
if (constr)
{
if (constr->width.GetDone())
return constr->width.GetValue();
else
return -1;
}
else
{
int w, h;
other->GetSize(&w, &h);
return w;
}
}
case wxHeight:
{
wxLayoutConstraints *constr = other->GetConstraints();
// If no constraints, it means the window is not dependent
// on anything, and therefore we know its value immediately
if (constr)
{
if (constr->height.GetDone())
return constr->height.GetValue();
else
return -1;
}
else
{
int w, h;
other->GetSize(&w, &h);
return h;
}
}
case wxCentreX:
{
wxLayoutConstraints *constr = other->GetConstraints();
// If no constraints, it means the window is not dependent
// on anything, and therefore we know its value immediately
if (constr)
{
if (constr->centreX.GetDone())
return constr->centreX.GetValue();
else
return -1;
}
else
{
int x, y, w, h;
other->GetPosition(&x, &y);
other->GetSize(&w, &h);
return (int)(x + (w/2));
}
}
case wxCentreY:
{
wxLayoutConstraints *constr = other->GetConstraints();
// If no constraints, it means the window is not dependent
// on anything, and therefore we know its value immediately
if (constr)
{
if (constr->centreY.GetDone())
return constr->centreY.GetValue();
else
return -1;
}
else
{
int x, y, w, h;
other->GetPosition(&x, &y);
other->GetSize(&w, &h);
return (int)(y + (h/2));
}
}
default:
break;
}
return -1;
}
wxLayoutConstraints::wxLayoutConstraints()
{
left.SetEdge(wxLeft);
top.SetEdge(wxTop);
right.SetEdge(wxRight);
bottom.SetEdge(wxBottom);
centreX.SetEdge(wxCentreX);
centreY.SetEdge(wxCentreY);
width.SetEdge(wxWidth);
height.SetEdge(wxHeight);
}
bool wxLayoutConstraints::SatisfyConstraints(wxWindowBase *win, int *nChanges)
{
int noChanges = 0;
bool done = width.GetDone();
bool newDone = (done ? true : width.SatisfyConstraint(this, win));
if (newDone != done)
noChanges ++;
done = height.GetDone();
newDone = (done ? true : height.SatisfyConstraint(this, win));
if (newDone != done)
noChanges ++;
done = left.GetDone();
newDone = (done ? true : left.SatisfyConstraint(this, win));
if (newDone != done)
noChanges ++;
done = top.GetDone();
newDone = (done ? true : top.SatisfyConstraint(this, win));
if (newDone != done)
noChanges ++;
done = right.GetDone();
newDone = (done ? true : right.SatisfyConstraint(this, win));
if (newDone != done)
noChanges ++;
done = bottom.GetDone();
newDone = (done ? true : bottom.SatisfyConstraint(this, win));
if (newDone != done)
noChanges ++;
done = centreX.GetDone();
newDone = (done ? true : centreX.SatisfyConstraint(this, win));
if (newDone != done)
noChanges ++;
done = centreY.GetDone();
newDone = (done ? true : centreY.SatisfyConstraint(this, win));
if (newDone != done)
noChanges ++;
*nChanges = noChanges;
return AreSatisfied();
}
#endif // wxUSE_CONSTRAINTS