100分求指导无标题栏透明窗口(桌面歌词)的移动和缩放实现

summer_insects 2013-04-08 05:12:48
是这样的,楼主写了一个简单的播放器,也大概实现了桌面歌词。想实现移动和缩放桌面歌词。

考虑到如果直接在歌词窗口上画背景,那样子CPU占用率略高,并且一旦在歌词窗口上设置WS_THICKFRAME风格,那么用定时器更新歌词时也会有问题。于是就了用另一个带WS_THICKFRAME风格的半透明辅助窗口,一举两得。

现在,移动基本上没问题,就是缩放的时候,问题就来了:

当点击在边框上时,如果原来播放器窗口处于有焦点状态,辅助窗口会闪一下,然后可以正常缩放。但如果播放器窗口是无焦点状态,那么辅助窗口直接就隐藏了,自然也就无法缩放了

应该如何解决当点击在边框上时,闪一下,或直接隐藏的问题?

下面的代码是简化出来的。求高手指导。

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

static LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK LyricsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ToolWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

static HWND gs_hwndLyrics = NULL; // 歌词窗口
static HWND gs_hwndTools = NULL; // 辅助窗口
static HFONT gs_hFont = NULL;

//#define USE_WIN32_CONSOLE

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpszCmdLine, int iCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASS wndcls;

#ifdef USE_WIN32_CONSOLE
AllocConsole();
_tfreopen(TEXT("CONOUT$"), TEXT("w"), stdout);
#endif

// 这是主窗口,相当于播放器窗口
wndcls.style = CS_HREDRAW | CS_VREDRAW;
wndcls.lpfnWndProc = &MainWndProc;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hInstance = hInstance;
wndcls.hIcon = NULL;
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndcls.hbrBackground = NULL;
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = TEXT("TestWindow");

if (!RegisterClass(&wndcls))
{
return 0;
}

hwnd = CreateWindowEx(0, wndcls.lpszClassName, TEXT("TestWindow"),
WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX,
0, 0, 200, 200,
NULL, NULL, hInstance, NULL);

ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);

while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

#ifdef USE_WIN32_CONSOLE
fclose(stdout);
FreeConsole();
#endif

return (int)msg.wParam;
}

// 主窗口回调函数
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
RECT screenRect;
int x, y, width = 900, height = 150;
WNDCLASS wndcls;

gs_hFont = CreateFont(64, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
GB2312_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, FF_MODERN, TEXT("黑体"));

// 歌词窗口跟辅助窗口共用一个类
wndcls.style = CS_HREDRAW | CS_VREDRAW;
wndcls.lpfnWndProc = &DefWindowProc; // 先用一下默认回调函数
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
wndcls.hIcon = NULL;
wndcls.hCursor = LoadCursor(NULL, IDC_SIZEALL);
wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // 黑色背景
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = TEXT("LyricsWindow");

RegisterClass(&wndcls);

// 计算坐标,将两窗口放在桌面底部
SystemParametersInfo(SPI_GETWORKAREA, 0, &screenRect, 0);
x = (screenRect.right - screenRect.left - width) >> 1;
y = screenRect.bottom - screenRect.top - height;

// 创建歌词窗口
gs_hwndLyrics = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
wndcls.lpszClassName, NULL,
WS_POPUP | WS_CLIPSIBLINGS,
x, y, width, height, NULL, NULL, wndcls.hInstance, NULL);

// 设置新回调函数
SetWindowLong(gs_hwndLyrics, GWL_WNDPROC, (LONG)&LyricsWndProc);
// 显示歌词窗口,以得到一条WM_PAINT消息
ShowWindow(gs_hwndLyrics, SW_SHOWNORMAL);

// 设置上WS_EX_LAYERED扩展风格,如果创建时就已设置,那么就收不到WM_PAINT消息了
SetWindowLong(gs_hwndLyrics, GWL_EXSTYLE, (LONG)
(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW));

// 设置黑色为透明色
SetLayeredWindowAttributes(gs_hwndLyrics, RGB(0, 0, 0), 0, LWA_COLORKEY);

// 创建辅助窗口
gs_hwndTools = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW,
wndcls.lpszClassName, NULL,
WS_POPUP | WS_CLIPSIBLINGS | WS_THICKFRAME,
x, y, width, height, NULL, NULL, wndcls.hInstance, NULL);
// 设置新回调函数
SetWindowLong(gs_hwndTools, GWL_WNDPROC, (LONG)&ToolWndProc);
// 设置透明度
SetLayeredWindowAttributes(gs_hwndTools, 0, 64, LWA_ALPHA);
}
return 0;

case WM_PAINT:
{
static TCHAR str[] = TEXT("这是主窗口");
HDC hdc;
PAINTSTRUCT paintStruct;

hdc = BeginPaint(hwnd, &paintStruct);
SetTextColor(hdc, RGB(0xFF, 0, 0));
SetBkMode(hdc, TRANSPARENT);
TextOut(hdc, 0, 0, str, _tcslen(str));
EndPaint(hwnd, &paintStruct);
}
return 0;

case WM_DESTROY:
DeleteObject(gs_hFont);
DestroyWindow(gs_hwndLyrics);
DestroyWindow(gs_hwndTools);
PostQuitMessage(0);
return 0;

default:
break;
}

return DefWindowProc(hwnd, message, wParam, lParam);
}

// 歌词窗口回调函数
LRESULT CALLBACK LyricsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static TCHAR str[] = TEXT("这是歌词窗口");

switch (message)
{
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT paintStruct;
hdc = BeginPaint(hwnd, &paintStruct);
SetTextColor(hdc, RGB(0, 0, 0xFF));
SetBkMode(hdc, TRANSPARENT);
SelectObject(hdc, gs_hFont);
TextOut(hdc, 160, 20, str, _tcslen(str));
EndPaint(hwnd, &paintStruct);
}
return 0;

case WM_MOUSEMOVE: // 检测到鼠标移动,就将辅助窗口显示出来
ShowWindow(gs_hwndTools, SW_SHOWNORMAL);
return 0;

case WM_SIZE:
case WM_MOVE: // 手动重绘
{
HDC hdc = GetDC(hwnd);
SetTextColor(hdc, RGB(0, 0, 0xFF));
SetBkMode(hdc, TRANSPARENT);
SelectObject(hdc, gs_hFont);
TextOut(hdc, 160, 20, str, _tcslen(str));
ReleaseDC(hwnd, hdc);
}
return 0;

case WM_CLOSE:
ShowWindow(hwnd, SW_HIDE);
return 0;

case WM_DESTROY:
return 0;

default:
break;
}

return DefWindowProc(hwnd, message, wParam, lParam);
}

// 辅助窗口回调函数
LRESULT CALLBACK ToolWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static TRACKMOUSEEVENT mouseEvent = { sizeof(TRACKMOUSEEVENT), 0, NULL, HOVER_DEFAULT };
static int iTrackedFlag = 0; // -1 客户区 0 无 1 非客客户区

switch (message)
{
case WM_LBUTTONDOWN: // 欺骗windows,让它以为在标题栏上,从而可以移动窗口
return DefWindowProc(hwnd, WM_SYSCOMMAND, (WPARAM)(SC_MOVE | HTCAPTION), 0);

case WM_NCMOUSEMOVE:
if (iTrackedFlag == -1) // 取消监测客户区
{
//printf("cancel track c\n");
mouseEvent.hwndTrack = hwnd;
mouseEvent.dwFlags = TME_LEAVE | TME_CANCEL;
TrackMouseEvent(&mouseEvent);
iTrackedFlag = 0;
}
if (iTrackedFlag == 0) // 监测非客户区
{
//printf("start track c\n");
mouseEvent.hwndTrack = hwnd;
mouseEvent.dwFlags = TME_LEAVE | TME_NONCLIENT;
TrackMouseEvent(&mouseEvent);
SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, NULL, 0);
iTrackedFlag = 1;
}
return 0;

case WM_MOUSEMOVE:
if (iTrackedFlag == 1) // 取消监测非客户区
{
//printf("cancel track nc\n");
mouseEvent.hwndTrack = hwnd;
mouseEvent.dwFlags = TME_LEAVE | TME_NONCLIENT | TME_CANCEL;
TrackMouseEvent(&mouseEvent);
iTrackedFlag = 0;
}
if (iTrackedFlag == 0) // 监测客户区
{
//printf("start track c\n");
mouseEvent.hwndTrack = hwnd;
mouseEvent.dwFlags = TME_LEAVE;
TrackMouseEvent(&mouseEvent);
SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, NULL, 0);
iTrackedFlag = -1;
}
return 0;

case WM_MOUSELEAVE: // 离开客户区
// 监测非客户区
//printf("start track nc\n");
mouseEvent.hwndTrack = hwnd;
mouseEvent.dwFlags = TME_LEAVE | TME_NONCLIENT;
TrackMouseEvent(&mouseEvent);
SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, NULL, 0);
iTrackedFlag = 1;
return 0;

case WM_NCMOUSELEAVE: // 离开非客户区
//printf("end track nc\n");
ShowWindow(hwnd, SW_HIDE); // 隐藏辅助窗口
SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, FALSE, NULL, 0);
iTrackedFlag = 0;
return 0;

case WM_MOVING: // 移动
case WM_SIZING: // 缩放
{
LPCRECT pRect = (LPCRECT)lParam;

// 使得正在移动/缩放时,辅助窗口不要隐藏
ShowWindow(hwnd, SW_SHOWNORMAL);

// 使歌词窗口随辅助窗口一起移动/缩放
MoveWindow(gs_hwndLyrics, pRect->left, pRect->top,
pRect->right - pRect->left,
pRect->bottom - pRect->top, TRUE);
}
return TRUE;

case WM_KILLFOCUS: // 失去焦点,则隐藏辅助窗口
ShowWindow(hwnd, SW_HIDE);
return 0;

case WM_CLOSE:
ShowWindow(hwnd, SW_HIDE);
return 0;

default:
break;
}

return DefWindowProc(hwnd, message, wParam, lParam);
}
...全文
194 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
summer_insects 2013-04-10
  • 打赏
  • 举报
回复
引用 6 楼 Daisy__Ben 的回复:
看到代码中用一个INT型变量iTrackedFlag做状态的跟踪变换,个人感觉不太好……
这是我调了很久才想出来的招。 如果只监测非客户区,那么反应会不灵敏。 如果只监测客户区,那么只要一点击边框,窗口就隐藏了。
Daisy__Ben 2013-04-10
  • 打赏
  • 举报
回复
刚才运行了下LZ的代码,看到代码中用一个INT型变量iTrackedFlag做状态的跟踪变换,个人感觉不太好……简化的代码无所谓,可是要放在工程项目中,除非脑子里足够清醒不然很难调试,很难搞清当前的状态是从哪个状态过渡来的。闪烁的问题很可能是因为这个原因,不过我没看到。最后,LZ顶你,继续围观
ttsping 2013-04-09
  • 打赏
  • 举报
回复
辅助窗口不如用UpdateLayedWindow来更新,可以不需要显示和隐藏窗口,鼠标在窗口上的时候背景Alpha值大一些,鼠标移开时,Alpha值设为1,相当于透明“隐藏”了。
淡定的飘着 2013-04-09
  • 打赏
  • 举报
回复
不错,但是不知道楼主所描述的情况,可以截图或者exe可以跑跑看吗?
jimette 2013-04-09
  • 打赏
  • 举报
回复
应该如何解决当点击在边框上时,闪一下 ---------------- 找到点击时候为什么会闪烁?
summer_insects 2013-04-09
  • 打赏
  • 举报
回复
引用 2 楼 sha_jinhao 的回复:
应该如何解决当点击在边框上时,闪一下 ---------------- 找到点击时候为什么会闪烁?
我就是不知道为什么会闪烁。具体说来就是,当鼠标按下时,辅助窗口就会隐藏, 如果此时继续拖动,则又重新显示或者不显示(取决于主窗口是否有焦点),这是我感到最奇怪的地方
Daisy__Ben 2013-04-09
  • 打赏
  • 举报
回复
LZ确实很有一套,围观中 ……

15,980

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧