mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Support nice empty topic view.
This commit is contained in:
parent
99564d3d44
commit
c8ed8e0e5f
18 changed files with 418 additions and 237 deletions
|
@ -1509,7 +1509,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
|
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
|
||||||
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
|
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
|
||||||
"lng_action_topic_created_inside" = "Topic created";
|
"lng_action_topic_created_inside" = "Topic created";
|
||||||
"lng_action_topic_closed_inside" = "Topics closed";
|
"lng_action_topic_closed_inside" = "Topic closed";
|
||||||
"lng_action_topic_reopened_inside" = "Topic reopened";
|
"lng_action_topic_reopened_inside" = "Topic reopened";
|
||||||
"lng_action_topic_created" = "{topic} — was created";
|
"lng_action_topic_created" = "{topic} — was created";
|
||||||
"lng_action_topic_closed" = "{topic} — was closed";
|
"lng_action_topic_closed" = "{topic} — was closed";
|
||||||
|
@ -3536,9 +3536,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_forum_topic_delete" = "Delete";
|
"lng_forum_topic_delete" = "Delete";
|
||||||
"lng_forum_topic_delete_sure" = "Are you sure you want to delete this topic?";
|
"lng_forum_topic_delete_sure" = "Are you sure you want to delete this topic?";
|
||||||
"lng_forum_topic_created_title_my" = "Almost done!";
|
"lng_forum_topic_created_title_my" = "Almost done!";
|
||||||
"lng_forum_topic_created_body_my" = "Send the first message to start this topic.";
|
"lng_forum_topic_created_body_my" = "Send the first message\nto start this topic.";
|
||||||
"lng_forum_topic_created_title" = "Topic started!";
|
"lng_forum_topic_created_title" = "Topic started!";
|
||||||
"lng_forum_topic_created_body" = "Send a message to open the discussion";
|
"lng_forum_topic_created_body" = "Send a message to open\nthe discussion.";
|
||||||
"lng_forum_topics_switch" = "Topics";
|
"lng_forum_topics_switch" = "Topics";
|
||||||
"lng_forum_topics_not_enough#one" = "Only groups with more than **{count} member** can have topics enabled.";
|
"lng_forum_topics_not_enough#one" = "Only groups with more than **{count} member** can have topics enabled.";
|
||||||
"lng_forum_topics_not_enough#other" = "Only groups with more than **{count} members** can have topics enabled.";
|
"lng_forum_topics_not_enough#other" = "Only groups with more than **{count} members** can have topics enabled.";
|
||||||
|
|
|
@ -2016,6 +2016,10 @@ void ApiWrap::saveCurrentDraftToCloud() {
|
||||||
for (const auto &controller : _session->windows()) {
|
for (const auto &controller : _session->windows()) {
|
||||||
controller->materializeLocalDrafts();
|
controller->materializeLocalDrafts();
|
||||||
if (const auto thread = controller->activeChatCurrent().thread()) {
|
if (const auto thread = controller->activeChatCurrent().thread()) {
|
||||||
|
const auto topic = thread->asTopic();
|
||||||
|
if (topic && topic->creating()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const auto history = thread->owningHistory();
|
const auto history = thread->owningHistory();
|
||||||
_session->local().writeDrafts(history);
|
_session->local().writeDrafts(history);
|
||||||
|
|
||||||
|
|
|
@ -468,7 +468,6 @@ void EditForumTopicBox(
|
||||||
state->iconId = (iconId != kDefaultIconId) ? iconId : 0;
|
state->iconId = (iconId != kDefaultIconId) ? iconId : 0;
|
||||||
}, box->lifetime());
|
}, box->lifetime());
|
||||||
|
|
||||||
const auto requestId = std::make_shared<mtpRequestId>();
|
|
||||||
const auto create = [=] {
|
const auto create = [=] {
|
||||||
const auto channel = forum->peer->asChannel();
|
const auto channel = forum->peer->asChannel();
|
||||||
if (!channel || !channel->isForum()) {
|
if (!channel || !channel->isForum()) {
|
||||||
|
@ -505,9 +504,11 @@ void EditForumTopicBox(
|
||||||
topic->applyTitle(title->getLastText().trimmed());
|
topic->applyTitle(title->getLastText().trimmed());
|
||||||
topic->applyColorId(state->defaultIcon.current().colorId);
|
topic->applyColorId(state->defaultIcon.current().colorId);
|
||||||
topic->applyIconId(state->iconId.current());
|
topic->applyIconId(state->iconId.current());
|
||||||
|
box->closeBox();
|
||||||
} else {
|
} else {
|
||||||
using Flag = MTPchannels_EditForumTopic::Flag;
|
using Flag = MTPchannels_EditForumTopic::Flag;
|
||||||
const auto api = &forum->session().api();
|
const auto api = &forum->session().api();
|
||||||
|
const auto weak = Ui::MakeWeak(box.get());
|
||||||
state->requestId = api->request(MTPchannels_EditForumTopic(
|
state->requestId = api->request(MTPchannels_EditForumTopic(
|
||||||
MTP_flags(Flag::f_title | Flag::f_icon_emoji_id),
|
MTP_flags(Flag::f_title | Flag::f_icon_emoji_id),
|
||||||
topic->channel()->inputChannel,
|
topic->channel()->inputChannel,
|
||||||
|
@ -517,12 +518,16 @@ void EditForumTopicBox(
|
||||||
MTPBool() // closed
|
MTPBool() // closed
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result) {
|
||||||
api->applyUpdates(result);
|
api->applyUpdates(result);
|
||||||
box->closeBox();
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->closeBox();
|
||||||
|
}
|
||||||
}).fail([=](const MTP::Error &error) {
|
}).fail([=](const MTP::Error &error) {
|
||||||
if (error.type() == u"TOPIC_NOT_MODIFIED") {
|
if (const auto strong = weak.data()) {
|
||||||
box->closeBox();
|
if (error.type() == u"TOPIC_NOT_MODIFIED") {
|
||||||
} else {
|
strong->closeBox();
|
||||||
state->requestId = -1;
|
} else {
|
||||||
|
state->requestId = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
|
@ -589,7 +589,7 @@ bool AddReplyToMessageAction(
|
||||||
const auto peer = item ? item->history()->peer.get() : nullptr;
|
const auto peer = item ? item->history()->peer.get() : nullptr;
|
||||||
if (!item
|
if (!item
|
||||||
|| !item->isRegular()
|
|| !item->isRegular()
|
||||||
|| (topic ? topic->canWrite() : !peer->canWrite())
|
|| (topic ? !topic->canWrite() : !peer->canWrite())
|
||||||
|| (context != Context::History && context != Context::Replies)) {
|
|| (context != Context::History && context != Context::Replies)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -702,7 +702,7 @@ auto Element::contextDependentServiceText() -> TextWithLinks {
|
||||||
const QString &title,
|
const QString &title,
|
||||||
std::optional<DocumentId> iconId) {
|
std::optional<DocumentId> iconId) {
|
||||||
auto result = TextWithEntities{ title };
|
auto result = TextWithEntities{ title };
|
||||||
auto full = iconId
|
auto full = (iconId && *iconId)
|
||||||
? wrapIcon(*iconId).append(' ').append(std::move(result))
|
? wrapIcon(*iconId).append(' ').append(std::move(result))
|
||||||
: result;
|
: result;
|
||||||
return Ui::Text::Link(std::move(full), topicUrl);
|
return Ui::Text::Link(std::move(full), topicUrl);
|
||||||
|
|
|
@ -1908,167 +1908,170 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
||||||
return this->itemTop(elem) < bottom;
|
return this->itemTop(elem) < bottom;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (from != end(_items)) {
|
auto context = preparePaintContext(clip);
|
||||||
_reactionsManager->startEffectsCollection();
|
if (from == end(_items)) {
|
||||||
|
_delegate->listPaintEmpty(p, context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_reactionsManager->startEffectsCollection();
|
||||||
|
|
||||||
const auto session = &controller()->session();
|
const auto session = &controller()->session();
|
||||||
auto top = itemTop(from->get());
|
auto top = itemTop(from->get());
|
||||||
auto context = preparePaintContext(clip).translated(0, -top);
|
context = context.translated(0, -top);
|
||||||
p.translate(0, top);
|
p.translate(0, top);
|
||||||
const auto &sendingAnimation = _controller->sendingAnimation();
|
const auto &sendingAnimation = _controller->sendingAnimation();
|
||||||
for (auto i = from; i != to; ++i) {
|
for (auto i = from; i != to; ++i) {
|
||||||
const auto view = *i;
|
const auto view = *i;
|
||||||
const auto item = view->data();
|
const auto item = view->data();
|
||||||
const auto height = view->height();
|
const auto height = view->height();
|
||||||
if (!sendingAnimation.hasAnimatedMessage(item)) {
|
if (!sendingAnimation.hasAnimatedMessage(item)) {
|
||||||
context.reactionInfo
|
context.reactionInfo
|
||||||
= _reactionsManager->currentReactionPaintInfo();
|
= _reactionsManager->currentReactionPaintInfo();
|
||||||
context.outbg = view->hasOutLayout();
|
context.outbg = view->hasOutLayout();
|
||||||
context.selection = itemRenderSelection(view);
|
context.selection = itemRenderSelection(view);
|
||||||
view->draw(p, context);
|
view->draw(p, context);
|
||||||
}
|
|
||||||
const auto isSponsored = item->isSponsored();
|
|
||||||
const auto isUnread = _delegate->listElementShownUnread(view)
|
|
||||||
&& item->isRegular();
|
|
||||||
const auto withReaction = item->hasUnreadReaction();
|
|
||||||
const auto yShown = [&](int y) {
|
|
||||||
return (_visibleBottom >= y && _visibleTop <= y);
|
|
||||||
};
|
|
||||||
const auto markShown = isSponsored
|
|
||||||
? view->markSponsoredViewed(_visibleBottom - top)
|
|
||||||
: withReaction
|
|
||||||
? yShown(top + context.reactionInfo->position.y())
|
|
||||||
: isUnread
|
|
||||||
? yShown(top + height)
|
|
||||||
: yShown(top + height / 2);
|
|
||||||
if (markShown) {
|
|
||||||
if (isSponsored) {
|
|
||||||
session->data().sponsoredMessages().view(
|
|
||||||
item->fullId());
|
|
||||||
} else if (isUnread) {
|
|
||||||
readTill = item;
|
|
||||||
}
|
|
||||||
if (item->hasViews()) {
|
|
||||||
session->api().views().scheduleIncrement(item);
|
|
||||||
}
|
|
||||||
if (withReaction) {
|
|
||||||
readContents.insert(item);
|
|
||||||
} else if (item->isUnreadMention()
|
|
||||||
&& !item->isUnreadMedia()) {
|
|
||||||
readContents.insert(item);
|
|
||||||
_highlighter.enqueue(view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
session->data().reactions().poll(item, context.now);
|
|
||||||
if (item->hasExtendedMediaPreview()) {
|
|
||||||
session->api().views().pollExtendedMedia(item);
|
|
||||||
}
|
|
||||||
_reactionsManager->recordCurrentReactionEffect(
|
|
||||||
item->fullId(),
|
|
||||||
QPoint(0, top));
|
|
||||||
top += height;
|
|
||||||
context.translate(0, -height);
|
|
||||||
p.translate(0, height);
|
|
||||||
}
|
}
|
||||||
context.translate(0, top);
|
const auto isSponsored = item->isSponsored();
|
||||||
p.translate(0, -top);
|
const auto isUnread = _delegate->listElementShownUnread(view)
|
||||||
|
&& item->isRegular();
|
||||||
enumerateUserpics([&](not_null<Element*> view, int userpicTop) {
|
const auto withReaction = item->hasUnreadReaction();
|
||||||
// stop the enumeration if the userpic is below the painted rect
|
const auto yShown = [&](int y) {
|
||||||
if (userpicTop >= clip.top() + clip.height()) {
|
return (_visibleBottom >= y && _visibleTop <= y);
|
||||||
return false;
|
};
|
||||||
|
const auto markShown = isSponsored
|
||||||
|
? view->markSponsoredViewed(_visibleBottom - top)
|
||||||
|
: withReaction
|
||||||
|
? yShown(top + context.reactionInfo->position.y())
|
||||||
|
: isUnread
|
||||||
|
? yShown(top + height)
|
||||||
|
: yShown(top + height / 2);
|
||||||
|
if (markShown) {
|
||||||
|
if (isSponsored) {
|
||||||
|
session->data().sponsoredMessages().view(
|
||||||
|
item->fullId());
|
||||||
|
} else if (isUnread) {
|
||||||
|
readTill = item;
|
||||||
}
|
}
|
||||||
|
if (item->hasViews()) {
|
||||||
|
session->api().views().scheduleIncrement(item);
|
||||||
|
}
|
||||||
|
if (withReaction) {
|
||||||
|
readContents.insert(item);
|
||||||
|
} else if (item->isUnreadMention()
|
||||||
|
&& !item->isUnreadMedia()) {
|
||||||
|
readContents.insert(item);
|
||||||
|
_highlighter.enqueue(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session->data().reactions().poll(item, context.now);
|
||||||
|
if (item->hasExtendedMediaPreview()) {
|
||||||
|
session->api().views().pollExtendedMedia(item);
|
||||||
|
}
|
||||||
|
_reactionsManager->recordCurrentReactionEffect(
|
||||||
|
item->fullId(),
|
||||||
|
QPoint(0, top));
|
||||||
|
top += height;
|
||||||
|
context.translate(0, -height);
|
||||||
|
p.translate(0, height);
|
||||||
|
}
|
||||||
|
context.translate(0, top);
|
||||||
|
p.translate(0, -top);
|
||||||
|
|
||||||
// paint the userpic if it intersects the painted rect
|
enumerateUserpics([&](not_null<Element*> view, int userpicTop) {
|
||||||
if (userpicTop + st::msgPhotoSize > clip.top()) {
|
// stop the enumeration if the userpic is below the painted rect
|
||||||
if (const auto from = view->data()->displayFrom()) {
|
if (userpicTop >= clip.top() + clip.height()) {
|
||||||
from->paintUserpicLeft(
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// paint the userpic if it intersects the painted rect
|
||||||
|
if (userpicTop + st::msgPhotoSize > clip.top()) {
|
||||||
|
if (const auto from = view->data()->displayFrom()) {
|
||||||
|
from->paintUserpicLeft(
|
||||||
|
p,
|
||||||
|
_userpics[from],
|
||||||
|
st::historyPhotoLeft,
|
||||||
|
userpicTop,
|
||||||
|
view->width(),
|
||||||
|
st::msgPhotoSize);
|
||||||
|
} else if (const auto info = view->data()->hiddenSenderInfo()) {
|
||||||
|
if (info->customUserpic.empty()) {
|
||||||
|
info->emptyUserpic.paint(
|
||||||
p,
|
p,
|
||||||
_userpics[from],
|
|
||||||
st::historyPhotoLeft,
|
st::historyPhotoLeft,
|
||||||
userpicTop,
|
userpicTop,
|
||||||
view->width(),
|
view->width(),
|
||||||
st::msgPhotoSize);
|
st::msgPhotoSize);
|
||||||
} else if (const auto info = view->data()->hiddenSenderInfo()) {
|
} else {
|
||||||
if (info->customUserpic.empty()) {
|
const auto painted = info->paintCustomUserpic(
|
||||||
info->emptyUserpic.paint(
|
p,
|
||||||
p,
|
st::historyPhotoLeft,
|
||||||
st::historyPhotoLeft,
|
userpicTop,
|
||||||
userpicTop,
|
view->width(),
|
||||||
view->width(),
|
st::msgPhotoSize);
|
||||||
st::msgPhotoSize);
|
if (!painted) {
|
||||||
} else {
|
const auto itemId = view->data()->fullId();
|
||||||
const auto painted = info->paintCustomUserpic(
|
auto &v = _sponsoredUserpics[itemId.msg];
|
||||||
p,
|
if (!info->customUserpic.isCurrentView(v)) {
|
||||||
st::historyPhotoLeft,
|
v = info->customUserpic.createView();
|
||||||
userpicTop,
|
info->customUserpic.load(session, itemId);
|
||||||
view->width(),
|
|
||||||
st::msgPhotoSize);
|
|
||||||
if (!painted) {
|
|
||||||
const auto itemId = view->data()->fullId();
|
|
||||||
auto &v = _sponsoredUserpics[itemId.msg];
|
|
||||||
if (!info->customUserpic.isCurrentView(v)) {
|
|
||||||
v = info->customUserpic.createView();
|
|
||||||
info->customUserpic.load(session, itemId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Unexpected("Corrupt forwarded information in message.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top();
|
||||||
|
auto scrollDateOpacity = _scrollDateOpacity.value(_scrollDateShown ? 1. : 0.);
|
||||||
|
enumerateDates([&](not_null<Element*> view, int itemtop, int dateTop) {
|
||||||
|
// stop the enumeration if the date is above the painted rect
|
||||||
|
if (dateTop + dateHeight <= clip.top()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto displayDate = view->displayDate();
|
||||||
|
auto dateInPlace = displayDate;
|
||||||
|
if (dateInPlace) {
|
||||||
|
const auto correctDateTop = itemtop + st::msgServiceMargin.top();
|
||||||
|
dateInPlace = (dateTop < correctDateTop + dateHeight);
|
||||||
|
}
|
||||||
|
//bool noFloatingDate = (item->date.date() == lastDate && displayDate);
|
||||||
|
//if (noFloatingDate) {
|
||||||
|
// if (itemtop < showFloatingBefore) {
|
||||||
|
// noFloatingDate = false;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
// paint the date if it intersects the painted rect
|
||||||
|
if (dateTop < clip.top() + clip.height()) {
|
||||||
|
auto opacity = (dateInPlace/* || noFloatingDate*/) ? 1. : scrollDateOpacity;
|
||||||
|
if (opacity > 0.) {
|
||||||
|
p.setOpacity(opacity);
|
||||||
|
int dateY = /*noFloatingDate ? itemtop :*/ (dateTop - st::msgServiceMargin.top());
|
||||||
|
int width = view->width();
|
||||||
|
if (const auto date = view->Get<HistoryView::DateBadge>()) {
|
||||||
|
date->paint(p, context.st, dateY, width, _isChatWide);
|
||||||
} else {
|
} else {
|
||||||
Unexpected("Corrupt forwarded information in message.");
|
ServiceMessagePainter::PaintDate(
|
||||||
|
p,
|
||||||
|
context.st,
|
||||||
|
ItemDateText(
|
||||||
|
view->data(),
|
||||||
|
IsItemScheduledUntilOnline(view->data())),
|
||||||
|
dateY,
|
||||||
|
width,
|
||||||
|
_isChatWide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
}
|
||||||
});
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
auto dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top();
|
_reactionsManager->paint(p, context);
|
||||||
auto scrollDateOpacity = _scrollDateOpacity.value(_scrollDateShown ? 1. : 0.);
|
_emojiInteractions->paint(p);
|
||||||
enumerateDates([&](not_null<Element*> view, int itemtop, int dateTop) {
|
|
||||||
// stop the enumeration if the date is above the painted rect
|
|
||||||
if (dateTop + dateHeight <= clip.top()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto displayDate = view->displayDate();
|
|
||||||
auto dateInPlace = displayDate;
|
|
||||||
if (dateInPlace) {
|
|
||||||
const auto correctDateTop = itemtop + st::msgServiceMargin.top();
|
|
||||||
dateInPlace = (dateTop < correctDateTop + dateHeight);
|
|
||||||
}
|
|
||||||
//bool noFloatingDate = (item->date.date() == lastDate && displayDate);
|
|
||||||
//if (noFloatingDate) {
|
|
||||||
// if (itemtop < showFloatingBefore) {
|
|
||||||
// noFloatingDate = false;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
// paint the date if it intersects the painted rect
|
|
||||||
if (dateTop < clip.top() + clip.height()) {
|
|
||||||
auto opacity = (dateInPlace/* || noFloatingDate*/) ? 1. : scrollDateOpacity;
|
|
||||||
if (opacity > 0.) {
|
|
||||||
p.setOpacity(opacity);
|
|
||||||
int dateY = /*noFloatingDate ? itemtop :*/ (dateTop - st::msgServiceMargin.top());
|
|
||||||
int width = view->width();
|
|
||||||
if (const auto date = view->Get<HistoryView::DateBadge>()) {
|
|
||||||
date->paint(p, context.st, dateY, width, _isChatWide);
|
|
||||||
} else {
|
|
||||||
ServiceMessagePainter::PaintDate(
|
|
||||||
p,
|
|
||||||
context.st,
|
|
||||||
ItemDateText(
|
|
||||||
view->data(),
|
|
||||||
IsItemScheduledUntilOnline(view->data())),
|
|
||||||
dateY,
|
|
||||||
width,
|
|
||||||
_isChatWide);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
_reactionsManager->paint(p, context);
|
|
||||||
_emojiInteractions->paint(p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::maybeMarkReactionsRead(not_null<HistoryItem*> item) {
|
void ListWidget::maybeMarkReactionsRead(not_null<HistoryItem*> item) {
|
||||||
|
|
|
@ -132,6 +132,9 @@ public:
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
FullMsgId context,
|
FullMsgId context,
|
||||||
bool showInMediaView) = 0;
|
bool showInMediaView) = 0;
|
||||||
|
virtual void listPaintEmpty(
|
||||||
|
Painter &p,
|
||||||
|
const Ui::ChatPaintContext &context) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SelectionData {
|
struct SelectionData {
|
||||||
|
|
|
@ -603,6 +603,11 @@ void PinnedWidget::listOpenDocument(
|
||||||
controller()->openDocument(document, context, MsgId(), showInMediaView);
|
controller()->openDocument(document, context, MsgId(), showInMediaView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PinnedWidget::listPaintEmpty(
|
||||||
|
Painter &p,
|
||||||
|
const Ui::ChatPaintContext &context) {
|
||||||
|
}
|
||||||
|
|
||||||
void PinnedWidget::confirmDeleteSelected() {
|
void PinnedWidget::confirmDeleteSelected() {
|
||||||
ConfirmDeleteSelectedItems(_inner);
|
ConfirmDeleteSelectedItems(_inner);
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,9 @@ public:
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
FullMsgId context,
|
FullMsgId context,
|
||||||
bool showInMediaView) override;
|
bool showInMediaView) override;
|
||||||
|
void listPaintEmpty(
|
||||||
|
Painter &p,
|
||||||
|
const Ui::ChatPaintContext &context) override;
|
||||||
|
|
||||||
// CornerButtonsDelegate delegate.
|
// CornerButtonsDelegate delegate.
|
||||||
void cornerButtonsShowAtPosition(
|
void cornerButtonsShowAtPosition(
|
||||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_sticker_toast.h"
|
#include "history/view/history_view_sticker_toast.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "history/view/history_view_contact_status.h"
|
#include "history/view/history_view_contact_status.h"
|
||||||
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_drag_area.h"
|
#include "history/history_drag_area.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
|
@ -355,16 +356,17 @@ RepliesWidget::RepliesWidget(
|
||||||
}
|
}
|
||||||
|
|
||||||
RepliesWidget::~RepliesWidget() {
|
RepliesWidget::~RepliesWidget() {
|
||||||
|
base::take(_sendAction);
|
||||||
|
session().api().saveCurrentDraftToCloud();
|
||||||
|
controller()->sendingAnimation().clear();
|
||||||
if (_topic && _topic->creating()) {
|
if (_topic && _topic->creating()) {
|
||||||
|
_emptyPainter = nullptr;
|
||||||
_topic->discard();
|
_topic->discard();
|
||||||
_topic = nullptr;
|
_topic = nullptr;
|
||||||
}
|
}
|
||||||
base::take(_sendAction);
|
|
||||||
_history->owner().sendActionManager().repliesPainterRemoved(
|
_history->owner().sendActionManager().repliesPainterRemoved(
|
||||||
_history,
|
_history,
|
||||||
_rootId);
|
_rootId);
|
||||||
session().api().saveCurrentDraftToCloud();
|
|
||||||
controller()->sendingAnimation().clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RepliesWidget::orderWidgets() {
|
void RepliesWidget::orderWidgets() {
|
||||||
|
@ -518,6 +520,11 @@ void RepliesWidget::setTopic(Data::ForumTopic *topic) {
|
||||||
}
|
}
|
||||||
subscribeToTopic();
|
subscribeToTopic();
|
||||||
}
|
}
|
||||||
|
if (_topic && emptyShown()) {
|
||||||
|
setupEmptyPainter();
|
||||||
|
} else {
|
||||||
|
_emptyPainter = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryItem *RepliesWidget::lookupRoot() const {
|
HistoryItem *RepliesWidget::lookupRoot() const {
|
||||||
|
@ -1772,6 +1779,12 @@ void RepliesWidget::paintEvent(QPaintEvent *e) {
|
||||||
SectionWidget::PaintBackground(controller(), _theme.get(), this, bg);
|
SectionWidget::PaintBackground(controller(), _theme.get(), this, bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RepliesWidget::emptyShown() const {
|
||||||
|
return _topic
|
||||||
|
&& (_inner->isEmpty()
|
||||||
|
|| (_topic->lastKnownServerMessageId() == _rootId));
|
||||||
|
}
|
||||||
|
|
||||||
void RepliesWidget::onScroll() {
|
void RepliesWidget::onScroll() {
|
||||||
if (_skipScrollEvent) {
|
if (_skipScrollEvent) {
|
||||||
return;
|
return;
|
||||||
|
@ -2065,6 +2078,32 @@ void RepliesWidget::listOpenDocument(
|
||||||
controller()->openDocument(document, context, _rootId, showInMediaView);
|
controller()->openDocument(document, context, _rootId, showInMediaView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RepliesWidget::listPaintEmpty(
|
||||||
|
Painter &p,
|
||||||
|
const Ui::ChatPaintContext &context) {
|
||||||
|
if (!emptyShown()) {
|
||||||
|
return;
|
||||||
|
} else if (!_emptyPainter) {
|
||||||
|
setupEmptyPainter();
|
||||||
|
}
|
||||||
|
_emptyPainter->paint(p, context.st, width(), _scroll->height());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RepliesWidget::setupEmptyPainter() {
|
||||||
|
Expects(_topic != nullptr);
|
||||||
|
|
||||||
|
_emptyPainter = std::make_unique<EmptyPainter>(_topic, [=] {
|
||||||
|
return controller()->isGifPausedAtLeastFor(
|
||||||
|
Window::GifPauseReason::Any);
|
||||||
|
}, [=] {
|
||||||
|
if (emptyShown()) {
|
||||||
|
update();
|
||||||
|
} else {
|
||||||
|
_emptyPainter = nullptr;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void RepliesWidget::confirmDeleteSelected() {
|
void RepliesWidget::confirmDeleteSelected() {
|
||||||
ConfirmDeleteSelectedItems(_inner);
|
ConfirmDeleteSelectedItems(_inner);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ class ComposeControls;
|
||||||
class SendActionPainter;
|
class SendActionPainter;
|
||||||
class StickerToast;
|
class StickerToast;
|
||||||
class TopicReopenBar;
|
class TopicReopenBar;
|
||||||
|
class EmptyPainter;
|
||||||
|
|
||||||
class RepliesWidget final
|
class RepliesWidget final
|
||||||
: public Window::SectionWidget
|
: public Window::SectionWidget
|
||||||
|
@ -158,6 +159,9 @@ public:
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
FullMsgId context,
|
FullMsgId context,
|
||||||
bool showInMediaView) override;
|
bool showInMediaView) override;
|
||||||
|
void listPaintEmpty(
|
||||||
|
Painter &p,
|
||||||
|
const Ui::ChatPaintContext &context) override;
|
||||||
|
|
||||||
// CornerButtonsDelegate delegate.
|
// CornerButtonsDelegate delegate.
|
||||||
void cornerButtonsShowAtPosition(
|
void cornerButtonsShowAtPosition(
|
||||||
|
@ -275,7 +279,9 @@ private:
|
||||||
Api::SendOptions options,
|
Api::SendOptions options,
|
||||||
std::optional<MsgId> localMessageId);
|
std::optional<MsgId> localMessageId);
|
||||||
|
|
||||||
|
void setupEmptyPainter();
|
||||||
void refreshJoinGroupButton();
|
void refreshJoinGroupButton();
|
||||||
|
[[nodiscard]] bool emptyShown() const;
|
||||||
[[nodiscard]] bool showSlowmodeError();
|
[[nodiscard]] bool showSlowmodeError();
|
||||||
[[nodiscard]] std::optional<QString> writeRestriction() const;
|
[[nodiscard]] std::optional<QString> writeRestriction() const;
|
||||||
|
|
||||||
|
@ -296,6 +302,7 @@ private:
|
||||||
std::unique_ptr<ComposeControls> _composeControls;
|
std::unique_ptr<ComposeControls> _composeControls;
|
||||||
std::unique_ptr<Ui::FlatButton> _joinGroup;
|
std::unique_ptr<Ui::FlatButton> _joinGroup;
|
||||||
std::unique_ptr<TopicReopenBar> _topicReopenBar;
|
std::unique_ptr<TopicReopenBar> _topicReopenBar;
|
||||||
|
std::unique_ptr<EmptyPainter> _emptyPainter;
|
||||||
bool _skipScrollEvent = false;
|
bool _skipScrollEvent = false;
|
||||||
|
|
||||||
std::unique_ptr<Ui::PinnedBar> _rootView;
|
std::unique_ptr<Ui::PinnedBar> _rootView;
|
||||||
|
|
|
@ -1282,6 +1282,11 @@ void ScheduledWidget::listOpenDocument(
|
||||||
controller()->openDocument(document, context, MsgId(), showInMediaView);
|
controller()->openDocument(document, context, MsgId(), showInMediaView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScheduledWidget::listPaintEmpty(
|
||||||
|
Painter &p,
|
||||||
|
const Ui::ChatPaintContext &context) {
|
||||||
|
}
|
||||||
|
|
||||||
void ScheduledWidget::confirmSendNowSelected() {
|
void ScheduledWidget::confirmSendNowSelected() {
|
||||||
ConfirmSendNowSelectedItems(_inner);
|
ConfirmSendNowSelectedItems(_inner);
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,6 +142,9 @@ public:
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
FullMsgId context,
|
FullMsgId context,
|
||||||
bool showInMediaView) override;
|
bool showInMediaView) override;
|
||||||
|
void listPaintEmpty(
|
||||||
|
Painter &p,
|
||||||
|
const Ui::ChatPaintContext &context) override;
|
||||||
|
|
||||||
// CornerButtonsDelegate delegate.
|
// CornerButtonsDelegate delegate.
|
||||||
void cornerButtonsShowAtPosition(
|
void cornerButtonsShowAtPosition(
|
||||||
|
|
|
@ -15,18 +15,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_abstract_structure.h"
|
#include "data/data_abstract_structure.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
|
#include "info/profile/info_profile_cover.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/ui_utility.h"
|
#include "ui/ui_utility.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "menu/menu_ttl_validator.h"
|
#include "menu/menu_ttl_validator.h"
|
||||||
|
#include "data/data_forum_topic.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_info.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
TextParseOptions EmptyLineOptions = {
|
||||||
|
TextParseMultiline, // flags
|
||||||
|
4096, // maxw
|
||||||
|
1, // maxh
|
||||||
|
Qt::LayoutDirectionAuto, // lang-dependent
|
||||||
|
};
|
||||||
|
|
||||||
enum CircleMask {
|
enum CircleMask {
|
||||||
NormalMask = 0x00,
|
NormalMask = 0x00,
|
||||||
InvertedMask = 0x01,
|
InvertedMask = 0x01,
|
||||||
|
@ -181,7 +191,11 @@ bool NeedAboutGroup(not_null<History*> history) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namepsace
|
void SetText(Ui::Text::String &text, const QString &content) {
|
||||||
|
text.setText(st::serviceTextStyle, content, EmptyLineOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
int WideChatWidth() {
|
int WideChatWidth() {
|
||||||
return st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left();
|
return st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left();
|
||||||
|
@ -657,6 +671,21 @@ EmptyPainter::EmptyPainter(not_null<History*> history)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EmptyPainter::EmptyPainter(
|
||||||
|
not_null<Data::ForumTopic*> topic,
|
||||||
|
Fn<bool()> paused,
|
||||||
|
Fn<void()> update)
|
||||||
|
: _history(topic->history())
|
||||||
|
, _topic(topic)
|
||||||
|
, _icon(
|
||||||
|
std::make_unique<Info::Profile::TopicIconView>(topic, paused, update))
|
||||||
|
, _header(st::msgMinWidth)
|
||||||
|
, _text(st::msgMinWidth) {
|
||||||
|
fillAboutTopic();
|
||||||
|
}
|
||||||
|
|
||||||
|
EmptyPainter::~EmptyPainter() = default;
|
||||||
|
|
||||||
void EmptyPainter::fillAboutGroup() {
|
void EmptyPainter::fillAboutGroup() {
|
||||||
const auto phrases = {
|
const auto phrases = {
|
||||||
tr::lng_group_about1(tr::now),
|
tr::lng_group_about1(tr::now),
|
||||||
|
@ -664,34 +693,38 @@ void EmptyPainter::fillAboutGroup() {
|
||||||
tr::lng_group_about3(tr::now),
|
tr::lng_group_about3(tr::now),
|
||||||
tr::lng_group_about4(tr::now),
|
tr::lng_group_about4(tr::now),
|
||||||
};
|
};
|
||||||
const auto setText = [](Ui::Text::String &text, const QString &content) {
|
SetText(_header, tr::lng_group_about_header(tr::now));
|
||||||
text.setText(
|
SetText(_text, tr::lng_group_about_text(tr::now));
|
||||||
st::serviceTextStyle,
|
|
||||||
content,
|
|
||||||
Ui::NameTextOptions());
|
|
||||||
};
|
|
||||||
setText(_header, tr::lng_group_about_header(tr::now));
|
|
||||||
setText(_text, tr::lng_group_about_text(tr::now));
|
|
||||||
for (const auto &text : phrases) {
|
for (const auto &text : phrases) {
|
||||||
_phrases.emplace_back(st::msgMinWidth);
|
_phrases.emplace_back(st::msgMinWidth);
|
||||||
setText(_phrases.back(), text);
|
SetText(_phrases.back(), text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmptyPainter::fillAboutTopic() {
|
||||||
|
SetText(_header, _topic->my()
|
||||||
|
? tr::lng_forum_topic_created_title_my(tr::now)
|
||||||
|
: tr::lng_forum_topic_created_title(tr::now));
|
||||||
|
SetText(_text, _topic->my()
|
||||||
|
? tr::lng_forum_topic_created_body_my(tr::now)
|
||||||
|
: tr::lng_forum_topic_created_body(tr::now));
|
||||||
|
}
|
||||||
|
|
||||||
void EmptyPainter::paint(
|
void EmptyPainter::paint(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
not_null<const Ui::ChatStyle*> st,
|
not_null<const Ui::ChatStyle*> st,
|
||||||
int width,
|
int width,
|
||||||
int height) {
|
int height) {
|
||||||
if (_phrases.empty()) {
|
if (_phrases.empty() && _text.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
constexpr auto kMaxTextLines = 3;
|
constexpr auto kMaxTextLines = 3;
|
||||||
const auto maxPhraseWidth = ranges::max_element(
|
const auto maxPhraseWidth = _phrases.empty()
|
||||||
_phrases,
|
? 0
|
||||||
ranges::less(),
|
: ranges::max_element(
|
||||||
&Ui::Text::String::maxWidth
|
_phrases,
|
||||||
)->maxWidth();
|
ranges::less(),
|
||||||
|
&Ui::Text::String::maxWidth)->maxWidth();
|
||||||
|
|
||||||
const auto &font = st::serviceTextStyle.font;
|
const auto &font = st::serviceTextStyle.font;
|
||||||
const auto maxBubbleWidth = width - 2 * st::historyGroupAboutMargin;
|
const auto maxBubbleWidth = width - 2 * st::historyGroupAboutMargin;
|
||||||
|
@ -708,13 +741,17 @@ void EmptyPainter::paint(
|
||||||
text.countHeight(innerWidth),
|
text.countHeight(innerWidth),
|
||||||
kMaxTextLines * font->height);
|
kMaxTextLines * font->height);
|
||||||
};
|
};
|
||||||
|
const auto iconHeight = _icon
|
||||||
|
? st::infoTopicCover.photo.size.height()
|
||||||
|
: 0;
|
||||||
const auto bubbleHeight = padding.top()
|
const auto bubbleHeight = padding.top()
|
||||||
|
+ (_icon ? (iconHeight + st::historyGroupAboutHeaderSkip) : 0)
|
||||||
+ textHeight(_header)
|
+ textHeight(_header)
|
||||||
+ st::historyGroupAboutHeaderSkip
|
+ st::historyGroupAboutHeaderSkip
|
||||||
+ textHeight(_text)
|
+ textHeight(_text)
|
||||||
+ st::historyGroupAboutTextSkip
|
+ st::historyGroupAboutTextSkip
|
||||||
+ ranges::accumulate(_phrases, 0, ranges::plus(), textHeight)
|
+ ranges::accumulate(_phrases, 0, ranges::plus(), textHeight)
|
||||||
+ st::historyGroupAboutSkip * int(_phrases.size() - 1)
|
+ st::historyGroupAboutSkip * std::max(int(_phrases.size()) - 1, 0)
|
||||||
+ padding.bottom();
|
+ padding.bottom();
|
||||||
const auto bubbleLeft = (width - bubbleWidth) / 2;
|
const auto bubbleLeft = (width - bubbleWidth) / 2;
|
||||||
const auto bubbleTop = (height - bubbleHeight) / 2;
|
const auto bubbleTop = (height - bubbleHeight) / 2;
|
||||||
|
@ -731,6 +768,13 @@ void EmptyPainter::paint(
|
||||||
const auto left = bubbleLeft + padding.left();
|
const auto left = bubbleLeft + padding.left();
|
||||||
auto top = bubbleTop + padding.top();
|
auto top = bubbleTop + padding.top();
|
||||||
|
|
||||||
|
if (_icon) {
|
||||||
|
_icon->paintInRect(
|
||||||
|
p,
|
||||||
|
QRect(bubbleLeft, top, bubbleWidth, iconHeight));
|
||||||
|
top += iconHeight + st::historyGroupAboutHeaderSkip;
|
||||||
|
}
|
||||||
|
|
||||||
_header.drawElided(
|
_header.drawElided(
|
||||||
p,
|
p,
|
||||||
left,
|
left,
|
||||||
|
@ -745,7 +789,8 @@ void EmptyPainter::paint(
|
||||||
left,
|
left,
|
||||||
top,
|
top,
|
||||||
innerWidth,
|
innerWidth,
|
||||||
kMaxTextLines);
|
kMaxTextLines,
|
||||||
|
_topic ? style::al_top : style::al_topleft);
|
||||||
top += textHeight(_text) + st::historyGroupAboutTextSkip;
|
top += textHeight(_text) + st::historyGroupAboutTextSkip;
|
||||||
|
|
||||||
for (const auto &text : _phrases) {
|
for (const auto &text : _phrases) {
|
||||||
|
|
|
@ -16,6 +16,14 @@ class ChatStyle;
|
||||||
struct CornersPixmaps;
|
struct CornersPixmaps;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class ForumTopic;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Info::Profile {
|
||||||
|
class TopicIconView;
|
||||||
|
} // namespace Info::Profile
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
|
||||||
class Service final : public Element {
|
class Service final : public Element {
|
||||||
|
@ -116,6 +124,11 @@ private:
|
||||||
class EmptyPainter {
|
class EmptyPainter {
|
||||||
public:
|
public:
|
||||||
explicit EmptyPainter(not_null<History*> history);
|
explicit EmptyPainter(not_null<History*> history);
|
||||||
|
EmptyPainter(
|
||||||
|
not_null<Data::ForumTopic*> topic,
|
||||||
|
Fn<bool()> paused,
|
||||||
|
Fn<void()> update);
|
||||||
|
~EmptyPainter();
|
||||||
|
|
||||||
void paint(
|
void paint(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
|
@ -125,12 +138,17 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void fillAboutGroup();
|
void fillAboutGroup();
|
||||||
|
void fillAboutTopic();
|
||||||
|
|
||||||
not_null<History*> _history;
|
not_null<History*> _history;
|
||||||
|
Data::ForumTopic *_topic = nullptr;
|
||||||
|
std::unique_ptr<Info::Profile::TopicIconView> _icon;
|
||||||
Ui::Text::String _header;
|
Ui::Text::String _header;
|
||||||
Ui::Text::String _text;
|
Ui::Text::String _text;
|
||||||
std::vector<Ui::Text::String> _phrases;
|
std::vector<Ui::Text::String> _phrases;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -123,7 +123,6 @@ StaticStickerPlayer::StaticStickerPlayer(
|
||||||
: _frame(Images::Read({
|
: _frame(Images::Read({
|
||||||
.path = location.name(),
|
.path = location.name(),
|
||||||
.content = data,
|
.content = data,
|
||||||
.forceOpaque = true,
|
|
||||||
}).image) {
|
}).image) {
|
||||||
if (!_frame.isNull()) {
|
if (!_frame.isNull()) {
|
||||||
size = _frame.size().scaled(size, Qt::KeepAspectRatio);
|
size = _frame.size().scaled(size, Qt::KeepAspectRatio);
|
||||||
|
|
|
@ -118,54 +118,59 @@ Cover::Cover(
|
||||||
}
|
}
|
||||||
|
|
||||||
TopicIconView::TopicIconView(
|
TopicIconView::TopicIconView(
|
||||||
QWidget *parent,
|
not_null<Data::ForumTopic*> topic,
|
||||||
not_null<Window::SessionController*> controller,
|
Fn<bool()> paused,
|
||||||
not_null<Data::ForumTopic*> topic)
|
Fn<void()> update)
|
||||||
: AbstractButton(parent) {
|
: _topic(topic)
|
||||||
setup(topic, [=] {
|
, _paused(std::move(paused))
|
||||||
return controller->isGifPausedAtLeastFor(
|
, _update(std::move(update)) {
|
||||||
Window::GifPauseReason::Layer);
|
setup(topic);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TopicIconView::setup(
|
void TopicIconView::paintInRect(QPainter &p, QRect rect) {
|
||||||
not_null<Data::ForumTopic*> topic,
|
const auto paint = [&](const QImage &image) {
|
||||||
Fn<bool()> paused) {
|
const auto size = image.size() / image.devicePixelRatio();
|
||||||
|
p.drawImage(
|
||||||
|
rect.x() + (rect.width() - size.width()) / 2,
|
||||||
|
rect.y() + (rect.height() - size.height()) / 2,
|
||||||
|
image);
|
||||||
|
};
|
||||||
|
if (_player && _player->ready()) {
|
||||||
|
paint(_player->frame(
|
||||||
|
st::infoTopicCover.photo.size,
|
||||||
|
QColor(0, 0, 0, 0),
|
||||||
|
false,
|
||||||
|
crl::now(),
|
||||||
|
_paused()).image);
|
||||||
|
_player->markFrameShown();
|
||||||
|
} else if (!_topic->iconId() && !_image.isNull()) {
|
||||||
|
paint(_image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TopicIconView::setup(not_null<Data::ForumTopic*> topic) {
|
||||||
setupPlayer(topic);
|
setupPlayer(topic);
|
||||||
setupImage(topic);
|
setupImage(topic);
|
||||||
|
|
||||||
resize(st::infoTopicCover.photo.size);
|
|
||||||
paintRequest(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
auto p = QPainter(this);
|
|
||||||
const auto paint = [&](const QImage &image) {
|
|
||||||
const auto size = image.size() / image.devicePixelRatio();
|
|
||||||
p.drawImage(
|
|
||||||
(st::infoTopicCover.photo.size.width() - size.width()) / 2,
|
|
||||||
(st::infoTopicCover.photo.size.height() - size.height()) / 2,
|
|
||||||
image);
|
|
||||||
};
|
|
||||||
if (_player && _player->ready()) {
|
|
||||||
paint(_player->frame(
|
|
||||||
st::infoTopicCover.photo.size,
|
|
||||||
QColor(0, 0, 0, 0),
|
|
||||||
false,
|
|
||||||
crl::now(),
|
|
||||||
paused()).image);
|
|
||||||
_player->markFrameShown();
|
|
||||||
} else if (!topic->iconId() && !_image.isNull()) {
|
|
||||||
paint(_image);
|
|
||||||
}
|
|
||||||
}, lifetime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TopicIconView::setupPlayer(not_null<Data::ForumTopic*> topic) {
|
void TopicIconView::setupPlayer(not_null<Data::ForumTopic*> topic) {
|
||||||
IconIdValue(
|
IconIdValue(
|
||||||
topic
|
topic
|
||||||
) | rpl::map([=](DocumentId id) {
|
) | rpl::map([=](DocumentId id) -> rpl::producer<DocumentData*> {
|
||||||
return topic->owner().customEmojiManager().resolve(id);
|
if (!id) {
|
||||||
|
return rpl::single((DocumentData*)nullptr);
|
||||||
|
}
|
||||||
|
return topic->owner().customEmojiManager().resolve(
|
||||||
|
id
|
||||||
|
) | rpl::map([=](not_null<DocumentData*> document) {
|
||||||
|
return document.get();
|
||||||
|
});
|
||||||
}) | rpl::flatten_latest(
|
}) | rpl::flatten_latest(
|
||||||
) | rpl::map([=](not_null<DocumentData*> document) {
|
) | rpl::map([=](DocumentData *document)
|
||||||
|
-> rpl::producer<std::shared_ptr<StickerPlayer>> {
|
||||||
|
if (!document) {
|
||||||
|
return rpl::single(std::shared_ptr<StickerPlayer>());
|
||||||
|
}
|
||||||
const auto media = document->createMediaView();
|
const auto media = document->createMediaView();
|
||||||
media->checkStickerLarge();
|
media->checkStickerLarge();
|
||||||
media->goodThumbnailWanted();
|
media->goodThumbnailWanted();
|
||||||
|
@ -195,13 +200,16 @@ void TopicIconView::setupPlayer(not_null<Data::ForumTopic*> topic) {
|
||||||
media->bytes(),
|
media->bytes(),
|
||||||
st::infoTopicCover.photo.size);
|
st::infoTopicCover.photo.size);
|
||||||
}
|
}
|
||||||
result->setRepaintCallback([=] { update(); });
|
result->setRepaintCallback(_update);
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
}) | rpl::flatten_latest(
|
}) | rpl::flatten_latest(
|
||||||
) | rpl::start_with_next([=](std::shared_ptr<StickerPlayer> player) {
|
) | rpl::start_with_next([=](std::shared_ptr<StickerPlayer> player) {
|
||||||
_player = std::move(player);
|
_player = std::move(player);
|
||||||
}, lifetime());
|
if (!_player) {
|
||||||
|
_update();
|
||||||
|
}
|
||||||
|
}, _lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TopicIconView::setupImage(not_null<Data::ForumTopic*> topic) {
|
void TopicIconView::setupImage(not_null<Data::ForumTopic*> topic) {
|
||||||
|
@ -213,7 +221,25 @@ void TopicIconView::setupImage(not_null<Data::ForumTopic*> topic) {
|
||||||
return ForumTopicIconFrame(colorId, title, st::infoForumTopicIcon);
|
return ForumTopicIconFrame(colorId, title, st::infoForumTopicIcon);
|
||||||
}) | rpl::start_with_next([=](QImage &&image) {
|
}) | rpl::start_with_next([=](QImage &&image) {
|
||||||
_image = std::move(image);
|
_image = std::move(image);
|
||||||
update();
|
_update();
|
||||||
|
}, _lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
TopicIconButton::TopicIconButton(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<Data::ForumTopic*> topic)
|
||||||
|
: AbstractButton(parent)
|
||||||
|
, _view(
|
||||||
|
topic,
|
||||||
|
[=] { return controller->isGifPausedAtLeastFor(
|
||||||
|
Window::GifPauseReason::Layer); },
|
||||||
|
[=] { update(); }) {
|
||||||
|
resize(st::infoTopicCover.photo.size);
|
||||||
|
paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(this);
|
||||||
|
_view.paintInRect(p, rect());
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,8 +274,8 @@ Cover::Cover(
|
||||||
_peer,
|
_peer,
|
||||||
Ui::UserpicButton::Role::OpenPhoto,
|
Ui::UserpicButton::Role::OpenPhoto,
|
||||||
_st.photo))
|
_st.photo))
|
||||||
, _iconView(topic
|
, _iconButton(topic
|
||||||
? object_ptr<TopicIconView>(this, controller, topic)
|
? object_ptr<TopicIconButton>(this, controller, topic)
|
||||||
: nullptr)
|
: nullptr)
|
||||||
, _name(this, _st.name)
|
, _name(this, _st.name)
|
||||||
, _status(this, _st.status)
|
, _status(this, _st.status)
|
||||||
|
@ -285,7 +311,7 @@ Cover::Cover(
|
||||||
_userpic->takeResultImage());
|
_userpic->takeResultImage());
|
||||||
}, _userpic->lifetime());
|
}, _userpic->lifetime());
|
||||||
} else if (topic->canEdit()) {
|
} else if (topic->canEdit()) {
|
||||||
_iconView->setClickedCallback([=] {
|
_iconButton->setClickedCallback([=] {
|
||||||
_controller->show(Box(
|
_controller->show(Box(
|
||||||
EditForumTopicBox,
|
EditForumTopicBox,
|
||||||
_controller,
|
_controller,
|
||||||
|
@ -293,7 +319,7 @@ Cover::Cover(
|
||||||
topic->rootId()));
|
topic->rootId()));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
_iconView->setAttribute(Qt::WA_TransparentForMouseEvents);
|
_iconButton->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +329,7 @@ void Cover::setupChildGeometry() {
|
||||||
if (_userpic) {
|
if (_userpic) {
|
||||||
_userpic->moveToLeft(_st.photoLeft, _st.photoTop, newWidth);
|
_userpic->moveToLeft(_st.photoLeft, _st.photoTop, newWidth);
|
||||||
} else {
|
} else {
|
||||||
_iconView->moveToLeft(_st.photoLeft, _st.photoTop, newWidth);
|
_iconButton->moveToLeft(_st.photoLeft, _st.photoTop, newWidth);
|
||||||
}
|
}
|
||||||
refreshNameGeometry(newWidth);
|
refreshNameGeometry(newWidth);
|
||||||
refreshStatusGeometry(newWidth);
|
refreshStatusGeometry(newWidth);
|
||||||
|
|
|
@ -44,24 +44,40 @@ namespace Info::Profile {
|
||||||
class EmojiStatusPanel;
|
class EmojiStatusPanel;
|
||||||
class Badge;
|
class Badge;
|
||||||
|
|
||||||
class TopicIconView final : public Ui::AbstractButton {
|
class TopicIconView final {
|
||||||
public:
|
public:
|
||||||
TopicIconView(
|
TopicIconView(
|
||||||
|
not_null<Data::ForumTopic*> topic,
|
||||||
|
Fn<bool()> paused,
|
||||||
|
Fn<void()> update);
|
||||||
|
|
||||||
|
void paintInRect(QPainter &p, QRect rect);
|
||||||
|
|
||||||
|
private:
|
||||||
|
using StickerPlayer = HistoryView::StickerPlayer;
|
||||||
|
|
||||||
|
void setup(not_null<Data::ForumTopic*> topic);
|
||||||
|
void setupPlayer(not_null<Data::ForumTopic*> topic);
|
||||||
|
void setupImage(not_null<Data::ForumTopic*> topic);
|
||||||
|
|
||||||
|
const not_null<Data::ForumTopic*> _topic;
|
||||||
|
Fn<bool()> _paused;
|
||||||
|
Fn<void()> _update;
|
||||||
|
std::shared_ptr<StickerPlayer> _player;
|
||||||
|
QImage _image;
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class TopicIconButton final : public Ui::AbstractButton {
|
||||||
|
public:
|
||||||
|
TopicIconButton(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
not_null<Data::ForumTopic*> topic);
|
not_null<Data::ForumTopic*> topic);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using StickerPlayer = HistoryView::StickerPlayer;
|
TopicIconView _view;
|
||||||
|
|
||||||
void setup(
|
|
||||||
not_null<Data::ForumTopic*> topic,
|
|
||||||
Fn<bool()> paused);
|
|
||||||
void setupPlayer(not_null<Data::ForumTopic*> topic);
|
|
||||||
void setupImage(not_null<Data::ForumTopic*> topic);
|
|
||||||
|
|
||||||
std::shared_ptr<StickerPlayer> _player;
|
|
||||||
QImage _image;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,7 +128,7 @@ private:
|
||||||
int _onlineCount = 0;
|
int _onlineCount = 0;
|
||||||
|
|
||||||
object_ptr<Ui::UserpicButton> _userpic;
|
object_ptr<Ui::UserpicButton> _userpic;
|
||||||
object_ptr<TopicIconView> _iconView;
|
object_ptr<TopicIconButton> _iconButton;
|
||||||
object_ptr<Ui::FlatLabel> _name = { nullptr };
|
object_ptr<Ui::FlatLabel> _name = { nullptr };
|
||||||
object_ptr<Ui::FlatLabel> _status = { nullptr };
|
object_ptr<Ui::FlatLabel> _status = { nullptr };
|
||||||
//object_ptr<CoverDropArea> _dropArea = { nullptr };
|
//object_ptr<CoverDropArea> _dropArea = { nullptr };
|
||||||
|
|
Loading…
Add table
Reference in a new issue