0

我想用 MFC 在 C++ 中做一个布局管理器。此布局将包含一个窗口矩阵。布局初始形式将由矩阵选择器定义(如在 MS Word 中)。之后,用户可以通过拉动它的边缘来调整每个窗口的大小。我正在考虑使用 2d 碰撞检测算法,但我不知道这将如何在实时调整大小时执行(或如何使用 mfc 进行)

如果违反此限制,我想对每个元素施加最小尺寸并阻止元素的收缩(当另一个元素正在扩展时)。等待任何建议

4

1 回答 1

0

我不得不在 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;
}
于 2012-10-02T17:30:16.450 回答