[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