I want to do a layout manager in C++ with MFC. This layout would contain a matrix of windows. The layout initial form would be defined by a matrix selector( like in MS Word ). After that the user can resize each window by pulling it's edge. I am thinking about using a 2d collision detection algorithm but I don't know how this would perform on real time resizing( or how to do it with mfc)
I would like to impose a minimum size to each element and block shrinking of element(when another is expanding) if it violates this restriction. Waiting for any sugestion
I had to do a similar layout manager in C++ and MFC. I implemented a N-ary tree (called CDisplayObject) that contains rectangles and each leaf in the tree can have a CFrame. To construct a such tree a simple text parsing is performed. Each rectangle is between 0 and 1 and each have childs that the sum of the sizes are 1.
I do not resize the windows but it can probably be implemented quite easy because you just resize the CDisplayObject which in it's turn resize the windows.
Here is the code that handles the displayobjects which resize itself according to a "display" rectangle which in our program is the MainView.
To create a "display-tree" with one big rectangle on the left side and three smaller on the right:
CDisplayObject* tmp;
CString szCurrentName = "Big + three"
tmp = new CDisplayObject(szCurrentName);
tmp->Add(szCurrentName + "|Column1",0.75);
tmp->Add(szCurrentName + "|Column2",0.25);
tmp->Add(szCurrentName + "|Column2-Row1",0.3333333);
tmp->Add(szCurrentName + "|Column2-Row2",0.3333333);
tmp->Add(szCurrentName + "|Column2-Row3",0.3333333);
the "|" signals that it is a column and the "-" signals that this is a row.
Set the leafs to contain childwindows with SwitchFrameWnd and resize all the displayobjects in the tree with SetPos(CRect). You could add re-sizing of window by implementing a function that changes the weight of the row/column/leaf througout the tree.
#pragma once
#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif
class CDisplayObject : public CObject
{
friend class CMainView;
typedef std::list<CDisplayObject*> Childrens;
public:
typedef std::list<CDisplayObject*> Leafs;
typedef enum
{
LEAF,
ROW,
COLUMN
} ITEM_TYPE;
CDisplayObject(LPCTSTR name,CDisplayObject* Parent = NULL, CoordinateType weight = 1.0);
virtual ~CDisplayObject();
bool HasViewOrFrame(const CWnd* wnd) const;
void TRACE_IT();
void Draw(CDC* pDC, const CRect& drawRect,BOOL isIconMode);
CView* GetView() const;
void Add(LPCSTR layoutItemDesc, CoordinateType weigth);
//returns all the leafs in the tree
virtual Leafs GetLeafs() ;
//this removes the framewnd from any other object in same layout-tree
//and sets the view in this object
virtual BOOL SwitchFrameWnd(CFrameWnd* view);
CFrameWnd* GetFrameWnd() const {return m_pFrameWnd;}
void SetPos(const RECT& rect);
CString GetName() const { return m_Name;}
CString GetUniqueName() const {return m_UUID;}
//returns the complete name for this leaf i.e: main|column-row
CString GetCompleteName() const;
CRect GetDisplayRect() const { return m_DisplayRect; }
CDisplayObject* GetFirstEmptyLeaf();
CDisplayObject* GetFirstEmptyLeafAfterLastFrame();
//removes the wnd in the subtree
void RemoveWnd(CFrameWnd* view);
//calls updateSlaveWindows for each view
void UpdateSlaveWindows();
//returns the CDisplayObject that has the wnd as framewnd or view
//can return null!
CDisplayObject* GetObjectWithWnd(const CWnd* wnd) ;
CDisplayObject* GetLeafViewFromPoint(const CPoint& point);
protected:
CDisplayObject(){};
BOOL IsLeaf() const { return m_type == LEAF && m_Childrens.size() == 0;}
CRect CalculateChildrenRect(CDisplayObject* children, const CRect& mainRect,Vector2d &topLeft);
void SetDisplayRect(CRect val) ;
CFrameWnd* GetLastView() const { return m_pLastView; }
void HideViews();
void ShowViews();
CDisplayObject::ITEM_TYPE GetType() const { return m_type; }
private:
static CString translateString(LPCTSTR str);
static CString GetPrefix( LPCSTR str);
static CString GetSuffix( LPCTSTR str );
static ITEM_TYPE GetDividerType( LPCTSTR str );
void RepositionView();
void SetWeight( CoordinateType weight );
CoordinateType GetWeight() const;
CoordinateType GetChildrenWeight() const;
CDisplayObject* GetRootObject();
CFrameWnd *m_pFrameWnd;
//used for distributing documents correctly (=last place used) when changing layouts
CFrameWnd *m_pLastView;
Childrens m_Childrens;
CoordinateType m_Weight;
CString m_Name;
CString m_UUID;
ITEM_TYPE m_type;
//this is the client rect for this window
CRect m_DisplayRect;
//the parent for this CDisplayObject. if m_parent == NULL => root-object
CDisplayObject* m_Parent;
//used for draw the name of the object
#ifdef _DEBUG
CFont *m_Font;
#endif // _DEBUG
DECLARE_DYNCREATE(CDisplayObject)
};
cpp implementation
#include "stdafx.h"
#include "DisplayObject.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CDisplayObject, CObject )
//////////////////////////////////////////////////////////////////////////
///CDisplayObject
//////////////////////////////////////////////////////////////////////////
CDisplayObject::CDisplayObject(LPCTSTR name, CDisplayObject* Parent /*= NULL*/, CoordinateType weight /*= 1.0*/)
:CObject()
{
m_UUID = CreateUUID();
m_Name = GetPrefix(name);
m_type = LEAF;
ASSERT(!m_Name.IsEmpty());
if( !GetSuffix(name).IsEmpty() )
{
this->Add(name, weight);
}
else
{
m_Weight = weight;
}
m_Parent = Parent;
m_pFrameWnd = NULL;
m_pLastView = NULL;
CViewSettings viewSettings;
viewSettings = AfxGetProperties()->m_ViewSettings;
m_DisplayRect = CRect(0,0,0,0);
#ifdef _DEBUG
m_Font = new CFont;
m_Font->CreateFont(
12, // nHeight
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight,
FALSE, // bItalic
FALSE, // bUnderline
FALSE, // cStrikeOut
DEFAULT_CHARSET, // CharSet
OUT_CHARACTER_PRECIS,
CLIP_DEFAULT_PRECIS,
PROOF_QUALITY,
FF_DONTCARE,
"Arial" );
#endif // _DEBUG
}
CDisplayObject::~CDisplayObject()
{
// TRACE_LINE;
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
delete *it;
m_Childrens.clear();
#ifdef _DEBUG
if( m_Font )
{
m_Font->DeleteObject();
SAFE_DELETE(m_Font);
}
#endif // _DEBUG
}
void CDisplayObject::Add( LPCSTR layoutItemDesc, CoordinateType weigth )
{
CString prefix = GetPrefix(layoutItemDesc);
CString suffix = GetSuffix(layoutItemDesc);
if(prefix == m_Name )
{
if(!suffix.IsEmpty())
{
m_type = (m_type == LEAF) ? GetDividerType(layoutItemDesc) : m_type;
CString prefixSuffix = GetPrefix(suffix);
CDisplayObject* tmp = NULL;
if( !prefixSuffix.IsEmpty() )
{
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
if( (*it)->GetName() == prefixSuffix )
{
tmp = *it;
break;
}
}
if( tmp == NULL)
{
tmp = new CDisplayObject(prefixSuffix, this);
m_Childrens.push_back(tmp);
}
}
tmp->Add(suffix, weigth);
}
else
{
SetWeight(weigth);
}
}
}
void CDisplayObject::TRACE_IT()
{
TRACE( m_Name );
TRACE( " %.2f", m_Weight );
if( m_Childrens.size() > 0)
{
TRACE(" ( ");
CString divider;
for(Childrens::const_iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
TRACE(divider);
(*it)->TRACE_IT();
divider = (m_type == ROW) ? " - " : " | ";
}
TRACE( " ) ");
}
}
CString CDisplayObject::translateString( LPCTSTR str )
{
CString retValue = str;
retValue.MakeLower();
retValue.Trim();
return str;
}
CString CDisplayObject::GetPrefix( LPCSTR str )
{
CString tmpStr = translateString(str);
int index = tmpStr.FindOneOf("|-");
if( index == -1 )
{
return tmpStr;
}
else
{
return tmpStr.Left(index);
}
}
CString CDisplayObject::GetSuffix( LPCTSTR str )
{
CString tmpStr = translateString(str);
int index = tmpStr.FindOneOf("|-");
if( index == -1 )
{
return "";
}
else
{
return tmpStr.Mid(index + 1);
}
}
CDisplayObject::ITEM_TYPE CDisplayObject::GetDividerType( LPCTSTR str )
{
CString tmpStr = translateString(str);
int index = tmpStr.Find("-");
if(index != -1) //horizontal
{
return ROW;
}
index = tmpStr.Find("|");
if(index != -1) //vertical
{
return COLUMN;
}
return LEAF;
}
CoordinateType CDisplayObject::GetWeight() const
{
return m_Weight;
}
CoordinateType CDisplayObject::GetChildrenWeight() const
{
CoordinateType sum = 0;
for(Childrens::const_iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
sum += (*it)->GetWeight( );
}
return sum;
}
static bool IsRectEqual(const CRect &obj1, const CRect &obj2)
{
return obj1.left == obj2.left && obj1.right == obj2.right && obj1.top == obj2.top && obj1.bottom == obj2.bottom;
}
void CDisplayObject::SetWeight( CoordinateType weight )
{
m_Weight = weight;
}
void CDisplayObject::SetPos(const RECT& rect)
{
SetDisplayRect(rect);
if(IsLeaf())
{
SetDisplayRect(rect);
}
else
{
Vector2d topLeft;
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
CRect newDrawRect = CalculateChildrenRect(*it,rect,topLeft);
(*it)->SetPos(newDrawRect);
}
}
}
CRect CDisplayObject::CalculateChildrenRect( CDisplayObject* children, const CRect& mainRect, Vector2d& topLeft )
{
ASSERT(m_Childrens.size() > 0);
ASSERT(m_type == COLUMN || m_type == ROW);
CRect drawRectangle;
if(m_type == COLUMN)
{
CoordinateType weight = children->GetWeight();
Vector2d BottomRight(topLeft.X() + weight, 1.0);
CLayoutRectangle childrenRectangle(topLeft, BottomRight);
drawRectangle = childrenRectangle.ToCRect(mainRect);
topLeft = childrenRectangle.TopRight();
}
else if( m_type == ROW)
{
CoordinateType weight = children->GetWeight();
Vector2d BottomRight(1.0,topLeft.Y() + weight);
CLayoutRectangle childrenRectangle(topLeft, BottomRight);
drawRectangle = childrenRectangle.ToCRect(mainRect);
topLeft = childrenRectangle.BottomLeft();
}
return drawRectangle;
}
void CDisplayObject::Draw( CDC* pDC, const CRect& drawRect,BOOL isIconMode )
{
if(IsLeaf())
{
if(!isIconMode)
{
SetDisplayRect(drawRect);
}
pDC->FillRect(drawRect,&CBrush( m_ViewSettings.m_BackgroundColor ) );
/*
#ifdef _DEBUG
if( !isIconMode )
{
pDC->SetTextAlign( TA_CENTER );
CFont* pOldFont = pDC->SelectObject( m_Font );
pDC->SetBkMode( TRANSPARENT );
pDC->SetTextColor( RGB( 255, 255, 255 ) );
pDC->TextOut(drawRect.left + drawRect.Width() / 2,
drawRect.top + drawRect.Height() / 2,
GetCompleteName() );
pDC->SelectObject( pOldFont);
}
#endif // _DEBUG
*/
CPen FramePen(PS_SOLID, m_ViewSettings.m_FrameWidth, m_ViewSettings.m_FrameColor);
CPen* oldPen;
oldPen = pDC->SelectObject(&FramePen);
CLayoutRectangle(drawRect).DoPaint(pDC);
pDC->SelectObject(oldPen);
}
else
{
Vector2d topLeft;
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
CRect newDrawRect = CalculateChildrenRect(*it,drawRect,topLeft);
(*it)->Draw(pDC,newDrawRect,isIconMode);
}
}
}
CDisplayObject* CDisplayObject::GetFirstEmptyLeaf()
{
if(IsLeaf() && m_pFrameWnd == NULL)
{
return this;
}
else
{
for(Childrens::const_iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
CDisplayObject* tmpDisplayObject = (*it)->GetFirstEmptyLeaf();
if( tmpDisplayObject != NULL )
{
return tmpDisplayObject;
}
}
}
return NULL;
}
CDisplayObject* CDisplayObject::GetFirstEmptyLeafAfterLastFrame()
{
Leafs leafs = GetLeafs();
Leafs::reverse_iterator it = leafs.rbegin();
while(1)
{
Leafs::reverse_iterator tmpIt = it;
if((*tmpIt)->GetFrameWnd() != NULL)
{
return NULL;
}
it++;
if(it != leafs.rend())
{
if( (*it)->GetFrameWnd() != NULL)
return *tmpIt;
}
else
{
//no frameWnd present
return (*leafs.begin());
}
}
return NULL;
}
CDisplayObject* CDisplayObject::GetLeafViewFromPoint( const CPoint& point )
{
if(IsLeaf() && m_DisplayRect.PtInRect(point))
{
return this;
}
else
{
for(Childrens::const_iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
CDisplayObject* tmpDisplayObject = (*it)->GetLeafViewFromPoint( point );
if( tmpDisplayObject != NULL )
{
return tmpDisplayObject;
}
}
}
return NULL;
}
BOOL CDisplayObject::SwitchFrameWnd( CFrameWnd* View )
{
//walk through whole tree and remove the view
GetRootObject()->RemoveWnd(View);
if(!IsLeaf())
return FALSE;
if(m_pFrameWnd && ::IsWindow(m_pFrameWnd->GetSafeHwnd()))
RemoveWindow(m_pFrameWnd); //hide the window
m_pLastView = m_pFrameWnd;
m_pFrameWnd = View;
if(m_pFrameWnd)
{
RepositionView();
if(m_pFrameWnd && ::IsWindow(m_pFrameWnd->GetSafeHwnd()))
AddWindow(m_pFrameWnd);
if(m_pFrameWnd && ::IsWindow(m_pFrameWnd->GetSafeHwnd()))
{
if(m_pFrameWnd->GetActiveView())
{
CImageView* pImageView = (CImageView*)m_pFrameWnd->GetActiveView();
pImageView->ResetOrigoPanPos();
pImageView->ResetPanPos(FALSE);
pImageView->UpdateShownImage();
}
m_pFrameWnd->Invalidate();
m_pFrameWnd->RedrawWindow();
}
}
return TRUE;
}
CDisplayObject::Leafs CDisplayObject::GetLeafs( )
{
Leafs retValue;
if(IsLeaf())
{
retValue.push_back(this);
}
else
{
for(Childrens::const_iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
Leafs tmpLeafs = (*it)->GetLeafs( );
retValue.merge(tmpLeafs);
}
}
return retValue;
}
void CDisplayObject::SetDisplayRect( CRect val )
{
if( IsRectEqual( val , m_DisplayRect) )
return;
//TRACE_LINE;
int frameWidth = m_ViewSettings.m_FrameWidth;
m_DisplayRect = val;
//deflate the rect for accomodate the own frame.
if(IsLeaf())
m_DisplayRect.DeflateRect(frameWidth,frameWidth,frameWidth,frameWidth);
//m_DisplayRect.OffsetRect(CPoint(0,-100));
RepositionView();
}
void CDisplayObject::RepositionView()
{
if(m_pFrameWnd && ::IsWindow(m_pFrameWnd->GetSafeHwnd()))
{
m_pFrameWnd->SetWindowPos(NULL,m_DisplayRect.left,m_DisplayRect.top,m_DisplayRect.Width(),m_DisplayRect.Height(),SWP_NOZORDER);
}
}
void CDisplayObject::HideViews()
{
if(GetFrameWnd() && ::IsWindow(GetFrameWnd()->GetSafeHwnd()))
RemoveWindow(GetFrameWnd());
if(!IsLeaf())
{
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
(*it)->HideViews( );
}
}
}
void CDisplayObject::ShowViews()
{
if(GetFrameWnd() && ::IsWindow(GetFrameWnd()->GetSafeHwnd()))
AddWindow(GetFrameWnd());
if(!IsLeaf())
{
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
(*it)->ShowViews( );
}
}
}
void CDisplayObject::RemoveWnd( CFrameWnd* view )
{
if(m_pFrameWnd == view)
{
m_pFrameWnd = NULL;
}
if(!IsLeaf())
{
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
(*it)->RemoveWnd( view );
}
}
}
CDisplayObject* CDisplayObject::GetRootObject()
{
//get the root display object
CDisplayObject* rootParent = m_Parent;
CDisplayObject* currentObject = this;
while(rootParent)
{
CDisplayObject* tmp = rootParent;
currentObject = rootParent;
rootParent = tmp->m_Parent;
}
ASSERT(currentObject);
return currentObject;
}
CView* CDisplayObject::GetView() const
{
if( m_pFrameWnd && m_pFrameWnd->IsKindOf(RUNTIME_CLASS(CImageFrameWnd)))
{
CView* view = m_pFrameWnd->GetActiveView();
if(view && view->IsKindOf(RUNTIME_CLASS(CImageView)))
{
return (CImageView*)view;
}
}
return NULL;
}
bool CDisplayObject::HasViewOrFrame( const CWnd* wnd ) const
{
if( this->m_pFrameWnd == wnd)
return true;
if(this->GetFrameWnd() && this->GetView() == wnd)
{
return true;
}
return false;
}
CDisplayObject* CDisplayObject::GetObjectWithWnd(const CWnd* wnd)
{
if(this->HasViewOrFrame( wnd ))
{
return this;
}
if(!IsLeaf())
{
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
CDisplayObject* retValue;
if( (retValue = (*it)->GetObjectWithWnd( wnd )) != NULL )
{
return retValue;
}
}
}
return NULL;
}
CString CDisplayObject::GetCompleteName() const
{
CString name = GetName();
const CDisplayObject* rootParent = m_Parent;
const CDisplayObject* currentObject = this;
while(rootParent)
{
if( rootParent->GetType() == COLUMN )
{
name = rootParent->GetName() + CString("|") + name;
}
else if( rootParent->GetType() == ROW )
{
name = rootParent->GetName() + CString("-") + name;
}
else
{
name = rootParent->GetName() + CString(" ERROR! ") + name;
}
const CDisplayObject* tmp = rootParent;
currentObject = rootParent;
rootParent = tmp->m_Parent;
}
return name;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.