有没有办法使用 C 或 C++ 为特定窗口/窗口 ID 以编程方式调用 Aera 最大化效果?
例如:
或者
(来源:thebuzzmedia.com)
我正在使用一个无边框的 Qt 窗口,并且 Qt 有一个用于获取窗口 ID 的 API。我想在没有已知触发器的情况下以编程方式触发 windows 效果。
我不想谈论实现此效果所涉及的每一个细节,不仅有很多事情发生,而且您还提到您了解将窗户放置在其特定位置的逻辑。在这个答案中,我将解决我认为的两个主要挑战:
如何接收和处理最大化事件?
如何创建近似的aero snap效果?
为了回答第一个问题,我们必须分析窗口最大化时触发了哪些事件处理程序:
void resizeEvent(QResizeEvent* evt); // Invoked first,
void paintEvent(QPaintEvent* event); // then second,
void changeEvent(QEvent* evt); // and at last.
Qt 应用程序首先收到通知 a resizeEvent()
,然后是 apaintEvent()
以绘制窗口(或小部件),并且只有在所有内容都显示后,changeEvent()
才会调用以通知您小部件已最大化(可能接收到有点晚了这样的通知,我不知道)。
在所有这些中,我们唯一关心的是resizeEvent()
。此事件处理程序通知可用于与桌面大小进行比较的新窗口/小部件大小,从而让我们知道该事件是否实际上是最大化请求。一旦我们确定了最大化请求,我们就可以确定应用程序是否应该最大化(并锚定)到屏幕的右侧、左侧或中心。
这将是创建aero snap 小部件并将其放置在屏幕上作为用户视觉线索的时候了。
要回答第二个问题,我认为不可能调用本机 Windows API 并礼貌地要求它在您的窗口上执行此效果。唯一的另一个合乎逻辑的选择是自己编写一个近似这种效果的代码。
可以通过绘制带有阴影边框的透明窗口来复制视觉外观。下面的源代码中演示的方法创建和自定义 aQWidget
以使其行为和看起来像一个航空快照窗口:
我知道,这不是世界上最美好的事物。这个演示创建了一个常规窗口供用户交互,一旦最大化,它就会将自己放置在屏幕的左侧。在屏幕的正确尺寸上,它会显示类似于aero snap窗口的内容(如上所示)。
航空快照小部件背后的想法非常简单:QWidget
具有透明背景和自定义绘画程序。换句话说,它是一个透明的窗口,它绘制了一个带有阴影的圆角矩形,仅此而已。
为了让它更真实一点,你应该添加一些动画来一点一点地调整小部件的大小。循环可能会解决问题,但如果你需要一些花哨的for
东西,你最终会使用计时器。如果你看一下这里,你可以看到使用 Qt 执行动画的最快和最脏的方法,以及处理动画的更好方法。但是,对于像这样的简单任务,请坚持使用基于帧的动画。
主.cpp:
#include "window.h"
#include <QApplication>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}
窗口.h:
#pragma once
#include "snapwindow.h"
#include <QMainWindow>
#include <QEvent>
class Window : public QMainWindow
{
public:
Window();
void resizeEvent(QResizeEvent* evt);
//void paintEvent(QPaintEvent* event);
void changeEvent(QEvent* evt);
private:
SnapWindow* _sw;
};
窗口.cpp:
#include "window.h"
#include "snapwindow.h"
#include <QDebug>
#include <QWindowStateChangeEvent>
#include <QApplication>
#include <QDesktopWidget>
Window::Window()
{
setWindowTitle("AeroSnap");
resize(300, 300);
_sw = new SnapWindow(this);
_sw->hide();
}
void Window::changeEvent(QEvent* evt)
{
if (evt->type() == QEvent::WindowStateChange)
{
QWindowStateChangeEvent* event = static_cast<QWindowStateChangeEvent*>(evt);
if (event->oldState() == Qt::WindowNoState &&
windowState() == Qt::WindowMaximized)
{
qDebug() << "changeEvent: window is now maximized!";
}
}
}
// resizeEvent is triggered before window_maximized event
void Window::resizeEvent(QResizeEvent* evt)
{
qDebug() << "resizeEvent: request to resize window to: " << evt->size();
QSize desktop_sz = QApplication::desktop()->size();
//qDebug() << "resizeEvent: desktop sz " << desktop_sz.width() << "x" << desktop_sz.height();
// Apparently, the maximum size a window can have in my system (1920x1080)
// is actually 1920x990. I suspect this happens because the taskbar has 90px of height:
desktop_sz.setHeight(desktop_sz.height() - 90);
// If this not a request to maximize the window, don't do anything crazy.
if (desktop_sz.width() != evt->size().width() ||
desktop_sz.height() != evt->size().height())
return;
// Alright, now we known it's a maximize request:
qDebug() << "resizeEvent: maximize this window to the left";
// so we update the window geometry (i.e. size and position)
// to what we think it's appropriate: half width to the left
int new_width = evt->size().width();
int new_height = evt->size().height();
int x_offset = 10;
setGeometry(x_offset, 45, new_width/2, new_height-45); // y 45 and height -45 are due to the 90px problem
/* Draw aero snap widget */
_sw->setGeometry(new_width/2-x_offset, 0, new_width/2, new_height);
_sw->show();
// paintEvent() will be called automatically after this method ends,
// and will draw this window with the appropriate geometry.
}
快照窗口.h:
#pragma once
#include <QWidget>
class SnapWindow : public QWidget
{
public:
SnapWindow(QWidget* parent = 0);
void paintEvent(QPaintEvent *event);
};
快照窗口.cpp:
#include "snapwindow.h"
#include <QPainter>
#include <QGraphicsDropShadowEffect>
SnapWindow::SnapWindow(QWidget* parent)
: QWidget(parent)
{
// Set this widget as top-level (i.e. owned by user)
setParent(0);
/* Behold: the magic of creating transparent windows */
setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
setStyleSheet("background:transparent;");
setAttribute(Qt::WA_NoSystemBackground, true); // speed up drawing by removing unnecessary background initialization
setAttribute(Qt::WA_TranslucentBackground);
//setAutoFillBackground(true);
/* Use Qt tricks to paint stuff with shadows */
QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect();
effect->setBlurRadius(12);
effect->setOffset(0);
effect->setColor(QColor(0, 0, 0, 255));
setGraphicsEffect(effect);
}
void SnapWindow::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
/* Lazy way of painting a shadow */
QPainter painter(this);
QPen pen(QColor(180, 180, 180, 200));
pen.setWidth(3);
painter.setPen(pen);
// Offset 6 and 9 pixels so the shadow shows up properly
painter.drawRoundedRect(QRect(6, 6, (width()-1)-9, (height()-1)-9), 18, 18);
}
这只是一个快速演示,可以为您指明正确的方向。它绝不是您正在寻找的效果的完整实现。
也许这不是你需要的,但这个效果只是调整大小和移动窗口然后尝试使用 Qt 方法来做到这一点。
bool left = false;
QSize size = QApplication::desktop()->size();//resolution of current screen
if(left)
{//left side
this->setGeometry(0, 0, size.width()/2, size.height());//(maybe need do some changes)
}
else
{//right side
this->setGeometry(size.width()/2, 0, size.width()/2, size.height());
}
使用QApplication::desktop()
它可以在不同分辨率的屏幕上正常工作。
在 web 我发现了类似的东西winapi
,但它不能正常工作:
HWND act = GetForegroundWindow();
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
最好的方法
结合这些方法。例如:
HWND act = GetForegroundWindow();
bool left = false;
QSize size = QApplication::desktop()->size();
if(left)
{
this->move(0,0);
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
this->resize(size.width()/2,QApplication::desktop()->height());
}
else
{
this->move(size.width()/2,0);
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
this->resize(size.width()/2,QApplication::desktop()->height());
}
为什么?因为move()
调节左右两侧,但是PostMessage
( winapi
) 在每个屏幕上正确设置窗口的高度(窗口不会低于taskbar
,如您的示例所示)
编辑
我稍微更改了代码,现在更好了。是的,它再次调整大小,但现在它没有 winapi 代码(PostMessage
等),所以 Photoshop 没有捕捉到它,Qt 中有一个有趣的方法,称为 availableGeometry。它返回我们需要的正常屏幕高度,使用这种方法无边框窗口完美地模拟了不同方向的 Aero Snap 效果。它是有效的,也许不是那么好,但正如我所见,没有用于 Aero 效果的 API。也许这种方法对你来说是正常的。
Qt 中有 Aero Peek:http: //qt-project.org/doc/qt-5/qtwinextras-overview.html ,但它也不能解决这个问题。
代码:
bool left = true;
bool upper = true;
if(upper)
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
this->setGeometry(rect);
}
else if(left)
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
rect.setWidth(rect.width()/2);
this->setGeometry(rect);
}
else
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
int half = rect.width()/2;
rect.setX(half);
rect.setWidth(half);
this->setGeometry(rect);
}
试试无框窗吧!您应该选择一个方向或让用户选择它。