refactoring
This commit is contained in:
@@ -0,0 +1,555 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Solutions component.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
/* */
|
||||
/* */
|
||||
/* File is originally from
|
||||
* https://github.com/qtproject/qt-solutions/tree/master/qtwinmigrate/src */
|
||||
/* */
|
||||
/* It has been modified to support borderless window (HTTRANSPARENT) & to remove
|
||||
* pre Qt5 cruft */
|
||||
/* */
|
||||
/* */
|
||||
|
||||
#include "QWinWidget.h"
|
||||
|
||||
#include <qt_windows.h>
|
||||
#include <QApplication>
|
||||
#include <QEvent>
|
||||
#include <QFocusEvent>
|
||||
#include <QWindow>
|
||||
|
||||
/*!
|
||||
\class QWinWidget qwinwidget.h
|
||||
\brief The QWinWidget class is a Qt widget that can be child of a
|
||||
native Win32 widget.
|
||||
|
||||
The QWinWidget class is the bridge between an existing application
|
||||
user interface developed using native Win32 APIs or toolkits like
|
||||
MFC, and Qt based GUI elements.
|
||||
|
||||
Using QWinWidget as the parent of QDialogs will ensure that
|
||||
modality, placement and stacking works properly throughout the
|
||||
entire application. If the child widget is a top level window that
|
||||
uses the \c WDestructiveClose flag, QWinWidget will destroy itself
|
||||
when the child window closes down.
|
||||
|
||||
Applications moving to Qt can use QWinWidget to add new
|
||||
functionality, and gradually replace the existing interface.
|
||||
*/
|
||||
|
||||
QWinWidget::QWinWidget()
|
||||
: QWidget(nullptr)
|
||||
, m_Layout()
|
||||
, p_Widget(nullptr)
|
||||
, m_ParentNativeWindowHandle(nullptr)
|
||||
, _prevFocus(nullptr)
|
||||
, _reenableParent(false)
|
||||
{
|
||||
// Create a native window and give it geometry values * devicePixelRatio for
|
||||
// HiDPI support
|
||||
p_ParentWinNativeWindow = new WinNativeWindow(
|
||||
1 * window()->devicePixelRatio(), 1 * window()->devicePixelRatio(),
|
||||
1 * window()->devicePixelRatio(), 1 * window()->devicePixelRatio());
|
||||
|
||||
// If you want to set a minimize size for your app, do so here
|
||||
// p_ParentWinNativeWindow->setMinimumSize(1024 *
|
||||
// window()->devicePixelRatio(), 768 * window()->devicePixelRatio());
|
||||
|
||||
// If you want to set a maximum size for your app, do so here
|
||||
// p_ParentWinNativeWindow->setMaximumSize(1024 *
|
||||
// window()->devicePixelRatio(), 768 * window()->devicePixelRatio());
|
||||
|
||||
// Save the native window handle for shorthand use
|
||||
m_ParentNativeWindowHandle = p_ParentWinNativeWindow->hWnd;
|
||||
Q_ASSERT(m_ParentNativeWindowHandle);
|
||||
|
||||
// Create the child window & embed it into the native one
|
||||
if (m_ParentNativeWindowHandle) {
|
||||
SetWindowLong((HWND)winId(), GWL_STYLE,
|
||||
WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
|
||||
QWindow *window = windowHandle();
|
||||
window->setProperty("_q_embedded_native_parent_handle",
|
||||
(WId)m_ParentNativeWindowHandle);
|
||||
|
||||
SetParent((HWND)winId(), m_ParentNativeWindowHandle);
|
||||
window->setFlags(Qt::FramelessWindowHint);
|
||||
QEvent e(QEvent::EmbeddingControl);
|
||||
QApplication::sendEvent(this, &e);
|
||||
}
|
||||
|
||||
// Pass along our window handle & widget pointer to WinFramelessWidget so we
|
||||
// can exchange messages
|
||||
p_ParentWinNativeWindow->childWindow = (HWND)winId();
|
||||
p_ParentWinNativeWindow->childWidget = this;
|
||||
|
||||
// Clear margins & spacing & add the layout to prepare for the MainAppWidget
|
||||
setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(&m_Layout);
|
||||
m_Layout.setContentsMargins(0, 0, 0, 0);
|
||||
m_Layout.setSpacing(0);
|
||||
|
||||
// Create the true app widget
|
||||
// p_Widget = new Widget(this);
|
||||
// m_Layout.addWidget(p_Widget);
|
||||
// p_Widget->setParent(this, Qt::Widget);
|
||||
// p_Widget->setVisible(true);
|
||||
|
||||
// Update the BORDERWIDTH value if needed for HiDPI displays
|
||||
BORDERWIDTH = BORDERWIDTH * window()->devicePixelRatio();
|
||||
|
||||
// Update the TOOLBARHEIGHT value to match the height of toolBar * if
|
||||
// needed, the HiDPI display
|
||||
// if (p_Widget->toolBar) {
|
||||
// TOOLBARHEIGHT =
|
||||
// p_Widget->toolBar->height() * window()->devicePixelRatio();
|
||||
// }
|
||||
|
||||
// You need to keep the native window in sync with the Qt window & children,
|
||||
// so wire min/max/close buttons to
|
||||
// slots inside of QWinWidget. QWinWidget can then talk with the native
|
||||
// window as needed
|
||||
// if (p_Widget->minimizeButton) {
|
||||
// connect(p_Widget->minimizeButton, &QPushButton::clicked, this,
|
||||
// &QWinWidget::onMinimizeButtonClicked);
|
||||
// }
|
||||
// if (p_Widget->maximizeButton) {
|
||||
// connect(p_Widget->maximizeButton, &QPushButton::clicked, this,
|
||||
// &QWinWidget::onMaximizeButtonClicked);
|
||||
// }
|
||||
// if (p_Widget->closeButton) {
|
||||
// connect(p_Widget->closeButton, &QPushButton::clicked, this,
|
||||
// &QWinWidget::onCloseButtonClicked);
|
||||
// }
|
||||
|
||||
// Send the parent native window a WM_SIZE message to update the widget size
|
||||
SendMessage(m_ParentNativeWindowHandle, WM_SIZE, 0, 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys this object, freeing all allocated resources.
|
||||
*/
|
||||
QWinWidget::~QWinWidget()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the handle of the native Win32 parent window.
|
||||
*/
|
||||
HWND
|
||||
QWinWidget::getParentWindow() const
|
||||
{
|
||||
return m_ParentNativeWindowHandle;
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
void
|
||||
QWinWidget::childEvent(QChildEvent *e)
|
||||
{
|
||||
QObject *obj = e->child();
|
||||
if (obj->isWidgetType()) {
|
||||
if (e->added()) {
|
||||
if (obj->isWidgetType()) {
|
||||
obj->installEventFilter(this);
|
||||
}
|
||||
} else if (e->removed() && _reenableParent) {
|
||||
_reenableParent = false;
|
||||
EnableWindow(m_ParentNativeWindowHandle, true);
|
||||
obj->removeEventFilter(this);
|
||||
}
|
||||
}
|
||||
QWidget::childEvent(e);
|
||||
}
|
||||
|
||||
/*! \internal */
|
||||
void
|
||||
QWinWidget::saveFocus()
|
||||
{
|
||||
if (!_prevFocus)
|
||||
_prevFocus = ::GetFocus();
|
||||
if (!_prevFocus)
|
||||
_prevFocus = getParentWindow();
|
||||
}
|
||||
|
||||
/*!
|
||||
Shows this widget. Overrides QWidget::show().
|
||||
|
||||
\sa showCentered()
|
||||
*/
|
||||
void
|
||||
QWinWidget::show()
|
||||
{
|
||||
ShowWindow(m_ParentNativeWindowHandle, true);
|
||||
saveFocus();
|
||||
QWidget::show();
|
||||
}
|
||||
|
||||
/*!
|
||||
Centers this widget over the native parent window. Use this
|
||||
function to have Qt toplevel windows (i.e. dialogs) positioned
|
||||
correctly over their native parent windows.
|
||||
|
||||
\code
|
||||
QWinWidget qwin(hParent);
|
||||
qwin.center();
|
||||
|
||||
QMessageBox::information(&qwin, "Caption", "Information Text");
|
||||
\endcode
|
||||
|
||||
This will center the message box over the client area of hParent.
|
||||
*/
|
||||
void
|
||||
QWinWidget::center()
|
||||
{
|
||||
const QWidget *child = findChild<QWidget *>();
|
||||
if (child && !child->isWindow()) {
|
||||
qWarning("QWinWidget::center: Call this function only for QWinWidgets "
|
||||
"with toplevel children");
|
||||
}
|
||||
RECT r;
|
||||
GetWindowRect(m_ParentNativeWindowHandle, &r);
|
||||
setGeometry((r.right - r.left) / 2 + r.left, (r.bottom - r.top) / 2 + r.top,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
\obsolete
|
||||
|
||||
Call center() instead.
|
||||
*/
|
||||
void
|
||||
QWinWidget::showCentered()
|
||||
{
|
||||
center();
|
||||
show();
|
||||
}
|
||||
|
||||
void
|
||||
QWinWidget::setGeometry(int x, int y, int w, int h)
|
||||
{
|
||||
p_ParentWinNativeWindow->setGeometry(
|
||||
x * window()->devicePixelRatio(), y * window()->devicePixelRatio(),
|
||||
w * window()->devicePixelRatio(), h * window()->devicePixelRatio());
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the focus to the window that had the focus before this widget
|
||||
was shown, or if there was no previous window, sets the focus to
|
||||
the parent window.
|
||||
*/
|
||||
void
|
||||
QWinWidget::resetFocus()
|
||||
{
|
||||
if (_prevFocus)
|
||||
::SetFocus(_prevFocus);
|
||||
else
|
||||
::SetFocus(getParentWindow());
|
||||
}
|
||||
|
||||
// Tell the parent native window to minimize
|
||||
void
|
||||
QWinWidget::onMinimizeButtonClicked()
|
||||
{
|
||||
SendMessage(m_ParentNativeWindowHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
|
||||
}
|
||||
|
||||
// Tell the parent native window to maximize or restore as appropriate
|
||||
void
|
||||
QWinWidget::onMaximizeButtonClicked()
|
||||
{
|
||||
if (p_Widget->maximizeButton->isChecked()) {
|
||||
SendMessage(m_ParentNativeWindowHandle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
|
||||
} else {
|
||||
SendMessage(m_ParentNativeWindowHandle, WM_SYSCOMMAND, SC_RESTORE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QWinWidget::onCloseButtonClicked()
|
||||
{
|
||||
if (true /* put your check for it if it safe to close your app here */) // eg, does the user need to save a document
|
||||
{
|
||||
// Safe to close, so hide the parent window
|
||||
ShowWindow(m_ParentNativeWindowHandle, false);
|
||||
|
||||
// And then quit
|
||||
QApplication::quit();
|
||||
} else {
|
||||
// Do nothing, and thus, don't actually close the window
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
QWinWidget::nativeEvent(const QByteArray &, void *message, long *result)
|
||||
{
|
||||
MSG *msg = (MSG *)message;
|
||||
|
||||
if (msg->message == WM_SETFOCUS) {
|
||||
Qt::FocusReason reason;
|
||||
if (::GetKeyState(VK_LBUTTON) < 0 || ::GetKeyState(VK_RBUTTON) < 0)
|
||||
reason = Qt::MouseFocusReason;
|
||||
else if (::GetKeyState(VK_SHIFT) < 0)
|
||||
reason = Qt::BacktabFocusReason;
|
||||
else
|
||||
reason = Qt::TabFocusReason;
|
||||
QFocusEvent e(QEvent::FocusIn, reason);
|
||||
QApplication::sendEvent(this, &e);
|
||||
}
|
||||
|
||||
// Only close if safeToClose clears()
|
||||
if (msg->message == WM_CLOSE) {
|
||||
if (true /* put your check for it if it safe to close your app here */) // eg, does the user need to save a document
|
||||
{
|
||||
// Safe to close, so hide the parent window
|
||||
ShowWindow(m_ParentNativeWindowHandle, false);
|
||||
|
||||
// And then quit
|
||||
QApplication::quit();
|
||||
} else {
|
||||
*result = 0; // Set the message to 0 to ignore it, and thus, don't
|
||||
// actually close
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Double check WM_SIZE messages to see if the parent native window is
|
||||
// maximized
|
||||
if (msg->message == WM_SIZE) {
|
||||
if (p_Widget && p_Widget->maximizeButton) {
|
||||
// Get the window state
|
||||
WINDOWPLACEMENT wp;
|
||||
GetWindowPlacement(m_ParentNativeWindowHandle, &wp);
|
||||
|
||||
// If we're maximized,
|
||||
if (wp.showCmd == SW_MAXIMIZE) {
|
||||
// Maximize button should show as Restore
|
||||
p_Widget->maximizeButton->setChecked(true);
|
||||
} else {
|
||||
// Maximize button should show as Maximize
|
||||
p_Widget->maximizeButton->setChecked(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass NCHITTESTS on the window edges as determined by BORDERWIDTH &
|
||||
// TOOLBARHEIGHT through to the parent native window
|
||||
if (msg->message == WM_NCHITTEST) {
|
||||
RECT WindowRect;
|
||||
int x, y;
|
||||
|
||||
GetWindowRect(msg->hwnd, &WindowRect);
|
||||
x = GET_X_LPARAM(msg->lParam) - WindowRect.left;
|
||||
y = GET_Y_LPARAM(msg->lParam) - WindowRect.top;
|
||||
|
||||
if (x >= BORDERWIDTH &&
|
||||
x <= WindowRect.right - WindowRect.left - BORDERWIDTH &&
|
||||
y >= BORDERWIDTH && y <= TOOLBARHEIGHT) {
|
||||
if (false) { // if (p_Widget->toolBar) {
|
||||
// If the mouse is over top of the toolbar area BUT is actually
|
||||
// positioned over a child widget of the toolbar,
|
||||
// Then we don't want to enable dragging. This allows for
|
||||
// buttons in the toolbar, eg, a Maximize button, to keep the
|
||||
// mouse interaction
|
||||
if (QApplication::widgetAt(QCursor::pos()) != p_Widget->toolBar)
|
||||
return false;
|
||||
} else {
|
||||
// The mouse is over the toolbar area & is NOT over a child of
|
||||
// the toolbar, so pass this message
|
||||
// through to the native window for HTCAPTION dragging
|
||||
*result = HTTRANSPARENT;
|
||||
return true;
|
||||
}
|
||||
} else if (x < BORDERWIDTH && y < BORDERWIDTH) {
|
||||
*result = HTTRANSPARENT;
|
||||
return true;
|
||||
} else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH &&
|
||||
y < BORDERWIDTH) {
|
||||
*result = HTTRANSPARENT;
|
||||
return true;
|
||||
} else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH &&
|
||||
y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) {
|
||||
*result = HTTRANSPARENT;
|
||||
return true;
|
||||
} else if (x < BORDERWIDTH &&
|
||||
y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) {
|
||||
*result = HTTRANSPARENT;
|
||||
return true;
|
||||
} else if (x < BORDERWIDTH) {
|
||||
*result = HTTRANSPARENT;
|
||||
return true;
|
||||
} else if (y < BORDERWIDTH) {
|
||||
*result = HTTRANSPARENT;
|
||||
return true;
|
||||
} else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH) {
|
||||
*result = HTTRANSPARENT;
|
||||
return true;
|
||||
} else if (y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) {
|
||||
*result = HTTRANSPARENT;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
bool
|
||||
QWinWidget::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
QWidget *w = (QWidget *)o;
|
||||
|
||||
switch (e->type()) {
|
||||
case QEvent::WindowDeactivate:
|
||||
if (w->isModal() && w->isHidden())
|
||||
BringWindowToTop(m_ParentNativeWindowHandle);
|
||||
break;
|
||||
|
||||
case QEvent::Hide:
|
||||
if (_reenableParent) {
|
||||
EnableWindow(m_ParentNativeWindowHandle, true);
|
||||
_reenableParent = false;
|
||||
}
|
||||
resetFocus();
|
||||
|
||||
if (w->testAttribute(Qt::WA_DeleteOnClose) && w->isWindow())
|
||||
deleteLater();
|
||||
break;
|
||||
|
||||
case QEvent::Show:
|
||||
if (w->isWindow()) {
|
||||
saveFocus();
|
||||
hide();
|
||||
if (w->isModal() && !_reenableParent) {
|
||||
EnableWindow(m_ParentNativeWindowHandle, false);
|
||||
_reenableParent = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case QEvent::Close: {
|
||||
::SetActiveWindow(m_ParentNativeWindowHandle);
|
||||
if (w->testAttribute(Qt::WA_DeleteOnClose))
|
||||
deleteLater();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QWidget::eventFilter(o, e);
|
||||
}
|
||||
|
||||
/*! \reimp
|
||||
*/
|
||||
void
|
||||
QWinWidget::focusInEvent(QFocusEvent *e)
|
||||
{
|
||||
QWidget *candidate = this;
|
||||
|
||||
switch (e->reason()) {
|
||||
case Qt::TabFocusReason:
|
||||
case Qt::BacktabFocusReason:
|
||||
while (!(candidate->focusPolicy() & Qt::TabFocus)) {
|
||||
candidate = candidate->nextInFocusChain();
|
||||
if (candidate == this) {
|
||||
candidate = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (candidate) {
|
||||
candidate->setFocus(e->reason());
|
||||
if (e->reason() == Qt::BacktabFocusReason ||
|
||||
e->reason() == Qt::TabFocusReason) {
|
||||
candidate->setAttribute(Qt::WA_KeyboardFocusChange);
|
||||
candidate->window()->setAttribute(
|
||||
Qt::WA_KeyboardFocusChange);
|
||||
}
|
||||
if (e->reason() == Qt::BacktabFocusReason)
|
||||
QWidget::focusNextPrevChild(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \reimp
|
||||
*/
|
||||
bool
|
||||
QWinWidget::focusNextPrevChild(bool next)
|
||||
{
|
||||
QWidget *curFocus = focusWidget();
|
||||
if (!next) {
|
||||
if (!curFocus->isWindow()) {
|
||||
QWidget *nextFocus = curFocus->nextInFocusChain();
|
||||
QWidget *prevFocus = 0;
|
||||
QWidget *topLevel = 0;
|
||||
while (nextFocus != curFocus) {
|
||||
if (nextFocus->focusPolicy() & Qt::TabFocus) {
|
||||
prevFocus = nextFocus;
|
||||
topLevel = 0;
|
||||
}
|
||||
nextFocus = nextFocus->nextInFocusChain();
|
||||
}
|
||||
|
||||
if (!topLevel) {
|
||||
return QWidget::focusNextPrevChild(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QWidget *nextFocus = curFocus;
|
||||
while (1 && nextFocus != 0) {
|
||||
nextFocus = nextFocus->nextInFocusChain();
|
||||
if (nextFocus->focusPolicy() & Qt::TabFocus) {
|
||||
return QWidget::focusNextPrevChild(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::SetFocus(m_ParentNativeWindowHandle);
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user