[wx-dev] Borderless frames with custom resize/more operations:

Brian Vanderburg II BrianVanderburg2 at aim.com
Wed Apr 2 07:04:46 PDT 2008


I don't know if this would be useful as part of the toolkit or not, but =

I've got some code working, so far on Windows against wx 2.8.5.  Haven't =

had a chance to test any other platforms.

This code creates a new type of frame, wxBorderlessFrame.  It still =

honors some of the frame flags, and implements another.

It has built in moving and resizing code, so the entire frame can be =

drawn with no problem.  There is no 'non-client' area.  I created this =

because when trying to create a borderless or static/simple border frame =

with wxRESIZE_BORDER on MSW, it always made it a 3D frame, which was =

undesired.

Several things about this code:

Both event handlers and DoSetSize are provided, so the real size will be =

updated by events and by programatic size changes when it is not collapsed.

When creating a frame WITHOUT wxBORDERLESS_FRAME_AUTOCOLLAPSE, it is =

relatively simple.  The frame is created about like normal and it can be =

moved and resized (if wxRESIZE_BORDER) is passed, but still has a flat =

look allowing custom drawing of 'borders' background, etc.  It's size is =

adjusted by the user or program.  It does not automatically 'collapse' =

when losing activation.

If the frame is created with wxBORDERLESS_FRAME_AUTOCOLLAPSE, it is a =

little more work.  After creating the frame, the 'real' size should be =

set with SetRealSize, and the state should be set with Collapse or =

Uncollapse.  The real size will be the initial frame size otherwise. =

But, (at least with tests on MSW), if the frame is also created with =

wxFRAME_TOOL_WINDOW, it will not receive focus (become activated), but =

not be deactivated either, so it will sit there with the size it has.  =

IsCollapsed will return false as if it is active, but clicking out of it =

will not cause it to collapse since it is not active so won't get a =

wxEVT_ACTIVATE event.  This isn't a real problem it still has the =

initial size, and the user can click on it and out of it to collapse =

it.  Program code can restore the state by directly calling Collapse or =

Uncollapse

The source file contains a small test showing it work.


Brian Vanderburg II
-------------- next part --------------
// File:    borderlesswnd.cpp
// Author:  Brian Vanderburg II
// Purpose: create borderless windows with moving and resizing operations
//------------------------------------------------------------------------


// Includes
#include <wx/defs.h>
#include <wx/frame.h>
#include <wx/display.h>
#include <wx/cursor.h>
#include <wx/utils.h>

#include "borderlesswnd.h"

// Constructors
wxBorderlessFrame::wxBorderlessFrame()
{
    Init();
}

wxBorderlessFrame::wxBorderlessFrame(wxWindow* parent,
                                     wxWindowID id,
                                     const wxString& title,
                                     const wxPoint& pos,
                                     const wxSize& size,
                                     long style,
                                     const wxString& name)
{
    Init();
    Create(parent, id, title, pos, size, style, name);
}

// Destructor
wxBorderlessFrame::~wxBorderlessFrame()
{
    wxDELETE(m_cursorNS);
    wxDELETE(m_cursorWE);
    wxDELETE(m_cursorNESW);
    wxDELETE(m_cursorNWSE);
}

// Creation
bool wxBorderlessFrame::Create(wxWindow* parent,
                               wxWindowID id,
                               const wxString& title,
                               const wxPoint& pos,
                               const wxSize& size,
                               long style,
                               const wxString& name)
{
    // Style fix
    m_ourStyle =3D style;

    // Remove borders
    style &=3D ~wxBORDER_MASK;
    style |=3D wxBORDER_NONE;


    // Remove caption components
    style &=3D ~(wxCAPTION | wxSYSTEM_MENU | wxMINIMIZE_BOX | wxMAXIMIZE_BO=
X | wxCLOSE_BOX);

    // We do our own resizing
    style &=3D ~wxRESIZE_BORDER; =


    // Create the frame
    if(wxFrame::Create(parent, id, title, pos, size, style, name) =3D=3D fa=
lse)
    {
        return false;
    }

    // Create cursors for the items here
    m_cursorNS =3D new wxCursor(wxCURSOR_SIZENS);
    m_cursorWE =3D new wxCursor(wxCURSOR_SIZEWE);
    m_cursorNESW =3D new wxCursor(wxCURSOR_SIZENESW);
    m_cursorNWSE =3D new wxCursor(wxCURSOR_SIZENWSE);
}

// Set the real position
void wxBorderlessFrame::SetRealPosition(const wxRect& position)
{
    m_realPosition =3D position;
    if(m_collapsed =3D=3D false)
    {
        SetSize(m_realPosition.x, m_realPosition.y,
                m_realPosition.width, m_realPosition.height);
    }
}


void wxBorderlessFrame::Collapse()
{
    // Set this FIRST so calls to DoSetSize will not save 'real' position
    m_collapsed =3D true;

    // Get display and set up new position/size
    wxDisplay display(wxDisplay::GetFromWindow(this));
    const wxRect displayRect =3D display.GetClientArea();
    =

    wxRect newPos;
    wxSize minSize =3D GetMinSize();
    newPos.width =3D minSize.x;
    newPos.height =3D minSize.y;

    // Adjust x
    if(m_realPosition.x + m_realPosition.width / 2 < displayRect.x + displa=
yRect.width / 2)
    {
        newPos.x =3D m_realPosition.x;
    }
    else
    {
        newPos.x =3D m_realPosition.x + (m_realPosition.width - newPos.widt=
h);
    }

    // Adjust y
    if(m_realPosition.y + m_realPosition.height / 2 < displayRect.y + displ=
ayRect.height / 2)
    {
        newPos.y =3D m_realPosition.y;
    }
    else
    {
        newPos.y =3D m_realPosition.y + (m_realPosition.height - newPos.hei=
ght);
    }

    // Ensure on screen
    if(newPos.x > displayRect.x + displayRect.width - newPos.width)
    {
        newPos.x =3D displayRect.x + displayRect.width - newPos.width;
    }
    /* NOT else */
    if(newPos.x < displayRect.x)
    {
        newPos.x =3D displayRect.x;
    }

    if(newPos.y > displayRect.y + displayRect.height - newPos.height)
    {
        newPos.y =3D displayRect.y + displayRect.height - newPos.height;
    }
    /* NOT else */
    if(newPos.y < displayRect.y)
    {
        newPos.y =3D displayRect.y;
    }

    SetSize(newPos.x, newPos.y,
            newPos.width, newPos.height);
}

void wxBorderlessFrame::Uncollapse()
{
    wxDisplay display(wxDisplay::GetFromWindow(this));
    const wxRect displayRect =3D display.GetClientArea();

    if(m_realPosition.x > displayRect.x + displayRect.width - m_realPositio=
n.width)
    {
        m_realPosition.x =3D displayRect.x + displayRect.width - m_realPosi=
tion.width;
    }
    /* NOT else */
    if(m_realPosition.x < displayRect.x)
    {
        m_realPosition.x =3D displayRect.x;
    }

    if(m_realPosition.y > displayRect.y + displayRect.height - m_realPositi=
on.height)
    {
        m_realPosition.y =3D displayRect.y + displayRect.height - m_realPos=
ition.height;
    }
    /* NOT else */
    if(m_realPosition.y < displayRect.y)
    {
        m_realPosition.y =3D displayRect.y;
    }

    // Allow DoSetSize to change real position
    m_collapsed =3D false;
    =

    SetSize(m_realPosition.x, m_realPosition.y,
            m_realPosition.width, m_realPosition.height);
}

// event handlers
void wxBorderlessFrame::OnActivate(wxActivateEvent& evt)
{
    if(m_ourStyle & wxBORDERLESS_FRAME_AUTOCOLLAPSE)
    {
        if(evt.GetActive())
        {
            Uncollapse();
        }
        else
        {
            Collapse();
        }
    }
}

void wxBorderlessFrame::OnLeaveWindow(wxMouseEvent& WXUNUSED(evt))
{
    SetCursor(wxNullCursor);
}

void wxBorderlessFrame::OnLeftDown(wxMouseEvent& WXUNUSED(evt))
{
    m_oldMousePos =3D ::wxGetMousePosition();
    CaptureMouse();
}

void wxBorderlessFrame::OnLeftUp(wxMouseEvent& WXUNUSED(evt))
{
    if(HasCapture())
    {
        ReleaseMouse();
    }
}

void wxBorderlessFrame::OnMotion(wxMouseEvent& WXUNUSED(evt))
{
    wxPoint mousePos =3D ::wxGetMousePosition();

    if(HasCapture())
    {
        // Operating mode?
        if(m_mode =3D=3D -1)
            m_mode =3D 4; // Moving

        int deltax, deltay;

        deltax =3D (mousePos.x - m_oldMousePos.x);
        deltay =3D (mousePos.y - m_oldMousePos.y);

        if(deltax =3D=3D 0 && deltay =3D=3D 0)
        {
            return;
        }

        // Some variables
        wxRect winPos =3D GetScreenRect();

        // Resize if needed
        if((m_ourStyle & wxRESIZE_BORDER) && m_collapsed =3D=3D false)
        {
            const wxRect curPos =3D winPos;
            const wxSize minSize =3D GetMinSize();

            // Adjust x/w
            if(m_mode =3D=3D 0 || m_mode =3D=3D 3 || m_mode =3D=3D 6)
            {
                winPos.x +=3D deltax;
                winPos.width -=3D deltax;
                if(winPos.width < minSize.x)
                {
                    winPos.x =3D curPos.x;
                    winPos.width =3D curPos.width;
                }
                else
                {
                    m_oldMousePos.x =3D mousePos.x;
                }
            }
            else if(m_mode =3D=3D 2 || m_mode =3D=3D 5 || m_mode =3D=3D 8)
            {
                winPos.width +=3D deltax;
                if(winPos.width < minSize.x)
                {
                    winPos.width =3D curPos.width;
                }
                else
                {
                    m_oldMousePos.x =3D mousePos.x;
                }
            }

            // Adjust y/h
            if(m_mode =3D=3D 0 || m_mode =3D=3D 1 || m_mode =3D=3D 2)
            {
                winPos.y +=3D deltay;
                winPos.height -=3D deltay;
                if(winPos.height < minSize.y)
                {
                    winPos.y =3D curPos.y;
                    winPos.height =3D curPos.height;
                }
                else
                {
                    m_oldMousePos.y =3D mousePos.y;
                }
            }
            else if(m_mode =3D=3D 6 || m_mode =3D=3D 7 || m_mode =3D=3D 8)
            {
                winPos.height +=3D deltay;
                if(winPos.height < minSize.y)
                {
                    winPos.height =3D curPos.height;
                }
                else
                {
                    m_oldMousePos.y =3D mousePos.y;
                }
            }
        } // wxRESIZE_BORDER

        // Move
        if(m_mode =3D=3D 4)
        {
            winPos.x +=3D deltax;
            winPos.y +=3D deltay;
            m_oldMousePos =3D mousePos;
        }

        // Set the size
        // The real position will get correctly set
        SetSize(winPos.x, winPos.y,
                winPos.width, winPos.height);
    }
    else
    {
        mousePos =3D ScreenToClient(mousePos);
        wxSize size =3D GetSize();

        // What is the mode
        int part1 =3D 1;
        int part2 =3D 1;

        if((m_ourStyle & wxRESIZE_BORDER) && m_collapsed =3D=3D false)
        {
            if(mousePos.x < 5)
            {
                part1 =3D 0;
            }
            else if(mousePos.x > size.x - 5)
            {
                part1 =3D 2;
            }

            if(mousePos.y < 5)
            {
                part2 =3D 0;
            }
            else if(mousePos.y > size.y - 5)
            {
                part2 =3D 2;
            }
        }

        m_mode =3D part1 + (3 * part2);

        // Mouse cursor
        switch(m_mode)
        {
            case 0:
            case 8:
                SetCursor(*m_cursorNWSE);
                break;

            case 1:
            case 7:
                SetCursor(*m_cursorNS);
                break;

            case 2:
            case 6:
                SetCursor(*m_cursorNESW);
                break;

            case 3:
            case 5:
                SetCursor(*m_cursorWE);
                break;

            case 4:
            default:
                SetCursor(wxNullCursor);
                break;
        };
    }
}

void wxBorderlessFrame::OnCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(evt=
))
{
    m_mode =3D -1;
}

void wxBorderlessFrame::OnMove(wxMoveEvent& evt)
{
    if(m_collapsed =3D=3D false)
    {
        wxPoint p =3D evt.GetPosition();
        m_realPosition.x =3D p.x;
        m_realPosition.y =3D p.y;
    }
    evt.Skip();
}

void wxBorderlessFrame::OnSize(wxSizeEvent& evt)
{
    if(m_collapsed =3D=3D false)
    {
        wxSize s =3D evt.GetSize();
        m_realPosition.width =3D s.x;
        m_realPosition.height =3D s.y;
    }
    evt.Skip();    =

}

void wxBorderlessFrame::DoSetSize(int x, int y,
                                  int width, int height,
                                  int sizeFlags)
{
    // Pass it on
    wxFrame::DoSetSize(x, y, width, height, sizeFlags);

    // If not collapsed, then record as the real size
    if(m_collapsed =3D=3D false)
    {
        m_realPosition =3D GetScreenRect(); // GetRect ok for TLWs?
    }
}

void wxBorderlessFrame::DoSetClientSize(int width, int height)
{
    // Pass it on
    wxFrame::DoSetClientSize(width, height);

    // If not collapsed, then record as the real size
    if(m_collapsed =3D=3D false)
    {
        m_realPosition =3D GetScreenRect(); // GetRect ok for TLWs?
    }
}

void wxBorderlessFrame::Init()
{
    m_cursorNS =3D NULL;
    m_cursorWE =3D NULL;
    m_cursorNESW =3D NULL;
    m_cursorNWSE =3D NULL;

    // An arbitrary real position until set or discovered
    m_realPosition =3D wxRect(100, 100, 400, 300);
    m_collapsed =3D false;

    m_mode =3D -1;
    m_ourStyle =3D 0;
}

BEGIN_EVENT_TABLE(wxBorderlessFrame, wxFrame)
    EVT_ACTIVATE(wxBorderlessFrame::OnActivate)
    EVT_LEAVE_WINDOW(wxBorderlessFrame::OnLeaveWindow)
    EVT_LEFT_DOWN(wxBorderlessFrame::OnLeftDown)
    EVT_LEFT_UP(wxBorderlessFrame::OnLeftUp)
    EVT_MOTION(wxBorderlessFrame::OnMotion)
    EVT_MOUSE_CAPTURE_LOST(wxBorderlessFrame::OnCaptureLost)
    EVT_MOVE(wxBorderlessFrame::OnMove)
    EVT_SIZE(wxBorderlessFrame::OnSize)
END_EVENT_TABLE()

// A test application
#include <wx/app.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/sizer.h>
#include <wx/dcclient.h>

class MyFrame : public wxBorderlessFrame
{
public:
    MyFrame() :
        wxBorderlessFrame(NULL, -1, wxT("A Title"), wxDefaultPosition, wxDe=
faultSize,
                          wxRESIZE_BORDER | wxBORDERLESS_FRAME_AUTOCOLLAPSE=
 |
                          wxFRAME_TOOL_WINDOW | wxFULL_REPAINT_ON_RESIZE)
             =

    {
        m_edit =3D new wxTextCtrl(this, -1, wxT(""), wxDefaultPosition, wxD=
efaultSize,
            wxTE_MULTILINE | wxWANTS_CHARS | wxTE_NO_VSCROLL | wxBORDER_NON=
E);
        m_edit->SetBackgroundColour(wxColour(0, 0, 128));
        m_edit->SetForegroundColour(wxColour(255, 255, 0));

        wxSizer* sizer =3D new wxBoxSizer(wxVERTICAL);
        sizer->Add(200, 24, 0);
        sizer->Add(m_edit, 1, wxEXPAND | wxALL, 5);



        SetSizer(sizer);
        Layout();
    };

    void OnPaint(wxPaintEvent& evt)
    {
        wxPaintDC dc(this);

        dc.SetBrush(wxColour(0, 0, 64));
        dc.SetPen(wxColour(255, 127, 0));

        wxSize s =3D GetClientSize();
        dc.DrawRectangle(0, 0, s.x, s.y);

        wxRect p =3D m_edit->GetRect();
        dc.DrawRectangle(p.x - 1, p.y - 1, p.width + 2, p.height + 2);

        dc.SetBrush(wxNullBrush);
        dc.SetPen(wxNullPen);
    }

    void OnErase(wxEraseEvent& evt)
    {
    }
private:
    wxTextCtrl* m_edit;

DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(MyFrame, wxBorderlessFrame)
    EVT_PAINT(MyFrame::OnPaint)
    EVT_ERASE_BACKGROUND(MyFrame::OnErase)
END_EVENT_TABLE()

class MyApp : public wxApp
{
public:
    bool OnInit()
    {
        wxBorderlessFrame* frame;
        frame =3D new MyFrame();
        frame->SetMinSize(wxSize(200, 45));

        SetTopWindow(frame);
        frame->Show();
        //frame->Raise();
        return true;
    }
};

IMPLEMENT_APP(MyApp)
-------------- next part --------------
// File:    borderlesswnd.h
// Author:  Brian Vanderburg II
// Purpose: create borderless windows with moving and resizing operations
//------------------------------------------------------------------------


#ifndef __BORDERLESS_WND_H__
#define __BORDERLESS_WND_H__

// Includes
#include <wx/defs.h>
#include <wx/frame.h>

// Foward declarations
class WXDLLEXPORT wxWindow;
class WXDLLEXPORT wxCursor;



// Since wxCAPTION is ignored by this class, it is reused for the this
#define wxBORDERLESS_FRAME_AUTOCOLLAPSE wxCAPTION

// A borderless frame with moving/resizing function support
// NOTE: Because of the way this code works, a derived frame should
// not handle any mouse events.  If custom mouse events are =

// needed then they should be done in a wxPanel child window
class wxBorderlessFrame : public wxFrame
{
public:
    // reckognized style:
    // wxRESIZE_BORDER - Resizing is allowed
    // wxBORDERLESS_FRAME_AUTOCOLLAPSE - Auto-collapse when activation is l=
ost (to minimum size)
    // reset styles (ignored)
    // BORDERS
    // Caption/system menu/minimize/maximize/close box

    // ctors
    wxBorderlessFrame(); =

    wxBorderlessFrame(wxWindow* parent,
                      wxWindowID id,
                      const wxString& title,
                      const wxPoint& pos =3D wxDefaultPosition,
                      const wxSize& size =3D wxDefaultSize,
                      long style =3D 0,
                      const wxString& name =3D wxFrameNameStr);

    // dtor
    ~wxBorderlessFrame();

    // 2 step creation
    bool Create(wxWindow* parent,
                wxWindowID id,
                const wxString& title,
                const wxPoint& pos =3D wxDefaultPosition,
                const wxSize& size =3D wxDefaultSize,
                long style =3D 0,
                const wxString& name =3D wxFrameNameStr);

    // Set the real position and size of the item
    // This will allow setting the 'real' position of the item
    // when it is collapsed, otherwise SetSize will set the real position w=
hen it is uncollapsed
    void SetRealPosition(const wxRect& position);
    void SetRealPosition(int x, int y, int w, int h)
    {
        SetRealPosition(wxRect(x, y, w, h));
    }

    const wxRect& GetRealPosition() const { return m_realPosition; }

    void Collapse();
    void Uncollapse(); // Restore is taken by window
    bool IsCollapsed() const { return m_collapsed; }

    // event handlers
    void OnActivate(wxActivateEvent& evt);
    void OnLeaveWindow(wxMouseEvent& evt);
    void OnLeftDown(wxMouseEvent& evt);
    void OnLeftUp(wxMouseEvent& evt);
    void OnMotion(wxMouseEvent& evt);
    void OnCaptureLost(wxMouseCaptureLostEvent& evt);
    void OnMove(wxMoveEvent& evt);
    void OnSize(wxSizeEvent& evt);

protected:
    void DoSetSize(int x, int y,
                   int width, int height,
                   int sizeFlags =3D wxSIZE_AUTO);
    void DoSetClientSize(int width, int height);

private:
    // init variables
    void Init();

    wxCursor* m_cursorNS;
    wxCursor* m_cursorWE;
    wxCursor* m_cursorNESW;
    wxCursor* m_cursorNWSE;

    wxRect m_realPosition;
    bool m_collapsed;

    int m_mode;
    long m_ourStyle;

    wxPoint m_oldMousePos;

DECLARE_NO_COPY_CLASS(wxBorderlessFrame)
DECLARE_EVENT_TABLE()
};

#endif // Include once header


More information about the wx-dev mailing list