mirror-chatterino2/src/widgets/basewindow.cpp

500 lines
16 KiB
C++
Raw Normal View History

#include "basewindow.hpp"
#include "application.hpp"
#include "debug/log.hpp"
#include "singletons/settingsmanager.hpp"
#include "util/nativeeventhelper.hpp"
2018-02-05 23:32:38 +01:00
#include "widgets/helper/rippleeffectlabel.hpp"
#include "widgets/tooltipwidget.hpp"
#include <QApplication>
#include <QDebug>
#include <QDesktopWidget>
#include <QIcon>
#ifdef USEWINSDK
#include <ObjIdl.h>
2018-04-08 17:07:40 +02:00
#include <VersionHelpers.h>
#include <Windows.h>
#include <dwmapi.h>
#include <gdiplus.h>
#include <windowsx.h>
#pragma comment(lib, "Dwmapi.lib")
#include <QHBoxLayout>
#include <QVBoxLayout>
#define WM_DPICHANGED 0x02E0
#endif
2018-01-14 21:59:45 +01:00
#include "widgets/helper/titlebarbutton.hpp"
2018-01-15 04:08:48 +01:00
namespace chatterino {
namespace widgets {
BaseWindow::BaseWindow(QWidget *parent, Flags _flags)
: BaseWidget(parent,
Qt::Window | ((_flags & TopMost) ? Qt::WindowStaysOnTopHint : Qt::WindowFlags(0)))
, enableCustomFrame(_flags & EnableCustomFrame)
2018-06-06 10:46:23 +02:00
, frameless(_flags & Frameless)
, flags(_flags)
{
2018-05-23 22:27:29 +02:00
if (this->frameless) {
this->enableCustomFrame = false;
this->setWindowFlag(Qt::FramelessWindowHint);
}
this->init();
}
BaseWindow::Flags BaseWindow::getFlags()
{
return this->flags;
}
void BaseWindow::init()
{
this->setWindowIcon(QIcon(":/images/icon.png"));
#ifdef USEWINSDK
if (this->hasCustomWindowFrame()) {
// CUSTOM WINDOW FRAME
2018-01-25 20:49:49 +01:00
QVBoxLayout *layout = new QVBoxLayout();
2018-04-08 17:07:40 +02:00
layout->setContentsMargins(0, 1, 0, 0);
2018-01-24 15:08:22 +01:00
layout->setSpacing(0);
this->setLayout(layout);
{
2018-05-23 22:27:29 +02:00
if (!this->frameless) {
QHBoxLayout *buttonLayout = this->ui.titlebarBox = new QHBoxLayout();
buttonLayout->setMargin(0);
layout->addLayout(buttonLayout);
// title
QLabel *title = new QLabel(" Chatterino");
QObject::connect(this, &QWidget::windowTitleChanged,
[title](const QString &text) { title->setText(" " + text); });
QSizePolicy policy(QSizePolicy::Ignored, QSizePolicy::Preferred);
policy.setHorizontalStretch(1);
// title->setBaseSize(0, 0);
title->setScaledContents(true);
title->setSizePolicy(policy);
buttonLayout->addWidget(title);
this->ui.titleLabel = title;
// buttons
TitleBarButton *_minButton = new TitleBarButton;
_minButton->setButtonStyle(TitleBarButton::Minimize);
TitleBarButton *_maxButton = new TitleBarButton;
_maxButton->setButtonStyle(TitleBarButton::Maximize);
TitleBarButton *_exitButton = new TitleBarButton;
_exitButton->setButtonStyle(TitleBarButton::Close);
QObject::connect(_minButton, &TitleBarButton::clicked, this, [this] {
this->setWindowState(Qt::WindowMinimized | this->windowState());
});
QObject::connect(_maxButton, &TitleBarButton::clicked, this, [this] {
this->setWindowState(this->windowState() == Qt::WindowMaximized
? Qt::WindowActive
: Qt::WindowMaximized);
});
QObject::connect(_exitButton, &TitleBarButton::clicked, this,
[this] { this->close(); });
this->ui.minButton = _minButton;
this->ui.maxButton = _maxButton;
this->ui.exitButton = _exitButton;
this->ui.buttons.push_back(_minButton);
this->ui.buttons.push_back(_maxButton);
this->ui.buttons.push_back(_exitButton);
// buttonLayout->addStretch(1);
buttonLayout->addWidget(_minButton);
buttonLayout->addWidget(_maxButton);
buttonLayout->addWidget(_exitButton);
buttonLayout->setSpacing(0);
}
}
this->ui.layoutBase = new BaseWidget(this);
layout->addWidget(this->ui.layoutBase);
}
// DPI
auto dpi = util::getWindowDpi(this->winId());
if (dpi) {
2018-01-25 20:49:49 +01:00
this->scale = dpi.value() / 96.f;
}
#endif
#ifdef USEWINSDK
// fourtf: don't ask me why we need to delay this
2018-05-26 17:11:09 +02:00
if (!(this->flags & Flags::TopMost)) {
QTimer::singleShot(1, this, [this] {
getApp()->settings->windowTopMost.connect([this](bool topMost, auto) {
2018-05-26 17:11:09 +02:00
::SetWindowPos(HWND(this->winId()), topMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0,
0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
});
2018-05-26 17:11:09 +02:00
});
}
#else
// if (getApp()->settings->windowTopMost.getValue()) {
// this->setWindowFlag(Qt::WindowStaysOnTopHint);
// }
#endif
}
void BaseWindow::setStayInScreenRect(bool value)
{
this->stayInScreenRect = value;
}
bool BaseWindow::getStayInScreenRect() const
{
return this->stayInScreenRect;
}
QWidget *BaseWindow::getLayoutContainer()
{
2018-01-15 04:08:48 +01:00
if (this->hasCustomWindowFrame()) {
return this->ui.layoutBase;
} else {
return this;
}
}
bool BaseWindow::hasCustomWindowFrame()
{
#ifdef USEWINSDK
2018-04-08 17:07:40 +02:00
static bool isWin8 = IsWindows8OrGreater();
return isWin8 && this->enableCustomFrame;
#else
return false;
#endif
}
2018-01-25 20:49:49 +01:00
void BaseWindow::themeRefreshEvent()
{
2018-04-20 23:54:19 +02:00
if (this->hasCustomWindowFrame()) {
QPalette palette;
2018-04-08 17:07:40 +02:00
palette.setColor(QPalette::Background, QColor(0, 0, 0, 0));
palette.setColor(QPalette::Foreground, this->themeManager->window.text);
this->setPalette(palette);
2018-05-23 22:27:29 +02:00
if (this->ui.titleLabel) {
QPalette palette_title;
palette_title.setColor(QPalette::Foreground,
this->themeManager->isLightTheme() ? "#333" : "#ccc");
this->ui.titleLabel->setPalette(palette_title);
}
2018-04-18 18:55:49 +02:00
for (RippleEffectButton *button : this->ui.buttons) {
button->setMouseEffectColor(this->themeManager->window.text);
}
2018-04-13 23:41:52 +02:00
} else {
QPalette palette;
palette.setColor(QPalette::Background, this->themeManager->window.background);
palette.setColor(QPalette::Foreground, this->themeManager->window.text);
2018-04-13 23:41:52 +02:00
this->setPalette(palette);
2018-01-24 15:08:22 +01:00
}
}
void BaseWindow::addTitleBarButton(const TitleBarButton::Style &style,
std::function<void()> onClicked)
{
TitleBarButton *button = new TitleBarButton;
2018-01-25 21:11:14 +01:00
button->setScaleIndependantSize(30, 30);
this->ui.buttons.push_back(button);
2018-04-18 18:55:49 +02:00
this->ui.titlebarBox->insertWidget(1, button);
button->setButtonStyle(style);
QObject::connect(button, &TitleBarButton::clicked, this, [onClicked] { onClicked(); });
}
2018-02-05 23:32:38 +01:00
RippleEffectLabel *BaseWindow::addTitleBarLabel(std::function<void()> onClicked)
{
RippleEffectLabel *button = new RippleEffectLabel;
button->setScaleIndependantHeight(30);
this->ui.buttons.push_back(button);
2018-04-18 18:55:49 +02:00
this->ui.titlebarBox->insertWidget(1, button);
2018-02-05 23:32:38 +01:00
QObject::connect(button, &RippleEffectLabel::clicked, this, [onClicked] { onClicked(); });
return button;
}
void BaseWindow::changeEvent(QEvent *)
{
TooltipWidget::getInstance()->hide();
#ifdef USEWINSDK
2018-05-23 22:27:29 +02:00
if (this->ui.maxButton) {
this->ui.maxButton->setButtonStyle(this->windowState() & Qt::WindowMaximized
? TitleBarButton::Unmaximize
: TitleBarButton::Maximize);
}
#endif
2018-04-13 22:50:19 +02:00
2018-04-13 23:41:52 +02:00
#ifndef Q_OS_WIN
2018-04-13 22:50:19 +02:00
this->update();
2018-04-13 23:41:52 +02:00
#endif
}
void BaseWindow::leaveEvent(QEvent *)
{
TooltipWidget::getInstance()->hide();
}
2018-05-26 17:11:09 +02:00
void BaseWindow::moveTo(QWidget *parent, QPoint point, bool offset)
{
2018-05-26 17:11:09 +02:00
if (offset) {
point.rx() += 16;
point.ry() += 16;
}
this->move(point);
this->moveIntoDesktopRect(parent);
}
void BaseWindow::resizeEvent(QResizeEvent *)
{
this->moveIntoDesktopRect(this);
this->calcButtonsSizes();
}
void BaseWindow::moveIntoDesktopRect(QWidget *parent)
{
if (!this->stayInScreenRect)
return;
// move the widget into the screen geometry if it's not already in there
QDesktopWidget *desktop = QApplication::desktop();
QRect s = desktop->screenGeometry(parent);
QPoint p = this->pos();
if (p.x() < s.left()) {
p.setX(s.left());
}
if (p.y() < s.top()) {
p.setY(s.top());
}
if (p.x() + this->width() > s.right()) {
p.setX(s.right() - this->width());
}
if (p.y() + this->height() > s.bottom()) {
p.setY(s.bottom() - this->height());
}
if (p != this->pos())
this->move(p);
}
#ifdef USEWINSDK
bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
2018-01-14 21:59:45 +01:00
MSG *msg = reinterpret_cast<MSG *>(message);
switch (msg->message) {
case WM_DPICHANGED: {
int dpi = HIWORD(msg->wParam);
2018-01-25 20:49:49 +01:00
float oldScale = this->scale;
float _scale = dpi / 96.f;
float resizeScale = _scale / oldScale;
2018-01-25 20:49:49 +01:00
this->resize(static_cast<int>(this->width() * resizeScale),
static_cast<int>(this->height() * resizeScale));
2018-01-25 20:49:49 +01:00
this->setScale(_scale);
return true;
}
case WM_NCCALCSIZE: {
if (this->hasCustomWindowFrame()) {
2018-04-08 17:07:40 +02:00
int cx = GetSystemMetrics(SM_CXSIZEFRAME);
int cy = GetSystemMetrics(SM_CYSIZEFRAME);
if (msg->wParam == TRUE) {
NCCALCSIZE_PARAMS *ncp = (reinterpret_cast<NCCALCSIZE_PARAMS *>(msg->lParam));
ncp->lppos->flags |= SWP_NOREDRAW;
RECT *clientRect = &ncp->rgrc[0];
if (IsWindows10OrGreater()) {
clientRect->left += cx;
clientRect->top += 0;
clientRect->right -= cx;
clientRect->bottom -= cy;
} else {
clientRect->left += 1;
clientRect->top += 0;
clientRect->right -= 1;
clientRect->bottom -= 1;
}
2018-04-08 17:07:40 +02:00
}
*result = 0;
return true;
} else {
return QWidget::nativeEvent(eventType, message, result);
}
2018-05-26 17:11:09 +02:00
} break;
case WM_NCHITTEST: {
if (this->hasCustomWindowFrame()) {
*result = 0;
const LONG border_width = 8; // in pixels
RECT winrect;
2018-05-26 17:11:09 +02:00
GetWindowRect(HWND(winId()), &winrect);
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
bool resizeWidth = minimumWidth() != maximumWidth();
bool resizeHeight = minimumHeight() != maximumHeight();
if (resizeWidth) {
// left border
2018-04-08 17:07:40 +02:00
if (x < winrect.left + border_width) {
*result = HTLEFT;
}
// right border
2018-04-08 17:07:40 +02:00
if (x >= winrect.right - border_width) {
*result = HTRIGHT;
}
}
if (resizeHeight) {
// bottom border
2018-04-08 17:07:40 +02:00
if (y >= winrect.bottom - border_width) {
*result = HTBOTTOM;
}
// top border
2018-04-08 17:07:40 +02:00
if (y < winrect.top + border_width) {
*result = HTTOP;
}
}
if (resizeWidth && resizeHeight) {
// bottom left corner
if (x >= winrect.left && x < winrect.left + border_width &&
y < winrect.bottom && y >= winrect.bottom - border_width) {
*result = HTBOTTOMLEFT;
}
// bottom right corner
if (x < winrect.right && x >= winrect.right - border_width &&
y < winrect.bottom && y >= winrect.bottom - border_width) {
*result = HTBOTTOMRIGHT;
}
// top left corner
if (x >= winrect.left && x < winrect.left + border_width && y >= winrect.top &&
y < winrect.top + border_width) {
*result = HTTOPLEFT;
}
// top right corner
if (x < winrect.right && x >= winrect.right - border_width &&
y >= winrect.top && y < winrect.top + border_width) {
*result = HTTOPRIGHT;
}
}
2018-01-14 21:59:45 +01:00
if (*result == 0) {
bool client = false;
QPoint point(x - winrect.left, y - winrect.top);
for (QWidget *widget : this->ui.buttons) {
if (widget->geometry().contains(point)) {
client = true;
}
}
if (this->ui.layoutBase->geometry().contains(point)) {
2018-01-24 15:08:22 +01:00
client = true;
}
if (client) {
*result = HTCLIENT;
} else {
*result = HTCAPTION;
}
}
return true;
} else {
return QWidget::nativeEvent(eventType, message, result);
}
break;
}
default:
return QWidget::nativeEvent(eventType, message, result);
}
}
2018-01-24 15:08:22 +01:00
void BaseWindow::showEvent(QShowEvent *event)
{
2018-01-24 15:08:22 +01:00
if (!this->shown && this->isVisible() && this->hasCustomWindowFrame()) {
this->shown = true;
// SetWindowLongPtr((HWND)this->winId(), GWL_STYLE,
// WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZEBOX |
// WS_MINIMIZEBOX);
2018-04-08 17:37:48 +02:00
const MARGINS shadow = {8, 8, 8, 8};
2018-05-26 17:11:09 +02:00
DwmExtendFrameIntoClientArea(HWND(this->winId()), &shadow);
}
2018-01-24 15:08:22 +01:00
BaseWidget::showEvent(event);
}
2018-01-14 21:59:45 +01:00
2018-06-06 10:46:23 +02:00
void BaseWindow::scaleChangedEvent(float)
{
this->calcButtonsSizes();
}
#endif
2018-05-26 17:11:09 +02:00
void BaseWindow::paintEvent(QPaintEvent *)
{
2018-06-06 10:46:23 +02:00
if (this->frameless) {
QPainter painter(this);
painter.setPen(QColor("#999"));
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
}
#ifdef USEWINSDK
if (this->hasCustomWindowFrame()) {
QPainter painter(this);
2018-04-08 17:07:40 +02:00
// bool windowFocused = this->window() == QApplication::activeWindow();
2018-01-24 15:08:22 +01:00
2018-04-08 17:37:48 +02:00
painter.fillRect(QRect(0, 1, this->width() - 0, this->height() - 0),
this->themeManager->window.background);
}
#endif
2018-06-06 10:46:23 +02:00
}
void BaseWindow::calcButtonsSizes()
{
if (!this->shown) {
return;
}
if ((this->width() / this->getScale()) < 300) {
2018-05-23 22:27:29 +02:00
if (this->ui.minButton)
this->ui.minButton->setScaleIndependantSize(30, 30);
if (this->ui.maxButton)
this->ui.maxButton->setScaleIndependantSize(30, 30);
if (this->ui.exitButton)
this->ui.exitButton->setScaleIndependantSize(30, 30);
} else {
2018-05-23 22:27:29 +02:00
if (this->ui.minButton)
this->ui.minButton->setScaleIndependantSize(46, 30);
if (this->ui.maxButton)
this->ui.maxButton->setScaleIndependantSize(46, 30);
if (this->ui.exitButton)
this->ui.exitButton->setScaleIndependantSize(46, 30);
}
}
} // namespace widgets
} // namespace chatterino