wxWidgets 自绘窗口

前言

​ 目前很多应用程序界面的标题栏都是自绘的,wxWidgets如果想要达到360安全卫士那种效果就需要重新定义一个Frame。

自绘Frame窗口

​ 首先定义一个自定类,这个类继承wxFrame,在里面重写wxFrame:

头文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#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

实现文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#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();
}

这个窗口并没有绘制最小化按钮和最大化按钮以及关闭按钮,简单的显示一个皮肤界面,下面是调用的示例:

1
2
3
4
5
6
7
8
9
10
11
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();

接下来用动态图演示效果:

总结

​ 自绘窗口有两个关键点,一个是在客户区绘制皮肤,另一个是处理鼠标拖拽事件。其余标题栏的控件可以根据项目需求来排列组合。

评论