Allow sending multiple votes in a poll.
BIN
Telegram/Resources/icons/poll_choice_right.png
Normal file
After Width: | Height: | Size: 166 B |
BIN
Telegram/Resources/icons/poll_choice_right@2x.png
Normal file
After Width: | Height: | Size: 219 B |
BIN
Telegram/Resources/icons/poll_choice_right@3x.png
Normal file
After Width: | Height: | Size: 294 B |
BIN
Telegram/Resources/icons/poll_choice_wrong.png
Normal file
After Width: | Height: | Size: 157 B |
BIN
Telegram/Resources/icons/poll_choice_wrong@2x.png
Normal file
After Width: | Height: | Size: 213 B |
BIN
Telegram/Resources/icons/poll_choice_wrong@3x.png
Normal file
After Width: | Height: | Size: 260 B |
BIN
Telegram/Resources/icons/poll_select_check.png
Normal file
After Width: | Height: | Size: 249 B |
BIN
Telegram/Resources/icons/poll_select_check@2x.png
Normal file
After Width: | Height: | Size: 364 B |
BIN
Telegram/Resources/icons/poll_select_check@3x.png
Normal file
After Width: | Height: | Size: 556 B |
@ -2173,6 +2173,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
"lng_polls_votes_count#one" = "{count} vote";
|
"lng_polls_votes_count#one" = "{count} vote";
|
||||||
"lng_polls_votes_count#other" = "{count} votes";
|
"lng_polls_votes_count#other" = "{count} votes";
|
||||||
"lng_polls_votes_none" = "No 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_retract" = "Retract vote";
|
||||||
"lng_polls_stop" = "Stop poll";
|
"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.";
|
"lng_polls_stop_warning" = "If you stop this poll now, nobody will be able to vote in it anymore. This action cannot be undone.";
|
||||||
|
@ -5882,13 +5882,13 @@ void ApiWrap::sendPollVotes(
|
|||||||
const auto hideSending = [=] {
|
const auto hideSending = [=] {
|
||||||
if (showSending) {
|
if (showSending) {
|
||||||
if (const auto item = _session->data().message(itemId)) {
|
if (const auto item = _session->data().message(itemId)) {
|
||||||
poll->sendingVote = QByteArray();
|
poll->sendingVotes.clear();
|
||||||
_session->data().requestItemRepaint(item);
|
_session->data().requestItemRepaint(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (showSending) {
|
if (showSending) {
|
||||||
poll->sendingVote = options.front();
|
poll->sendingVotes = options;
|
||||||
_session->data().requestItemRepaint(item);
|
_session->data().requestItemRepaint(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ struct PollData {
|
|||||||
std::vector<PollAnswer> answers;
|
std::vector<PollAnswer> answers;
|
||||||
std::vector<not_null<UserData*>> recentVoters;
|
std::vector<not_null<UserData*>> recentVoters;
|
||||||
int totalVoters = 0;
|
int totalVoters = 0;
|
||||||
QByteArray sendingVote;
|
std::vector<QByteArray> sendingVotes;
|
||||||
crl::time lastResultsUpdate = 0;
|
crl::time lastResultsUpdate = 0;
|
||||||
|
|
||||||
int version = 0;
|
int version = 0;
|
||||||
|
@ -573,6 +573,14 @@ historyPollRippleOpacity: 0.3;
|
|||||||
historyPollRecentVotersSkip: 4px;
|
historyPollRecentVotersSkip: 4px;
|
||||||
historyPollRecentVoterSize: 18px;
|
historyPollRecentVoterSize: 18px;
|
||||||
historyPollRecentVoterSkip: 13px;
|
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) {
|
boxAttachEmoji: IconButton(historyAttachEmoji) {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "calls/calls_instance.h"
|
#include "calls/calls_instance.h"
|
||||||
#include "ui/text_options.h"
|
#include "ui/text_options.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
#include "ui/effects/radial_animation.h"
|
#include "ui/effects/radial_animation.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
@ -148,6 +149,7 @@ struct Poll::Answer {
|
|||||||
QString votesPercentString;
|
QString votesPercentString;
|
||||||
bool chosen = false;
|
bool chosen = false;
|
||||||
bool correct = false;
|
bool correct = false;
|
||||||
|
bool selected = false;
|
||||||
ClickHandlerPtr handler;
|
ClickHandlerPtr handler;
|
||||||
mutable std::unique_ptr<Ui::RippleAnimation> ripple;
|
mutable std::unique_ptr<Ui::RippleAnimation> ripple;
|
||||||
};
|
};
|
||||||
@ -184,7 +186,15 @@ Poll::Poll(
|
|||||||
not_null<PollData*> poll)
|
not_null<PollData*> poll)
|
||||||
: Media(parent)
|
: Media(parent)
|
||||||
, _poll(poll)
|
, _poll(poll)
|
||||||
, _question(st::msgMinWidth / 2) {
|
, _question(st::msgMinWidth / 2)
|
||||||
|
, _showResultsLink(
|
||||||
|
std::make_shared<LambdaClickHandler>(crl::guard(
|
||||||
|
this,
|
||||||
|
[=] { showResults(); })))
|
||||||
|
, _sendVotesLink(
|
||||||
|
std::make_shared<LambdaClickHandler>(crl::guard(
|
||||||
|
this,
|
||||||
|
[=] { sendMultiOptions(); }))) {
|
||||||
history()->owner().registerPollView(_poll, _parent);
|
history()->owner().registerPollView(_poll, _parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,6 +222,9 @@ QSize Poll::countOptimalSize() {
|
|||||||
+ st::historyPollAnswerPadding.bottom();
|
+ st::historyPollAnswerPadding.bottom();
|
||||||
}), 0);
|
}), 0);
|
||||||
|
|
||||||
|
const auto bottomButtonHeight = inlineFooter()
|
||||||
|
? 0
|
||||||
|
: st::historyPollBottomButtonSkip;
|
||||||
auto minHeight = st::historyPollQuestionTop
|
auto minHeight = st::historyPollQuestionTop
|
||||||
+ _question.minHeight()
|
+ _question.minHeight()
|
||||||
+ st::historyPollSubtitleSkip
|
+ st::historyPollSubtitleSkip
|
||||||
@ -219,6 +232,7 @@ QSize Poll::countOptimalSize() {
|
|||||||
+ st::historyPollAnswersSkip
|
+ st::historyPollAnswersSkip
|
||||||
+ answersHeight
|
+ answersHeight
|
||||||
+ st::msgPadding.bottom()
|
+ st::msgPadding.bottom()
|
||||||
|
+ bottomButtonHeight
|
||||||
+ st::msgDateFont->height
|
+ st::msgDateFont->height
|
||||||
+ st::msgPadding.bottom();
|
+ st::msgPadding.bottom();
|
||||||
if (!isBubbleTop()) {
|
if (!isBubbleTop()) {
|
||||||
@ -235,6 +249,21 @@ bool Poll::canVote() const {
|
|||||||
return !showVotes() && IsServerMsgId(_parent->data()->id);
|
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(
|
int Poll::countAnswerTop(
|
||||||
const Answer &answer,
|
const Answer &answer,
|
||||||
int innerWidth) const {
|
int innerWidth) const {
|
||||||
@ -288,6 +317,9 @@ QSize Poll::countCurrentSize(int newWidth) {
|
|||||||
return countAnswerHeight(answer, innerWidth);
|
return countAnswerHeight(answer, innerWidth);
|
||||||
}), 0);
|
}), 0);
|
||||||
|
|
||||||
|
const auto bottomButtonHeight = inlineFooter()
|
||||||
|
? 0
|
||||||
|
: st::historyPollBottomButtonSkip;
|
||||||
auto newHeight = st::historyPollQuestionTop
|
auto newHeight = st::historyPollQuestionTop
|
||||||
+ _question.countHeight(innerWidth)
|
+ _question.countHeight(innerWidth)
|
||||||
+ st::historyPollSubtitleSkip
|
+ st::historyPollSubtitleSkip
|
||||||
@ -295,6 +327,7 @@ QSize Poll::countCurrentSize(int newWidth) {
|
|||||||
+ st::historyPollAnswersSkip
|
+ st::historyPollAnswersSkip
|
||||||
+ answersHeight
|
+ answersHeight
|
||||||
+ st::historyPollTotalVotesSkip
|
+ st::historyPollTotalVotesSkip
|
||||||
|
+ bottomButtonHeight
|
||||||
+ st::msgDateFont->height
|
+ st::msgDateFont->height
|
||||||
+ st::msgPadding.bottom();
|
+ st::msgPadding.bottom();
|
||||||
if (!isBubbleTop()) {
|
if (!isBubbleTop()) {
|
||||||
@ -384,12 +417,59 @@ void Poll::updateAnswers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClickHandlerPtr Poll::createAnswerClickHandler(
|
ClickHandlerPtr Poll::createAnswerClickHandler(
|
||||||
const Answer &answer) const {
|
const Answer &answer) {
|
||||||
const auto option = answer.option;
|
const auto option = answer.option;
|
||||||
const auto itemId = _parent->data()->fullId();
|
if (_flags & PollData::Flag::MultiChoice) {
|
||||||
return std::make_shared<LambdaClickHandler>([=] {
|
return std::make_shared<LambdaClickHandler>(crl::guard(this, [=] {
|
||||||
history()->session().api().sendPollVotes(itemId, { option });
|
toggleMultiOption(option);
|
||||||
});
|
}));
|
||||||
|
}
|
||||||
|
return std::make_shared<LambdaClickHandler>(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() {
|
void Poll::updateVotes() {
|
||||||
@ -399,21 +479,23 @@ void Poll::updateVotes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Poll::checkSendingAnimation() const {
|
void Poll::checkSendingAnimation() const {
|
||||||
const auto &sending = _poll->sendingVote;
|
const auto &sending = _poll->sendingVotes;
|
||||||
if (sending.isEmpty() == !_sendingAnimation) {
|
const auto sendingRadial = (sending.size() == 1)
|
||||||
|
&& !(_flags & PollData::Flag::MultiChoice);
|
||||||
|
if (sendingRadial == (_sendingAnimation != nullptr)) {
|
||||||
if (_sendingAnimation) {
|
if (_sendingAnimation) {
|
||||||
_sendingAnimation->option = sending;
|
_sendingAnimation->option = sending.front();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sending.isEmpty()) {
|
if (!sendingRadial) {
|
||||||
if (!_answersAnimation) {
|
if (!_answersAnimation) {
|
||||||
_sendingAnimation = nullptr;
|
_sendingAnimation = nullptr;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_sendingAnimation = std::make_unique<SendingAnimation>(
|
_sendingAnimation = std::make_unique<SendingAnimation>(
|
||||||
sending,
|
sending.front(),
|
||||||
[=] { radialAnimationCallback(); });
|
[=] { radialAnimationCallback(); });
|
||||||
_sendingAnimation->animation.start();
|
_sendingAnimation->animation.start();
|
||||||
}
|
}
|
||||||
@ -547,23 +629,72 @@ void Poll::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m
|
|||||||
selection);
|
selection);
|
||||||
tshift += height;
|
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);
|
p.setPen(regular);
|
||||||
_totalVotesLabel.drawLeftElided(
|
_totalVotesLabel.drawLeftElided(
|
||||||
p,
|
p,
|
||||||
padding.left(),
|
left,
|
||||||
tshift,
|
top,
|
||||||
std::min(
|
std::min(
|
||||||
_totalVotesLabel.maxWidth(),
|
_totalVotesLabel.maxWidth(),
|
||||||
paintw - _parent->infoWidth()),
|
paintw - _parent->infoWidth()),
|
||||||
width());
|
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.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 {
|
void Poll::resetAnswersAnimation() const {
|
||||||
_answersAnimation = nullptr;
|
_answersAnimation = nullptr;
|
||||||
if (_poll->sendingVote.isEmpty()) {
|
if (_poll->sendingVotes.size() != 1
|
||||||
|
|| (_flags & PollData::Flag::MultiChoice)) {
|
||||||
_sendingAnimation = nullptr;
|
_sendingAnimation = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -708,9 +839,19 @@ void Poll::paintRadio(
|
|||||||
const auto over = ClickHandler::showAsActive(answer.handler);
|
const auto over = ClickHandler::showAsActive(answer.handler);
|
||||||
const auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg);
|
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();
|
const auto o = p.opacity();
|
||||||
|
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));
|
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.));
|
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) {
|
if (_sendingAnimation && _sendingAnimation->option == answer.option) {
|
||||||
@ -729,10 +870,16 @@ void Poll::paintRadio(
|
|||||||
state.arcLength);
|
state.arcLength);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (!checkmark) {
|
||||||
auto pen = regular->p;
|
auto pen = regular->p;
|
||||||
pen.setWidth(st.thickness);
|
pen.setWidth(st.thickness);
|
||||||
p.setPen(pen);
|
p.setPen(pen);
|
||||||
|
}
|
||||||
p.drawEllipse(rect);
|
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);
|
p.setOpacity(o);
|
||||||
@ -795,13 +942,19 @@ void Poll::paintFilling(
|
|||||||
auto barleft = aleft;
|
auto barleft = aleft;
|
||||||
auto barwidth = size;
|
auto barwidth = size;
|
||||||
if (chosen || correct) {
|
if (chosen || correct) {
|
||||||
p.drawEllipse(aleft, ftop - thickness, thickness * 3, thickness * 3);
|
const auto &icon = (chosen && !correct)
|
||||||
barleft += thickness * 3 - radius;
|
? st::historyPollChoiceWrong
|
||||||
barwidth -= thickness * 3 - radius;
|
: 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 {
|
bool Poll::answerVotesChanged() const {
|
||||||
if (_poll->answers.size() != _answers.size()
|
if (_poll->answers.size() != _answers.size()
|
||||||
@ -874,7 +1027,7 @@ void Poll::startAnswersAnimation() const {
|
|||||||
|
|
||||||
TextState Poll::textState(QPoint point, StateRequest request) const {
|
TextState Poll::textState(QPoint point, StateRequest request) const {
|
||||||
auto result = TextState(_parent);
|
auto result = TextState(_parent);
|
||||||
if (!_poll->sendingVote.isEmpty()) {
|
if (!_poll->sendingVotes.empty()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -912,6 +1065,25 @@ TextState Poll::textState(QPoint point, StateRequest request) const {
|
|||||||
}
|
}
|
||||||
tshift += height;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,10 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
|
|
||||||
#include "history/view/media/history_view_media.h"
|
#include "history/view/media/history_view_media.h"
|
||||||
#include "data/data_poll.h"
|
#include "data/data_poll.h"
|
||||||
|
#include "base/weak_ptr.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
|
||||||
class Poll : public Media {
|
class Poll : public Media, public base::has_weak_ptr {
|
||||||
public:
|
public:
|
||||||
Poll(
|
Poll(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
@ -52,6 +53,7 @@ private:
|
|||||||
|
|
||||||
[[nodiscard]] bool showVotes() const;
|
[[nodiscard]] bool showVotes() const;
|
||||||
[[nodiscard]] bool canVote() const;
|
[[nodiscard]] bool canVote() const;
|
||||||
|
[[nodiscard]] bool canSendVotes() const;
|
||||||
|
|
||||||
[[nodiscard]] int countAnswerTop(
|
[[nodiscard]] int countAnswerTop(
|
||||||
const Answer &answer,
|
const Answer &answer,
|
||||||
@ -60,12 +62,14 @@ private:
|
|||||||
const Answer &answer,
|
const Answer &answer,
|
||||||
int innerWidth) const;
|
int innerWidth) const;
|
||||||
[[nodiscard]] ClickHandlerPtr createAnswerClickHandler(
|
[[nodiscard]] ClickHandlerPtr createAnswerClickHandler(
|
||||||
const Answer &answer) const;
|
const Answer &answer);
|
||||||
void updateTexts();
|
void updateTexts();
|
||||||
void updateRecentVoters();
|
void updateRecentVoters();
|
||||||
void updateAnswers();
|
void updateAnswers();
|
||||||
void updateVotes();
|
void updateVotes();
|
||||||
void updateTotalVotes();
|
void updateTotalVotes();
|
||||||
|
bool showVotersCount() const;
|
||||||
|
bool inlineFooter() const;
|
||||||
void updateAnswerVotes();
|
void updateAnswerVotes();
|
||||||
void updateAnswerVotesFromOriginal(
|
void updateAnswerVotesFromOriginal(
|
||||||
Answer &answer,
|
Answer &answer,
|
||||||
@ -112,6 +116,18 @@ private:
|
|||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
TextSelection selection) const;
|
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 checkAnimationStart() const;
|
||||||
bool answerVotesChanged() const;
|
bool answerVotesChanged() const;
|
||||||
@ -121,6 +137,9 @@ private:
|
|||||||
void radialAnimationCallback() const;
|
void radialAnimationCallback() const;
|
||||||
|
|
||||||
void toggleRipple(Answer &answer, bool pressed);
|
void toggleRipple(Answer &answer, bool pressed);
|
||||||
|
void toggleMultiOption(const QByteArray &option);
|
||||||
|
void sendMultiOptions();
|
||||||
|
void showResults();
|
||||||
|
|
||||||
const not_null<PollData*> _poll;
|
const not_null<PollData*> _poll;
|
||||||
int _pollVersion = 0;
|
int _pollVersion = 0;
|
||||||
@ -135,6 +154,9 @@ private:
|
|||||||
|
|
||||||
std::vector<Answer> _answers;
|
std::vector<Answer> _answers;
|
||||||
Ui::Text::String _totalVotesLabel;
|
Ui::Text::String _totalVotesLabel;
|
||||||
|
ClickHandlerPtr _showResultsLink;
|
||||||
|
ClickHandlerPtr _sendVotesLink;
|
||||||
|
bool _hasSelected = false;
|
||||||
|
|
||||||
mutable std::unique_ptr<AnswersAnimation> _answersAnimation;
|
mutable std::unique_ptr<AnswersAnimation> _answersAnimation;
|
||||||
mutable std::unique_ptr<SendingAnimation> _sendingAnimation;
|
mutable std::unique_ptr<SendingAnimation> _sendingAnimation;
|
||||||
|