Move some style code to lib_ui.

This commit is contained in:
John Preston 2019-09-13 13:24:06 +03:00
parent 5a1c8e6a0a
commit e2f54eb3e9
69 changed files with 1119 additions and 856 deletions

View file

@ -9,6 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <cstdlib>
// Ensures/Expects.
#include <gsl/gsl_assert>
namespace base {
namespace assertion {

View file

@ -7,3 +7,4 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "base/base_pch.h"
// Precompiled header helper.

View file

@ -65,8 +65,8 @@ private:
};
QCursor EditColorBox::Picker::generateCursor() {
auto diameter = ConvertScale(16);
auto line = ConvertScale(1);
auto diameter = style::ConvertScale(16);
auto line = style::ConvertScale(1);
auto size = ((diameter + 2 * line) >= 32) ? 64 : 32;
auto cursor = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
cursor.setDevicePixelRatio(cRetinaFactor());

View file

@ -393,7 +393,10 @@ EmojiListWidget::EmojiListWidget(
_esize = Ui::Emoji::GetSizeLarge();
for (auto i = 0; i != kEmojiSectionCount; ++i) {
_counts[i] = Ui::Emoji::GetSectionCount(static_cast<Section>(i));
const auto section = static_cast<Section>(i);
_counts[i] = (section == Section::Recent)
? GetRecentEmoji().size()
: Ui::Emoji::GetSectionCount(section);
}
_picker->chosen(
@ -488,10 +491,13 @@ int EmojiListWidget::countDesiredHeight(int newWidth) {
void EmojiListWidget::ensureLoaded(int section) {
Expects(section >= 0 && section < kEmojiSectionCount);
if (!_emoji[section].isEmpty()) {
return;
}
_emoji[section] = Ui::Emoji::GetSection(static_cast<Section>(section));
_emoji[section] = (static_cast<Section>(section) == Section::Recent)
? GetRecentEmojiSection()
: Ui::Emoji::GetSection(static_cast<Section>(section));
_counts[section] = _emoji[section].size();
if (static_cast<Section>(section) == Section::Recent) {
return;
@ -531,7 +537,7 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
if (info.section > 0 && r.top() < info.rowsTop) {
p.setFont(st::emojiPanHeaderFont);
p.setPen(st::emojiPanHeaderFg);
p.drawTextLeft(st::emojiPanHeaderLeft - st::buttonRadius, info.top + st::emojiPanHeaderTop, width(), Ui::Emoji::CategoryTitle(info.section)(tr::now));
p.drawTextLeft(st::emojiPanHeaderLeft - st::buttonRadius, info.top + st::emojiPanHeaderTop, width(), ChatHelpers::EmojiCategoryTitle(info.section)(tr::now));
}
if (r.top() + r.height() > info.rowsTop) {
ensureLoaded(info.section);
@ -640,7 +646,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
}
void EmojiListWidget::selectEmoji(EmojiPtr emoji) {
Ui::Emoji::AddRecent(emoji);
AddRecentEmoji(emoji);
_chosen.fire_copy(emoji);
}
@ -776,7 +782,7 @@ void EmojiListWidget::processHideFinished() {
void EmojiListWidget::refreshRecent() {
clearSelection();
_emoji[0] = Ui::Emoji::GetSection(Section::Recent);
_emoji[0] = GetRecentEmojiSection();
_counts[0] = _emoji[0].size();
resizeToWidth(width());
}
@ -859,4 +865,17 @@ void EmojiListWidget::showEmojiSection(Section section) {
update();
}
tr::phrase<> EmojiCategoryTitle(int index) {
switch (index) {
case 1: return tr::lng_emoji_category1;
case 2: return tr::lng_emoji_category2;
case 3: return tr::lng_emoji_category3;
case 4: return tr::lng_emoji_category4;
case 5: return tr::lng_emoji_category5;
case 6: return tr::lng_emoji_category6;
case 7: return tr::lng_emoji_category7;
}
Unexpected("Index in CategoryTitle.");
}
} // namespace ChatHelpers

View file

@ -11,6 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/tooltip.h"
#include "base/timer.h"
namespace tr {
template <typename ...Tags>
struct phrase;
} // namespace tr
namespace Ui {
namespace Emoji {
enum class Section;
@ -23,7 +28,7 @@ class SessionController;
namespace ChatHelpers {
constexpr auto kEmojiSectionCount = 8;
inline constexpr auto kEmojiSectionCount = 8;
class EmojiColorPicker;
@ -120,4 +125,6 @@ private:
};
tr::phrase<> EmojiCategoryTitle(int index);
} // namespace ChatHelpers

View file

@ -114,7 +114,7 @@ auto SuggestionsWidget::getRowsByQuery() const -> std::vector<Row> {
}) | ranges::to_vector;
auto lastRecent = begin(result);
const auto &recent = GetRecent();
const auto &recent = GetRecentEmoji();
for (const auto &item : recent) {
const auto emoji = item.first->original()
? item.first->original()

View file

@ -384,7 +384,7 @@ void InitMessageField(
field->setTagMimeProcessor(std::make_unique<FieldTagMimeProcessor>());
field->document()->setDocumentMargin(4.);
field->setAdditionalMargin(ConvertScale(4) - 4);
field->setAdditionalMargin(style::ConvertScale(4) - 4);
field->customTab(true);
field->setInstantReplaces(Ui::InstantReplaces::Default());

View file

@ -53,6 +53,11 @@ CppFile &CppFile::include(const QString &header) {
return newline();
}
CppFile &CppFile::includeFromLibrary(const QString &header) {
stream() << "#include <" << header << ">";
return newline();
}
CppFile &CppFile::pushNamespace(const QString &name) {
namespaces_.push_back(name);

View file

@ -36,6 +36,7 @@ public:
return *this;
}
CppFile &include(const QString &header);
CppFile &includeFromLibrary(const QString &header);
// Empty name adds anonymous namespace.
CppFile &pushNamespace(const QString &name = QString());

View file

@ -396,6 +396,22 @@ void Init() {\n\
bool Generator::writeHeader() {
auto header = std::make_unique<common::CppFile>(outputPath_ + ".h", project_);
header->includeFromLibrary("QtCore/QChar");
header->includeFromLibrary("QtCore/QString");
header->includeFromLibrary("QtCore/QVector");
header->newline();
header->includeFromLibrary("vector");
header->newline();
header->pushNamespace("Ui").pushNamespace("Emoji");
header->stream() << "class One;\n";
header->popNamespace().popNamespace().newline();
header->stream() << "\
using EmojiPtr = const Ui::Emoji::One*;\n\
using EmojiPack = QVector<EmojiPtr>;\n\
\n";
header->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace("internal");
header->stream() << "\
\n\
@ -406,20 +422,6 @@ EmojiPtr ByIndex(int index);\n\
\n\
EmojiPtr Find(const QChar *ch, const QChar *end, int *outLength = nullptr);\n\
\n\
inline bool IsReplaceEdge(const QChar *ch) {\n\
return true;\n\
\n\
// switch (ch->unicode()) {\n\
// case '.': case ',': case ':': case ';': case '!': case '?': case '#': case '@':\n\
// case '(': case ')': case '[': case ']': case '{': case '}': case '<': case '>':\n\
// case '+': case '=': case '-': case '_': case '*': case '/': case '\\\\': case '^': case '$':\n\
// case '\"': case '\\'':\n\
// case 8212: case 171: case 187: // --, <<, >>\n\
// return true;\n\
// }\n\
// return false;\n\
}\n\
\n\
const std::vector<std::pair<QString, int>> GetReplacementPairs();\n\
EmojiPtr FindReplace(const QChar *ch, const QChar *end, int *outLength = nullptr);\n\
\n";
@ -439,7 +441,7 @@ enum class Section {\n\
};\n\
\n\
int GetSectionCount(Section section);\n\
EmojiPack GetSection(Section section);\n\
QVector<const One*> GetSection(Section section);\n\
\n";
return header->finalize();
}
@ -558,8 +560,9 @@ bool Generator::writeGetSections() {
source_->stream() << "\
\n\
int GetSectionCount(Section section) {\n\
switch (section) {\n\
case Section::Recent: return GetRecent().size();\n";
Expects(section != Section::Recent);\n\
\n\
switch (section) {\n";
auto countIndex = 0;
for (auto name : sectionNames) {
if (countIndex >= int(data_.categories.size())) {
@ -575,15 +578,9 @@ int GetSectionCount(Section section) {\n\
}\n\
\n\
EmojiPack GetSection(Section section) {\n\
switch (section) {\n\
case Section::Recent: {\n\
auto result = EmojiPack();\n\
result.reserve(GetRecent().size());\n\
for (auto &item : GetRecent()) {\n\
result.push_back(item.first);\n\
}\n\
return result;\n\
} break;\n";
Expects(section != Section::Recent);\n\
\n\
switch (section) {\n";
auto index = 0;
auto offset = 0;
for (auto name : sectionNames) {
@ -614,7 +611,7 @@ bool Generator::writeFindReplace() {
const std::vector<std::pair<QString, int>> ReplacementPairs = {\n";
for (const auto &[what, index] : data_.replaces) {
source_->stream() << "\
{ qsl(\"" << what << "\"), " << index << " },\n";
{ \"" << what << "\", " << index << " },\n";
}
source_->stream() << "\
};\n\
@ -766,10 +763,6 @@ bool Generator::writeFindFromDictionary(
source_->stream() << tabs(tabsUsed) << "if (outLength) *outLength = (ch - start);\n";
}
// While IsReplaceEdge() currently is always true we just return the value.
//source_->stream() << tabs(1 + chars.size()) << "if (ch + " << chars.size() << " == end || IsReplaceEdge(*(ch + " << chars.size() << ")) || (ch + " << chars.size() << ")->unicode() == ' ') {\n";
//source_->stream() << tabs(1 + chars.size()) << "\treturn &Items[" << item.second << "];\n";
//source_->stream() << tabs(1 + chars.size()) << "}\n";
source_->stream() << tabs(tabsUsed) << "return " << (item.second + 1) << ";\n";
}
finishChecksTillKey(QString());

View file

@ -210,6 +210,9 @@ bool Generator::writeHeader() {
header_->include("ui/style/style_core.h").newline();
if (!writeHeaderRequiredIncludes()) {
return false;
}
if (!writeHeaderStyleNamespace()) {
return false;
}
@ -233,12 +236,9 @@ bool inited = false;\n\
class Module_" << baseName_ << " : public style::internal::ModuleBase {\n\
public:\n\
Module_" << baseName_ << "() { style::internal::registerModule(this); }\n\
~Module_" << baseName_ << "() { style::internal::unregisterModule(this); }\n\
\n\
void start() override {\n\
style::internal::init_" << baseName_ << "();\n\
}\n\
void stop() override {\n\
void start(int scale) override {\n\
style::internal::init_" << baseName_ << "(scale);\n\
}\n\
};\n\
Module_" << baseName_ << " registrator;\n";
@ -397,6 +397,50 @@ QString Generator::valueAssignmentCode(structure::Value value) const {
return QString();
}
bool Generator::writeHeaderRequiredIncludes() {
std::function<QString(const Module&, structure::FullName)> findInIncludes = [&](const Module &module, const structure::FullName &name) {
auto result = QString();
module.enumIncludes([&](const Module &included) {
if (Module::findStructInModule(name, included)) {
result = moduleBaseName(included);
return false;
}
result = findInIncludes(included, name);
return true;
});
return result;
};
auto includes = QStringList();
const auto written = module_.enumStructs([&](const Struct &value) -> bool {
for (const auto &field : value.fields) {
if (field.type.tag == structure::TypeTag::Struct) {
const auto name = field.type.name;
if (!module_.findStructInModule(name, module_)) {
const auto base = findInIncludes(module_, name);
if (base.isEmpty()) {
return false;
}
if (!includes.contains(base)) {
includes.push_back(base);
}
}
}
}
return true;
});
if (!written) {
return false;
} else if (includes.isEmpty()) {
return true;
}
for (const auto base : includes) {
header_->include(base + ".h");
}
header_->newline();
return true;
}
bool Generator::writeHeaderStyleNamespace() {
if (!module_.hasStructs() && !module_.hasVariables()) {
return true;
@ -405,7 +449,7 @@ bool Generator::writeHeaderStyleNamespace() {
if (module_.hasVariables()) {
header_->pushNamespace("internal").newline();
header_->stream() << "void init_" << baseName_ << "();\n\n";
header_->stream() << "void init_" << baseName_ << "(int scale);\n\n";
header_->popNamespace();
}
bool wroteForwardDeclarations = writeStructsForwardDeclarations();
@ -1076,7 +1120,7 @@ bool Generator::writeVariableInit() {
}
source_->stream() << "\
void init_" << baseName_ << "() {\n\
void init_" << baseName_ << "(int scale) {\n\
if (inited) return;\n\
inited = true;\n\n";
@ -1084,7 +1128,7 @@ void init_" << baseName_ << "() {\n\
bool writtenAtLeastOne = false;
bool result = module_.enumIncludes([&](const Module &module) -> bool {
if (module.hasVariables()) {
source_->stream() << "\tinit_" + moduleBaseName(module) + "();\n";
source_->stream() << "\tinit_" + moduleBaseName(module) + "(scale);\n";
writtenAtLeastOne = true;
}
return true;
@ -1099,7 +1143,7 @@ void init_" << baseName_ << "() {\n\
if (!pxValues_.isEmpty() || !fontFamilies_.isEmpty()) {
if (!pxValues_.isEmpty()) {
source_->stream() << "\tinitPxValues();\n";
source_->stream() << "\tinitPxValues(scale);\n";
}
if (!fontFamilies_.isEmpty()) {
source_->stream() << "\tinitFontFamilies();\n";
@ -1134,8 +1178,7 @@ bool Generator::writePxValuesInit() {
source_->stream() << "int " << pxValueName(i.key()) << " = " << i.key() << ";\n";
}
source_->stream() << "\
void initPxValues() {\n\
const auto scale = cScale();\n";
void initPxValues(int scale) {\n";
for (auto it = pxValues_.cbegin(), e = pxValues_.cend(); it != e; ++it) {
auto value = it.key();
source_->stream() << "\t" << pxValueName(value) << " = ConvertScale(" << value << ", scale);\n";

View file

@ -35,6 +35,7 @@ private:
QString typeToDefaultValue(structure::Type type) const;
QString valueAssignmentCode(structure::Value value) const;
bool writeHeaderRequiredIncludes();
bool writeHeaderStyleNamespace();
bool writeStructsForwardDeclarations();
bool writeStructsDefinitions();

View file

@ -69,7 +69,7 @@ const Variable *Module::findVariable(const FullName &name, bool *outFromThisModu
return nullptr;
}
const Struct *Module::findStructInModule(const FullName &name, const Module &module) const {
const Struct *Module::findStructInModule(const FullName &name, const Module &module) {
auto index = module.structsByName_.value(fullNameKey(name), -1);
if (index < 0) {
return nullptr;
@ -77,7 +77,7 @@ const Struct *Module::findStructInModule(const FullName &name, const Module &mod
return &module.structs_.at(index);
}
const Variable *Module::findVariableInModule(const FullName &name, const Module &module) const {
const Variable *Module::findVariableInModule(const FullName &name, const Module &module) {
auto index = module.variablesByName_.value(fullNameKey(name), -1);
if (index < 0) {
return nullptr;

View file

@ -81,8 +81,8 @@ public:
return !fullpath_.isEmpty();
}
const Struct *findStructInModule(const FullName &name, const Module &module) const;
const Variable *findVariableInModule(const FullName &name, const Module &module) const;
static const Struct *findStructInModule(const FullName &name, const Module &module);
static const Variable *findVariableInModule(const FullName &name, const Module &module);
private:
QString fullpath_;

View file

@ -157,13 +157,14 @@ Application::~Application() {
}
void Application::run() {
Fonts::Start();
style::internal::StartFonts();
ThirdParty::start();
Global::start();
refreshGlobalProxy(); // Depends on Global::started().
startLocalStorage();
ValidateScale();
if (Local::oldSettingsVersion() < AppVersion) {
psNewVersion();
@ -178,7 +179,7 @@ void Application::run() {
_translator = std::make_unique<Lang::Translator>();
QCoreApplication::instance()->installTranslator(_translator.get());
style::startManager();
style::startManager(cScale());
Ui::InitTextOptions();
Ui::Emoji::Init();
Media::Player::start(_audio.get());

View file

@ -31,7 +31,7 @@ constexpr auto kDefaultProxyPort = 80;
PreLaunchWindow *PreLaunchWindowInstance = nullptr;
PreLaunchWindow::PreLaunchWindow(QString title) {
Fonts::Start();
style::internal::StartFonts();
setWindowIcon(Window::CreateIcon());
setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
@ -74,7 +74,7 @@ PreLaunchWindow::~PreLaunchWindow() {
PreLaunchLabel::PreLaunchLabel(QWidget *parent) : QLabel(parent) {
QFont labelFont(font());
labelFont.setFamily(Fonts::GetOverride(qsl("Open Sans Semibold")));
labelFont.setFamily(style::internal::GetFontOverride(qsl("Open Sans Semibold")));
labelFont.setPixelSize(static_cast<PreLaunchWindow*>(parent)->basicSize());
setFont(labelFont);
@ -92,7 +92,7 @@ void PreLaunchLabel::setText(const QString &text) {
PreLaunchInput::PreLaunchInput(QWidget *parent, bool password) : QLineEdit(parent) {
QFont logFont(font());
logFont.setFamily(Fonts::GetOverride(qsl("Open Sans")));
logFont.setFamily(style::internal::GetFontOverride(qsl("Open Sans")));
logFont.setPixelSize(static_cast<PreLaunchWindow*>(parent)->basicSize());
setFont(logFont);
@ -110,7 +110,7 @@ PreLaunchInput::PreLaunchInput(QWidget *parent, bool password) : QLineEdit(paren
PreLaunchLog::PreLaunchLog(QWidget *parent) : QTextEdit(parent) {
QFont logFont(font());
logFont.setFamily(Fonts::GetOverride(qsl("Open Sans")));
logFont.setFamily(style::internal::GetFontOverride(qsl("Open Sans")));
logFont.setPixelSize(static_cast<PreLaunchWindow*>(parent)->basicSize());
setFont(logFont);
@ -132,7 +132,7 @@ PreLaunchButton::PreLaunchButton(QWidget *parent, bool confirm) : QPushButton(pa
setObjectName(confirm ? "confirm" : "cancel");
QFont closeFont(font());
closeFont.setFamily(Fonts::GetOverride(qsl("Open Sans Semibold")));
closeFont.setFamily(style::internal::GetFontOverride(qsl("Open Sans Semibold")));
closeFont.setPixelSize(static_cast<PreLaunchWindow*>(parent)->basicSize());
setFont(closeFont);
@ -151,7 +151,7 @@ PreLaunchCheckbox::PreLaunchCheckbox(QWidget *parent) : QCheckBox(parent) {
setCheckState(Qt::Checked);
QFont closeFont(font());
closeFont.setFamily(Fonts::GetOverride(qsl("Open Sans Semibold")));
closeFont.setFamily(style::internal::GetFontOverride(qsl("Open Sans Semibold")));
closeFont.setPixelSize(static_cast<PreLaunchWindow*>(parent)->basicSize());
setFont(closeFont);

View file

@ -445,7 +445,7 @@ void Launcher::processArguments() {
if (scaleKey.size() > 0) {
const auto value = scaleKey[0].toInt();
gConfigScale = ((value < 75) || (value > 300))
? kInterfaceScaleAuto
? style::kScaleAuto
: value;
}
}

View file

@ -190,9 +190,8 @@ void Sandbox::setupScreenScale() {
LOG(("Environmental variables: QT_AUTO_SCREEN_SCALE_FACTOR='%1'").arg(QString::fromLatin1(qgetenv("QT_AUTO_SCREEN_SCALE_FACTOR"))));
LOG(("Environmental variables: QT_SCREEN_SCALE_FACTORS='%1'").arg(QString::fromLatin1(qgetenv("QT_SCREEN_SCALE_FACTORS"))));
}
cSetRetinaFactor(ratio);
cSetIntRetinaFactor(int32(ratio));
cSetScreenScale(kInterfaceScaleDefault);
style::SetDevicePixelRatio(int(ratio));
cSetScreenScale(style::kScaleDefault);
}
}

View file

@ -1075,7 +1075,7 @@ void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
if (!_pressed || _dragging || _state != WidgetState::Default) {
return;
} else if (qAbs(localPosition.y() - _dragStart.y())
< ConvertScale(kStartReorderThreshold)) {
< style::ConvertScale(kStartReorderThreshold)) {
return;
}
_dragging = _pressed;

View file

@ -7,3 +7,4 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "export/export_pch.h"
// Precompiled header helper.

View file

@ -124,8 +124,8 @@ QSize Document::countOptimalSize() {
auto thumbed = Get<HistoryDocumentThumbed>();
if (thumbed) {
_data->loadThumbnail(_realParent->fullId());
auto tw = ConvertScale(_data->thumbnail()->width());
auto th = ConvertScale(_data->thumbnail()->height());
auto tw = style::ConvertScale(_data->thumbnail()->width());
auto th = style::ConvertScale(_data->thumbnail()->height());
if (tw > th) {
thumbed->_thumbw = (tw * st::msgFileThumbSize) / th;
} else {

View file

@ -76,7 +76,7 @@ QSize Gif::countOptimalSize() {
const auto maxSize = _data->isVideoMessage()
? st::maxVideoMessageSize
: st::maxGifSize;
const auto size = ConvertScale(videoSize());
const auto size = style::ConvertScale(videoSize());
auto tw = size.width();
auto th = size.height();
if (tw > maxSize) {
@ -125,7 +125,7 @@ QSize Gif::countCurrentSize(int newWidth) {
const auto maxSize = _data->isVideoMessage()
? st::maxVideoMessageSize
: st::maxGifSize;
const auto size = ConvertScale(videoSize());
const auto size = style::ConvertScale(videoSize());
auto tw = size.width();
auto th = size.height();
if (tw > maxSize) {

View file

@ -70,8 +70,8 @@ QSize Photo::countOptimalSize() {
auto maxWidth = 0;
auto minHeight = 0;
auto tw = ConvertScale(_data->width());
auto th = ConvertScale(_data->height());
auto tw = style::ConvertScale(_data->width());
auto th = style::ConvertScale(_data->height());
if (!tw || !th) {
tw = th = 1;
}
@ -102,7 +102,8 @@ QSize Photo::countOptimalSize() {
}
QSize Photo::countCurrentSize(int newWidth) {
int tw = ConvertScale(_data->width()), th = ConvertScale(_data->height());
auto tw = style::ConvertScale(_data->width());
auto th = style::ConvertScale(_data->height());
if (tw > st::maxMediaSize) {
th = (st::maxMediaSize * th) / tw;
tw = st::maxMediaSize;
@ -511,8 +512,8 @@ void Photo::validateGroupedCache(
return;
}
const auto originalWidth = ConvertScale(_data->width());
const auto originalHeight = ConvertScale(_data->height());
const auto originalWidth = style::ConvertScale(_data->width());
const auto originalHeight = style::ConvertScale(_data->height());
const auto pixSize = Ui::GetImageScaleSizeForGeometry(
{ originalWidth, originalHeight },
{ width, height });

View file

@ -56,8 +56,8 @@ QSize ThemeDocument::countOptimalSize() {
if (_data->isTheme()) {
return st::historyThemeSize;
}
auto tw = ConvertScale(_data->thumbnail()->width());
auto th = ConvertScale(_data->thumbnail()->height());
auto tw = style::ConvertScale(_data->thumbnail()->width());
auto th = style::ConvertScale(_data->thumbnail()->height());
if (!tw || !th) {
tw = th = 1;
}
@ -78,8 +78,8 @@ QSize ThemeDocument::countCurrentSize(int newWidth) {
_pixh = st::historyThemeSize.height();
return st::historyThemeSize;
}
auto tw = ConvertScale(_data->thumbnail()->width());
auto th = ConvertScale(_data->thumbnail()->height());
auto tw = style::ConvertScale(_data->thumbnail()->width());
auto th = style::ConvertScale(_data->thumbnail()->height());
if (!tw || !th) {
tw = th = 1;
}
@ -215,8 +215,8 @@ void ThemeDocument::prepareThumbnailFrom(
? Images::Option::TransparentBackground
: Images::Option(0));
auto original = image->original();
auto tw = isTheme ? _pixw : ConvertScale(_data->thumbnail()->width());
auto th = isTheme ? _pixh : ConvertScale(_data->thumbnail()->height());
auto tw = isTheme ? _pixw : style::ConvertScale(_data->thumbnail()->width());
auto th = isTheme ? _pixh : style::ConvertScale(_data->thumbnail()->height());
if (!tw || !th) {
tw = th = 1;
}

View file

@ -57,7 +57,7 @@ QSize Video::sizeForAspectRatio() const {
}
QSize Video::countOptimalDimensions() const {
const auto desired = ConvertScale(_data->dimensions);
const auto desired = style::ConvertScale(_data->dimensions);
const auto size = desired.isEmpty() ? sizeForAspectRatio() : desired;
auto tw = size.width();
auto th = size.height();
@ -554,8 +554,8 @@ void Video::validateGroupedCache(
}
const auto original = sizeForAspectRatio();
const auto originalWidth = ConvertScale(original.width());
const auto originalHeight = ConvertScale(original.height());
const auto originalWidth = style::ConvertScale(original.width());
const auto originalHeight = style::ConvertScale(original.height());
const auto pixSize = Ui::GetImageScaleSizeForGeometry(
{ originalWidth, originalHeight },
{ width, height });

View file

@ -446,8 +446,8 @@ void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim
auto pw = qMax(_pixw, lineHeight);
auto ph = _pixh;
auto pixw = _pixw, pixh = articleThumbHeight(_data->photo, _pixw);
const auto maxw = ConvertScale(_data->photo->thumbnail()->width());
const auto maxh = ConvertScale(_data->photo->thumbnail()->height());
const auto maxw = style::ConvertScale(_data->photo->thumbnail()->width());
const auto maxh = style::ConvertScale(_data->photo->thumbnail()->height());
if (pixw * ph != pixh * pw) {
float64 coef = (pixw * ph > pixh * pw) ? qMin(ph / float64(pixh), maxh / float64(pixh)) : qMin(pw / float64(pixw), maxw / float64(pixw));
pixh = qRound(pixh * coef);

View file

@ -55,7 +55,7 @@ int FileBase::content_width() const {
return document->dimensions.width();
}
if (const auto thumb = document->thumbnail()) {
return ConvertScale(thumb->width());
return style::ConvertScale(thumb->width());
}
}
return 0;
@ -67,7 +67,7 @@ int FileBase::content_height() const {
return document->dimensions.height();
}
if (const auto thumb = document->thumbnail()) {
return ConvertScale(thumb->height());
return style::ConvertScale(thumb->height());
}
}
return 0;
@ -720,7 +720,8 @@ void Video::prepareThumbnail(QSize size) const {
if (_thumb.size() != size * cIntRetinaFactor()) {
const auto width = size.width();
const auto height = size.height();
int32 w = qMax(ConvertScale(thumb->width()), 1), h = qMax(ConvertScale(thumb->height()), 1);
auto w = qMax(style::ConvertScale(thumb->width()), 1);
auto h = qMax(style::ConvertScale(thumb->height()), 1);
if (w * height > h * width) {
if (height < h) {
w = w * height / h;
@ -1034,7 +1035,8 @@ void Contact::prepareThumbnail(int width, int height) const {
const auto origin = fileOrigin();
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
int w = qMax(ConvertScale(thumb->width()), 1), h = qMax(ConvertScale(thumb->height()), 1);
auto w = qMax(style::ConvertScale(thumb->width()), 1);
auto h = qMax(style::ConvertScale(thumb->height()), 1);
if (w * height > h * width) {
if (height < h) {
w = w * height / h;
@ -1183,7 +1185,8 @@ void Article::prepareThumbnail(int width, int height) const {
const auto origin = fileOrigin();
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
int w = qMax(ConvertScale(thumb->width()), 1), h = qMax(ConvertScale(thumb->height()), 1);
auto w = qMax(style::ConvertScale(thumb->width()), 1);
auto h = qMax(style::ConvertScale(thumb->height()), 1);
if (w * height > h * width) {
if (height < h) {
w = w * height / h;
@ -1371,8 +1374,8 @@ void Game::validateThumbnail(Image *image, QSize size, bool good) const {
}
const auto width = size.width();
const auto height = size.height();
auto w = qMax(ConvertScale(image->width()), 1);
auto h = qMax(ConvertScale(image->height()), 1);
auto w = qMax(style::ConvertScale(image->width()), 1);
auto h = qMax(style::ConvertScale(image->height()), 1);
auto resizeByHeight1 = (w * height > h * width) && (h >= height);
auto resizeByHeight2 = (h * width >= w * height) && (w < width);
if (resizeByHeight1 || resizeByHeight2) {

View file

@ -1800,8 +1800,8 @@ void OverlayWidget::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item)
_blurred = true;
_current = QPixmap();
_down = OverNone;
_w = ConvertScale(photo->width());
_h = ConvertScale(photo->height());
_w = style::ConvertScale(photo->width());
_h = style::ConvertScale(photo->height());
contentSizeChanged();
refreshFromLabel(item);
_photo->download(fileOrigin());
@ -1937,10 +1937,10 @@ void OverlayWidget::displayDocument(
updateThemePreviewGeometry();
} else if (!_current.isNull()) {
_current.setDevicePixelRatio(cRetinaFactor());
_w = ConvertScale(_current.width());
_h = ConvertScale(_current.height());
_w = style::ConvertScale(_current.width());
_h = style::ConvertScale(_current.height());
} else if (videoShown()) {
const auto contentSize = ConvertScale(videoSize());
const auto contentSize = style::ConvertScale(videoSize());
_w = contentSize.width();
_h = contentSize.height();
}
@ -2059,7 +2059,7 @@ void OverlayWidget::streamingReady(Streaming::Information &&info) {
_streamed->info = std::move(info);
validateStreamedGoodThumbnail();
if (videoShown()) {
const auto contentSize = ConvertScale(videoSize());
const auto contentSize = style::ConvertScale(videoSize());
if (contentSize != QSize(_width, _height)) {
update(contentRect());
_w = contentSize.width();
@ -3088,7 +3088,7 @@ void OverlayWidget::setZoomLevel(int newZoom) {
float64 nx, ny, z = (_zoom == ZoomToScreenLevel) ? _zoomToScreen : _zoom;
const auto contentSize = videoShown()
? ConvertScale(videoSize())
? style::ConvertScale(videoSize())
: QSize(_width, _height);
_w = contentSize.width();
_h = contentSize.height();

View file

@ -7,3 +7,4 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "mtproto/mtp_pch.h"
// Precompiled header helper.

View file

@ -892,7 +892,8 @@ Document::Document(
if (withThumb()) {
_data->loadThumbnail(parent->fullId());
int32 tw = ConvertScale(_data->thumbnail()->width()), th = ConvertScale(_data->thumbnail()->height());
auto tw = style::ConvertScale(_data->thumbnail()->width());
auto th = style::ConvertScale(_data->thumbnail()->height());
if (tw > th) {
_thumbw = (tw * _st.fileThumbSize) / th;
} else {
@ -1407,13 +1408,13 @@ Link::Link(
_page->photo->loadThumbnailSmall(parent->fullId());
}
tw = ConvertScale(_page->photo->width());
th = ConvertScale(_page->photo->height());
tw = style::ConvertScale(_page->photo->width());
th = style::ConvertScale(_page->photo->height());
} else if (_page && _page->document && _page->document->hasThumbnail()) {
_page->document->loadThumbnail(parent->fullId());
tw = ConvertScale(_page->document->thumbnail()->width());
th = ConvertScale(_page->document->thumbnail()->height());
tw = style::ConvertScale(_page->document->thumbnail()->width());
th = style::ConvertScale(_page->document->thumbnail()->height());
}
if (tw > st::linksPhotoSize) {
if (th > tw) {

View file

@ -376,13 +376,15 @@ void AppendFavedStickers(std::vector<PickerScrubberItem> &to) {
void AppendEmojiPacks(std::vector<PickerScrubberItem> &to) {
for (auto i = 0; i != ChatHelpers::kEmojiSectionCount; ++i) {
const auto section = Ui::Emoji::GetSection(
static_cast<Ui::Emoji::Section>(i));
const auto title = i
? Ui::Emoji::CategoryTitle(i)(tr::now)
: TitleRecentlyUsed();
const auto section = static_cast<Ui::Emoji::Section>(i);
const auto list = (section == Ui::Emoji::Section::Recent)
? GetRecentEmojiSection()
: Ui::Emoji::GetSection(section);
const auto title = (section == Ui::Emoji::Section::Recent)
? TitleRecentlyUsed()
: ChatHelpers::EmojiCategoryTitle(i)(tr::now);
to.emplace_back(title);
for (const auto &emoji : section) {
for (const auto &emoji : list) {
to.emplace_back(PickerScrubberItem(emoji));
}
}

View file

@ -7,8 +7,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "settings.h"
bool gRtl = false;
Qt::LayoutDirection gLangDir = gRtl ? Qt::RightToLeft : Qt::LeftToRight;
#include "ui/emoji_config.h"
namespace {
constexpr auto kRecentEmojiLimit = 42;
auto UpdatesRecentEmoji = rpl::event_stream<>();
} // namespace
Qt::LayoutDirection gLangDir = Qt::LeftToRight;
bool gInstallBetaVersion = AppBetaVersion;
uint64 gAlphaVersion = AppAlphaVersion;
@ -44,8 +53,8 @@ uint32 gConnectionsInSession = 1;
QString gLoggedPhoneNumber;
QByteArray gLocalSalt;
int gScreenScale = kInterfaceScaleAuto;
int gConfigScale = kInterfaceScaleAuto;
int gScreenScale = style::kScaleAuto;
int gConfigScale = style::kScaleAuto;
QString gTimeFormat = qsl("hh:mm");
@ -72,3 +81,128 @@ int gOtherOnline = 0;
int32 gAutoDownloadPhoto = 0; // all auto download
int32 gAutoDownloadAudio = 0;
int32 gAutoDownloadGif = 0;
RecentEmojiPack &GetRecentEmoji() {
if (cRecentEmoji().isEmpty()) {
RecentEmojiPack result;
auto haveAlready = [&result](EmojiPtr emoji) {
for (auto &row : result) {
if (row.first->id() == emoji->id()) {
return true;
}
}
return false;
};
if (!cRecentEmojiPreload().isEmpty()) {
auto preload = cRecentEmojiPreload();
cSetRecentEmojiPreload(RecentEmojiPreload());
result.reserve(preload.size());
for (auto i = preload.cbegin(), e = preload.cend(); i != e; ++i) {
if (auto emoji = Ui::Emoji::Find(i->first)) {
if (!haveAlready(emoji)) {
result.push_back(qMakePair(emoji, i->second));
}
}
}
}
const auto defaultRecent = {
0xD83DDE02LLU,
0xD83DDE18LLU,
0x2764LLU,
0xD83DDE0DLLU,
0xD83DDE0ALLU,
0xD83DDE01LLU,
0xD83DDC4DLLU,
0x263ALLU,
0xD83DDE14LLU,
0xD83DDE04LLU,
0xD83DDE2DLLU,
0xD83DDC8BLLU,
0xD83DDE12LLU,
0xD83DDE33LLU,
0xD83DDE1CLLU,
0xD83DDE48LLU,
0xD83DDE09LLU,
0xD83DDE03LLU,
0xD83DDE22LLU,
0xD83DDE1DLLU,
0xD83DDE31LLU,
0xD83DDE21LLU,
0xD83DDE0FLLU,
0xD83DDE1ELLU,
0xD83DDE05LLU,
0xD83DDE1ALLU,
0xD83DDE4ALLU,
0xD83DDE0CLLU,
0xD83DDE00LLU,
0xD83DDE0BLLU,
0xD83DDE06LLU,
0xD83DDC4CLLU,
0xD83DDE10LLU,
0xD83DDE15LLU,
};
for (const auto emoji : Ui::Emoji::GetDefaultRecent()) {
if (result.size() >= kRecentEmojiLimit) break;
if (!haveAlready(emoji)) {
result.push_back(qMakePair(emoji, 1));
}
}
cSetRecentEmoji(result);
}
return cRefRecentEmoji();
}
EmojiPack GetRecentEmojiSection() {
const auto &recent = GetRecentEmoji();
auto result = EmojiPack();
result.reserve(recent.size());
for (const auto &item : recent) {
result.push_back(item.first);
}
return result;
}
void AddRecentEmoji(EmojiPtr emoji) {
auto &recent = GetRecentEmoji();
auto i = recent.begin(), e = recent.end();
for (; i != e; ++i) {
if (i->first == emoji) {
++i->second;
if (i->second > 0x8000) {
for (auto j = recent.begin(); j != e; ++j) {
if (j->second > 1) {
j->second /= 2;
} else {
j->second = 1;
}
}
}
for (; i != recent.begin(); --i) {
if ((i - 1)->second > i->second) {
break;
}
std::swap(*i, *(i - 1));
}
break;
}
}
if (i == e) {
while (recent.size() >= kRecentEmojiLimit) {
recent.pop_back();
}
recent.push_back(qMakePair(emoji, 1));
for (i = recent.end() - 1; i != recent.begin(); --i) {
if ((i - 1)->second > i->second) {
break;
}
std::swap(*i, *(i - 1));
}
}
UpdatesRecentEmoji.fire({});
}
rpl::producer<> UpdatedRecentEmoji() {
return UpdatesRecentEmoji.events();
}

View file

@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/style/style_core.h"
#include "emoji.h"
#define DeclareReadSetting(Type, Name) extern Type g##Name; \
inline const Type &c##Name() { \
return g##Name; \
@ -22,10 +25,9 @@ inline Type &cRef##Name() { \
return g##Name; \
}
DeclareSetting(bool, Rtl);
DeclareSetting(Qt::LayoutDirection, LangDir);
inline bool rtl() {
return cRtl();
return style::RightToLeft();
}
DeclareSetting(bool, InstallBetaVersion);
@ -94,19 +96,6 @@ DeclareSetting(int, ScreenScale);
DeclareSetting(int, ConfigScale);
DeclareSetting(QString, TimeFormat);
inline void cChangeTimeFormat(const QString &newFormat) {
if (!newFormat.isEmpty()) cSetTimeFormat(newFormat);
}
namespace Ui {
namespace Emoji {
class One;
} // namespace Emoji
} // namespace Ui
using EmojiPtr = const Ui::Emoji::One*;
using EmojiPack = QVector<EmojiPtr>;
using RecentEmojiPreloadOldOld = QVector<QPair<uint32, ushort>>;
using RecentEmojiPreloadOld = QVector<QPair<uint64, ushort>>;
using RecentEmojiPreload = QVector<QPair<QString, ushort>>;
@ -138,6 +127,20 @@ DeclareSetting(bool, PasswordRecovered);
DeclareSetting(int32, PasscodeBadTries);
DeclareSetting(crl::time, PasscodeLastTry);
DeclareSetting(QStringList, SendPaths);
DeclareSetting(QString, StartUrl);
DeclareSetting(int, OtherOnline);
inline void cChangeTimeFormat(const QString &newFormat) {
if (!newFormat.isEmpty()) cSetTimeFormat(newFormat);
}
RecentEmojiPack &GetRecentEmoji();
QVector<EmojiPtr> GetRecentEmojiSection();
void AddRecentEmoji(EmojiPtr emoji);
[[nodiscard]] rpl::producer<> UpdatedRecentEmoji();
inline bool passcodeCanTry() {
if (cPasscodeBadTries() < 3) return true;
auto dt = crl::now() - cPasscodeLastTry();
@ -151,46 +154,27 @@ inline bool passcodeCanTry() {
return dt >= 30000;
}
DeclareSetting(QStringList, SendPaths);
DeclareSetting(QString, StartUrl);
inline float64 cRetinaFactor() {
return style::DevicePixelRatio();
}
DeclareSetting(float64, RetinaFactor);
DeclareSetting(int32, IntRetinaFactor);
DeclareSetting(int, OtherOnline);
constexpr auto kInterfaceScaleAuto = 0;
constexpr auto kInterfaceScaleMin = 100;
constexpr auto kInterfaceScaleDefault = 100;
constexpr auto kInterfaceScaleMax = 300;
inline int32 cIntRetinaFactor() {
return style::DevicePixelRatio();
}
inline int cEvalScale(int scale) {
return (scale == kInterfaceScaleAuto) ? cScreenScale() : scale;
return (scale == style::kScaleAuto) ? cScreenScale() : scale;
}
inline int cScale() {
return cEvalScale(cConfigScale());
}
template <typename T>
inline T ConvertScale(T value, int scale) {
return (value < 0.)
? (-ConvertScale(-value, scale))
: T(std::round((float64(value) * scale / 100.) - 0.01));
}
template <typename T>
inline T ConvertScale(T value) {
return ConvertScale(value, cScale());
}
inline QSize ConvertScale(QSize size) {
return QSize(ConvertScale(size.width()), ConvertScale(size.height()));
return style::Scale();
}
inline void SetScaleChecked(int scale) {
const auto checked = (scale == kInterfaceScaleAuto)
? kInterfaceScaleAuto
: snap(scale, kInterfaceScaleMin, kInterfaceScaleMax / cIntRetinaFactor());
cSetConfigScale(checked);
cSetConfigScale(style::CheckScale(scale));
}
inline void ValidateScale() {
SetScaleChecked(cConfigScale());
style::SetScale(cEvalScale(cConfigScale()));
}

View file

@ -124,7 +124,7 @@ void SetupInterfaceScale(
const auto toggled = Ui::CreateChild<rpl::event_stream<bool>>(
container.get());
const auto switched = (cConfigScale() == kInterfaceScaleAuto);
const auto switched = (cConfigScale() == style::kScaleAuto);
const auto button = AddButton(
container,
tr::lng_settings_default_scale(),
@ -140,7 +140,7 @@ void SetupInterfaceScale(
auto values = (cIntRetinaFactor() > 1)
? std::vector<int>{ 100, 110, 120, 130, 140, 150 }
: std::vector<int>{ 100, 125, 150, 200, 250, 300 };
if (cConfigScale() == kInterfaceScaleAuto) {
if (cConfigScale() == style::kScaleAuto) {
return values;
}
if (ranges::find(values, cConfigScale()) == end(values)) {
@ -167,7 +167,7 @@ void SetupInterfaceScale(
*inSetScale = true;
const auto guard = gsl::finally([=] { *inSetScale = false; });
toggled->fire(scale == kInterfaceScaleAuto);
toggled->fire(scale == style::kScaleAuto);
slider->setActiveSection(sectionFromScale(scale));
if (cEvalScale(scale) != cEvalScale(cConfigScale())) {
const auto confirmed = crl::guard(button, [=] {
@ -208,13 +208,13 @@ void SetupInterfaceScale(
return scaleByIndex(section);
}) | rpl::start_with_next([=](int scale) {
(*setScale)((scale == cScreenScale())
? kInterfaceScaleAuto
? style::kScaleAuto
: scale);
}, slider->lifetime());
button->toggledValue(
) | rpl::map([](bool checked) {
return checked ? kInterfaceScaleAuto : cEvalScale(cConfigScale());
return checked ? style::kScaleAuto : cEvalScale(cConfigScale());
}) | rpl::start_with_next([=](int scale) {
(*setScale)(scale);
}, button->lifetime());

View file

@ -99,9 +99,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <range/v3/all.hpp>
// Ensures/Expects.
#include <gsl/gsl_assert>
// Redefine Ensures/Expects by our own assertions.
#include "base/assertion.h"

View file

@ -1476,7 +1476,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
constexpr auto kOneAndHalf = 3;
constexpr auto kTwo = 4;
switch (v) {
case kAuto: return kInterfaceScaleAuto;
case kAuto: return style::kScaleAuto;
case kOne: return 100;
case kOneAndQuarter: return 125;
case kOneAndHalf: return 150;
@ -1492,7 +1492,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
if (!_checkStreamStatus(stream)) return false;
// If cConfigScale() has value then it was set via command line.
if (cConfigScale() == kInterfaceScaleAuto) {
if (cConfigScale() == style::kScaleAuto) {
SetScaleChecked(v);
}
} break;
@ -2045,8 +2045,8 @@ void _writeUserSettings() {
auto recentEmojiPreloadData = cRecentEmojiPreload();
if (recentEmojiPreloadData.isEmpty()) {
recentEmojiPreloadData.reserve(Ui::Emoji::GetRecent().size());
for (auto &item : Ui::Emoji::GetRecent()) {
recentEmojiPreloadData.reserve(GetRecentEmoji().size());
for (auto &item : GetRecentEmoji()) {
recentEmojiPreloadData.push_back(qMakePair(item.first->id(), item.second));
}
}

View file

@ -90,7 +90,7 @@ bool PrepareAlbumMediaIsWaiting(
if (ValidPhotoForAlbum(*image, file.mime)) {
file.shownDimensions = PrepareShownDimensions(image->data);
file.preview = Images::prepareOpaque(image->data.scaledToWidth(
std::min(previewWidth, ConvertScale(image->data.width()))
std::min(previewWidth, style::ConvertScale(image->data.width()))
* cIntRetinaFactor(),
Qt::SmoothTransformation));
Assert(!file.preview.isNull());

View file

@ -7,3 +7,4 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "storage/storage_pch.h"
// Precompiled header helper.

View file

@ -164,7 +164,7 @@ TG_FORCE_INLINE Shifted non_premultiplied(QColor color) {
}
TG_FORCE_INLINE QColor color(QColor a, QColor b, float64 b_ratio) {
auto bOpacity = snap(interpolate(0, 255, b_ratio), 0, 255) + 1;
auto bOpacity = std::clamp(interpolate(0, 255, b_ratio), 0, 255) + 1;
auto aOpacity = (256 - bOpacity);
auto components = (non_premultiplied(a) * aOpacity + non_premultiplied(b) * bOpacity);
return {

View file

@ -24,7 +24,6 @@ namespace Ui {
namespace Emoji {
namespace {
constexpr auto kSaveRecentEmojiTimeout = 3000;
constexpr auto kUniversalSize = 72;
constexpr auto kImagesPerRow = 32;
constexpr auto kImageRowsPerSprite = 16;
@ -74,7 +73,6 @@ auto InstanceLarge = std::unique_ptr<Instance>();
auto Universal = std::shared_ptr<UniversalImages>();
auto CanClearUniversal = false;
auto Updates = rpl::event_stream<>();
auto UpdatesRecent = rpl::event_stream<>();
#if defined Q_OS_MAC && !defined OS_MAC_OLD
auto TouchbarSize = -1;
@ -345,47 +343,6 @@ std::vector<QImage> LoadAndValidateSprites(int id) {
return result;
}
void AppendPartToResult(TextWithEntities &result, const QChar *start, const QChar *from, const QChar *to) {
if (to <= from) {
return;
}
for (auto &entity : result.entities) {
if (entity.offset() >= to - start) break;
if (entity.offset() + entity.length() < from - start) continue;
if (entity.offset() >= from - start) {
entity.extendToLeft(from - start - result.text.size());
}
if (entity.offset() + entity.length() <= to - start) {
entity.shrinkFromRight(from - start - result.text.size());
}
}
result.text.append(from, to - from);
}
bool IsReplacementPart(ushort ch) {
return (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '-') || (ch == '+') || (ch == '_');
}
EmojiPtr FindReplacement(const QChar *start, const QChar *end, int *outLength) {
if (start != end && *start == ':') {
auto maxLength = GetSuggestionMaxLength();
for (auto till = start + 1; till != end; ++till) {
if (*till == ':') {
auto text = QString::fromRawData(start, till + 1 - start);
auto emoji = GetSuggestionEmoji(QStringToUTF16(text));
auto result = Find(QStringFromUTF16(emoji));
if (result) {
if (outLength) *outLength = (till + 1 - start);
}
return result;
} else if (!IsReplacementPart(till->unicode()) || (till - start) > maxLength) {
break;
}
}
}
return internal::FindReplace(start, end, outLength);
}
void ClearUniversalChecked() {
Expects(InstanceNormal != nullptr && InstanceLarge != nullptr);
@ -513,8 +470,8 @@ void Init() {
const auto persprite = kImagesPerRow * kImageRowsPerSprite;
SpritesCount = (count / persprite) + ((count % persprite) ? 1 : 0);
SizeNormal = ConvertScale(18, cScale() * cIntRetinaFactor());
SizeLarge = int(ConvertScale(18 * 4 / 3., cScale() * cIntRetinaFactor()));
SizeNormal = style::ConvertScale(18, cScale() * cIntRetinaFactor());
SizeLarge = int(style::ConvertScale(18 * 4 / 3., cScale() * cIntRetinaFactor()));
Universal = std::make_shared<UniversalImages>(ReadCurrentSetId());
CanClearUniversal = false;
@ -523,7 +480,7 @@ void Init() {
#if defined Q_OS_MAC && !defined OS_MAC_OLD
if (cScale() != kScaleForTouchBar) {
TouchbarSize = int(ConvertScale(18 * 4 / 3.,
TouchbarSize = int(style::ConvertScale(18 * 4 / 3.,
kScaleForTouchBar * cIntRetinaFactor()));
TouchbarInstance = std::make_unique<Instance>(TouchbarSize);
TouchbarEmoji = TouchbarInstance.get();
@ -566,19 +523,6 @@ void ClearIrrelevantCache() {
});
}
tr::phrase<> CategoryTitle(int index) {
switch (index) {
case 1: return tr::lng_emoji_category1;
case 2: return tr::lng_emoji_category2;
case 3: return tr::lng_emoji_category3;
case 4: return tr::lng_emoji_category4;
case 5: return tr::lng_emoji_category5;
case 6: return tr::lng_emoji_category6;
case 7: return tr::lng_emoji_category7;
}
Unexpected("Index in CategoryTitle.");
}
std::vector<Set> Sets() {
return kSets | ranges::to_vector;
}
@ -714,173 +658,50 @@ QString IdFromOldKey(uint64 oldKey) {
return QString();
}
void ReplaceInText(TextWithEntities &result) {
auto newText = TextWithEntities();
newText.entities = std::move(result.entities);
auto currentEntity = newText.entities.begin();
auto entitiesEnd = newText.entities.end();
auto emojiStart = result.text.constData();
auto emojiEnd = emojiStart;
auto end = emojiStart + result.text.size();
auto canFindEmoji = true;
for (auto ch = emojiEnd; ch != end;) {
auto emojiLength = 0;
auto emoji = canFindEmoji ? FindReplacement(ch, end, &emojiLength) : nullptr;
auto newEmojiEnd = ch + emojiLength;
while (currentEntity != entitiesEnd && ch >= emojiStart + currentEntity->offset() + currentEntity->length()) {
++currentEntity;
}
if (emoji &&
(ch == emojiStart || !ch->isLetterOrNumber() || !(ch - 1)->isLetterOrNumber()) &&
(newEmojiEnd == end || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) &&
(currentEntity == entitiesEnd || (ch < emojiStart + currentEntity->offset() && newEmojiEnd <= emojiStart + currentEntity->offset()) || (ch >= emojiStart + currentEntity->offset() + currentEntity->length() && newEmojiEnd > emojiStart + currentEntity->offset() + currentEntity->length()))
) {
if (newText.text.isEmpty()) newText.text.reserve(result.text.size());
AppendPartToResult(newText, emojiStart, emojiEnd, ch);
if (emoji->hasVariants()) {
auto it = cEmojiVariants().constFind(emoji->nonColoredId());
if (it != cEmojiVariants().cend()) {
emoji = emoji->variant(it.value());
}
}
newText.text.append(emoji->text());
ch = emojiEnd = newEmojiEnd;
canFindEmoji = true;
} else {
if (internal::IsReplaceEdge(ch)) {
canFindEmoji = true;
} else {
canFindEmoji = false;
}
++ch;
QVector<EmojiPtr> GetDefaultRecent() {
const auto defaultRecent = {
0xD83DDE02LLU,
0xD83DDE18LLU,
0x2764LLU,
0xD83DDE0DLLU,
0xD83DDE0ALLU,
0xD83DDE01LLU,
0xD83DDC4DLLU,
0x263ALLU,
0xD83DDE14LLU,
0xD83DDE04LLU,
0xD83DDE2DLLU,
0xD83DDC8BLLU,
0xD83DDE12LLU,
0xD83DDE33LLU,
0xD83DDE1CLLU,
0xD83DDE48LLU,
0xD83DDE09LLU,
0xD83DDE03LLU,
0xD83DDE22LLU,
0xD83DDE1DLLU,
0xD83DDE31LLU,
0xD83DDE21LLU,
0xD83DDE0FLLU,
0xD83DDE1ELLU,
0xD83DDE05LLU,
0xD83DDE1ALLU,
0xD83DDE4ALLU,
0xD83DDE0CLLU,
0xD83DDE00LLU,
0xD83DDE0BLLU,
0xD83DDE06LLU,
0xD83DDC4CLLU,
0xD83DDE10LLU,
0xD83DDE15LLU,
};
auto result = QVector<EmojiPtr>();
for (const auto oldKey : defaultRecent) {
if (const auto emoji = Ui::Emoji::FromOldKey(oldKey)) {
result.push_back(emoji);
}
}
if (newText.text.isEmpty()) {
result.entities = std::move(newText.entities);
} else {
AppendPartToResult(newText, emojiStart, emojiEnd, end);
result = std::move(newText);
}
}
RecentEmojiPack &GetRecent() {
if (cRecentEmoji().isEmpty()) {
RecentEmojiPack result;
auto haveAlready = [&result](EmojiPtr emoji) {
for (auto &row : result) {
if (row.first->id() == emoji->id()) {
return true;
}
}
return false;
};
if (!cRecentEmojiPreload().isEmpty()) {
auto preload = cRecentEmojiPreload();
cSetRecentEmojiPreload(RecentEmojiPreload());
result.reserve(preload.size());
for (auto i = preload.cbegin(), e = preload.cend(); i != e; ++i) {
if (auto emoji = Ui::Emoji::Find(i->first)) {
if (!haveAlready(emoji)) {
result.push_back(qMakePair(emoji, i->second));
}
}
}
}
auto defaultRecent = {
0xD83DDE02LLU,
0xD83DDE18LLU,
0x2764LLU,
0xD83DDE0DLLU,
0xD83DDE0ALLU,
0xD83DDE01LLU,
0xD83DDC4DLLU,
0x263ALLU,
0xD83DDE14LLU,
0xD83DDE04LLU,
0xD83DDE2DLLU,
0xD83DDC8BLLU,
0xD83DDE12LLU,
0xD83DDE33LLU,
0xD83DDE1CLLU,
0xD83DDE48LLU,
0xD83DDE09LLU,
0xD83DDE03LLU,
0xD83DDE22LLU,
0xD83DDE1DLLU,
0xD83DDE31LLU,
0xD83DDE21LLU,
0xD83DDE0FLLU,
0xD83DDE1ELLU,
0xD83DDE05LLU,
0xD83DDE1ALLU,
0xD83DDE4ALLU,
0xD83DDE0CLLU,
0xD83DDE00LLU,
0xD83DDE0BLLU,
0xD83DDE06LLU,
0xD83DDC4CLLU,
0xD83DDE10LLU,
0xD83DDE15LLU,
};
for (auto oldKey : defaultRecent) {
if (result.size() >= kRecentLimit) break;
if (auto emoji = Ui::Emoji::FromOldKey(oldKey)) {
if (!haveAlready(emoji)) {
result.push_back(qMakePair(emoji, 1));
}
}
}
cSetRecentEmoji(result);
}
return cRefRecentEmoji();
}
void AddRecent(EmojiPtr emoji) {
auto &recent = GetRecent();
auto i = recent.begin(), e = recent.end();
for (; i != e; ++i) {
if (i->first == emoji) {
++i->second;
if (i->second > 0x8000) {
for (auto j = recent.begin(); j != e; ++j) {
if (j->second > 1) {
j->second /= 2;
} else {
j->second = 1;
}
}
}
for (; i != recent.begin(); --i) {
if ((i - 1)->second > i->second) {
break;
}
qSwap(*i, *(i - 1));
}
break;
}
}
if (i == e) {
while (recent.size() >= kRecentLimit) {
recent.pop_back();
}
recent.push_back(qMakePair(emoji, 1));
for (i = recent.end() - 1; i != recent.begin(); --i) {
if ((i - 1)->second > i->second) {
break;
}
qSwap(*i, *(i - 1));
}
}
UpdatesRecent.fire({});
}
rpl::producer<> UpdatedRecent() {
return UpdatesRecent.events();
return result;
}
const QPixmap &SinglePixmap(EmojiPtr emoji, int fontHeight) {

View file

@ -7,9 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/basic_types.h"
#include "base/binary_guard.h"
#include "emoji.h"
#include "lang/lang_keys.h"
#include <QtGui/QPainter>
#include <QtGui/QPixmap>
#include <rpl/producer.h>
using EmojiPtr = const Ui::Emoji::One*;
namespace Ui {
namespace Emoji {
@ -20,8 +27,6 @@ namespace internal {
} // namespace internal
constexpr auto kRecentLimit = 42;
void Init();
void Clear();
@ -38,8 +43,6 @@ struct Set {
// Thread safe, callback is called on main thread.
void SwitchToSet(int id, Fn<void(bool)> callback);
tr::phrase<> CategoryTitle(int index);
std::vector<Set> Sets();
int CurrentSetId();
bool SetIsReady(int id);
@ -108,7 +111,7 @@ public:
}
QString toUrl() const {
return qsl("emoji://e.") + QString::number(index());
return "emoji://e." + QString::number(index());
}
private:
@ -159,10 +162,7 @@ inline int ColorIndexFromOldKey(uint64 oldKey) {
return ColorIndexFromCode(uint32(oldKey & 0xFFFFFFFFLLU));
}
void ReplaceInText(TextWithEntities &result);
RecentEmojiPack &GetRecent();
void AddRecent(EmojiPtr emoji);
rpl::producer<> UpdatedRecent();
QVector<EmojiPtr> GetDefaultRecent();
const QPixmap &SinglePixmap(EmojiPtr emoji, int fontHeight);
void Draw(QPainter &p, EmojiPtr emoji, int size, int x, int y);

View file

@ -0,0 +1,118 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "styles/style_basic.h"
#include <QtCore/QPoint>
#include <QtGui/QPainter>
class Painter : public QPainter {
public:
explicit Painter(QPaintDevice *device) : QPainter(device) {
}
void drawTextLeft(int x, int y, int outerw, const QString &text, int textWidth = -1) {
QFontMetrics m(fontMetrics());
if (style::RightToLeft() && textWidth < 0) textWidth = m.width(text);
drawText(style::RightToLeft() ? (outerw - x - textWidth) : x, y + m.ascent(), text);
}
void drawTextRight(int x, int y, int outerw, const QString &text, int textWidth = -1) {
QFontMetrics m(fontMetrics());
if (!style::RightToLeft() && textWidth < 0) textWidth = m.width(text);
drawText(style::RightToLeft() ? x : (outerw - x - textWidth), y + m.ascent(), text);
}
void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix, const QRect &from) {
drawPixmap(QPoint(style::RightToLeft() ? (outerw - x - (from.width() / pix.devicePixelRatio())) : x, y), pix, from);
}
void drawPixmapLeft(const QPoint &p, int outerw, const QPixmap &pix, const QRect &from) {
return drawPixmapLeft(p.x(), p.y(), outerw, pix, from);
}
void drawPixmapLeft(int x, int y, int w, int h, int outerw, const QPixmap &pix, const QRect &from) {
drawPixmap(QRect(style::RightToLeft() ? (outerw - x - w) : x, y, w, h), pix, from);
}
void drawPixmapLeft(const QRect &r, int outerw, const QPixmap &pix, const QRect &from) {
return drawPixmapLeft(r.x(), r.y(), r.width(), r.height(), outerw, pix, from);
}
void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix) {
drawPixmap(QPoint(style::RightToLeft() ? (outerw - x - (pix.width() / pix.devicePixelRatio())) : x, y), pix);
}
void drawPixmapLeft(const QPoint &p, int outerw, const QPixmap &pix) {
return drawPixmapLeft(p.x(), p.y(), outerw, pix);
}
void drawPixmapRight(int x, int y, int outerw, const QPixmap &pix, const QRect &from) {
drawPixmap(QPoint(style::RightToLeft() ? x : (outerw - x - (from.width() / pix.devicePixelRatio())), y), pix, from);
}
void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix, const QRect &from) {
return drawPixmapRight(p.x(), p.y(), outerw, pix, from);
}
void drawPixmapRight(int x, int y, int w, int h, int outerw, const QPixmap &pix, const QRect &from) {
drawPixmap(QRect(style::RightToLeft() ? x : (outerw - x - w), y, w, h), pix, from);
}
void drawPixmapRight(const QRect &r, int outerw, const QPixmap &pix, const QRect &from) {
return drawPixmapRight(r.x(), r.y(), r.width(), r.height(), outerw, pix, from);
}
void drawPixmapRight(int x, int y, int outerw, const QPixmap &pix) {
drawPixmap(QPoint(style::RightToLeft() ? x : (outerw - x - (pix.width() / pix.devicePixelRatio())), y), pix);
}
void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix) {
return drawPixmapRight(p.x(), p.y(), outerw, pix);
}
void setTextPalette(const style::TextPalette &palette) {
_textPalette = &palette;
}
void restoreTextPalette() {
_textPalette = nullptr;
}
const style::TextPalette &textPalette() const {
return _textPalette ? *_textPalette : st::defaultTextPalette;
}
private:
const style::TextPalette *_textPalette = nullptr;
};
class PainterHighQualityEnabler {
public:
PainterHighQualityEnabler(QPainter &p) : _painter(p) {
static constexpr QPainter::RenderHint Hints[] = {
QPainter::Antialiasing,
QPainter::SmoothPixmapTransform,
QPainter::TextAntialiasing,
QPainter::HighQualityAntialiasing
};
const auto hints = _painter.renderHints();
for (const auto hint : Hints) {
if (!(hints & hint)) {
_hints |= hint;
}
}
if (_hints) {
_painter.setRenderHints(_hints);
}
}
PainterHighQualityEnabler(
const PainterHighQualityEnabler &other) = delete;
PainterHighQualityEnabler &operator=(
const PainterHighQualityEnabler &other) = delete;
~PainterHighQualityEnabler() {
if (_hints) {
_painter.setRenderHints(_hints, false);
}
}
private:
QPainter &_painter;
QPainter::RenderHints _hints = 0;
};

View file

@ -8,6 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/style/style_core.h"
#include "ui/effects/animation_value.h"
#include "ui/painter.h"
#include "styles/style_basic.h"
#include "styles/palette.h"
#include <QtGui/QPainter>
namespace style {
namespace internal {
@ -17,52 +22,42 @@ constexpr auto kMinContrastAlpha = 64;
constexpr auto kMinContrastDistance = 64 * 64 * 4;
constexpr auto kContrastDeltaL = 64;
using ModulesList = QList<internal::ModuleBase*>;
NeverFreedPointer<ModulesList> styleModules;
int DevicePixelRatioValue = 1;
bool RightToLeftValue = false;
void startModules() {
if (!styleModules) return;
for_const (auto module, *styleModules) {
module->start();
}
std::vector<internal::ModuleBase*> &StyleModules() {
static auto result = std::vector<internal::ModuleBase*>();
return result;
}
void stopModules() {
if (!styleModules) return;
for_const (auto module, *styleModules) {
module->stop();
void startModules(int scale) {
for (const auto module : StyleModules()) {
module->start(scale);
}
}
} // namespace
void registerModule(ModuleBase *module) {
styleModules.createIfNull();
styleModules->push_back(module);
}
void unregisterModule(ModuleBase *module) {
styleModules->removeOne(module);
if (styleModules->isEmpty()) {
styleModules.clear();
}
StyleModules().push_back(module);
}
} // namespace internal
void startManager() {
if (cIntRetinaFactor() * cConfigScale() > kInterfaceScaleMax) {
cSetConfigScale(kInterfaceScaleDefault);
}
bool RightToLeft() {
return internal::RightToLeftValue;
}
internal::registerFontFamily(qsl("Open Sans"));
internal::startModules();
void SetRightToLeft(bool rtl) {
internal::RightToLeftValue = rtl;
}
void startManager(int scale) {
internal::registerFontFamily("Open Sans");
internal::startModules(scale);
}
void stopManager() {
internal::stopModules();
internal::destroyFonts();
internal::destroyIcons();
}
@ -113,15 +108,15 @@ void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect
}
QBrush transparentPlaceholderBrush() {
auto size = st::transparentPlaceholderSize * cIntRetinaFactor();
auto size = st::transparentPlaceholderSize * DevicePixelRatio();
auto transparent = QImage(2 * size, 2 * size, QImage::Format_ARGB32_Premultiplied);
transparent.fill(st::mediaviewTransparentBg->c);
{
Painter p(&transparent);
p.fillRect(rtlrect(0, size, size, size, 2 * size), st::mediaviewTransparentFg);
p.fillRect(rtlrect(size, 0, size, size, 2 * size), st::mediaviewTransparentFg);
QPainter p(&transparent);
p.fillRect(0, size, size, size, st::mediaviewTransparentFg);
p.fillRect(size, 0, size, size, st::mediaviewTransparentFg);
}
transparent.setDevicePixelRatio(cRetinaFactor());
transparent.setDevicePixelRatio(DevicePixelRatio());
return QBrush(transparent);
}
@ -129,14 +124,14 @@ QBrush transparentPlaceholderBrush() {
namespace internal {
QImage createCircleMask(int size, QColor bg, QColor fg) {
int realSize = size * cIntRetinaFactor();
int realSize = size * DevicePixelRatio();
#ifndef OS_MAC_OLD
auto result = QImage(realSize, realSize, QImage::Format::Format_Grayscale8);
#else // OS_MAC_OLD
auto result = QImage(realSize, realSize, QImage::Format::Format_RGB32);
#endif // OS_MAC_OLD
{
Painter p(&result);
QPainter p(&result);
PainterHighQualityEnabler hq(p);
p.fillRect(0, 0, realSize, realSize, bg);
@ -144,7 +139,7 @@ QImage createCircleMask(int size, QColor bg, QColor fg) {
p.setBrush(fg);
p.drawEllipse(0, 0, realSize, realSize);
}
result.setDevicePixelRatio(cRetinaFactor());
result.setDevicePixelRatio(DevicePixelRatio());
return result;
}

View file

@ -7,26 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/style/style_core_scale.h"
#include "ui/style/style_core_types.h"
inline QPoint rtlpoint(int x, int y, int outerw) {
return QPoint(rtl() ? (outerw - x) : x, y);
}
inline QPoint rtlpoint(const QPoint &p, int outerw) {
return rtl() ? QPoint(outerw - p.x(), p.y()) : p;
}
inline QPointF rtlpoint(const QPointF &p, int outerw) {
return rtl() ? QPointF(outerw - p.x(), p.y()) : p;
}
inline QRect rtlrect(int x, int y, int w, int h, int outerw) {
return QRect(rtl() ? (outerw - x - w) : x, y, w, h);
}
inline QRect rtlrect(const QRect &r, int outerw) {
return rtl() ? QRect(outerw - r.x() - r.width(), r.y(), r.width(), r.height()) : r;
}
inline QRectF rtlrect(const QRectF &r, int outerw) {
return rtl() ? QRectF(outerw - r.x() - r.width(), r.y(), r.width(), r.height()) : r;
}
inline QRect centerrect(const QRect &inRect, const QRect &rect) {
return QRect(inRect.x() + (inRect.width() - rect.width()) / 2, inRect.y() + (inRect.height() - rect.height()) / 2, rect.width(), rect.height());
}
@ -41,15 +24,13 @@ namespace internal {
// They call [un]registerModule() in [de|con]structor.
class ModuleBase {
public:
virtual void start() = 0;
virtual void stop() = 0;
virtual void start(int scale) = 0;
virtual ~ModuleBase() = default;
};
void registerModule(ModuleBase *module);
void unregisterModule(ModuleBase *module);
// This method is implemented in palette.cpp (codegen).
bool setPaletteColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);
@ -59,7 +40,10 @@ void EnsureContrast(ColorData &over, const ColorData &under);
} // namespace internal
void startManager();
[[nodiscard]] bool RightToLeft();
void SetRightToLeft(bool rtl);
void startManager(int scale);
void stopManager();
// *outResult must be r.width() x r.height(), ARGB32_Premultiplied.

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/style/style_core_color.h"
#include "styles/palette.h"
namespace style {
namespace internal {

View file

@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtGui/QColor>
#include <QtGui/QPen>
#include <QtGui/QBrush>
namespace style {
class palette;

View file

@ -7,25 +7,128 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/style/style_core_font.h"
#include "base/algorithm.h"
#include "logs.h"
#include <QtCore/QMap>
#include <QtCore/QVector>
#include <QtGui/QFontInfo>
#include <QtGui/QFontDatabase>
namespace style {
namespace internal {
namespace {
typedef QMap<QString, int> FontFamilyMap;
FontFamilyMap fontFamilyMap;
typedef QVector<QString> FontFamilies;
FontFamilies fontFamilies;
typedef QMap<uint32, FontData*> FontDatas;
FontDatas fontsMap;
QMap<QString, int> fontFamilyMap;
QVector<QString> fontFamilies;
QMap<uint32, FontData*> fontsMap;
uint32 fontKey(int size, uint32 flags, int family) {
return (((uint32(family) << 10) | uint32(size)) << 4) | flags;
}
bool ValidateFont(const QString &familyName, int flags = 0) {
QFont checkFont(familyName);
checkFont.setPixelSize(13);
checkFont.setBold(flags & style::internal::FontBold);
checkFont.setItalic(flags & style::internal::FontItalic);
checkFont.setUnderline(flags & style::internal::FontUnderline);
checkFont.setStyleStrategy(QFont::PreferQuality);
auto realFamily = QFontInfo(checkFont).family();
if (realFamily.trimmed().compare(familyName, Qt::CaseInsensitive)) {
LOG(("Font Error: could not resolve '%1' font, got '%2'.").arg(familyName).arg(realFamily));
return false;
}
auto metrics = QFontMetrics(checkFont);
if (!metrics.height()) {
LOG(("Font Error: got a zero height in '%1'.").arg(familyName));
return false;
}
return true;
}
bool LoadCustomFont(const QString &filePath, const QString &familyName, int flags = 0) {
auto regularId = QFontDatabase::addApplicationFont(filePath);
if (regularId < 0) {
LOG(("Font Error: could not add '%1'.").arg(filePath));
return false;
}
auto found = [&familyName, regularId] {
for (auto &family : QFontDatabase::applicationFontFamilies(regularId)) {
if (!family.trimmed().compare(familyName, Qt::CaseInsensitive)) {
return true;
}
}
return false;
};
if (!found()) {
LOG(("Font Error: could not locate '%1' font in '%2'.").arg(familyName).arg(filePath));
return false;
}
return ValidateFont(familyName, flags);
}
bool Started = false;
QString OpenSansOverride;
QString OpenSansSemiboldOverride;
} // namespace
void Start() {
if (Started) {
return;
}
Started = true;
auto regular = LoadCustomFont(":/gui/fonts/OpenSans-Regular.ttf", "Open Sans");
auto bold = LoadCustomFont(":/gui/fonts/OpenSans-Bold.ttf", "Open Sans", style::internal::FontBold);
auto semibold = LoadCustomFont(":/gui/fonts/OpenSans-Semibold.ttf", "Open Sans Semibold");
#ifdef Q_OS_WIN
// Attempt to workaround a strange font bug with Open Sans Semibold not loading.
// See https://github.com/telegramdesktop/tdesktop/issues/3276 for details.
// Crash happens on "options.maxh / _t->_st->font->height" with "division by zero".
// In that place "_t->_st->font" is "semiboldFont" is "font(13 "Open Sans Semibold").
if (!regular || !bold) {
if (ValidateFont("Segoe UI") && ValidateFont("Segoe UI", style::internal::FontBold)) {
OpenSansOverride = "Segoe UI";
LOG(("Fonts Info: Using Segoe UI instead of Open Sans."));
}
}
if (!semibold) {
if (ValidateFont("Segoe UI Semibold")) {
OpenSansSemiboldOverride = "Segoe UI Semibold";
LOG(("Fonts Info: Using Segoe UI Semibold instead of Open Sans Semibold."));
}
}
// Disable default fallbacks to Segoe UI, see:
// https://github.com/telegramdesktop/tdesktop/issues/5368
//
//QFont::insertSubstitution("Open Sans", "Segoe UI");
//QFont::insertSubstitution("Open Sans Semibold", "Segoe UI Semibold");
#elif defined Q_OS_MAC // Q_OS_WIN
auto list = QStringList();
list.append(".SF NS Text");
list.append("Helvetica Neue");
list.append("Lucida Grande");
QFont::insertSubstitutions("Open Sans", list);
QFont::insertSubstitutions("Open Sans Semibold", list);
#endif // Q_OS_WIN || Q_OS_MAC
}
QString GetFontOverride(const QString &familyName) {
if (familyName == qstr("Open Sans")) {
return OpenSansOverride.isEmpty() ? familyName : OpenSansOverride;
} else if (familyName == qstr("Open Sans Semibold")) {
return OpenSansSemiboldOverride.isEmpty() ? familyName : OpenSansSemiboldOverride;
}
return familyName;
}
void destroyFonts() {
for (auto fontData : fontsMap) {
delete fontData;
@ -44,7 +147,7 @@ int registerFontFamily(const QString &family) {
}
FontData::FontData(int size, uint32 flags, int family, Font *other)
: f(Fonts::GetOverride(fontFamilies[family]))
: f(GetFontOverride(fontFamilies[family]))
, m(f)
, _size(size)
, _flags(flags)
@ -72,7 +175,7 @@ FontData::FontData(int size, uint32 flags, int family, Font *other)
ascent = m.ascent();
descent = m.descent();
spacew = width(QLatin1Char(' '));
elidew = width(qsl("..."));
elidew = width("...");
}
Font FontData::bold(bool set) const {

View file

@ -7,9 +7,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/basic_types.h"
#include <QtGui/QFont>
#include <QtGui/QFontMetrics>
namespace style {
namespace internal {
void StartFonts();
[[nodiscard]] QString GetFontOverride(const QString &familyName);
void destroyFonts();
int registerFontFamily(const QString &family);

View file

@ -7,7 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/style/style_core_icon.h"
#include "app.h"
#include "ui/style/style_core.h"
#include "base/basic_types.h"
#include <QtGui/QPainter>
namespace style {
namespace internal {
@ -17,22 +20,19 @@ uint32 colorKey(QColor c) {
return (((((uint32(c.red()) << 8) | uint32(c.green())) << 8) | uint32(c.blue())) << 8) | uint32(c.alpha());
}
using IconMasks = QMap<const IconMask*, QImage>;
using IconPixmaps = QMap<QPair<const IconMask*, uint32>, QPixmap>;
using IconDatas = OrderedSet<IconData*>;
NeverFreedPointer<IconMasks> iconMasks;
NeverFreedPointer<IconPixmaps> iconPixmaps;
NeverFreedPointer<IconDatas> iconData;
QMap<const IconMask*, QImage> iconMasks;
QMap<QPair<const IconMask*, uint32>, QPixmap> iconPixmaps;
OrderedSet<IconData*> iconData;
QImage createIconMask(const IconMask *mask, int scale) {
auto maskImage = QImage::fromData(mask->data(), mask->size(), "PNG");
maskImage.setDevicePixelRatio(cRetinaFactor());
maskImage.setDevicePixelRatio(DevicePixelRatio());
Assert(!maskImage.isNull());
// images are layouted like this:
// 100x 200x
// 300x
const auto factor = cIntRetinaFactor();
const auto factor = DevicePixelRatio();
const auto realscale = scale * factor;
const auto width = maskImage.width() / 3;
const auto height = maskImage.height() / 5;
@ -120,7 +120,7 @@ QPoint MonoIcon::offset() const {
void MonoIcon::paint(QPainter &p, const QPoint &pos, int outerw) const {
int w = width(), h = height();
QPoint fullOffset = pos + offset();
int partPosX = rtl() ? (outerw - fullOffset.x() - w) : fullOffset.x();
int partPosX = RightToLeft() ? (outerw - fullOffset.x() - w) : fullOffset.x();
int partPosY = fullOffset.y();
ensureLoaded();
@ -143,7 +143,7 @@ void MonoIcon::fill(QPainter &p, const QRect &rect) const {
void MonoIcon::paint(QPainter &p, const QPoint &pos, int outerw, QColor colorOverride) const {
int w = width(), h = height();
QPoint fullOffset = pos + offset();
int partPosX = rtl() ? (outerw - fullOffset.x() - w) : fullOffset.x();
int partPosX = RightToLeft() ? (outerw - fullOffset.x() - w) : fullOffset.x();
int partPosY = fullOffset.y();
ensureLoaded();
@ -170,17 +170,17 @@ void MonoIcon::paint(
const QPoint &pos,
int outerw,
const style::palette &paletteOverride) const {
auto size = readGeneratedSize(_mask, cScale());
auto size = readGeneratedSize(_mask, Scale());
auto maskImage = QImage();
if (size.isEmpty()) {
maskImage = createIconMask(_mask, cScale());
size = maskImage.size() / cIntRetinaFactor();
maskImage = createIconMask(_mask, Scale());
size = maskImage.size() / DevicePixelRatio();
}
const auto w = size.width();
const auto h = size.height();
const auto fullOffset = pos + offset();
const auto partPosX = rtl() ? (outerw - fullOffset.x() - w) : fullOffset.x();
const auto partPosX = RightToLeft() ? (outerw - fullOffset.x() - w) : fullOffset.x();
const auto partPosY = fullOffset.y();
if (!maskImage.isNull()) {
@ -198,11 +198,11 @@ void MonoIcon::fill(
QPainter &p,
const QRect &rect,
const style::palette &paletteOverride) const {
auto size = readGeneratedSize(_mask, cScale());
auto size = readGeneratedSize(_mask, Scale());
auto maskImage = QImage();
if (size.isEmpty()) {
maskImage = createIconMask(_mask, cScale());
size = maskImage.size() / cIntRetinaFactor();
maskImage = createIconMask(_mask, Scale());
size = maskImage.size() / DevicePixelRatio();
}
if (!maskImage.isNull()) {
auto colorizedImage = QImage(
@ -216,10 +216,10 @@ void MonoIcon::fill(
}
QImage MonoIcon::instance(QColor colorOverride, int scale) const {
if (scale == kInterfaceScaleAuto) {
if (scale == kScaleAuto) {
ensureLoaded();
auto result = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
auto result = QImage(size() * DevicePixelRatio(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(DevicePixelRatio());
if (_pixmap.isNull()) {
result.fill(colorOverride);
} else {
@ -229,14 +229,14 @@ QImage MonoIcon::instance(QColor colorOverride, int scale) const {
}
auto size = readGeneratedSize(_mask, scale);
if (!size.isEmpty()) {
auto result = QImage(size * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
auto result = QImage(size * DevicePixelRatio(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(DevicePixelRatio());
result.fill(colorOverride);
return result;
}
auto mask = createIconMask(_mask, scale);
auto result = QImage(mask.size(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
result.setDevicePixelRatio(DevicePixelRatio());
colorizeImage(mask, colorOverride, &result);
return result;
}
@ -250,12 +250,11 @@ void MonoIcon::ensureLoaded() const {
return;
}
_size = readGeneratedSize(_mask, cScale());
_size = readGeneratedSize(_mask, Scale());
if (_size.isEmpty()) {
iconMasks.createIfNull();
auto i = iconMasks->constFind(_mask);
if (i == iconMasks->cend()) {
i = iconMasks->insert(_mask, createIconMask(_mask, cScale()));
auto i = iconMasks.constFind(_mask);
if (i == iconMasks.cend()) {
i = iconMasks.insert(_mask, createIconMask(_mask, Scale()));
}
_maskImage = i.value();
@ -269,27 +268,25 @@ void MonoIcon::ensureColorizedImage(QColor color) const {
}
void MonoIcon::createCachedPixmap() const {
iconPixmaps.createIfNull();
auto key = qMakePair(_mask, colorKey(_color->c));
auto j = iconPixmaps->constFind(key);
if (j == iconPixmaps->cend()) {
auto j = iconPixmaps.constFind(key);
if (j == iconPixmaps.cend()) {
auto image = colorizeImage(_maskImage, _color);
j = iconPixmaps->insert(key, App::pixmapFromImageInPlace(std::move(image)));
j = iconPixmaps.insert(key, QPixmap::fromImage(std::move(image)));
}
_pixmap = j.value();
_size = _pixmap.size() / cIntRetinaFactor();
_size = _pixmap.size() / DevicePixelRatio();
}
void IconData::created() {
iconData.createIfNull();
iconData->insert(this);
iconData.insert(this);
}
void IconData::fill(QPainter &p, const QRect &rect) const {
if (_parts.empty()) return;
auto partSize = _parts[0].size();
for_const (auto &part, _parts) {
for (const auto &part : _parts) {
Assert(part.offset() == QPoint(0, 0));
Assert(part.size() == partSize);
part.fill(p, rect);
@ -300,7 +297,7 @@ void IconData::fill(QPainter &p, const QRect &rect, QColor colorOverride) const
if (_parts.empty()) return;
auto partSize = _parts[0].size();
for_const (auto &part, _parts) {
for (const auto &part : _parts) {
Assert(part.offset() == QPoint(0, 0));
Assert(part.size() == partSize);
part.fill(p, rect, colorOverride);
@ -317,7 +314,7 @@ QImage IconData::instance(QColor colorOverride, int scale) const {
int IconData::width() const {
if (_width < 0) {
_width = 0;
for_const (auto &part, _parts) {
for (const auto &part : _parts) {
accumulate_max(_width, part.offset().x() + part.width());
}
}
@ -327,7 +324,7 @@ int IconData::width() const {
int IconData::height() const {
if (_height < 0) {
_height = 0;
for_const (auto &part, _parts) {
for (const auto &part : _parts) {
accumulate_max(_height, part.offset().x() + part.height());
}
}
@ -336,10 +333,8 @@ int IconData::height() const {
void resetIcons() {
iconPixmaps.clear();
if (iconData) {
for (auto data : *iconData) {
data->reset();
}
for (const auto data : iconData) {
data->reset();
}
}

View file

@ -8,6 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "ui/style/style_core_color.h"
#include "ui/style/style_core_scale.h"
#include "base/algorithm.h"
#include "base/assertion.h"
#include <vector>
namespace style {
@ -87,7 +91,7 @@ public:
}
void reset() {
for_const (auto &part, _parts) {
for (const auto &part : _parts) {
part.reset();
}
}
@ -96,21 +100,21 @@ public:
}
void paint(QPainter &p, const QPoint &pos, int outerw) const {
for_const (auto &part, _parts) {
for (const auto &part : _parts) {
part.paint(p, pos, outerw);
}
}
void fill(QPainter &p, const QRect &rect) const;
void paint(QPainter &p, const QPoint &pos, int outerw, QColor colorOverride) const {
for_const (auto &part, _parts) {
for (const auto &part : _parts) {
part.paint(p, pos, outerw, colorOverride);
}
}
void fill(QPainter &p, const QRect &rect, QColor colorOverride) const;
void paint(QPainter &p, const QPoint &pos, int outerw, const style::palette &paletteOverride) const {
for_const (auto &part, _parts) {
for (const auto &part : _parts) {
part.paint(p, pos, outerw, paletteOverride);
}
}
@ -153,13 +157,15 @@ public:
Icon(Icon &&other) : _data(base::take(other._data)), _owner(base::take(_owner)) {
}
Icon &operator=(const Icon &other) {
Assert(!_owner);
Expects(!_owner);
_data = other._data;
_owner = false;
return *this;
}
Icon &operator=(Icon &&other) {
Assert(!_owner);
Expects(!_owner);
_data = base::take(other._data);
_owner = base::take(other._owner);
return *this;
@ -205,7 +211,7 @@ public:
return _data->fill(p, rect, colorOverride);
}
QImage instance(QColor colorOverride, int scale = kInterfaceScaleAuto) const {
QImage instance(QColor colorOverride, int scale = kScaleAuto) const {
return _data->instance(colorOverride, scale);
}

View file

@ -0,0 +1,38 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/style/style_core_scale.h"
#include "base/assertion.h"
namespace style {
namespace {
int DevicePixelRatioValue = 1;
int ScaleValue = kScaleDefault;
} // namespace
int DevicePixelRatio() {
return DevicePixelRatioValue;
}
void SetDevicePixelRatio(int ratio) {
DevicePixelRatioValue = ratio;
}
int Scale() {
return ScaleValue;
}
void SetScale(int scale) {
Expects(scale != 0);
ScaleValue = scale;
}
} // namespace style

View file

@ -0,0 +1,50 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QSize>
#include <algorithm>
#include <cmath>
namespace style {
inline constexpr auto kScaleAuto = 0;
inline constexpr auto kScaleMin = 100;
inline constexpr auto kScaleDefault = 100;
inline constexpr auto kScaleMax = 300;
[[nodiscard]] int DevicePixelRatio();
void SetDevicePixelRatio(int ratio);
[[nodiscard]] int Scale();
void SetScale(int scale);
[[nodiscard]] inline int CheckScale(int scale) {
return (scale == kScaleAuto)
? kScaleAuto
: std::clamp(scale, kScaleMin, kScaleMax / DevicePixelRatio());
}
template <typename T>
[[nodiscard]] inline T ConvertScale(T value, int scale) {
return (value < 0.)
? (-ConvertScale(-value, scale))
: T(std::round((double(value) * scale / 100.) - 0.01));
}
template <typename T>
[[nodiscard]] inline T ConvertScale(T value) {
return ConvertScale(value, Scale());
}
[[nodiscard]] inline QSize ConvertScale(QSize size) {
return QSize(ConvertScale(size.width()), ConvertScale(size.height()));
}
} // namespace style

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "ui/text/text_entity.h"
#include "ui/painter.h"
#include "core/click_handler.h"
#include "base/flags.h"

View file

@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/basic_types.h"
#include <QtCore/QList>
enum class EntityType {
Invalid = 0,

View file

@ -13,114 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QWindow>
#include <QtGui/QGuiApplication>
#include <QtGui/QFontDatabase>
namespace Fonts {
namespace {
bool ValidateFont(const QString &familyName, int flags = 0) {
QFont checkFont(familyName);
checkFont.setPixelSize(13);
checkFont.setBold(flags & style::internal::FontBold);
checkFont.setItalic(flags & style::internal::FontItalic);
checkFont.setUnderline(flags & style::internal::FontUnderline);
checkFont.setStyleStrategy(QFont::PreferQuality);
auto realFamily = QFontInfo(checkFont).family();
if (realFamily.trimmed().compare(familyName, Qt::CaseInsensitive)) {
LOG(("Font Error: could not resolve '%1' font, got '%2'.").arg(familyName).arg(realFamily));
return false;
}
auto metrics = QFontMetrics(checkFont);
if (!metrics.height()) {
LOG(("Font Error: got a zero height in '%1'.").arg(familyName));
return false;
}
return true;
}
bool LoadCustomFont(const QString &filePath, const QString &familyName, int flags = 0) {
auto regularId = QFontDatabase::addApplicationFont(filePath);
if (regularId < 0) {
LOG(("Font Error: could not add '%1'.").arg(filePath));
return false;
}
auto found = [&familyName, regularId] {
for (auto &family : QFontDatabase::applicationFontFamilies(regularId)) {
if (!family.trimmed().compare(familyName, Qt::CaseInsensitive)) {
return true;
}
}
return false;
};
if (!found()) {
LOG(("Font Error: could not locate '%1' font in '%2'.").arg(familyName).arg(filePath));
return false;
}
return ValidateFont(familyName, flags);
}
bool Started = false;
QString OpenSansOverride;
QString OpenSansSemiboldOverride;
} // namespace
void Start() {
if (Started) {
return;
}
Started = true;
auto regular = LoadCustomFont(qsl(":/gui/fonts/OpenSans-Regular.ttf"), qsl("Open Sans"));
auto bold = LoadCustomFont(qsl(":/gui/fonts/OpenSans-Bold.ttf"), qsl("Open Sans"), style::internal::FontBold);
auto semibold = LoadCustomFont(qsl(":/gui/fonts/OpenSans-Semibold.ttf"), qsl("Open Sans Semibold"));
#ifdef Q_OS_WIN
// Attempt to workaround a strange font bug with Open Sans Semibold not loading.
// See https://github.com/telegramdesktop/tdesktop/issues/3276 for details.
// Crash happens on "options.maxh / _t->_st->font->height" with "division by zero".
// In that place "_t->_st->font" is "semiboldFont" is "font(13 "Open Sans Semibold").
if (!regular || !bold) {
if (ValidateFont(qsl("Segoe UI")) && ValidateFont(qsl("Segoe UI"), style::internal::FontBold)) {
OpenSansOverride = qsl("Segoe UI");
LOG(("Fonts Info: Using Segoe UI instead of Open Sans."));
}
}
if (!semibold) {
if (ValidateFont(qsl("Segoe UI Semibold"))) {
OpenSansSemiboldOverride = qsl("Segoe UI Semibold");
LOG(("Fonts Info: Using Segoe UI Semibold instead of Open Sans Semibold."));
}
}
// Disable default fallbacks to Segoe UI, see:
// https://github.com/telegramdesktop/tdesktop/issues/5368
//
//QFont::insertSubstitution(qsl("Open Sans"), qsl("Segoe UI"));
//QFont::insertSubstitution(qsl("Open Sans Semibold"), qsl("Segoe UI Semibold"));
#elif defined Q_OS_MAC // Q_OS_WIN
auto list = QStringList();
list.append(qsl(".SF NS Text"));
list.append(qsl("Helvetica Neue"));
list.append(qsl("Lucida Grande"));
QFont::insertSubstitutions(qsl("Open Sans"), list);
QFont::insertSubstitutions(qsl("Open Sans Semibold"), list);
#endif // Q_OS_WIN || Q_OS_MAC
}
QString GetOverride(const QString &familyName) {
if (familyName == qstr("Open Sans")) {
return OpenSansOverride.isEmpty() ? familyName : OpenSansOverride;
} else if (familyName == qstr("Open Sans Semibold")) {
return OpenSansSemiboldOverride.isEmpty() ? familyName : OpenSansSemiboldOverride;
}
return familyName;
}
} // Fonts
namespace Ui {
namespace {

View file

@ -9,18 +9,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/flags.h"
namespace Fonts {
void Start();
QString GetOverride(const QString &familyName);
} // namespace
class TWidget;
template <typename Object>
class object_ptr;
inline QPoint rtlpoint(int x, int y, int outerw) {
return QPoint(rtl() ? (outerw - x) : x, y);
}
inline QPoint rtlpoint(const QPoint &p, int outerw) {
return rtl() ? QPoint(outerw - p.x(), p.y()) : p;
}
inline QPointF rtlpoint(const QPointF &p, int outerw) {
return rtl() ? QPointF(outerw - p.x(), p.y()) : p;
}
inline QRect rtlrect(int x, int y, int w, int h, int outerw) {
return QRect(rtl() ? (outerw - x - w) : x, y, w, h);
}
inline QRect rtlrect(const QRect &r, int outerw) {
return rtl() ? QRect(outerw - r.x() - r.width(), r.y(), r.width(), r.height()) : r;
}
inline QRectF rtlrect(const QRectF &r, int outerw) {
return rtl() ? QRectF(outerw - r.x() - r.width(), r.y(), r.width(), r.height()) : r;
}
namespace Ui {
inline bool InFocusChain(not_null<const QWidget*> widget) {
@ -133,111 +145,6 @@ inline bool IsRightCorner(RectPart corner) {
return (corner == RectPart::TopRight) || (corner == RectPart::BottomRight);
}
class Painter : public QPainter {
public:
explicit Painter(QPaintDevice *device) : QPainter(device) {
}
void drawTextLeft(int x, int y, int outerw, const QString &text, int textWidth = -1) {
QFontMetrics m(fontMetrics());
if (rtl() && textWidth < 0) textWidth = m.width(text);
drawText(rtl() ? (outerw - x - textWidth) : x, y + m.ascent(), text);
}
void drawTextRight(int x, int y, int outerw, const QString &text, int textWidth = -1) {
QFontMetrics m(fontMetrics());
if (!rtl() && textWidth < 0) textWidth = m.width(text);
drawText(rtl() ? x : (outerw - x - textWidth), y + m.ascent(), text);
}
void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix, const QRect &from) {
drawPixmap(QPoint(rtl() ? (outerw - x - (from.width() / pix.devicePixelRatio())) : x, y), pix, from);
}
void drawPixmapLeft(const QPoint &p, int outerw, const QPixmap &pix, const QRect &from) {
return drawPixmapLeft(p.x(), p.y(), outerw, pix, from);
}
void drawPixmapLeft(int x, int y, int w, int h, int outerw, const QPixmap &pix, const QRect &from) {
drawPixmap(QRect(rtl() ? (outerw - x - w) : x, y, w, h), pix, from);
}
void drawPixmapLeft(const QRect &r, int outerw, const QPixmap &pix, const QRect &from) {
return drawPixmapLeft(r.x(), r.y(), r.width(), r.height(), outerw, pix, from);
}
void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix) {
drawPixmap(QPoint(rtl() ? (outerw - x - (pix.width() / pix.devicePixelRatio())) : x, y), pix);
}
void drawPixmapLeft(const QPoint &p, int outerw, const QPixmap &pix) {
return drawPixmapLeft(p.x(), p.y(), outerw, pix);
}
void drawPixmapRight(int x, int y, int outerw, const QPixmap &pix, const QRect &from) {
drawPixmap(QPoint(rtl() ? x : (outerw - x - (from.width() / pix.devicePixelRatio())), y), pix, from);
}
void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix, const QRect &from) {
return drawPixmapRight(p.x(), p.y(), outerw, pix, from);
}
void drawPixmapRight(int x, int y, int w, int h, int outerw, const QPixmap &pix, const QRect &from) {
drawPixmap(QRect(rtl() ? x : (outerw - x - w), y, w, h), pix, from);
}
void drawPixmapRight(const QRect &r, int outerw, const QPixmap &pix, const QRect &from) {
return drawPixmapRight(r.x(), r.y(), r.width(), r.height(), outerw, pix, from);
}
void drawPixmapRight(int x, int y, int outerw, const QPixmap &pix) {
drawPixmap(QPoint(rtl() ? x : (outerw - x - (pix.width() / pix.devicePixelRatio())), y), pix);
}
void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix) {
return drawPixmapRight(p.x(), p.y(), outerw, pix);
}
void setTextPalette(const style::TextPalette &palette) {
_textPalette = &palette;
}
void restoreTextPalette() {
_textPalette = nullptr;
}
const style::TextPalette &textPalette() const {
return _textPalette ? *_textPalette : st::defaultTextPalette;
}
private:
const style::TextPalette *_textPalette = nullptr;
};
class PainterHighQualityEnabler {
public:
PainterHighQualityEnabler(QPainter &p) : _painter(p) {
static constexpr QPainter::RenderHint Hints[] = {
QPainter::Antialiasing,
QPainter::SmoothPixmapTransform,
QPainter::TextAntialiasing,
QPainter::HighQualityAntialiasing
};
const auto hints = _painter.renderHints();
for (const auto hint : Hints) {
if (!(hints & hint)) {
_hints |= hint;
}
}
if (_hints) {
_painter.setRenderHints(_hints);
}
}
PainterHighQualityEnabler(
const PainterHighQualityEnabler &other) = delete;
PainterHighQualityEnabler &operator=(
const PainterHighQualityEnabler &other) = delete;
~PainterHighQualityEnabler() {
if (_hints) {
_painter.setRenderHints(_hints, false);
}
}
private:
QPainter &_painter;
QPainter::RenderHints _hints = 0;
};
template <typename Base>
class TWidgetHelper : public Base {
public:

View file

@ -0,0 +1,10 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/ui_pch.h"
// Precompiled header helper.

View file

@ -0,0 +1,21 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include <QtCore/QString>
#include <QtCore/QPoint>
#include <QtCore/QRect>
#include <QtCore/QSize>
#include <QtGui/QColor>
#include <QtGui/QPen>
#include <QtGui/QBrush>
#include <QtGui/QCursor>
#include <QtGui/QFont>
#include <QtGui/QFontMetrics>
#include <QtWidgets/QWidget>

View file

@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/popup_menu.h"
#include "ui/countryinput.h"
#include "ui/emoji_config.h"
#include "emoji_suggestions_data.h"
#include "chat_helpers/emoji_suggestions_helper.h"
#include "chat_helpers/message_field.h" // ConvertTextTagsToEntities
#include "platform/platform_info.h"
@ -3072,7 +3071,7 @@ void InputField::commitInstantReplacement(
? emoji->variant(it.value())
: emoji;
}();
Ui::Emoji::AddRecent(use);
AddRecentEmoji(use);
return PrepareEmojiFormat(use, _st.font);
}();
const auto replacement = format.isImageFormat()

View file

@ -634,7 +634,7 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
const auto scale = cScale() * cIntRetinaFactor();
if (scale != 100) {
image = image.scaledToWidth(
ConvertScale(image.width(), scale),
style::ConvertScale(image.width(), scale),
Qt::SmoothTransformation);
}
} else if (Data::IsDefaultWallPaper(_paper)

View file

@ -293,8 +293,8 @@ void Generator::addDateBubble(QString date) {
void Generator::addPhotoBubble(QString image, QString caption, QString date, Status status) {
Bubble bubble;
bubble.photo.load(image);
bubble.photoWidth = ConvertScale(bubble.photo.width() / 2);
bubble.photoHeight = ConvertScale(bubble.photo.height() / 2);
bubble.photoWidth = style::ConvertScale(bubble.photo.width() / 2);
bubble.photoHeight = style::ConvertScale(bubble.photo.height() / 2);
auto skipBlock = computeSkipBlock(status, date);
bubble.text.setRichText(st::messageTextStyle, caption + textcmdSkipBlock(skipBlock.width(), skipBlock.height()), Ui::ItemTextDefaultOptions());

View file

@ -493,7 +493,7 @@ void MainMenu::initResetScaleButton() {
} else {
_resetScaleButton.create(this);
_resetScaleButton->addClickHandler([] {
cSetConfigScale(kInterfaceScaleDefault);
cSetConfigScale(style::kScaleDefault);
Local::writeSettings();
App::restart();
});

View file

@ -203,7 +203,7 @@ QSize MediaPreviewWidget::currentDimensions() const {
box = QSize(2 * st::maxStickerSize, 2 * st::maxStickerSize);
}
}
result = QSize(qMax(ConvertScale(result.width()), 1), qMax(ConvertScale(result.height()), 1));
result = QSize(qMax(style::ConvertScale(result.width()), 1), qMax(style::ConvertScale(result.height()), 1));
if (result.width() > box.width()) {
result.setHeight(qMax((box.width() * result.height()) / result.width(), 1));
result.setWidth(box.width());

View file

@ -22,8 +22,6 @@
'sp_media_key_tap_loc': '<(submodules_loc)/SPMediaKeyTap',
'emoji_suggestions_loc': '<(submodules_loc)/emoji_suggestions',
'style_files': [
'<(res_loc)/colors.palette',
'<(res_loc)/basic.style',
'<(src_loc)/boxes/boxes.style',
'<(src_loc)/calls/calls.style',
'<(src_loc)/dialogs/dialogs.style',
@ -38,9 +36,14 @@
'<(src_loc)/profile/profile.style',
'<(src_loc)/settings/settings.style',
'<(src_loc)/chat_helpers/chat_helpers.style',
'<(src_loc)/ui/widgets/widgets.style',
'<(src_loc)/window/window.style',
],
'dependent_style_files': [
'<(res_loc)/colors.palette',
'<(res_loc)/basic.style',
'<(src_loc)/ui/widgets/widgets.style',
],
'style_timestamp': '<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles.timestamp',
'langpacks': [
'en',
'de',
@ -84,6 +87,7 @@
'lib_lottie.gyp:lib_lottie',
'lib_ffmpeg.gyp:lib_ffmpeg',
'lib_mtproto.gyp:lib_mtproto',
'lib_ui.gyp:lib_ui',
],
'defines': [

View file

@ -5,23 +5,10 @@
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
{
'includes': [
'codegen_styles_rule.gypi',
],
'actions': [{
'action_name': 'update_dependent_styles',
'inputs': [
'<(DEPTH)/update_dependent.py',
'<@(style_files)',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles.timestamp',
],
'action': [
'python', '<(DEPTH)/update_dependent.py', '--styles',
'-I', '<(res_loc)', '-I', '<(src_loc)',
'-o', '<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles.timestamp',
'<@(style_files)',
],
'message': 'Updating dependent style files..',
}, {
'action_name': 'update_dependent_qrc',
'inputs': [
'<(DEPTH)/update_dependent.py',
@ -37,33 +24,6 @@
'<@(qrc_files)',
],
'message': 'Updating dependent qrc files..',
}, {
'action_name': 'codegen_palette',
'inputs': [
'<(PRODUCT_DIR)/codegen_style<(exe_ext)',
'<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles.timestamp',
'<(res_loc)/colors.palette',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/styles/palette.h',
'<(SHARED_INTERMEDIATE_DIR)/styles/palette.cpp',
],
'action': [
'<(PRODUCT_DIR)/codegen_style<(exe_ext)',
'-I', '<(res_loc)', '-I', '<(src_loc)',
'-o', '<(SHARED_INTERMEDIATE_DIR)/styles',
'-w', '<(PRODUCT_DIR)/..',
# GYP/Ninja bug workaround: if we specify just <(RULE_INPUT_PATH)
# the <(RULE_INPUT_ROOT) variables won't be available in Ninja,
# and the 'message' will be just 'codegen_style-ing .style..'
# Looks like the using the <(RULE_INPUT_ROOT) here "exports" it
# for using in the 'message' field.
'<(res_loc)/colors.palette',
],
'message': 'codegen_palette-ing colors..',
'process_outputs_as_sources': 1,
}, {
'action_name': 'codegen_lang',
'inputs': [
@ -98,52 +58,5 @@
],
'message': 'codegen_numbers-ing numbers.txt..',
'process_outputs_as_sources': 1,
}, {
'action_name': 'codegen_emoji',
'inputs': [
'<(PRODUCT_DIR)/codegen_emoji<(exe_ext)',
'<(res_loc)/emoji_autocomplete.json',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/emoji.cpp',
'<(SHARED_INTERMEDIATE_DIR)/emoji.h',
'<(SHARED_INTERMEDIATE_DIR)/emoji_suggestions_data.cpp',
'<(SHARED_INTERMEDIATE_DIR)/emoji_suggestions_data.h',
],
'action': [
'<(PRODUCT_DIR)/codegen_emoji<(exe_ext)',
'<(res_loc)/emoji_autocomplete.json',
'-o', '<(SHARED_INTERMEDIATE_DIR)',
],
'message': 'codegen_emoji-ing..',
'process_outputs_as_sources': 1,
}],
'rules': [{
'rule_name': 'codegen_style',
'extension': 'style',
'inputs': [
'<(PRODUCT_DIR)/codegen_style<(exe_ext)',
'<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles.timestamp',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/styles/style_<(RULE_INPUT_ROOT).h',
'<(SHARED_INTERMEDIATE_DIR)/styles/style_<(RULE_INPUT_ROOT).cpp',
],
'action': [
'<(PRODUCT_DIR)/codegen_style<(exe_ext)',
'-I', '<(res_loc)', '-I', '<(src_loc)',
'-o', '<(SHARED_INTERMEDIATE_DIR)/styles',
'-w', '<(PRODUCT_DIR)/..',
# GYP/Ninja bug workaround: if we specify just <(RULE_INPUT_PATH)
# the <(RULE_INPUT_ROOT) variables won't be available in Ninja,
# and the 'message' will be just 'codegen_style-ing .style..'
# Looks like the using the <(RULE_INPUT_ROOT) here "exports" it
# for using in the 'message' field.
'<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT)<(RULE_INPUT_EXT)',
],
'message': 'codegen_style-ing <(RULE_INPUT_ROOT).style..',
'process_outputs_as_sources': 1,
}],
}

View file

@ -0,0 +1,58 @@
# This file is part of Telegram Desktop,
# the official desktop application for the Telegram messaging service.
#
# For license and copyright information please follow this link:
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
{
'includes': [
'codegen_styles_rule.gypi',
],
'actions': [{
'action_name': 'codegen_palette',
'inputs': [
'<(PRODUCT_DIR)/codegen_style<(exe_ext)',
'<(style_timestamp)',
'<(res_loc)/colors.palette',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/styles/palette.h',
'<(SHARED_INTERMEDIATE_DIR)/styles/palette.cpp',
],
'action': [
'<(PRODUCT_DIR)/codegen_style<(exe_ext)',
'-I', '<(res_loc)', '-I', '<(src_loc)',
'-o', '<(SHARED_INTERMEDIATE_DIR)/styles',
'-w', '<(PRODUCT_DIR)/..',
# GYP/Ninja bug workaround: if we specify just <(RULE_INPUT_PATH)
# the <(RULE_INPUT_ROOT) variables won't be available in Ninja,
# and the 'message' will be just 'codegen_style-ing .style..'
# Looks like the using the <(RULE_INPUT_ROOT) here "exports" it
# for using in the 'message' field.
'<(res_loc)/colors.palette',
],
'message': 'codegen_palette-ing colors..',
'process_outputs_as_sources': 1,
}, {
'action_name': 'codegen_emoji',
'inputs': [
'<(PRODUCT_DIR)/codegen_emoji<(exe_ext)',
'<(res_loc)/emoji_autocomplete.json',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/emoji.cpp',
'<(SHARED_INTERMEDIATE_DIR)/emoji.h',
'<(SHARED_INTERMEDIATE_DIR)/emoji_suggestions_data.cpp',
'<(SHARED_INTERMEDIATE_DIR)/emoji_suggestions_data.h',
],
'action': [
'<(PRODUCT_DIR)/codegen_emoji<(exe_ext)',
'<(res_loc)/emoji_autocomplete.json',
'-o', '<(SHARED_INTERMEDIATE_DIR)',
],
'message': 'codegen_emoji-ing..',
'process_outputs_as_sources': 1,
}],
}

View file

@ -0,0 +1,54 @@
# This file is part of Telegram Desktop,
# the official desktop application for the Telegram messaging service.
#
# For license and copyright information please follow this link:
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
{
'actions': [{
'action_name': 'update_dependent_styles',
'inputs': [
'<(DEPTH)/update_dependent.py',
'<@(style_files)',
'<@(dependent_style_files)',
],
'outputs': [
'<(style_timestamp)',
],
'action': [
'python', '<(DEPTH)/update_dependent.py', '--styles',
'-I', '<(res_loc)', '-I', '<(src_loc)',
'-o', '<(style_timestamp)',
'<@(style_files)',
],
'message': 'Updating dependent style files..',
}],
'rules': [{
'rule_name': 'codegen_style',
'extension': 'style',
'inputs': [
'<(PRODUCT_DIR)/codegen_style<(exe_ext)',
'<(style_timestamp)',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/styles/style_<(RULE_INPUT_ROOT).h',
'<(SHARED_INTERMEDIATE_DIR)/styles/style_<(RULE_INPUT_ROOT).cpp',
],
'action': [
'<(PRODUCT_DIR)/codegen_style<(exe_ext)',
'-I', '<(res_loc)', '-I', '<(src_loc)',
'-o', '<(SHARED_INTERMEDIATE_DIR)/styles',
'-w', '<(PRODUCT_DIR)/..',
# GYP/Ninja bug workaround: if we specify just <(RULE_INPUT_PATH)
# the <(RULE_INPUT_ROOT) variables won't be available in Ninja,
# and the 'message' will be just 'codegen_style-ing .style..'
# Looks like the using the <(RULE_INPUT_ROOT) here "exports" it
# for using in the 'message' field.
'<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT)<(RULE_INPUT_EXT)',
],
'message': 'codegen_style-ing <(RULE_INPUT_ROOT).style..',
'process_outputs_as_sources': 1,
}],
}

View file

@ -51,8 +51,8 @@
'action': [
'python', '<(src_loc)/codegen/scheme/codegen_scheme.py',
'-o', '<(SHARED_INTERMEDIATE_DIR)',
'<(res_loc)/tl/mtproto.tl',
'<(res_loc)/tl/api.tl',
'<(res_loc)/tl/mtproto.tl',
'<(res_loc)/tl/api.tl',
],
'message': 'codegen_scheme-ing *.tl..',
'process_outputs_as_sources': 1,

78
Telegram/gyp/lib_ui.gyp Normal file
View file

@ -0,0 +1,78 @@
# This file is part of Telegram Desktop,
# the official desktop application for the Telegram messaging service.
#
# For license and copyright information please follow this link:
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
{
'includes': [
'common.gypi',
],
'targets': [{
'target_name': 'lib_ui',
'type': 'static_library',
'hard_dependency': 1,
'includes': [
'common.gypi',
'qt.gypi',
'qt_moc.gypi',
'codegen_rules_ui.gypi',
'pch.gypi',
],
'dependencies': [
'codegen.gyp:codegen_emoji',
'codegen.gyp:codegen_style',
'crl.gyp:crl',
],
'variables': {
'variables': {
'libs_loc': '../../../Libraries',
},
'libs_loc': '<(libs_loc)',
'src_loc': '../SourceFiles',
'res_loc': '../Resources',
'official_build_target%': '',
'submodules_loc': '../ThirdParty',
'emoji_suggestions_loc': '<(submodules_loc)/emoji_suggestions',
'style_files': [
'<(res_loc)/colors.palette',
'<(res_loc)/basic.style',
'<(src_loc)/ui/widgets/widgets.style',
],
'dependent_style_files': [
],
'style_timestamp': '<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles_ui.timestamp',
'pch_source': '<(src_loc)/ui/ui_pch.cpp',
'pch_header': '<(src_loc)/ui/ui_pch.h',
},
'defines': [
],
'include_dirs': [
'<(src_loc)',
'<(SHARED_INTERMEDIATE_DIR)',
'<(libs_loc)/range-v3/include',
'<(submodules_loc)/GSL/include',
'<(submodules_loc)/variant/include',
'<(submodules_loc)/crl/src',
'<(emoji_suggestions_loc)',
],
'sources': [
'<@(style_files)',
'<(src_loc)/ui/style/style_core.cpp',
'<(src_loc)/ui/style/style_core.h',
'<(src_loc)/ui/style/style_core_color.cpp',
'<(src_loc)/ui/style/style_core_color.h',
'<(src_loc)/ui/style/style_core_font.cpp',
'<(src_loc)/ui/style/style_core_font.h',
'<(src_loc)/ui/style/style_core_icon.cpp',
'<(src_loc)/ui/style/style_core_icon.h',
'<(src_loc)/ui/style/style_core_scale.cpp',
'<(src_loc)/ui/style/style_core_scale.h',
'<(src_loc)/ui/style/style_core_types.cpp',
'<(src_loc)/ui/style/style_core_types.h',
'<(src_loc)/ui/painter.h',
'<(emoji_suggestions_loc)/emoji_suggestions.cpp',
'<(emoji_suggestions_loc)/emoji_suggestions.h',
],
}],
}