简体   繁体   English

以编程方式调用Snap / Aero最大化

[英]Programmatically invoke Snap/Aero maximize

Is there a way to programmatically invoke the Aera maximize effect using C or C++ for a specific window/window ID? 有没有办法以编程方式使用C或C ++为特定的窗口/窗口ID调用Aera最大化效果?

For example: 例如:

在此输入图像描述

or 要么

enter link description here http://www.thebuzzmedia.com/wp-content/uploads/2009/05/windows-7-aero-snap-to-edge-effect.png 在此输入链接说明http://www.thebuzzmedia.com/wp-content/uploads/2009/05/windows-7-aero-snap-to-edge-effect.png

I am using a border-less Qt window and Qt has an API for getting the window ID. 我使用的是无边框Qt窗口,Qt有一个用于获取窗口ID的API。 I want to programmatically trigger the windows effects without the known triggers. 我想以编程方式触发Windows效果而不使用已知的触发器。

I don't want to talk about every single detail involved in achieving this effect, not only there's a lot that goes on but you also mentioned you understand the logic to place the windows at their specific locations. 我不想谈论实现这种效果所涉及的每一个细节,不仅有很多事情发生,而且你还提到你理解将窗口放在特定位置的逻辑。 In this answer I'll address what I believe are the 2 main challenges: 在这个答案中,我将解决我认为的两个主要挑战:

  • How to receive and handle a maximize event? 如何接收和处理最大化事件?

  • How to create an approximation of the aero snap effect? 如何创建近似的航空按扣效果?

In order to answer the first question , we must analyze which event handlers are triggered when the window is maximized: 为了回答第一个问题 ,我们必须分析窗口最大化时触发的事件处理程序

void resizeEvent(QResizeEvent* evt);   // Invoked first,
void paintEvent(QPaintEvent* event);   // then second, 
void changeEvent(QEvent* evt);         // and at last.

A Qt application is first notified of a resizeEvent() , which is followed by a paintEvent() to draw the window (or widget), and only after everything has been displayed, changeEvent() is invoked to let you know the widget was maximized (maybe it's a little bit late to receive such notification, I don't know). 首先通知Qt应用程序resizeEvent() ,然后使用paintEvent()绘制窗口(或窗口小部件),并且只有在显示所有内容之后,才会调用changeEvent()以告知窗口小部件已最大化(也许接收这样的通知有点晚了,我不知道)。

Of all these, the only one we care about is resizeEvent() . 在所有这些中,我们唯一关心的是resizeEvent() This event handler informs the new window/widget size that can be used for comparison with the desktop size, thus allowing us to know if the event was actually a maximize request. 此事件处理程序通知可用于与桌面大小进行比较的新窗口/窗口小部件大小,从而允许我们知道事件是否实际上是最大化请求。 Once we identify a maximize request, we can figure out whether the application should be maximized (and anchored) to right , left or to the center of the screen. 一旦我们确定了最大化请求,我们就可以确定应用程序是否应该最大化(并锚定)到屏幕的右侧左侧中心

This would be the time to create the aero snap widget and place it on the screen as a visual clue to the user. 这将是创建aero snap小部件并将其作为用户视觉线索放置在屏幕上的时间。

To answer the second question , I don't think is possible to call the native Windows API and ask it politely to perform this effect on your window. 要回答第二个问题 ,我认为无法调用本机Windows API并礼貌地请求它在您的窗口上执行此效果。 The only other logical choice is to write a code that approximates this effect ourselves. 唯一合乎逻辑的选择是编写一个自己近似于这种效果的代码。

The visual appearance can be replicated by drawing a transparent window with a shadow-ish border. 可以通过绘制带阴影边框的透明窗口来复制视觉外观。 The approach demonstrated in the source code below, creates and customizes a QWidget to make it behave and look like a aero snap window: 在下面的源代码中演示的方法,创建并自定义QWidget ,使其行为和看起来像一个航空快照窗口:

在此输入图像描述

It's not the most beautiful thing in the world, I know. 我知道,这不是世界上最美丽的事物。 This demo creates a regular window for the user to interact with, and once it's maximized, it places itself to the left of the screen. 此演示为用户创建一个常规窗口进行交互,一旦最大化,它就会将自己置于屏幕左侧。 To the right size of the screen it displays something that resembles an aero snap window (shown above). 对于正确尺寸的屏幕,它会显示类似于航空快照窗口的内容(如上所示)。

The idea behind the aero snap widget is very simple : a QWidget with transparent background and a custom painting procedure. aero snap小部件背后的想法非常简单 :具有透明背景和自定义绘制过程的QWidget In other words, it's a transparent window which draws a rounded rectangle with a shadow and that's it. 换句话说,它是一个透明的窗口,它绘制一个带阴影的圆角矩形,就是这样。

To make it a bit more realistic, you should add some animation to resize the widget little by little. 为了使它更逼真,你应该添加一些动画来逐渐调整小部件的大小。 A for loop might do the trick, but if you need something fancy you'll end up using timers. for循环可能会起作用,但如果你需要一些花哨的东西,你最终会使用定时器。 If you take a look here , you can see the quickest & dirtiest method to perform animation with Qt in action, and better ways to deal with animation. 如果你看一下这里 ,你可以看到使用Qt执行动画的最快最脏的方法,以及处理动画的更好方法。 However, for simple tasks like this, stick with frame-based animation . 但是,对于像这样的简单任务,请坚持使用基于帧的动画

main.cpp : main.cpp

#include "window.h"
#include <QApplication>

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    Window window;
    window.show();

    return app.exec();
}

window.h : window.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;
};

window.cpp : window.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.
}

snapwindow.h : snapwindow.h

#pragma once
#include <QWidget>

class SnapWindow : public QWidget
{
public:
    SnapWindow(QWidget* parent = 0);

    void paintEvent(QPaintEvent *event);
};

snapwindow.cpp : snapwindow.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);
}

This is just a quick demo to point you to the right direction. 这只是一个快速演示,可以指出您正确的方向。 It is by no means a complete implementation of the effect you are looking for. 它绝不是您正在寻找的效果的完整实现。

Maybe it is not what you need, but this effect is just resizing and moving window then try use Qt methods to do this. 也许它不是你需要的,但这个效果只是调整大小和移动窗口然后尝试使用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());
}

With QApplication::desktop() it will work properly on screen with different resolutions. 使用QApplication::desktop()它可以在具有不同分辨率的屏幕上正常工作。

In web I found something similar in winapi , but it didn't work properly: 在网络上我在winapi发现了类似的东西,但它没有正常工作:

HWND act = GetForegroundWindow();
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);

The best way 最好的方法

Combine this approaches. 结合这种方法。 For example: 例如:

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());
}

Why? 为什么? Because move() regulate left and right sides, but PostMessage ( winapi ) set window's height properly on every screen (window will not locate lower then taskbar , as in your example) 因为move()调节左右两侧,但PostMessagewinapi )在每个屏幕上正确设置窗口的高度(窗口不会低于taskbar ,如在您的示例中)

EDIT 编辑

I changed code a little and now it is better. 我改变了一点代码,现在它变得更好了。 Yes, it is resizing again, but now it hasn't winapi code ( PostMessage etc), so Photoshop doesn't catch it, there is one interesting method in Qt which called availableGeometry. 是的,它再次调整大小,但现在它没有winapi代码( PostMessage等),所以Photoshop没有捕获它,Qt中有一个有趣的方法叫做availableGeometry。 It return normal height of screen which we need, with this method borderless windows perfectly simulates Aero Snap effects in different directions. 它返回我们需要的正常屏幕高度,这种方法无边框窗口完美地模拟不同方向的Aero Snap效果。 It is works, maybe don't so good, but as I can see, there isn't API for Aero effects. 这是有效的,也许不是那么好,但正如我所看到的,Aero效果没有API。 Maybe this approach will be normal for yoo. 也许这种方法对于你来说是正常的。

There is Aero Peek in Qt : http://qt-project.org/doc/qt-5/qtwinextras-overview.html , but it is can't solve this problem too. Qt中有Aero Peek: http//qt-project.org/doc/qt-5/qtwinextras-overview.html ,但它也无法解决这个问题。

Code: 码:

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);
    }

Try it with frameless window! 尝试使用无框窗口! You should choose one direction or let user choose it. 您应该选择一个方向或让用户选择它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM