Программно вызвать Snap/Aero развернуть
Есть ли способ программно вызвать эффект максимизации Aera, используя C или C++ для определенного окна / идентификатора окна?
Например:
или же
http://www.thebuzzmedia.com/wp-content/uploads/2009/05/windows-7-aero-snap-to-edge-effect.png
Я использую окно без границ Qt, и у Qt есть API для получения идентификатора окна. Я хочу программно запускать эффекты Windows без известных триггеров.
2 ответа
Я не хочу говорить о каждой детали, связанной с достижением этого эффекта, не только многое происходит, но вы также упомянули, что понимаете логику размещения окон в их конкретных местах. В этом ответе я расскажу о двух основных проблемах, которые я считаю:
Как получить и обработать максимизировать событие?
Как создать приближение эффекта аэроснимка?
Чтобы ответить на первый вопрос, мы должны проанализировать, какие обработчики событий запускаются, когда окно развернуто:
void resizeEvent(QResizeEvent* evt); // Invoked first,
void paintEvent(QPaintEvent* event); // then second,
void changeEvent(QEvent* evt); // and at last.
Приложение Qt сначала уведомляется о resizeEvent()
, за которым следует paintEvent()
нарисовать окно (или виджет), и только после того, как все было отображено, changeEvent()
вызывается, чтобы вы знали, что виджет был развернут (возможно, немного поздно получить такое уведомление, я не знаю).
Единственное, о чем мы заботимся, resizeEvent()
, Этот обработчик события сообщает о новом размере окна / виджета, который можно использовать для сравнения с размером рабочего стола, что позволяет нам узнать, действительно ли событие было максимизированным запросом. Как только мы определим запрос на максимизацию, мы можем выяснить, должно ли приложение быть развернуто (и привязано) вправо, влево или к центру экрана.
Это было бы временем для создания виджета аэроснимков и размещения его на экране в качестве визуальной подсказки для пользователя.
Чтобы ответить на второй вопрос, я не думаю, что можно вызвать собственный API-интерфейс Windows и попросить его вежливо выполнить этот эффект в вашем окне. Единственный логичный выбор - написать код, который сам по себе аппроксимирует этот эффект.
Визуальный внешний вид можно воспроизвести, нарисовав прозрачное окно с рамкой тени. Подход, продемонстрированный в исходном коде ниже, создает и настраивает QWidget
чтобы заставить его вести себя и выглядеть как окно аэроснимка:
Я знаю, это не самая красивая вещь в мире. Эта демонстрация создает обычное окно для взаимодействия с пользователем, и, когда оно развернуто, оно располагается слева от экрана. По правому размеру экрана он отображает нечто, похожее на окно аэросъемки (показано выше).
Идея, лежащая в основе виджета " Аэро", очень проста: QWidget
с прозрачным фоном и пользовательской процедурой покраски. Другими словами, это прозрачное окно, которое рисует скругленный прямоугольник с тенью и все.
Чтобы сделать его более реалистичным, вы должны добавить анимацию, чтобы постепенно изменять размер виджета. for
Цикл может сделать свое дело, но если вам нужно что-то необычное, вы в конечном итоге использовать таймеры. Если вы посмотрите здесь, вы увидите самый быстрый и самый грязный метод для выполнения анимации с Qt в действии и лучшие способы борьбы с анимацией. Однако для простых задач, подобных этой, придерживайтесь покадровой анимации.
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'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:
#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);
}
Это просто быстрое демо, чтобы указать вам правильное направление. Это ни в коем случае не полная реализация эффекта, который вы ищете.
Возможно, это не то, что вам нужно, но этот эффект - просто изменение размера и перемещение окна, затем попробуйте использовать методы 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()
он будет работать правильно на экране с разными разрешениями.
В сети я нашел что-то похожее в 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 в разных направлениях. Это работает, может быть, не так хорошо, но, как я вижу, нет API для эффектов Aero. Может быть, такой подход будет нормальным для тебя.
В 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);
}
Попробуйте это с безрамным окном! Вы должны выбрать одно направление или позволить пользователю выбрать его.