mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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_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" = "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_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_channel" = "Response to your join request";
|
||||||
"lng_from_request_title_group" = "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}.";
|
"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();
|
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
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
@ -734,7 +756,9 @@ void PeerData::checkFolder(FolderId folderId) {
|
||||||
|
|
||||||
void PeerData::clearBusinessBot() {
|
void PeerData::clearBusinessBot() {
|
||||||
if (const auto details = _barDetails.get()) {
|
if (const auto details = _barDetails.get()) {
|
||||||
if (details->requestChatDate || details->paysPerMessage) {
|
if (details->requestChatDate
|
||||||
|
|| details->paysPerMessage
|
||||||
|
|| !details->phoneCountryCode.isEmpty()) {
|
||||||
details->businessBot = nullptr;
|
details->businessBot = nullptr;
|
||||||
details->businessBotManageUrl = QString();
|
details->businessBotManageUrl = QString();
|
||||||
} else {
|
} else {
|
||||||
|
@ -780,12 +804,24 @@ void PeerData::setBarSettings(const MTPPeerSettings &data) {
|
||||||
const auto wasPaysPerMessage = paysPerMessage();
|
const auto wasPaysPerMessage = paysPerMessage();
|
||||||
if (!data.vbusiness_bot_id()
|
if (!data.vbusiness_bot_id()
|
||||||
&& !data.vrequest_chat_title()
|
&& !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;
|
_barDetails = nullptr;
|
||||||
} else if (!_barDetails) {
|
} else if (!_barDetails) {
|
||||||
_barDetails = std::make_unique<PeerBarDetails>();
|
_barDetails = std::make_unique<PeerBarDetails>();
|
||||||
}
|
}
|
||||||
if (_barDetails) {
|
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
|
_barDetails->requestChatTitle
|
||||||
= qs(data.vrequest_chat_title().value_or_empty());
|
= qs(data.vrequest_chat_title().value_or_empty());
|
||||||
_barDetails->requestChatDate
|
_barDetails->requestChatDate
|
||||||
|
@ -835,7 +871,9 @@ int PeerData::paysPerMessage() const {
|
||||||
void PeerData::clearPaysPerMessage() {
|
void PeerData::clearPaysPerMessage() {
|
||||||
if (const auto details = _barDetails.get()) {
|
if (const auto details = _barDetails.get()) {
|
||||||
if (details->paysPerMessage) {
|
if (details->paysPerMessage) {
|
||||||
if (details->businessBot || details->requestChatDate) {
|
if (details->businessBot
|
||||||
|
|| details->requestChatDate
|
||||||
|
|| !details->phoneCountryCode.isEmpty()) {
|
||||||
details->paysPerMessage = 0;
|
details->paysPerMessage = 0;
|
||||||
} else {
|
} else {
|
||||||
_barDetails = nullptr;
|
_barDetails = nullptr;
|
||||||
|
@ -863,6 +901,28 @@ QString PeerData::businessBotManageUrl() const {
|
||||||
return _barDetails ? _barDetails->businessBotManageUrl : QString();
|
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(
|
bool PeerData::changeColorIndex(
|
||||||
const tl::conditional<MTPint> &cloudColorIndex) {
|
const tl::conditional<MTPint> &cloudColorIndex) {
|
||||||
return cloudColorIndex
|
return cloudColorIndex
|
||||||
|
|
|
@ -173,6 +173,10 @@ inline constexpr bool is_flag_type(PeerBarSetting) { return true; };
|
||||||
using PeerBarSettings = base::flags<PeerBarSetting>;
|
using PeerBarSettings = base::flags<PeerBarSetting>;
|
||||||
|
|
||||||
struct PeerBarDetails {
|
struct PeerBarDetails {
|
||||||
|
QString phoneCountryCode;
|
||||||
|
int registrationDate = 0; // YYYYMM or 0, YYYY > 2012, MM > 0.
|
||||||
|
TimeId nameChangeDate = 0;
|
||||||
|
TimeId photoChangeDate = 0;
|
||||||
QString requestChatTitle;
|
QString requestChatTitle;
|
||||||
TimeId requestChatDate;
|
TimeId requestChatDate;
|
||||||
UserData *businessBot = nullptr;
|
UserData *businessBot = nullptr;
|
||||||
|
@ -420,6 +424,11 @@ public:
|
||||||
[[nodiscard]] UserData *businessBot() const;
|
[[nodiscard]] UserData *businessBot() const;
|
||||||
[[nodiscard]] QString businessBotManageUrl() const;
|
[[nodiscard]] QString businessBotManageUrl() const;
|
||||||
void clearBusinessBot();
|
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 {
|
enum class TranslationFlag : uchar {
|
||||||
Unknown,
|
Unknown,
|
||||||
|
|
|
@ -4350,6 +4350,9 @@ void HistoryInner::refreshAboutView(bool force) {
|
||||||
if (!info->inited) {
|
if (!info->inited) {
|
||||||
session().api().requestFullPeer(user);
|
session().api().requestFullPeer(user);
|
||||||
}
|
}
|
||||||
|
} else if (!user->isContact()
|
||||||
|
&& !user->phoneCountryCode().isEmpty()) {
|
||||||
|
refresh();
|
||||||
} else if (!historyHeight()) {
|
} else if (!historyHeight()) {
|
||||||
if (user->starsPerMessage() > 0
|
if (user->starsPerMessage() > 0
|
||||||
|| (user->requiresPremiumToWrite()
|
|| (user->requiresPremiumToWrite()
|
||||||
|
|
|
@ -14,7 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/premium_preview_box.h"
|
#include "boxes/premium_preview_box.h"
|
||||||
#include "chat_helpers/stickers_lottie.h"
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
#include "core/click_handler_types.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/business/data_business_common.h"
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.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_service_box.h"
|
||||||
#include "history/view/media/history_view_sticker_player_abstract.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_sticker.h"
|
||||||
|
#include "history/view/media/history_view_unique_gift.h"
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
@ -43,6 +47,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kLabelOpacity = 0.85;
|
||||||
|
|
||||||
class EmptyChatLockedBox final
|
class EmptyChatLockedBox final
|
||||||
: public ServiceBoxContent
|
: public ServiceBoxContent
|
||||||
, public base::has_weak_ptr {
|
, 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)
|
EmptyChatLockedBox::EmptyChatLockedBox(not_null<Element*> parent, Type type)
|
||||||
: _parent(parent)
|
: _parent(parent)
|
||||||
, _type(type) {
|
, _type(type) {
|
||||||
|
@ -277,7 +356,15 @@ bool AboutView::refresh() {
|
||||||
const auto user = _history->peer->asUser();
|
const auto user = _history->peer->asUser();
|
||||||
const auto info = user ? user->botInfo.get() : nullptr;
|
const auto info = user ? user->botInfo.get() : nullptr;
|
||||||
if (!info) {
|
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) {
|
if (_item) {
|
||||||
return false;
|
return false;
|
||||||
} else if (user->requiresPremiumToWrite()
|
} else if (user->requiresPremiumToWrite()
|
||||||
|
@ -396,6 +483,27 @@ void AboutView::setItem(AdminLog::OwnedItem item, DocumentData *sticker) {
|
||||||
toggleStickerRegistered(true);
|
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() {
|
AdminLog::OwnedItem AboutView::makeAboutVerifyCodes() {
|
||||||
return makeAboutSimple(
|
return makeAboutSimple(
|
||||||
tr::lng_verification_codes_about(tr::now, Ui::Text::RichLangValue));
|
tr::lng_verification_codes_about(tr::now, Ui::Text::RichLangValue));
|
||||||
|
|
|
@ -42,6 +42,8 @@ private:
|
||||||
PhotoData *photo = nullptr);
|
PhotoData *photo = nullptr);
|
||||||
[[nodiscard]] AdminLog::OwnedItem makePremiumRequired();
|
[[nodiscard]] AdminLog::OwnedItem makePremiumRequired();
|
||||||
[[nodiscard]] AdminLog::OwnedItem makeStarsPerMessage(int stars);
|
[[nodiscard]] AdminLog::OwnedItem makeStarsPerMessage(int stars);
|
||||||
|
[[nodiscard]] AdminLog::OwnedItem makeNewPeerInfo(
|
||||||
|
not_null<UserData*> user);
|
||||||
[[nodiscard]] AdminLog::OwnedItem makeBlocked();
|
[[nodiscard]] AdminLog::OwnedItem makeBlocked();
|
||||||
void makeIntro(not_null<UserData*> user);
|
void makeIntro(not_null<UserData*> user);
|
||||||
void setItem(AdminLog::OwnedItem item, DocumentData *sticker);
|
void setItem(AdminLog::OwnedItem item, DocumentData *sticker);
|
||||||
|
|
|
@ -39,60 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
namespace {
|
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 {
|
class ButtonPart final : public MediaGenericPart {
|
||||||
public:
|
public:
|
||||||
ButtonPart(
|
ButtonPart(
|
||||||
|
@ -270,116 +216,7 @@ QSize ButtonPart::countCurrentSize(int newWidth) {
|
||||||
return optimalSize();
|
return optimalSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
TextPartColored::TextPartColored(
|
} // namespace
|
||||||
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
|
|
||||||
|
|
||||||
auto GenerateUniqueGiftMedia(
|
auto GenerateUniqueGiftMedia(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
|
@ -402,7 +239,7 @@ auto GenerateUniqueGiftMedia(
|
||||||
push(std::make_unique<TextPartColored>(
|
push(std::make_unique<TextPartColored>(
|
||||||
std::move(text),
|
std::move(text),
|
||||||
margins,
|
margins,
|
||||||
color,
|
[color](const auto&) { return color; },
|
||||||
st));
|
st));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -447,15 +284,19 @@ auto GenerateUniqueGiftMedia(
|
||||||
gift->backdrop.textColor,
|
gift->backdrop.textColor,
|
||||||
st::chatUniqueTextPadding);
|
st::chatUniqueTextPadding);
|
||||||
|
|
||||||
|
const auto name = [](const Data::UniqueGiftAttribute &value) {
|
||||||
|
return Ui::Text::Bold(value.name);
|
||||||
|
};
|
||||||
auto attributes = std::vector<AttributeTable::Entry>{
|
auto attributes = std::vector<AttributeTable::Entry>{
|
||||||
{ tr::lng_gift_unique_model(tr::now), gift->model.name },
|
{ tr::lng_gift_unique_model(tr::now), name(gift->model) },
|
||||||
{ tr::lng_gift_unique_backdrop(tr::now), gift->backdrop.name },
|
{ tr::lng_gift_unique_backdrop(tr::now), name(gift->backdrop) },
|
||||||
{ tr::lng_gift_unique_symbol(tr::now), gift->pattern.name },
|
{ tr::lng_gift_unique_symbol(tr::now), name(gift->pattern) },
|
||||||
};
|
};
|
||||||
push(std::make_unique<AttributeTable>(
|
push(std::make_unique<AttributeTable>(
|
||||||
std::move(attributes),
|
std::move(attributes),
|
||||||
st::chatUniqueTextPadding,
|
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());
|
auto link = OpenStarGiftLink(parent->data());
|
||||||
push(std::make_unique<ButtonPart>(
|
push(std::make_unique<ButtonPart>(
|
||||||
|
@ -594,4 +435,117 @@ std::unique_ptr<MediaGenericPart> MakeGenericButtonPart(
|
||||||
return std::make_unique<ButtonPart>(text, margins, repaint, link, bg);
|
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
|
} // namespace HistoryView
|
||||||
|
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "history/view/media/history_view_media_generic.h"
|
||||||
|
|
||||||
class Painter;
|
class Painter;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
@ -55,4 +57,62 @@ class MediaGenericPart;
|
||||||
ClickHandlerPtr link,
|
ClickHandlerPtr link,
|
||||||
QColor bg = QColor(0, 0, 0, 0));
|
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
|
} // namespace HistoryView
|
||||||
|
|
|
@ -1225,3 +1225,6 @@ chatUniqueRowSkip: 4px;
|
||||||
chatUniqueButtonPadding: margins(12px, 4px, 12px, 16px);
|
chatUniqueButtonPadding: margins(12px, 4px, 12px, 16px);
|
||||||
|
|
||||||
markupWebview: icon {{ "chat/markup_webview", windowFg }};
|
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