mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
moved BaseWindow and related files into appbase
This commit is contained in:
parent
0f8a1a3ff8
commit
38b23d77ce
|
@ -16,6 +16,8 @@ TARGET = chatterino
|
|||
TEMPLATE = app
|
||||
PRECOMPILED_HEADER = src/PrecompiledHeader.hpp
|
||||
CONFIG += precompile_header
|
||||
DEFINES += CHATTERINO
|
||||
DEFINES += "AB_NAMESPACE=chatterino"
|
||||
|
||||
useBreakpad {
|
||||
LIBS += -L$$PWD/lib/qBreakpad/handler/build
|
||||
|
@ -39,7 +41,6 @@ macx {
|
|||
# Submodules
|
||||
include(lib/appbase.pri)
|
||||
include(lib/humanize.pri)
|
||||
include(lib/fmt.pri)
|
||||
DEFINES += IRC_NAMESPACE=Communi
|
||||
include(lib/libcommuni.pri)
|
||||
include(lib/websocketpp.pri)
|
||||
|
@ -114,11 +115,9 @@ SOURCES += \
|
|||
src/util/DebugCount.cpp \
|
||||
src/util/RapidjsonHelpers.cpp \
|
||||
src/util/StreamLink.cpp \
|
||||
src/util/WindowsHelper.cpp \
|
||||
src/widgets/AccountSwitchPopupWidget.cpp \
|
||||
src/widgets/AccountSwitchWidget.cpp \
|
||||
src/widgets/AttachedWindow.cpp \
|
||||
src/widgets/BaseWindow.cpp \
|
||||
src/widgets/dialogs/EmotePopup.cpp \
|
||||
src/widgets/dialogs/LastRunCrashDialog.cpp \
|
||||
src/widgets/dialogs/LoginDialog.cpp \
|
||||
|
@ -142,7 +141,6 @@ SOURCES += \
|
|||
src/widgets/helper/SettingsDialogTab.cpp \
|
||||
src/widgets/helper/SignalLabel.cpp \
|
||||
src/widgets/helper/TitlebarButton.cpp \
|
||||
src/widgets/Label.cpp \
|
||||
src/widgets/Notebook.cpp \
|
||||
src/widgets/Scrollbar.cpp \
|
||||
src/widgets/settingspages/AboutPage.cpp \
|
||||
|
@ -164,13 +162,11 @@ SOURCES += \
|
|||
src/widgets/splits/SplitInput.cpp \
|
||||
src/widgets/splits/SplitOverlay.cpp \
|
||||
src/widgets/StreamView.cpp \
|
||||
src/widgets/TooltipWidget.cpp \
|
||||
src/widgets/Window.cpp \
|
||||
src/common/LinkParser.cpp \
|
||||
src/controllers/moderationactions/ModerationActions.cpp \
|
||||
src/singletons/NativeMessaging.cpp \
|
||||
src/singletons/Emotes.cpp \
|
||||
src/singletons/Fonts.cpp \
|
||||
src/singletons/Logging.cpp \
|
||||
src/singletons/Paths.cpp \
|
||||
src/singletons/Resources.cpp \
|
||||
|
@ -204,7 +200,6 @@ SOURCES += \
|
|||
src/controllers/notifications/NotificationModel.cpp \
|
||||
src/singletons/Toasts.cpp \
|
||||
src/common/DownloadManager.cpp \
|
||||
src/widgets/helper/EffectLabel.cpp \
|
||||
src/widgets/helper/Button.cpp \
|
||||
src/messages/MessageContainer.cpp \
|
||||
src/debug/Benchmark.cpp \
|
||||
|
@ -233,7 +228,6 @@ HEADERS += \
|
|||
src/common/NetworkWorker.hpp \
|
||||
src/common/NullablePtr.hpp \
|
||||
src/common/ProviderId.hpp \
|
||||
src/util/RapidJsonSerializeQString.hpp \
|
||||
src/common/SignalVectorModel.hpp \
|
||||
src/common/Version.hpp \
|
||||
src/controllers/accounts/Account.hpp \
|
||||
|
@ -303,18 +297,15 @@ HEADERS += \
|
|||
src/util/Helpers.hpp \
|
||||
src/util/IrcHelpers.hpp \
|
||||
src/util/LayoutCreator.hpp \
|
||||
src/util/PostToThread.hpp \
|
||||
src/util/QStringHash.hpp \
|
||||
src/util/RapidjsonHelpers.hpp \
|
||||
src/util/RemoveScrollAreaBackground.hpp \
|
||||
src/util/SharedPtrElementLess.hpp \
|
||||
src/util/StandardItemHelper.hpp \
|
||||
src/util/StreamLink.hpp \
|
||||
src/util/WindowsHelper.hpp \
|
||||
src/widgets/AccountSwitchPopupWidget.hpp \
|
||||
src/widgets/AccountSwitchWidget.hpp \
|
||||
src/widgets/AttachedWindow.hpp \
|
||||
src/widgets/BaseWindow.hpp \
|
||||
src/widgets/dialogs/EmotePopup.hpp \
|
||||
src/widgets/dialogs/LastRunCrashDialog.hpp \
|
||||
src/widgets/dialogs/LoginDialog.hpp \
|
||||
|
@ -337,10 +328,8 @@ HEADERS += \
|
|||
src/widgets/helper/ScrollbarHighlight.hpp \
|
||||
src/widgets/helper/SearchPopup.hpp \
|
||||
src/widgets/helper/SettingsDialogTab.hpp \
|
||||
src/widgets/helper/Shortcut.hpp \
|
||||
src/widgets/helper/SignalLabel.hpp \
|
||||
src/widgets/helper/TitlebarButton.hpp \
|
||||
src/widgets/Label.hpp \
|
||||
src/widgets/Notebook.hpp \
|
||||
src/widgets/Scrollbar.hpp \
|
||||
src/widgets/settingspages/AboutPage.hpp \
|
||||
|
@ -362,14 +351,12 @@ HEADERS += \
|
|||
src/widgets/splits/SplitInput.hpp \
|
||||
src/widgets/splits/SplitOverlay.hpp \
|
||||
src/widgets/StreamView.hpp \
|
||||
src/widgets/TooltipWidget.hpp \
|
||||
src/widgets/Window.hpp \
|
||||
src/providers/twitch/TwitchCommon.hpp \
|
||||
src/util/IsBigEndian.hpp \
|
||||
src/common/LinkParser.hpp \
|
||||
src/controllers/moderationactions/ModerationActions.hpp \
|
||||
src/singletons/Emotes.hpp \
|
||||
src/singletons/Fonts.hpp \
|
||||
src/singletons/Logging.hpp \
|
||||
src/singletons/Paths.hpp \
|
||||
src/singletons/Resources.hpp \
|
||||
|
@ -409,7 +396,6 @@ HEADERS += \
|
|||
src/controllers/notifications/NotificationModel.hpp \
|
||||
src/singletons/Toasts.hpp \
|
||||
src/common/DownloadManager.hpp \
|
||||
src/widgets/helper/EffectLabel.hpp \
|
||||
src/util/LayoutHelper.hpp \
|
||||
src/widgets/helper/Button.hpp \
|
||||
src/messages/MessageContainer.hpp \
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
Third party libraries are stored here
|
||||
|
||||
Fetched via `git submodule update --init --recursive`
|
||||
|
||||
Using [fmtlib/fmt](https://github.com/fmtlib/fmt)
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
# fmt
|
||||
SOURCES += $$PWD/../lib/fmt/fmt/format.cpp
|
||||
|
||||
INCLUDEPATH += $$PWD/../lib/fmt/
|
|
@ -1,535 +0,0 @@
|
|||
/*
|
||||
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
4012
lib/fmt/fmt/format.h
File diff suppressed because it is too large
Load diff
|
@ -1,154 +0,0 @@
|
|||
#include "singletons/Fonts.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "debug/AssertInGuiThread.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QtGlobal>
|
||||
|
||||
#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 chatterino {
|
||||
|
||||
Fonts::Fonts()
|
||||
: chatFontFamily("/appearance/currentFontFamily", DEFAULT_FONT_FAMILY)
|
||||
, chatFontSize("/appearance/currentFontSize", DEFAULT_FONT_SIZE)
|
||||
{
|
||||
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);
|
||||
|
||||
getSettings()->boldScale.connect(
|
||||
[this]() {
|
||||
assertInGuiThread();
|
||||
|
||||
getApp()->windows->incGeneration();
|
||||
|
||||
for (auto &map : this->fontsByType_)
|
||||
{
|
||||
map.clear();
|
||||
}
|
||||
this->fontChanged.invoke();
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
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(getSettings()->boldScale.getValue())}},
|
||||
{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(getSettings()->boldScale.getValue())};
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,89 +0,0 @@
|
|||
#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 chatterino {
|
||||
|
||||
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;
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -17,7 +17,7 @@ class Theme final : public Singleton
|
|||
public:
|
||||
Theme();
|
||||
|
||||
inline bool isLightTheme() const
|
||||
bool isLightTheme() const
|
||||
{
|
||||
return this->isLight_;
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <QRunnable>
|
||||
#include <QThreadPool>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#define async_exec(a) \
|
||||
QThreadPool::globalInstance()->start(new LambdaRunnable(a));
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
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 chatterino
|
|
@ -1,45 +0,0 @@
|
|||
#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
|
|
@ -1,56 +0,0 @@
|
|||
#include "WindowsHelper.hpp"
|
||||
|
||||
#ifdef USEWINSDK
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
#endif
|
|
@ -1,15 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef USEWINSDK
|
||||
|
||||
# include <Windows.h>
|
||||
# include <boost/optional.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
boost::optional<UINT> getWindowDpi(HWND hwnd);
|
||||
void flushClipboard();
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
#endif
|
|
@ -1,895 +0,0 @@
|
|||
#include "BaseWindow.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "boost/algorithm/algorithm.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/PostToThread.hpp"
|
||||
#include "util/WindowsHelper.hpp"
|
||||
#include "widgets/Label.hpp"
|
||||
#include "widgets/TooltipWidget.hpp"
|
||||
#include "widgets/helper/EffectLabel.hpp"
|
||||
#include "widgets/helper/Shortcut.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QDesktopWidget>
|
||||
#include <QIcon>
|
||||
#include <functional>
|
||||
|
||||
#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 chatterino {
|
||||
|
||||
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(0); });
|
||||
|
||||
// QTimer::this->scaleChangedEvent(this->getScale());
|
||||
}
|
||||
|
||||
float BaseWindow::scale() const
|
||||
{
|
||||
return this->overrideScale().value_or(this->scale_);
|
||||
}
|
||||
|
||||
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("Chatterino");
|
||||
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()->uiScale.setValue(WindowManager::clampUiScale(
|
||||
getSettings()->uiScale.getValue() + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
getSettings()->uiScale.setValue(WindowManager::clampUiScale(
|
||||
getSettings()->uiScale.getValue() - 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 *)
|
||||
{
|
||||
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
|
||||
getApp()->windows->queueSave();
|
||||
|
||||
this->moveIntoDesktopRect(this);
|
||||
|
||||
this->calcButtonsSizes();
|
||||
}
|
||||
|
||||
void BaseWindow::moveEvent(QMoveEvent *event)
|
||||
{
|
||||
// Queue up save because: Window position changed
|
||||
getApp()->windows->queueSave();
|
||||
|
||||
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)
|
||||
{
|
||||
#ifdef USEWINSDK
|
||||
this->calcButtonsSizes();
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
: getApp()->windows->getUiScaleValue());
|
||||
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;
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,120 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/BaseWidget.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <pajlada/signals/signalholder.hpp>
|
||||
|
||||
class QHBoxLayout;
|
||||
struct tagMSG;
|
||||
typedef struct tagMSG MSG;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
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;
|
||||
|
||||
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_;
|
||||
}; // namespace chatterino
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,138 +0,0 @@
|
|||
#include "Label.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
Label::Label(QString text, FontStyle style)
|
||||
: Label(nullptr, text, style)
|
||||
{
|
||||
}
|
||||
|
||||
Label::Label(BaseWidget *parent, QString text, FontStyle style)
|
||||
: BaseWidget(parent)
|
||||
, text_(text)
|
||||
, fontStyle_(style)
|
||||
{
|
||||
auto app = getApp();
|
||||
|
||||
this->connections_.managedConnect(app->fonts->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 *)
|
||||
{
|
||||
auto app = getApp();
|
||||
|
||||
QPainter painter(this);
|
||||
QFontMetrics metrics = app->fonts->getFontMetrics(
|
||||
this->getFontStyle(), this->scale() * 96.f / this->logicalDpiX() *
|
||||
this->devicePixelRatioF());
|
||||
painter.setFont(app->fonts->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()
|
||||
{
|
||||
auto app = getApp();
|
||||
|
||||
QFontMetrics metrics =
|
||||
app->fonts->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 chatterino
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "singletons/Fonts.hpp"
|
||||
#include "widgets/BaseWidget.hpp"
|
||||
|
||||
#include <pajlada/signals/signalholder.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
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 chatterino
|
|
@ -10,7 +10,7 @@
|
|||
#include "widgets/dialogs/SettingsDialog.hpp"
|
||||
#include "widgets/helper/NotebookButton.hpp"
|
||||
#include "widgets/helper/NotebookTab.hpp"
|
||||
#include "widgets/helper/Shortcut.hpp"
|
||||
#include "util/Shortcut.hpp"
|
||||
#include "widgets/splits/Split.hpp"
|
||||
#include "widgets/splits/SplitContainer.hpp"
|
||||
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
#include "TooltipWidget.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "singletons/Fonts.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDesktopWidget>
|
||||
#include <QStyle>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#ifdef USEWINSDK
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
TooltipWidget *TooltipWidget::getInstance()
|
||||
{
|
||||
static TooltipWidget *tooltipWidget = nullptr;
|
||||
if (tooltipWidget == nullptr)
|
||||
{
|
||||
tooltipWidget = new TooltipWidget();
|
||||
}
|
||||
return tooltipWidget;
|
||||
}
|
||||
|
||||
TooltipWidget::TooltipWidget(BaseWidget *parent)
|
||||
: BaseWindow(parent, BaseWindow::TopMost)
|
||||
, displayText_(new QLabel())
|
||||
{
|
||||
auto app = getApp();
|
||||
|
||||
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);
|
||||
|
||||
displayText_->setAlignment(Qt::AlignHCenter);
|
||||
displayText_->setText("tooltip text");
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->setContentsMargins(10, 5, 10, 5);
|
||||
layout->addWidget(displayText_);
|
||||
this->setLayout(layout);
|
||||
|
||||
this->fontChangedConnection_ =
|
||||
app->fonts->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()
|
||||
{
|
||||
auto app = getApp();
|
||||
|
||||
this->setFont(
|
||||
app->fonts->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::changeEvent(QEvent *)
|
||||
{
|
||||
// clear parents event
|
||||
}
|
||||
|
||||
void TooltipWidget::leaveEvent(QEvent *)
|
||||
{
|
||||
// clear parents event
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
|
@ -1,41 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/BaseWindow.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QWidget>
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class TooltipWidget : public BaseWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static TooltipWidget *getInstance();
|
||||
|
||||
TooltipWidget(BaseWidget *parent = nullptr);
|
||||
virtual ~TooltipWidget() override;
|
||||
|
||||
void setText(QString text);
|
||||
void setWordWrap(bool wrap);
|
||||
|
||||
#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 *displayText_;
|
||||
pajlada::Signals::Connection fontChangedConnection_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
|
@ -16,7 +16,7 @@
|
|||
#include "widgets/dialogs/WelcomeDialog.hpp"
|
||||
#include "widgets/helper/EffectLabel.hpp"
|
||||
#include "widgets/helper/NotebookTab.hpp"
|
||||
#include "widgets/helper/Shortcut.hpp"
|
||||
#include "util/Shortcut.hpp"
|
||||
#include "widgets/helper/TitlebarButton.hpp"
|
||||
#include "widgets/splits/ClosedSplits.hpp"
|
||||
#include "widgets/splits/Split.hpp"
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QShortcut>
|
||||
#include <QWidget>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
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 chatterino
|
|
@ -23,7 +23,7 @@
|
|||
#include "widgets/helper/NotebookTab.hpp"
|
||||
#include "widgets/helper/ResizingTextEdit.hpp"
|
||||
#include "widgets/helper/SearchPopup.hpp"
|
||||
#include "widgets/helper/Shortcut.hpp"
|
||||
#include "util/Shortcut.hpp"
|
||||
#include "widgets/splits/ClosedSplits.hpp"
|
||||
#include "widgets/splits/SplitContainer.hpp"
|
||||
#include "widgets/splits/SplitHeader.hpp"
|
||||
|
|
Loading…
Reference in a new issue