mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
fixed text selection
This commit is contained in:
parent
1133b33318
commit
a190eda075
20 changed files with 278 additions and 1052 deletions
|
@ -258,14 +258,6 @@ win32 {
|
|||
-ldwmapi \
|
||||
-lgdi32
|
||||
|
||||
# SOURCES += platform/borderless/qwinwidget.cpp \
|
||||
# platform/borderless/winnativewindow.cpp \
|
||||
# platform/borderless/widget.cpp
|
||||
|
||||
# HEADERS += platform/borderless/qwinwidget.h \
|
||||
# platform/borderless/winnativewindow.h \
|
||||
# platform/borderless/widget.h
|
||||
|
||||
DEFINES += "USEWINSDK"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,7 +160,7 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection
|
|||
this->bufferValid = false;
|
||||
}
|
||||
|
||||
if (!this->bufferValid) {
|
||||
if (!this->bufferValid || !selection.isEmpty()) {
|
||||
this->updateBuffer(pixmap, messageIndex, selection);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ int MessageLayoutContainer::getHeight() const
|
|||
void MessageLayoutContainer::clear()
|
||||
{
|
||||
this->elements.clear();
|
||||
this->lines.clear();
|
||||
|
||||
this->height = 0;
|
||||
this->line = 0;
|
||||
|
@ -36,6 +37,7 @@ void MessageLayoutContainer::clear()
|
|||
this->currentY = 0;
|
||||
this->lineStart = 0;
|
||||
this->lineHeight = 0;
|
||||
this->charIndex = 0;
|
||||
}
|
||||
|
||||
void MessageLayoutContainer::addElement(MessageLayoutElement *element)
|
||||
|
@ -106,7 +108,16 @@ void MessageLayoutContainer::breakLine()
|
|||
element->getRect().y() + this->lineHeight + yExtra));
|
||||
}
|
||||
|
||||
this->lines.push_back({(int)lineStart, QRect(0, this->currentY, this->width, lineHeight)});
|
||||
if (this->lines.size() != 0) {
|
||||
this->lines.back().endIndex = this->lineStart;
|
||||
this->lines.back().endCharIndex = this->charIndex;
|
||||
}
|
||||
this->lines.push_back({(int)lineStart, 0, this->charIndex, 0,
|
||||
QRect(-100000, this->currentY, 200000, lineHeight)});
|
||||
|
||||
for (int i = this->lineStart; i < this->elements.size(); i++) {
|
||||
this->charIndex += this->elements[i]->getSelectionIndexCount();
|
||||
}
|
||||
|
||||
this->lineStart = this->elements.size();
|
||||
this->currentX = 0;
|
||||
|
@ -132,8 +143,10 @@ void MessageLayoutContainer::finish()
|
|||
}
|
||||
|
||||
if (this->lines.size() != 0) {
|
||||
this->lines[0].rect.setTop(0);
|
||||
this->lines.back().rect.setBottom(this->height);
|
||||
this->lines[0].rect.setTop(-100000);
|
||||
this->lines.back().rect.setBottom(100000);
|
||||
this->lines.back().endIndex = this->elements.size();
|
||||
this->lines.back().endCharIndex = this->charIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,6 +179,166 @@ void MessageLayoutContainer::paintAnimatedElements(QPainter &painter, int yOffse
|
|||
void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex,
|
||||
Selection &selection)
|
||||
{
|
||||
// don't draw anything
|
||||
if (selection.min.messageIndex > messageIndex || selection.max.messageIndex < messageIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fully selected
|
||||
if (selection.min.messageIndex < messageIndex && selection.max.messageIndex > messageIndex) {
|
||||
for (Line &line : this->lines) {
|
||||
QRect rect = line.rect;
|
||||
|
||||
rect.setTop(std::max(0, rect.top()));
|
||||
rect.setBottom(std::min(this->height, rect.bottom()));
|
||||
rect.setLeft(this->elements[line.startIndex]->getRect().left());
|
||||
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
|
||||
|
||||
painter.fillRect(rect, QColor(255, 255, 0, 127));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int lineIndex = 0;
|
||||
int index = 0;
|
||||
|
||||
// start in this message
|
||||
if (selection.min.messageIndex == messageIndex) {
|
||||
for (; lineIndex < this->lines.size(); lineIndex++) {
|
||||
Line &line = this->lines[lineIndex];
|
||||
index = line.startCharIndex;
|
||||
|
||||
bool returnAfter = false;
|
||||
bool breakAfter = false;
|
||||
int x = this->elements[line.startIndex]->getRect().left();
|
||||
int r = this->elements[line.endIndex - 1]->getRect().right();
|
||||
|
||||
if (line.endCharIndex < selection.min.charIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = line.startIndex; i < line.endIndex; i++) {
|
||||
int c = this->elements[i]->getSelectionIndexCount();
|
||||
|
||||
if (index + c > selection.min.charIndex) {
|
||||
x = this->elements[i]->getXFromIndex(selection.min.charIndex - index);
|
||||
|
||||
// ends in same line
|
||||
if (selection.max.messageIndex == messageIndex &&
|
||||
line.endCharIndex > /*=*/selection.max.charIndex) //
|
||||
{
|
||||
returnAfter = true;
|
||||
index = line.startCharIndex;
|
||||
for (int i = line.startIndex; i < line.endIndex; i++) {
|
||||
int c = this->elements[i]->getSelectionIndexCount();
|
||||
|
||||
if (index + c > selection.max.charIndex) {
|
||||
r = this->elements[i]->getXFromIndex(selection.max.charIndex -
|
||||
index);
|
||||
break;
|
||||
}
|
||||
index += c;
|
||||
}
|
||||
}
|
||||
// ends in same line end
|
||||
|
||||
if (selection.max.messageIndex != messageIndex) {
|
||||
int lineIndex2 = lineIndex + 1;
|
||||
for (; lineIndex2 < this->lines.size(); lineIndex2++) {
|
||||
Line &line = this->lines[lineIndex2];
|
||||
QRect rect = line.rect;
|
||||
|
||||
rect.setTop(std::max(0, rect.top()));
|
||||
rect.setBottom(std::min(this->height, rect.bottom()));
|
||||
rect.setLeft(this->elements[line.startIndex]->getRect().left());
|
||||
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
|
||||
|
||||
painter.fillRect(rect, QColor(255, 255, 0, 127));
|
||||
}
|
||||
returnAfter = true;
|
||||
} else {
|
||||
lineIndex++;
|
||||
breakAfter = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
index += c;
|
||||
}
|
||||
|
||||
QRect rect = line.rect;
|
||||
|
||||
rect.setTop(std::max(0, rect.top()));
|
||||
rect.setBottom(std::min(this->height, rect.bottom()));
|
||||
rect.setLeft(x);
|
||||
rect.setRight(r);
|
||||
|
||||
painter.fillRect(rect, QColor(255, 255, 0, 127));
|
||||
|
||||
if (returnAfter) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (breakAfter) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// start in this message
|
||||
for (; lineIndex < this->lines.size(); lineIndex++) {
|
||||
Line &line = this->lines[lineIndex];
|
||||
index = line.startCharIndex;
|
||||
bool breakAfter = false;
|
||||
|
||||
// just draw the garbage
|
||||
if (line.endCharIndex < /*=*/selection.max.charIndex) {
|
||||
QRect rect = line.rect;
|
||||
|
||||
rect.setTop(std::max(0, rect.top()));
|
||||
rect.setBottom(std::min(this->height, rect.bottom()));
|
||||
rect.setLeft(this->elements[line.startIndex]->getRect().left());
|
||||
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
|
||||
|
||||
painter.fillRect(rect, QColor(255, 255, 0, 127));
|
||||
continue;
|
||||
}
|
||||
|
||||
int r = this->elements[line.endIndex - 1]->getRect().right();
|
||||
|
||||
for (int i = line.startIndex; i < line.endIndex; i++) {
|
||||
int c = this->elements[i]->getSelectionIndexCount();
|
||||
|
||||
if (index + c > selection.max.charIndex) {
|
||||
r = this->elements[i]->getXFromIndex(selection.max.charIndex - index);
|
||||
break;
|
||||
}
|
||||
|
||||
index += c;
|
||||
}
|
||||
|
||||
QRect rect = line.rect;
|
||||
|
||||
rect.setTop(std::max(0, rect.top()));
|
||||
rect.setBottom(std::min(this->height, rect.bottom()));
|
||||
rect.setLeft(this->elements[line.startIndex]->getRect().left());
|
||||
rect.setRight(r);
|
||||
|
||||
painter.fillRect(rect, QColor(255, 255, 0, 127));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageLayoutContainer::_paintLine(QPainter &painter, Line &line, int x, int y)
|
||||
{
|
||||
QRect rect = line.rect;
|
||||
|
||||
rect.setTop(std::max(0, rect.top()));
|
||||
rect.setBottom(std::min(this->height, rect.bottom()));
|
||||
rect.setLeft(this->elements[line.startIndex]->getRect().left());
|
||||
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
|
||||
|
||||
painter.fillRect(rect, QColor(255, 255, 0, 127));
|
||||
}
|
||||
|
||||
// selection
|
||||
|
@ -183,10 +356,10 @@ int MessageLayoutContainer::getSelectionIndex(QPoint point)
|
|||
}
|
||||
}
|
||||
|
||||
assert(line != this->lines.end());
|
||||
|
||||
int lineStart = line->startIndex;
|
||||
int lineStart = line == this->lines.end() ? this->lines.back().startIndex : line->startIndex;
|
||||
if (line != this->lines.end()) {
|
||||
line++;
|
||||
}
|
||||
int lineEnd = line == this->lines.end() ? this->elements.size() : line->startIndex;
|
||||
|
||||
int index = 0;
|
||||
|
@ -204,10 +377,12 @@ int MessageLayoutContainer::getSelectionIndex(QPoint point)
|
|||
}
|
||||
|
||||
// this is the word
|
||||
if (point.x() > this->elements[i]->getRect().left()) {
|
||||
if (point.x() < this->elements[i]->getRect().right()) {
|
||||
index += this->elements[i]->getMouseOverIndex(point);
|
||||
break;
|
||||
}
|
||||
|
||||
index += this->elements[i]->getSelectionIndexCount();
|
||||
}
|
||||
|
||||
qDebug() << index;
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
#include <QPoint>
|
||||
#include <QRect>
|
||||
|
||||
#include "messages/selection.hpp"
|
||||
|
||||
class QPainter;
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
class Selection;
|
||||
|
||||
namespace layouts {
|
||||
class MessageLayoutElement;
|
||||
|
@ -72,23 +73,28 @@ public:
|
|||
int getSelectionIndex(QPoint point);
|
||||
|
||||
private:
|
||||
struct Line {
|
||||
int startIndex;
|
||||
int endIndex;
|
||||
int startCharIndex;
|
||||
int endCharIndex;
|
||||
QRect rect;
|
||||
};
|
||||
|
||||
// helpers
|
||||
void _addElement(MessageLayoutElement *element);
|
||||
void _paintLine(QPainter &painter, Line &line, int x, int y);
|
||||
|
||||
// variables
|
||||
int line;
|
||||
int height;
|
||||
int currentX, currentY;
|
||||
int charIndex = 0;
|
||||
size_t lineStart = 0;
|
||||
int lineHeight = 0;
|
||||
int spaceWidth = 4;
|
||||
std::vector<std::unique_ptr<MessageLayoutElement>> elements;
|
||||
|
||||
struct Line {
|
||||
int startIndex;
|
||||
QRect rect;
|
||||
};
|
||||
|
||||
std::vector<Line> lines;
|
||||
};
|
||||
} // namespace layouts
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "messages/layouts/messagelayoutelement.hpp"
|
||||
#include "messages/messageelement.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
|
||||
namespace chatterino {
|
||||
|
@ -88,6 +89,18 @@ int ImageLayoutElement::getMouseOverIndex(const QPoint &abs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int ImageLayoutElement::getXFromIndex(int index)
|
||||
{
|
||||
if (index <= 0) {
|
||||
return this->getRect().left();
|
||||
} else if (index == 1) {
|
||||
// fourtf: remove space width
|
||||
return this->getRect().right();
|
||||
} else {
|
||||
return this->getRect().right();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TEXT
|
||||
//
|
||||
|
@ -127,8 +140,45 @@ void TextLayoutElement::paintAnimated(QPainter &, int)
|
|||
|
||||
int TextLayoutElement::getMouseOverIndex(const QPoint &abs)
|
||||
{
|
||||
if (abs.x() < this->getRect().left()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QFontMetrics &metrics =
|
||||
singletons::FontManager::getInstance().getFontMetrics(this->style, this->scale);
|
||||
|
||||
int x = this->getRect().left();
|
||||
|
||||
for (int i = 0; i < this->text.size(); i++) {
|
||||
int w = metrics.width(this->text[i]);
|
||||
|
||||
if (x + w > abs.x()) {
|
||||
return i;
|
||||
}
|
||||
|
||||
x += w;
|
||||
}
|
||||
|
||||
return this->getSelectionIndexCount();
|
||||
}
|
||||
|
||||
int TextLayoutElement::getXFromIndex(int index)
|
||||
{
|
||||
QFontMetrics &metrics =
|
||||
singletons::FontManager::getInstance().getFontMetrics(this->style, this->scale);
|
||||
|
||||
if (index <= 0) {
|
||||
return this->getRect().left();
|
||||
} else if (index < this->text.size()) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < index; i++) {
|
||||
x += metrics.width(this->text[i]);
|
||||
}
|
||||
return x + this->getRect().left();
|
||||
} else {
|
||||
return this->getRect().right();
|
||||
}
|
||||
}
|
||||
} // namespace layouts
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -36,6 +36,7 @@ public:
|
|||
virtual void paint(QPainter &painter) = 0;
|
||||
virtual void paintAnimated(QPainter &painter, int yOffset) = 0;
|
||||
virtual int getMouseOverIndex(const QPoint &abs) = 0;
|
||||
virtual int getXFromIndex(int index) = 0;
|
||||
|
||||
protected:
|
||||
bool trailingSpace = true;
|
||||
|
@ -58,6 +59,7 @@ protected:
|
|||
virtual void paint(QPainter &painter) override;
|
||||
virtual void paintAnimated(QPainter &painter, int yOffset) override;
|
||||
virtual int getMouseOverIndex(const QPoint &abs) override;
|
||||
virtual int getXFromIndex(int index) override;
|
||||
|
||||
private:
|
||||
Image ℑ
|
||||
|
@ -76,6 +78,7 @@ protected:
|
|||
virtual void paint(QPainter &painter) override;
|
||||
virtual void paintAnimated(QPainter &painter, int yOffset) override;
|
||||
virtual int getMouseOverIndex(const QPoint &abs) override;
|
||||
virtual int getXFromIndex(int index) override;
|
||||
|
||||
private:
|
||||
QString text;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <QTime>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <util/emotemap.hpp>
|
||||
#include "util/emotemap.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
class Channel;
|
||||
|
|
|
@ -8,24 +8,36 @@ struct SelectionItem {
|
|||
|
||||
SelectionItem()
|
||||
{
|
||||
messageIndex = charIndex = 0;
|
||||
this->messageIndex = 0;
|
||||
this->charIndex = 0;
|
||||
}
|
||||
|
||||
SelectionItem(int _messageIndex, int _charIndex)
|
||||
{
|
||||
this->messageIndex = _messageIndex;
|
||||
|
||||
this->charIndex = _charIndex;
|
||||
}
|
||||
|
||||
bool isSmallerThan(const SelectionItem &other) const
|
||||
bool operator<(const SelectionItem &b) const
|
||||
{
|
||||
return this->messageIndex < other.messageIndex ||
|
||||
(this->messageIndex == other.messageIndex && this->charIndex < other.charIndex);
|
||||
if (this->messageIndex < b.messageIndex) {
|
||||
return true;
|
||||
}
|
||||
if (this->messageIndex == b.messageIndex && this->charIndex < b.charIndex) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool equals(const SelectionItem &other) const
|
||||
bool operator>(const SelectionItem &b) const
|
||||
{
|
||||
return this->messageIndex == other.messageIndex && this->charIndex == other.charIndex;
|
||||
return b.operator<(*this);
|
||||
}
|
||||
|
||||
bool operator==(const SelectionItem &b) const
|
||||
{
|
||||
return this->messageIndex == b.messageIndex && this->charIndex == b.charIndex;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -45,14 +57,14 @@ struct Selection {
|
|||
, min(start)
|
||||
, max(end)
|
||||
{
|
||||
if (max.isSmallerThan(min)) {
|
||||
if (min > max) {
|
||||
std::swap(this->min, this->max);
|
||||
}
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return this->start.equals(this->end);
|
||||
return this->start == this->end;
|
||||
}
|
||||
|
||||
bool isSingleMessage() const
|
||||
|
@ -60,5 +72,5 @@ struct Selection {
|
|||
return this->min.messageIndex == this->max.messageIndex;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016 Ian Bannerman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,555 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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;
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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 (HTTTRANSPARENT) & to
|
||||
* remove pre Qt5 cruft */
|
||||
/* */
|
||||
/* */
|
||||
|
||||
// Declaration of the QWinWidget classes
|
||||
|
||||
#ifndef QWINWIDGET_H
|
||||
#define QWINWIDGET_H
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "WinNativeWindow.h"
|
||||
#include "widget.h"
|
||||
|
||||
class QWinWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QWinWidget();
|
||||
~QWinWidget();
|
||||
|
||||
void show();
|
||||
void center();
|
||||
void showCentered();
|
||||
void setGeometry(int x, int y, int w, int h);
|
||||
|
||||
HWND getParentWindow() const;
|
||||
|
||||
public slots:
|
||||
void onMaximizeButtonClicked();
|
||||
void onMinimizeButtonClicked();
|
||||
void onCloseButtonClicked();
|
||||
|
||||
protected:
|
||||
void childEvent(QChildEvent *e) override;
|
||||
bool eventFilter(QObject *o, QEvent *e) override;
|
||||
|
||||
bool focusNextPrevChild(bool next) override;
|
||||
void focusInEvent(QFocusEvent *e) override;
|
||||
|
||||
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
|
||||
|
||||
private:
|
||||
QVBoxLayout m_Layout;
|
||||
|
||||
Widget *p_Widget;
|
||||
|
||||
WinNativeWindow *p_ParentWinNativeWindow;
|
||||
HWND m_ParentNativeWindowHandle;
|
||||
|
||||
HWND _prevFocus;
|
||||
bool _reenableParent;
|
||||
|
||||
int BORDERWIDTH = 6; // Adjust this as you wish for # of pixels on the
|
||||
// edges to show resize handles
|
||||
int TOOLBARHEIGHT = 40; // Adjust this as you wish for # of pixels from the
|
||||
// top to allow dragging the window
|
||||
|
||||
void saveFocus();
|
||||
void resetFocus();
|
||||
};
|
||||
|
||||
#endif // QWINWIDGET_H
|
|
@ -1,11 +0,0 @@
|
|||
#include "widget.h"
|
||||
|
||||
Widget::Widget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
// Set a black background for funsies
|
||||
QPalette Pal(palette());
|
||||
Pal.setColor(QPalette::Background, Qt::blue);
|
||||
setAutoFillBackground(true);
|
||||
setPalette(Pal);
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
#ifndef WIDGET_H
|
||||
#define WIDGET_H
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QToolBar>
|
||||
#include <QWidget>
|
||||
|
||||
class Widget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Widget(QWidget *parent = 0);
|
||||
|
||||
// If you want to have Max/Min/Close buttons, look at how QWinWidget uses these
|
||||
QPushButton *maximizeButton = nullptr;
|
||||
QPushButton *minimizeButton = nullptr;
|
||||
QPushButton *closeButton = nullptr;
|
||||
|
||||
// If you want to enable dragging the window when the mouse is over top of, say, a QToolBar,
|
||||
// then look at how QWinWidget uses this
|
||||
QToolBar *toolBar = nullptr;
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
||||
#endif // WIDGET_H
|
|
@ -1,235 +0,0 @@
|
|||
#include "WinNativeWindow.h"
|
||||
|
||||
#include <dwmapi.h>
|
||||
#include <stdexcept>
|
||||
|
||||
HWND WinNativeWindow::childWindow = nullptr;
|
||||
QWidget *WinNativeWindow::childWidget = nullptr;
|
||||
|
||||
WinNativeWindow::WinNativeWindow(const int x, const int y, const int width, const int height)
|
||||
: hWnd(nullptr)
|
||||
{
|
||||
// The native window technically has a background color. You can set it here
|
||||
HBRUSH windowBackground = CreateSolidBrush(RGB(255, 255, 255));
|
||||
|
||||
HINSTANCE hInstance = GetModuleHandle(nullptr);
|
||||
WNDCLASSEX wcx = {0};
|
||||
|
||||
wcx.cbSize = sizeof(WNDCLASSEX);
|
||||
wcx.style = CS_HREDRAW | CS_VREDRAW | CS_DROPSHADOW;
|
||||
wcx.hInstance = hInstance;
|
||||
wcx.lpfnWndProc = WndProc;
|
||||
wcx.cbClsExtra = 0;
|
||||
wcx.cbWndExtra = 0;
|
||||
wcx.lpszClassName = L"WindowClass";
|
||||
wcx.hbrBackground = windowBackground;
|
||||
wcx.hCursor = LoadCursor(hInstance, IDC_ARROW);
|
||||
|
||||
RegisterClassEx(&wcx);
|
||||
if (FAILED(RegisterClassEx(&wcx))) {
|
||||
throw std::runtime_error("Couldn't register window class");
|
||||
}
|
||||
|
||||
// Create a native window with the appropriate style
|
||||
hWnd = CreateWindow(L"WindowClass", L"WindowTitle", aero_borderless, x, y, width, height, 0, 0,
|
||||
hInstance, nullptr);
|
||||
if (!hWnd) {
|
||||
throw std::runtime_error("couldn't create window because of reasons");
|
||||
}
|
||||
|
||||
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
|
||||
|
||||
// This code may be required for aero shadows on some versions of Windows
|
||||
// const MARGINS aero_shadow_on = { 1, 1, 1, 1 };
|
||||
// DwmExtendFrameIntoClientArea(hWnd, &aero_shadow_on);
|
||||
|
||||
SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
|
||||
}
|
||||
|
||||
WinNativeWindow::~WinNativeWindow()
|
||||
{
|
||||
// Hide the window & send the destroy message
|
||||
ShowWindow(hWnd, SW_HIDE);
|
||||
DestroyWindow(hWnd);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK WinNativeWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
WinNativeWindow *window =
|
||||
reinterpret_cast<WinNativeWindow *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
|
||||
|
||||
if (!window) {
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
switch (message) {
|
||||
// ALT + SPACE or F10 system menu
|
||||
case WM_SYSCOMMAND: {
|
||||
if (wParam == SC_KEYMENU) {
|
||||
RECT winrect;
|
||||
GetWindowRect(hWnd, &winrect);
|
||||
TrackPopupMenu(GetSystemMenu(hWnd, false), TPM_TOPALIGN | TPM_LEFTALIGN,
|
||||
winrect.left + 5, winrect.top + 5, 0, hWnd, NULL);
|
||||
break;
|
||||
} else {
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
}
|
||||
case WM_NCCALCSIZE: {
|
||||
// this kills the window frame and title bar we added with
|
||||
// WS_THICKFRAME and WS_CAPTION
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the parent window gets any close messages, send them over to
|
||||
// QWinWidget and don't actually close here
|
||||
case WM_CLOSE: {
|
||||
if (childWindow) {
|
||||
SendMessage(childWindow, WM_CLOSE, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_DESTROY: {
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_NCHITTEST: {
|
||||
const LONG borderWidth =
|
||||
8 * childWidget->window()->devicePixelRatio(); // This value can be arbitrarily
|
||||
// large as only
|
||||
// intentionally-HTTRANSPARENT'd
|
||||
// messages arrive here
|
||||
RECT winrect;
|
||||
GetWindowRect(hWnd, &winrect);
|
||||
long x = GET_X_LPARAM(lParam);
|
||||
long y = GET_Y_LPARAM(lParam);
|
||||
|
||||
// bottom left corner
|
||||
if (x >= winrect.left && x < winrect.left + borderWidth && y < winrect.bottom &&
|
||||
y >= winrect.bottom - borderWidth) {
|
||||
return HTBOTTOMLEFT;
|
||||
}
|
||||
// bottom right corner
|
||||
if (x < winrect.right && x >= winrect.right - borderWidth && y < winrect.bottom &&
|
||||
y >= winrect.bottom - borderWidth) {
|
||||
return HTBOTTOMRIGHT;
|
||||
}
|
||||
// top left corner
|
||||
if (x >= winrect.left && x < winrect.left + borderWidth && y >= winrect.top &&
|
||||
y < winrect.top + borderWidth) {
|
||||
return HTTOPLEFT;
|
||||
}
|
||||
// top right corner
|
||||
if (x < winrect.right && x >= winrect.right - borderWidth && y >= winrect.top &&
|
||||
y < winrect.top + borderWidth) {
|
||||
return HTTOPRIGHT;
|
||||
}
|
||||
// left border
|
||||
if (x >= winrect.left && x < winrect.left + borderWidth) {
|
||||
return HTLEFT;
|
||||
}
|
||||
// right border
|
||||
if (x < winrect.right && x >= winrect.right - borderWidth) {
|
||||
return HTRIGHT;
|
||||
}
|
||||
// bottom border
|
||||
if (y < winrect.bottom && y >= winrect.bottom - borderWidth) {
|
||||
return HTBOTTOM;
|
||||
}
|
||||
// top border
|
||||
if (y >= winrect.top && y < winrect.top + borderWidth) {
|
||||
return HTTOP;
|
||||
}
|
||||
|
||||
// If it wasn't a border but we still got the message, return
|
||||
// HTCAPTION to allow click-dragging the window
|
||||
return HTCAPTION;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// When this native window changes size, it needs to manually resize the
|
||||
// QWinWidget child
|
||||
case WM_SIZE: {
|
||||
RECT winrect;
|
||||
GetClientRect(hWnd, &winrect);
|
||||
|
||||
WINDOWPLACEMENT wp;
|
||||
wp.length = sizeof(WINDOWPLACEMENT);
|
||||
GetWindowPlacement(hWnd, &wp);
|
||||
if (childWidget) {
|
||||
if (wp.showCmd == SW_MAXIMIZE) {
|
||||
childWidget->setGeometry(
|
||||
8, 8 // Maximized window draw 8 pixels off screen
|
||||
,
|
||||
winrect.right / childWidget->window()->devicePixelRatio() - 16,
|
||||
winrect.bottom / childWidget->window()->devicePixelRatio() - 16);
|
||||
} else {
|
||||
childWidget->setGeometry(
|
||||
0, 0, winrect.right / childWidget->window()->devicePixelRatio(),
|
||||
winrect.bottom / childWidget->window()->devicePixelRatio());
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_GETMINMAXINFO: {
|
||||
MINMAXINFO *minMaxInfo = (MINMAXINFO *)lParam;
|
||||
if (window->minimumSize.required) {
|
||||
minMaxInfo->ptMinTrackSize.x = window->getMinimumWidth();
|
||||
;
|
||||
minMaxInfo->ptMinTrackSize.y = window->getMinimumHeight();
|
||||
}
|
||||
|
||||
if (window->maximumSize.required) {
|
||||
minMaxInfo->ptMaxTrackSize.x = window->getMaximumWidth();
|
||||
minMaxInfo->ptMaxTrackSize.y = window->getMaximumHeight();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
void WinNativeWindow::setGeometry(const int x, const int y, const int width, const int height)
|
||||
{
|
||||
MoveWindow(hWnd, x, y, width, height, 1);
|
||||
}
|
||||
|
||||
void WinNativeWindow::setMinimumSize(const int width, const int height)
|
||||
{
|
||||
this->minimumSize.required = true;
|
||||
this->minimumSize.width = width;
|
||||
this->minimumSize.height = height;
|
||||
}
|
||||
|
||||
int WinNativeWindow::getMinimumWidth()
|
||||
{
|
||||
return minimumSize.width;
|
||||
}
|
||||
|
||||
int WinNativeWindow::getMinimumHeight()
|
||||
{
|
||||
return minimumSize.height;
|
||||
}
|
||||
|
||||
void WinNativeWindow::setMaximumSize(const int width, const int height)
|
||||
{
|
||||
this->maximumSize.required = true;
|
||||
this->maximumSize.width = width;
|
||||
this->maximumSize.height = height;
|
||||
}
|
||||
|
||||
int WinNativeWindow::getMaximumWidth()
|
||||
{
|
||||
return maximumSize.width;
|
||||
}
|
||||
|
||||
int WinNativeWindow::getMaximumHeight()
|
||||
{
|
||||
return maximumSize.height;
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
#ifndef WINNATIVEWINDOW_H
|
||||
#define WINNATIVEWINDOW_H
|
||||
|
||||
#include "Windows.h"
|
||||
#include "Windowsx.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class WinNativeWindow
|
||||
{
|
||||
public:
|
||||
WinNativeWindow(const int x, const int y, const int width, const int height);
|
||||
~WinNativeWindow();
|
||||
|
||||
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
// These six functions exist to restrict native window resizing to whatever
|
||||
// you want your app minimum/maximum size to be
|
||||
void setMinimumSize(const int width, const int height);
|
||||
int getMinimumHeight();
|
||||
int getMinimumWidth();
|
||||
|
||||
void setMaximumSize(const int width, const int height);
|
||||
int getMaximumHeight();
|
||||
int getMaximumWidth();
|
||||
void setGeometry(const int x, const int y, const int width, const int height);
|
||||
|
||||
HWND hWnd;
|
||||
|
||||
static HWND childWindow;
|
||||
static QWidget *childWidget;
|
||||
|
||||
private:
|
||||
struct sizeType {
|
||||
sizeType()
|
||||
: required(false)
|
||||
, width(0)
|
||||
, height(0)
|
||||
{
|
||||
}
|
||||
bool required;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
sizeType minimumSize;
|
||||
sizeType maximumSize;
|
||||
|
||||
DWORD aero_borderless = WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX |
|
||||
WS_THICKFRAME | WS_CLIPCHILDREN;
|
||||
};
|
||||
|
||||
#endif // WINNATIVEWINDOW_H
|
|
@ -4,7 +4,7 @@
|
|||
#include <QColor>
|
||||
#include <boost/signals2.hpp>
|
||||
#include <pajlada/settings/setting.hpp>
|
||||
#include <util/serialize-custom.hpp>
|
||||
#include "util/serialize-custom.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace singletons {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "aboutpage.hpp"
|
||||
|
||||
#include <util/layoutcreator.hpp>
|
||||
#include "util/layoutcreator.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <util/layoutcreator.hpp>
|
||||
#include "util/layoutcreator.hpp"
|
||||
|
||||
#define THEME_ITEMS "White", "Light", "Dark", "Black"
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
#include <QLabel>
|
||||
#include <QTextEdit>
|
||||
|
||||
#include <util/layoutcreator.hpp>
|
||||
#include "singletons/commandmanager.hpp"
|
||||
#include "util/layoutcreator.hpp"
|
||||
|
||||
// clang-format off
|
||||
#define TEXT "One command per line.\n"\
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "emotespage.hpp"
|
||||
|
||||
#include <util/layoutcreator.hpp>
|
||||
#include "util/layoutcreator.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
|
Loading…
Reference in a new issue