Очень интересная задача попалась мне на днях. Казалось бы, совершенно обыкновенное действие — дать пользователю возможность только для пропорционального изменения размеров окна. Естественно речь идет в первую очередь о создании приложений с использованием Qt, поэтому я не долго думая сразу же отправил читать ассистант и ловить ResizeEvent.
Однако, когда результат оказался отрицательным, я, дабы почувствовать себя Великим Разумом полез в ассистант сам. Как минимум, ResizeEvent в виджетах возникает уже после изменения его в размерах. Кроме фиксирования размера, никаких других средств запретить пользователю менять размер «не по нашему» найти не удалось. Точнее удалось, но найденный «вкусный» вариант уже имеющихся кутэшных функций оказался не работающим на Win платформе.
Пришлось копать далее. Дальнейший поиск привел к тому, что данный вопрос придется решать на уровне системных API. Исследуя «вопли раненых», ищущих то же, что и мы, было обнаружено, что нужное нам событие — это некий WM_SIZING. Что ж, далее все очень просто.
Данное сообщение несет в себе прямоугольник, которое должно занять наше окно, но главная особенно заключается в том, что мы пока еще ничего не сделали, а значит может подредактировать эти значения и отправить дальше уже нужный нам размер.
Говоря об окнах, не стоит забывать о том, что системное сообщение дает нам размеры всего окошка, включая рамку и заголовок, которые нам при учете пропорций совершенно не нужны, поэтому не стоит забывать об этом и вовремя убирать лишние пиксели при рассчетах.
Ловить необходимое сообщение нужно в событии bool winEvent( MSG * message, long * result );. Не забывайте, что это не кроссплатформенное решение.
Вкратце это все, ниже вы можете найти исходный код для создания такого виджета. А пока можете взглянуть на результат:
Как и обещал, исходный код. Ключевые места с комментариями на русском.
Заголовок класса:
//----------------------------------------------------------------------------- // File: a_utest_proportions.h // // Desc: //----------------------------------------------------------------------------- #ifndef A_UTEST_WIDGET_H #define A_UTEST_WIDGET_H #include <QWidget> //-------------------------------------------------------------------------------------- // Name: cUProportionTestWidget // Desc: Testing proportional resize on Win platform //-------------------------------------------------------------------------------------- class cUProportionTestWidget : public QWidget { Q_OBJECT public: explicit cUProportionTestWidget(QWidget *parent = 0); private: // Data for resize QSize m_actualSize; qreal m_scaleParamWtoH; protected: // Events reimplementations //---------------------------------------------------------------------------------- bool winEvent( MSG * message, long * result ); }; #endif // A_UTEST_WIDGET_H
Реализация:
//----------------------------------------------------------------------------- // File: a_utest_proportions.cpp // // Desc: //----------------------------------------------------------------------------- #include "a_utest_proportions.h" // for winEvent #include <qt_windows.h> //-------------------------------------------------------------------------------------- // cUProportionTestWidget class constructor //-------------------------------------------------------------------------------------- cUProportionTestWidget::cUProportionTestWidget(QWidget *parent) : QWidget(parent) { // Set main options m_actualSize = QSize(320, 240); // 4 x 3 m_scaleParamWtoH = (qreal)m_actualSize.width() / m_actualSize.height(); // for testing setMinimumSize(m_actualSize); } //============================================================================= // EVENTS //============================================================================= //-------------------------------------------------------------------------------------- bool cUProportionTestWidget::winEvent( MSG * message, long * result ) { switch(message->message) { case (WM_SIZING): { // Получаем прямоугольник окна, которым мы должны стать RECT* rect = (RECT*) message->lParam; // Вычленяем размеры рабочей области int fWidth = frameGeometry().width() - width(); int fHeight = frameGeometry().height() - height(); int nWidth = rect->right-rect->left - fWidth; int nHeight = rect->bottom-rect->top - fHeight; // Меняем размеры на нужные нам switch(message->wParam) { case WMSZ_BOTTOM: case WMSZ_TOP: rect->right = rect->left+(qreal)nHeight*m_scaleParamWtoH + fWidth; break; case WMSZ_BOTTOMLEFT: case WMSZ_BOTTOMRIGHT: if( (qreal)nWidth / nHeight > m_scaleParamWtoH ) rect->bottom = rect->top+(qreal)nWidth/m_scaleParamWtoH +fHeight; else rect->right = rect->left+(qreal)nHeight*m_scaleParamWtoH + fWidth; break; case WMSZ_LEFT: case WMSZ_RIGHT: rect->bottom = rect->top+(qreal)nWidth/m_scaleParamWtoH +fHeight; break; case WMSZ_TOPLEFT: case WMSZ_TOPRIGHT: if( (qreal)nWidth / nHeight > m_scaleParamWtoH ) rect->bottom = rect->top+(qreal)nWidth/m_scaleParamWtoH +fHeight; else rect->right = rect->left+(qreal)nHeight*m_scaleParamWtoH + fWidth; break; } } return true; default: return false; }; }