mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Remove appbase as an external dependency.
This commit is contained in:
parent
628c64d138
commit
a85e5821ba
16
.gitmodules
vendored
16
.gitmodules
vendored
|
@ -14,7 +14,15 @@
|
|||
[submodule "lib/WinToast"]
|
||||
path = lib/WinToast
|
||||
url = https://github.com/mohabouje/WinToast.git
|
||||
|
||||
[submodule "lib/appbase"]
|
||||
path = lib/appbase
|
||||
url = https://github.com/Chatterino/appbase
|
||||
[submodule "lib/settings"]
|
||||
path = lib/settings
|
||||
url = https://github.com/pajlada/settings
|
||||
[submodule "lib/signals"]
|
||||
path = lib/signals
|
||||
url = https://github.com/pajlada/signals
|
||||
[submodule "lib/serialize"]
|
||||
path = lib/serialize
|
||||
url = https://github.com/pajlada/serialize
|
||||
[submodule "lib/rapidjson"]
|
||||
path = lib/rapidjson
|
||||
url = https://github.com/Tencent/rapidjson
|
||||
|
|
|
@ -19,6 +19,13 @@ useBreakpad {
|
|||
DEFINES += C_USE_BREAKPAD
|
||||
}
|
||||
|
||||
# use C++17
|
||||
win32-msvc* {
|
||||
QMAKE_CXXFLAGS += /std:c++17
|
||||
} else {
|
||||
QMAKE_CXXFLAGS += -std=c++17
|
||||
}
|
||||
|
||||
# https://bugreports.qt.io/browse/QTBUG-27018
|
||||
equals(QMAKE_CXX, "clang++")|equals(QMAKE_CXX, "g++") {
|
||||
TARGET = bin/chatterino
|
||||
|
@ -33,13 +40,21 @@ macx {
|
|||
}
|
||||
|
||||
# Submodules
|
||||
include(lib/appbase.pri)
|
||||
include(lib/humanize.pri)
|
||||
DEFINES += IRC_NAMESPACE=Communi
|
||||
|
||||
include(lib/appbase.pri)
|
||||
include(lib/boost.pri)
|
||||
include(lib/fmt.pri)
|
||||
include(lib/humanize.pri)
|
||||
include(lib/libcommuni.pri)
|
||||
include(lib/websocketpp.pri)
|
||||
include(lib/openssl.pri)
|
||||
include(lib/wintoast.pri)
|
||||
include(lib/signals.pri)
|
||||
include(lib/settings.pri)
|
||||
include(lib/serialize.pri)
|
||||
include(lib/winsdk.pri)
|
||||
include(lib/rapidjson.pri)
|
||||
|
||||
# Optional feature: QtWebEngine
|
||||
#exists ($(QTDIR)/include/QtWebEngine/QtWebEngine) {
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit d054925734cf26576346a1da856f7ab0d4b6c0a5
|
|
@ -1,3 +1,3 @@
|
|||
# include appbase
|
||||
include($$PWD/appbase/appbase/main/main.pro)
|
||||
INCLUDEPATH += $$PWD/appbase/appbase/main
|
||||
include($$PWD/appbase/main.pro)
|
||||
INCLUDEPATH += $$PWD/appbase
|
||||
|
|
128
lib/appbase/BaseSettings.cpp
Normal file
128
lib/appbase/BaseSettings.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
#include "BaseSettings.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "util/Clamp.hpp"
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
std::vector<std::weak_ptr<pajlada::Settings::SettingData>> _settings;
|
||||
|
||||
AB_SETTINGS_CLASS *AB_SETTINGS_CLASS::instance = nullptr;
|
||||
|
||||
void _actuallyRegisterSetting(
|
||||
std::weak_ptr<pajlada::Settings::SettingData> 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<float>(this->uiScale.getValue(), 0.1, 10);
|
||||
}
|
||||
|
||||
void AB_SETTINGS_CLASS::setClampedUiScale(float value)
|
||||
{
|
||||
this->uiScale.setValue(clamp<float>(value, 0.1, 10));
|
||||
}
|
||||
|
||||
#ifndef AB_CUSTOM_SETTINGS
|
||||
Settings *getSettings()
|
||||
{
|
||||
static_assert(std::is_same_v<AB_SETTINGS_CLASS, Settings>,
|
||||
"`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
|
52
lib/appbase/BaseSettings.hpp
Normal file
52
lib/appbase/BaseSettings.hpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
#ifndef AB_SETTINGS_H
|
||||
#define AB_SETTINGS_H
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
#include <pajlada/settings/settingdata.hpp>
|
||||
|
||||
#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<pajlada::Settings::SettingData> 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<rapidjson::Document> snapshot_;
|
||||
};
|
||||
|
||||
Settings *getSettings();
|
||||
AB_SETTINGS_CLASS *getABSettings();
|
||||
|
||||
} // namespace AB_NAMESPACE
|
||||
|
||||
#ifdef CHATTERINO
|
||||
# include "singletons/Settings.hpp"
|
||||
#endif
|
||||
#endif
|
226
lib/appbase/BaseTheme.cpp
Normal file
226
lib/appbase/BaseTheme.cpp
Normal file
|
@ -0,0 +1,226 @@
|
|||
#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
|
||||
{
|
||||
QColor bg =
|
||||
#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
|
117
lib/appbase/BaseTheme.hpp
Normal file
117
lib/appbase/BaseTheme.hpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
#ifndef AB_THEME_H
|
||||
#define AB_THEME_H
|
||||
|
||||
#include <QBrush>
|
||||
#include <QColor>
|
||||
#include <common/ChatterinoSetting.hpp>
|
||||
|
||||
#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
|
36
lib/appbase/common/Aliases.hpp
Normal file
36
lib/appbase/common/Aliases.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
|
||||
#define QStringAlias(name) \
|
||||
namespace chatterino { \
|
||||
struct name { \
|
||||
QString string; \
|
||||
bool operator==(const name &other) const \
|
||||
{ \
|
||||
return this->string == other.string; \
|
||||
} \
|
||||
bool operator!=(const name &other) const \
|
||||
{ \
|
||||
return this->string != other.string; \
|
||||
} \
|
||||
}; \
|
||||
} /* namespace chatterino */ \
|
||||
namespace std { \
|
||||
template <> \
|
||||
struct hash<chatterino::name> { \
|
||||
size_t operator()(const chatterino::name &s) const \
|
||||
{ \
|
||||
return qHash(s.string); \
|
||||
} \
|
||||
}; \
|
||||
} /* namespace std */
|
||||
|
||||
QStringAlias(UserName);
|
||||
QStringAlias(UserId);
|
||||
QStringAlias(Url);
|
||||
QStringAlias(Tooltip);
|
||||
QStringAlias(EmoteId);
|
||||
QStringAlias(EmoteName);
|
12
lib/appbase/common/ChatterinoSetting.cpp
Normal file
12
lib/appbase/common/ChatterinoSetting.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "common/ChatterinoSetting.hpp"
|
||||
|
||||
#include "BaseSettings.hpp"
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
void _registerSetting(std::weak_ptr<pajlada::Settings::SettingData> setting)
|
||||
{
|
||||
_actuallyRegisterSetting(setting);
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
54
lib/appbase/common/ChatterinoSetting.hpp
Normal file
54
lib/appbase/common/ChatterinoSetting.hpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <pajlada/settings.hpp>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
void _registerSetting(std::weak_ptr<pajlada::Settings::SettingData> setting);
|
||||
|
||||
template <typename Type>
|
||||
class ChatterinoSetting : public pajlada::Settings::Setting<Type>
|
||||
{
|
||||
public:
|
||||
ChatterinoSetting(const std::string &path)
|
||||
: pajlada::Settings::Setting<Type>(path)
|
||||
{
|
||||
_registerSetting(this->getData());
|
||||
}
|
||||
|
||||
ChatterinoSetting(const std::string &path, const Type &defaultValue)
|
||||
: pajlada::Settings::Setting<Type>(path, defaultValue)
|
||||
{
|
||||
_registerSetting(this->getData());
|
||||
}
|
||||
|
||||
template <typename T2>
|
||||
ChatterinoSetting &operator=(const T2 &newValue)
|
||||
{
|
||||
this->setValue(newValue);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ChatterinoSetting &operator=(Type &&newValue) noexcept
|
||||
{
|
||||
pajlada::Settings::Setting<Type>::operator=(newValue);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
using pajlada::Settings::Setting<Type>::operator==;
|
||||
using pajlada::Settings::Setting<Type>::operator!=;
|
||||
|
||||
using pajlada::Settings::Setting<Type>::operator Type;
|
||||
};
|
||||
|
||||
using BoolSetting = ChatterinoSetting<bool>;
|
||||
using FloatSetting = ChatterinoSetting<float>;
|
||||
using DoubleSetting = ChatterinoSetting<double>;
|
||||
using IntSetting = ChatterinoSetting<int>;
|
||||
using StringSetting = ChatterinoSetting<std::string>;
|
||||
using QStringSetting = ChatterinoSetting<QString>;
|
||||
|
||||
} // namespace AB_NAMESPACE
|
82
lib/appbase/common/FlagsEnum.hpp
Normal file
82
lib/appbase/common/FlagsEnum.hpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
template <typename T, typename Q = typename std::underlying_type<T>::type>
|
||||
class FlagsEnum
|
||||
{
|
||||
public:
|
||||
FlagsEnum()
|
||||
: value_(static_cast<T>(0))
|
||||
{
|
||||
}
|
||||
|
||||
FlagsEnum(T value)
|
||||
: value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
FlagsEnum(std::initializer_list<T> flags)
|
||||
{
|
||||
for (auto flag : flags)
|
||||
{
|
||||
this->set(flag);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const FlagsEnum<T> &other)
|
||||
{
|
||||
return this->value_ == other.value_;
|
||||
}
|
||||
|
||||
bool operator!=(const FlagsEnum &other)
|
||||
{
|
||||
return this->value_ != other.value_;
|
||||
}
|
||||
|
||||
void set(T flag)
|
||||
{
|
||||
reinterpret_cast<Q &>(this->value_) |= static_cast<Q>(flag);
|
||||
}
|
||||
|
||||
void unset(T flag)
|
||||
{
|
||||
reinterpret_cast<Q &>(this->value_) &= ~static_cast<Q>(flag);
|
||||
}
|
||||
|
||||
void set(T flag, bool value)
|
||||
{
|
||||
if (value)
|
||||
this->set(flag);
|
||||
else
|
||||
this->unset(flag);
|
||||
}
|
||||
|
||||
bool has(T flag) const
|
||||
{
|
||||
return static_cast<Q>(this->value_) & static_cast<Q>(flag);
|
||||
}
|
||||
|
||||
bool hasAny(FlagsEnum flags) const
|
||||
{
|
||||
return static_cast<Q>(this->value_) & static_cast<Q>(flags.value_);
|
||||
}
|
||||
|
||||
bool hasAll(FlagsEnum<T> flags) const
|
||||
{
|
||||
return (static_cast<Q>(this->value_) & static_cast<Q>(flags.value_)) &&
|
||||
static_cast<Q>(flags->value);
|
||||
}
|
||||
|
||||
bool hasNone(std::initializer_list<T> flags) const
|
||||
{
|
||||
return !this->hasAny(flags);
|
||||
}
|
||||
|
||||
private:
|
||||
T value_{};
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
51
lib/appbase/common/Outcome.hpp
Normal file
51
lib/appbase/common/Outcome.hpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct SuccessTag {
|
||||
};
|
||||
|
||||
struct FailureTag {
|
||||
};
|
||||
|
||||
const SuccessTag Success{};
|
||||
const FailureTag Failure{};
|
||||
|
||||
class Outcome
|
||||
{
|
||||
public:
|
||||
Outcome(SuccessTag)
|
||||
: success_(true)
|
||||
{
|
||||
}
|
||||
|
||||
Outcome(FailureTag)
|
||||
: success_(false)
|
||||
{
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return this->success_;
|
||||
}
|
||||
|
||||
bool operator!() const
|
||||
{
|
||||
return !this->success_;
|
||||
}
|
||||
|
||||
bool operator==(const Outcome &other) const
|
||||
{
|
||||
return this->success_ == other.success_;
|
||||
}
|
||||
|
||||
bool operator!=(const Outcome &other) const
|
||||
{
|
||||
return !this->operator==(other);
|
||||
}
|
||||
|
||||
private:
|
||||
bool success_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
26
lib/appbase/common/Singleton.hpp
Normal file
26
lib/appbase/common/Singleton.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
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
|
16
lib/appbase/debug/AssertInGuiThread.hpp
Normal file
16
lib/appbase/debug/AssertInGuiThread.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
#include <cassert>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
static void assertInGuiThread()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
assert(QCoreApplication::instance()->thread() == QThread::currentThread());
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
21
lib/appbase/debug/Benchmark.cpp
Normal file
21
lib/appbase/debug/Benchmark.cpp
Normal file
|
@ -0,0 +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
|
22
lib/appbase/debug/Benchmark.hpp
Normal file
22
lib/appbase/debug/Benchmark.hpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "debug/Log.hpp"
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
class BenchmarkGuard : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
BenchmarkGuard(const QString &_name);
|
||||
~BenchmarkGuard();
|
||||
qreal getElapsedMs();
|
||||
|
||||
private:
|
||||
QElapsedTimer timer_;
|
||||
QString name_;
|
||||
};
|
||||
|
||||
} // namespace AB_NAMESPACE
|
29
lib/appbase/debug/Log.hpp
Normal file
29
lib/appbase/debug/Log.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/Helpers.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTime>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
template <typename... Args>
|
||||
inline void log(const std::string &formatString, Args &&... args)
|
||||
{
|
||||
qDebug().noquote() << QTime::currentTime().toString("hh:mm:ss.zzz")
|
||||
<< fS(formatString, std::forward<Args>(args)...).c_str();
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void log(const char *formatString, Args &&... args)
|
||||
{
|
||||
log(std::string(formatString), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void log(const QString &formatString, Args &&... args)
|
||||
{
|
||||
log(formatString.toStdString(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
31
lib/appbase/main.cpp
Normal file
31
lib/appbase/main.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#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();
|
||||
}
|
108
lib/appbase/main.pro
Normal file
108
lib/appbase/main.pro
Normal file
|
@ -0,0 +1,108 @@
|
|||
#-------------------------------------------------
|
||||
#
|
||||
# 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
|
||||
}
|
||||
}
|
||||
|
||||
include(../warnings.pri)
|
||||
include(../lib/winsdk.pri)
|
||||
include(../lib/fmt.pri)
|
||||
include(../lib/boost.pri)
|
||||
include(../lib/rapidjson.pri)
|
||||
include(../lib/serialize.pri)
|
||||
include(../lib/signals.pri)
|
||||
include(../lib/settings.pri)
|
||||
|
||||
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/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 \
|
179
lib/appbase/singletons/Fonts.cpp
Normal file
179
lib/appbase/singletons/Fonts.cpp
Normal file
|
@ -0,0 +1,179 @@
|
|||
#include "singletons/Fonts.hpp"
|
||||
|
||||
#include "BaseSettings.hpp"
|
||||
#include "debug/AssertInGuiThread.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QtGlobal>
|
||||
|
||||
#ifdef CHATTERINO
|
||||
# include "Application.hpp"
|
||||
# include "singletons/WindowManager.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
# define DEFAULT_FONT_FAMILY "Segoe UI"
|
||||
# define DEFAULT_FONT_SIZE 10
|
||||
#else
|
||||
# ifdef Q_OS_MACOS
|
||||
# define DEFAULT_FONT_FAMILY "Helvetica Neue"
|
||||
# define DEFAULT_FONT_SIZE 12
|
||||
# else
|
||||
# define DEFAULT_FONT_FAMILY "Arial"
|
||||
# define DEFAULT_FONT_SIZE 11
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
namespace {
|
||||
int getBoldness()
|
||||
{
|
||||
#ifdef CHATTERINO
|
||||
return getSettings()->boldScale.getValue();
|
||||
#else
|
||||
return QFont::Bold;
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Fonts *Fonts::instance = nullptr;
|
||||
|
||||
Fonts::Fonts()
|
||||
: chatFontFamily("/appearance/currentFontFamily", DEFAULT_FONT_FAMILY)
|
||||
, chatFontSize("/appearance/currentFontSize", DEFAULT_FONT_SIZE)
|
||||
{
|
||||
Fonts::instance = this;
|
||||
|
||||
this->fontsByType_.resize(size_t(FontStyle::EndType));
|
||||
}
|
||||
|
||||
void Fonts::initialize(Settings &, Paths &)
|
||||
{
|
||||
this->chatFontFamily.connect(
|
||||
[this]() {
|
||||
assertInGuiThread();
|
||||
|
||||
for (auto &map : this->fontsByType_)
|
||||
{
|
||||
map.clear();
|
||||
}
|
||||
this->fontChanged.invoke();
|
||||
},
|
||||
false);
|
||||
|
||||
this->chatFontSize.connect(
|
||||
[this]() {
|
||||
assertInGuiThread();
|
||||
|
||||
for (auto &map : this->fontsByType_)
|
||||
{
|
||||
map.clear();
|
||||
}
|
||||
this->fontChanged.invoke();
|
||||
},
|
||||
false);
|
||||
|
||||
#ifdef CHATTERINO
|
||||
getSettings()->boldScale.connect(
|
||||
[this]() {
|
||||
assertInGuiThread();
|
||||
|
||||
// REMOVED
|
||||
getApp()->windows->incGeneration();
|
||||
|
||||
for (auto &map : this->fontsByType_)
|
||||
{
|
||||
map.clear();
|
||||
}
|
||||
this->fontChanged.invoke();
|
||||
},
|
||||
false);
|
||||
#endif
|
||||
} // namespace AB_NAMESPACE
|
||||
|
||||
QFont Fonts::getFont(FontStyle type, float scale)
|
||||
{
|
||||
return this->getOrCreateFontData(type, scale).font;
|
||||
}
|
||||
|
||||
QFontMetrics Fonts::getFontMetrics(FontStyle type, float scale)
|
||||
{
|
||||
return this->getOrCreateFontData(type, scale).metrics;
|
||||
}
|
||||
|
||||
Fonts::FontData &Fonts::getOrCreateFontData(FontStyle type, float scale)
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
assert(type < FontStyle::EndType);
|
||||
|
||||
auto &map = this->fontsByType_[size_t(type)];
|
||||
|
||||
// find element
|
||||
auto it = map.find(scale);
|
||||
if (it != map.end())
|
||||
{
|
||||
// return if found
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// emplace new element
|
||||
auto result = map.emplace(scale, this->createFontData(type, scale));
|
||||
assert(result.second);
|
||||
|
||||
return result.first->second;
|
||||
}
|
||||
|
||||
Fonts::FontData Fonts::createFontData(FontStyle type, float scale)
|
||||
{
|
||||
// check if it's a chat (scale the setting)
|
||||
if (type >= FontStyle::ChatStart && type <= FontStyle::ChatEnd)
|
||||
{
|
||||
static std::unordered_map<FontStyle, ChatFontData> sizeScale{
|
||||
{FontStyle::ChatSmall, {0.6f, false, QFont::Normal}},
|
||||
{FontStyle::ChatMediumSmall, {0.8f, false, QFont::Normal}},
|
||||
{FontStyle::ChatMedium, {1, false, QFont::Normal}},
|
||||
{FontStyle::ChatMediumBold,
|
||||
{1, false, QFont::Weight(getBoldness())}},
|
||||
{FontStyle::ChatMediumItalic, {1, true, QFont::Normal}},
|
||||
{FontStyle::ChatLarge, {1.2f, false, QFont::Normal}},
|
||||
{FontStyle::ChatVeryLarge, {1.4f, false, QFont::Normal}},
|
||||
};
|
||||
sizeScale[FontStyle::ChatMediumBold] = {1, false,
|
||||
QFont::Weight(getBoldness())};
|
||||
auto data = sizeScale[type];
|
||||
return FontData(
|
||||
QFont(this->chatFontFamily.getValue(),
|
||||
int(this->chatFontSize.getValue() * data.scale * scale),
|
||||
data.weight, data.italic));
|
||||
}
|
||||
|
||||
// normal Ui font (use pt size)
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
constexpr float multiplier = 0.8f;
|
||||
#else
|
||||
constexpr float multiplier = 1.f;
|
||||
#endif
|
||||
|
||||
static std::unordered_map<FontStyle, UiFontData> defaultSize{
|
||||
{FontStyle::Tiny, {8, "Monospace", false, QFont::Normal}},
|
||||
{FontStyle::UiMedium,
|
||||
{int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}},
|
||||
{FontStyle::UiTabs,
|
||||
{int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}},
|
||||
};
|
||||
|
||||
UiFontData &data = defaultSize[type];
|
||||
QFont font(data.name, int(data.size * scale), data.weight, data.italic);
|
||||
return FontData(font);
|
||||
}
|
||||
}
|
||||
|
||||
Fonts *getFonts()
|
||||
{
|
||||
return Fonts::instance;
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
92
lib/appbase/singletons/Fonts.hpp
Normal file
92
lib/appbase/singletons/Fonts.hpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/ChatterinoSetting.hpp"
|
||||
#include "common/Singleton.hpp"
|
||||
|
||||
#include <QFont>
|
||||
#include <QFontDatabase>
|
||||
#include <QFontMetrics>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
class Settings;
|
||||
class Paths;
|
||||
|
||||
enum class FontStyle : uint8_t {
|
||||
Tiny,
|
||||
ChatSmall,
|
||||
ChatMediumSmall,
|
||||
ChatMedium,
|
||||
ChatMediumBold,
|
||||
ChatMediumItalic,
|
||||
ChatLarge,
|
||||
ChatVeryLarge,
|
||||
|
||||
UiMedium,
|
||||
UiTabs,
|
||||
|
||||
// don't remove this value
|
||||
EndType,
|
||||
|
||||
// make sure to update these values accordingly!
|
||||
ChatStart = ChatSmall,
|
||||
ChatEnd = ChatVeryLarge,
|
||||
};
|
||||
|
||||
class Fonts final : public Singleton
|
||||
{
|
||||
public:
|
||||
Fonts();
|
||||
|
||||
virtual void initialize(Settings &settings, Paths &paths) override;
|
||||
|
||||
// font data gets set in createFontData(...)
|
||||
|
||||
QFont getFont(FontStyle type, float scale);
|
||||
QFontMetrics getFontMetrics(FontStyle type, float scale);
|
||||
|
||||
QStringSetting chatFontFamily;
|
||||
IntSetting chatFontSize;
|
||||
|
||||
pajlada::Signals::NoArgSignal fontChanged;
|
||||
static Fonts *instance;
|
||||
|
||||
private:
|
||||
struct FontData {
|
||||
FontData(const QFont &_font)
|
||||
: font(_font)
|
||||
, metrics(_font)
|
||||
{
|
||||
}
|
||||
|
||||
const QFont font;
|
||||
const QFontMetrics metrics;
|
||||
};
|
||||
|
||||
struct ChatFontData {
|
||||
float scale;
|
||||
bool italic;
|
||||
QFont::Weight weight;
|
||||
};
|
||||
|
||||
struct UiFontData {
|
||||
float size;
|
||||
const char *name;
|
||||
bool italic;
|
||||
QFont::Weight weight;
|
||||
};
|
||||
|
||||
FontData &getOrCreateFontData(FontStyle type, float scale);
|
||||
FontData createFontData(FontStyle type, float scale);
|
||||
|
||||
std::vector<std::unordered_map<float, FontData>> fontsByType_;
|
||||
};
|
||||
|
||||
Fonts *getFonts();
|
||||
|
||||
} // namespace AB_NAMESPACE
|
13
lib/appbase/util/Clamp.hpp
Normal file
13
lib/appbase/util/Clamp.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
// http://en.cppreference.com/w/cpp/algorithm/clamp
|
||||
|
||||
template <class T>
|
||||
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
|
14
lib/appbase/util/CombinePath.hpp
Normal file
14
lib/appbase/util/CombinePath.hpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
|
||||
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
|
20
lib/appbase/util/DistanceBetweenPoints.hpp
Normal file
20
lib/appbase/util/DistanceBetweenPoints.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <QPointF>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
inline float distanceBetweenPoints(const QPointF &p1, const QPointF &p2)
|
||||
{
|
||||
QPointF tmp = p1 - p2;
|
||||
|
||||
float distance = 0.f;
|
||||
distance += tmp.x() * tmp.x();
|
||||
distance += tmp.y() * tmp.y();
|
||||
|
||||
return sqrt(distance);
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
17
lib/appbase/util/FunctionEventFilter.cpp
Normal file
17
lib/appbase/util/FunctionEventFilter.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include "FunctionEventFilter.hpp"
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
FunctionEventFilter::FunctionEventFilter(
|
||||
QObject *parent, std::function<bool(QObject *, QEvent *)> function)
|
||||
: QObject(parent)
|
||||
, function_(std::move(function))
|
||||
{
|
||||
}
|
||||
|
||||
bool FunctionEventFilter::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
return this->function_(watched, event);
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
24
lib/appbase/util/FunctionEventFilter.hpp
Normal file
24
lib/appbase/util/FunctionEventFilter.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <QEvent>
|
||||
#include <QObject>
|
||||
#include <functional>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
class FunctionEventFilter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FunctionEventFilter(QObject *parent,
|
||||
std::function<bool(QObject *, QEvent *)> function);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
private:
|
||||
std::function<bool(QObject *, QEvent *)> function_;
|
||||
};
|
||||
|
||||
} // namespace AB_NAMESPACE
|
33
lib/appbase/util/FuzzyConvert.cpp
Normal file
33
lib/appbase/util/FuzzyConvert.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "FuzzyConvert.hpp"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
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
|
10
lib/appbase/util/FuzzyConvert.hpp
Normal file
10
lib/appbase/util/FuzzyConvert.hpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
int fuzzyToInt(const QString &str, int default_);
|
||||
float fuzzyToFloat(const QString &str, float default_);
|
||||
|
||||
} // namespace chatterino
|
56
lib/appbase/util/Helpers.hpp
Normal file
56
lib/appbase/util/Helpers.hpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <QUuid>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
template <typename... Args>
|
||||
auto fS(Args &&... args)
|
||||
{
|
||||
return fmt::format(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static QString CreateUUID()
|
||||
{
|
||||
auto uuid = QUuid::createUuid();
|
||||
return uuid.toString();
|
||||
}
|
||||
|
||||
static QString createLink(const QString &url, bool file = false)
|
||||
{
|
||||
return QString("<a href=\"") + (file ? "file:///" : "") + url + "\">" +
|
||||
url + "</a>";
|
||||
}
|
||||
|
||||
static QString createNamedLink(const QString &url, const QString &name,
|
||||
bool file = false)
|
||||
{
|
||||
return QString("<a href=\"") + (file ? "file:///" : "") + url + "\">" +
|
||||
name + "</a>";
|
||||
}
|
||||
|
||||
static QString shortenString(const QString &str, unsigned maxWidth = 50)
|
||||
{
|
||||
auto shortened = QString(str);
|
||||
|
||||
if (str.size() > int(maxWidth))
|
||||
{
|
||||
shortened.resize(int(maxWidth));
|
||||
shortened += "...";
|
||||
}
|
||||
|
||||
return shortened;
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
||||
|
||||
namespace fmt {
|
||||
|
||||
// format_arg for QString
|
||||
inline void format_arg(BasicFormatter<char> &f, const char *&, const QString &v)
|
||||
{
|
||||
f.writer().write("{}", v.toStdString());
|
||||
}
|
||||
|
||||
} // namespace fmt
|
42
lib/appbase/util/LayoutHelper.hpp
Normal file
42
lib/appbase/util/LayoutHelper.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <QLayout>
|
||||
#include <QWidget>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
using LayoutItem = boost::variant<QWidget *, QLayout *>;
|
||||
|
||||
template <typename T>
|
||||
T *makeLayout(std::initializer_list<LayoutItem> items)
|
||||
{
|
||||
auto t = new T;
|
||||
|
||||
for (auto &item : items)
|
||||
{
|
||||
switch (item.which())
|
||||
{
|
||||
case 0:
|
||||
t->addItem(new QWidgetItem(boost::get<QWidget *>(item)));
|
||||
break;
|
||||
case 1:
|
||||
t->addItem(boost::get<QLayout *>(item));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T, typename With>
|
||||
T *makeWidget(With with)
|
||||
{
|
||||
auto t = new T;
|
||||
|
||||
with(t);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
59
lib/appbase/util/PostToThread.hpp
Normal file
59
lib/appbase/util/PostToThread.hpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <QRunnable>
|
||||
#include <QThreadPool>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#define async_exec(a) \
|
||||
QThreadPool::globalInstance()->start(new LambdaRunnable(a));
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
class LambdaRunnable : public QRunnable
|
||||
{
|
||||
public:
|
||||
LambdaRunnable(std::function<void()> action)
|
||||
{
|
||||
this->action_ = action;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
this->action_();
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> action_;
|
||||
};
|
||||
|
||||
// Taken from
|
||||
// https://stackoverflow.com/questions/21646467/how-to-execute-a-functor-or-a-lambda-in-a-given-thread-in-qt-gcd-style
|
||||
// Qt 5/4 - preferred, has least allocations
|
||||
template <typename F>
|
||||
static void postToThread(F &&fun, QObject *obj = qApp)
|
||||
{
|
||||
struct Event : public QEvent {
|
||||
using Fun = typename std::decay<F>::type;
|
||||
Fun fun;
|
||||
Event(Fun &&fun)
|
||||
: QEvent(QEvent::None)
|
||||
, fun(std::move(fun))
|
||||
{
|
||||
}
|
||||
Event(const Fun &fun)
|
||||
: QEvent(QEvent::None)
|
||||
, fun(fun)
|
||||
{
|
||||
}
|
||||
~Event() override
|
||||
{
|
||||
fun();
|
||||
}
|
||||
};
|
||||
QCoreApplication::postEvent(obj, new Event(std::forward<F>(fun)));
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
45
lib/appbase/util/RapidJsonSerializeQString.hpp
Normal file
45
lib/appbase/util/RapidJsonSerializeQString.hpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <pajlada/serialize.hpp>
|
||||
|
||||
namespace pajlada {
|
||||
|
||||
template <>
|
||||
struct Serialize<QString> {
|
||||
static rapidjson::Value get(const QString &value,
|
||||
rapidjson::Document::AllocatorType &a)
|
||||
{
|
||||
return rapidjson::Value(value.toUtf8(), a);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Deserialize<QString> {
|
||||
static QString get(const rapidjson::Value &value, bool *error = nullptr)
|
||||
{
|
||||
if (!value.IsString())
|
||||
{
|
||||
PAJLADA_REPORT_ERROR(error)
|
||||
return QString{};
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return QString::fromUtf8(value.GetString(),
|
||||
value.GetStringLength());
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
// int x = 5;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// int y = 5;
|
||||
}
|
||||
|
||||
return QString{};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pajlada
|
24
lib/appbase/util/Shortcut.hpp
Normal file
24
lib/appbase/util/Shortcut.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <QShortcut>
|
||||
#include <QWidget>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
template <typename WidgetType, typename Func>
|
||||
inline void createShortcut(WidgetType *w, const char *key, Func func)
|
||||
{
|
||||
auto s = new QShortcut(QKeySequence(key), w);
|
||||
s->setContext(Qt::WidgetWithChildrenShortcut);
|
||||
QObject::connect(s, &QShortcut::activated, w, func);
|
||||
}
|
||||
|
||||
template <typename WidgetType, typename Func>
|
||||
inline void createWindowShortcut(WidgetType *w, const char *key, Func func)
|
||||
{
|
||||
auto s = new QShortcut(QKeySequence(key), w);
|
||||
s->setContext(Qt::WindowShortcut);
|
||||
QObject::connect(s, &QShortcut::activated, w, func);
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
86
lib/appbase/util/WindowsHelper.cpp
Normal file
86
lib/appbase/util/WindowsHelper.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
#include "WindowsHelper.hpp"
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
#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<UINT> 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
|
18
lib/appbase/util/WindowsHelper.hpp
Normal file
18
lib/appbase/util/WindowsHelper.hpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef USEWINSDK
|
||||
|
||||
# include <Windows.h>
|
||||
# include <boost/optional.hpp>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
boost::optional<UINT> getWindowDpi(HWND hwnd);
|
||||
void flushClipboard();
|
||||
|
||||
bool isRegisteredForStartup();
|
||||
void setRegisteredForStartup(bool isRegistered);
|
||||
|
||||
} // namespace AB_NAMESPACE
|
||||
|
||||
#endif
|
164
lib/appbase/widgets/BaseWidget.cpp
Normal file
164
lib/appbase/widgets/BaseWidget.cpp
Normal file
|
@ -0,0 +1,164 @@
|
|||
#include "widgets/BaseWidget.hpp"
|
||||
|
||||
#include "BaseSettings.hpp"
|
||||
#include "BaseTheme.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "widgets/BaseWindow.hpp"
|
||||
|
||||
#include <QChildEvent>
|
||||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
#include <QLayout>
|
||||
#include <QtGlobal>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
BaseWidget::BaseWidget(QWidget *parent, Qt::WindowFlags f)
|
||||
: QWidget(parent, f)
|
||||
{
|
||||
// REMOVED
|
||||
this->theme = getTheme();
|
||||
|
||||
this->signalHolder_.managedConnect(this->theme->updated, [this]() {
|
||||
this->themeChangedEvent();
|
||||
|
||||
this->update();
|
||||
});
|
||||
}
|
||||
|
||||
float BaseWidget::scale() const
|
||||
{
|
||||
if (this->overrideScale_)
|
||||
{
|
||||
return this->overrideScale_.get();
|
||||
}
|
||||
else if (auto baseWidget = dynamic_cast<BaseWidget *>(this->window()))
|
||||
{
|
||||
return baseWidget->scale_;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseWidget::setScale(float value)
|
||||
{
|
||||
// update scale value
|
||||
this->scale_ = value;
|
||||
|
||||
this->scaleChangedEvent(this->scale());
|
||||
this->scaleChanged.invoke(this->scale());
|
||||
|
||||
this->setScaleIndependantSize(this->scaleIndependantSize());
|
||||
}
|
||||
|
||||
void BaseWidget::setOverrideScale(boost::optional<float> value)
|
||||
{
|
||||
this->overrideScale_ = value;
|
||||
this->setScale(this->scale());
|
||||
}
|
||||
|
||||
boost::optional<float> BaseWidget::overrideScale() const
|
||||
{
|
||||
return this->overrideScale_;
|
||||
}
|
||||
|
||||
QSize BaseWidget::scaleIndependantSize() const
|
||||
{
|
||||
return this->scaleIndependantSize_;
|
||||
}
|
||||
|
||||
int BaseWidget::scaleIndependantWidth() const
|
||||
{
|
||||
return this->scaleIndependantSize_.width();
|
||||
}
|
||||
|
||||
int BaseWidget::scaleIndependantHeight() const
|
||||
{
|
||||
return this->scaleIndependantSize_.height();
|
||||
}
|
||||
|
||||
void BaseWidget::setScaleIndependantSize(int width, int height)
|
||||
{
|
||||
this->setScaleIndependantSize(QSize(width, height));
|
||||
}
|
||||
|
||||
void BaseWidget::setScaleIndependantSize(QSize size)
|
||||
{
|
||||
this->scaleIndependantSize_ = size;
|
||||
|
||||
if (size.width() > 0)
|
||||
{
|
||||
this->setFixedWidth(int(size.width() * this->scale()));
|
||||
}
|
||||
if (size.height() > 0)
|
||||
{
|
||||
this->setFixedHeight(int(size.height() * this->scale()));
|
||||
}
|
||||
}
|
||||
|
||||
void BaseWidget::setScaleIndependantWidth(int value)
|
||||
{
|
||||
this->setScaleIndependantSize(
|
||||
QSize(value, this->scaleIndependantSize_.height()));
|
||||
}
|
||||
|
||||
void BaseWidget::setScaleIndependantHeight(int value)
|
||||
{
|
||||
this->setScaleIndependantSize(
|
||||
QSize(this->scaleIndependantSize_.width(), value));
|
||||
}
|
||||
|
||||
float BaseWidget::qtFontScale() const
|
||||
{
|
||||
if (auto window = dynamic_cast<BaseWindow *>(this->window()))
|
||||
{
|
||||
return this->scale() / window->nativeScale_;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->scale();
|
||||
}
|
||||
}
|
||||
|
||||
void BaseWidget::childEvent(QChildEvent *event)
|
||||
{
|
||||
if (event->added())
|
||||
{
|
||||
// add element if it's a basewidget
|
||||
if (auto widget = dynamic_cast<BaseWidget *>(event->child()))
|
||||
{
|
||||
this->widgets_.push_back(widget);
|
||||
}
|
||||
}
|
||||
else if (event->removed())
|
||||
{
|
||||
// find element to be removed
|
||||
auto it = std::find_if(this->widgets_.begin(), this->widgets_.end(),
|
||||
[&](auto &&x) { return x == event->child(); });
|
||||
|
||||
// remove if found
|
||||
if (it != this->widgets_.end())
|
||||
{
|
||||
this->widgets_.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BaseWidget::showEvent(QShowEvent *)
|
||||
{
|
||||
this->setScale(this->scale());
|
||||
this->themeChangedEvent();
|
||||
}
|
||||
|
||||
void BaseWidget::scaleChangedEvent(float newDpi)
|
||||
{
|
||||
}
|
||||
|
||||
void BaseWidget::themeChangedEvent()
|
||||
{
|
||||
// Do any color scheme updates here
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
59
lib/appbase/widgets/BaseWidget.hpp
Normal file
59
lib/appbase/widgets/BaseWidget.hpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <boost/optional.hpp>
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
#include <pajlada/signals/signalholder.hpp>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
class Theme;
|
||||
class BaseWindow;
|
||||
|
||||
class BaseWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BaseWidget(QWidget *parent, Qt::WindowFlags f = Qt::WindowFlags());
|
||||
|
||||
virtual float scale() const;
|
||||
pajlada::Signals::Signal<float> scaleChanged;
|
||||
|
||||
boost::optional<float> overrideScale() const;
|
||||
void setOverrideScale(boost::optional<float>);
|
||||
|
||||
QSize scaleIndependantSize() const;
|
||||
int scaleIndependantWidth() const;
|
||||
int scaleIndependantHeight() const;
|
||||
void setScaleIndependantSize(int width, int height);
|
||||
void setScaleIndependantSize(QSize);
|
||||
void setScaleIndependantWidth(int value);
|
||||
void setScaleIndependantHeight(int value);
|
||||
|
||||
float qtFontScale() const;
|
||||
|
||||
protected:
|
||||
virtual void childEvent(QChildEvent *) override;
|
||||
virtual void showEvent(QShowEvent *) override;
|
||||
|
||||
virtual void scaleChangedEvent(float newScale);
|
||||
virtual void themeChangedEvent();
|
||||
|
||||
void setScale(float value);
|
||||
|
||||
Theme *theme;
|
||||
|
||||
private:
|
||||
float scale_{1.f};
|
||||
boost::optional<float> overrideScale_;
|
||||
QSize scaleIndependantSize_;
|
||||
|
||||
std::vector<BaseWidget *> widgets_;
|
||||
|
||||
pajlada::Signals::SignalHolder signalHolder_;
|
||||
|
||||
friend class BaseWindow;
|
||||
};
|
||||
|
||||
} // namespace AB_NAMESPACE
|
917
lib/appbase/widgets/BaseWindow.cpp
Normal file
917
lib/appbase/widgets/BaseWindow.cpp
Normal file
|
@ -0,0 +1,917 @@
|
|||
#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 <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QDesktopWidget>
|
||||
#include <QFont>
|
||||
#include <QIcon>
|
||||
#include <functional>
|
||||
|
||||
#ifdef CHATTERINO
|
||||
# include "Application.hpp"
|
||||
# include "singletons/WindowManager.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef USEWINSDK
|
||||
# include <ObjIdl.h>
|
||||
# include <VersionHelpers.h>
|
||||
# include <Windows.h>
|
||||
# include <dwmapi.h>
|
||||
# include <gdiplus.h>
|
||||
# include <windowsx.h>
|
||||
|
||||
//#include <ShellScalingApi.h>
|
||||
# pragma comment(lib, "Dwmapi.lib")
|
||||
|
||||
# include <QHBoxLayout>
|
||||
# include <QVBoxLayout>
|
||||
|
||||
# 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->connections_);
|
||||
|
||||
this->updateScale();
|
||||
|
||||
createWindowShortcut(this, "CTRL+0",
|
||||
[] { getSettings()->uiScale.setValue(1); });
|
||||
|
||||
// QTimer::this->scaleChangedEvent(this->getScale());
|
||||
|
||||
this->resize(300, 300);
|
||||
}
|
||||
|
||||
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(0, 1, 0, 0);
|
||||
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);
|
||||
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()
|
||||
{
|
||||
#ifdef USEWINSDK
|
||||
static bool isWin8 = IsWindows8OrGreater();
|
||||
|
||||
return isWin8 && this->enableCustomFrame_;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void BaseWindow::themeChangedEvent()
|
||||
{
|
||||
if (this->hasCustomWindowFrame())
|
||||
{
|
||||
QPalette palette;
|
||||
palette.setColor(QPalette::Background, QColor(0, 0, 0, 0));
|
||||
palette.setColor(QPalette::Foreground, this->theme->window.text);
|
||||
this->setPalette(palette);
|
||||
|
||||
if (this->ui_.titleLabel)
|
||||
{
|
||||
QPalette palette_title;
|
||||
palette_title.setColor(
|
||||
QPalette::Foreground,
|
||||
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<bool(QWidget *)> 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<void()> 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<void()> 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);
|
||||
}
|
||||
#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<MSG **>(message);
|
||||
# else
|
||||
MSG *msg = reinterpret_cast<MSG *>(message);
|
||||
# endif
|
||||
|
||||
bool returnValue = false;
|
||||
|
||||
switch (msg->message)
|
||||
{
|
||||
case WM_DPICHANGED:
|
||||
returnValue = 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_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<BaseWidget *>())
|
||||
{
|
||||
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())
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
QColor bg = this->overrideBackgroundColor_.value_or(
|
||||
this->theme->window.background);
|
||||
|
||||
painter.fillRect(QRect(0, 1, this->width() - 0, this->height() - 0),
|
||||
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<RECT *>(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() && this->hasCustomWindowFrame())
|
||||
{
|
||||
this->shown_ = true;
|
||||
|
||||
const MARGINS shadow = {8, 8, 8, 8};
|
||||
DwmExtendFrameIntoClientArea(HWND(this->winId()), &shadow);
|
||||
}
|
||||
|
||||
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<NCCALCSIZE_PARAMS *>(msg->lParam));
|
||||
ncp->lppos->flags |= SWP_NOREDRAW;
|
||||
RECT *clientRect = &ncp->rgrc[0];
|
||||
|
||||
clientRect->left += 1;
|
||||
clientRect->top += 0;
|
||||
clientRect->right -= 1;
|
||||
clientRect->bottom -= 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(this->scale() * 8);
|
||||
|
||||
this->ui_.windowLayout->setContentsMargins(offset, offset,
|
||||
offset, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->ui_.windowLayout->setContentsMargins(0, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
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<bool(QWidget *)> 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
|
123
lib/appbase/widgets/BaseWindow.hpp
Normal file
123
lib/appbase/widgets/BaseWindow.hpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/BaseWidget.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <pajlada/signals/signalholder.hpp>
|
||||
|
||||
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);
|
||||
|
||||
QWidget *getLayoutContainer();
|
||||
bool hasCustomWindowFrame();
|
||||
TitleBarButton *addTitleBarButton(const TitleBarButtonStyle &style,
|
||||
std::function<void()> onClicked);
|
||||
EffectLabel *addTitleBarLabel(std::function<void()> 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;
|
||||
|
||||
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<QColor> 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 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<Button *> buttons;
|
||||
} ui_;
|
||||
|
||||
pajlada::Signals::SignalHolder connections_;
|
||||
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
|
||||
|
||||
friend class BaseWidget;
|
||||
};
|
||||
|
||||
} // namespace AB_NAMESPACE
|
130
lib/appbase/widgets/Label.cpp
Normal file
130
lib/appbase/widgets/Label.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
#include "Label.hpp"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
Label::Label(QString text, FontStyle style)
|
||||
: Label(nullptr, text, style)
|
||||
{
|
||||
}
|
||||
|
||||
Label::Label(BaseWidget *parent, QString text, FontStyle style)
|
||||
: BaseWidget(parent)
|
||||
, text_(text)
|
||||
, fontStyle_(style)
|
||||
{
|
||||
this->connections_.managedConnect(getFonts()->fontChanged,
|
||||
[this] { this->updateSize(); });
|
||||
}
|
||||
|
||||
const QString &Label::getText() const
|
||||
{
|
||||
return this->text_;
|
||||
}
|
||||
|
||||
void Label::setText(const QString &text)
|
||||
{
|
||||
if (this->text_ != text)
|
||||
{
|
||||
this->text_ = text;
|
||||
this->updateSize();
|
||||
this->update();
|
||||
}
|
||||
}
|
||||
|
||||
FontStyle Label::getFontStyle() const
|
||||
{
|
||||
return this->fontStyle_;
|
||||
}
|
||||
|
||||
bool Label::getCentered() const
|
||||
{
|
||||
return this->centered_;
|
||||
}
|
||||
|
||||
void Label::setCentered(bool centered)
|
||||
{
|
||||
this->centered_ = centered;
|
||||
this->updateSize();
|
||||
}
|
||||
|
||||
bool Label::getHasOffset() const
|
||||
{
|
||||
return this->hasOffset_;
|
||||
}
|
||||
|
||||
void Label::setHasOffset(bool hasOffset)
|
||||
{
|
||||
this->hasOffset_ = hasOffset;
|
||||
this->updateSize();
|
||||
}
|
||||
void Label::setFontStyle(FontStyle style)
|
||||
{
|
||||
this->fontStyle_ = style;
|
||||
this->updateSize();
|
||||
}
|
||||
|
||||
void Label::scaleChangedEvent(float scale)
|
||||
{
|
||||
this->updateSize();
|
||||
}
|
||||
|
||||
QSize Label::sizeHint() const
|
||||
{
|
||||
return this->preferedSize_;
|
||||
}
|
||||
|
||||
QSize Label::minimumSizeHint() const
|
||||
{
|
||||
return this->preferedSize_;
|
||||
}
|
||||
|
||||
void Label::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
QFontMetrics metrics = getFonts()->getFontMetrics(
|
||||
this->getFontStyle(),
|
||||
this->scale() * 96.f / this->logicalDpiX() * this->devicePixelRatioF());
|
||||
painter.setFont(getFonts()->getFont(
|
||||
this->getFontStyle(), this->scale() * 96.f / this->logicalDpiX() *
|
||||
this->devicePixelRatioF()));
|
||||
|
||||
int offset = this->getOffset();
|
||||
|
||||
// draw text
|
||||
QRect textRect(offset, 0, this->width() - offset - offset, this->height());
|
||||
|
||||
int width = metrics.width(this->text_);
|
||||
Qt::Alignment alignment = !this->centered_ || width > textRect.width()
|
||||
? Qt::AlignLeft | Qt::AlignVCenter
|
||||
: Qt::AlignCenter;
|
||||
|
||||
QTextOption option(alignment);
|
||||
option.setWrapMode(QTextOption::NoWrap);
|
||||
painter.drawText(textRect, this->text_, option);
|
||||
|
||||
#if 0
|
||||
painter.setPen(QColor(255, 0, 0));
|
||||
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Label::updateSize()
|
||||
{
|
||||
QFontMetrics metrics =
|
||||
getFonts()->getFontMetrics(this->fontStyle_, this->scale());
|
||||
|
||||
int width = metrics.width(this->text_) + (2 * this->getOffset());
|
||||
int height = metrics.height();
|
||||
this->preferedSize_ = QSize(width, height);
|
||||
|
||||
this->updateGeometry();
|
||||
}
|
||||
|
||||
int Label::getOffset()
|
||||
{
|
||||
return this->hasOffset_ ? int(8 * this->scale()) : 0;
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
50
lib/appbase/widgets/Label.hpp
Normal file
50
lib/appbase/widgets/Label.hpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include "singletons/Fonts.hpp"
|
||||
#include "widgets/BaseWidget.hpp"
|
||||
|
||||
#include <pajlada/signals/signalholder.hpp>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
class Label : public BaseWidget
|
||||
{
|
||||
public:
|
||||
explicit Label(QString text = QString(),
|
||||
FontStyle style = FontStyle::UiMedium);
|
||||
explicit Label(BaseWidget *parent, QString text = QString(),
|
||||
FontStyle style = FontStyle::UiMedium);
|
||||
|
||||
const QString &getText() const;
|
||||
void setText(const QString &text);
|
||||
|
||||
FontStyle getFontStyle() const;
|
||||
void setFontStyle(FontStyle style);
|
||||
|
||||
bool getCentered() const;
|
||||
void setCentered(bool centered);
|
||||
|
||||
bool getHasOffset() const;
|
||||
void setHasOffset(bool hasOffset);
|
||||
|
||||
protected:
|
||||
virtual void scaleChangedEvent(float scale_) override;
|
||||
virtual void paintEvent(QPaintEvent *) override;
|
||||
|
||||
virtual QSize sizeHint() const override;
|
||||
virtual QSize minimumSizeHint() const override;
|
||||
|
||||
private:
|
||||
void updateSize();
|
||||
int getOffset();
|
||||
|
||||
QString text_;
|
||||
FontStyle fontStyle_;
|
||||
QSize preferedSize_;
|
||||
bool centered_ = false;
|
||||
bool hasOffset_ = true;
|
||||
|
||||
pajlada::Signals::SignalHolder connections_;
|
||||
};
|
||||
|
||||
} // namespace AB_NAMESPACE
|
112
lib/appbase/widgets/TooltipWidget.cpp
Normal file
112
lib/appbase/widgets/TooltipWidget.cpp
Normal file
|
@ -0,0 +1,112 @@
|
|||
#include "TooltipWidget.hpp"
|
||||
|
||||
#include "BaseTheme.hpp"
|
||||
#include "singletons/Fonts.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDesktopWidget>
|
||||
#include <QStyle>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#ifdef USEWINSDK
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
TooltipWidget *TooltipWidget::getInstance()
|
||||
{
|
||||
static TooltipWidget *tooltipWidget = new TooltipWidget();
|
||||
return tooltipWidget;
|
||||
}
|
||||
|
||||
TooltipWidget::TooltipWidget(BaseWidget *parent)
|
||||
: BaseWindow(parent, BaseWindow::TopMost)
|
||||
, displayImage_(new QLabel())
|
||||
, displayText_(new QLabel())
|
||||
{
|
||||
this->setStyleSheet("color: #fff; background: #000");
|
||||
this->setWindowOpacity(0.8);
|
||||
this->updateFont();
|
||||
this->setStayInScreenRect(true);
|
||||
|
||||
this->setAttribute(Qt::WA_ShowWithoutActivating);
|
||||
this->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint |
|
||||
Qt::X11BypassWindowManagerHint |
|
||||
Qt::BypassWindowManagerHint);
|
||||
|
||||
displayImage_->hide();
|
||||
displayImage_->setAlignment(Qt::AlignHCenter);
|
||||
displayText_->setAlignment(Qt::AlignHCenter);
|
||||
displayText_->setText("tooltip text");
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->setContentsMargins(10, 5, 10, 5);
|
||||
layout->addWidget(displayImage_);
|
||||
layout->addWidget(displayText_);
|
||||
this->setLayout(layout);
|
||||
|
||||
this->fontChangedConnection_ =
|
||||
getFonts()->fontChanged.connect([this] { this->updateFont(); });
|
||||
}
|
||||
|
||||
TooltipWidget::~TooltipWidget()
|
||||
{
|
||||
this->fontChangedConnection_.disconnect();
|
||||
}
|
||||
|
||||
#ifdef USEWINSDK
|
||||
void TooltipWidget::raise()
|
||||
{
|
||||
::SetWindowPos(HWND(this->winId()), HWND_TOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||
}
|
||||
#endif
|
||||
|
||||
void TooltipWidget::themeChangedEvent()
|
||||
{
|
||||
this->setStyleSheet("color: #fff; background: #000");
|
||||
}
|
||||
|
||||
void TooltipWidget::scaleChangedEvent(float)
|
||||
{
|
||||
this->updateFont();
|
||||
}
|
||||
|
||||
void TooltipWidget::updateFont()
|
||||
{
|
||||
this->setFont(
|
||||
getFonts()->getFont(FontStyle::ChatMediumSmall, this->scale()));
|
||||
}
|
||||
|
||||
void TooltipWidget::setText(QString text)
|
||||
{
|
||||
this->displayText_->setText(text);
|
||||
}
|
||||
|
||||
void TooltipWidget::setWordWrap(bool wrap)
|
||||
{
|
||||
this->displayText_->setWordWrap(wrap);
|
||||
}
|
||||
|
||||
void TooltipWidget::clearImage()
|
||||
{
|
||||
this->displayImage_->hide();
|
||||
}
|
||||
|
||||
void TooltipWidget::setImage(QPixmap image)
|
||||
{
|
||||
this->displayImage_->show();
|
||||
this->displayImage_->setPixmap(image);
|
||||
}
|
||||
|
||||
void TooltipWidget::changeEvent(QEvent *)
|
||||
{
|
||||
// clear parents event
|
||||
}
|
||||
|
||||
void TooltipWidget::leaveEvent(QEvent *)
|
||||
{
|
||||
// clear parents event
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
44
lib/appbase/widgets/TooltipWidget.hpp
Normal file
44
lib/appbase/widgets/TooltipWidget.hpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/BaseWindow.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QWidget>
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
class TooltipWidget : public BaseWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static TooltipWidget *getInstance();
|
||||
|
||||
TooltipWidget(BaseWidget *parent = nullptr);
|
||||
~TooltipWidget() override;
|
||||
|
||||
void setText(QString text);
|
||||
void setWordWrap(bool wrap);
|
||||
void clearImage();
|
||||
void setImage(QPixmap image);
|
||||
|
||||
#ifdef USEWINSDK
|
||||
void raise();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent *) override;
|
||||
void leaveEvent(QEvent *) override;
|
||||
void themeChangedEvent() override;
|
||||
void scaleChangedEvent(float) override;
|
||||
|
||||
private:
|
||||
void updateFont();
|
||||
|
||||
QLabel *displayImage_;
|
||||
QLabel *displayText_;
|
||||
pajlada::Signals::Connection fontChangedConnection_;
|
||||
};
|
||||
|
||||
} // namespace AB_NAMESPACE
|
338
lib/appbase/widgets/helper/Button.cpp
Normal file
338
lib/appbase/widgets/helper/Button.cpp
Normal file
|
@ -0,0 +1,338 @@
|
|||
#include "Button.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QDesktopWidget>
|
||||
#include <QPainter>
|
||||
|
||||
#include "BaseTheme.hpp"
|
||||
#include "util/FunctionEventFilter.hpp"
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
Button::Button(BaseWidget *parent)
|
||||
: BaseWidget(parent)
|
||||
{
|
||||
connect(&effectTimer_, &QTimer::timeout, this,
|
||||
&Button::onMouseEffectTimeout);
|
||||
|
||||
this->effectTimer_.setInterval(20);
|
||||
this->effectTimer_.start();
|
||||
|
||||
this->setMouseTracking(true);
|
||||
}
|
||||
|
||||
void Button::setMouseEffectColor(boost::optional<QColor> color)
|
||||
{
|
||||
this->mouseEffectColor_ = color;
|
||||
}
|
||||
|
||||
void Button::setPixmap(const QPixmap &_pixmap)
|
||||
{
|
||||
this->pixmap_ = _pixmap;
|
||||
this->update();
|
||||
}
|
||||
|
||||
const QPixmap &Button::getPixmap() const
|
||||
{
|
||||
return this->pixmap_;
|
||||
}
|
||||
|
||||
void Button::setDim(bool value)
|
||||
{
|
||||
this->dimPixmap_ = value;
|
||||
|
||||
this->update();
|
||||
}
|
||||
|
||||
bool Button::getDim() const
|
||||
{
|
||||
return this->dimPixmap_;
|
||||
}
|
||||
|
||||
void Button::setEnable(bool value)
|
||||
{
|
||||
this->enabled_ = value;
|
||||
|
||||
this->update();
|
||||
}
|
||||
|
||||
bool Button::getEnable() const
|
||||
{
|
||||
return this->enabled_;
|
||||
}
|
||||
|
||||
void Button::setEnableMargin(bool value)
|
||||
{
|
||||
this->enableMargin_ = value;
|
||||
|
||||
this->update();
|
||||
}
|
||||
|
||||
bool Button::getEnableMargin() const
|
||||
{
|
||||
return this->enableMargin_;
|
||||
}
|
||||
|
||||
qreal Button::getCurrentDimAmount() const
|
||||
{
|
||||
return this->dimPixmap_ && !this->mouseOver_ ? 0.7 : 1;
|
||||
}
|
||||
|
||||
void Button::setBorderColor(const QColor &color)
|
||||
{
|
||||
this->borderColor_ = color;
|
||||
|
||||
this->update();
|
||||
}
|
||||
|
||||
const QColor &Button::getBorderColor() const
|
||||
{
|
||||
return this->borderColor_;
|
||||
}
|
||||
|
||||
void Button::setMenu(std::unique_ptr<QMenu> menu)
|
||||
{
|
||||
this->menu_ = std::move(menu);
|
||||
|
||||
this->menu_->installEventFilter(
|
||||
new FunctionEventFilter(this, [this](QObject *, QEvent *event) {
|
||||
if (event->type() == QEvent::Hide)
|
||||
{
|
||||
QTimer::singleShot(20, this,
|
||||
[this] { this->menuVisible_ = false; });
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
|
||||
void Button::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
|
||||
if (!this->pixmap_.isNull())
|
||||
{
|
||||
if (!this->mouseOver_ && this->dimPixmap_ && this->enabled_)
|
||||
{
|
||||
painter.setOpacity(this->getCurrentDimAmount());
|
||||
}
|
||||
|
||||
QRect rect = this->rect();
|
||||
int s = this->enableMargin_ ? int(6 * this->scale()) : 0;
|
||||
|
||||
rect.moveLeft(s);
|
||||
rect.setRight(rect.right() - s - s);
|
||||
rect.moveTop(s);
|
||||
rect.setBottom(rect.bottom() - s - s);
|
||||
|
||||
painter.drawPixmap(rect, this->pixmap_);
|
||||
|
||||
painter.setOpacity(1);
|
||||
}
|
||||
|
||||
this->fancyPaint(painter);
|
||||
|
||||
if (this->borderColor_.isValid())
|
||||
{
|
||||
painter.setRenderHint(QPainter::Antialiasing, false);
|
||||
painter.setPen(this->borderColor_);
|
||||
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::fancyPaint(QPainter &painter)
|
||||
{
|
||||
if (!this->enabled_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
painter.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
QColor c;
|
||||
|
||||
if (this->mouseEffectColor_)
|
||||
{
|
||||
c = this->mouseEffectColor_.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
c = this->theme->isLightTheme() ? QColor(0, 0, 0)
|
||||
: QColor(255, 255, 255);
|
||||
}
|
||||
|
||||
if (this->hoverMultiplier_ > 0)
|
||||
{
|
||||
QRadialGradient gradient(QPointF(mousePos_), this->width() / 2);
|
||||
|
||||
gradient.setColorAt(0, QColor(c.red(), c.green(), c.blue(),
|
||||
int(50 * this->hoverMultiplier_)));
|
||||
gradient.setColorAt(1, QColor(c.red(), c.green(), c.blue(),
|
||||
int(40 * this->hoverMultiplier_)));
|
||||
|
||||
painter.fillRect(this->rect(), gradient);
|
||||
}
|
||||
|
||||
for (auto effect : this->clickEffects_)
|
||||
{
|
||||
QRadialGradient gradient(effect.position.x(), effect.position.y(),
|
||||
effect.progress * qreal(width()) * 2,
|
||||
effect.position.x(), effect.position.y());
|
||||
|
||||
gradient.setColorAt(0, QColor(c.red(), c.green(), c.blue(),
|
||||
int((1 - effect.progress) * 95)));
|
||||
gradient.setColorAt(0.9999, QColor(c.red(), c.green(), c.blue(),
|
||||
int((1 - effect.progress) * 95)));
|
||||
gradient.setColorAt(1, QColor(c.red(), c.green(), c.blue(), int(0)));
|
||||
|
||||
painter.fillRect(this->rect(), gradient);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::enterEvent(QEvent *)
|
||||
{
|
||||
this->mouseOver_ = true;
|
||||
}
|
||||
|
||||
void Button::leaveEvent(QEvent *)
|
||||
{
|
||||
this->mouseOver_ = false;
|
||||
}
|
||||
|
||||
void Button::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!this->enabled_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() != Qt::LeftButton)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->clickEffects_.push_back(ClickEffect(event->pos()));
|
||||
|
||||
this->mouseDown_ = true;
|
||||
|
||||
emit this->leftMousePress();
|
||||
|
||||
if (this->menu_ && !this->menuVisible_)
|
||||
{
|
||||
QTimer::singleShot(80, this, [this] { this->showMenu(); });
|
||||
this->mouseDown_ = false;
|
||||
this->mouseOver_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Button::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!this->enabled_)
|
||||
return;
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
this->mouseDown_ = false;
|
||||
|
||||
if (this->rect().contains(event->pos()))
|
||||
emit leftClicked();
|
||||
}
|
||||
|
||||
emit clicked(event->button());
|
||||
}
|
||||
|
||||
void Button::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (this->enabled_)
|
||||
{
|
||||
this->mousePos_ = event->pos();
|
||||
|
||||
this->update();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::onMouseEffectTimeout()
|
||||
{
|
||||
bool performUpdate = false;
|
||||
|
||||
if (selected_)
|
||||
{
|
||||
if (this->hoverMultiplier_ != 0)
|
||||
{
|
||||
this->hoverMultiplier_ =
|
||||
std::max(0.0, this->hoverMultiplier_ - 0.1);
|
||||
performUpdate = true;
|
||||
}
|
||||
}
|
||||
else if (mouseOver_)
|
||||
{
|
||||
if (this->hoverMultiplier_ != 1)
|
||||
{
|
||||
this->hoverMultiplier_ =
|
||||
std::min(1.0, this->hoverMultiplier_ + 0.5);
|
||||
performUpdate = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this->hoverMultiplier_ != 0)
|
||||
{
|
||||
this->hoverMultiplier_ =
|
||||
std::max(0.0, this->hoverMultiplier_ - 0.3);
|
||||
performUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->clickEffects_.size() != 0)
|
||||
{
|
||||
performUpdate = true;
|
||||
|
||||
for (auto it = this->clickEffects_.begin();
|
||||
it != this->clickEffects_.end();)
|
||||
{
|
||||
it->progress += mouseDown_ ? 0.02 : 0.07;
|
||||
|
||||
if (it->progress >= 1.0)
|
||||
{
|
||||
it = this->clickEffects_.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (performUpdate)
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void Button::showMenu()
|
||||
{
|
||||
if (!this->menu_)
|
||||
return;
|
||||
|
||||
auto point = [this] {
|
||||
auto bounds = QApplication::desktop()->availableGeometry(this);
|
||||
|
||||
auto point = this->mapToGlobal(
|
||||
QPoint(this->width() - this->menu_->width(), this->height()));
|
||||
|
||||
if (point.y() + this->menu_->height() > bounds.bottom())
|
||||
{
|
||||
point.setY(point.y() - this->menu_->height() - this->height());
|
||||
}
|
||||
|
||||
return point;
|
||||
};
|
||||
|
||||
this->menu_->popup(point());
|
||||
this->menu_->move(point());
|
||||
this->menuVisible_ = true;
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
89
lib/appbase/widgets/helper/Button.hpp
Normal file
89
lib/appbase/widgets/helper/Button.hpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "widgets/BaseWidget.hpp"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPoint>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
class Button : public BaseWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
struct ClickEffect {
|
||||
double progress = 0.0;
|
||||
QPoint position;
|
||||
|
||||
ClickEffect(QPoint _position)
|
||||
: position(_position)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
Button(BaseWidget *parent = nullptr);
|
||||
|
||||
void setMouseEffectColor(boost::optional<QColor> color);
|
||||
void setPixmap(const QPixmap &pixmap_);
|
||||
const QPixmap &getPixmap() const;
|
||||
|
||||
void setDim(bool value);
|
||||
bool getDim() const;
|
||||
qreal getCurrentDimAmount() const;
|
||||
|
||||
void setEnable(bool value);
|
||||
bool getEnable() const;
|
||||
|
||||
void setEnableMargin(bool value);
|
||||
bool getEnableMargin() const;
|
||||
|
||||
void setBorderColor(const QColor &color);
|
||||
const QColor &getBorderColor() const;
|
||||
|
||||
void setMenu(std::unique_ptr<QMenu> menu);
|
||||
|
||||
signals:
|
||||
void leftClicked();
|
||||
void clicked(Qt::MouseButton button);
|
||||
void leftMousePress();
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *) override;
|
||||
virtual void enterEvent(QEvent *) override;
|
||||
virtual void leaveEvent(QEvent *) override;
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
void fancyPaint(QPainter &painter);
|
||||
|
||||
bool enabled_{true};
|
||||
bool selected_{false};
|
||||
bool mouseOver_{false};
|
||||
bool mouseDown_{false};
|
||||
bool menuVisible_{false};
|
||||
|
||||
private:
|
||||
void onMouseEffectTimeout();
|
||||
void showMenu();
|
||||
|
||||
QColor borderColor_{};
|
||||
QPixmap pixmap_{};
|
||||
bool dimPixmap_{true};
|
||||
bool enableMargin_{true};
|
||||
QPoint mousePos_{};
|
||||
double hoverMultiplier_{0.0};
|
||||
QTimer effectTimer_{};
|
||||
std::vector<ClickEffect> clickEffects_{};
|
||||
boost::optional<QColor> mouseEffectColor_{};
|
||||
std::unique_ptr<QMenu> menu_{};
|
||||
};
|
||||
|
||||
} // namespace AB_NAMESPACE
|
43
lib/appbase/widgets/helper/EffectLabel.cpp
Normal file
43
lib/appbase/widgets/helper/EffectLabel.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "widgets/helper/EffectLabel.hpp"
|
||||
|
||||
#include <QBrush>
|
||||
#include <QPainter>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
EffectLabel::EffectLabel(BaseWidget *parent, int spacing)
|
||||
: Button(parent)
|
||||
, label_(this)
|
||||
{
|
||||
setLayout(&this->hbox_);
|
||||
|
||||
this->label_.setAlignment(Qt::AlignCenter);
|
||||
|
||||
this->hbox_.setMargin(0);
|
||||
this->hbox_.addSpacing(spacing);
|
||||
this->hbox_.addWidget(&this->label_);
|
||||
this->hbox_.addSpacing(spacing);
|
||||
}
|
||||
|
||||
EffectLabel2::EffectLabel2(BaseWidget *parent, int padding)
|
||||
: Button(parent)
|
||||
, label_(this)
|
||||
{
|
||||
auto *hbox = new QHBoxLayout(this);
|
||||
this->setLayout(hbox);
|
||||
|
||||
// this->label_.setAlignment(Qt::AlignCenter);
|
||||
this->label_.setCentered(true);
|
||||
|
||||
hbox->setMargin(0);
|
||||
// hbox.addSpacing(spacing);
|
||||
hbox->addWidget(&this->label_);
|
||||
// hbox.addSpacing(spacing);
|
||||
}
|
||||
|
||||
Label &EffectLabel2::getLabel()
|
||||
{
|
||||
return this->label_;
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
41
lib/appbase/widgets/helper/EffectLabel.hpp
Normal file
41
lib/appbase/widgets/helper/EffectLabel.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/BaseWidget.hpp"
|
||||
#include "widgets/Label.hpp"
|
||||
#include "widgets/helper/Button.hpp"
|
||||
#include "widgets/helper/SignalLabel.hpp"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPaintEvent>
|
||||
#include <QWidget>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
class EffectLabel : public Button
|
||||
{
|
||||
public:
|
||||
explicit EffectLabel(BaseWidget *parent = nullptr, int spacing = 6);
|
||||
|
||||
SignalLabel &getLabel()
|
||||
{
|
||||
return this->label_;
|
||||
}
|
||||
|
||||
private:
|
||||
QHBoxLayout hbox_;
|
||||
SignalLabel label_;
|
||||
};
|
||||
|
||||
class EffectLabel2 : public Button
|
||||
{
|
||||
public:
|
||||
explicit EffectLabel2(BaseWidget *parent = nullptr, int padding = 6);
|
||||
|
||||
Label &getLabel();
|
||||
|
||||
private:
|
||||
Label label_;
|
||||
};
|
||||
|
||||
} // namespace AB_NAMESPACE
|
41
lib/appbase/widgets/helper/SignalLabel.cpp
Normal file
41
lib/appbase/widgets/helper/SignalLabel.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include "widgets/helper/SignalLabel.hpp"
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
SignalLabel::SignalLabel(QWidget *parent, Qt::WindowFlags f)
|
||||
: QLabel(parent, f)
|
||||
{
|
||||
}
|
||||
|
||||
void SignalLabel::mouseDoubleClickEvent(QMouseEvent *ev)
|
||||
{
|
||||
emit this->mouseDoubleClick(ev);
|
||||
}
|
||||
|
||||
void SignalLabel::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
emit mouseDown();
|
||||
}
|
||||
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void SignalLabel::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
emit mouseUp();
|
||||
}
|
||||
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void SignalLabel::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
emit this->mouseMove(event);
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
32
lib/appbase/widgets/helper/SignalLabel.hpp
Normal file
32
lib/appbase/widgets/helper/SignalLabel.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <QFlags>
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QWidget>
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
class SignalLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SignalLabel(QWidget *parent = nullptr, Qt::WindowFlags f = 0);
|
||||
virtual ~SignalLabel() override = default;
|
||||
|
||||
signals:
|
||||
void mouseDoubleClick(QMouseEvent *ev);
|
||||
|
||||
void mouseDown();
|
||||
void mouseUp();
|
||||
void mouseMove(QMouseEvent *event);
|
||||
|
||||
protected:
|
||||
void mouseDoubleClickEvent(QMouseEvent *ev) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
};
|
||||
|
||||
} // namespace AB_NAMESPACE
|
134
lib/appbase/widgets/helper/TitlebarButton.cpp
Normal file
134
lib/appbase/widgets/helper/TitlebarButton.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
#include "TitlebarButton.hpp"
|
||||
|
||||
#include "BaseTheme.hpp"
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
TitleBarButton::TitleBarButton()
|
||||
: Button(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TitleBarButtonStyle TitleBarButton::getButtonStyle() const
|
||||
{
|
||||
return this->style_;
|
||||
}
|
||||
|
||||
void TitleBarButton::setButtonStyle(TitleBarButtonStyle _style)
|
||||
{
|
||||
this->style_ = _style;
|
||||
this->update();
|
||||
}
|
||||
|
||||
void TitleBarButton::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
painter.setOpacity(this->getCurrentDimAmount());
|
||||
|
||||
QColor color = this->theme->window.text;
|
||||
QColor background = this->theme->window.background;
|
||||
|
||||
int xD = this->height() / 3;
|
||||
int centerX = this->width() / 2;
|
||||
|
||||
painter.setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
switch (this->style_)
|
||||
{
|
||||
case TitleBarButtonStyle::Minimize:
|
||||
{
|
||||
painter.fillRect(centerX - xD / 2, xD * 3 / 2, xD, 1, color);
|
||||
break;
|
||||
}
|
||||
case TitleBarButtonStyle::Maximize:
|
||||
{
|
||||
painter.setPen(color);
|
||||
painter.drawRect(centerX - xD / 2, xD, xD - 1, xD - 1);
|
||||
break;
|
||||
}
|
||||
case TitleBarButtonStyle::Unmaximize:
|
||||
{
|
||||
int xD2 = xD * 1 / 5;
|
||||
int xD3 = xD * 4 / 5;
|
||||
|
||||
painter.drawRect(centerX - xD / 2 + xD2, xD, xD3, xD3);
|
||||
painter.fillRect(centerX - xD / 2, xD + xD2, xD3, xD3,
|
||||
this->theme->window.background);
|
||||
painter.drawRect(centerX - xD / 2, xD + xD2, xD3, xD3);
|
||||
break;
|
||||
}
|
||||
case TitleBarButtonStyle::Close:
|
||||
{
|
||||
QRect rect(centerX - xD / 2, xD, xD - 1, xD - 1);
|
||||
painter.setPen(QPen(color, 1));
|
||||
|
||||
painter.drawLine(rect.topLeft(), rect.bottomRight());
|
||||
painter.drawLine(rect.topRight(), rect.bottomLeft());
|
||||
break;
|
||||
}
|
||||
case TitleBarButtonStyle::User:
|
||||
{
|
||||
color = "#999";
|
||||
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
|
||||
auto a = xD / 3;
|
||||
QPainterPath path;
|
||||
|
||||
painter.save();
|
||||
painter.translate(3, 3);
|
||||
|
||||
path.arcMoveTo(a, 4 * a, 6 * a, 6 * a, 0);
|
||||
path.arcTo(a, 4 * a, 6 * a, 6 * a, 0, 180);
|
||||
|
||||
painter.fillPath(path, color);
|
||||
|
||||
painter.setBrush(background);
|
||||
painter.drawEllipse(2 * a, 1 * a, 4 * a, 4 * a);
|
||||
|
||||
painter.setBrush(color);
|
||||
painter.drawEllipse(2.5 * a, 1.5 * a, 3 * a + 1, 3 * a);
|
||||
painter.restore();
|
||||
|
||||
break;
|
||||
}
|
||||
case TitleBarButtonStyle::Settings:
|
||||
{
|
||||
color = "#999";
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
|
||||
painter.save();
|
||||
painter.translate(3, 3);
|
||||
|
||||
auto a = xD / 3;
|
||||
QPainterPath path;
|
||||
|
||||
path.arcMoveTo(a, a, 6 * a, 6 * a, 0 - (360 / 32.0));
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
path.arcTo(a, a, 6 * a, 6 * a, i * (360 / 8.0) - (360 / 32.0),
|
||||
(360 / 32.0));
|
||||
path.arcTo(2 * a, 2 * a, 4 * a, 4 * a,
|
||||
i * (360 / 8.0) + (360 / 32.0), (360 / 32.0));
|
||||
}
|
||||
|
||||
painter.strokePath(path, color);
|
||||
painter.fillPath(path, color);
|
||||
|
||||
painter.setBrush(background);
|
||||
painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a);
|
||||
painter.restore();
|
||||
break;
|
||||
}
|
||||
default:;
|
||||
}
|
||||
|
||||
Button::paintEvent(event);
|
||||
// this->fancyPaint(painter);
|
||||
}
|
||||
|
||||
} // namespace AB_NAMESPACE
|
32
lib/appbase/widgets/helper/TitlebarButton.hpp
Normal file
32
lib/appbase/widgets/helper/TitlebarButton.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/helper/Button.hpp"
|
||||
|
||||
namespace AB_NAMESPACE {
|
||||
|
||||
enum class TitleBarButtonStyle {
|
||||
None = 0,
|
||||
Minimize = 1,
|
||||
Maximize = 2,
|
||||
Unmaximize = 4,
|
||||
Close = 8,
|
||||
User = 16,
|
||||
Settings = 32
|
||||
};
|
||||
|
||||
class TitleBarButton : public Button
|
||||
{
|
||||
public:
|
||||
TitleBarButton();
|
||||
|
||||
TitleBarButtonStyle getButtonStyle() const;
|
||||
void setButtonStyle(TitleBarButtonStyle style_);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
|
||||
private:
|
||||
TitleBarButtonStyle style_;
|
||||
};
|
||||
|
||||
} // namespace AB_NAMESPACE
|
21
lib/boost.pri
Normal file
21
lib/boost.pri
Normal file
|
@ -0,0 +1,21 @@
|
|||
pajlada {
|
||||
BOOST_DIRECTORY = C:\dev\projects\boost_1_66_0\
|
||||
}
|
||||
|
||||
win32 {
|
||||
isEmpty(BOOST_DIRECTORY) {
|
||||
message(Using default boost directory C:\\local\\boost\\)
|
||||
BOOST_DIRECTORY = C:\local\boost\
|
||||
}
|
||||
|
||||
INCLUDEPATH += $$BOOST_DIRECTORY
|
||||
|
||||
isEmpty(BOOST_LIB_SUFFIX) {
|
||||
message(Using default boost lib directory suffix lib)
|
||||
BOOST_LIB_SUFFIX = lib
|
||||
}
|
||||
|
||||
LIBS += -L$$BOOST_DIRECTORY\\$$BOOST_LIB_SUFFIX
|
||||
} else {
|
||||
LIBS += -lboost_system -lboost_filesystem
|
||||
}
|
4
lib/fmt.pri
Normal file
4
lib/fmt.pri
Normal file
|
@ -0,0 +1,4 @@
|
|||
# fmt
|
||||
SOURCES += $$PWD/fmt/fmt/format.cpp
|
||||
|
||||
INCLUDEPATH += $$PWD/fmt/
|
535
lib/fmt/fmt/format.cpp
Normal file
535
lib/fmt/fmt/format.cpp
Normal file
|
@ -0,0 +1,535 @@
|
|||
/*
|
||||
Formatting library for C++
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cstddef> // for std::ptrdiff_t
|
||||
|
||||
#if defined(_WIN32) && defined(__MINGW32__)
|
||||
# include <cstring>
|
||||
#endif
|
||||
|
||||
#if FMT_USE_WINDOWS_H
|
||||
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
|
||||
# include <windows.h>
|
||||
# else
|
||||
# define NOMINMAX
|
||||
# include <windows.h>
|
||||
# undef NOMINMAX
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if FMT_EXCEPTIONS
|
||||
# define FMT_TRY try
|
||||
# define FMT_CATCH(x) catch (x)
|
||||
#else
|
||||
# define FMT_TRY if (true)
|
||||
# define FMT_CATCH(x) if (false)
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4127) // conditional expression is constant
|
||||
# pragma warning(disable: 4702) // unreachable code
|
||||
// Disable deprecation warning for strerror. The latter is not called but
|
||||
// MSVC fails to detect it.
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
// Dummy implementations of strerror_r and strerror_s called if corresponding
|
||||
// system functions are not available.
|
||||
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
|
||||
return fmt::internal::Null<>();
|
||||
}
|
||||
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
|
||||
return fmt::internal::Null<>();
|
||||
}
|
||||
|
||||
namespace fmt {
|
||||
|
||||
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
|
||||
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
|
||||
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
|
||||
|
||||
namespace {
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# define FMT_SNPRINTF snprintf
|
||||
#else // _MSC_VER
|
||||
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
# define FMT_SNPRINTF fmt_snprintf
|
||||
#endif // _MSC_VER
|
||||
|
||||
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
||||
# define FMT_SWPRINTF snwprintf
|
||||
#else
|
||||
# define FMT_SWPRINTF swprintf
|
||||
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
||||
|
||||
const char RESET_COLOR[] = "\x1b[0m";
|
||||
|
||||
typedef void (*FormatFunc)(Writer &, int, StringRef);
|
||||
|
||||
// Portable thread-safe version of strerror.
|
||||
// Sets buffer to point to a string describing the error code.
|
||||
// This can be either a pointer to a string stored in buffer,
|
||||
// or a pointer to some static immutable string.
|
||||
// Returns one of the following values:
|
||||
// 0 - success
|
||||
// ERANGE - buffer is not large enough to store the error message
|
||||
// other - failure
|
||||
// Buffer should be at least of size 1.
|
||||
int safe_strerror(
|
||||
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
|
||||
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
|
||||
|
||||
class StrError {
|
||||
private:
|
||||
int error_code_;
|
||||
char *&buffer_;
|
||||
std::size_t buffer_size_;
|
||||
|
||||
// A noop assignment operator to avoid bogus warnings.
|
||||
void operator=(const StrError &) {}
|
||||
|
||||
// Handle the result of XSI-compliant version of strerror_r.
|
||||
int handle(int result) {
|
||||
// glibc versions before 2.13 return result in errno.
|
||||
return result == -1 ? errno : result;
|
||||
}
|
||||
|
||||
// Handle the result of GNU-specific version of strerror_r.
|
||||
int handle(char *message) {
|
||||
// If the buffer is full then the message is probably truncated.
|
||||
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
|
||||
return ERANGE;
|
||||
buffer_ = message;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle the case when strerror_r is not available.
|
||||
int handle(internal::Null<>) {
|
||||
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
||||
}
|
||||
|
||||
// Fallback to strerror_s when strerror_r is not available.
|
||||
int fallback(int result) {
|
||||
// If the buffer is full then the message is probably truncated.
|
||||
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
|
||||
ERANGE : result;
|
||||
}
|
||||
|
||||
// Fallback to strerror if strerror_r and strerror_s are not available.
|
||||
int fallback(internal::Null<>) {
|
||||
errno = 0;
|
||||
buffer_ = strerror(error_code_);
|
||||
return errno;
|
||||
}
|
||||
|
||||
public:
|
||||
StrError(int err_code, char *&buf, std::size_t buf_size)
|
||||
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
||||
|
||||
int run() {
|
||||
// Suppress a warning about unused strerror_r.
|
||||
strerror_r(0, FMT_NULL, "");
|
||||
return handle(strerror_r(error_code_, buffer_, buffer_size_));
|
||||
}
|
||||
};
|
||||
return StrError(error_code, buffer, buffer_size).run();
|
||||
}
|
||||
|
||||
void format_error_code(Writer &out, int error_code,
|
||||
StringRef message) FMT_NOEXCEPT {
|
||||
// Report error code making sure that the output fits into
|
||||
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
|
||||
// bad_alloc.
|
||||
out.clear();
|
||||
static const char SEP[] = ": ";
|
||||
static const char ERROR_STR[] = "error ";
|
||||
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
|
||||
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
|
||||
typedef internal::IntTraits<int>::MainType MainType;
|
||||
MainType abs_value = static_cast<MainType>(error_code);
|
||||
if (internal::is_negative(error_code)) {
|
||||
abs_value = 0 - abs_value;
|
||||
++error_code_size;
|
||||
}
|
||||
error_code_size += internal::count_digits(abs_value);
|
||||
if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
|
||||
out << message << SEP;
|
||||
out << ERROR_STR << error_code;
|
||||
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void report_error(FormatFunc func, int error_code,
|
||||
StringRef message) FMT_NOEXCEPT {
|
||||
MemoryWriter full_message;
|
||||
func(full_message, error_code, message);
|
||||
// Use Writer::data instead of Writer::c_str to avoid potential memory
|
||||
// allocation.
|
||||
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
|
||||
std::fputc('\n', stderr);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
FMT_FUNC void SystemError::init(
|
||||
int err_code, CStringRef format_str, ArgList args) {
|
||||
error_code_ = err_code;
|
||||
MemoryWriter w;
|
||||
format_system_error(w, err_code, format(format_str, args));
|
||||
std::runtime_error &base = *this;
|
||||
base = std::runtime_error(w.str());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int internal::CharTraits<char>::format_float(
|
||||
char *buffer, std::size_t size, const char *format,
|
||||
unsigned width, int precision, T value) {
|
||||
if (width == 0) {
|
||||
return precision < 0 ?
|
||||
FMT_SNPRINTF(buffer, size, format, value) :
|
||||
FMT_SNPRINTF(buffer, size, format, precision, value);
|
||||
}
|
||||
return precision < 0 ?
|
||||
FMT_SNPRINTF(buffer, size, format, width, value) :
|
||||
FMT_SNPRINTF(buffer, size, format, width, precision, value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int internal::CharTraits<wchar_t>::format_float(
|
||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||
unsigned width, int precision, T value) {
|
||||
if (width == 0) {
|
||||
return precision < 0 ?
|
||||
FMT_SWPRINTF(buffer, size, format, value) :
|
||||
FMT_SWPRINTF(buffer, size, format, precision, value);
|
||||
}
|
||||
return precision < 0 ?
|
||||
FMT_SWPRINTF(buffer, size, format, width, value) :
|
||||
FMT_SWPRINTF(buffer, size, format, width, precision, value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const char internal::BasicData<T>::DIGITS[] =
|
||||
"0001020304050607080910111213141516171819"
|
||||
"2021222324252627282930313233343536373839"
|
||||
"4041424344454647484950515253545556575859"
|
||||
"6061626364656667686970717273747576777879"
|
||||
"8081828384858687888990919293949596979899";
|
||||
|
||||
#define FMT_POWERS_OF_10(factor) \
|
||||
factor * 10, \
|
||||
factor * 100, \
|
||||
factor * 1000, \
|
||||
factor * 10000, \
|
||||
factor * 100000, \
|
||||
factor * 1000000, \
|
||||
factor * 10000000, \
|
||||
factor * 100000000, \
|
||||
factor * 1000000000
|
||||
|
||||
template <typename T>
|
||||
const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
|
||||
0, FMT_POWERS_OF_10(1)
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
|
||||
0,
|
||||
FMT_POWERS_OF_10(1),
|
||||
FMT_POWERS_OF_10(ULongLong(1000000000)),
|
||||
// Multiply several constants instead of using a single long long constant
|
||||
// to avoid warnings about C++98 not supporting long long.
|
||||
ULongLong(1000000000) * ULongLong(1000000000) * 10
|
||||
};
|
||||
|
||||
FMT_FUNC void internal::report_unknown_type(char code, const char *type) {
|
||||
(void)type;
|
||||
if (std::isprint(static_cast<unsigned char>(code))) {
|
||||
FMT_THROW(FormatError(
|
||||
format("unknown format code '{}' for {}", code, type)));
|
||||
}
|
||||
FMT_THROW(FormatError(
|
||||
format("unknown format code '\\x{:02x}' for {}",
|
||||
static_cast<unsigned>(code), type)));
|
||||
}
|
||||
|
||||
#if FMT_USE_WINDOWS_H
|
||||
|
||||
FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
|
||||
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
|
||||
if (s.size() > INT_MAX)
|
||||
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
|
||||
int s_size = static_cast<int>(s.size());
|
||||
int length = MultiByteToWideChar(
|
||||
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
|
||||
if (length == 0)
|
||||
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
|
||||
buffer_.resize(length + 1);
|
||||
length = MultiByteToWideChar(
|
||||
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
|
||||
if (length == 0)
|
||||
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
|
||||
buffer_[length] = 0;
|
||||
}
|
||||
|
||||
FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
|
||||
if (int error_code = convert(s)) {
|
||||
FMT_THROW(WindowsError(error_code,
|
||||
"cannot convert string from UTF-16 to UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
|
||||
if (s.size() > INT_MAX)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
int s_size = static_cast<int>(s.size());
|
||||
int length = WideCharToMultiByte(
|
||||
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
|
||||
if (length == 0)
|
||||
return GetLastError();
|
||||
buffer_.resize(length + 1);
|
||||
length = WideCharToMultiByte(
|
||||
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
|
||||
if (length == 0)
|
||||
return GetLastError();
|
||||
buffer_[length] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
FMT_FUNC void WindowsError::init(
|
||||
int err_code, CStringRef format_str, ArgList args) {
|
||||
error_code_ = err_code;
|
||||
MemoryWriter w;
|
||||
internal::format_windows_error(w, err_code, format(format_str, args));
|
||||
std::runtime_error &base = *this;
|
||||
base = std::runtime_error(w.str());
|
||||
}
|
||||
|
||||
FMT_FUNC void internal::format_windows_error(
|
||||
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
|
||||
FMT_TRY {
|
||||
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
|
||||
buffer.resize(INLINE_BUFFER_SIZE);
|
||||
for (;;) {
|
||||
wchar_t *system_message = &buffer[0];
|
||||
int result = FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
|
||||
if (result != 0) {
|
||||
UTF16ToUTF8 utf8_message;
|
||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
||||
out << message << ": " << utf8_message;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
break; // Can't get error message, report error code instead.
|
||||
buffer.resize(buffer.size() * 2);
|
||||
}
|
||||
} FMT_CATCH(...) {}
|
||||
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
|
||||
}
|
||||
|
||||
#endif // FMT_USE_WINDOWS_H
|
||||
|
||||
FMT_FUNC void format_system_error(
|
||||
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
|
||||
FMT_TRY {
|
||||
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
|
||||
buffer.resize(internal::INLINE_BUFFER_SIZE);
|
||||
for (;;) {
|
||||
char *system_message = &buffer[0];
|
||||
int result = safe_strerror(error_code, system_message, buffer.size());
|
||||
if (result == 0) {
|
||||
out << message << ": " << system_message;
|
||||
return;
|
||||
}
|
||||
if (result != ERANGE)
|
||||
break; // Can't get error message, report error code instead.
|
||||
buffer.resize(buffer.size() * 2);
|
||||
}
|
||||
} FMT_CATCH(...) {}
|
||||
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void internal::ArgMap<Char>::init(const ArgList &args) {
|
||||
if (!map_.empty())
|
||||
return;
|
||||
typedef internal::NamedArg<Char> NamedArg;
|
||||
const NamedArg *named_arg = FMT_NULL;
|
||||
bool use_values =
|
||||
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
|
||||
if (use_values) {
|
||||
for (unsigned i = 0;/*nothing*/; ++i) {
|
||||
internal::Arg::Type arg_type = args.type(i);
|
||||
switch (arg_type) {
|
||||
case internal::Arg::NONE:
|
||||
return;
|
||||
case internal::Arg::NAMED_ARG:
|
||||
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
|
||||
map_.push_back(Pair(named_arg->name, *named_arg));
|
||||
break;
|
||||
default:
|
||||
/*nothing*/;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
|
||||
internal::Arg::Type arg_type = args.type(i);
|
||||
if (arg_type == internal::Arg::NAMED_ARG) {
|
||||
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
|
||||
map_.push_back(Pair(named_arg->name, *named_arg));
|
||||
}
|
||||
}
|
||||
for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
|
||||
switch (args.args_[i].type) {
|
||||
case internal::Arg::NONE:
|
||||
return;
|
||||
case internal::Arg::NAMED_ARG:
|
||||
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
|
||||
map_.push_back(Pair(named_arg->name, *named_arg));
|
||||
break;
|
||||
default:
|
||||
/*nothing*/;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void internal::FixedBuffer<Char>::grow(std::size_t) {
|
||||
FMT_THROW(std::runtime_error("buffer overflow"));
|
||||
}
|
||||
|
||||
FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg(
|
||||
unsigned arg_index, const char *&error) {
|
||||
internal::Arg arg = args_[arg_index];
|
||||
switch (arg.type) {
|
||||
case internal::Arg::NONE:
|
||||
error = "argument index out of range";
|
||||
break;
|
||||
case internal::Arg::NAMED_ARG:
|
||||
arg = *static_cast<const internal::Arg*>(arg.pointer);
|
||||
break;
|
||||
default:
|
||||
/*nothing*/;
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
FMT_FUNC void report_system_error(
|
||||
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
|
||||
// 'fmt::' is for bcc32.
|
||||
report_error(format_system_error, error_code, message);
|
||||
}
|
||||
|
||||
#if FMT_USE_WINDOWS_H
|
||||
FMT_FUNC void report_windows_error(
|
||||
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
|
||||
// 'fmt::' is for bcc32.
|
||||
report_error(internal::format_windows_error, error_code, message);
|
||||
}
|
||||
#endif
|
||||
|
||||
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
|
||||
MemoryWriter w;
|
||||
w.write(format_str, args);
|
||||
std::fwrite(w.data(), 1, w.size(), f);
|
||||
}
|
||||
|
||||
FMT_FUNC void print(CStringRef format_str, ArgList args) {
|
||||
print(stdout, format_str, args);
|
||||
}
|
||||
|
||||
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
|
||||
char escape[] = "\x1b[30m";
|
||||
escape[3] = static_cast<char>('0' + c);
|
||||
std::fputs(escape, stdout);
|
||||
print(format, args);
|
||||
std::fputs(RESET_COLOR, stdout);
|
||||
}
|
||||
|
||||
#ifndef FMT_HEADER_ONLY
|
||||
|
||||
template struct internal::BasicData<void>;
|
||||
|
||||
// Explicit instantiations for char.
|
||||
|
||||
template void internal::FixedBuffer<char>::grow(std::size_t);
|
||||
|
||||
template void internal::ArgMap<char>::init(const ArgList &args);
|
||||
|
||||
template FMT_API int internal::CharTraits<char>::format_float(
|
||||
char *buffer, std::size_t size, const char *format,
|
||||
unsigned width, int precision, double value);
|
||||
|
||||
template FMT_API int internal::CharTraits<char>::format_float(
|
||||
char *buffer, std::size_t size, const char *format,
|
||||
unsigned width, int precision, long double value);
|
||||
|
||||
// Explicit instantiations for wchar_t.
|
||||
|
||||
template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
|
||||
|
||||
template void internal::ArgMap<wchar_t>::init(const ArgList &args);
|
||||
|
||||
template FMT_API int internal::CharTraits<wchar_t>::format_float(
|
||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||
unsigned width, int precision, double value);
|
||||
|
||||
template FMT_API int internal::CharTraits<wchar_t>::format_float(
|
||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||
unsigned width, int precision, long double value);
|
||||
|
||||
#endif // FMT_HEADER_ONLY
|
||||
|
||||
} // namespace fmt
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
4012
lib/fmt/fmt/format.h
Normal file
4012
lib/fmt/fmt/format.h
Normal file
File diff suppressed because it is too large
Load diff
1
lib/rapidjson
Submodule
1
lib/rapidjson
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit d87b698d0fcc10a5f632ecbc80a9cb2a8fa094a5
|
2
lib/rapidjson.pri
Normal file
2
lib/rapidjson.pri
Normal file
|
@ -0,0 +1,2 @@
|
|||
# rapidjson
|
||||
INCLUDEPATH += $$PWD/rapidjson/include/
|
1
lib/serialize
Submodule
1
lib/serialize
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 130ffc3ec722284ca454a1e70c5478a75f380144
|
2
lib/serialize.pri
Normal file
2
lib/serialize.pri
Normal file
|
@ -0,0 +1,2 @@
|
|||
# serialize
|
||||
INCLUDEPATH += $$PWD/serialize/include/
|
1
lib/settings
Submodule
1
lib/settings
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit a5040463c01e6b0e562eab82e0decb29cab9b450
|
8
lib/settings.pri
Normal file
8
lib/settings.pri
Normal file
|
@ -0,0 +1,8 @@
|
|||
# settings
|
||||
DEFINES += PAJLADA_SETTINGS_BOOST_FILESYSTEM
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/settings/src/settings/settingdata.cpp \
|
||||
$$PWD/settings/src/settings/settingmanager.cpp
|
||||
|
||||
INCLUDEPATH += $$PWD/settings/include/
|
1
lib/signals
Submodule
1
lib/signals
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 1c38746b05d9311e73c8c8acdfdc4d36c9c551be
|
2
lib/signals.pri
Normal file
2
lib/signals.pri
Normal file
|
@ -0,0 +1,2 @@
|
|||
# signals
|
||||
INCLUDEPATH += $$PWD/signals/include/
|
24
lib/winsdk.pri
Normal file
24
lib/winsdk.pri
Normal file
|
@ -0,0 +1,24 @@
|
|||
win32 {
|
||||
LIBS += -luser32
|
||||
}
|
||||
|
||||
# Optional dependency on Windows SDK 7
|
||||
!contains(QMAKE_TARGET.arch, x86_64) {
|
||||
win32:exists(C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\Windows.h) {
|
||||
LIBS += -L"C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib" -ldwmapi
|
||||
|
||||
DEFINES += "USEWINSDK"
|
||||
message(Using Windows SDK 7)
|
||||
}
|
||||
}
|
||||
|
||||
# Optional dependency on Windows SDK 10
|
||||
contains(QMAKE_TARGET.arch, x86_64) {
|
||||
WIN_SDK_VERSION = $$(WindowsSDKVersion)
|
||||
!isEmpty(WIN_SDK_VERSION) {
|
||||
!equals(WIN_SDK_VERSION, "\\") {
|
||||
DEFINES += "USEWINSDK"
|
||||
message(Using Windows SDK 10)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue