diff --git a/appveyor.yml b/appveyor.yml index d7423b275..f215d8d15 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,59 +1,59 @@ -version: "{build}" -branches: - only: - - master -image: Visual Studio 2017 -platform: Any CPU -clone_depth: 1 -init: -- cmd: '' -install: -- cmd: >- - git submodule update --init --recursive - - set QTDIR=C:\Qt\5.11\msvc2017_64 - - set PATH=%PATH%;%QTDIR%\bin - - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 - - pip install conan -q -build_script: -- cmd: >- - dir - - mkdir build - - cd build - - conan install .. - - set dateOfBuild=%date:~7,2%.%date:~4,2%.%date:~10,4% - - qmake ../chatterino.pro DEFINES+="CHATTERINO_NIGHTLY_VERSION_STRING=\\\"'$s%dateOfBuild% '$$system(git describe --always)-$$system(git rev-list master --count)\\\"" - - set cl=/MP - - nmake /S /NOLOGO - - windeployqt release/chatterino.exe --release --no-compiler-runtime --no-translations --no-opengl-sw --dir Chatterino2/ - - cp release/chatterino.exe Chatterino2/ - - 7z a chatterino-windows-x86-64.zip Chatterino2/ -artifacts: -- path: build/chatterino-windows-x86-64.zip - name: chatterino -deploy: -- provider: GitHub - tag: nightly-build - release: nightly-build - description: 'nightly v$(appveyor_build_version) built $(APPVEYOR_REPO_COMMIT_TIMESTAMP)\nLast change: $(APPVEYOR_REPO_COMMIT_MESSAGE) \n$(APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED)' - auth_token: - secure: sAJzAbiQSsYZLT+byDar9u61X0E9o35anaPMSFkOzdHeDFHjx1kW4cDP/4EEbxhx - repository: Chatterino/chatterino2 - artifact: build/chatterino-windows-x86-64.zip - prerelease: true - force_update: true - on: - branch: master +version: "{build}" +branches: + only: + - master +image: Visual Studio 2017 +platform: Any CPU +clone_depth: 1 +init: +- cmd: '' +install: +- cmd: >- + git submodule update --init --recursive + + set QTDIR=C:\Qt\5.11\msvc2017_64 + + set PATH=%PATH%;%QTDIR%\bin + + call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 + + pip install conan -q +build_script: +- cmd: >- + dir + + mkdir build + + cd build + + conan install .. + + set dateOfBuild=%date:~7,2%.%date:~4,2%.%date:~10,4% + + qmake ../chatterino.pro DEFINES+="CHATTERINO_NIGHTLY_VERSION_STRING=\\\"'$s%dateOfBuild% '$$system(git describe --always)-$$system(git rev-list master --count)\\\"" + + set cl=/MP + + nmake /S /NOLOGO + + windeployqt release/chatterino.exe --release --no-compiler-runtime --no-translations --no-opengl-sw --dir Chatterino2/ + + cp release/chatterino.exe Chatterino2/ + + 7z a chatterino-windows-x86-64.zip Chatterino2/ +artifacts: +- path: build/chatterino-windows-x86-64.zip + name: chatterino +deploy: +- provider: GitHub + tag: nightly-build + release: nightly-build + description: 'nightly v$(appveyor_build_version) built $(APPVEYOR_REPO_COMMIT_TIMESTAMP)\nLast change: $(APPVEYOR_REPO_COMMIT_MESSAGE) \n$(APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED)' + auth_token: + secure: sAJzAbiQSsYZLT+byDar9u61X0E9o35anaPMSFkOzdHeDFHjx1kW4cDP/4EEbxhx + repository: Chatterino/chatterino2 + artifact: build/chatterino-windows-x86-64.zip + prerelease: true + force_update: true + on: + branch: master diff --git a/conanfile.txt b/conanfile.txt index d9bedecdd..adde363f7 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -1,12 +1,12 @@ -[requires] -OpenSSL/1.0.2o@conan/stable -boost/1.69.0@conan/stable - -[generators] -qmake - -[options] -OpenSSL:shared=True - -[imports] -bin, *.dll -> ./Chatterino2 @ keep_path=False +[requires] +OpenSSL/1.0.2o@conan/stable +boost/1.69.0@conan/stable + +[generators] +qmake + +[options] +OpenSSL:shared=True + +[imports] +bin, *.dll -> ./Chatterino2 @ keep_path=False diff --git a/lib/appbase/BaseSettings.cpp b/lib/appbase/BaseSettings.cpp index 78c179268..01897555d 100644 --- a/lib/appbase/BaseSettings.cpp +++ b/lib/appbase/BaseSettings.cpp @@ -1,128 +1,128 @@ -#include "BaseSettings.hpp" - -#include - -#include "util/Clamp.hpp" - -namespace AB_NAMESPACE { - -std::vector> _settings; - -AB_SETTINGS_CLASS *AB_SETTINGS_CLASS::instance = nullptr; - -void _actuallyRegisterSetting( - std::weak_ptr setting) -{ - _settings.push_back(setting); -} - -AB_SETTINGS_CLASS::AB_SETTINGS_CLASS(const QString &settingsDirectory) -{ - AB_SETTINGS_CLASS::instance = this; - - QString settingsPath = settingsDirectory + "/settings.json"; - - // get global instance of the settings library - auto settingsInstance = pajlada::Settings::SettingManager::getInstance(); - - settingsInstance->load(qPrintable(settingsPath)); - - settingsInstance->setBackupEnabled(true); - settingsInstance->setBackupSlots(9); - settingsInstance->saveMethod = - pajlada::Settings::SettingManager::SaveMethod::SaveOnExit; -} - -void AB_SETTINGS_CLASS::saveSnapshot() -{ - rapidjson::Document *d = new rapidjson::Document(rapidjson::kObjectType); - rapidjson::Document::AllocatorType &a = d->GetAllocator(); - - for (const auto &weakSetting : _settings) - { - auto setting = weakSetting.lock(); - if (!setting) - { - continue; - } - - rapidjson::Value key(setting->getPath().c_str(), a); - auto curVal = setting->unmarshalJSON(); - if (curVal == nullptr) - { - continue; - } - - rapidjson::Value val; - val.CopyFrom(*curVal, a); - d->AddMember(key.Move(), val.Move(), a); - } - - // log("Snapshot state: {}", rj::stringify(*d)); - - this->snapshot_.reset(d); -} - -void AB_SETTINGS_CLASS::restoreSnapshot() -{ - if (!this->snapshot_) - { - return; - } - - const auto &snapshot = *(this->snapshot_.get()); - - if (!snapshot.IsObject()) - { - return; - } - - for (const auto &weakSetting : _settings) - { - auto setting = weakSetting.lock(); - if (!setting) - { - continue; - } - - const char *path = setting->getPath().c_str(); - - if (!snapshot.HasMember(path)) - { - continue; - } - - setting->marshalJSON(snapshot[path]); - } -} - -float AB_SETTINGS_CLASS::getClampedUiScale() const -{ - return clamp(this->uiScale.getValue(), 0.2f, 10); -} - -void AB_SETTINGS_CLASS::setClampedUiScale(float value) -{ - this->uiScale.setValue(clamp(value, 0.2f, 10)); -} - -#ifndef AB_CUSTOM_SETTINGS -Settings *getSettings() -{ - static_assert(std::is_same_v, - "`AB_SETTINGS_CLASS` must be the same as `Settings`"); - - assert(AB_SETTINGS_CLASS::instance); - - return AB_SETTINGS_CLASS::instance; -} -#endif - -AB_SETTINGS_CLASS *getABSettings() -{ - assert(AB_SETTINGS_CLASS::instance); - - return AB_SETTINGS_CLASS::instance; -} - -} // namespace AB_NAMESPACE +#include "BaseSettings.hpp" + +#include + +#include "util/Clamp.hpp" + +namespace AB_NAMESPACE { + +std::vector> _settings; + +AB_SETTINGS_CLASS *AB_SETTINGS_CLASS::instance = nullptr; + +void _actuallyRegisterSetting( + std::weak_ptr setting) +{ + _settings.push_back(setting); +} + +AB_SETTINGS_CLASS::AB_SETTINGS_CLASS(const QString &settingsDirectory) +{ + AB_SETTINGS_CLASS::instance = this; + + QString settingsPath = settingsDirectory + "/settings.json"; + + // get global instance of the settings library + auto settingsInstance = pajlada::Settings::SettingManager::getInstance(); + + settingsInstance->load(qPrintable(settingsPath)); + + settingsInstance->setBackupEnabled(true); + settingsInstance->setBackupSlots(9); + settingsInstance->saveMethod = + pajlada::Settings::SettingManager::SaveMethod::SaveOnExit; +} + +void AB_SETTINGS_CLASS::saveSnapshot() +{ + rapidjson::Document *d = new rapidjson::Document(rapidjson::kObjectType); + rapidjson::Document::AllocatorType &a = d->GetAllocator(); + + for (const auto &weakSetting : _settings) + { + auto setting = weakSetting.lock(); + if (!setting) + { + continue; + } + + rapidjson::Value key(setting->getPath().c_str(), a); + auto curVal = setting->unmarshalJSON(); + if (curVal == nullptr) + { + continue; + } + + rapidjson::Value val; + val.CopyFrom(*curVal, a); + d->AddMember(key.Move(), val.Move(), a); + } + + // log("Snapshot state: {}", rj::stringify(*d)); + + this->snapshot_.reset(d); +} + +void AB_SETTINGS_CLASS::restoreSnapshot() +{ + if (!this->snapshot_) + { + return; + } + + const auto &snapshot = *(this->snapshot_.get()); + + if (!snapshot.IsObject()) + { + return; + } + + for (const auto &weakSetting : _settings) + { + auto setting = weakSetting.lock(); + if (!setting) + { + continue; + } + + const char *path = setting->getPath().c_str(); + + if (!snapshot.HasMember(path)) + { + continue; + } + + setting->marshalJSON(snapshot[path]); + } +} + +float AB_SETTINGS_CLASS::getClampedUiScale() const +{ + return clamp(this->uiScale.getValue(), 0.2f, 10); +} + +void AB_SETTINGS_CLASS::setClampedUiScale(float value) +{ + this->uiScale.setValue(clamp(value, 0.2f, 10)); +} + +#ifndef AB_CUSTOM_SETTINGS +Settings *getSettings() +{ + static_assert(std::is_same_v, + "`AB_SETTINGS_CLASS` must be the same as `Settings`"); + + assert(AB_SETTINGS_CLASS::instance); + + return AB_SETTINGS_CLASS::instance; +} +#endif + +AB_SETTINGS_CLASS *getABSettings() +{ + assert(AB_SETTINGS_CLASS::instance); + + return AB_SETTINGS_CLASS::instance; +} + +} // namespace AB_NAMESPACE diff --git a/lib/appbase/BaseSettings.hpp b/lib/appbase/BaseSettings.hpp index c8a2cba36..58b1f8465 100644 --- a/lib/appbase/BaseSettings.hpp +++ b/lib/appbase/BaseSettings.hpp @@ -1,52 +1,52 @@ -#ifndef AB_SETTINGS_H -#define AB_SETTINGS_H - -#include -#include -#include -#include - -#include "common/ChatterinoSetting.hpp" - -#ifdef AB_CUSTOM_SETTINGS -# define AB_SETTINGS_CLASS ABSettings -#else -# define AB_SETTINGS_CLASS Settings -#endif - -namespace AB_NAMESPACE { - -class Settings; - -void _actuallyRegisterSetting( - std::weak_ptr setting); - -class AB_SETTINGS_CLASS -{ -public: - AB_SETTINGS_CLASS(const QString &settingsDirectory); - - void saveSnapshot(); - void restoreSnapshot(); - - static AB_SETTINGS_CLASS *instance; - - FloatSetting uiScale = {"/appearance/uiScale2", 1}; - BoolSetting windowTopMost = {"/appearance/windowAlwaysOnTop", false}; - - float getClampedUiScale() const; - void setClampedUiScale(float value); - -private: - std::unique_ptr snapshot_; -}; - -Settings *getSettings(); -AB_SETTINGS_CLASS *getABSettings(); - -} // namespace AB_NAMESPACE - -#ifdef CHATTERINO -# include "singletons/Settings.hpp" -#endif -#endif +#ifndef AB_SETTINGS_H +#define AB_SETTINGS_H + +#include +#include +#include +#include + +#include "common/ChatterinoSetting.hpp" + +#ifdef AB_CUSTOM_SETTINGS +# define AB_SETTINGS_CLASS ABSettings +#else +# define AB_SETTINGS_CLASS Settings +#endif + +namespace AB_NAMESPACE { + +class Settings; + +void _actuallyRegisterSetting( + std::weak_ptr setting); + +class AB_SETTINGS_CLASS +{ +public: + AB_SETTINGS_CLASS(const QString &settingsDirectory); + + void saveSnapshot(); + void restoreSnapshot(); + + static AB_SETTINGS_CLASS *instance; + + FloatSetting uiScale = {"/appearance/uiScale2", 1}; + BoolSetting windowTopMost = {"/appearance/windowAlwaysOnTop", false}; + + float getClampedUiScale() const; + void setClampedUiScale(float value); + +private: + std::unique_ptr snapshot_; +}; + +Settings *getSettings(); +AB_SETTINGS_CLASS *getABSettings(); + +} // namespace AB_NAMESPACE + +#ifdef CHATTERINO +# include "singletons/Settings.hpp" +#endif +#endif diff --git a/lib/appbase/BaseTheme.cpp b/lib/appbase/BaseTheme.cpp index 2342222fe..ab4e314d1 100644 --- a/lib/appbase/BaseTheme.cpp +++ b/lib/appbase/BaseTheme.cpp @@ -1,225 +1,225 @@ -#include "BaseTheme.hpp" - -namespace AB_NAMESPACE { -namespace { - double getMultiplierByTheme(const QString &themeName) - { - if (themeName == "Light") - { - return 0.8; - } - else if (themeName == "White") - { - return 1.0; - } - else if (themeName == "Black") - { - return -1.0; - } - else if (themeName == "Dark") - { - return -0.8; - } - /* - else if (themeName == "Custom") - { - return getSettings()->customThemeMultiplier.getValue(); - } - */ - - return -0.8; - } -} // namespace - -bool AB_THEME_CLASS::isLightTheme() const -{ - return this->isLight_; -} - -void AB_THEME_CLASS::update() -{ - this->actuallyUpdate(this->themeHue, - getMultiplierByTheme(this->themeName.getValue())); - - this->updated.invoke(); -} - -void AB_THEME_CLASS::actuallyUpdate(double hue, double multiplier) -{ - this->isLight_ = multiplier > 0; - bool lightWin = isLight_; - - // QColor themeColor = QColor::fromHslF(hue, 0.43, 0.5); - QColor themeColor = QColor::fromHslF(hue, 0.8, 0.5); - QColor themeColorNoSat = QColor::fromHslF(hue, 0, 0.5); - - qreal sat = 0; - // 0.05; - - auto getColor = [multiplier](double h, double s, double l, double a = 1.0) { - return QColor::fromHslF(h, s, ((l - 0.5) * multiplier) + 0.5, a); - }; - - /// WINDOW - { -#ifdef Q_OS_LINUX - this->window.background = lightWin ? "#fff" : QColor(61, 60, 56); -#else - this->window.background = lightWin ? "#fff" : "#111"; -#endif - - QColor fg = this->window.text = lightWin ? "#000" : "#eee"; - this->window.borderFocused = lightWin ? "#ccc" : themeColor; - this->window.borderUnfocused = lightWin ? "#ccc" : themeColorNoSat; - - // Ubuntu style - // TODO: add setting for this - // TabText = QColor(210, 210, 210); - // TabBackground = QColor(61, 60, 56); - // TabHoverText = QColor(210, 210, 210); - // TabHoverBackground = QColor(73, 72, 68); - - // message (referenced later) - this->messages.textColors.caret = // - this->messages.textColors.regular = isLight_ ? "#000" : "#fff"; - - QColor highlighted = lightWin ? QColor("#ff0000") : QColor("#ee6166"); - - /// TABS - if (lightWin) - { - this->tabs.regular = { - QColor("#444"), - {QColor("#fff"), QColor("#eee"), QColor("#fff")}, - {QColor("#fff"), QColor("#fff"), QColor("#fff")}}; - this->tabs.newMessage = { - QColor("#222"), - {QColor("#fff"), QColor("#eee"), QColor("#fff")}, - {QColor("#bbb"), QColor("#bbb"), QColor("#bbb")}}; - this->tabs.highlighted = { - fg, - {QColor("#fff"), QColor("#eee"), QColor("#fff")}, - {highlighted, highlighted, highlighted}}; - this->tabs.selected = { - QColor("#000"), - {QColor("#b4d7ff"), QColor("#b4d7ff"), QColor("#b4d7ff")}, - {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; - } - else - { - this->tabs.regular = { - QColor("#aaa"), - {QColor("#252525"), QColor("#252525"), QColor("#252525")}, - {QColor("#444"), QColor("#444"), QColor("#444")}}; - this->tabs.newMessage = { - fg, - {QColor("#252525"), QColor("#252525"), QColor("#252525")}, - {QColor("#888"), QColor("#888"), QColor("#888")}}; - this->tabs.highlighted = { - fg, - {QColor("#252525"), QColor("#252525"), QColor("#252525")}, - {highlighted, highlighted, highlighted}}; - - this->tabs.selected = { - QColor("#fff"), - {QColor("#555555"), QColor("#555555"), QColor("#555555")}, - {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; - } - - // scrollbar - this->scrollbars.highlights.highlight = QColor("#ee6166"); - this->scrollbars.highlights.subscription = QColor("#C466FF"); - - // this->tabs.newMessage = { - // fg, - // {QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern), - // QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern), - // QBrush(blendColors(themeColorNoSat, "#ccc", 0.9), - // Qt::FDiagPattern)}}; - - // this->tabs.newMessage = { - // fg, - // {QBrush(blendColors(themeColor, "#666", 0.7), - // Qt::FDiagPattern), - // QBrush(blendColors(themeColor, "#666", 0.5), - // Qt::FDiagPattern), - // QBrush(blendColors(themeColorNoSat, "#666", 0.7), - // Qt::FDiagPattern)}}; - // this->tabs.highlighted = {fg, {QColor("#777"), - // QColor("#777"), QColor("#666")}}; - - this->tabs.bottomLine = this->tabs.selected.backgrounds.regular.color(); - } // namespace AB_NAMESPACE - - // Split - bool flat = isLight_; - - // Message - this->messages.textColors.link = - isLight_ ? QColor(66, 134, 244) : QColor(66, 134, 244); - this->messages.textColors.system = QColor(140, 127, 127); - - this->messages.backgrounds.regular = getColor(0, sat, 1); - this->messages.backgrounds.alternate = getColor(0, sat, 0.96); - - if (isLight_) - { - this->messages.backgrounds.highlighted = - blendColors(themeColor, this->messages.backgrounds.regular, 0.8); - } - else - { - // REMOVED - // this->messages.backgrounds.highlighted = - // QColor(getSettings()->highlightColor); - } - - this->messages.backgrounds.subscription = - blendColors(QColor("#C466FF"), this->messages.backgrounds.regular, 0.7); - - // this->messages.backgrounds.resub - // this->messages.backgrounds.whisper - this->messages.disabled = getColor(0, sat, 1, 0.6); - // this->messages.seperator = - // this->messages.seperatorInner = - - // Scrollbar - this->scrollbars.background = QColor(0, 0, 0, 0); - // this->scrollbars.background = splits.background; - // this->scrollbars.background.setAlphaF(qreal(0.2)); - this->scrollbars.thumb = getColor(0, sat, 0.70); - this->scrollbars.thumbSelected = getColor(0, sat, 0.65); - - // tooltip - this->tooltip.background = QColor(0, 0, 0); - this->tooltip.text = QColor(255, 255, 255); - - // Selection - this->messages.selection = - isLightTheme() ? QColor(0, 0, 0, 64) : QColor(255, 255, 255, 64); -} - -QColor AB_THEME_CLASS::blendColors(const QColor &color1, const QColor &color2, - qreal ratio) -{ - int r = int(color1.red() * (1 - ratio) + color2.red() * ratio); - int g = int(color1.green() * (1 - ratio) + color2.green() * ratio); - int b = int(color1.blue() * (1 - ratio) + color2.blue() * ratio); - - return QColor(r, g, b, 255); -} - -#ifndef AB_CUSTOM_THEME -Theme *getTheme() -{ - static auto theme = [] { - auto theme = new Theme(); - theme->update(); - return theme; - }(); - - return theme; -} -#endif - -} // namespace AB_NAMESPACE +#include "BaseTheme.hpp" + +namespace AB_NAMESPACE { +namespace { + double getMultiplierByTheme(const QString &themeName) + { + if (themeName == "Light") + { + return 0.8; + } + else if (themeName == "White") + { + return 1.0; + } + else if (themeName == "Black") + { + return -1.0; + } + else if (themeName == "Dark") + { + return -0.8; + } + /* + else if (themeName == "Custom") + { + return getSettings()->customThemeMultiplier.getValue(); + } + */ + + return -0.8; + } +} // namespace + +bool AB_THEME_CLASS::isLightTheme() const +{ + return this->isLight_; +} + +void AB_THEME_CLASS::update() +{ + this->actuallyUpdate(this->themeHue, + getMultiplierByTheme(this->themeName.getValue())); + + this->updated.invoke(); +} + +void AB_THEME_CLASS::actuallyUpdate(double hue, double multiplier) +{ + this->isLight_ = multiplier > 0; + bool lightWin = isLight_; + + // QColor themeColor = QColor::fromHslF(hue, 0.43, 0.5); + QColor themeColor = QColor::fromHslF(hue, 0.8, 0.5); + QColor themeColorNoSat = QColor::fromHslF(hue, 0, 0.5); + + qreal sat = 0; + // 0.05; + + auto getColor = [multiplier](double h, double s, double l, double a = 1.0) { + return QColor::fromHslF(h, s, ((l - 0.5) * multiplier) + 0.5, a); + }; + + /// WINDOW + { +#ifdef Q_OS_LINUX + this->window.background = lightWin ? "#fff" : QColor(61, 60, 56); +#else + this->window.background = lightWin ? "#fff" : "#111"; +#endif + + QColor fg = this->window.text = lightWin ? "#000" : "#eee"; + this->window.borderFocused = lightWin ? "#ccc" : themeColor; + this->window.borderUnfocused = lightWin ? "#ccc" : themeColorNoSat; + + // Ubuntu style + // TODO: add setting for this + // TabText = QColor(210, 210, 210); + // TabBackground = QColor(61, 60, 56); + // TabHoverText = QColor(210, 210, 210); + // TabHoverBackground = QColor(73, 72, 68); + + // message (referenced later) + this->messages.textColors.caret = // + this->messages.textColors.regular = isLight_ ? "#000" : "#fff"; + + QColor highlighted = lightWin ? QColor("#ff0000") : QColor("#ee6166"); + + /// TABS + if (lightWin) + { + this->tabs.regular = { + QColor("#444"), + {QColor("#fff"), QColor("#eee"), QColor("#fff")}, + {QColor("#fff"), QColor("#fff"), QColor("#fff")}}; + this->tabs.newMessage = { + QColor("#222"), + {QColor("#fff"), QColor("#eee"), QColor("#fff")}, + {QColor("#bbb"), QColor("#bbb"), QColor("#bbb")}}; + this->tabs.highlighted = { + fg, + {QColor("#fff"), QColor("#eee"), QColor("#fff")}, + {highlighted, highlighted, highlighted}}; + this->tabs.selected = { + QColor("#000"), + {QColor("#b4d7ff"), QColor("#b4d7ff"), QColor("#b4d7ff")}, + {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; + } + else + { + this->tabs.regular = { + QColor("#aaa"), + {QColor("#252525"), QColor("#252525"), QColor("#252525")}, + {QColor("#444"), QColor("#444"), QColor("#444")}}; + this->tabs.newMessage = { + fg, + {QColor("#252525"), QColor("#252525"), QColor("#252525")}, + {QColor("#888"), QColor("#888"), QColor("#888")}}; + this->tabs.highlighted = { + fg, + {QColor("#252525"), QColor("#252525"), QColor("#252525")}, + {highlighted, highlighted, highlighted}}; + + this->tabs.selected = { + QColor("#fff"), + {QColor("#555555"), QColor("#555555"), QColor("#555555")}, + {QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}}; + } + + // scrollbar + this->scrollbars.highlights.highlight = QColor("#ee6166"); + this->scrollbars.highlights.subscription = QColor("#C466FF"); + + // this->tabs.newMessage = { + // fg, + // {QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern), + // QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern), + // QBrush(blendColors(themeColorNoSat, "#ccc", 0.9), + // Qt::FDiagPattern)}}; + + // this->tabs.newMessage = { + // fg, + // {QBrush(blendColors(themeColor, "#666", 0.7), + // Qt::FDiagPattern), + // QBrush(blendColors(themeColor, "#666", 0.5), + // Qt::FDiagPattern), + // QBrush(blendColors(themeColorNoSat, "#666", 0.7), + // Qt::FDiagPattern)}}; + // this->tabs.highlighted = {fg, {QColor("#777"), + // QColor("#777"), QColor("#666")}}; + + this->tabs.bottomLine = this->tabs.selected.backgrounds.regular.color(); + } // namespace AB_NAMESPACE + + // Split + bool flat = isLight_; + + // Message + this->messages.textColors.link = + isLight_ ? QColor(66, 134, 244) : QColor(66, 134, 244); + this->messages.textColors.system = QColor(140, 127, 127); + + this->messages.backgrounds.regular = getColor(0, sat, 1); + this->messages.backgrounds.alternate = getColor(0, sat, 0.96); + + if (isLight_) + { + this->messages.backgrounds.highlighted = + blendColors(themeColor, this->messages.backgrounds.regular, 0.8); + } + else + { + // REMOVED + // this->messages.backgrounds.highlighted = + // QColor(getSettings()->highlightColor); + } + + this->messages.backgrounds.subscription = + blendColors(QColor("#C466FF"), this->messages.backgrounds.regular, 0.7); + + // this->messages.backgrounds.resub + // this->messages.backgrounds.whisper + this->messages.disabled = getColor(0, sat, 1, 0.6); + // this->messages.seperator = + // this->messages.seperatorInner = + + // Scrollbar + this->scrollbars.background = QColor(0, 0, 0, 0); + // this->scrollbars.background = splits.background; + // this->scrollbars.background.setAlphaF(qreal(0.2)); + this->scrollbars.thumb = getColor(0, sat, 0.70); + this->scrollbars.thumbSelected = getColor(0, sat, 0.65); + + // tooltip + this->tooltip.background = QColor(0, 0, 0); + this->tooltip.text = QColor(255, 255, 255); + + // Selection + this->messages.selection = + isLightTheme() ? QColor(0, 0, 0, 64) : QColor(255, 255, 255, 64); +} + +QColor AB_THEME_CLASS::blendColors(const QColor &color1, const QColor &color2, + qreal ratio) +{ + int r = int(color1.red() * (1 - ratio) + color2.red() * ratio); + int g = int(color1.green() * (1 - ratio) + color2.green() * ratio); + int b = int(color1.blue() * (1 - ratio) + color2.blue() * ratio); + + return QColor(r, g, b, 255); +} + +#ifndef AB_CUSTOM_THEME +Theme *getTheme() +{ + static auto theme = [] { + auto theme = new Theme(); + theme->update(); + return theme; + }(); + + return theme; +} +#endif + +} // namespace AB_NAMESPACE diff --git a/lib/appbase/BaseTheme.hpp b/lib/appbase/BaseTheme.hpp index d5bc9c406..21c6c46cd 100644 --- a/lib/appbase/BaseTheme.hpp +++ b/lib/appbase/BaseTheme.hpp @@ -1,117 +1,117 @@ -#ifndef AB_THEME_H -#define AB_THEME_H - -#include -#include -#include - -#ifdef AB_CUSTOM_THEME -# define AB_THEME_CLASS BaseTheme -#else -# define AB_THEME_CLASS Theme -#endif - -namespace AB_NAMESPACE { - -class Theme; - -class AB_THEME_CLASS -{ -public: - bool isLightTheme() const; - - struct TabColors { - QColor text; - struct { - QBrush regular; - QBrush hover; - QBrush unfocused; - } backgrounds; - struct { - QColor regular; - QColor hover; - QColor unfocused; - } line; - }; - - /// WINDOW - struct { - QColor background; - QColor text; - QColor borderUnfocused; - QColor borderFocused; - } window; - - /// TABS - struct { - TabColors regular; - TabColors newMessage; - TabColors highlighted; - TabColors selected; - QColor border; - QColor bottomLine; - } tabs; - - /// MESSAGES - struct { - struct { - QColor regular; - QColor caret; - QColor link; - QColor system; - } textColors; - - struct { - QColor regular; - QColor alternate; - QColor highlighted; - QColor subscription; - // QColor whisper; - } backgrounds; - - QColor disabled; - // QColor seperator; - // QColor seperatorInner; - QColor selection; - } messages; - - /// SCROLLBAR - struct { - QColor background; - QColor thumb; - QColor thumbSelected; - struct { - QColor highlight; - QColor subscription; - } highlights; - } scrollbars; - - /// TOOLTIP - struct { - QColor text; - QColor background; - } tooltip; - - void update(); - virtual void actuallyUpdate(double hue, double multiplier); - QColor blendColors(const QColor &color1, const QColor &color2, qreal ratio); - - pajlada::Signals::NoArgSignal updated; - - QStringSetting themeName{"/appearance/theme/name", "Dark"}; - DoubleSetting themeHue{"/appearance/theme/hue", 0.0}; - -private: - bool isLight_ = false; -}; - -// Implemented in parent project if AB_CUSTOM_THEME is set. -// Otherwise implemented in BaseThemecpp -Theme *getTheme(); - -} // namespace AB_NAMESPACE - -#ifdef CHATTERINO -# include "singletons/Theme.hpp" -#endif -#endif +#ifndef AB_THEME_H +#define AB_THEME_H + +#include +#include +#include + +#ifdef AB_CUSTOM_THEME +# define AB_THEME_CLASS BaseTheme +#else +# define AB_THEME_CLASS Theme +#endif + +namespace AB_NAMESPACE { + +class Theme; + +class AB_THEME_CLASS +{ +public: + bool isLightTheme() const; + + struct TabColors { + QColor text; + struct { + QBrush regular; + QBrush hover; + QBrush unfocused; + } backgrounds; + struct { + QColor regular; + QColor hover; + QColor unfocused; + } line; + }; + + /// WINDOW + struct { + QColor background; + QColor text; + QColor borderUnfocused; + QColor borderFocused; + } window; + + /// TABS + struct { + TabColors regular; + TabColors newMessage; + TabColors highlighted; + TabColors selected; + QColor border; + QColor bottomLine; + } tabs; + + /// MESSAGES + struct { + struct { + QColor regular; + QColor caret; + QColor link; + QColor system; + } textColors; + + struct { + QColor regular; + QColor alternate; + QColor highlighted; + QColor subscription; + // QColor whisper; + } backgrounds; + + QColor disabled; + // QColor seperator; + // QColor seperatorInner; + QColor selection; + } messages; + + /// SCROLLBAR + struct { + QColor background; + QColor thumb; + QColor thumbSelected; + struct { + QColor highlight; + QColor subscription; + } highlights; + } scrollbars; + + /// TOOLTIP + struct { + QColor text; + QColor background; + } tooltip; + + void update(); + virtual void actuallyUpdate(double hue, double multiplier); + QColor blendColors(const QColor &color1, const QColor &color2, qreal ratio); + + pajlada::Signals::NoArgSignal updated; + + QStringSetting themeName{"/appearance/theme/name", "Dark"}; + DoubleSetting themeHue{"/appearance/theme/hue", 0.0}; + +private: + bool isLight_ = false; +}; + +// Implemented in parent project if AB_CUSTOM_THEME is set. +// Otherwise implemented in BaseThemecpp +Theme *getTheme(); + +} // namespace AB_NAMESPACE + +#ifdef CHATTERINO +# include "singletons/Theme.hpp" +#endif +#endif diff --git a/lib/appbase/common/ChatterinoSetting.hpp b/lib/appbase/common/ChatterinoSetting.hpp index 6b965f230..cebcb33f2 100644 --- a/lib/appbase/common/ChatterinoSetting.hpp +++ b/lib/appbase/common/ChatterinoSetting.hpp @@ -1,54 +1,54 @@ -#pragma once - -#include -#include - -namespace AB_NAMESPACE { - -void _registerSetting(std::weak_ptr setting); - -template -class ChatterinoSetting : public pajlada::Settings::Setting -{ -public: - ChatterinoSetting(const std::string &path) - : pajlada::Settings::Setting(path) - { - _registerSetting(this->getData()); - } - - ChatterinoSetting(const std::string &path, const Type &defaultValue) - : pajlada::Settings::Setting(path, defaultValue) - { - _registerSetting(this->getData()); - } - - template - ChatterinoSetting &operator=(const T2 &newValue) - { - this->setValue(newValue); - - return *this; - } - - ChatterinoSetting &operator=(Type &&newValue) noexcept - { - pajlada::Settings::Setting::operator=(newValue); - - return *this; - } - - using pajlada::Settings::Setting::operator==; - using pajlada::Settings::Setting::operator!=; - - using pajlada::Settings::Setting::operator Type; -}; - -using BoolSetting = ChatterinoSetting; -using FloatSetting = ChatterinoSetting; -using DoubleSetting = ChatterinoSetting; -using IntSetting = ChatterinoSetting; -using StringSetting = ChatterinoSetting; -using QStringSetting = ChatterinoSetting; - -} // namespace AB_NAMESPACE +#pragma once + +#include +#include + +namespace AB_NAMESPACE { + +void _registerSetting(std::weak_ptr setting); + +template +class ChatterinoSetting : public pajlada::Settings::Setting +{ +public: + ChatterinoSetting(const std::string &path) + : pajlada::Settings::Setting(path) + { + _registerSetting(this->getData()); + } + + ChatterinoSetting(const std::string &path, const Type &defaultValue) + : pajlada::Settings::Setting(path, defaultValue) + { + _registerSetting(this->getData()); + } + + template + ChatterinoSetting &operator=(const T2 &newValue) + { + this->setValue(newValue); + + return *this; + } + + ChatterinoSetting &operator=(Type &&newValue) noexcept + { + pajlada::Settings::Setting::operator=(newValue); + + return *this; + } + + using pajlada::Settings::Setting::operator==; + using pajlada::Settings::Setting::operator!=; + + using pajlada::Settings::Setting::operator Type; +}; + +using BoolSetting = ChatterinoSetting; +using FloatSetting = ChatterinoSetting; +using DoubleSetting = ChatterinoSetting; +using IntSetting = ChatterinoSetting; +using StringSetting = ChatterinoSetting; +using QStringSetting = ChatterinoSetting; + +} // namespace AB_NAMESPACE diff --git a/lib/appbase/common/FlagsEnum.hpp b/lib/appbase/common/FlagsEnum.hpp index 1a393e9c6..9df975872 100644 --- a/lib/appbase/common/FlagsEnum.hpp +++ b/lib/appbase/common/FlagsEnum.hpp @@ -1,82 +1,82 @@ -#pragma once - -#include - -namespace chatterino { - -template ::type> -class FlagsEnum -{ -public: - FlagsEnum() - : value_(static_cast(0)) - { - } - - FlagsEnum(T value) - : value_(value) - { - } - - FlagsEnum(std::initializer_list flags) - { - for (auto flag : flags) - { - this->set(flag); - } - } - - bool operator==(const FlagsEnum &other) - { - return this->value_ == other.value_; - } - - bool operator!=(const FlagsEnum &other) - { - return this->value_ != other.value_; - } - - void set(T flag) - { - reinterpret_cast(this->value_) |= static_cast(flag); - } - - void unset(T flag) - { - reinterpret_cast(this->value_) &= ~static_cast(flag); - } - - void set(T flag, bool value) - { - if (value) - this->set(flag); - else - this->unset(flag); - } - - bool has(T flag) const - { - return static_cast(this->value_) & static_cast(flag); - } - - bool hasAny(FlagsEnum flags) const - { - return static_cast(this->value_) & static_cast(flags.value_); - } - - bool hasAll(FlagsEnum flags) const - { - return (static_cast(this->value_) & static_cast(flags.value_)) && - static_cast(flags->value); - } - - bool hasNone(std::initializer_list flags) const - { - return !this->hasAny(flags); - } - -private: - T value_{}; -}; - -} // namespace chatterino +#pragma once + +#include + +namespace chatterino { + +template ::type> +class FlagsEnum +{ +public: + FlagsEnum() + : value_(static_cast(0)) + { + } + + FlagsEnum(T value) + : value_(value) + { + } + + FlagsEnum(std::initializer_list flags) + { + for (auto flag : flags) + { + this->set(flag); + } + } + + bool operator==(const FlagsEnum &other) + { + return this->value_ == other.value_; + } + + bool operator!=(const FlagsEnum &other) + { + return this->value_ != other.value_; + } + + void set(T flag) + { + reinterpret_cast(this->value_) |= static_cast(flag); + } + + void unset(T flag) + { + reinterpret_cast(this->value_) &= ~static_cast(flag); + } + + void set(T flag, bool value) + { + if (value) + this->set(flag); + else + this->unset(flag); + } + + bool has(T flag) const + { + return static_cast(this->value_) & static_cast(flag); + } + + bool hasAny(FlagsEnum flags) const + { + return static_cast(this->value_) & static_cast(flags.value_); + } + + bool hasAll(FlagsEnum flags) const + { + return (static_cast(this->value_) & static_cast(flags.value_)) && + static_cast(flags->value); + } + + bool hasNone(std::initializer_list flags) const + { + return !this->hasAny(flags); + } + +private: + T value_{}; +}; + +} // namespace chatterino diff --git a/lib/appbase/common/Singleton.hpp b/lib/appbase/common/Singleton.hpp index 3aa6c5f83..5bf3857a1 100644 --- a/lib/appbase/common/Singleton.hpp +++ b/lib/appbase/common/Singleton.hpp @@ -1,26 +1,26 @@ -#pragma once - -#include - -namespace AB_NAMESPACE { - -class Settings; -class Paths; - -class Singleton : boost::noncopyable -{ -public: - virtual ~Singleton() = default; - - virtual void initialize(Settings &settings, Paths &paths) - { - (void)(settings); - (void)(paths); - } - - virtual void save() - { - } -}; - -} // namespace AB_NAMESPACE +#pragma once + +#include + +namespace AB_NAMESPACE { + +class Settings; +class Paths; + +class Singleton : boost::noncopyable +{ +public: + virtual ~Singleton() = default; + + virtual void initialize(Settings &settings, Paths &paths) + { + (void)(settings); + (void)(paths); + } + + virtual void save() + { + } +}; + +} // namespace AB_NAMESPACE diff --git a/lib/appbase/debug/AssertInGuiThread.hpp b/lib/appbase/debug/AssertInGuiThread.hpp index a9ba41fcf..b43f2695d 100644 --- a/lib/appbase/debug/AssertInGuiThread.hpp +++ b/lib/appbase/debug/AssertInGuiThread.hpp @@ -1,21 +1,21 @@ -#pragma once - -#include -#include -#include - -namespace AB_NAMESPACE { - -static bool isGuiThread() -{ - return QCoreApplication::instance()->thread() == QThread::currentThread(); -} - -static void assertInGuiThread() -{ -#ifdef _DEBUG - assert(isGuiThread()); -#endif -} - -} // namespace AB_NAMESPACE +#pragma once + +#include +#include +#include + +namespace AB_NAMESPACE { + +static bool isGuiThread() +{ + return QCoreApplication::instance()->thread() == QThread::currentThread(); +} + +static void assertInGuiThread() +{ +#ifdef _DEBUG + assert(isGuiThread()); +#endif +} + +} // namespace AB_NAMESPACE diff --git a/lib/appbase/debug/Benchmark.cpp b/lib/appbase/debug/Benchmark.cpp index 765ebcb09..afa80cf27 100644 --- a/lib/appbase/debug/Benchmark.cpp +++ b/lib/appbase/debug/Benchmark.cpp @@ -1,21 +1,21 @@ -#include "Benchmark.hpp" - -namespace AB_NAMESPACE { - -BenchmarkGuard::BenchmarkGuard(const QString &_name) - : name_(_name) -{ - timer_.start(); -} - -BenchmarkGuard::~BenchmarkGuard() -{ - log("{} {} ms", this->name_, float(timer_.nsecsElapsed()) / 1000000.0f); -} - -qreal BenchmarkGuard::getElapsedMs() -{ - return qreal(timer_.nsecsElapsed()) / 1000000.0; -} - -} // namespace AB_NAMESPACE +#include "Benchmark.hpp" + +namespace AB_NAMESPACE { + +BenchmarkGuard::BenchmarkGuard(const QString &_name) + : name_(_name) +{ + timer_.start(); +} + +BenchmarkGuard::~BenchmarkGuard() +{ + log("{} {} ms", this->name_, float(timer_.nsecsElapsed()) / 1000000.0f); +} + +qreal BenchmarkGuard::getElapsedMs() +{ + return qreal(timer_.nsecsElapsed()) / 1000000.0; +} + +} // namespace AB_NAMESPACE diff --git a/lib/appbase/main.cpp b/lib/appbase/main.cpp index 7a1254e89..6b95a9c2f 100644 --- a/lib/appbase/main.cpp +++ b/lib/appbase/main.cpp @@ -1,31 +1,31 @@ -#include -#include -#include -#include - -#include "ABSettings.hpp" -#include "ABTheme.hpp" -#include "singletons/Fonts.hpp" -#include "widgets/BaseWindow.hpp" - -int main(int argc, char *argv[]) -{ - using namespace AB_NAMESPACE; - - QApplication a(argc, argv); - - auto path = - QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - qDebug() << path; - - QDir(path).mkdir("."); - - new Settings(path); - new Fonts(); - - BaseWindow widget(nullptr, BaseWindow::EnableCustomFrame); - widget.setWindowTitle("asdf"); - widget.show(); - - return a.exec(); -} +#include +#include +#include +#include + +#include "ABSettings.hpp" +#include "ABTheme.hpp" +#include "singletons/Fonts.hpp" +#include "widgets/BaseWindow.hpp" + +int main(int argc, char *argv[]) +{ + using namespace AB_NAMESPACE; + + QApplication a(argc, argv); + + auto path = + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + qDebug() << path; + + QDir(path).mkdir("."); + + new Settings(path); + new Fonts(); + + BaseWindow widget(nullptr, BaseWindow::EnableCustomFrame); + widget.setWindowTitle("asdf"); + widget.show(); + + return a.exec(); +} diff --git a/lib/appbase/main.pro b/lib/appbase/main.pro index 0d3bd1c95..bc4645530 100644 --- a/lib/appbase/main.pro +++ b/lib/appbase/main.pro @@ -1,100 +1,100 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2018-11-19T19:03:22 -# -#------------------------------------------------- - -!AB_NOT_STANDALONE { - message(appbase standalone) - QT += core gui widgets - TARGET = main - TEMPLATE = app - SOURCES += main.cpp - - # https://bugreports.qt.io/browse/QTBUG-27018 - equals(QMAKE_CXX, "clang++")|equals(QMAKE_CXX, "g++") { - TARGET = bin/appbase - } -} - -#DEFINES += QT_DEPRECATED_WARNINGS -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 - -macx { - # osx (Tested on macOS Mojave and High Sierra) - CONFIG += c++17 -} else { - CONFIG += c++17 - win32-msvc* { - # win32 msvc - QMAKE_CXXFLAGS += /std:c++17 - } else { - # clang/gcc on linux or win32 - QMAKE_CXXFLAGS += -std=c++17 - } -} - -debug { - DEFINES += QT_DEBUG -} - -linux { - LIBS += -lrt - QMAKE_LFLAGS += -lrt -} - -macx { - INCLUDEPATH += /usr/local/include - INCLUDEPATH += /usr/local/opt/openssl/include - LIBS += -L/usr/local/opt/openssl/lib -} - -SOURCES += \ - $$PWD/BaseSettings.cpp \ - $$PWD/BaseTheme.cpp \ - $$PWD/common/ChatterinoSetting.cpp \ - $$PWD/debug/Benchmark.cpp \ - $$PWD/singletons/Fonts.cpp \ - $$PWD/util/FunctionEventFilter.cpp \ - $$PWD/util/FuzzyConvert.cpp \ - $$PWD/util/Helpers.cpp \ - $$PWD/util/WindowsHelper.cpp \ - $$PWD/widgets/BaseWidget.cpp \ - $$PWD/widgets/BaseWindow.cpp \ - $$PWD/widgets/Label.cpp \ - $$PWD/widgets/TooltipWidget.cpp \ - $$PWD/widgets/helper/Button.cpp \ - $$PWD/widgets/helper/EffectLabel.cpp \ - $$PWD/widgets/helper/SignalLabel.cpp \ - $$PWD/widgets/helper/TitlebarButton.cpp \ - -HEADERS += \ - $$PWD/BaseSettings.hpp \ - $$PWD/BaseTheme.hpp \ - $$PWD/common/ChatterinoSetting.hpp \ - $$PWD/common/FlagsEnum.hpp \ - $$PWD/common/Outcome.hpp \ - $$PWD/common/Singleton.hpp \ - $$PWD/debug/AssertInGuiThread.hpp \ - $$PWD/debug/Benchmark.hpp \ - $$PWD/debug/Log.hpp \ - $$PWD/singletons/Fonts.hpp \ - $$PWD/util/Clamp.hpp \ - $$PWD/util/CombinePath.hpp \ - $$PWD/util/DistanceBetweenPoints.hpp \ - $$PWD/util/FunctionEventFilter.hpp \ - $$PWD/util/FuzzyConvert.hpp \ - $$PWD/util/Helpers.hpp \ - $$PWD/util/LayoutHelper.hpp \ - $$PWD/util/PostToThread.hpp \ - $$PWD/util/RapidJsonSerializeQString.hpp \ - $$PWD/util/Shortcut.hpp \ - $$PWD/util/WindowsHelper.hpp \ - $$PWD/widgets/BaseWidget.hpp \ - $$PWD/widgets/BaseWindow.hpp \ - $$PWD/widgets/Label.hpp \ - $$PWD/widgets/TooltipWidget.hpp \ - $$PWD/widgets/helper/Button.hpp \ - $$PWD/widgets/helper/EffectLabel.hpp \ - $$PWD/widgets/helper/SignalLabel.hpp \ - $$PWD/widgets/helper/TitlebarButton.hpp \ +#------------------------------------------------- +# +# Project created by QtCreator 2018-11-19T19:03:22 +# +#------------------------------------------------- + +!AB_NOT_STANDALONE { + message(appbase standalone) + QT += core gui widgets + TARGET = main + TEMPLATE = app + SOURCES += main.cpp + + # https://bugreports.qt.io/browse/QTBUG-27018 + equals(QMAKE_CXX, "clang++")|equals(QMAKE_CXX, "g++") { + TARGET = bin/appbase + } +} + +#DEFINES += QT_DEPRECATED_WARNINGS +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 + +macx { + # osx (Tested on macOS Mojave and High Sierra) + CONFIG += c++17 +} else { + CONFIG += c++17 + win32-msvc* { + # win32 msvc + QMAKE_CXXFLAGS += /std:c++17 + } else { + # clang/gcc on linux or win32 + QMAKE_CXXFLAGS += -std=c++17 + } +} + +debug { + DEFINES += QT_DEBUG +} + +linux { + LIBS += -lrt + QMAKE_LFLAGS += -lrt +} + +macx { + INCLUDEPATH += /usr/local/include + INCLUDEPATH += /usr/local/opt/openssl/include + LIBS += -L/usr/local/opt/openssl/lib +} + +SOURCES += \ + $$PWD/BaseSettings.cpp \ + $$PWD/BaseTheme.cpp \ + $$PWD/common/ChatterinoSetting.cpp \ + $$PWD/debug/Benchmark.cpp \ + $$PWD/singletons/Fonts.cpp \ + $$PWD/util/FunctionEventFilter.cpp \ + $$PWD/util/FuzzyConvert.cpp \ + $$PWD/util/Helpers.cpp \ + $$PWD/util/WindowsHelper.cpp \ + $$PWD/widgets/BaseWidget.cpp \ + $$PWD/widgets/BaseWindow.cpp \ + $$PWD/widgets/Label.cpp \ + $$PWD/widgets/TooltipWidget.cpp \ + $$PWD/widgets/helper/Button.cpp \ + $$PWD/widgets/helper/EffectLabel.cpp \ + $$PWD/widgets/helper/SignalLabel.cpp \ + $$PWD/widgets/helper/TitlebarButton.cpp \ + +HEADERS += \ + $$PWD/BaseSettings.hpp \ + $$PWD/BaseTheme.hpp \ + $$PWD/common/ChatterinoSetting.hpp \ + $$PWD/common/FlagsEnum.hpp \ + $$PWD/common/Outcome.hpp \ + $$PWD/common/Singleton.hpp \ + $$PWD/debug/AssertInGuiThread.hpp \ + $$PWD/debug/Benchmark.hpp \ + $$PWD/debug/Log.hpp \ + $$PWD/singletons/Fonts.hpp \ + $$PWD/util/Clamp.hpp \ + $$PWD/util/CombinePath.hpp \ + $$PWD/util/DistanceBetweenPoints.hpp \ + $$PWD/util/FunctionEventFilter.hpp \ + $$PWD/util/FuzzyConvert.hpp \ + $$PWD/util/Helpers.hpp \ + $$PWD/util/LayoutHelper.hpp \ + $$PWD/util/PostToThread.hpp \ + $$PWD/util/RapidJsonSerializeQString.hpp \ + $$PWD/util/Shortcut.hpp \ + $$PWD/util/WindowsHelper.hpp \ + $$PWD/widgets/BaseWidget.hpp \ + $$PWD/widgets/BaseWindow.hpp \ + $$PWD/widgets/Label.hpp \ + $$PWD/widgets/TooltipWidget.hpp \ + $$PWD/widgets/helper/Button.hpp \ + $$PWD/widgets/helper/EffectLabel.hpp \ + $$PWD/widgets/helper/SignalLabel.hpp \ + $$PWD/widgets/helper/TitlebarButton.hpp \ diff --git a/lib/appbase/util/Clamp.hpp b/lib/appbase/util/Clamp.hpp index 867e891ad..0bb8febd7 100644 --- a/lib/appbase/util/Clamp.hpp +++ b/lib/appbase/util/Clamp.hpp @@ -1,13 +1,13 @@ -#pragma once - -namespace AB_NAMESPACE { - -// http://en.cppreference.com/w/cpp/algorithm/clamp - -template -constexpr const T &clamp(const T &v, const T &lo, const T &hi) -{ - return assert(!(hi < lo)), (v < lo) ? lo : (hi < v) ? hi : v; -} - -} // namespace AB_NAMESPACE +#pragma once + +namespace AB_NAMESPACE { + +// http://en.cppreference.com/w/cpp/algorithm/clamp + +template +constexpr const T &clamp(const T &v, const T &lo, const T &hi) +{ + return assert(!(hi < lo)), (v < lo) ? lo : (hi < v) ? hi : v; +} + +} // namespace AB_NAMESPACE diff --git a/lib/appbase/util/CombinePath.hpp b/lib/appbase/util/CombinePath.hpp index d0ed8e7ad..9a1acf800 100644 --- a/lib/appbase/util/CombinePath.hpp +++ b/lib/appbase/util/CombinePath.hpp @@ -1,14 +1,14 @@ -#pragma once - -#include -#include - -namespace chatterino { - -// https://stackoverflow.com/a/13014491 -inline QString combinePath(const QString &a, const QString &b) -{ - return QDir::cleanPath(a + QDir::separator() + b); -} - -} // namespace chatterino +#pragma once + +#include +#include + +namespace chatterino { + +// https://stackoverflow.com/a/13014491 +inline QString combinePath(const QString &a, const QString &b) +{ + return QDir::cleanPath(a + QDir::separator() + b); +} + +} // namespace chatterino diff --git a/lib/appbase/util/FunctionEventFilter.cpp b/lib/appbase/util/FunctionEventFilter.cpp index 923c2e7fd..9b0ab94f0 100644 --- a/lib/appbase/util/FunctionEventFilter.cpp +++ b/lib/appbase/util/FunctionEventFilter.cpp @@ -1,17 +1,17 @@ -#include "FunctionEventFilter.hpp" - -namespace AB_NAMESPACE { - -FunctionEventFilter::FunctionEventFilter( - QObject *parent, std::function function) - : QObject(parent) - , function_(std::move(function)) -{ -} - -bool FunctionEventFilter::eventFilter(QObject *watched, QEvent *event) -{ - return this->function_(watched, event); -} - -} // namespace AB_NAMESPACE +#include "FunctionEventFilter.hpp" + +namespace AB_NAMESPACE { + +FunctionEventFilter::FunctionEventFilter( + QObject *parent, std::function function) + : QObject(parent) + , function_(std::move(function)) +{ +} + +bool FunctionEventFilter::eventFilter(QObject *watched, QEvent *event) +{ + return this->function_(watched, event); +} + +} // namespace AB_NAMESPACE diff --git a/lib/appbase/util/FunctionEventFilter.hpp b/lib/appbase/util/FunctionEventFilter.hpp index cecac1bf1..99cb7d9cc 100644 --- a/lib/appbase/util/FunctionEventFilter.hpp +++ b/lib/appbase/util/FunctionEventFilter.hpp @@ -1,24 +1,24 @@ -#pragma once - -#include -#include -#include - -namespace AB_NAMESPACE { - -class FunctionEventFilter : public QObject -{ - Q_OBJECT - -public: - FunctionEventFilter(QObject *parent, - std::function function); - -protected: - bool eventFilter(QObject *watched, QEvent *event) override; - -private: - std::function function_; -}; - -} // namespace AB_NAMESPACE +#pragma once + +#include +#include +#include + +namespace AB_NAMESPACE { + +class FunctionEventFilter : public QObject +{ + Q_OBJECT + +public: + FunctionEventFilter(QObject *parent, + std::function function); + +protected: + bool eventFilter(QObject *watched, QEvent *event) override; + +private: + std::function function_; +}; + +} // namespace AB_NAMESPACE diff --git a/lib/appbase/util/FuzzyConvert.cpp b/lib/appbase/util/FuzzyConvert.cpp index cfdc3f01c..2d62e5648 100644 --- a/lib/appbase/util/FuzzyConvert.cpp +++ b/lib/appbase/util/FuzzyConvert.cpp @@ -1,33 +1,33 @@ -#include "FuzzyConvert.hpp" - -#include - -namespace chatterino { - -int fuzzyToInt(const QString &str, int default_) -{ - static auto intFinder = QRegularExpression("[0-9]+"); - - auto match = intFinder.match(str); - if (match.hasMatch()) - { - return match.captured().toInt(); - } - - return default_; -} - -float fuzzyToFloat(const QString &str, float default_) -{ - static auto floatFinder = QRegularExpression("[0-9]+(\\.[0-9]+)?"); - - auto match = floatFinder.match(str); - if (match.hasMatch()) - { - return match.captured().toFloat(); - } - - return default_; -} - -} // namespace chatterino +#include "FuzzyConvert.hpp" + +#include + +namespace chatterino { + +int fuzzyToInt(const QString &str, int default_) +{ + static auto intFinder = QRegularExpression("[0-9]+"); + + auto match = intFinder.match(str); + if (match.hasMatch()) + { + return match.captured().toInt(); + } + + return default_; +} + +float fuzzyToFloat(const QString &str, float default_) +{ + static auto floatFinder = QRegularExpression("[0-9]+(\\.[0-9]+)?"); + + auto match = floatFinder.match(str); + if (match.hasMatch()) + { + return match.captured().toFloat(); + } + + return default_; +} + +} // namespace chatterino diff --git a/lib/appbase/util/FuzzyConvert.hpp b/lib/appbase/util/FuzzyConvert.hpp index a3875da97..25fbf33c6 100644 --- a/lib/appbase/util/FuzzyConvert.hpp +++ b/lib/appbase/util/FuzzyConvert.hpp @@ -1,10 +1,10 @@ -#pragma once - -#include - -namespace chatterino { - -int fuzzyToInt(const QString &str, int default_); -float fuzzyToFloat(const QString &str, float default_); - -} // namespace chatterino +#pragma once + +#include + +namespace chatterino { + +int fuzzyToInt(const QString &str, int default_); +float fuzzyToFloat(const QString &str, float default_); + +} // namespace chatterino diff --git a/lib/appbase/util/LayoutHelper.hpp b/lib/appbase/util/LayoutHelper.hpp index f66fbef87..11157f0fa 100644 --- a/lib/appbase/util/LayoutHelper.hpp +++ b/lib/appbase/util/LayoutHelper.hpp @@ -1,42 +1,42 @@ -#pragma once - -#include -#include -#include - -namespace chatterino { - -using LayoutItem = boost::variant; - -template -T *makeLayout(std::initializer_list items) -{ - auto t = new T; - - for (auto &item : items) - { - switch (item.which()) - { - case 0: - t->addItem(new QWidgetItem(boost::get(item))); - break; - case 1: - t->addItem(boost::get(item)); - break; - } - } - - return t; -} - -template -T *makeWidget(With with) -{ - auto t = new T; - - with(t); - - return t; -} - -} // namespace chatterino +#pragma once + +#include +#include +#include + +namespace chatterino { + +using LayoutItem = boost::variant; + +template +T *makeLayout(std::initializer_list items) +{ + auto t = new T; + + for (auto &item : items) + { + switch (item.which()) + { + case 0: + t->addItem(new QWidgetItem(boost::get(item))); + break; + case 1: + t->addItem(boost::get(item)); + break; + } + } + + return t; +} + +template +T *makeWidget(With with) +{ + auto t = new T; + + with(t); + + return t; +} + +} // namespace chatterino diff --git a/lib/appbase/util/WindowsHelper.cpp b/lib/appbase/util/WindowsHelper.cpp index 9d6ba612a..b2649b855 100644 --- a/lib/appbase/util/WindowsHelper.cpp +++ b/lib/appbase/util/WindowsHelper.cpp @@ -1,86 +1,86 @@ -#include "WindowsHelper.hpp" - -#include - -#ifdef USEWINSDK - -namespace AB_NAMESPACE { - -typedef enum MONITOR_DPI_TYPE { - MDT_EFFECTIVE_DPI = 0, - MDT_ANGULAR_DPI = 1, - MDT_RAW_DPI = 2, - MDT_DEFAULT = MDT_EFFECTIVE_DPI -} MONITOR_DPI_TYPE; - -typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *, - UINT *); - -boost::optional getWindowDpi(HWND hwnd) -{ - static HINSTANCE shcore = LoadLibrary(L"Shcore.dll"); - if (shcore != nullptr) - { - if (auto getDpiForMonitor = - GetDpiForMonitor_(GetProcAddress(shcore, "GetDpiForMonitor"))) - { - HMONITOR monitor = - MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - - UINT xScale, yScale; - - getDpiForMonitor(monitor, MDT_DEFAULT, &xScale, &yScale); - - return xScale; - } - } - - return boost::none; -} - -typedef HRESULT(CALLBACK *OleFlushClipboard_)(); - -void flushClipboard() -{ - static HINSTANCE ole32 = LoadLibrary(L"Ole32.dll"); - if (ole32 != nullptr) - { - if (auto oleFlushClipboard = - OleFlushClipboard_(GetProcAddress(ole32, "OleFlushClipboard"))) - { - oleFlushClipboard(); - } - } -} - -constexpr const char *runKey = - "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"; - -bool isRegisteredForStartup() -{ - QSettings settings(runKey, QSettings::NativeFormat); - - return !settings.value("Chatterino").toString().isEmpty(); -} - -void setRegisteredForStartup(bool isRegistered) -{ - QSettings settings(runKey, QSettings::NativeFormat); - - if (isRegistered) - { - auto exePath = QFileInfo(QCoreApplication::applicationFilePath()) - .absoluteFilePath() - .replace('/', '\\'); - - settings.setValue("Chatterino", "\"" + exePath + "\" --autorun"); - } - else - { - settings.remove("Chatterino"); - } -} - -} // namespace AB_NAMESPACE - -#endif +#include "WindowsHelper.hpp" + +#include + +#ifdef USEWINSDK + +namespace AB_NAMESPACE { + +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; + +typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *, + UINT *); + +boost::optional getWindowDpi(HWND hwnd) +{ + static HINSTANCE shcore = LoadLibrary(L"Shcore.dll"); + if (shcore != nullptr) + { + if (auto getDpiForMonitor = + GetDpiForMonitor_(GetProcAddress(shcore, "GetDpiForMonitor"))) + { + HMONITOR monitor = + MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + + UINT xScale, yScale; + + getDpiForMonitor(monitor, MDT_DEFAULT, &xScale, &yScale); + + return xScale; + } + } + + return boost::none; +} + +typedef HRESULT(CALLBACK *OleFlushClipboard_)(); + +void flushClipboard() +{ + static HINSTANCE ole32 = LoadLibrary(L"Ole32.dll"); + if (ole32 != nullptr) + { + if (auto oleFlushClipboard = + OleFlushClipboard_(GetProcAddress(ole32, "OleFlushClipboard"))) + { + oleFlushClipboard(); + } + } +} + +constexpr const char *runKey = + "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"; + +bool isRegisteredForStartup() +{ + QSettings settings(runKey, QSettings::NativeFormat); + + return !settings.value("Chatterino").toString().isEmpty(); +} + +void setRegisteredForStartup(bool isRegistered) +{ + QSettings settings(runKey, QSettings::NativeFormat); + + if (isRegistered) + { + auto exePath = QFileInfo(QCoreApplication::applicationFilePath()) + .absoluteFilePath() + .replace('/', '\\'); + + settings.setValue("Chatterino", "\"" + exePath + "\" --autorun"); + } + else + { + settings.remove("Chatterino"); + } +} + +} // namespace AB_NAMESPACE + +#endif diff --git a/lib/appbase/util/WindowsHelper.hpp b/lib/appbase/util/WindowsHelper.hpp index 2665ad0ed..e32f4b266 100644 --- a/lib/appbase/util/WindowsHelper.hpp +++ b/lib/appbase/util/WindowsHelper.hpp @@ -1,18 +1,18 @@ -#pragma once - -#ifdef USEWINSDK - -# include -# include - -namespace AB_NAMESPACE { - -boost::optional getWindowDpi(HWND hwnd); -void flushClipboard(); - -bool isRegisteredForStartup(); -void setRegisteredForStartup(bool isRegistered); - -} // namespace AB_NAMESPACE - -#endif +#pragma once + +#ifdef USEWINSDK + +# include +# include + +namespace AB_NAMESPACE { + +boost::optional getWindowDpi(HWND hwnd); +void flushClipboard(); + +bool isRegisteredForStartup(); +void setRegisteredForStartup(bool isRegistered); + +} // namespace AB_NAMESPACE + +#endif diff --git a/lib/appbase/widgets/BaseWindow.cpp b/lib/appbase/widgets/BaseWindow.cpp index dc9f9cafb..ef0455d80 100644 --- a/lib/appbase/widgets/BaseWindow.cpp +++ b/lib/appbase/widgets/BaseWindow.cpp @@ -1,1001 +1,1001 @@ -#include "BaseWindow.hpp" - -#include "BaseSettings.hpp" -#include "BaseTheme.hpp" -#include "boost/algorithm/algorithm.hpp" -#include "debug/Log.hpp" -#include "util/PostToThread.hpp" -#include "util/Shortcut.hpp" -#include "util/WindowsHelper.hpp" -#include "widgets/Label.hpp" -#include "widgets/TooltipWidget.hpp" -#include "widgets/helper/EffectLabel.hpp" - -#include -#include -#include -#include -#include -#include - -#ifdef CHATTERINO -# include "Application.hpp" -# include "singletons/WindowManager.hpp" -#endif - -#ifdef USEWINSDK -# include -# include -# include -# include -# include -# include - -//#include -# pragma comment(lib, "Dwmapi.lib") - -# include -# include - -# define WM_DPICHANGED 0x02E0 -#endif - -#include "widgets/helper/TitlebarButton.hpp" - -namespace AB_NAMESPACE { - -BaseWindow::BaseWindow(QWidget *parent, Flags _flags) - : BaseWidget(parent, - Qt::Window | ((_flags & TopMost) ? Qt::WindowStaysOnTopHint - : Qt::WindowFlags())) - , enableCustomFrame_(_flags & EnableCustomFrame) - , frameless_(_flags & Frameless) - , flags_(_flags) -{ - if (this->frameless_) - { - this->enableCustomFrame_ = false; - this->setWindowFlag(Qt::FramelessWindowHint); - } - - this->init(); - - getSettings()->uiScale.connect( - [this]() { - postToThread([this] { - this->updateScale(); - this->updateScale(); - }); - }, - this->connections_); - - this->updateScale(); - - createWindowShortcut(this, "CTRL+0", - [] { getSettings()->uiScale.setValue(1); }); - - // QTimer::this->scaleChangedEvent(this->getScale()); - - this->resize(300, 150); - -#ifdef USEWINSDK - this->useNextBounds_.setSingleShot(true); - QObject::connect(&this->useNextBounds_, &QTimer::timeout, this, - [this]() { this->currentBounds_ = this->nextBounds_; }); -#endif -} - -void BaseWindow::setInitialBounds(const QRect &bounds) -{ -#ifdef USEWINSDK - this->initalBounds_ = bounds; -#else - this->setGeometry(bounds); -#endif -} - -QRect BaseWindow::getBounds() -{ -#ifdef USEWINSDK - return this->currentBounds_; -#else - return this->geometry(); -#endif -} - -float BaseWindow::scale() const -{ - return this->overrideScale().value_or(this->scale_); -} - -float BaseWindow::qtFontScale() const -{ - return this->scale() / this->nativeScale_; -} - -BaseWindow::Flags BaseWindow::getFlags() -{ - return this->flags_; -} - -void BaseWindow::init() -{ - this->setWindowIcon(QIcon(":/images/icon.png")); - -#ifdef USEWINSDK - if (this->hasCustomWindowFrame()) - { - // CUSTOM WINDOW FRAME - QVBoxLayout *layout = new QVBoxLayout(); - this->ui_.windowLayout = layout; - layout->setContentsMargins(1, 1, 1, 1); - layout->setSpacing(0); - this->setLayout(layout); - { - if (!this->frameless_) - { - QHBoxLayout *buttonLayout = this->ui_.titlebarBox = - new QHBoxLayout(); - buttonLayout->setMargin(0); - layout->addLayout(buttonLayout); - - // title - Label *title = new Label; - QObject::connect( - this, &QWidget::windowTitleChanged, - [title](const QString &text) { title->setText(text); }); - - QSizePolicy policy(QSizePolicy::Ignored, - QSizePolicy::Preferred); - policy.setHorizontalStretch(1); - title->setSizePolicy(policy); - buttonLayout->addWidget(title); - this->ui_.titleLabel = title; - - // buttons - TitleBarButton *_minButton = new TitleBarButton; - _minButton->setButtonStyle(TitleBarButtonStyle::Minimize); - TitleBarButton *_maxButton = new TitleBarButton; - _maxButton->setButtonStyle(TitleBarButtonStyle::Maximize); - TitleBarButton *_exitButton = new TitleBarButton; - _exitButton->setButtonStyle(TitleBarButtonStyle::Close); - - QObject::connect(_minButton, &TitleBarButton::leftClicked, this, - [this] { - this->setWindowState(Qt::WindowMinimized | - this->windowState()); - }); - QObject::connect(_maxButton, &TitleBarButton::leftClicked, this, - [this, _maxButton] { - this->setWindowState( - _maxButton->getButtonStyle() != - TitleBarButtonStyle::Maximize - ? Qt::WindowActive - : Qt::WindowMaximized); - }); - QObject::connect(_exitButton, &TitleBarButton::leftClicked, - 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); - this->ui_.layoutBase->setContentsMargins(1, 0, 1, 1); - layout->addWidget(this->ui_.layoutBase); - } - -// DPI -// auto dpi = getWindowDpi(this->winId()); - -// if (dpi) { -// this->scale = dpi.value() / 96.f; -// } -#endif - -#ifdef USEWINSDK - // fourtf: don't ask me why we need to delay this - if (!(this->flags_ & Flags::TopMost)) - { - QTimer::singleShot(1, this, [this] { - getSettings()->windowTopMost.connect( - [this](bool topMost, auto) { - ::SetWindowPos(HWND(this->winId()), - topMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, - 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - }, - this->managedConnections_); - }); - } -#else -// if (getSettings()->windowTopMost.getValue()) { -// this->setWindowFlag(Qt::WindowStaysOnTopHint); -// } -#endif -} - -void BaseWindow::setStayInScreenRect(bool value) -{ - this->stayInScreenRect_ = value; - - this->moveIntoDesktopRect(this); -} - -bool BaseWindow::getStayInScreenRect() const -{ - return this->stayInScreenRect_; -} - -void BaseWindow::setActionOnFocusLoss(ActionOnFocusLoss value) -{ - this->actionOnFocusLoss_ = value; -} - -BaseWindow::ActionOnFocusLoss BaseWindow::getActionOnFocusLoss() const -{ - return this->actionOnFocusLoss_; -} - -QWidget *BaseWindow::getLayoutContainer() -{ - if (this->hasCustomWindowFrame()) - { - return this->ui_.layoutBase; - } - else - { - return this; - } -} - -bool BaseWindow::hasCustomWindowFrame() -{ - return BaseWindow::supportsCustomWindowFrame() && this->enableCustomFrame_; -} - -bool BaseWindow::supportsCustomWindowFrame() -{ -#ifdef USEWINSDK - static bool isWin8 = IsWindows8OrGreater(); - - return isWin8; -#else - return false; -#endif -} - -void BaseWindow::themeChangedEvent() -{ - if (this->hasCustomWindowFrame()) - { - QPalette palette; - palette.setColor(QPalette::Window, QColor(80, 80, 80, 255)); - palette.setColor(QPalette::WindowText, this->theme->window.text); - this->setPalette(palette); - - if (this->ui_.titleLabel) - { - QPalette palette_title; - palette_title.setColor( - QPalette::WindowText, - this->theme->isLightTheme() ? "#333" : "#ccc"); - this->ui_.titleLabel->setPalette(palette_title); - } - - for (Button *button : this->ui_.buttons) - { - button->setMouseEffectColor(this->theme->window.text); - } - } - else - { - QPalette palette; - palette.setColor(QPalette::Background, this->theme->window.background); - palette.setColor(QPalette::Foreground, this->theme->window.text); - this->setPalette(palette); - } -} - -bool BaseWindow::event(QEvent *event) -{ - if (event->type() == - QEvent::WindowDeactivate /*|| event->type() == QEvent::FocusOut*/) - { - this->onFocusLost(); - } - - return QWidget::event(event); -} - -void BaseWindow::wheelEvent(QWheelEvent *event) -{ - if (event->orientation() != Qt::Vertical) - { - return; - } - - if (event->modifiers() & Qt::ControlModifier) - { - if (event->delta() > 0) - { - getSettings()->setClampedUiScale( - getSettings()->getClampedUiScale() + 0.1); - } - else - { - getSettings()->setClampedUiScale( - getSettings()->getClampedUiScale() - 0.1); - } - } -} - -void BaseWindow::onFocusLost() -{ - switch (this->getActionOnFocusLoss()) - { - case Delete: - { - this->deleteLater(); - } - break; - - case Close: - { - this->close(); - } - break; - - case Hide: - { - this->hide(); - } - break; - - default:; - } -} - -void BaseWindow::mousePressEvent(QMouseEvent *event) -{ -#ifndef Q_OS_WIN - if (this->flags_ & FramelessDraggable) - { - this->movingRelativePos = event->localPos(); - if (auto widget = - this->childAt(event->localPos().x(), event->localPos().y())) - { - std::function recursiveCheckMouseTracking; - recursiveCheckMouseTracking = [&](QWidget *widget) { - if (widget == nullptr) - { - return false; - } - - if (widget->hasMouseTracking()) - { - return true; - } - - return recursiveCheckMouseTracking(widget->parentWidget()); - }; - - if (!recursiveCheckMouseTracking(widget)) - { - log("Start moving"); - this->moving = true; - } - } - } -#endif - - BaseWidget::mousePressEvent(event); -} - -void BaseWindow::mouseReleaseEvent(QMouseEvent *event) -{ -#ifndef Q_OS_WIN - if (this->flags_ & FramelessDraggable) - { - if (this->moving) - { - log("Stop moving"); - this->moving = false; - } - } -#endif - - BaseWidget::mouseReleaseEvent(event); -} - -void BaseWindow::mouseMoveEvent(QMouseEvent *event) -{ -#ifndef Q_OS_WIN - if (this->flags_ & FramelessDraggable) - { - if (this->moving) - { - const auto &newPos = event->screenPos() - this->movingRelativePos; - this->move(newPos.x(), newPos.y()); - } - } -#endif - - BaseWidget::mouseMoveEvent(event); -} - -TitleBarButton *BaseWindow::addTitleBarButton(const TitleBarButtonStyle &style, - std::function onClicked) -{ - TitleBarButton *button = new TitleBarButton; - button->setScaleIndependantSize(30, 30); - - this->ui_.buttons.push_back(button); - this->ui_.titlebarBox->insertWidget(1, button); - button->setButtonStyle(style); - - QObject::connect(button, &TitleBarButton::leftClicked, this, - [onClicked] { onClicked(); }); - - return button; -} - -EffectLabel *BaseWindow::addTitleBarLabel(std::function onClicked) -{ - EffectLabel *button = new EffectLabel; - button->setScaleIndependantHeight(30); - - this->ui_.buttons.push_back(button); - this->ui_.titlebarBox->insertWidget(1, button); - - QObject::connect(button, &EffectLabel::leftClicked, this, - [onClicked] { onClicked(); }); - - return button; -} - -void BaseWindow::changeEvent(QEvent *) -{ - if (this->isVisible()) - { - TooltipWidget::getInstance()->hide(); - } - -#ifdef USEWINSDK - if (this->ui_.maxButton) - { - this->ui_.maxButton->setButtonStyle( - this->windowState() & Qt::WindowMaximized - ? TitleBarButtonStyle::Unmaximize - : TitleBarButtonStyle::Maximize); - } - - if (this->isVisible() && this->hasCustomWindowFrame()) - { - auto palette = this->palette(); - palette.setColor(QPalette::Window, - GetForegroundWindow() == HWND(this->winId()) - ? QColor(90, 90, 90) - : QColor(50, 50, 50)); - this->setPalette(palette); - } -#endif - -#ifndef Q_OS_WIN - this->update(); -#endif -} - -void BaseWindow::leaveEvent(QEvent *) -{ - TooltipWidget::getInstance()->hide(); -} - -void BaseWindow::moveTo(QWidget *parent, QPoint point, bool offset) -{ - if (offset) - { - point.rx() += 16; - point.ry() += 16; - } - - this->move(point); - this->moveIntoDesktopRect(parent); -} - -void BaseWindow::resizeEvent(QResizeEvent *) -{ - // Queue up save because: Window resized -#ifdef CHATTERINO - getApp()->windows->queueSave(); -#endif - - //this->moveIntoDesktopRect(this); - - this->calcButtonsSizes(); -} - -void BaseWindow::moveEvent(QMoveEvent *event) -{ - // Queue up save because: Window position changed -#ifdef CHATTERINO - getApp()->windows->queueSave(); -#endif - - BaseWidget::moveEvent(event); -} - -void BaseWindow::closeEvent(QCloseEvent *) -{ - this->closing.invoke(); -} - -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->availableGeometry(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); -} - -bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, - long *result) -{ -#ifdef USEWINSDK -# if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1)) - MSG *msg = *reinterpret_cast(message); -# else - MSG *msg = reinterpret_cast(message); -# endif - - bool returnValue = false; - - switch (msg->message) - { - case WM_DPICHANGED: - returnValue = this->handleDPICHANGED(msg); - break; - - case WM_SHOWWINDOW: - returnValue = this->handleSHOWWINDOW(msg); - break; - - case WM_NCCALCSIZE: - returnValue = this->handleNCCALCSIZE(msg, result); - break; - - case WM_SIZE: - returnValue = this->handleSIZE(msg); - break; - - case WM_MOVE: - returnValue = this->handleMOVE(msg); - *result = 0; - break; - - case WM_NCHITTEST: - returnValue = this->handleNCHITTEST(msg, result); - break; - - default: - return QWidget::nativeEvent(eventType, message, result); - } - - QWidget::nativeEvent(eventType, message, result); - - return returnValue; -#else - return QWidget::nativeEvent(eventType, message, result); -#endif -} - -void BaseWindow::scaleChangedEvent(float scale) -{ -#ifdef USEWINSDK - this->calcButtonsSizes(); -#endif - - this->setFont(getFonts()->getFont(FontStyle::UiTabs, this->qtFontScale())); -} - -void BaseWindow::paintEvent(QPaintEvent *) -{ - QPainter painter(this); - - if (this->frameless_) - { - painter.setPen(QColor("#999")); - painter.drawRect(0, 0, this->width() - 1, this->height() - 1); - } - - this->drawCustomWindowFrame(painter); -} - -void BaseWindow::updateScale() -{ - auto scale = - this->nativeScale_ * (this->flags_ & DisableCustomScaling - ? 1 - : getABSettings()->getClampedUiScale()); - - this->setScale(scale); - - for (auto child : this->findChildren()) - { - child->setScale(scale); - } -} - -void BaseWindow::calcButtonsSizes() -{ - if (!this->shown_) - { - return; - } - - if ((this->width() / this->scale()) < 300) - { - 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 - { - 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); - } -} - -void BaseWindow::drawCustomWindowFrame(QPainter &painter) -{ -#ifdef USEWINSDK - if (this->hasCustomWindowFrame()) - { - QColor bg = this->overrideBackgroundColor_.value_or( - this->theme->window.background); - painter.fillRect(QRect(1, 2, this->width() - 2, this->height() - 3), - bg); - } -#endif -} - -bool BaseWindow::handleDPICHANGED(MSG *msg) -{ -#ifdef USEWINSDK - int dpi = HIWORD(msg->wParam); - - float _scale = dpi / 96.f; - - static bool firstResize = true; - - if (!firstResize) - { - auto *prcNewWindow = reinterpret_cast(msg->lParam); - SetWindowPos(msg->hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, - prcNewWindow->right - prcNewWindow->left, - prcNewWindow->bottom - prcNewWindow->top, - SWP_NOZORDER | SWP_NOACTIVATE); - } - firstResize = false; - - this->nativeScale_ = _scale; - this->updateScale(); - - return true; -#else - return false; -#endif -} - -bool BaseWindow::handleSHOWWINDOW(MSG *msg) -{ -#ifdef USEWINSDK - if (auto dpi = getWindowDpi(msg->hwnd)) - { - this->nativeScale_ = dpi.get() / 96.f; - this->updateScale(); - } - - if (!this->shown_ && this->isVisible()) - { - if (this->hasCustomWindowFrame()) - { - this->shown_ = true; - - const MARGINS shadow = {8, 8, 8, 8}; - DwmExtendFrameIntoClientArea(HWND(this->winId()), &shadow); - } - if (!this->initalBounds_.isNull()) - { - ::SetWindowPos(msg->hwnd, nullptr, this->initalBounds_.x(), - this->initalBounds_.y(), this->initalBounds_.width(), - this->initalBounds_.height(), - SWP_NOZORDER | SWP_NOACTIVATE); - this->currentBounds_ = this->initalBounds_; - } - } - - this->calcButtonsSizes(); - - return true; -#else - return false; -#endif -} - -bool BaseWindow::handleNCCALCSIZE(MSG *msg, long *result) -{ -#ifdef USEWINSDK - if (this->hasCustomWindowFrame()) - { - // int cx = GetSystemMetrics(SM_CXSIZEFRAME); - // int cy = GetSystemMetrics(SM_CYSIZEFRAME); - - if (msg->wParam == TRUE) - { - NCCALCSIZE_PARAMS *ncp = - (reinterpret_cast(msg->lParam)); - ncp->lppos->flags |= SWP_NOREDRAW; - RECT *clientRect = &ncp->rgrc[0]; - - clientRect->top -= 1; - } - - *result = 0; - return true; - } - return false; -#else - return false; -#endif -} - -bool BaseWindow::handleSIZE(MSG *msg) -{ -#ifdef USEWINSDK - if (this->ui_.windowLayout) - { - if (this->frameless_) - { - // - } - else if (this->hasCustomWindowFrame()) - { - if (msg->wParam == SIZE_MAXIMIZED) - { - auto offset = int( - getWindowDpi(HWND(this->winId())).value_or(96) * 8 / 96); - - this->ui_.windowLayout->setContentsMargins(offset, offset, - offset, offset); - } - else - { - this->ui_.windowLayout->setContentsMargins(0, 1, 0, 0); - } - - this->isNotMinimizedOrMaximized_ = msg->wParam == SIZE_RESTORED; - - if (this->isNotMinimizedOrMaximized_) - { - RECT rect; - ::GetWindowRect(msg->hwnd, &rect); - this->currentBounds_ = - QRect(QPoint(rect.left, rect.top), - QPoint(rect.right - 1, rect.bottom - 1)); - } - this->useNextBounds_.stop(); - } - } - return false; -#else - return false; -#endif -} - -bool BaseWindow::handleMOVE(MSG *msg) -{ -#ifdef USEWINSDK - if (this->isNotMinimizedOrMaximized_) - { - RECT rect; - ::GetWindowRect(msg->hwnd, &rect); - this->nextBounds_ = QRect(QPoint(rect.left, rect.top), - QPoint(rect.right - 1, rect.bottom - 1)); - - this->useNextBounds_.start(10); - } -#endif - return false; -} - -bool BaseWindow::handleNCHITTEST(MSG *msg, long *result) -{ -#ifdef USEWINSDK - 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); - - QPoint point(x - winrect.left, y - winrect.top); - - if (this->hasCustomWindowFrame()) - { - *result = 0; - - bool resizeWidth = minimumWidth() != maximumWidth(); - bool resizeHeight = minimumHeight() != maximumHeight(); - - if (resizeWidth) - { - // left border - if (x < winrect.left + border_width) - { - *result = HTLEFT; - } - // right border - if (x >= winrect.right - border_width) - { - *result = HTRIGHT; - } - } - if (resizeHeight) - { - // bottom border - if (y >= winrect.bottom - border_width) - { - *result = HTBOTTOM; - } - // top border - 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; - } - } - - if (*result == 0) - { - bool client = false; - - for (QWidget *widget : this->ui_.buttons) - { - if (widget->geometry().contains(point)) - { - client = true; - } - } - - if (this->ui_.layoutBase->geometry().contains(point)) - { - client = true; - } - - if (client) - { - *result = HTCLIENT; - } - else - { - *result = HTCAPTION; - } - } - - return true; - } - else if (this->flags_ & FramelessDraggable) - { - *result = 0; - bool client = false; - - if (auto widget = this->childAt(point)) - { - std::function recursiveCheckMouseTracking; - recursiveCheckMouseTracking = [&](QWidget *widget) { - if (widget == nullptr) - { - return false; - } - - if (widget->hasMouseTracking()) - { - return true; - } - - return recursiveCheckMouseTracking(widget->parentWidget()); - }; - - if (recursiveCheckMouseTracking(widget)) - { - client = true; - } - } - - if (client) - { - *result = HTCLIENT; - } - else - { - *result = HTCAPTION; - } - - return true; - } - return false; -#else - return false; -#endif -} - -} // namespace AB_NAMESPACE +#include "BaseWindow.hpp" + +#include "BaseSettings.hpp" +#include "BaseTheme.hpp" +#include "boost/algorithm/algorithm.hpp" +#include "debug/Log.hpp" +#include "util/PostToThread.hpp" +#include "util/Shortcut.hpp" +#include "util/WindowsHelper.hpp" +#include "widgets/Label.hpp" +#include "widgets/TooltipWidget.hpp" +#include "widgets/helper/EffectLabel.hpp" + +#include +#include +#include +#include +#include +#include + +#ifdef CHATTERINO +# include "Application.hpp" +# include "singletons/WindowManager.hpp" +#endif + +#ifdef USEWINSDK +# include +# include +# include +# include +# include +# include + +//#include +# pragma comment(lib, "Dwmapi.lib") + +# include +# include + +# define WM_DPICHANGED 0x02E0 +#endif + +#include "widgets/helper/TitlebarButton.hpp" + +namespace AB_NAMESPACE { + +BaseWindow::BaseWindow(QWidget *parent, Flags _flags) + : BaseWidget(parent, + Qt::Window | ((_flags & TopMost) ? Qt::WindowStaysOnTopHint + : Qt::WindowFlags())) + , enableCustomFrame_(_flags & EnableCustomFrame) + , frameless_(_flags & Frameless) + , flags_(_flags) +{ + if (this->frameless_) + { + this->enableCustomFrame_ = false; + this->setWindowFlag(Qt::FramelessWindowHint); + } + + this->init(); + + getSettings()->uiScale.connect( + [this]() { + postToThread([this] { + this->updateScale(); + this->updateScale(); + }); + }, + this->connections_); + + this->updateScale(); + + createWindowShortcut(this, "CTRL+0", + [] { getSettings()->uiScale.setValue(1); }); + + // QTimer::this->scaleChangedEvent(this->getScale()); + + this->resize(300, 150); + +#ifdef USEWINSDK + this->useNextBounds_.setSingleShot(true); + QObject::connect(&this->useNextBounds_, &QTimer::timeout, this, + [this]() { this->currentBounds_ = this->nextBounds_; }); +#endif +} + +void BaseWindow::setInitialBounds(const QRect &bounds) +{ +#ifdef USEWINSDK + this->initalBounds_ = bounds; +#else + this->setGeometry(bounds); +#endif +} + +QRect BaseWindow::getBounds() +{ +#ifdef USEWINSDK + return this->currentBounds_; +#else + return this->geometry(); +#endif +} + +float BaseWindow::scale() const +{ + return this->overrideScale().value_or(this->scale_); +} + +float BaseWindow::qtFontScale() const +{ + return this->scale() / this->nativeScale_; +} + +BaseWindow::Flags BaseWindow::getFlags() +{ + return this->flags_; +} + +void BaseWindow::init() +{ + this->setWindowIcon(QIcon(":/images/icon.png")); + +#ifdef USEWINSDK + if (this->hasCustomWindowFrame()) + { + // CUSTOM WINDOW FRAME + QVBoxLayout *layout = new QVBoxLayout(); + this->ui_.windowLayout = layout; + layout->setContentsMargins(1, 1, 1, 1); + layout->setSpacing(0); + this->setLayout(layout); + { + if (!this->frameless_) + { + QHBoxLayout *buttonLayout = this->ui_.titlebarBox = + new QHBoxLayout(); + buttonLayout->setMargin(0); + layout->addLayout(buttonLayout); + + // title + Label *title = new Label; + QObject::connect( + this, &QWidget::windowTitleChanged, + [title](const QString &text) { title->setText(text); }); + + QSizePolicy policy(QSizePolicy::Ignored, + QSizePolicy::Preferred); + policy.setHorizontalStretch(1); + title->setSizePolicy(policy); + buttonLayout->addWidget(title); + this->ui_.titleLabel = title; + + // buttons + TitleBarButton *_minButton = new TitleBarButton; + _minButton->setButtonStyle(TitleBarButtonStyle::Minimize); + TitleBarButton *_maxButton = new TitleBarButton; + _maxButton->setButtonStyle(TitleBarButtonStyle::Maximize); + TitleBarButton *_exitButton = new TitleBarButton; + _exitButton->setButtonStyle(TitleBarButtonStyle::Close); + + QObject::connect(_minButton, &TitleBarButton::leftClicked, this, + [this] { + this->setWindowState(Qt::WindowMinimized | + this->windowState()); + }); + QObject::connect(_maxButton, &TitleBarButton::leftClicked, this, + [this, _maxButton] { + this->setWindowState( + _maxButton->getButtonStyle() != + TitleBarButtonStyle::Maximize + ? Qt::WindowActive + : Qt::WindowMaximized); + }); + QObject::connect(_exitButton, &TitleBarButton::leftClicked, + 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); + this->ui_.layoutBase->setContentsMargins(1, 0, 1, 1); + layout->addWidget(this->ui_.layoutBase); + } + +// DPI +// auto dpi = getWindowDpi(this->winId()); + +// if (dpi) { +// this->scale = dpi.value() / 96.f; +// } +#endif + +#ifdef USEWINSDK + // fourtf: don't ask me why we need to delay this + if (!(this->flags_ & Flags::TopMost)) + { + QTimer::singleShot(1, this, [this] { + getSettings()->windowTopMost.connect( + [this](bool topMost, auto) { + ::SetWindowPos(HWND(this->winId()), + topMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, + 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + }, + this->managedConnections_); + }); + } +#else +// if (getSettings()->windowTopMost.getValue()) { +// this->setWindowFlag(Qt::WindowStaysOnTopHint); +// } +#endif +} + +void BaseWindow::setStayInScreenRect(bool value) +{ + this->stayInScreenRect_ = value; + + this->moveIntoDesktopRect(this); +} + +bool BaseWindow::getStayInScreenRect() const +{ + return this->stayInScreenRect_; +} + +void BaseWindow::setActionOnFocusLoss(ActionOnFocusLoss value) +{ + this->actionOnFocusLoss_ = value; +} + +BaseWindow::ActionOnFocusLoss BaseWindow::getActionOnFocusLoss() const +{ + return this->actionOnFocusLoss_; +} + +QWidget *BaseWindow::getLayoutContainer() +{ + if (this->hasCustomWindowFrame()) + { + return this->ui_.layoutBase; + } + else + { + return this; + } +} + +bool BaseWindow::hasCustomWindowFrame() +{ + return BaseWindow::supportsCustomWindowFrame() && this->enableCustomFrame_; +} + +bool BaseWindow::supportsCustomWindowFrame() +{ +#ifdef USEWINSDK + static bool isWin8 = IsWindows8OrGreater(); + + return isWin8; +#else + return false; +#endif +} + +void BaseWindow::themeChangedEvent() +{ + if (this->hasCustomWindowFrame()) + { + QPalette palette; + palette.setColor(QPalette::Window, QColor(80, 80, 80, 255)); + palette.setColor(QPalette::WindowText, this->theme->window.text); + this->setPalette(palette); + + if (this->ui_.titleLabel) + { + QPalette palette_title; + palette_title.setColor( + QPalette::WindowText, + this->theme->isLightTheme() ? "#333" : "#ccc"); + this->ui_.titleLabel->setPalette(palette_title); + } + + for (Button *button : this->ui_.buttons) + { + button->setMouseEffectColor(this->theme->window.text); + } + } + else + { + QPalette palette; + palette.setColor(QPalette::Background, this->theme->window.background); + palette.setColor(QPalette::Foreground, this->theme->window.text); + this->setPalette(palette); + } +} + +bool BaseWindow::event(QEvent *event) +{ + if (event->type() == + QEvent::WindowDeactivate /*|| event->type() == QEvent::FocusOut*/) + { + this->onFocusLost(); + } + + return QWidget::event(event); +} + +void BaseWindow::wheelEvent(QWheelEvent *event) +{ + if (event->orientation() != Qt::Vertical) + { + return; + } + + if (event->modifiers() & Qt::ControlModifier) + { + if (event->delta() > 0) + { + getSettings()->setClampedUiScale( + getSettings()->getClampedUiScale() + 0.1); + } + else + { + getSettings()->setClampedUiScale( + getSettings()->getClampedUiScale() - 0.1); + } + } +} + +void BaseWindow::onFocusLost() +{ + switch (this->getActionOnFocusLoss()) + { + case Delete: + { + this->deleteLater(); + } + break; + + case Close: + { + this->close(); + } + break; + + case Hide: + { + this->hide(); + } + break; + + default:; + } +} + +void BaseWindow::mousePressEvent(QMouseEvent *event) +{ +#ifndef Q_OS_WIN + if (this->flags_ & FramelessDraggable) + { + this->movingRelativePos = event->localPos(); + if (auto widget = + this->childAt(event->localPos().x(), event->localPos().y())) + { + std::function recursiveCheckMouseTracking; + recursiveCheckMouseTracking = [&](QWidget *widget) { + if (widget == nullptr) + { + return false; + } + + if (widget->hasMouseTracking()) + { + return true; + } + + return recursiveCheckMouseTracking(widget->parentWidget()); + }; + + if (!recursiveCheckMouseTracking(widget)) + { + log("Start moving"); + this->moving = true; + } + } + } +#endif + + BaseWidget::mousePressEvent(event); +} + +void BaseWindow::mouseReleaseEvent(QMouseEvent *event) +{ +#ifndef Q_OS_WIN + if (this->flags_ & FramelessDraggable) + { + if (this->moving) + { + log("Stop moving"); + this->moving = false; + } + } +#endif + + BaseWidget::mouseReleaseEvent(event); +} + +void BaseWindow::mouseMoveEvent(QMouseEvent *event) +{ +#ifndef Q_OS_WIN + if (this->flags_ & FramelessDraggable) + { + if (this->moving) + { + const auto &newPos = event->screenPos() - this->movingRelativePos; + this->move(newPos.x(), newPos.y()); + } + } +#endif + + BaseWidget::mouseMoveEvent(event); +} + +TitleBarButton *BaseWindow::addTitleBarButton(const TitleBarButtonStyle &style, + std::function onClicked) +{ + TitleBarButton *button = new TitleBarButton; + button->setScaleIndependantSize(30, 30); + + this->ui_.buttons.push_back(button); + this->ui_.titlebarBox->insertWidget(1, button); + button->setButtonStyle(style); + + QObject::connect(button, &TitleBarButton::leftClicked, this, + [onClicked] { onClicked(); }); + + return button; +} + +EffectLabel *BaseWindow::addTitleBarLabel(std::function onClicked) +{ + EffectLabel *button = new EffectLabel; + button->setScaleIndependantHeight(30); + + this->ui_.buttons.push_back(button); + this->ui_.titlebarBox->insertWidget(1, button); + + QObject::connect(button, &EffectLabel::leftClicked, this, + [onClicked] { onClicked(); }); + + return button; +} + +void BaseWindow::changeEvent(QEvent *) +{ + if (this->isVisible()) + { + TooltipWidget::getInstance()->hide(); + } + +#ifdef USEWINSDK + if (this->ui_.maxButton) + { + this->ui_.maxButton->setButtonStyle( + this->windowState() & Qt::WindowMaximized + ? TitleBarButtonStyle::Unmaximize + : TitleBarButtonStyle::Maximize); + } + + if (this->isVisible() && this->hasCustomWindowFrame()) + { + auto palette = this->palette(); + palette.setColor(QPalette::Window, + GetForegroundWindow() == HWND(this->winId()) + ? QColor(90, 90, 90) + : QColor(50, 50, 50)); + this->setPalette(palette); + } +#endif + +#ifndef Q_OS_WIN + this->update(); +#endif +} + +void BaseWindow::leaveEvent(QEvent *) +{ + TooltipWidget::getInstance()->hide(); +} + +void BaseWindow::moveTo(QWidget *parent, QPoint point, bool offset) +{ + if (offset) + { + point.rx() += 16; + point.ry() += 16; + } + + this->move(point); + this->moveIntoDesktopRect(parent); +} + +void BaseWindow::resizeEvent(QResizeEvent *) +{ + // Queue up save because: Window resized +#ifdef CHATTERINO + getApp()->windows->queueSave(); +#endif + + //this->moveIntoDesktopRect(this); + + this->calcButtonsSizes(); +} + +void BaseWindow::moveEvent(QMoveEvent *event) +{ + // Queue up save because: Window position changed +#ifdef CHATTERINO + getApp()->windows->queueSave(); +#endif + + BaseWidget::moveEvent(event); +} + +void BaseWindow::closeEvent(QCloseEvent *) +{ + this->closing.invoke(); +} + +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->availableGeometry(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); +} + +bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, + long *result) +{ +#ifdef USEWINSDK +# if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1)) + MSG *msg = *reinterpret_cast(message); +# else + MSG *msg = reinterpret_cast(message); +# endif + + bool returnValue = false; + + switch (msg->message) + { + case WM_DPICHANGED: + returnValue = this->handleDPICHANGED(msg); + break; + + case WM_SHOWWINDOW: + returnValue = this->handleSHOWWINDOW(msg); + break; + + case WM_NCCALCSIZE: + returnValue = this->handleNCCALCSIZE(msg, result); + break; + + case WM_SIZE: + returnValue = this->handleSIZE(msg); + break; + + case WM_MOVE: + returnValue = this->handleMOVE(msg); + *result = 0; + break; + + case WM_NCHITTEST: + returnValue = this->handleNCHITTEST(msg, result); + break; + + default: + return QWidget::nativeEvent(eventType, message, result); + } + + QWidget::nativeEvent(eventType, message, result); + + return returnValue; +#else + return QWidget::nativeEvent(eventType, message, result); +#endif +} + +void BaseWindow::scaleChangedEvent(float scale) +{ +#ifdef USEWINSDK + this->calcButtonsSizes(); +#endif + + this->setFont(getFonts()->getFont(FontStyle::UiTabs, this->qtFontScale())); +} + +void BaseWindow::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + + if (this->frameless_) + { + painter.setPen(QColor("#999")); + painter.drawRect(0, 0, this->width() - 1, this->height() - 1); + } + + this->drawCustomWindowFrame(painter); +} + +void BaseWindow::updateScale() +{ + auto scale = + this->nativeScale_ * (this->flags_ & DisableCustomScaling + ? 1 + : getABSettings()->getClampedUiScale()); + + this->setScale(scale); + + for (auto child : this->findChildren()) + { + child->setScale(scale); + } +} + +void BaseWindow::calcButtonsSizes() +{ + if (!this->shown_) + { + return; + } + + if ((this->width() / this->scale()) < 300) + { + 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 + { + 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); + } +} + +void BaseWindow::drawCustomWindowFrame(QPainter &painter) +{ +#ifdef USEWINSDK + if (this->hasCustomWindowFrame()) + { + QColor bg = this->overrideBackgroundColor_.value_or( + this->theme->window.background); + painter.fillRect(QRect(1, 2, this->width() - 2, this->height() - 3), + bg); + } +#endif +} + +bool BaseWindow::handleDPICHANGED(MSG *msg) +{ +#ifdef USEWINSDK + int dpi = HIWORD(msg->wParam); + + float _scale = dpi / 96.f; + + static bool firstResize = true; + + if (!firstResize) + { + auto *prcNewWindow = reinterpret_cast(msg->lParam); + SetWindowPos(msg->hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, + prcNewWindow->right - prcNewWindow->left, + prcNewWindow->bottom - prcNewWindow->top, + SWP_NOZORDER | SWP_NOACTIVATE); + } + firstResize = false; + + this->nativeScale_ = _scale; + this->updateScale(); + + return true; +#else + return false; +#endif +} + +bool BaseWindow::handleSHOWWINDOW(MSG *msg) +{ +#ifdef USEWINSDK + if (auto dpi = getWindowDpi(msg->hwnd)) + { + this->nativeScale_ = dpi.get() / 96.f; + this->updateScale(); + } + + if (!this->shown_ && this->isVisible()) + { + if (this->hasCustomWindowFrame()) + { + this->shown_ = true; + + const MARGINS shadow = {8, 8, 8, 8}; + DwmExtendFrameIntoClientArea(HWND(this->winId()), &shadow); + } + if (!this->initalBounds_.isNull()) + { + ::SetWindowPos(msg->hwnd, nullptr, this->initalBounds_.x(), + this->initalBounds_.y(), this->initalBounds_.width(), + this->initalBounds_.height(), + SWP_NOZORDER | SWP_NOACTIVATE); + this->currentBounds_ = this->initalBounds_; + } + } + + this->calcButtonsSizes(); + + return true; +#else + return false; +#endif +} + +bool BaseWindow::handleNCCALCSIZE(MSG *msg, long *result) +{ +#ifdef USEWINSDK + if (this->hasCustomWindowFrame()) + { + // int cx = GetSystemMetrics(SM_CXSIZEFRAME); + // int cy = GetSystemMetrics(SM_CYSIZEFRAME); + + if (msg->wParam == TRUE) + { + NCCALCSIZE_PARAMS *ncp = + (reinterpret_cast(msg->lParam)); + ncp->lppos->flags |= SWP_NOREDRAW; + RECT *clientRect = &ncp->rgrc[0]; + + clientRect->top -= 1; + } + + *result = 0; + return true; + } + return false; +#else + return false; +#endif +} + +bool BaseWindow::handleSIZE(MSG *msg) +{ +#ifdef USEWINSDK + if (this->ui_.windowLayout) + { + if (this->frameless_) + { + // + } + else if (this->hasCustomWindowFrame()) + { + if (msg->wParam == SIZE_MAXIMIZED) + { + auto offset = int( + getWindowDpi(HWND(this->winId())).value_or(96) * 8 / 96); + + this->ui_.windowLayout->setContentsMargins(offset, offset, + offset, offset); + } + else + { + this->ui_.windowLayout->setContentsMargins(0, 1, 0, 0); + } + + this->isNotMinimizedOrMaximized_ = msg->wParam == SIZE_RESTORED; + + if (this->isNotMinimizedOrMaximized_) + { + RECT rect; + ::GetWindowRect(msg->hwnd, &rect); + this->currentBounds_ = + QRect(QPoint(rect.left, rect.top), + QPoint(rect.right - 1, rect.bottom - 1)); + } + this->useNextBounds_.stop(); + } + } + return false; +#else + return false; +#endif +} + +bool BaseWindow::handleMOVE(MSG *msg) +{ +#ifdef USEWINSDK + if (this->isNotMinimizedOrMaximized_) + { + RECT rect; + ::GetWindowRect(msg->hwnd, &rect); + this->nextBounds_ = QRect(QPoint(rect.left, rect.top), + QPoint(rect.right - 1, rect.bottom - 1)); + + this->useNextBounds_.start(10); + } +#endif + return false; +} + +bool BaseWindow::handleNCHITTEST(MSG *msg, long *result) +{ +#ifdef USEWINSDK + 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); + + QPoint point(x - winrect.left, y - winrect.top); + + if (this->hasCustomWindowFrame()) + { + *result = 0; + + bool resizeWidth = minimumWidth() != maximumWidth(); + bool resizeHeight = minimumHeight() != maximumHeight(); + + if (resizeWidth) + { + // left border + if (x < winrect.left + border_width) + { + *result = HTLEFT; + } + // right border + if (x >= winrect.right - border_width) + { + *result = HTRIGHT; + } + } + if (resizeHeight) + { + // bottom border + if (y >= winrect.bottom - border_width) + { + *result = HTBOTTOM; + } + // top border + 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; + } + } + + if (*result == 0) + { + bool client = false; + + for (QWidget *widget : this->ui_.buttons) + { + if (widget->geometry().contains(point)) + { + client = true; + } + } + + if (this->ui_.layoutBase->geometry().contains(point)) + { + client = true; + } + + if (client) + { + *result = HTCLIENT; + } + else + { + *result = HTCAPTION; + } + } + + return true; + } + else if (this->flags_ & FramelessDraggable) + { + *result = 0; + bool client = false; + + if (auto widget = this->childAt(point)) + { + std::function recursiveCheckMouseTracking; + recursiveCheckMouseTracking = [&](QWidget *widget) { + if (widget == nullptr) + { + return false; + } + + if (widget->hasMouseTracking()) + { + return true; + } + + return recursiveCheckMouseTracking(widget->parentWidget()); + }; + + if (recursiveCheckMouseTracking(widget)) + { + client = true; + } + } + + if (client) + { + *result = HTCLIENT; + } + else + { + *result = HTCAPTION; + } + + return true; + } + return false; +#else + return false; +#endif +} + +} // namespace AB_NAMESPACE diff --git a/lib/appbase/widgets/BaseWindow.hpp b/lib/appbase/widgets/BaseWindow.hpp index a54a3ba80..03be8a56e 100644 --- a/lib/appbase/widgets/BaseWindow.hpp +++ b/lib/appbase/widgets/BaseWindow.hpp @@ -1,137 +1,137 @@ -#pragma once - -#include "widgets/BaseWidget.hpp" - -#include -#include - -class QHBoxLayout; -struct tagMSG; -typedef struct tagMSG MSG; - -namespace AB_NAMESPACE { - -class Button; -class EffectLabel; -class TitleBarButton; -enum class TitleBarButtonStyle; - -class BaseWindow : public BaseWidget -{ - Q_OBJECT - -public: - enum Flags { - None = 0, - EnableCustomFrame = 1, - Frameless = 2, - TopMost = 4, - DisableCustomScaling = 8, - FramelessDraggable = 16, - }; - - enum ActionOnFocusLoss { Nothing, Delete, Close, Hide }; - - explicit BaseWindow(QWidget *parent = nullptr, Flags flags_ = None); - - void setInitialBounds(const QRect &bounds); - QRect getBounds(); - - QWidget *getLayoutContainer(); - bool hasCustomWindowFrame(); - TitleBarButton *addTitleBarButton(const TitleBarButtonStyle &style, - std::function onClicked); - EffectLabel *addTitleBarLabel(std::function onClicked); - - void setStayInScreenRect(bool value); - bool getStayInScreenRect() const; - - void setActionOnFocusLoss(ActionOnFocusLoss value); - ActionOnFocusLoss getActionOnFocusLoss() const; - - void moveTo(QWidget *widget, QPoint point, bool offset = true); - - virtual float scale() const override; - float qtFontScale() const; - - Flags getFlags(); - - pajlada::Signals::NoArgSignal closing; - - static bool supportsCustomWindowFrame(); - -protected: - virtual bool nativeEvent(const QByteArray &eventType, void *message, - long *result) override; - virtual void scaleChangedEvent(float) override; - - virtual void paintEvent(QPaintEvent *) override; - - virtual void changeEvent(QEvent *) override; - virtual void leaveEvent(QEvent *) override; - virtual void resizeEvent(QResizeEvent *) override; - virtual void moveEvent(QMoveEvent *) override; - virtual void closeEvent(QCloseEvent *) override; - - virtual void themeChangedEvent() override; - virtual bool event(QEvent *event) override; - virtual void wheelEvent(QWheelEvent *event) override; - - void mousePressEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - QPointF movingRelativePos; - bool moving{}; - - void updateScale(); - - boost::optional overrideBackgroundColor_; - -private: - void init(); - void moveIntoDesktopRect(QWidget *parent); - void calcButtonsSizes(); - void drawCustomWindowFrame(QPainter &painter); - void onFocusLost(); - - bool handleDPICHANGED(MSG *msg); - bool handleSHOWWINDOW(MSG *msg); - bool handleNCCALCSIZE(MSG *msg, long *result); - bool handleSIZE(MSG *msg); - bool handleMOVE(MSG *msg); - bool handleNCHITTEST(MSG *msg, long *result); - - bool enableCustomFrame_; - ActionOnFocusLoss actionOnFocusLoss_ = Nothing; - bool frameless_; - bool stayInScreenRect_ = false; - bool shown_ = false; - Flags flags_; - float nativeScale_ = 1; - - struct { - QLayout *windowLayout = nullptr; - QHBoxLayout *titlebarBox = nullptr; - QWidget *titleLabel = nullptr; - TitleBarButton *minButton = nullptr; - TitleBarButton *maxButton = nullptr; - TitleBarButton *exitButton = nullptr; - QWidget *layoutBase = nullptr; - std::vector