mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-11 11:47:09 +02:00
Initial new peer information display.
This commit is contained in:
parent
a1e555267e
commit
0fc8229be1
9 changed files with 386 additions and 173 deletions
|
@ -3642,6 +3642,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_new_contact_from_request_group" = "{user} is an admin of {name}, a group you requested to join.";
|
||||
"lng_new_contact_about_status" = "This account uses {emoji} as a custom status next to its\nname. Such emoji statuses are available to all\nsubscribers of {link}.";
|
||||
"lng_new_contact_about_status_link" = "Telegram Premium";
|
||||
"lng_new_contact_not_contact" = "Not a contact";
|
||||
"lng_new_contact_phone_number" = "Phone number";
|
||||
"lng_new_contact_registration" = "Registration";
|
||||
"lng_new_contact_common_groups" = "Common groups";
|
||||
"lng_new_contact_not_official" = "Not an official account";
|
||||
"lng_new_contact_updated_name" = "User updated name {when}";
|
||||
"lng_new_contact_updated_photo" = "User updated photo {when}";
|
||||
"lng_new_contact_updated_now" = "less than an hour ago";
|
||||
"lng_new_contact_updated_hours#one" = "{count} hour ago";
|
||||
"lng_new_contact_updated_hours#other" = "{count} hours ago";
|
||||
"lng_new_contact_updated_days#one" = "{count} day ago";
|
||||
"lng_new_contact_updated_days#other" = "{count} days ago";
|
||||
"lng_new_contact_updated_months#one" = "{count} month ago";
|
||||
"lng_new_contact_updated_months#other" = "{count} months ago";
|
||||
"lng_from_request_title_channel" = "Response to your join request";
|
||||
"lng_from_request_title_group" = "Response to your join request";
|
||||
"lng_from_request_body" = "You received this message because you requested to join {name} on {date}.";
|
||||
|
|
|
@ -66,6 +66,28 @@ using UpdateFlag = Data::PeerUpdate::Flag;
|
|||
return session->appConfig().ignoredRestrictionReasons();
|
||||
}
|
||||
|
||||
[[nodiscard]] int ParseRegistrationDate(const QString &text) {
|
||||
// MM.YYYY
|
||||
if (text.size() != 7 || text[2] != '.') {
|
||||
return 0;
|
||||
}
|
||||
const auto month = text.mid(0, 2).toInt();
|
||||
const auto year = text.mid(3, 4).toInt();
|
||||
return (year > 2012 && year < 2100 && month > 0 && month <= 12)
|
||||
? (year * 100) + month
|
||||
: 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] int RegistrationYear(int date) {
|
||||
const auto year = date / 100;
|
||||
return (year > 2012 && year < 2100) ? year : 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] int RegistrationMonth(int date) {
|
||||
const auto month = date % 100;
|
||||
return (month > 0 && month <= 12) ? month : 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Data {
|
||||
|
@ -734,7 +756,9 @@ void PeerData::checkFolder(FolderId folderId) {
|
|||
|
||||
void PeerData::clearBusinessBot() {
|
||||
if (const auto details = _barDetails.get()) {
|
||||
if (details->requestChatDate || details->paysPerMessage) {
|
||||
if (details->requestChatDate
|
||||
|| details->paysPerMessage
|
||||
|| !details->phoneCountryCode.isEmpty()) {
|
||||
details->businessBot = nullptr;
|
||||
details->businessBotManageUrl = QString();
|
||||
} else {
|
||||
|
@ -780,12 +804,24 @@ void PeerData::setBarSettings(const MTPPeerSettings &data) {
|
|||
const auto wasPaysPerMessage = paysPerMessage();
|
||||
if (!data.vbusiness_bot_id()
|
||||
&& !data.vrequest_chat_title()
|
||||
&& !data.vcharge_paid_message_stars()) {
|
||||
&& !data.vcharge_paid_message_stars()
|
||||
&& !data.vphone_country()
|
||||
&& !data.vregistration_month()
|
||||
&& !data.vname_change_date()
|
||||
&& !data.vphoto_change_date()) {
|
||||
_barDetails = nullptr;
|
||||
} else if (!_barDetails) {
|
||||
_barDetails = std::make_unique<PeerBarDetails>();
|
||||
}
|
||||
if (_barDetails) {
|
||||
_barDetails->phoneCountryCode
|
||||
= qs(data.vphone_country().value_or_empty());
|
||||
_barDetails->registrationDate = ParseRegistrationDate(
|
||||
data.vregistration_month().value_or_empty());
|
||||
_barDetails->nameChangeDate
|
||||
= data.vname_change_date().value_or_empty();
|
||||
_barDetails->photoChangeDate
|
||||
= data.vphoto_change_date().value_or_empty();
|
||||
_barDetails->requestChatTitle
|
||||
= qs(data.vrequest_chat_title().value_or_empty());
|
||||
_barDetails->requestChatDate
|
||||
|
@ -835,7 +871,9 @@ int PeerData::paysPerMessage() const {
|
|||
void PeerData::clearPaysPerMessage() {
|
||||
if (const auto details = _barDetails.get()) {
|
||||
if (details->paysPerMessage) {
|
||||
if (details->businessBot || details->requestChatDate) {
|
||||
if (details->businessBot
|
||||
|| details->requestChatDate
|
||||
|| !details->phoneCountryCode.isEmpty()) {
|
||||
details->paysPerMessage = 0;
|
||||
} else {
|
||||
_barDetails = nullptr;
|
||||
|
@ -863,6 +901,28 @@ QString PeerData::businessBotManageUrl() const {
|
|||
return _barDetails ? _barDetails->businessBotManageUrl : QString();
|
||||
}
|
||||
|
||||
QString PeerData::phoneCountryCode() const {
|
||||
return _barDetails ? _barDetails->phoneCountryCode : QString();
|
||||
}
|
||||
|
||||
int PeerData::registrationMonth() const {
|
||||
return _barDetails
|
||||
? RegistrationMonth(_barDetails->registrationDate)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int PeerData::registrationYear() const {
|
||||
return _barDetails ? RegistrationYear(_barDetails->registrationDate) : 0;
|
||||
}
|
||||
|
||||
TimeId PeerData::nameChangeDate() const {
|
||||
return _barDetails ? _barDetails->nameChangeDate : 0;
|
||||
}
|
||||
|
||||
TimeId PeerData::photoChangeDate() const {
|
||||
return _barDetails ? _barDetails->photoChangeDate : 0;
|
||||
}
|
||||
|
||||
bool PeerData::changeColorIndex(
|
||||
const tl::conditional<MTPint> &cloudColorIndex) {
|
||||
return cloudColorIndex
|
||||
|
|
|
@ -173,6 +173,10 @@ inline constexpr bool is_flag_type(PeerBarSetting) { return true; };
|
|||
using PeerBarSettings = base::flags<PeerBarSetting>;
|
||||
|
||||
struct PeerBarDetails {
|
||||
QString phoneCountryCode;
|
||||
int registrationDate = 0; // YYYYMM or 0, YYYY > 2012, MM > 0.
|
||||
TimeId nameChangeDate = 0;
|
||||
TimeId photoChangeDate = 0;
|
||||
QString requestChatTitle;
|
||||
TimeId requestChatDate;
|
||||
UserData *businessBot = nullptr;
|
||||
|
@ -420,6 +424,11 @@ public:
|
|||
[[nodiscard]] UserData *businessBot() const;
|
||||
[[nodiscard]] QString businessBotManageUrl() const;
|
||||
void clearBusinessBot();
|
||||
[[nodiscard]] QString phoneCountryCode() const;
|
||||
[[nodiscard]] int registrationMonth() const;
|
||||
[[nodiscard]] int registrationYear() const;
|
||||
[[nodiscard]] TimeId nameChangeDate() const;
|
||||
[[nodiscard]] TimeId photoChangeDate() const;
|
||||
|
||||
enum class TranslationFlag : uchar {
|
||||
Unknown,
|
||||
|
|
|
@ -4350,6 +4350,9 @@ void HistoryInner::refreshAboutView(bool force) {
|
|||
if (!info->inited) {
|
||||
session().api().requestFullPeer(user);
|
||||
}
|
||||
} else if (!user->isContact()
|
||||
&& !user->phoneCountryCode().isEmpty()) {
|
||||
refresh();
|
||||
} else if (!historyHeight()) {
|
||||
if (user->starsPerMessage() > 0
|
||||
|| (user->requiresPremiumToWrite()
|
||||
|
|
|
@ -14,7 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "boxes/premium_preview_box.h"
|
||||
#include "chat_helpers/stickers_lottie.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "countries/countries_instance.h"
|
||||
#include "data/business/data_business_common.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
|
@ -22,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/media/history_view_service_box.h"
|
||||
#include "history/view/media/history_view_sticker_player_abstract.h"
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
#include "history/view/media/history_view_unique_gift.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
|
@ -43,6 +47,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
constexpr auto kLabelOpacity = 0.85;
|
||||
|
||||
class EmptyChatLockedBox final
|
||||
: public ServiceBoxContent
|
||||
, public base::has_weak_ptr {
|
||||
|
@ -156,6 +162,79 @@ auto GenerateChatIntro(
|
|||
};
|
||||
}
|
||||
|
||||
auto GenerateNewPeerInfo(
|
||||
not_null<Element*> parent,
|
||||
Element *replacing,
|
||||
not_null<UserData*> user)
|
||||
-> Fn<void(
|
||||
not_null<MediaGeneric*>,
|
||||
Fn<void(std::unique_ptr<MediaGenericPart>)>)> {
|
||||
return [=](
|
||||
not_null<MediaGeneric*> media,
|
||||
Fn<void(std::unique_ptr<MediaGenericPart>)> push) {
|
||||
const auto normalFg = [](const PaintContext &context) {
|
||||
return context.st->msgServiceFg()->c;
|
||||
};
|
||||
const auto fadedFg = [](const PaintContext &context) {
|
||||
auto result = context.st->msgServiceFg()->c;
|
||||
result.setAlphaF(result.alphaF() * kLabelOpacity);
|
||||
return result;
|
||||
};
|
||||
push(std::make_unique<MediaGenericTextPart>(
|
||||
Ui::Text::Bold(user->name()),
|
||||
st::newPeerTitleMargin));
|
||||
push(std::make_unique<TextPartColored>(
|
||||
tr::lng_new_contact_not_contact(tr::now, Ui::Text::WithEntities),
|
||||
st::newPeerSubtitleMargin,
|
||||
fadedFg));
|
||||
|
||||
auto entries = std::vector<AttributeTable::Entry>();
|
||||
const auto country = user->phoneCountryCode();
|
||||
if (!country.isEmpty()) {
|
||||
const auto &countries = Countries::Instance();
|
||||
const auto name = countries.countryNameByISO2(country);
|
||||
const auto flag = countries.flagEmojiByISO2(country);
|
||||
entries.push_back({
|
||||
tr::lng_new_contact_phone_number(tr::now),
|
||||
Ui::Text::Bold(flag + QChar(0xA0) + name),
|
||||
});
|
||||
}
|
||||
const auto month = user->registrationMonth();
|
||||
const auto year = user->registrationYear();
|
||||
if (month && year) {
|
||||
entries.push_back({
|
||||
tr::lng_new_contact_registration(tr::now),
|
||||
Ui::Text::Bold(langMonthOfYearFull(month, year)),
|
||||
});
|
||||
}
|
||||
|
||||
push(std::make_unique<AttributeTable>(
|
||||
std::move(entries),
|
||||
st::newPeerSubtitleMargin,
|
||||
fadedFg,
|
||||
normalFg));
|
||||
|
||||
const auto context = Core::MarkedTextContext{
|
||||
.session = &parent->history()->session(),
|
||||
.customEmojiRepaint = [parent] { parent->repaint(); },
|
||||
};
|
||||
const auto details = user->botVerifyDetails();
|
||||
const auto text = details
|
||||
? Data::SingleCustomEmoji(
|
||||
details->iconId
|
||||
).append(' ').append(details->description)
|
||||
: TextWithEntities().append(
|
||||
tr::lng_new_contact_not_official(tr::now));
|
||||
push(std::make_unique<TextPartColored>(
|
||||
text,
|
||||
st::newPeerSubtitleMargin,
|
||||
fadedFg,
|
||||
st::defaultTextStyle,
|
||||
base::flat_map<uint16, ClickHandlerPtr>(),
|
||||
context));
|
||||
};
|
||||
}
|
||||
|
||||
EmptyChatLockedBox::EmptyChatLockedBox(not_null<Element*> parent, Type type)
|
||||
: _parent(parent)
|
||||
, _type(type) {
|
||||
|
@ -277,7 +356,15 @@ bool AboutView::refresh() {
|
|||
const auto user = _history->peer->asUser();
|
||||
const auto info = user ? user->botInfo.get() : nullptr;
|
||||
if (!info) {
|
||||
if (user && !user->isSelf() && _history->isDisplayedEmpty()) {
|
||||
if (user
|
||||
&& !user->isContact()
|
||||
&& !user->phoneCountryCode().isEmpty()) {
|
||||
if (_item) {
|
||||
return false;
|
||||
}
|
||||
setItem(makeNewPeerInfo(user), nullptr);
|
||||
return true;
|
||||
} else if (user && !user->isSelf() && _history->isDisplayedEmpty()) {
|
||||
if (_item) {
|
||||
return false;
|
||||
} else if (user->requiresPremiumToWrite()
|
||||
|
@ -396,6 +483,27 @@ void AboutView::setItem(AdminLog::OwnedItem item, DocumentData *sticker) {
|
|||
toggleStickerRegistered(true);
|
||||
}
|
||||
|
||||
AdminLog::OwnedItem AboutView::makeNewPeerInfo(not_null<UserData*> user) {
|
||||
const auto text = user->name();
|
||||
const auto item = _history->makeMessage({
|
||||
.id = _history->nextNonHistoryEntryId(),
|
||||
.flags = (MessageFlag::FakeAboutView
|
||||
| MessageFlag::FakeHistoryItem
|
||||
| MessageFlag::Local),
|
||||
.from = _history->peer->id,
|
||||
}, PreparedServiceText{ { text }});
|
||||
|
||||
auto owned = AdminLog::OwnedItem(_delegate, item);
|
||||
owned->overrideMedia(std::make_unique<HistoryView::MediaGeneric>(
|
||||
owned.get(),
|
||||
GenerateNewPeerInfo(owned.get(), _item.get(), user),
|
||||
HistoryView::MediaGenericDescriptor{
|
||||
.service = true,
|
||||
.hideServiceText = true,
|
||||
}));
|
||||
return owned;
|
||||
}
|
||||
|
||||
AdminLog::OwnedItem AboutView::makeAboutVerifyCodes() {
|
||||
return makeAboutSimple(
|
||||
tr::lng_verification_codes_about(tr::now, Ui::Text::RichLangValue));
|
||||
|
|
|
@ -42,6 +42,8 @@ private:
|
|||
PhotoData *photo = nullptr);
|
||||
[[nodiscard]] AdminLog::OwnedItem makePremiumRequired();
|
||||
[[nodiscard]] AdminLog::OwnedItem makeStarsPerMessage(int stars);
|
||||
[[nodiscard]] AdminLog::OwnedItem makeNewPeerInfo(
|
||||
not_null<UserData*> user);
|
||||
[[nodiscard]] AdminLog::OwnedItem makeBlocked();
|
||||
void makeIntro(not_null<UserData*> user);
|
||||
void setItem(AdminLog::OwnedItem item, DocumentData *sticker);
|
||||
|
|
|
@ -39,60 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
class TextPartColored final : public MediaGenericTextPart {
|
||||
public:
|
||||
TextPartColored(
|
||||
TextWithEntities text,
|
||||
QMargins margins,
|
||||
QColor color,
|
||||
const style::TextStyle &st = st::defaultTextStyle,
|
||||
const base::flat_map<uint16, ClickHandlerPtr> &links = {},
|
||||
const std::any &context = {});
|
||||
|
||||
private:
|
||||
void setupPen(
|
||||
Painter &p,
|
||||
not_null<const MediaGeneric*> owner,
|
||||
const PaintContext &context) const override;
|
||||
|
||||
QColor _color;
|
||||
|
||||
};
|
||||
|
||||
class AttributeTable final : public MediaGenericPart {
|
||||
public:
|
||||
struct Entry {
|
||||
QString label;
|
||||
QString value;
|
||||
};
|
||||
|
||||
AttributeTable(
|
||||
std::vector<Entry> entries,
|
||||
QMargins margins,
|
||||
QColor labelColor);
|
||||
|
||||
void draw(
|
||||
Painter &p,
|
||||
not_null<const MediaGeneric*> owner,
|
||||
const PaintContext &context,
|
||||
int outerWidth) const override;
|
||||
|
||||
QSize countOptimalSize() override;
|
||||
QSize countCurrentSize(int newWidth) override;
|
||||
|
||||
private:
|
||||
struct Part {
|
||||
Ui::Text::String label;
|
||||
Ui::Text::String value;
|
||||
};
|
||||
|
||||
std::vector<Part> _parts;
|
||||
QMargins _margins;
|
||||
QColor _labelColor;
|
||||
int _valueLeft = 0;
|
||||
|
||||
};
|
||||
|
||||
class ButtonPart final : public MediaGenericPart {
|
||||
public:
|
||||
ButtonPart(
|
||||
|
@ -270,116 +216,7 @@ QSize ButtonPart::countCurrentSize(int newWidth) {
|
|||
return optimalSize();
|
||||
}
|
||||
|
||||
TextPartColored::TextPartColored(
|
||||
TextWithEntities text,
|
||||
QMargins margins,
|
||||
QColor color,
|
||||
const style::TextStyle &st,
|
||||
const base::flat_map<uint16, ClickHandlerPtr> &links,
|
||||
const std::any &context)
|
||||
: MediaGenericTextPart(text, margins, st, links, context)
|
||||
, _color(color) {
|
||||
}
|
||||
|
||||
void TextPartColored::setupPen(
|
||||
Painter &p,
|
||||
not_null<const MediaGeneric*> owner,
|
||||
const PaintContext &context) const {
|
||||
p.setPen(_color);
|
||||
}
|
||||
|
||||
AttributeTable::AttributeTable(
|
||||
std::vector<Entry> entries,
|
||||
QMargins margins,
|
||||
QColor labelColor)
|
||||
: _margins(margins)
|
||||
, _labelColor(labelColor) {
|
||||
for (const auto &entry : entries) {
|
||||
_parts.emplace_back();
|
||||
auto &part = _parts.back();
|
||||
part.label.setText(st::defaultTextStyle, entry.label);
|
||||
part.value.setMarkedText(
|
||||
st::defaultTextStyle,
|
||||
Ui::Text::Bold(entry.value));
|
||||
}
|
||||
}
|
||||
|
||||
void AttributeTable::draw(
|
||||
Painter &p,
|
||||
not_null<const MediaGeneric*> owner,
|
||||
const PaintContext &context,
|
||||
int outerWidth) const {
|
||||
const auto labelRight = _valueLeft - st::chatUniqueTableSkip;
|
||||
const auto palette = &context.st->serviceTextPalette();
|
||||
auto top = _margins.top();
|
||||
const auto paint = [&](
|
||||
const Ui::Text::String &text,
|
||||
int left,
|
||||
int availableWidth,
|
||||
style::align align) {
|
||||
text.draw(p, {
|
||||
.position = { left, top },
|
||||
.outerWidth = outerWidth,
|
||||
.availableWidth = availableWidth,
|
||||
.align = align,
|
||||
.palette = palette,
|
||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||
.now = context.now,
|
||||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
|
||||
.elisionLines = 1,
|
||||
});
|
||||
};
|
||||
const auto forLabel = labelRight - _margins.left();
|
||||
const auto forValue = width() - _valueLeft - _margins.right();
|
||||
const auto white = QColor(255, 255, 255);
|
||||
for (const auto &part : _parts) {
|
||||
p.setPen(_labelColor);
|
||||
paint(part.label, _margins.left(), forLabel, style::al_topright);
|
||||
p.setPen(white);
|
||||
paint(part.value, _valueLeft, forValue, style::al_topleft);
|
||||
top += st::normalFont->height + st::chatUniqueRowSkip;
|
||||
}
|
||||
}
|
||||
|
||||
QSize AttributeTable::countOptimalSize() {
|
||||
auto maxLabel = 0;
|
||||
auto maxValue = 0;
|
||||
for (const auto &part : _parts) {
|
||||
maxLabel = std::max(maxLabel, part.label.maxWidth());
|
||||
maxValue = std::max(maxValue, part.value.maxWidth());
|
||||
}
|
||||
const auto skip = st::chatUniqueTableSkip;
|
||||
const auto row = st::normalFont->height + st::chatUniqueRowSkip;
|
||||
const auto height = int(_parts.size()) * row - st::chatUniqueRowSkip;
|
||||
return {
|
||||
_margins.left() + maxLabel + skip + maxValue + _margins.right(),
|
||||
_margins.top() + height + _margins.bottom(),
|
||||
};
|
||||
}
|
||||
|
||||
QSize AttributeTable::countCurrentSize(int newWidth) {
|
||||
const auto skip = st::chatUniqueTableSkip;
|
||||
const auto width = newWidth - _margins.left() - _margins.right() - skip;
|
||||
auto maxLabel = 0;
|
||||
auto maxValue = 0;
|
||||
for (const auto &part : _parts) {
|
||||
maxLabel = std::max(maxLabel, part.label.maxWidth());
|
||||
maxValue = std::max(maxValue, part.value.maxWidth());
|
||||
}
|
||||
if (width <= 0 || !maxLabel) {
|
||||
_valueLeft = _margins.left();
|
||||
} else if (!maxValue) {
|
||||
_valueLeft = newWidth - _margins.right();
|
||||
} else {
|
||||
_valueLeft = _margins.left()
|
||||
+ int((int64(maxLabel) * width) / (maxLabel + maxValue))
|
||||
+ skip;
|
||||
}
|
||||
return { newWidth, minHeight() };
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
} // namespace
|
||||
|
||||
auto GenerateUniqueGiftMedia(
|
||||
not_null<Element*> parent,
|
||||
|
@ -402,7 +239,7 @@ auto GenerateUniqueGiftMedia(
|
|||
push(std::make_unique<TextPartColored>(
|
||||
std::move(text),
|
||||
margins,
|
||||
color,
|
||||
[color](const auto&) { return color; },
|
||||
st));
|
||||
};
|
||||
|
||||
|
@ -447,15 +284,19 @@ auto GenerateUniqueGiftMedia(
|
|||
gift->backdrop.textColor,
|
||||
st::chatUniqueTextPadding);
|
||||
|
||||
const auto name = [](const Data::UniqueGiftAttribute &value) {
|
||||
return Ui::Text::Bold(value.name);
|
||||
};
|
||||
auto attributes = std::vector<AttributeTable::Entry>{
|
||||
{ tr::lng_gift_unique_model(tr::now), gift->model.name },
|
||||
{ tr::lng_gift_unique_backdrop(tr::now), gift->backdrop.name },
|
||||
{ tr::lng_gift_unique_symbol(tr::now), gift->pattern.name },
|
||||
{ tr::lng_gift_unique_model(tr::now), name(gift->model) },
|
||||
{ tr::lng_gift_unique_backdrop(tr::now), name(gift->backdrop) },
|
||||
{ tr::lng_gift_unique_symbol(tr::now), name(gift->pattern) },
|
||||
};
|
||||
push(std::make_unique<AttributeTable>(
|
||||
std::move(attributes),
|
||||
st::chatUniqueTextPadding,
|
||||
gift->backdrop.textColor));
|
||||
[c = gift->backdrop.textColor](const auto&) { return c; },
|
||||
[](const auto&) { return QColor(255, 255, 255); }));
|
||||
|
||||
auto link = OpenStarGiftLink(parent->data());
|
||||
push(std::make_unique<ButtonPart>(
|
||||
|
@ -594,4 +435,117 @@ std::unique_ptr<MediaGenericPart> MakeGenericButtonPart(
|
|||
return std::make_unique<ButtonPart>(text, margins, repaint, link, bg);
|
||||
}
|
||||
|
||||
TextPartColored::TextPartColored(
|
||||
TextWithEntities text,
|
||||
QMargins margins,
|
||||
Fn<QColor(const PaintContext &)> color,
|
||||
const style::TextStyle &st,
|
||||
const base::flat_map<uint16, ClickHandlerPtr> &links,
|
||||
const std::any &context)
|
||||
: MediaGenericTextPart(text, margins, st, links, context)
|
||||
, _color(std::move(color)) {
|
||||
}
|
||||
|
||||
void TextPartColored::setupPen(
|
||||
Painter &p,
|
||||
not_null<const MediaGeneric*> owner,
|
||||
const PaintContext &context) const {
|
||||
p.setPen(_color(context));
|
||||
}
|
||||
|
||||
AttributeTable::AttributeTable(
|
||||
std::vector<Entry> entries,
|
||||
QMargins margins,
|
||||
Fn<QColor(const PaintContext &)> labelColor,
|
||||
Fn<QColor(const PaintContext &)> valueColor,
|
||||
const std::any &context)
|
||||
: _margins(margins)
|
||||
, _labelColor(std::move(labelColor))
|
||||
, _valueColor(std::move(valueColor)) {
|
||||
for (const auto &entry : entries) {
|
||||
_parts.emplace_back();
|
||||
auto &part = _parts.back();
|
||||
part.label.setText(st::defaultTextStyle, entry.label);
|
||||
part.value.setMarkedText(
|
||||
st::defaultTextStyle,
|
||||
entry.value,
|
||||
kMarkupTextOptions,
|
||||
context);
|
||||
}
|
||||
}
|
||||
|
||||
void AttributeTable::draw(
|
||||
Painter &p,
|
||||
not_null<const MediaGeneric*> owner,
|
||||
const PaintContext &context,
|
||||
int outerWidth) const {
|
||||
const auto labelRight = _valueLeft - st::chatUniqueTableSkip;
|
||||
const auto palette = &context.st->serviceTextPalette();
|
||||
auto top = _margins.top();
|
||||
const auto paint = [&](
|
||||
const Ui::Text::String &text,
|
||||
int left,
|
||||
int availableWidth,
|
||||
style::align align) {
|
||||
text.draw(p, {
|
||||
.position = { left, top },
|
||||
.outerWidth = outerWidth,
|
||||
.availableWidth = availableWidth,
|
||||
.align = align,
|
||||
.palette = palette,
|
||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||
.now = context.now,
|
||||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
|
||||
.elisionLines = 1,
|
||||
});
|
||||
};
|
||||
const auto forLabel = labelRight - _margins.left();
|
||||
const auto forValue = width() - _valueLeft - _margins.right();
|
||||
for (const auto &part : _parts) {
|
||||
p.setPen(_labelColor(context));
|
||||
paint(part.label, _margins.left(), forLabel, style::al_topright);
|
||||
p.setPen(_valueColor(context));
|
||||
paint(part.value, _valueLeft, forValue, style::al_topleft);
|
||||
top += st::normalFont->height + st::chatUniqueRowSkip;
|
||||
}
|
||||
}
|
||||
|
||||
QSize AttributeTable::countOptimalSize() {
|
||||
auto maxLabel = 0;
|
||||
auto maxValue = 0;
|
||||
for (const auto &part : _parts) {
|
||||
maxLabel = std::max(maxLabel, part.label.maxWidth());
|
||||
maxValue = std::max(maxValue, part.value.maxWidth());
|
||||
}
|
||||
const auto skip = st::chatUniqueTableSkip;
|
||||
const auto row = st::normalFont->height + st::chatUniqueRowSkip;
|
||||
const auto height = int(_parts.size()) * row - st::chatUniqueRowSkip;
|
||||
return {
|
||||
_margins.left() + maxLabel + skip + maxValue + _margins.right(),
|
||||
_margins.top() + height + _margins.bottom(),
|
||||
};
|
||||
}
|
||||
|
||||
QSize AttributeTable::countCurrentSize(int newWidth) {
|
||||
const auto skip = st::chatUniqueTableSkip;
|
||||
const auto width = newWidth - _margins.left() - _margins.right() - skip;
|
||||
auto maxLabel = 0;
|
||||
auto maxValue = 0;
|
||||
for (const auto &part : _parts) {
|
||||
maxLabel = std::max(maxLabel, part.label.maxWidth());
|
||||
maxValue = std::max(maxValue, part.value.maxWidth());
|
||||
}
|
||||
if (width <= 0 || !maxLabel) {
|
||||
_valueLeft = _margins.left();
|
||||
} else if (!maxValue) {
|
||||
_valueLeft = newWidth - _margins.right();
|
||||
} else {
|
||||
_valueLeft = _margins.left()
|
||||
+ int((int64(maxLabel) * width) / (maxLabel + maxValue))
|
||||
+ skip;
|
||||
}
|
||||
return { newWidth, minHeight() };
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "history/view/media/history_view_media_generic.h"
|
||||
|
||||
class Painter;
|
||||
|
||||
namespace Data {
|
||||
|
@ -55,4 +57,62 @@ class MediaGenericPart;
|
|||
ClickHandlerPtr link,
|
||||
QColor bg = QColor(0, 0, 0, 0));
|
||||
|
||||
|
||||
class TextPartColored : public MediaGenericTextPart {
|
||||
public:
|
||||
TextPartColored(
|
||||
TextWithEntities text,
|
||||
QMargins margins,
|
||||
Fn<QColor(const PaintContext &)> color,
|
||||
const style::TextStyle &st = st::defaultTextStyle,
|
||||
const base::flat_map<uint16, ClickHandlerPtr> &links = {},
|
||||
const std::any &context = {});
|
||||
|
||||
private:
|
||||
void setupPen(
|
||||
Painter &p,
|
||||
not_null<const MediaGeneric*> owner,
|
||||
const PaintContext &context) const override;
|
||||
|
||||
Fn<QColor(const PaintContext &)> _color;
|
||||
|
||||
};
|
||||
|
||||
class AttributeTable final : public MediaGenericPart {
|
||||
public:
|
||||
struct Entry {
|
||||
QString label;
|
||||
TextWithEntities value;
|
||||
};
|
||||
|
||||
AttributeTable(
|
||||
std::vector<Entry> entries,
|
||||
QMargins margins,
|
||||
Fn<QColor(const PaintContext &)> labelColor,
|
||||
Fn<QColor(const PaintContext &)> valueColor,
|
||||
const std::any &context = {});
|
||||
|
||||
void draw(
|
||||
Painter &p,
|
||||
not_null<const MediaGeneric*> owner,
|
||||
const PaintContext &context,
|
||||
int outerWidth) const override;
|
||||
|
||||
QSize countOptimalSize() override;
|
||||
QSize countCurrentSize(int newWidth) override;
|
||||
|
||||
private:
|
||||
struct Part {
|
||||
Ui::Text::String label;
|
||||
Ui::Text::String value;
|
||||
};
|
||||
|
||||
std::vector<Part> _parts;
|
||||
QMargins _margins;
|
||||
Fn<QColor(const PaintContext &)> _labelColor;
|
||||
Fn<QColor(const PaintContext &)> _valueColor;
|
||||
int _valueLeft = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -1225,3 +1225,6 @@ chatUniqueRowSkip: 4px;
|
|||
chatUniqueButtonPadding: margins(12px, 4px, 12px, 16px);
|
||||
|
||||
markupWebview: icon {{ "chat/markup_webview", windowFg }};
|
||||
|
||||
newPeerTitleMargin: margins(11px, 16px, 11px, 6px);
|
||||
newPeerSubtitleMargin: margins(11px, 0px, 11px, 16px);
|
||||
|
|
Loading…
Add table
Reference in a new issue