前言
目前很多应用程序界面的标题栏都是自绘的,wxWidgets如果想要达到360安全卫士那种效果就需要重新定义一个Frame。
自绘Frame窗口
首先定义一个自定类,这个类继承wxFrame,在里面重写wxFrame:
头文件如下:
#ifndef WXSKINXFRAME_H
#define WXSKINXFRAME_H
#include <wx/wx.h>
#include <wx/image.h>
class wxSkinXFrame : public wxFrame
{
//构造、析构、初始化
public:
wxSkinXFrame();
wxSkinXFrame(wxWindow* parent,
wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxNO_BORDER|wxFRAME_SHAPED, //自绘一般默认为无边框
const wxString& name = wxFrameNameStr);
void Create(wxWindow *parent,
wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxNO_BORDER|wxFRAME_SHAPED,//自绘一般默认为无边框
const wxString& name = wxFrameNameStr);
void Init();
~wxSkinXFrame();
public:
//设置皮肤
//
void SetShapeImage(const wxImage& skin){image_shape = skin;ReSetShape();}
void SetNormalImage(const wxImage& skin){image_normal = skin;Refresh();}
void SetOverImage(const wxImage& skin){image_over = skin;Refresh();};
void SetDisableImage(const wxImage& skin){image_disable = skin;Refresh();};
public:
//设置窗口形状
bool SetShape(const wxImage& img);
protected:
void OnErase(wxEraseEvent& e){};//擦除函数
void OnPaint(wxPaintEvent& e);//绘制函数
void OnSize(wxSizeEvent& e);//当窗口大小发生改变的时候
void OnEnterWindow(wxMouseEvent& event);//鼠标进入窗口的时候
void OnLeaveWindow(wxMouseEvent& event);//鼠标离开窗口的时候
protected:
void ReSetShape();
protected:
//关于移动窗口的函数
void OnMotion(wxMouseEvent& event);
void OnLeftDown(wxMouseEvent& event);
void OnLeftUp(wxMouseEvent& event);
void OnMouseLost(wxMouseCaptureLostEvent& event);
private:
wxImage image_shape; //窗口形状
wxImage image_normal; // 窗口皮肤
wxImage image_over;//鼠标在窗口上面的皮肤
wxImage image_disable;//窗口被禁用后的皮肤
bool m_bInside;//鼠标是否在窗口里面
wxPoint m_offset;//偏移坐标
wxRect m_rect;
private:
//将类加入到RTTI中
DECLARE_DYNAMIC_CLASS(wxSkinXFrame)
//定义事件表
DECLARE_EVENT_TABLE()
};
#endif // WXSKINXFRAME_H
实现文件如下:
#include "wxSkinXFrame.h"
#include <wx/dcbuffer.h>
IMPLEMENT_DYNAMIC_CLASS(wxSkinXFrame,wxFrame)
BEGIN_EVENT_TABLE(wxSkinXFrame,wxFrame)
//关联擦除背景函数
EVT_ERASE_BACKGROUND(wxSkinXFrame::OnErase)
//关联绘制函数
EVT_PAINT(wxSkinXFrame::OnPaint)
//关联窗口大小发生改变函数
EVT_SIZE(wxSkinXFrame::OnSize)
//关联鼠标进入窗口函数
EVT_ENTER_WINDOW(wxSkinXFrame::OnEnterWindow)
//关联鼠标离开窗口函数
EVT_LEAVE_WINDOW(wxSkinXFrame::OnLeaveWindow)
//鼠标移动事件
EVT_LEFT_DOWN(wxSkinXFrame::OnLeftDown)
EVT_LEFT_UP(wxSkinXFrame::OnLeftUp)
EVT_MOUSE_CAPTURE_LOST(wxSkinXFrame::OnMouseLost)
EVT_MOTION(wxSkinXFrame::OnMotion)
END_EVENT_TABLE()
wxSkinXFrame::wxSkinXFrame()
{
Init();
}
wxSkinXFrame::wxSkinXFrame(wxWindow* parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
Init();
Create(parent,id,pos,size,style,name);
}
void wxSkinXFrame::Create(wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
wxFrame::Create(parent,id,"",pos,size,style,name);
}
void wxSkinXFrame::Init()
{
m_bInside = false;
m_rect = wxRect(0,0,0,0);
m_offset = wxPoint(-1,-1);
}
wxSkinXFrame::~wxSkinXFrame()
{
}
bool wxSkinXFrame::SetShape(const wxImage& img)
{
wxImage m = img;
if(m.HasAlpha())
{
m.ConvertAlphaToMask();
}
return wxNonOwnedWindow::SetShape(wxRegion(m));
}
void wxSkinXFrame::OnPaint(wxPaintEvent& e)
{
//如果窗体长宽有一个为0,则不进行绘制
int w, h;
GetSize(&w,&h);
if(w == 0 || h == 0)
{
return;
}
//使用双缓冲绘图,防止闪烁
wxBufferedPaintDC dcp(this);
//清除背景
dcp.SetBrush(*wxTRANSPARENT_BRUSH);
dcp.Clear();
//在窗口上绘制图片,默认都是按照窗体大小进行等比例绘制
if(m_bInside && image_over.IsOk())
{
//当鼠标在窗口里面的时候
dcp.DrawBitmap(wxBitmap(image_over.Scale(w, h)),0,0,true);
}
else if(IsEnabled() && image_normal.IsOk())
{
//当窗口不属于禁用状态并且有正常的皮肤图片时
dcp.DrawBitmap(wxBitmap(image_normal.Scale(w, h)),0,0,true);
}
else if(!IsEnabled() && image_disable.IsOk() )
{
//当窗口处于禁用状态时
dcp.DrawBitmap(wxBitmap(image_disable.Scale(w, h)),0,0,true);
}else if(image_normal.IsOk())
{
//当窗口不属于上面几种条件时,默认绘制正常的皮肤图片
dcp.DrawBitmap(wxBitmap(image_normal.Scale(w, h)),0,0,true);
}
}
void wxSkinXFrame::OnSize(wxSizeEvent& e)
{
ReSetShape();
}
void wxSkinXFrame::OnEnterWindow(wxMouseEvent& event)
{
m_bInside = true;
if(image_over.IsOk())
{
Refresh();
}
}
void wxSkinXFrame::OnLeaveWindow(wxMouseEvent& event)
{
m_bInside = false;
Refresh();
//鼠标拖动事件,当鼠标离开窗口时,发送信号告诉窗口
//这个是为了解决拖动速度过快导致窗口没有跟踪上鼠标
bool isDown = event.LeftIsDown();
bool isDragging = event.Dragging();
if(isDown && isDragging)
{
wxCommandEvent ev(wxEVT_MOTION, GetId());
ev.SetEventObject(this);
GetEventHandler()->ProcessEvent(ev);
}
}
//鼠标移动的时候
void wxSkinXFrame::OnMotion(wxMouseEvent& event)
{
wxPoint pt = event.GetPosition();
bool isDown = event.LeftIsDown();
if (isDown && event.Dragging() && HasCapture())
{
wxPoint mouse_pos = ClientToScreen(pt);
if(m_offset.x != -1)
{
wxPoint dp = mouse_pos - m_offset;
this->Move(dp);
}
}
}
//鼠标左键按下的时候
void wxSkinXFrame::OnLeftDown(wxMouseEvent& event)
{
// 当前鼠标的屏幕坐标
wxPoint pt = event.GetPosition();
if(!HasCapture() && !m_rect.Contains(pt))
{
CaptureMouse();
}
wxPoint mouse_pos = ClientToScreen(pt);
// 当前主窗口的屏幕坐标
wxPoint wnd_pos = this->GetPosition();
// 计算鼠标的坐标点到窗口左上角的偏移量
m_offset.x = mouse_pos.x - wnd_pos.x;
m_offset.y = mouse_pos.y - wnd_pos.y;
}
//鼠标左键松开的时候
void wxSkinXFrame::OnLeftUp(wxMouseEvent& event)
{
m_offset = wxPoint(-1,-1);
if (HasCapture()) ReleaseMouse();
}
//鼠标丢失的时候
void wxSkinXFrame::OnMouseLost(wxMouseCaptureLostEvent& event)
{
m_offset = wxPoint(-1,-1);
if (HasCapture()) ReleaseMouse();
}
void wxSkinXFrame::ReSetShape()
{
if(image_shape.IsOk())
{
//当窗口大小改变是可以改变窗口的形状
int w, h;
GetSize(&w,&h);
if(w != 0 && h != 0)
{
SetShape(image_shape.Scale(w,h));
}
}
Layout();
Refresh();
}
这个窗口并没有绘制最小化按钮和最大化按钮以及关闭按钮,简单的显示一个皮肤界面,下面是调用的示例:
wxInitAllImageHandlers();
wxSkinXFrame* f = new wxSkinXFrame(NULL,wxID_ANY);
//设置窗口皮肤
f->SetNormalImage(wxImage("normal.jpg"));
f->SetShapeImage(wxImage("shape.png"));
f->SetOverImage(wxImage("over.jpg"));
f->SetDisableImage(wxImage("disable.jpg"));
f->SetSize(800,600);
f->Center();
f->Show();
接下来用动态图演示效果:
总结
自绘窗口有两个关键点,一个是在客户区绘制皮肤,另一个是处理鼠标拖拽事件。其余标题栏的控件可以根据项目需求来排列组合。