简体   繁体   中英

2d collision detection with mfc child windows

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM