mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
[poll-view] Added explanation block.
This commit is contained in:
@@ -56,6 +56,13 @@ historyPollRecentVoterSize: 18px;
|
||||
historyPollRecentVoterSkip: 13px;
|
||||
historyPollBottomButtonSkip: 15px;
|
||||
historyPollBottomButtonTop: 4px;
|
||||
historyPollExplanationSkip: 8px;
|
||||
historyPollExplanationTitleSkip: 4px;
|
||||
historyPollExplanationCloseSize: 20px;
|
||||
historyPollExplanationCloseIconSize: 8px;
|
||||
historyPollExplanationCloseStroke: 2px;
|
||||
historyPollExplanationMediaMaxHeight: 300px;
|
||||
historyPollExplanationMediaSkip: 6px;
|
||||
historyPollChoiceRight: icon {{ "poll_choice_right", activeButtonFg }};
|
||||
historyPollChoiceWrong: icon {{ "poll_choice_wrong", activeButtonFg }};
|
||||
historyPollOutChoiceRight: icon {{ "poll_choice_right", historyFileOutIconFg }};
|
||||
|
||||
@@ -30,8 +30,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/painter.h"
|
||||
#include "ui/dynamic_image.h"
|
||||
#include "ui/dynamic_thumbnails.h"
|
||||
#include "history/view/media/history_view_media_common.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_poll.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_session.h"
|
||||
@@ -310,6 +312,11 @@ struct Poll::AttachedMedia {
|
||||
uint64 id = 0;
|
||||
};
|
||||
|
||||
struct Poll::SolutionMedia {
|
||||
PollThumbnailKind kind = PollThumbnailKind::None;
|
||||
uint64 id = 0;
|
||||
};
|
||||
|
||||
struct Poll::CloseInformation {
|
||||
CloseInformation(TimeId date, TimeId period, Fn<void()> repaint);
|
||||
|
||||
@@ -602,6 +609,8 @@ void Poll::updateTexts() {
|
||||
updateRecentVoters();
|
||||
updateAnswers();
|
||||
updateAttachedMedia();
|
||||
updateSolutionText();
|
||||
updateSolutionMedia();
|
||||
updateVotes();
|
||||
|
||||
if (willStartAnimation) {
|
||||
@@ -639,6 +648,63 @@ void Poll::updateDescription() {
|
||||
context);
|
||||
}
|
||||
|
||||
void Poll::updateSolutionText() {
|
||||
if (_poll->solution.text.isEmpty()) {
|
||||
_solutionText = Ui::Text::String();
|
||||
return;
|
||||
}
|
||||
if (_solutionText.toTextWithEntities() == _poll->solution) {
|
||||
return;
|
||||
}
|
||||
_solutionText = Ui::Text::String(st::msgMinWidth);
|
||||
_solutionText.setMarkedText(
|
||||
st::webPageDescriptionStyle,
|
||||
_poll->solution,
|
||||
Ui::ItemTextOptions(_parent->data()),
|
||||
Core::TextContext({
|
||||
.session = &_poll->session(),
|
||||
.repaint = [=] { repaint(); },
|
||||
}));
|
||||
}
|
||||
|
||||
void Poll::updateSolutionMedia() {
|
||||
const auto item = _parent->data();
|
||||
const auto messageContext = Window::SessionController::MessageContext{
|
||||
.id = item->fullId(),
|
||||
.topicRootId = item->topicRootId(),
|
||||
.monoforumPeerId = item->sublistPeerId(),
|
||||
};
|
||||
const auto updated = MakePollThumbnail(
|
||||
_poll,
|
||||
_poll->solutionMedia,
|
||||
messageContext);
|
||||
if (!updated.thumbnail) {
|
||||
_solutionMedia = nullptr;
|
||||
_solutionAttach = nullptr;
|
||||
return;
|
||||
}
|
||||
if (_solutionMedia
|
||||
&& _solutionMedia->kind == updated.kind
|
||||
&& _solutionMedia->id == updated.id) {
|
||||
return;
|
||||
}
|
||||
if (!_solutionMedia) {
|
||||
_solutionMedia = std::make_unique<SolutionMedia>();
|
||||
}
|
||||
_solutionMedia->kind = updated.kind;
|
||||
_solutionMedia->id = updated.id;
|
||||
auto photo = (PhotoData*)(nullptr);
|
||||
auto document = (DocumentData*)(nullptr);
|
||||
if (updated.kind == PollThumbnailKind::Photo && updated.id) {
|
||||
photo = _poll->owner().photo(PhotoId(updated.id));
|
||||
} else if (updated.kind == PollThumbnailKind::Document && updated.id) {
|
||||
document = _poll->owner().document(DocumentId(updated.id));
|
||||
}
|
||||
_solutionAttach = (photo || document)
|
||||
? CreateAttach(_parent, document, photo)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void Poll::updateAttachedMedia() {
|
||||
const auto item = _parent->data();
|
||||
const auto messageContext = Window::SessionController::MessageContext{
|
||||
@@ -749,6 +815,33 @@ int Poll::countDescriptionHeight(int innerWidth) const {
|
||||
return _description.isEmpty() ? 0 : _description.countHeight(innerWidth);
|
||||
}
|
||||
|
||||
int Poll::countSolutionMediaHeight(int mediaWidth) const {
|
||||
if (!_solutionAttach) {
|
||||
return 0;
|
||||
}
|
||||
_solutionAttach->initDimensions();
|
||||
return _solutionAttach->resizeGetHeight(mediaWidth);
|
||||
}
|
||||
|
||||
int Poll::countSolutionBlockHeight(int innerWidth) const {
|
||||
if (!_solutionShown || !canShowSolution()) {
|
||||
return 0;
|
||||
}
|
||||
const auto &qst = st::historyPagePreview;
|
||||
const auto textWidth = innerWidth
|
||||
- qst.padding.left()
|
||||
- qst.padding.right();
|
||||
auto height = qst.padding.top();
|
||||
height += st::semiboldFont->height;
|
||||
height += st::historyPollExplanationTitleSkip;
|
||||
height += _solutionText.countHeight(textWidth);
|
||||
if (const auto mediaHeight = countSolutionMediaHeight(textWidth)) {
|
||||
height += st::historyPollExplanationMediaSkip + mediaHeight;
|
||||
}
|
||||
height += qst.padding.bottom();
|
||||
return height;
|
||||
}
|
||||
|
||||
int Poll::countQuestionTop(int innerWidth) const {
|
||||
auto result = countTopContentSkip();
|
||||
if (const auto mediaHeight = countTopMediaHeight()) {
|
||||
@@ -757,6 +850,9 @@ int Poll::countQuestionTop(int innerWidth) const {
|
||||
if (const auto descriptionHeight = countDescriptionHeight(innerWidth)) {
|
||||
result += descriptionHeight + st::historyPollDescriptionSkip;
|
||||
}
|
||||
if (const auto solutionHeight = countSolutionBlockHeight(innerWidth)) {
|
||||
result += solutionHeight + st::historyPollExplanationSkip;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -793,9 +889,6 @@ void Poll::checkQuizAnswered() {
|
||||
void Poll::showSolution() const {
|
||||
if (!_poll->solution.text.isEmpty()) {
|
||||
solutionToggled(true);
|
||||
_parent->delegate()->elementShowTooltip(
|
||||
_poll->solution,
|
||||
crl::guard(this, [=] { solutionToggled(false); }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,10 +906,8 @@ void Poll::solutionToggled(
|
||||
return;
|
||||
}
|
||||
_solutionButtonVisible = visible;
|
||||
history()->owner().notifyViewLayoutChange(_parent);
|
||||
if (animated == anim::type::instant) {
|
||||
_solutionButtonAnimation.stop();
|
||||
repaint();
|
||||
} else {
|
||||
_solutionButtonAnimation.start(
|
||||
[=] { repaint(); },
|
||||
@@ -824,6 +915,7 @@ void Poll::solutionToggled(
|
||||
visible ? 1. : 0.,
|
||||
st::fadeWrapDuration);
|
||||
}
|
||||
history()->owner().requestViewResize(_parent);
|
||||
}
|
||||
|
||||
void Poll::updateRecentVoters() {
|
||||
@@ -1190,6 +1282,11 @@ void Poll::draw(Painter &p, const PaintContext &context) const {
|
||||
tshift += descriptionHeight + st::historyPollDescriptionSkip;
|
||||
}
|
||||
|
||||
if (const auto solutionHeight = countSolutionBlockHeight(paintw)) {
|
||||
paintSolutionBlock(p, padding.left(), tshift, paintw, context);
|
||||
tshift += solutionHeight + st::historyPollExplanationSkip;
|
||||
}
|
||||
|
||||
p.setPen(stm->historyTextFg);
|
||||
_question.drawLeft(
|
||||
p,
|
||||
@@ -1482,6 +1579,100 @@ void Poll::paintShowSolution(
|
||||
}
|
||||
}
|
||||
|
||||
void Poll::paintSolutionBlock(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
int paintw,
|
||||
const PaintContext &context) const {
|
||||
if (!_solutionShown || !canShowSolution()) {
|
||||
return;
|
||||
}
|
||||
if (!_closeSolutionLink) {
|
||||
_closeSolutionLink = std::make_shared<LambdaClickHandler>(
|
||||
crl::guard(this, [=] { solutionToggled(false); }));
|
||||
}
|
||||
|
||||
const auto &qst = st::historyPagePreview;
|
||||
const auto blockHeight = countSolutionBlockHeight(paintw);
|
||||
const auto outer = QRect(left, top, paintw, blockHeight);
|
||||
|
||||
const auto stm = context.messageStyle();
|
||||
const auto view = _parent;
|
||||
const auto selected = context.selected();
|
||||
const auto colorIndex = view->contentColorIndex();
|
||||
const auto &chatSt = *context.st;
|
||||
const auto colorPattern = chatSt.colorPatternIndex(colorIndex);
|
||||
const auto useColorIndex = !context.outbg;
|
||||
const auto cache = useColorIndex
|
||||
? chatSt.coloredReplyCache(selected, colorIndex).get()
|
||||
: stm->replyCache[colorPattern].get();
|
||||
|
||||
Ui::Text::ValidateQuotePaintCache(*cache, qst);
|
||||
Ui::Text::FillQuotePaint(p, outer, *cache, qst);
|
||||
|
||||
const auto innerLeft = left + qst.padding.left();
|
||||
const auto innerRight = left + paintw - qst.padding.right();
|
||||
const auto textWidth = innerRight - innerLeft;
|
||||
auto yshift = top + qst.padding.top();
|
||||
|
||||
p.setPen(cache->outlines[0]);
|
||||
p.setFont(st::semiboldFont);
|
||||
const auto closeArea = st::historyPollExplanationCloseSize;
|
||||
p.drawTextLeft(
|
||||
innerLeft,
|
||||
yshift,
|
||||
width(),
|
||||
tr::lng_polls_solution_title(tr::now),
|
||||
textWidth - closeArea);
|
||||
|
||||
{
|
||||
const auto iconSize = st::historyPollExplanationCloseIconSize;
|
||||
const auto centerX = innerRight - closeArea / 2;
|
||||
const auto centerY = yshift + st::semiboldFont->height / 2;
|
||||
const auto half = iconSize / 2;
|
||||
auto pen = QPen(cache->outlines[0]);
|
||||
pen.setWidthF(st::historyPollExplanationCloseStroke * 1.);
|
||||
pen.setCapStyle(Qt::RoundCap);
|
||||
p.setPen(pen);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
p.drawLine(
|
||||
centerX - half, centerY - half,
|
||||
centerX + half, centerY + half);
|
||||
p.drawLine(
|
||||
centerX + half, centerY - half,
|
||||
centerX - half, centerY + half);
|
||||
p.setRenderHint(QPainter::Antialiasing, false);
|
||||
}
|
||||
|
||||
yshift += st::semiboldFont->height + st::historyPollExplanationTitleSkip;
|
||||
|
||||
p.setPen(stm->historyTextFg);
|
||||
_solutionText.draw(p, {
|
||||
.position = { innerLeft, yshift },
|
||||
.outerWidth = width(),
|
||||
.availableWidth = textWidth,
|
||||
.now = context.now,
|
||||
.pausedEmoji = context.paused,
|
||||
.pausedSpoiler = context.paused,
|
||||
});
|
||||
|
||||
if (countSolutionMediaHeight(textWidth)) {
|
||||
yshift += _solutionText.countHeight(textWidth)
|
||||
+ st::historyPollExplanationMediaSkip;
|
||||
const auto shift = st::msgFileLayout.padding.left();
|
||||
const auto attachLeft = rtl()
|
||||
? (width() - innerLeft + shift - _solutionAttach->width())
|
||||
: (innerLeft - shift);
|
||||
p.translate(attachLeft, yshift);
|
||||
_solutionAttach->draw(
|
||||
p,
|
||||
context.translated(-attachLeft, -yshift)
|
||||
.withSelection(TextSelection()));
|
||||
p.translate(-attachLeft, -yshift);
|
||||
}
|
||||
}
|
||||
|
||||
int Poll::paintAnswer(
|
||||
Painter &p,
|
||||
const Answer &answer,
|
||||
@@ -1939,6 +2130,74 @@ TextState Poll::textState(QPoint point, StateRequest request) const {
|
||||
tshift += descriptionHeight + st::historyPollDescriptionSkip;
|
||||
}
|
||||
|
||||
if (const auto solutionHeight = countSolutionBlockHeight(paintw)) {
|
||||
if (QRect(
|
||||
padding.left(),
|
||||
tshift,
|
||||
paintw,
|
||||
solutionHeight).contains(point)) {
|
||||
const auto &qst = st::historyPagePreview;
|
||||
const auto innerLeft = padding.left() + qst.padding.left();
|
||||
const auto innerRight = padding.left()
|
||||
+ paintw
|
||||
- qst.padding.right();
|
||||
const auto closeArea = st::historyPollExplanationCloseSize;
|
||||
const auto closeLeft = innerRight - closeArea;
|
||||
const auto closeTop = tshift + qst.padding.top();
|
||||
if (QRect(
|
||||
closeLeft,
|
||||
closeTop,
|
||||
closeArea,
|
||||
st::semiboldFont->height).contains(point)) {
|
||||
result.link = _closeSolutionLink;
|
||||
return result;
|
||||
}
|
||||
const auto textTop = tshift
|
||||
+ qst.padding.top()
|
||||
+ st::semiboldFont->height
|
||||
+ st::historyPollExplanationTitleSkip;
|
||||
const auto textWidth = innerRight - innerLeft;
|
||||
const auto textHeight = _solutionText.countHeight(textWidth);
|
||||
if (QRect(
|
||||
innerLeft,
|
||||
textTop,
|
||||
textWidth,
|
||||
textHeight).contains(point)) {
|
||||
auto textResult = _solutionText.getStateLeft(
|
||||
point - QPoint(innerLeft, textTop),
|
||||
textWidth,
|
||||
width(),
|
||||
request.forText());
|
||||
if (textResult.link) {
|
||||
result.link = textResult.link;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (const auto mh = countSolutionMediaHeight(textWidth)) {
|
||||
const auto mediaTop = textTop
|
||||
+ textHeight
|
||||
+ st::historyPollExplanationMediaSkip;
|
||||
const auto shift = st::msgFileLayout.padding.left();
|
||||
const auto mediaLeft = innerLeft - shift;
|
||||
if (_solutionAttach
|
||||
&& QRect(
|
||||
mediaLeft,
|
||||
mediaTop,
|
||||
_solutionAttach->width(),
|
||||
mh).contains(point)) {
|
||||
const auto attachLeft = rtl()
|
||||
? (width() - innerLeft - _solutionAttach->width())
|
||||
: innerLeft;
|
||||
result = _solutionAttach->textState(
|
||||
point - QPoint(attachLeft, mediaTop),
|
||||
request);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
tshift += solutionHeight + st::historyPollExplanationSkip;
|
||||
}
|
||||
|
||||
const auto questionH = _question.countHeight(paintw);
|
||||
if (QRect(padding.left(), tshift, paintw, questionH).contains(point)) {
|
||||
result = TextState(_parent, _question.getState(
|
||||
|
||||
@@ -73,6 +73,7 @@ private:
|
||||
struct SendingAnimation;
|
||||
struct Answer;
|
||||
struct AttachedMedia;
|
||||
struct SolutionMedia;
|
||||
struct CloseInformation;
|
||||
struct RecentVoter;
|
||||
|
||||
@@ -208,6 +209,17 @@ private:
|
||||
int right,
|
||||
int top) const;
|
||||
|
||||
void updateSolutionText();
|
||||
void updateSolutionMedia();
|
||||
[[nodiscard]] int countSolutionBlockHeight(int innerWidth) const;
|
||||
[[nodiscard]] int countSolutionMediaHeight(int mediaWidth) const;
|
||||
void paintSolutionBlock(
|
||||
Painter &p,
|
||||
int left,
|
||||
int top,
|
||||
int paintw,
|
||||
const PaintContext &context) const;
|
||||
|
||||
[[nodiscard]] int bottomButtonHeight() const;
|
||||
|
||||
const not_null<PollData*> _poll;
|
||||
@@ -247,6 +259,11 @@ private:
|
||||
mutable bool _solutionShown = false;
|
||||
mutable bool _solutionButtonVisible = false;
|
||||
|
||||
Ui::Text::String _solutionText;
|
||||
mutable ClickHandlerPtr _closeSolutionLink;
|
||||
std::unique_ptr<SolutionMedia> _solutionMedia;
|
||||
std::unique_ptr<Media> _solutionAttach;
|
||||
|
||||
bool _hasSelected = false;
|
||||
bool _votedFromHere = false;
|
||||
mutable bool _wrongAnswerAnimated = false;
|
||||
|
||||
Reference in New Issue
Block a user