#include "windowpreviewwidget.h" #include #include #include #include // 添加标签用于显示信息 #include #include #include #include #include #include #include #include #include #include #include #include class ThumbnailDelegate; // 前置声明 struct WindowInfo { HWND hwnd; std::wstring title; std::wstring className; // 窗口类名 }; static bool isTooltipWindow(HWND hwnd); static bool isAltTabWindow(HWND hwnd, const std::wstring &title, const std::wstring &className) { HWND shellWindow = GetShellWindow(); if (hwnd == shellWindow) { return false; } if (title.length() == 0 || title == L"NVIDIA GeForce Overlay") { return false; } if (!IsWindowVisible(hwnd)) { return false; } if (GetAncestor(hwnd, GA_ROOT) != hwnd) { return false; } // LONG style = GetWindowLong(hwnd, GWL_STYLE); // if (!((style & WS_DISABLED) != WS_DISABLED)) { // return false; // } // 跳过工具提示窗口 // if (isTooltipWindow(hwnd)) { // return false; // } // 跳过桌面窗口的特殊类名 if (className == L"WorkerW" || className == L"Progman") { return false; } return true; return !IsIconic(hwnd); } static bool isTooltipWindow(HWND hwnd) { wchar_t className[256] = {0}; GetClassNameW(hwnd, className, 256); // 类名过滤列表 const wchar_t *tooltipClasses[] = {L"tooltips_class32", L"Qt5QWindowToolSaveBtn", L"QWindowToolTipDropShadowEffect", L"QWindowToolTipDropShadows", L"PopupWindow", L"Xaml_WindowedPopupClass", L"Chrome_WidgetWin_2", L"QtQWindowIcon", L"QtQWindowOwnDCIcon", L"QtQWindowPopup", L"QtQWindowToolTip", L"Windows.UI.Core.CoreWindow"}; for (const wchar_t *cls : tooltipClasses) { if (wcscmp(className, cls) == 0) { return true; } } // 通过窗口标题过滤(如果标题包含工具提示相关关键词) wchar_t title[256] = {0}; GetWindowTextW(hwnd, title, 256); std::wstring titleStr(title); if (titleStr.find(L"Tooltip") != std::wstring::npos || titleStr.find(L"Tip") != std::wstring::npos || titleStr.find(L"提示") != std::wstring::npos || titleStr.find(L"Popup") != std::wstring::npos) { return true; } // // 通过窗口样式过滤 // LONG style = GetWindowLong(hwnd, GWL_STYLE); // LONG exStyle = GetWindowLong(hwnd, GWL_EXSTYLE); // // 过滤无边框、置顶且透明的弹出窗口 // if ((exStyle & WS_EX_TOOLWINDOW) && (style & WS_POPUP) && !(style & WS_CHILD) // && (exStyle & WS_EX_LAYERED)) { // return true; // } // 过滤小尺寸窗口(通常为工具提示) // RECT rect; // GetWindowRect(hwnd, &rect); // int width = rect.right - rect.left; // int height = rect.bottom - rect.top; // if (width < 100 && height < 50) { // return true; // } return false; } BOOL CALLBACK enumWindowsProc(HWND hwnd, LPARAM lParam) { // 确保窗口有效 if (!IsWindow(hwnd)) { return TRUE; } // 获取窗口标题 std::wstring title; wchar_t buffer[256] = {0}; GetWindowTextW(hwnd, buffer, 256); title = buffer; // 获取窗口类名 std::wstring className; wchar_t classBuffer[256] = {0}; GetClassNameW(hwnd, classBuffer, 256); className = classBuffer; // 检查是否是Alt+Tab窗口 if (!isAltTabWindow(hwnd, title, className)) { return TRUE; } // 添加到窗口列表 std::vector *windows = reinterpret_cast *>(lParam); windows->push_back({hwnd, title, className}); return TRUE; } class ThumbnailDelegate : public QStyledItemDelegate { public: explicit ThumbnailDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); // 整个项尺寸(160x120) const int itemWidth = 160; const int itemHeight = 120; // 计算绘制区域在父项中的位置(居中) const int left = option.rect.left() + (option.rect.width() - itemWidth) / 2; const int top = option.rect.top() + (option.rect.height() - itemHeight) / 2; // 主绘制区域(项边界) const QRect itemRect(left, top, itemWidth, itemHeight); // 创建圆角背景 painter->setPen(Qt::NoPen); painter->setBrush(QColor(40, 40, 40, 180)); painter->drawRoundedRect(itemRect, 8, 8); // 图标位置(左上角) const QRect iconRect(itemRect.left() + 5, itemRect.top() + 5, 30, 30); // 图标位置(右上角) const QRect isMinimizedRect(itemRect.width() - 25, itemRect.top() + 5, 30, 30); // 边框区域(内缩10像素) const QRect borderRect = itemRect.adjusted(10, 10, -10, -10); // 缩略图可用区域(减去底部标题空间) const QRect thumbArea = borderRect.adjusted(0, 0, 0, -20); // 底部留20像素给标题 // 计算最大正方形区域(1:1比例) const int thumbSize = qMin(thumbArea.width(), thumbArea.height()); const QRect thumbRect(thumbArea.left() + (thumbArea.width() - thumbSize) / 2, thumbArea.top() + (thumbArea.height() - thumbSize) / 2, thumbSize, thumbSize); // 标题区域(底部居中) const QRect titleRect(borderRect.left(), borderRect.bottom() - 20, borderRect.width(), 20); // 获取数据 const QPixmap thumbnail = qvariant_cast(index.data(Qt::DecorationRole)); const QString title = index.data(Qt::DisplayRole).toString(); const QString className = index.data(Qt::UserRole + 5).toString(); const bool isActive = index.data(Qt::UserRole + 1).toBool(); const bool isMinimized = index.data(Qt::UserRole + 2).toBool(); const QIcon icon = qvariant_cast(index.data(Qt::UserRole + 3)); const bool isDesktop = index.data(Qt::UserRole + 4).toBool(); const bool hasFocus = option.state & QStyle::State_HasFocus; const bool isHovered = option.state & QStyle::State_MouseOver; // 绘制缩略图背景(边框区域内) painter->fillRect(borderRect, QColor(30, 30, 30, 150)); // 居中绘制缩略图(保持1:1比例) if (!thumbnail.isNull()) { // 创建1:1比例的缩略图 QPixmap scaledThumb = thumbnail.scaled(thumbSize, thumbSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); // 居中绘制 const int x = thumbRect.left() + (thumbRect.width() - scaledThumb.width()) / 2; const int y = thumbRect.top() + (thumbRect.height() - scaledThumb.height()) / 2; painter->drawPixmap(x, y, scaledThumb); } else { // 无缩略图时显示占位符 painter->setPen(Qt::lightGray); painter->drawText(thumbRect, Qt::AlignCenter, "No Preview"); } // 绘制标题(底部居中,不换行) painter->setPen(isActive ? Qt::white : Qt::lightGray); QFont titleFont = painter->font(); titleFont.setBold(true); titleFont.setPointSize(9); painter->setFont(titleFont); // 截短标题以适应空间 QString shortTitle = title; QFontMetrics titleMetrics(titleFont); shortTitle = titleMetrics.elidedText(title, Qt::ElideRight, titleRect.width()); painter->drawText(titleRect, Qt::AlignCenter | Qt::TextSingleLine, shortTitle); // 绘制边框(焦点时变色) QPen borderPen; if (isHovered || hasFocus) { borderPen = QPen(QColor(0, 255, 0), 3); } else if (isActive) { borderPen = QPen(QColor(0, 120, 215), 3); } else { borderPen = QPen(Qt::gray, 1); } painter->setPen(borderPen); painter->setBrush(Qt::NoBrush); painter->drawRoundedRect(borderRect, 6, 6); // 绘制左上角图标 if (!icon.isNull()) { QPixmap iconPix = icon.pixmap(30, 30); painter->drawPixmap(iconRect, iconPix); } if (isMinimized) { // 使用红色矩形作为最小化标识 painter->setPen(QPen(Qt::red, 2)); painter->setBrush(Qt::NoBrush); // 在右上角区域绘制矩形框 const int markerSize = 20; QRect markerRect(isMinimizedRect.left() + (isMinimizedRect.width() - markerSize) / 2, isMinimizedRect.top() + (isMinimizedRect.height() - markerSize) / 2, markerSize, markerSize); painter->drawRect(markerRect); // 在矩形框内绘制减号"-" painter->setPen(QPen(Qt::red, 3)); painter->drawLine(markerRect.left() + 5, markerRect.center().y(), markerRect.right() - 5, markerRect.center().y()); } // 如果是桌面项,添加特殊标记 if (isDesktop) { painter->setPen(QPen(Qt::cyan, 2)); QFont desktopFont("Arial", 8, QFont::Bold); painter->setFont(desktopFont); painter->drawText(thumbRect.topLeft() + QPoint(10, 15), "Desktop"); } painter->restore(); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { Q_UNUSED(option); Q_UNUSED(index); return QSize(160, 120); // 调整大小以适应新布局 } }; WindowPreviewWidget::WindowPreviewWidget(QWidget *parent) : QListView(parent) { // 设置模型 model = new QStandardItemModel(this); setModel(model); // 设置委托 setItemDelegate(new ThumbnailDelegate(this)); // 设置视图属性 setViewMode(QListView::IconMode); setResizeMode(QListView::Adjust); setGridSize(QSize(180, 140)); // 增加网格大小以容纳间距 setSpacing(20); // 增加项间间距到20像素 setDragEnabled(false); setSelectionMode(QAbstractItemView::SingleSelection); setEditTriggers(QAbstractItemView::NoEditTriggers); // 设置焦点策略 setFocusPolicy(Qt::StrongFocus); // 启用滚动 setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // 设置定时器 updateTimer = new QTimer(this); connect(updateTimer, &QTimer::timeout, this, &WindowPreviewWidget::updateThumbnails); updateTimer->start(1000); // 初始1000ms更新一次 // 初始更新 QTimer::singleShot(100, this, &WindowPreviewWidget::updateThumbnails); // 启用鼠标跟踪 setMouseTracking(true); viewport()->setMouseTracking(true); } WindowPreviewWidget::~WindowPreviewWidget() { // 安全清理资源 for (auto it = thumbnailCache.begin(); it != thumbnailCache.end(); ++it) { if (it.value()) { delete it.value(); } } thumbnailCache.clear(); } void WindowPreviewWidget::mousePressEvent(QMouseEvent *event) { QListView::mousePressEvent(event); // if (event->button() == Qt::LeftButton) { // const QModelIndex index = indexAt(event->pos()); // if (index.isValid()) { // const quintptr hwndPtr = index.data(Qt::UserRole).value(); // const HWND hwnd = reinterpret_cast(hwndPtr); // const bool isDesktop = index.data(Qt::UserRole + 4).toBool(); // const bool isMinimized = index.data(Qt::UserRole + 2).toBool(); // if (isDesktop) { // // 切换到桌面 // const HWND desktopHwnd = FindWindow(L"Progman", L"Program Manager"); // if (desktopHwnd) { // SetForegroundWindow(desktopHwnd); // } // } else if (hwnd) { // // 激活窗口 // if (isMinimized) { // ShowWindow(hwnd, SW_RESTORE); // } // // 尝试多次激活窗口(解决某些情况下激活失败的问题) // for (int i = 0; i < 3; i++) { // if (SetForegroundWindow(hwnd)) { // break; // } // Sleep(50); // } // } // // 设置焦点索引 // focusedIndex = index; // update(); // } // } } // 添加双击事件处理 void WindowPreviewWidget::mouseDoubleClickEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { const QModelIndex index = indexAt(event->pos()); if (index.isValid()) { const quintptr hwndPtr = index.data(Qt::UserRole).value(); const HWND hwnd = reinterpret_cast(hwndPtr); const bool isDesktop = index.data(Qt::UserRole + 4).toBool(); const bool isMinimized = index.data(Qt::UserRole + 2).toBool(); const QString title = index.data(Qt::DisplayRole).toString(); // if (isDesktop) { // // 切换到桌面 // const HWND desktopHwnd = FindWindow(L"Progman", L"Program Manager"); // if (desktopHwnd) { // SetForegroundWindow(desktopHwnd); // } // } else if (hwnd) { // // 激活窗口 // if (isMinimized) { // ShowWindow(hwnd, SW_RESTORE); // } // // 尝试多次激活窗口(解决某些情况下激活失败的问题) // for (int i = 0; i < 3; i++) { // if (SetForegroundWindow(hwnd)) { // break; // } // Sleep(50); // } // } // 设置焦点索引 focusedIndex = index; update(); // 发出双击信号 emit windowDoubleClicked(hwndPtr, title); } } // 调用基类实现以确保正确处理事件 QListView::mouseDoubleClickEvent(event); } void WindowPreviewWidget::mouseMoveEvent(QMouseEvent *event) { QListView::mouseMoveEvent(event); // 获取当前鼠标位置下的索引 QModelIndex currentIndex = indexAt(event->pos()); // 更新悬停状态 if (hoveredIndex != currentIndex) { if (hoveredIndex.isValid()) { update(hoveredIndex); // 更新旧的悬停项 } hoveredIndex = currentIndex; if (hoveredIndex.isValid()) { update(hoveredIndex); // 更新新的悬停项 } } } void WindowPreviewWidget::leaveEvent(QEvent *event) { Q_UNUSED(event); // 清除悬停状态 if (hoveredIndex.isValid()) { update(hoveredIndex); hoveredIndex = QModelIndex(); } } void WindowPreviewWidget::enterEvent(QEvent *event) { Q_UNUSED(event); // 不需要额外处理 } void WindowPreviewWidget::focusInEvent(QFocusEvent *event) { QListView::focusInEvent(event); // 当获得焦点时更新视图 update(); } void WindowPreviewWidget::focusOutEvent(QFocusEvent *event) { QListView::focusOutEvent(event); // 当失去焦点时更新视图 update(); } void WindowPreviewWidget::updateThumbnails() { // 重置悬停索引 if (hoveredIndex.isValid()) { update(hoveredIndex); hoveredIndex = QModelIndex(); } // 获取活动窗口 const HWND activeWindow = GetForegroundWindow(); // 检查是否需要更新 bool needUpdate = (activeWindow != lastActiveWindow); lastActiveWindow = activeWindow; // 获取所有窗口 std::vector windows; EnumWindows(enumWindowsProc, reinterpret_cast(&windows)); // 清理缓存中的无效窗口 for (auto it = thumbnailCache.begin(); it != thumbnailCache.end();) { if (!IsWindow(it.key())) { delete it.value(); it = thumbnailCache.erase(it); } else { ++it; } } // 创建当前窗口集合(确保只包含有效窗口) QSet currentWindowSet; for (const auto &win : windows) { if (IsWindow(win.hwnd)) { currentWindowSet.insert(win.hwnd); } } // 创建缓存窗口集合(确保只包含有效窗口) QSet cachedWindowSet; for (auto it = thumbnailCache.begin(); it != thumbnailCache.end(); ++it) { if (IsWindow(it.key())) { cachedWindowSet.insert(it.key()); } } // 如果窗口集合不同,需要完全更新 if (currentWindowSet != cachedWindowSet) { needUpdate = true; } // 如果没有变化,不需要更新 if (!needUpdate) { return; } // 清空模型 model->clear(); // 添加桌面预览 addDesktopThumbnail(activeWindow); // 更新窗口预览 for (const auto &win : windows) { // 确保窗口有效 if (IsWindow(win.hwnd)) { addWindowThumbnail(win.hwnd, activeWindow); } } // 动态调整更新频率 updateTimer->setInterval(needFullUpdate() ? 500 : 1500); } void WindowPreviewWidget::addWindowThumbnail(HWND hwnd, HWND activeWindow) { // 跳过某些系统窗口 if (hwnd == (HWND) effectiveWinId()) return; // 确保窗口有效 if (!IsWindow(hwnd)) { return; } // 获取窗口标题 std::wstring title; wchar_t titleBuffer[256] = {0}; GetWindowTextW(hwnd, titleBuffer, 256); title = titleBuffer; // 获取窗口类名 std::wstring className; wchar_t classBuffer[256] = {0}; GetClassNameW(hwnd, classBuffer, 256); className = classBuffer; // 检查窗口状态 const bool isMinimized = IsIconic(hwnd); const bool isActive = (activeWindow == hwnd); // 获取窗口图标 QIcon icon = getWindowIcon(hwnd); // 获取窗口预览 QPixmap *thumbnail = nullptr; if (thumbnailCache.contains(hwnd) && !needFullUpdate()) { thumbnail = thumbnailCache[hwnd]; } else { thumbnail = captureWindow(hwnd); if (thumbnail) { thumbnailCache[hwnd] = thumbnail; } } // 创建列表项 QStandardItem *item = new QStandardItem; if (thumbnail) { item->setData(*thumbnail, Qt::DecorationRole); } item->setData(QString::fromWCharArray(title.c_str()), Qt::DisplayRole); item->setData(reinterpret_cast(hwnd), Qt::UserRole); // 使用quintptr存储句柄 item->setData(isActive, Qt::UserRole + 1); item->setData(isMinimized, Qt::UserRole + 2); item->setData(icon, Qt::UserRole + 3); item->setData(false, Qt::UserRole + 4); item->setData(QString::fromWCharArray(className.c_str()), Qt::UserRole + 5); model->appendRow(item); } QPixmap *WindowPreviewWidget::captureWindow(HWND hwnd) { // 确保窗口有效 if (!IsWindow(hwnd)) { return nullptr; } // 获取窗口大小 RECT rect; GetWindowRect(hwnd, &rect); const int width = rect.right - rect.left; const int height = rect.bottom - rect.top; if (width <= 0 || height <= 0) { return nullptr; } // 创建内存DC HDC hdcScreen = GetDC(NULL); HDC hdcMem = CreateCompatibleDC(hdcScreen); HBITMAP hbm = CreateCompatibleBitmap(hdcScreen, width, height); SelectObject(hdcMem, hbm); // 尝试两种捕获方法 bool captureSuccess = false; // 处理最小化窗口 bool wasMinimized = IsIconic(hwnd); if (wasMinimized) { // 临时恢复窗口位置 // WINDOWPLACEMENT wp; // wp.length = sizeof(WINDOWPLACEMENT); // GetWindowPlacement(hwnd, &wp); // // 移动窗口到屏幕外 // SetWindowPos(hwnd, // NULL, // -width - 10, // -height - 10, // width, // height, // SWP_NOZORDER | SWP_SHOWWINDOW); // ShowWindow(hwnd, SW_RESTORE); // 方法2: 使用PrintWindow (适用于最小化窗口) if (!captureSuccess) { if (PrintWindow(hwnd, hdcMem, PW_RENDERFULLCONTENT)) { captureSuccess = true; } } } if (!captureSuccess) { // 方法1: 使用BitBlt (最快) if (BitBlt(hdcMem, 0, 0, width, height, GetWindowDC(hwnd), 0, 0, SRCCOPY)) { captureSuccess = true; } } // 恢复最小化窗口状态 if (wasMinimized) { ShowWindow(hwnd, SW_MINIMIZE); } // 转换为QPixmap QImage image(width, height, QImage::Format_ARGB32); BITMAPINFO bmi = {0}; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = width; bmi.bmiHeader.biHeight = -height; // 从上到下 bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; if (captureSuccess) { GetDIBits(hdcMem, hbm, 0, height, image.bits(), &bmi, DIB_RGB_COLORS); } else { // 创建纯色背景作为回退 image.fill(QColor(60, 60, 60).rgb()); QPainter painter(&image); painter.setPen(Qt::white); painter.drawText(image.rect(), Qt::AlignCenter, "Capture Failed"); } QPixmap *pixmap = new QPixmap(QPixmap::fromImage(image)); // 缩放缩略图 *pixmap = pixmap->scaled(thumbnailSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); // 清理资源 DeleteObject(hbm); DeleteDC(hdcMem); ReleaseDC(NULL, hdcScreen); return pixmap; } void WindowPreviewWidget::addDesktopThumbnail(HWND activeWindow) { // 使用缓存 static QPixmap desktopThumbnail; if (desktopThumbnail.isNull() || needFullUpdate()) { // 捕获桌面 QScreen *screen = QApplication::primaryScreen(); desktopThumbnail = screen->grabWindow(0); } // 创建桌面项 QStandardItem *item = new QStandardItem; item->setData(desktopThumbnail.scaled(thumbnailSize, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::DecorationRole); item->setData("Desktop", Qt::DisplayRole); item->setData(quintptr(0), Qt::UserRole); // 桌面项使用0作为句柄 item->setData((activeWindow == GetDesktopWindow()), Qt::UserRole + 1); item->setData(false, Qt::UserRole + 2); item->setData(QIcon(), Qt::UserRole + 3); // 桌面不需要图标 item->setData(true, Qt::UserRole + 4); item->setData("Desktop", Qt::UserRole + 5); model->appendRow(item); } QIcon WindowPreviewWidget::getWindowIcon(HWND hwnd) { // 确保窗口有效 if (!IsWindow(hwnd)) { return QIcon(); } HICON hIcon = (HICON) SendMessage(hwnd, WM_GETICON, ICON_BIG, 0); if (!hIcon) { hIcon = (HICON) GetClassLongPtr(hwnd, GCLP_HICON); } if (!hIcon) { hIcon = LoadIcon(NULL, IDI_APPLICATION); } if (hIcon) { QIcon icon = QIcon(QtWin::fromHICON(hIcon)); DestroyIcon(hIcon); return icon; } return QIcon(); } bool WindowPreviewWidget::needFullUpdate() const { // 每5次更新做一次完全更新 static int counter = 0; return (++counter % 5) == 0; } // int main(int argc, char *argv[]) // { // QApplication app(argc, argv); // QWidget mainWidget; // QVBoxLayout *layout = new QVBoxLayout(&mainWidget); // layout->setContentsMargins(0, 0, 0, 0); // // 设置半透明背景 // mainWidget.setAttribute(Qt::WA_TranslucentBackground); // // mainWidget.setStyleSheet("background: rgba(0, 0, 0, 150);"); // WindowPreviewWidget *previewWidget = new WindowPreviewWidget; // layout->addWidget(previewWidget); // // 添加信息标签 // QLabel *infoLabel = new QLabel("Double-click a window to see its handle"); // infoLabel->setAlignment(Qt::AlignCenter); // infoLabel->setStyleSheet("color: white; font-weight: bold; background: rgba(0, 0, 0, 100);"); // layout->addWidget(infoLabel); // // 连接双击信号 // QObject::connect(previewWidget, // &WindowPreviewWidget::windowDoubleClicked, // [infoLabel](quintptr hwnd, const QString &title) { // QString info = QString("Double-clicked window: HWND=0x%1, Title=\"%2\"") // .arg(hwnd, 0, 16) // 以十六进制显示句柄 // .arg(title); // infoLabel->setText(info); // }); // mainWidget.setWindowTitle("Window Preview"); // mainWidget.resize(900, 700); // mainWidget.show(); // return app.exec(); // } // #include "main.moc"