started working on a custom window frame

This commit is contained in:
fourtf 2018-01-15 01:35:35 +01:00
parent 177cd734ab
commit c50e6d7809
11 changed files with 335 additions and 67 deletions

View file

@ -53,7 +53,23 @@ void ThemeManager::update()
// multiplier: 1 = white, 0.8 = light, -0.8 dark, -1 black // multiplier: 1 = white, 0.8 = light, -0.8 dark, -1 black
void ThemeManager::actuallyUpdate(double hue, double multiplier) void ThemeManager::actuallyUpdate(double hue, double multiplier)
{ {
lightTheme = multiplier > 0; isLight = multiplier > 0;
bool isLightTabs;
QColor themeColor = QColor::fromHslF(hue, 0.5, 0.5);
QColor themeColorNoSat = QColor::fromHslF(hue, 0.5, 0.5);
#ifdef USEWINSDK
QColor tabFg = isLight ? "#000" : "#fff";
this->windowBg = isLight ? "#fff" : "#444";
isLightTabs = isLight;
#else
QColor tabFg = lightTheme ? "#000" : "#fff";
this->windowBg = "#fff";
isLightTabs = true;
#endif
qreal sat = 0.05; qreal sat = 0.05;
@ -70,30 +86,30 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
// message (referenced later) // message (referenced later)
this->messages.textColors.caret = // this->messages.textColors.caret = //
this->messages.textColors.regular = lightTheme ? QColor(0, 0, 0) : QColor(255, 255, 255); this->messages.textColors.regular = isLight ? "#000" : "#fff";
// tabs // tabs
// text, {regular, hover, unfocused} // text, {regular, hover, unfocused}
this->tabs.regular = {QColor(0, 0, 0), this->tabs.regular = {tabFg, {windowBg, blendColors(windowBg, "#999", 0.5), windowBg}};
{QColor(255, 255, 255), QColor(200, 200, 200), QColor(255, 255, 255)}};
this->tabs.selected = {QColor(255, 255, 255), this->tabs.selected = {"#fff", {themeColor, themeColor, QColor::fromHslF(hue, 0, 0.5)}};
{QColor::fromHslF(hue, 0.5, 0.5), QColor::fromHslF(hue, 0.5, 0.5),
QColor::fromHslF(hue, 0, 0.5)}};
this->tabs.newMessage = {QColor(0, 0, 0), this->tabs.newMessage = {
{QBrush(QColor::fromHslF(hue, 0.5, 0.8), Qt::DiagCrossPattern), tabFg,
QBrush(QColor::fromHslF(hue, 0.5, 0.7), Qt::DiagCrossPattern), {QBrush(blendColors(themeColor, windowBg, 0.5), Qt::DiagCrossPattern),
QBrush(QColor::fromHslF(hue, 0, 0.8), Qt::DiagCrossPattern)}}; QBrush(blendColors(themeColor, windowBg, 0.3), Qt::DiagCrossPattern),
QBrush(blendColors(themeColorNoSat, windowBg, 0.5), Qt::DiagCrossPattern)}};
this->tabs.highlighted = {QColor(0, 0, 0), this->tabs.highlighted = {
{QColor::fromHslF(hue, 0.5, 0.8), QColor::fromHslF(hue, 0.5, 0.7), tabFg,
QColor::fromHslF(hue, 0, 0.8)}}; {QBrush(blendColors(themeColor, windowBg, 0.5), Qt::DiagCrossPattern),
QBrush(blendColors(themeColor, windowBg, 0.3), Qt::DiagCrossPattern),
QBrush(blendColors(themeColorNoSat, windowBg, 0.5), Qt::DiagCrossPattern)}};
// Split // Split
bool flat = lightTheme; bool flat = isLight;
this->splits.messageSeperator = lightTheme ? QColor(127, 127, 127) : QColor(80, 80, 80); this->splits.messageSeperator = isLight ? QColor(127, 127, 127) : QColor(80, 80, 80);
this->splits.background = getColor(0, sat, 1); this->splits.background = getColor(0, sat, 1);
this->splits.dropPreview = getColor(hue, 0.5, 0.5, 0.6); this->splits.dropPreview = getColor(hue, 0.5, 0.5, 0.6);
// this->splits.border // this->splits.border
@ -113,7 +129,7 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
"selection-background-color:" + this->tabs.selected.backgrounds.regular.color().name(); "selection-background-color:" + this->tabs.selected.backgrounds.regular.color().name();
// Message // Message
this->messages.textColors.link = lightTheme ? QColor(66, 134, 244) : QColor(66, 134, 244); this->messages.textColors.link = isLight ? QColor(66, 134, 244) : QColor(66, 134, 244);
this->messages.textColors.system = QColor(140, 127, 127); this->messages.textColors.system = QColor(140, 127, 127);
this->messages.backgrounds.regular = splits.background; this->messages.backgrounds.regular = splits.background;
@ -151,7 +167,7 @@ QColor ThemeManager::blendColors(const QColor &color1, const QColor &color2, qre
void ThemeManager::normalizeColor(QColor &color) void ThemeManager::normalizeColor(QColor &color)
{ {
if (this->lightTheme) { if (this->isLight) {
if (color.lightnessF() > 0.5f) { if (color.lightnessF() > 0.5f) {
color.setHslF(color.hueF(), color.saturationF(), 0.5f); color.setHslF(color.hueF(), color.saturationF(), 0.5f);
} }

View file

@ -20,7 +20,7 @@ public:
inline bool isLightTheme() const inline bool isLightTheme() const
{ {
return this->lightTheme; return this->isLight;
} }
struct TabColors { struct TabColors {
@ -97,6 +97,9 @@ public:
QColor background; QColor background;
} tooltip; } tooltip;
QColor windowBg;
QColor windowText;
void normalizeColor(QColor &color); void normalizeColor(QColor &color);
void update(); void update();
@ -116,7 +119,7 @@ private:
void fillLookupTableValues(double (&array)[360], double from, double to, double fromValue, void fillLookupTableValues(double (&array)[360], double from, double to, double fromValue,
double toValue); double toValue);
bool lightTheme = false; bool isLight = false;
pajlada::Signals::NoArgSignal repaintVisibleChatWidgets; pajlada::Signals::NoArgSignal repaintVisibleChatWidgets;

View file

@ -11,22 +11,22 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
BaseWidget::BaseWidget(singletons::ThemeManager &_themeManager, QWidget *parent) BaseWidget::BaseWidget(singletons::ThemeManager &_themeManager, QWidget *parent, Qt::WindowFlags f)
: QWidget(parent) : QWidget(parent, f)
, themeManager(_themeManager) , themeManager(_themeManager)
{ {
this->init(); this->init();
} }
BaseWidget::BaseWidget(BaseWidget *parent) BaseWidget::BaseWidget(BaseWidget *parent, Qt::WindowFlags f)
: QWidget(parent) : QWidget(parent, f)
, themeManager(singletons::ThemeManager::getInstance()) , themeManager(singletons::ThemeManager::getInstance())
{ {
this->init(); this->init();
} }
BaseWidget::BaseWidget(QWidget *parent) BaseWidget::BaseWidget(QWidget *parent, Qt::WindowFlags f)
: QWidget(parent) : QWidget(parent, f)
, themeManager(singletons::ThemeManager::getInstance()) , themeManager(singletons::ThemeManager::getInstance())
{ {
} }

View file

@ -14,9 +14,10 @@ class BaseWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
explicit BaseWidget(singletons::ThemeManager &_themeManager, QWidget *parent); explicit BaseWidget(singletons::ThemeManager &_themeManager, QWidget *parent,
explicit BaseWidget(BaseWidget *parent); Qt::WindowFlags f = Qt::WindowFlags());
explicit BaseWidget(QWidget *parent = nullptr); explicit BaseWidget(BaseWidget *parent, Qt::WindowFlags f = Qt::WindowFlags());
explicit BaseWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
singletons::ThemeManager &themeManager; singletons::ThemeManager &themeManager;

View file

@ -4,28 +4,46 @@
#include "util/nativeeventhelper.hpp" #include "util/nativeeventhelper.hpp"
#include "widgets/tooltipwidget.hpp" #include "widgets/tooltipwidget.hpp"
#include <QApplication>
#include <QDebug> #include <QDebug>
#include <QIcon> #include <QIcon>
#ifdef USEWINSDK
#include <dwmapi.h>
#include <gdiplus.h>
#include <objidl.h>
#include <windows.h> #include <windows.h>
#include <windowsx.h>
#pragma comment(lib, "Dwmapi.lib")
#include <QHBoxLayout>
#include <QVBoxLayout>
#include "widgets/helper/rippleeffectlabel.hpp"
#define WM_DPICHANGED 0x02E0
#endif
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
BaseWindow::BaseWindow(singletons::ThemeManager &_themeManager, QWidget *parent) BaseWindow::BaseWindow(singletons::ThemeManager &_themeManager, QWidget *parent,
: BaseWidget(_themeManager, parent) bool _enableCustomFrame)
: BaseWidget(_themeManager, parent, Qt::Window)
, enableCustomFrame(_enableCustomFrame)
{ {
this->init(); this->init();
} }
BaseWindow::BaseWindow(BaseWidget *parent) BaseWindow::BaseWindow(BaseWidget *parent, bool _enableCustomFrame)
: BaseWidget(parent) : BaseWidget(parent, Qt::Window)
, enableCustomFrame(_enableCustomFrame)
{ {
this->init(); this->init();
} }
BaseWindow::BaseWindow(QWidget *parent) BaseWindow::BaseWindow(QWidget *parent, bool _enableCustomFrame)
: BaseWidget(parent) : BaseWidget(parent, Qt::Window)
, enableCustomFrame(_enableCustomFrame)
{ {
this->init(); this->init();
} }
@ -35,6 +53,51 @@ void BaseWindow::init()
this->setWindowIcon(QIcon(":/images/icon.png")); this->setWindowIcon(QIcon(":/images/icon.png"));
#ifdef USEWINSDK #ifdef USEWINSDK
if (this->hasCustomWindowFrame()) {
// CUSTOM WINDOW FRAME
QVBoxLayout *layout = new QVBoxLayout;
layout->setMargin(1);
this->setLayout(layout);
{
QHBoxLayout *buttons = this->titlebarBox = new QHBoxLayout;
buttons->setMargin(0);
layout->addLayout(buttons);
// title
QLabel *titleLabel = new QLabel("Chatterino");
buttons->addWidget(titleLabel);
this->titleLabel = titleLabel;
// buttons
RippleEffectLabel *min = new RippleEffectLabel;
min->getLabel().setText("min");
min->setFixedSize(46, 30);
RippleEffectLabel *max = new RippleEffectLabel;
max->setFixedSize(46, 30);
max->getLabel().setText("max");
RippleEffectLabel *exit = new RippleEffectLabel;
exit->setFixedSize(46, 30);
exit->getLabel().setText("exit");
this->minButton = min;
this->maxButton = max;
this->exitButton = exit;
this->widgets.push_back(min);
this->widgets.push_back(max);
this->widgets.push_back(exit);
buttons->addStretch(1);
buttons->addWidget(min);
buttons->addWidget(max);
buttons->addWidget(exit);
}
this->layoutBase = new QWidget(this);
this->widgets.push_back(this->layoutBase);
layout->addWidget(this->layoutBase);
}
// DPI
auto dpi = util::getWindowDpi(this->winId()); auto dpi = util::getWindowDpi(this->winId());
if (dpi) { if (dpi) {
@ -49,6 +112,40 @@ void BaseWindow::init()
} }
} }
QWidget *BaseWindow::getLayoutContainer()
{
if (this->enableCustomFrame) {
return this->layoutBase;
} else {
return this;
}
}
bool BaseWindow::hasCustomWindowFrame()
{
#ifdef Q_OS_WIN
return this->enableCustomFrame;
#else
return false;
#endif
}
void BaseWindow::refreshTheme()
{
QPalette palette;
palette.setColor(QPalette::Background, this->themeManager.windowBg);
palette.setColor(QPalette::Foreground, this->themeManager.windowText);
this->setPalette(palette);
}
void BaseWindow::addTitleBarButton(const QString &text)
{
RippleEffectLabel *label = new RippleEffectLabel;
label->getLabel().setText(text);
this->widgets.push_back(label);
this->titlebarBox->insertWidget(2, label);
}
void BaseWindow::changeEvent(QEvent *) void BaseWindow::changeEvent(QEvent *)
{ {
TooltipWidget::getInstance()->hide(); TooltipWidget::getInstance()->hide();
@ -64,8 +161,8 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
{ {
MSG *msg = reinterpret_cast<MSG *>(message); MSG *msg = reinterpret_cast<MSG *>(message);
// WM_DPICHANGED switch (msg->message) {
if (msg->message == 0x02E0) { case WM_DPICHANGED: {
qDebug() << "dpi changed"; qDebug() << "dpi changed";
int dpi = HIWORD(msg->wParam); int dpi = HIWORD(msg->wParam);
@ -80,10 +177,140 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
return true; return true;
} }
case WM_NCCALCSIZE: {
if (this->hasCustomWindowFrame()) {
// this kills the window frame and title bar we added with
// WS_THICKFRAME and WS_CAPTION
*result = 0;
return true;
} else {
return QWidget::nativeEvent(eventType, message, result); return QWidget::nativeEvent(eventType, message, result);
} // namespace widgets }
break;
}
case WM_NCHITTEST: {
if (this->hasCustomWindowFrame()) {
*result = 0;
const LONG border_width = 8; // in pixels
RECT winrect;
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
if (x >= winrect.left && x < winrect.left + border_width) {
*result = HTLEFT;
}
// right border
if (x < winrect.right && x >= winrect.right - border_width) {
*result = HTRIGHT;
}
}
if (resizeHeight) {
// bottom border
if (y < winrect.bottom && y >= winrect.bottom - border_width) {
*result = HTBOTTOM;
}
// top border
if (y >= winrect.top && 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;
}
}
if (*result == 0) {
bool client = false;
QPoint point(x - winrect.left, y - winrect.top);
for (QWidget *widget : this->widgets) {
if (widget->geometry().contains(point)) {
client = true;
}
}
if (client) {
*result = HTCLIENT;
} else {
*result = HTCAPTION;
}
}
qDebug() << *result;
return true;
} else {
return QWidget::nativeEvent(eventType, message, result);
}
break;
} // end case WM_NCHITTEST
case WM_CLOSE: {
if (this->enableCustomFrame) {
return close();
}
break;
}
default:
return QWidget::nativeEvent(eventType, message, result);
}
}
void BaseWindow::showEvent(QShowEvent *)
{
if (this->isVisible() && this->hasCustomWindowFrame()) {
SetWindowLongPtr((HWND)this->winId(), GWL_STYLE,
WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
const MARGINS shadow = {1, 1, 1, 1};
DwmExtendFrameIntoClientArea((HWND)this->winId(), &shadow);
SetWindowPos((HWND)this->winId(), 0, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
}
}
void BaseWindow::paintEvent(QPaintEvent *event)
{
BaseWidget::paintEvent(event);
if (this->hasCustomWindowFrame()) {
QPainter painter(this);
bool windowFocused = this->window() == QApplication::activeWindow();
if (windowFocused) {
painter.setPen(this->themeManager.tabs.selected.backgrounds.regular.color());
} else {
painter.setPen(this->themeManager.tabs.selected.backgrounds.unfocused.color());
}
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
}
}
#endif #endif
} // namespace widgets } // namespace widgets

View file

@ -2,26 +2,47 @@
#include "basewidget.hpp" #include "basewidget.hpp"
class QHBoxLayout;
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
class BaseWindow : public BaseWidget class BaseWindow : public BaseWidget
{ {
public: public:
explicit BaseWindow(singletons::ThemeManager &_themeManager, QWidget *parent); explicit BaseWindow(singletons::ThemeManager &_themeManager, QWidget *parent,
explicit BaseWindow(BaseWidget *parent); bool enableCustomFrame = false);
explicit BaseWindow(QWidget *parent = nullptr); explicit BaseWindow(BaseWidget *parent, bool enableCustomFrame = false);
explicit BaseWindow(QWidget *parent = nullptr, bool enableCustomFrame = false);
QWidget *getLayoutContainer();
bool hasCustomWindowFrame();
void addTitleBarButton(const QString &text);
protected: protected:
#ifdef USEWINSDK #ifdef USEWINSDK
virtual void showEvent(QShowEvent *);
virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result) override; virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
virtual void paintEvent(QPaintEvent *event) override;
#endif #endif
virtual void changeEvent(QEvent *) override; virtual void changeEvent(QEvent *) override;
virtual void leaveEvent(QEvent *) override; virtual void leaveEvent(QEvent *) override;
virtual void refreshTheme() override;
private: private:
void init(); void init();
bool enableCustomFrame;
QHBoxLayout *titlebarBox;
QWidget *titleLabel;
QWidget *minButton;
QWidget *maxButton;
QWidget *exitButton;
QWidget *layoutBase;
std::vector<QWidget *> widgets;
}; };
} // namespace widgets } // namespace widgets
} // namespace chatterino } // namespace chatterino

View file

@ -15,7 +15,7 @@ namespace widgets {
class RippleEffectLabel : public RippleEffectButton class RippleEffectLabel : public RippleEffectButton
{ {
public: public:
explicit RippleEffectLabel(BaseWidget *parent, int spacing = 6); explicit RippleEffectLabel(BaseWidget *parent = nullptr, int spacing = 6);
SignalLabel &getLabel() SignalLabel &getLabel()
{ {

View file

@ -24,6 +24,7 @@ namespace widgets {
Notebook::Notebook(Window *parent, bool _showButtons, const std::string &settingPrefix) Notebook::Notebook(Window *parent, bool _showButtons, const std::string &settingPrefix)
: BaseWidget(parent) : BaseWidget(parent)
, parentWindow(parent)
, settingRoot(fS("{}/notebook", settingPrefix)) , settingRoot(fS("{}/notebook", settingPrefix))
, addButton(this) , addButton(this)
, settingsButton(this) , settingsButton(this)
@ -196,14 +197,15 @@ void Notebook::performLayout(bool animated)
int x = 0, y = 0; int x = 0, y = 0;
float scale = this->getDpiMultiplier(); float scale = this->getDpiMultiplier();
bool customFrame = this->parentWindow->hasCustomWindowFrame();
if (!showButtons || settings.hidePreferencesButton) { if (!this->showButtons || settings.hidePreferencesButton || customFrame) {
this->settingsButton.hide(); this->settingsButton.hide();
} else { } else {
this->settingsButton.show(); this->settingsButton.show();
x += settingsButton.width(); x += settingsButton.width();
} }
if (!showButtons || settings.hideUserButton) { if (!this->showButtons || settings.hideUserButton || customFrame) {
this->userButton.hide(); this->userButton.hide();
} else { } else {
this->userButton.move(x, 0); this->userButton.move(x, 0);
@ -211,7 +213,8 @@ void Notebook::performLayout(bool animated)
x += userButton.width(); x += userButton.width();
} }
if (!showButtons || (settings.hideUserButton && settings.hidePreferencesButton)) { if (customFrame || !this->showButtons ||
(settings.hideUserButton && settings.hidePreferencesButton)) {
x += (int)(scale * 2); x += (int)(scale * 2);
} }

View file

@ -57,6 +57,8 @@ public slots:
void addPageButtonClicked(); void addPageButtonClicked();
private: private:
Window *parentWindow;
QList<SplitContainer *> pages; QList<SplitContainer *> pages;
NotebookButton addButton; NotebookButton addButton;

View file

@ -18,7 +18,7 @@ namespace widgets {
Window::Window(const QString &windowName, singletons::ThemeManager &_themeManager, Window::Window(const QString &windowName, singletons::ThemeManager &_themeManager,
bool _isMainWindow) bool _isMainWindow)
: BaseWindow(_themeManager, nullptr) : BaseWindow(_themeManager, nullptr, true)
, settingRoot(fS("/windows/{}", windowName)) , settingRoot(fS("/windows/{}", windowName))
, windowGeometry(this->settingRoot) , windowGeometry(this->settingRoot)
, dpi(this->getDpiMultiplier()) , dpi(this->getDpiMultiplier())
@ -34,6 +34,11 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
} }
}); });
if (this->hasCustomWindowFrame()) {
this->addTitleBarButton("Preferences");
this->addTitleBarButton("User");
}
QVBoxLayout *layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
// add titlebar // add titlebar
@ -42,7 +47,7 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
// } // }
layout->addWidget(&this->notebook); layout->addWidget(&this->notebook);
setLayout(layout); this->getLayoutContainer()->setLayout(layout);
// set margin // set margin
// if (SettingsManager::getInstance().useCustomWindowFrame.get()) { // if (SettingsManager::getInstance().useCustomWindowFrame.get()) {
@ -139,14 +144,6 @@ void Window::closeEvent(QCloseEvent *)
this->closed(); this->closed();
} }
void Window::refreshTheme()
{
QPalette palette;
palette.setColor(QPalette::Background,
this->themeManager.tabs.regular.backgrounds.regular.color());
this->setPalette(palette);
}
void Window::loadGeometry() void Window::loadGeometry()
{ {
bool doSetGeometry = false; bool doSetGeometry = false;

View file

@ -63,8 +63,6 @@ private:
float dpi; float dpi;
virtual void refreshTheme() override;
void loadGeometry(); void loadGeometry();
Notebook notebook; Notebook notebook;