我想用 MFC 在 C++ 中做一个布局管理器。此布局将包含一个窗口矩阵。布局初始形式将由矩阵选择器定义(如在 MS Word 中)。之后,用户可以通过拉动它的边缘来调整每个窗口的大小。我正在考虑使用 2d 碰撞检测算法,但我不知道这将如何在实时调整大小时执行(或如何使用 mfc 进行)
如果违反此限制,我想对每个元素施加最小尺寸并阻止元素的收缩(当另一个元素正在扩展时)。等待任何建议
我不得不在 C++ 和 MFC 中做一个类似的布局管理器。我实现了一个包含矩形的 N 叉树(称为 CDisplayObject),树中的每个叶子都可以有一个 CFrame。为了构建这样的树,需要执行简单的文本解析。每个矩形都介于 0 和 1 之间,并且每个矩形都有大小总和为 1 的子级。
我不调整窗口的大小,但它可能很容易实现,因为您只需调整 CDisplayObject 的大小,然后再调整窗口的大小。
这是处理根据“显示”矩形调整自身大小的显示对象的代码,在我们的程序中是主视图。
要创建一个“显示树”,左侧有一个大矩形,右侧有三个小矩形:
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);
“|” 表示它是一列,“-”表示这是一行。
使用 SwitchFrameWnd 将叶子设置为包含子窗口,并使用 SetPos(CRect) 调整树中所有显示对象的大小。您可以通过实现更改整个树的行/列/叶权重的函数来重新调整窗口大小。
#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实现
#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;
}