From 2981a16e172a9935120d9fdc0f7fdca55def82cc Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 10 Jan 2020 15:47:36 +0300 Subject: [PATCH] Allow sending multiple votes in a poll. --- .../Resources/icons/poll_choice_right.png | Bin 0 -> 166 bytes .../Resources/icons/poll_choice_right@2x.png | Bin 0 -> 219 bytes .../Resources/icons/poll_choice_right@3x.png | Bin 0 -> 294 bytes .../Resources/icons/poll_choice_wrong.png | Bin 0 -> 157 bytes .../Resources/icons/poll_choice_wrong@2x.png | Bin 0 -> 213 bytes .../Resources/icons/poll_choice_wrong@3x.png | Bin 0 -> 260 bytes .../Resources/icons/poll_select_check.png | Bin 0 -> 249 bytes .../Resources/icons/poll_select_check@2x.png | Bin 0 -> 364 bytes .../Resources/icons/poll_select_check@3x.png | Bin 0 -> 556 bytes Telegram/Resources/langs/lang.strings | 2 + Telegram/SourceFiles/apiwrap.cpp | 4 +- Telegram/SourceFiles/data/data_poll.h | 2 +- Telegram/SourceFiles/history/history.style | 8 + .../history/view/media/history_view_poll.cpp | 238 +++++++++++++++--- .../history/view/media/history_view_poll.h | 26 +- 15 files changed, 242 insertions(+), 38 deletions(-) create mode 100644 Telegram/Resources/icons/poll_choice_right.png create mode 100644 Telegram/Resources/icons/poll_choice_right@2x.png create mode 100644 Telegram/Resources/icons/poll_choice_right@3x.png create mode 100644 Telegram/Resources/icons/poll_choice_wrong.png create mode 100644 Telegram/Resources/icons/poll_choice_wrong@2x.png create mode 100644 Telegram/Resources/icons/poll_choice_wrong@3x.png create mode 100644 Telegram/Resources/icons/poll_select_check.png create mode 100644 Telegram/Resources/icons/poll_select_check@2x.png create mode 100644 Telegram/Resources/icons/poll_select_check@3x.png diff --git a/Telegram/Resources/icons/poll_choice_right.png b/Telegram/Resources/icons/poll_choice_right.png new file mode 100644 index 0000000000000000000000000000000000000000..ebc2c9e9ed4acb68f85bc698d37b5087d5a6d0d2 GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a)4Tu&Fr5RHjvCvN0CpdjL+FL>uc z`ML*U=dw~47iXwO>^ilS;~-0tLg=T54=nDj{>Ri%QTSR~;y$-h(;VYD#nb1O3kqI~ zni;g{+OIQjjz*mh;_5Z@{~$0W=*zv$-_KpU#Upn}u~k4KdCtK${Yo3t!E&IWB4}ZZq!{_}2qf;(%D=nSV*#BjSW$<+Mb6Mw<&;$SyCsMHh literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/poll_choice_right@3x.png b/Telegram/Resources/icons/poll_choice_right@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..706c7078026dd261d77bdaef837273739b25f7aa GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbB-g>$?hD5Z!y}psF*+8J-VK%$5 z#qL+i7FNIND!I$A`|N9)z3vx7Q?q};QK#;yTf`r(OW7fB)b`t>=>ac;fYK{DVKxzw z2~8_RT$U)Ndawi;V|3}4JSoa`DH@Hxf7?#w2g8OvV&ceFdR zEjJ=G^u)gv2746Pcz9R6mioQ#__kcb(?*#*yi&(M+9pV_>30b_H}6{aTcX6xFgYV& z-gArdo5fWdIIFMNU8$S;{BrVvmd1SxSqvU7h}UpCV|szx)OWJTGpmev&q)ime*2lL qb$XI;^Dk{*u(LEfwDbfRY#3x+SZs_hm^uJG&fw|l=d#Wzp$PzLZ*(UB literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/poll_choice_wrong.png b/Telegram/Resources/icons/poll_choice_wrong.png new file mode 100644 index 0000000000000000000000000000000000000000..afb504540d173b6de110760b7469829c3c4a1757 GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a)46i*k&5RHjv10A^zDDa#Z^KF6lF?hQAxvXn(wA6`H%~xwmO@Wa8j~(p{-Qo<#9rn7nB{R+A6%d>u8Dbi9M#}cfw3C{$RJ` zdq-QG(fl}(Ev+$C%iMdzob^m+b?9(c?@sQOt5QDy=c3vJf5smSzqF2SeEMRM573zopr0BKlLZ~y=R literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/poll_choice_wrong@3x.png b/Telegram/Resources/icons/poll_choice_wrong@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..2caf9a23e8efe10168a6e51781681f71d8857c44 GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBPJ6mIhD5Z!oqn44hyssG`gTF9 z66umBK^g5=pR{a>%-JL(bJG9RS-Jgx6e<~Ca2&Co#1nMFkt=e^grml-UYY@+TdpZ6 zy(_r3seSL650}Ii}Nk?4oZ{TUUZ$^hNPu_n?OfzMo6Oa(}<~YEv>R>qc?3v^bKyNX4y85}S Ib4q9e0D`q_XaE2J literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/poll_select_check.png b/Telegram/Resources/icons/poll_select_check.png new file mode 100644 index 0000000000000000000000000000000000000000..535ef0917c9dcdc173940baaac6c4f2b2b5e89b5 GIT binary patch literal 249 zcmV3;@u0D1zWu^%J`~I64S!#nr_haO}tAjP&5(EVW#r zH(VgN#77d80RYy#EFPCh8}tXi-{}22WQ@T)&rnJM&UtqvdWS<?MM`P!&Uq~7 zj9Tk)(0h;8S`U{wFvp>wW{6+&p}Ys>+x4@xO)OsitPfJxgvJ=p zV*4PWw7Y4VP$@-)L&>(ndR!Ct|CFOSe!s&fyTZ?-;r_*#00000NkvXXu0mjfoor|t literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/poll_select_check@2x.png b/Telegram/Resources/icons/poll_select_check@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f3ddc5122134f302e30347bd90d583df586d8998 GIT binary patch literal 364 zcmV-y0h9iTP)kV<5IeoFFHNO#oZZYmgJfCV;KyHTX9P20l)4@NfOg%ejhhY6NF*-d!Q=0WmzE0GE!lvNA9*KK68R z49Q@9d*k4$CI=C=2Pd~q@xE^7D4@$TeP8eF1Ffs&mWG}e$i2>6;~Q=WSo3!8Prau$a=Q24&3o{BpX~$xh(5o|A*`qEyVE_tCz{Xh)e_^cP|@N0&=K+Z!NiSO z3C9Z)_QoClSkZDfkKIw=1LM-!jBBJUPH*~l`_azztGJ$h-YIfJAdI)hZvOoPe_Ysd z1y-}!$?!4l;Lnv<%@(I4RneTd75nK0yh-hh#U&DL z7xuKjYWrnl)+5Vz=Hlg-BHw=h6^^=Ys^WPlKVyq;oNz|j;auK1v3la)Ds0+zcRo3> zxIy)~(W%rI_q|`QV`?l77r9+A#bBFD$@zAnYq5bjj{^_g5V~k9?-fz*y0O=tGuhka idyPioUD2av_xNo-dQJKGG0qzp{S2P2elF{r5}E)7MfkS> literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 6dea30cf6..916146cd6 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2173,6 +2173,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_polls_votes_count#one" = "{count} vote"; "lng_polls_votes_count#other" = "{count} votes"; "lng_polls_votes_none" = "No votes"; +"lng_polls_submit_votes" = "Submit votes"; +"lng_polls_view_results" = "View results"; "lng_polls_retract" = "Retract vote"; "lng_polls_stop" = "Stop poll"; "lng_polls_stop_warning" = "If you stop this poll now, nobody will be able to vote in it anymore. This action cannot be undone."; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 13dff208e..6d86fdfa8 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -5882,13 +5882,13 @@ void ApiWrap::sendPollVotes( const auto hideSending = [=] { if (showSending) { if (const auto item = _session->data().message(itemId)) { - poll->sendingVote = QByteArray(); + poll->sendingVotes.clear(); _session->data().requestItemRepaint(item); } } }; if (showSending) { - poll->sendingVote = options.front(); + poll->sendingVotes = options; _session->data().requestItemRepaint(item); } diff --git a/Telegram/SourceFiles/data/data_poll.h b/Telegram/SourceFiles/data/data_poll.h index 13ff92bd1..893027e2c 100644 --- a/Telegram/SourceFiles/data/data_poll.h +++ b/Telegram/SourceFiles/data/data_poll.h @@ -60,7 +60,7 @@ struct PollData { std::vector answers; std::vector> recentVoters; int totalVoters = 0; - QByteArray sendingVote; + std::vector sendingVotes; crl::time lastResultsUpdate = 0; int version = 0; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index ca64ff18d..0b9451c66 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -573,6 +573,14 @@ historyPollRippleOpacity: 0.3; historyPollRecentVotersSkip: 4px; historyPollRecentVoterSize: 18px; historyPollRecentVoterSkip: 13px; +historyPollBottomButtonSkip: 15px; +historyPollBottomButtonTop: 4px; +historyPollChoiceRight: icon {{ "poll_choice_right", activeButtonFg }}; +historyPollChoiceWrong: icon {{ "poll_choice_wrong", activeButtonFg }}; +historyPollOutChosen: icon {{ "poll_select_check", historyFileOutIconFg }}; +historyPollOutChosenSelected: icon {{ "poll_select_check", historyFileOutIconFgSelected }}; +historyPollInChosen: icon {{ "poll_select_check", historyFileInIconFg }}; +historyPollInChosenSelected: icon {{ "poll_select_check", historyFileInIconFgSelected }}; boxAttachEmoji: IconButton(historyAttachEmoji) { width: 30px; diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp index a61bacdd5..c11549428 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "calls/calls_instance.h" #include "ui/text_options.h" +#include "ui/text/text_utilities.h" #include "ui/effects/animations.h" #include "ui/effects/radial_animation.h" #include "ui/effects/ripple_animation.h" @@ -148,6 +149,7 @@ struct Poll::Answer { QString votesPercentString; bool chosen = false; bool correct = false; + bool selected = false; ClickHandlerPtr handler; mutable std::unique_ptr ripple; }; @@ -184,7 +186,15 @@ Poll::Poll( not_null poll) : Media(parent) , _poll(poll) -, _question(st::msgMinWidth / 2) { +, _question(st::msgMinWidth / 2) +, _showResultsLink( + std::make_shared(crl::guard( + this, + [=] { showResults(); }))) +, _sendVotesLink( + std::make_shared(crl::guard( + this, + [=] { sendMultiOptions(); }))) { history()->owner().registerPollView(_poll, _parent); } @@ -212,6 +222,9 @@ QSize Poll::countOptimalSize() { + st::historyPollAnswerPadding.bottom(); }), 0); + const auto bottomButtonHeight = inlineFooter() + ? 0 + : st::historyPollBottomButtonSkip; auto minHeight = st::historyPollQuestionTop + _question.minHeight() + st::historyPollSubtitleSkip @@ -219,6 +232,7 @@ QSize Poll::countOptimalSize() { + st::historyPollAnswersSkip + answersHeight + st::msgPadding.bottom() + + bottomButtonHeight + st::msgDateFont->height + st::msgPadding.bottom(); if (!isBubbleTop()) { @@ -235,6 +249,21 @@ bool Poll::canVote() const { return !showVotes() && IsServerMsgId(_parent->data()->id); } +bool Poll::canSendVotes() const { + return canVote() && _hasSelected; +} + +bool Poll::showVotersCount() const { + return showVotes() + ? !(_flags & PollData::Flag::PublicVotes) + : !(_flags & PollData::Flag::MultiChoice); +} + +bool Poll::inlineFooter() const { + return !(_flags + & (PollData::Flag::PublicVotes | PollData::Flag::MultiChoice)); +} + int Poll::countAnswerTop( const Answer &answer, int innerWidth) const { @@ -288,6 +317,9 @@ QSize Poll::countCurrentSize(int newWidth) { return countAnswerHeight(answer, innerWidth); }), 0); + const auto bottomButtonHeight = inlineFooter() + ? 0 + : st::historyPollBottomButtonSkip; auto newHeight = st::historyPollQuestionTop + _question.countHeight(innerWidth) + st::historyPollSubtitleSkip @@ -295,6 +327,7 @@ QSize Poll::countCurrentSize(int newWidth) { + st::historyPollAnswersSkip + answersHeight + st::historyPollTotalVotesSkip + + bottomButtonHeight + st::msgDateFont->height + st::msgPadding.bottom(); if (!isBubbleTop()) { @@ -384,12 +417,59 @@ void Poll::updateAnswers() { } ClickHandlerPtr Poll::createAnswerClickHandler( - const Answer &answer) const { + const Answer &answer) { const auto option = answer.option; - const auto itemId = _parent->data()->fullId(); - return std::make_shared([=] { - history()->session().api().sendPollVotes(itemId, { option }); - }); + if (_flags & PollData::Flag::MultiChoice) { + return std::make_shared(crl::guard(this, [=] { + toggleMultiOption(option); + })); + } + return std::make_shared(crl::guard(this, [=] { + history()->session().api().sendPollVotes( + _parent->data()->fullId(), + { option }); + })); +} + +void Poll::toggleMultiOption(const QByteArray &option) { + const auto i = ranges::find( + _answers, + option, + &Answer::option); + if (i != end(_answers)) { + const auto selected = i->selected; + i->selected = !selected; + if (selected) { + const auto j = ranges::find( + _answers, + true, + &Answer::selected); + _hasSelected = (j != end(_answers)); + } else { + _hasSelected = true; + } + history()->owner().requestViewRepaint(_parent); + } +} + +void Poll::sendMultiOptions() { + auto chosen = _answers | ranges::view::filter( + &Answer::selected + ) | ranges::view::transform( + &Answer::option + ) | ranges::to_vector; + if (!chosen.empty()) { + for (auto &answer : _answers) { + answer.selected = false; + } + history()->session().api().sendPollVotes( + _parent->data()->fullId(), + std::move(chosen)); + } +} + +void Poll::showResults() { + // #TODO polls } void Poll::updateVotes() { @@ -399,21 +479,23 @@ void Poll::updateVotes() { } void Poll::checkSendingAnimation() const { - const auto &sending = _poll->sendingVote; - if (sending.isEmpty() == !_sendingAnimation) { + const auto &sending = _poll->sendingVotes; + const auto sendingRadial = (sending.size() == 1) + && !(_flags & PollData::Flag::MultiChoice); + if (sendingRadial == (_sendingAnimation != nullptr)) { if (_sendingAnimation) { - _sendingAnimation->option = sending; + _sendingAnimation->option = sending.front(); } return; } - if (sending.isEmpty()) { + if (!sendingRadial) { if (!_answersAnimation) { _sendingAnimation = nullptr; } return; } _sendingAnimation = std::make_unique( - sending, + sending.front(), [=] { radialAnimationCallback(); }); _sendingAnimation->animation.start(); } @@ -547,23 +629,72 @@ void Poll::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m selection); tshift += height; } - if (!_totalVotesLabel.isEmpty()) { - tshift += st::msgPadding.bottom(); + tshift += st::msgPadding.bottom(); + if (!inlineFooter()) { + paintBottom(p, padding.left(), tshift, paintw, selection); + } else if (!_totalVotesLabel.isEmpty()) { + paintInlineFooter(p, padding.left(), tshift, paintw, selection); + } +} + +void Poll::paintInlineFooter( + Painter &p, + int left, + int top, + int paintw, + TextSelection selection) const { + const auto selected = (selection == FullSelection); + const auto outbg = _parent->hasOutLayout(); + const auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); + p.setPen(regular); + _totalVotesLabel.drawLeftElided( + p, + left, + top, + std::min( + _totalVotesLabel.maxWidth(), + paintw - _parent->infoWidth()), + width()); +} + +void Poll::paintBottom( + Painter &p, + int left, + int top, + int paintw, + TextSelection selection) const { + const auto stringtop = top + st::historyPollBottomButtonTop; + const auto selected = (selection == FullSelection); + const auto outbg = _parent->hasOutLayout(); + const auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); + if (showVotersCount()) { p.setPen(regular); - _totalVotesLabel.drawLeftElided( - p, - padding.left(), - tshift, - std::min( - _totalVotesLabel.maxWidth(), - paintw - _parent->infoWidth()), - width()); + _totalVotesLabel.draw(p, left, stringtop, paintw, style::al_top); + } else { + const auto link = showVotes() + ? _showResultsLink + : canSendVotes() + ? _sendVotesLink + : nullptr; + const auto over = link ? ClickHandler::showAsActive(link) : false; + p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont); + if (!link) { + p.setPen(regular); + } else { + p.setPen(outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg)); + } + const auto string = showVotes() + ? tr::lng_polls_view_results(tr::now, Ui::Text::Upper) + : tr::lng_polls_submit_votes(tr::now, Ui::Text::Upper); + const auto stringw = st::semiboldFont->width(string); + p.drawTextLeft(left + (paintw - stringw) / 2, stringtop, width(), string, stringw); } } void Poll::resetAnswersAnimation() const { _answersAnimation = nullptr; - if (_poll->sendingVote.isEmpty()) { + if (_poll->sendingVotes.size() != 1 + || (_flags & PollData::Flag::MultiChoice)) { _sendingAnimation = nullptr; } } @@ -708,9 +839,19 @@ void Poll::paintRadio( const auto over = ClickHandler::showAsActive(answer.handler); const auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); - p.setBrush(Qt::NoBrush); + const auto checkmark = answer.selected; + const auto o = p.opacity(); - p.setOpacity(o * (over ? st::historyPollRadioOpacityOver : st::historyPollRadioOpacity)); + if (checkmark) { + const auto color = outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg); + auto pen = color->p; + pen.setWidth(st.thickness); + p.setPen(pen); + p.setBrush(color); + } else { + p.setBrush(Qt::NoBrush); + p.setOpacity(o * (over ? st::historyPollRadioOpacityOver : st::historyPollRadioOpacity)); + } const auto rect = QRectF(left, top, st.diameter, st.diameter).marginsRemoved(QMarginsF(st.thickness / 2., st.thickness / 2., st.thickness / 2., st.thickness / 2.)); if (_sendingAnimation && _sendingAnimation->option == answer.option) { @@ -729,10 +870,16 @@ void Poll::paintRadio( state.arcLength); } } else { - auto pen = regular->p; - pen.setWidth(st.thickness); - p.setPen(pen); + if (!checkmark) { + auto pen = regular->p; + pen.setWidth(st.thickness); + p.setPen(pen); + } p.drawEllipse(rect); + if (checkmark) { + const auto &icon = outbg ? (selected ? st::historyPollOutChosenSelected : st::historyPollOutChosen) : (selected ? st::historyPollInChosenSelected : st::historyPollInChosen); + icon.paint(p, left + (st.diameter - icon.width()) / 2, top + (st.diameter - icon.height()) / 2, width()); + } } p.setOpacity(o); @@ -795,12 +942,18 @@ void Poll::paintFilling( auto barleft = aleft; auto barwidth = size; if (chosen || correct) { - p.drawEllipse(aleft, ftop - thickness, thickness * 3, thickness * 3); - barleft += thickness * 3 - radius; - barwidth -= thickness * 3 - radius; + const auto &icon = (chosen && !correct) + ? st::historyPollChoiceWrong + : st::historyPollChoiceRight; + const auto ctop = ftop - (icon.height() - thickness) / 2; + p.drawEllipse(aleft, ctop, icon.width(), icon.height()); + icon.paint(p, aleft, ctop, width); + barleft += icon.width() - radius; + barwidth -= icon.width() - radius; + } + if (barwidth > 0) { + p.drawRoundedRect(barleft, ftop, barwidth, thickness, radius, radius); } - - p.drawRoundedRect(barleft, ftop, barwidth, thickness, radius, radius); } bool Poll::answerVotesChanged() const { @@ -874,7 +1027,7 @@ void Poll::startAnswersAnimation() const { TextState Poll::textState(QPoint point, StateRequest request) const { auto result = TextState(_parent); - if (!_poll->sendingVote.isEmpty()) { + if (!_poll->sendingVotes.empty()) { return result; } @@ -912,6 +1065,25 @@ TextState Poll::textState(QPoint point, StateRequest request) const { } tshift += height; } + tshift += st::msgPadding.bottom(); + if (!showVotersCount()) { + const auto link = showVotes() + ? _showResultsLink + : canSendVotes() + ? _sendVotesLink + : nullptr; + if (link) { + const auto string = showVotes() + ? tr::lng_polls_view_results(tr::now, Ui::Text::Upper) + : tr::lng_polls_submit_votes(tr::now, Ui::Text::Upper); + const auto stringw = st::semiboldFont->width(string); + const auto stringtop = tshift + st::historyPollBottomButtonTop; + if (QRect(padding.left() + (paintw - stringw) / 2, stringtop, stringw, st::semiboldFont->height).contains(point)) { + result.link = link; + return result; + } + } + } return result; } diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.h b/Telegram/SourceFiles/history/view/media/history_view_poll.h index 0d4232623..900c752a3 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.h +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.h @@ -9,10 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media.h" #include "data/data_poll.h" +#include "base/weak_ptr.h" namespace HistoryView { -class Poll : public Media { +class Poll : public Media, public base::has_weak_ptr { public: Poll( not_null parent, @@ -52,6 +53,7 @@ private: [[nodiscard]] bool showVotes() const; [[nodiscard]] bool canVote() const; + [[nodiscard]] bool canSendVotes() const; [[nodiscard]] int countAnswerTop( const Answer &answer, @@ -60,12 +62,14 @@ private: const Answer &answer, int innerWidth) const; [[nodiscard]] ClickHandlerPtr createAnswerClickHandler( - const Answer &answer) const; + const Answer &answer); void updateTexts(); void updateRecentVoters(); void updateAnswers(); void updateVotes(); void updateTotalVotes(); + bool showVotersCount() const; + bool inlineFooter() const; void updateAnswerVotes(); void updateAnswerVotesFromOriginal( Answer &answer, @@ -112,6 +116,18 @@ private: int width, int height, TextSelection selection) const; + void paintInlineFooter( + Painter &p, + int left, + int top, + int paintw, + TextSelection selection) const; + void paintBottom( + Painter &p, + int left, + int top, + int paintw, + TextSelection selection) const; bool checkAnimationStart() const; bool answerVotesChanged() const; @@ -121,6 +137,9 @@ private: void radialAnimationCallback() const; void toggleRipple(Answer &answer, bool pressed); + void toggleMultiOption(const QByteArray &option); + void sendMultiOptions(); + void showResults(); const not_null _poll; int _pollVersion = 0; @@ -135,6 +154,9 @@ private: std::vector _answers; Ui::Text::String _totalVotesLabel; + ClickHandlerPtr _showResultsLink; + ClickHandlerPtr _sendVotesLink; + bool _hasSelected = false; mutable std::unique_ptr _answersAnimation; mutable std::unique_ptr _sendingAnimation;