Programmatically call Snap / Aero to deploy

Is there a way to programmatically trigger the Aera maximization effect using C or C ++ for a specific window / window ID?

For example:

enter image description here

or

enter link description here
(source: thebuzzmedia.com )

I am using Qt borderless window and Qt has an API to get the window id. I want to programmatically run windows effects with no known triggers.

+4


source to share


2 answers


I don't want to talk about every detail involved in achieving this effect, not just what is happening, but you also mentioned that you understand the logic behind placing windows in their specific locations. In this answer, I will cover what I think are the two main problems:

  • How do I get and handle the maximize event?

  • How do I create an approximation of the aero snap effect?

To answer the first question , we need to analyze which event handlers are fired when the window is maximized:

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

      

The Qt app is first notified of resizeEvent()

, followed by paintEvent()

to draw the window (or widget), and only after everything has been rendered is it called changeEvent()

so you know the widget is at its maximum (maybe a little late to get such a notification, I'm not know).

Of all this, the only thing we care about is this resizeEvent()

. This event handler informs about the new window / widget size that can be used to compare against the desktop size, allowing us to know if this event is indeed the maximum request. Once we identify the maximization request, we can figure out whether to maximize (and bind) the application to the right, left, or center of the screen.

Now is the time to create the aero snap widget and place it on the screen as a visual clue to the user.

To answer the second question , I don't think it is possible to call the native Windows API and ask it to politely do this effect on your window. The only logical choice is to write code that itself comes close to this effect.

The visual appearance can be replicated by drawing a transparent window with a shadow border. The approach demonstrated in the source code below builds and configures QWidget

to make it behave and look like an auto-binding window:

enter image description here

This is not the most beautiful thing in the world, I know. This demo creates a regular window for user interaction, and once it is maximized, it is placed to the left of the screen. In the desired screen size, something similar to the auto-snap window (shown above) is displayed.

The idea of ​​the aero snap widget is very simple : a QWidget

with transparent background and custom drawing routine. In other words, it's a transparent window that draws a rounded rectangle with a drop shadow and that's it.



To make it more realistic, you must add animation to gradually resize the widget. A loop for

can do the trick, but if you need something, you end up using timers. If you look here , you can see the quickest and dirtiest way to do animations with Qt in action and the best ways to deal with animations. However, for simple tasks like this, stick to frame animation .

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

#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

#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 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 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 :

#pragma once
#include <QWidget>

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

    void paintEvent(QPaintEvent *event);
};

      

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 in the right direction. This is by no means a complete implementation of the effect you are looking for.

+4


source


This may not be what you want, but this effect just resizes and moves the window, then try using Qt methods to do that.

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 correctly on the screen with different resolutions.

On the internet I found something similar in winapi

, but it didn't work as expected:

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

      

The best way

Combine these 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 it move()

adjusts the left and right sides, but PostMessage

( winapi

) sets the window height correctly on each screen (the window will not be positioned lower than taskbar

in your example)

EDIT

I changed the code a bit and it's better now. Yes, it resizes again, but now it has no winapi code ( PostMessage

etc.), so Photoshop won't catch it, there is one interesting method in Qt called geometry available. It returns the normal screen height we want, with the borderless method perfectly mimicking the effects of Aero Snap in different directions. It may not work very well, but as I see there is no API for Aero effects. Perhaps this approach would be okay for yoo.

There is Aero Peek in Qt: http://qt-project.org/doc/qt-5/qtwinextras-overview.html , but it also can't solve this problem.

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 a frameless window! You must choose one direction or let the user choose one.

+3


source







All Articles