Support url authorizations.

This commit is contained in:
John Preston 2019-05-22 16:29:02 +02:00
parent 8660f976a9
commit c92a798e1b
12 changed files with 473 additions and 144 deletions

View File

@ -1223,6 +1223,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_open_link" = "Open";
"lng_allow_bot_pass" = "Allow {bot_name} to pass your Telegram name and ID to the web pages you open via this bot?";
"lng_allow_bot" = "Allow";
"lng_url_auth_open_confirm" = "Do you want to open {link}?";
"lng_url_auth_login_option" = "Log in to {domain} as {user}";
"lng_url_auth_allow_messages" = "Allow {bot} to send me messages";
"lng_bot_start" = "Start";
"lng_bot_choose_group" = "Select a Group";

View File

@ -970,3 +970,7 @@ backgroundCheck: ServiceCheck {
color: msgServiceFg;
duration: 200;
}
urlAuthCheckbox: Checkbox(defaultBoxCheckbox) {
width: 240px;
}

View File

@ -0,0 +1,204 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/url_auth_box.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_item_components.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "core/click_handler_types.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "lang/lang_keys.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "styles/style_boxes.h"
void UrlAuthBox::Activate(
not_null<const HistoryItem*> message,
int row,
int column) {
const auto itemId = message->fullId();
const auto button = HistoryMessageMarkupButton::Get(itemId, row, column);
if (button->requestId || !IsServerMsgId(itemId.msg)) {
return;
}
const auto session = &message->history()->session();
const auto inputPeer = message->history()->peer->input;
const auto buttonId = button->buttonId;
const auto url = QString::fromUtf8(button->data);
button->requestId = session->api().request(MTPmessages_RequestUrlAuth(
inputPeer,
MTP_int(itemId.msg),
MTP_int(buttonId)
)).done([=](const MTPUrlAuthResult &result) {
const auto button = HistoryMessageMarkupButton::Get(
itemId,
row,
column);
if (!button) return;
button->requestId = 0;
result.match([&](const MTPDurlAuthResultAccepted &data) {
UrlClickHandler::Open(qs(data.vurl));
}, [&](const MTPDurlAuthResultDefault &data) {
HiddenUrlClickHandler::Open(url);
}, [&](const MTPDurlAuthResultRequest &data) {
Request(data, session->data().message(itemId), row, column);
});
}).fail([=](const RPCError &error) {
const auto button = HistoryMessageMarkupButton::Get(
itemId,
row,
column);
if (!button) return;
button->requestId = 0;
HiddenUrlClickHandler::Open(url);
}).send();
}
void UrlAuthBox::Request(
const MTPDurlAuthResultRequest &request,
not_null<const HistoryItem*> message,
int row,
int column) {
const auto itemId = message->fullId();
const auto button = HistoryMessageMarkupButton::Get(itemId, row, column);
if (button->requestId || !IsServerMsgId(itemId.msg)) {
return;
}
const auto session = &message->history()->session();
const auto inputPeer = message->history()->peer->input;
const auto buttonId = button->buttonId;
const auto url = QString::fromUtf8(button->data);
const auto bot = request.is_request_write_access()
? session->data().processUser(request.vbot).get()
: nullptr;
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto finishWithUrl = [=](const QString &url) {
if (*box) {
(*box)->closeBox();
}
UrlClickHandler::Open(url);
};
const auto callback = [=](Result result) {
if (result == Result::None) {
finishWithUrl(url);
} else if (const auto msg = session->data().message(itemId)) {
const auto allowWrite = (result == Result::AuthAndAllowWrite);
using Flag = MTPmessages_AcceptUrlAuth::Flag;
session->api().request(MTPmessages_AcceptUrlAuth(
MTP_flags(allowWrite ? Flag::f_write_allowed : Flag(0)),
inputPeer,
MTP_int(itemId.msg),
MTP_int(buttonId)
)).done([=](const MTPUrlAuthResult &result) {
const auto to = result.match(
[&](const MTPDurlAuthResultAccepted &data) {
return qs(data.vurl);
}, [&](const MTPDurlAuthResultDefault &data) {
return url;
}, [&](const MTPDurlAuthResultRequest &data) {
LOG(("API Error: "
"got urlAuthResultRequest after acceptUrlAuth."));
return url;
});
finishWithUrl(to);
}).fail([=](const RPCError &error) {
finishWithUrl(url);
}).send();
}
};
*box = Ui::show(
Box<UrlAuthBox>(url, qs(request.vdomain), bot, callback),
LayerOption::KeepOther);
}
UrlAuthBox::UrlAuthBox(
QWidget*,
const QString &url,
const QString &domain,
UserData *bot,
Fn<void(Result)> callback)
: _content(setupContent(url, domain, bot, std::move(callback))) {
}
void UrlAuthBox::prepare() {
setDimensionsToContent(st::boxWidth, _content);
addButton(langFactory(lng_open_link), [=] { _callback(); });
addButton(langFactory(lng_cancel), [=] { closeBox(); });
}
not_null<Ui::RpWidget*> UrlAuthBox::setupContent(
const QString &url,
const QString &domain,
UserData *bot,
Fn<void(Result)> callback) {
const auto result = Ui::CreateChild<Ui::VerticalLayout>(this);
result->add(
object_ptr<Ui::FlatLabel>(
result,
lng_url_auth_open_confirm(lt_link, url),
Ui::FlatLabel::InitType::Simple,
st::boxLabel),
st::boxPadding);
const auto addCheckbox = [&](const QString &text) {
const auto checkbox = result->add(
object_ptr<Ui::Checkbox>(
result,
QString(),
false,
st::urlAuthCheckbox),
style::margins(
st::boxPadding.left(),
st::boxPadding.bottom(),
st::boxPadding.right(),
st::boxPadding.bottom()));
checkbox->setAllowMultiline(true);
checkbox->setText(text, true);
return checkbox;
};
const auto auth = addCheckbox(
lng_url_auth_login_option(
lt_domain,
textcmdStartSemibold() + domain + textcmdStopSemibold(),
lt_user,
(textcmdStartSemibold()
+ App::peerName(Auth().user())
+ textcmdStopSemibold())));
const auto allow = bot
? addCheckbox(lng_url_auth_allow_messages(
lt_bot,
textcmdStartSemibold() + bot->firstName + textcmdStopSemibold()))
: nullptr;
if (allow) {
rpl::single(
auth->checked()
) | rpl::then(
auth->checkedChanges()
) | rpl::start_with_next([=](bool checked) {
allow->setDisabled(!checked);
}, auth->lifetime());
}
_callback = [=, callback = std::move(callback)]() {
const auto authed = auth->checked();
const auto allowed = (authed && allow && allow->checked());
const auto onstack = callback;
onstack(allowed
? Result::AuthAndAllowWrite
: authed
? Result::Auth
: Result::None);
};
return result;
}

View File

@ -0,0 +1,56 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "boxes/abstract_box.h"
class HistoryItem;
struct HistoryMessageMarkupButton;
class UrlAuthBox : public BoxContent {
public:
static void Activate(
not_null<const HistoryItem*> message,
int row,
int column);
protected:
void prepare() override;
private:
static void Request(
const MTPDurlAuthResultRequest &request,
not_null<const HistoryItem*> message,
int row,
int column);
enum class Result {
None,
Auth,
AuthAndAllowWrite,
};
public:
UrlAuthBox(
QWidget*,
const QString &url,
const QString &domain,
UserData *bot,
Fn<void(Result)> callback);
private:
not_null<Ui::RpWidget*> setupContent(
const QString &url,
const QString &domain,
UserData *bot,
Fn<void(Result)> callback);
Fn<void()> _callback;
not_null<Ui::RpWidget*> _content;
};

View File

@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "auth_session.h"
#include "boxes/confirm_box.h"
#include "boxes/url_auth_box.h"
#include "window/layer_widget.h"
#include "lang/lang_keys.h"
#include "base/observer.h"
@ -62,15 +63,7 @@ void activateBotCommand(
not_null<const HistoryItem*> msg,
int row,
int column) {
const HistoryMessageMarkupButton *button = nullptr;
if (auto markup = msg->Get<HistoryMessageReplyMarkup>()) {
if (row < markup->rows.size()) {
auto &buttonRow = markup->rows[row];
if (column < buttonRow.size()) {
button = &buttonRow[column];
}
}
}
const auto button = HistoryMessageMarkupButton::Get(msg->fullId(), row, column);
if (!button) return;
using ButtonType = HistoryMessageMarkupButton::Type;
@ -147,6 +140,10 @@ void activateBotCommand(
}
}
} break;
case ButtonType::Auth:
UrlAuthBox::Activate(msg, row, column);
break;
}
}

View File

@ -365,17 +365,7 @@ QString ReplyMarkupClickHandler::copyToClipboardContextItemText() const {
// Note: it is possible that we will point to the different button
// than the one was used when constructing the handler, but not a big deal.
const HistoryMessageMarkupButton *ReplyMarkupClickHandler::getButton() const {
if (const auto item = Auth().data().message(_itemId)) {
if (const auto markup = item->Get<HistoryMessageReplyMarkup>()) {
if (_row < markup->rows.size()) {
const auto &row = markup->rows[_row];
if (_column < row.size()) {
return &row[_column];
}
}
}
}
return nullptr;
return HistoryMessageMarkupButton::Get(_itemId, _row, _column);
}
void ReplyMarkupClickHandler::onClickImpl() const {
@ -730,66 +720,93 @@ void ReplyKeyboard::Style::paintButton(
button.text.drawElided(p, tx, rect.y() + _st->textTop + ((rect.height() - _st->height) / 2), tw, 1, style::al_top);
}
void HistoryMessageReplyMarkup::createFromButtonRows(const QVector<MTPKeyboardButtonRow> &v) {
if (v.isEmpty()) {
rows.clear();
HistoryMessageMarkupButton::HistoryMessageMarkupButton(
Type type,
const QString &text,
const QByteArray &data,
int32 buttonId)
: type(type)
, text(text)
, data(data)
, buttonId(buttonId) {
}
HistoryMessageMarkupButton *HistoryMessageMarkupButton::Get(
FullMsgId itemId,
int row,
int column) {
if (const auto item = Auth().data().message(itemId)) {
if (const auto markup = item->Get<HistoryMessageReplyMarkup>()) {
if (row < markup->rows.size()) {
auto &buttons = markup->rows[row];
if (column < buttons.size()) {
return &buttons[column];
}
}
}
}
return nullptr;
}
void HistoryMessageReplyMarkup::createFromButtonRows(
const QVector<MTPKeyboardButtonRow> &list) {
rows.clear();
if (list.isEmpty()) {
return;
}
rows.reserve(v.size());
for_const (auto &row, v) {
switch (row.type()) {
case mtpc_keyboardButtonRow: {
auto &r = row.c_keyboardButtonRow();
auto &b = r.vbuttons.v;
if (!b.isEmpty()) {
auto buttonRow = std::vector<Button>();
buttonRow.reserve(b.size());
for_const (auto &button, b) {
switch (button.type()) {
case mtpc_keyboardButton: {
buttonRow.push_back({ Button::Type::Default, qs(button.c_keyboardButton().vtext), QByteArray(), 0 });
} break;
case mtpc_keyboardButtonCallback: {
auto &buttonData = button.c_keyboardButtonCallback();
buttonRow.push_back({ Button::Type::Callback, qs(buttonData.vtext), qba(buttonData.vdata), 0 });
} break;
case mtpc_keyboardButtonRequestGeoLocation: {
buttonRow.push_back({ Button::Type::RequestLocation, qs(button.c_keyboardButtonRequestGeoLocation().vtext), QByteArray(), 0 });
} break;
case mtpc_keyboardButtonRequestPhone: {
buttonRow.push_back({ Button::Type::RequestPhone, qs(button.c_keyboardButtonRequestPhone().vtext), QByteArray(), 0 });
} break;
case mtpc_keyboardButtonUrl: {
auto &buttonData = button.c_keyboardButtonUrl();
buttonRow.push_back({ Button::Type::Url, qs(buttonData.vtext), qba(buttonData.vurl), 0 });
} break;
case mtpc_keyboardButtonSwitchInline: {
auto &buttonData = button.c_keyboardButtonSwitchInline();
auto buttonType = buttonData.is_same_peer() ? Button::Type::SwitchInlineSame : Button::Type::SwitchInline;
buttonRow.push_back({ buttonType, qs(buttonData.vtext), qba(buttonData.vquery), 0 });
if (buttonType == Button::Type::SwitchInline) {
// Optimization flag.
// Fast check on all new messages if there is a switch button to auto-click it.
flags |= MTPDreplyKeyboardMarkup_ClientFlag::f_has_switch_inline_button;
}
} break;
case mtpc_keyboardButtonGame: {
auto &buttonData = button.c_keyboardButtonGame();
buttonRow.push_back({ Button::Type::Game, qs(buttonData.vtext), QByteArray(), 0 });
} break;
case mtpc_keyboardButtonBuy: {
auto &buttonData = button.c_keyboardButtonBuy();
buttonRow.push_back({ Button::Type::Buy, qs(buttonData.vtext), QByteArray(), 0 });
rows.reserve(list.size());
for (const auto &row : list) {
row.match([&](const MTPDkeyboardButtonRow &data) {
auto row = std::vector<Button>();
row.reserve(data.vbuttons.v.size());
for (const auto &button : data.vbuttons.v) {
using Type = Button::Type;
button.match([&](const MTPDkeyboardButton &data) {
row.emplace_back(Type::Default, qs(data.vtext));
}, [&](const MTPDkeyboardButtonCallback &data) {
row.emplace_back(
Type::Callback,
qs(data.vtext),
qba(data.vdata));
}, [&](const MTPDkeyboardButtonRequestGeoLocation &data) {
row.emplace_back(Type::RequestLocation, qs(data.vtext));
}, [&](const MTPDkeyboardButtonRequestPhone &data) {
row.emplace_back(Type::RequestPhone, qs(data.vtext));
}, [&](const MTPDkeyboardButtonUrl &data) {
row.emplace_back(
Type::Url,
qs(data.vtext),
qba(data.vurl));
}, [&](const MTPDkeyboardButtonSwitchInline &data) {
const auto type = data.is_same_peer()
? Type::SwitchInlineSame
: Type::SwitchInline;
row.emplace_back(type, qs(data.vtext), qba(data.vquery));
if (type == Type::SwitchInline) {
// Optimization flag.
// Fast check on all new messages if there is a switch button to auto-click it.
flags |= MTPDreplyKeyboardMarkup_ClientFlag::f_has_switch_inline_button;
}
}
}
if (!buttonRow.empty()) {
rows.push_back(std::move(buttonRow));
}
}, [&](const MTPDkeyboardButtonGame &data) {
row.emplace_back(Type::Game, qs(data.vtext));
}, [&](const MTPDkeyboardButtonBuy &data) {
row.emplace_back(Type::Buy, qs(data.vtext));
}, [&](const MTPDkeyboardButtonUrlAuth &data) {
row.emplace_back(
Type::Auth,
qs(data.vtext),
qba(data.vurl),
data.vbutton_id.v);
}, [&](const MTPDinputKeyboardButtonUrlAuth &data) {
LOG(("API Error: inputKeyboardButtonUrlAuth received."));
// Should not get those for the users.
});
}
} break;
}
if (!row.empty()) {
rows.push_back(std::move(row));
}
});
}
}
@ -831,14 +848,19 @@ void HistoryMessageReplyMarkup::create(
inlineKeyboard = nullptr;
rows.clear();
for (const auto &row : markup.rows) {
auto buttonRow = std::vector<Button>();
buttonRow.reserve(row.size());
for (const auto &button : row) {
buttonRow.push_back({ button.type, button.text, button.data, 0 });
rows.reserve(markup.rows.size());
for (const auto &existing : markup.rows) {
auto row = std::vector<Button>();
row.reserve(existing.size());
for (const auto &button : existing) {
row.emplace_back(
button.type,
button.text,
button.data,
button.buttonId);
}
if (!buttonRow.empty()) {
rows.push_back(std::move(buttonRow));
if (!row.empty()) {
rows.push_back(std::move(row));
}
}
}

View File

@ -164,11 +164,25 @@ struct HistoryMessageMarkupButton {
SwitchInlineSame,
Game,
Buy,
Auth,
};
HistoryMessageMarkupButton(
Type type,
const QString &text,
const QByteArray &data = QByteArray(),
int32 buttonId = 0);
static HistoryMessageMarkupButton *Get(
FullMsgId itemId,
int row,
int column);
Type type;
QString text;
QByteArray data;
mutable mtpRequestId requestId;
int32 buttonId = 0;
mutable mtpRequestId requestId = 0;
};

View File

@ -3327,28 +3327,22 @@ void HistoryWidget::botCallbackDone(
BotCallbackInfo info,
const MTPmessages_BotCallbackAnswer &answer,
mtpRequestId req) {
auto item = Auth().data().message(info.msgId);
if (item) {
if (const auto markup = item->Get<HistoryMessageReplyMarkup>()) {
if (info.row < markup->rows.size()
&& info.col < markup->rows[info.row].size()) {
if (markup->rows[info.row][info.col].requestId == req) {
markup->rows[info.row][info.col].requestId = 0;
Auth().data().requestItemRepaint(item);
}
}
const auto item = Auth().data().message(info.msgId);
if (const auto button = HistoryMessageMarkupButton::Get(info.msgId, info.row, info.col)) {
if (button->requestId == req) {
button->requestId = 0;
Auth().data().requestItemRepaint(item);
}
}
if (answer.type() == mtpc_messages_botCallbackAnswer) {
auto &answerData = answer.c_messages_botCallbackAnswer();
if (answerData.has_message()) {
if (answerData.is_alert()) {
Ui::show(Box<InformBox>(qs(answerData.vmessage)));
answer.match([&](const MTPDmessages_botCallbackAnswer &data) {
if (data.has_message()) {
if (data.is_alert()) {
Ui::show(Box<InformBox>(qs(data.vmessage)));
} else {
Ui::Toast::Show(qs(answerData.vmessage));
Ui::Toast::Show(qs(data.vmessage));
}
} else if (answerData.has_url()) {
auto url = qs(answerData.vurl);
} else if (data.has_url()) {
auto url = qs(data.vurl);
if (info.game) {
url = AppendShareGameScoreUrl(url, info.msgId);
BotGameUrlClickHandler(info.bot, url).onClick({});
@ -3359,7 +3353,7 @@ void HistoryWidget::botCallbackDone(
UrlClickHandler(url).onClick({});
}
}
}
});
}
bool HistoryWidget::botCallbackFail(
@ -3367,15 +3361,10 @@ bool HistoryWidget::botCallbackFail(
const RPCError &error,
mtpRequestId req) {
// show error?
if (const auto item = Auth().data().message(info.msgId)) {
if (const auto markup = item->Get<HistoryMessageReplyMarkup>()) {
if (info.row < markup->rows.size()
&& info.col < markup->rows[info.row].size()) {
if (markup->rows[info.row][info.col].requestId == req) {
markup->rows[info.row][info.col].requestId = 0;
Auth().data().requestItemRepaint(item);
}
}
if (const auto button = HistoryMessageMarkupButton::Get(info.msgId, info.row, info.col)) {
if (button->requestId == req) {
button->requestId = 0;
Auth().data().requestItemRepaint(Auth().data().message(info.msgId));
}
}
return true;

View File

@ -84,16 +84,16 @@ void KeyboardStyle::paintButtonIcon(
const QRect &rect,
int outerWidth,
HistoryMessageMarkupButton::Type type) const {
using Button = HistoryMessageMarkupButton;
auto getIcon = [](Button::Type type) -> const style::icon* {
using Type = HistoryMessageMarkupButton::Type;
const auto getIcon = [](Type type) -> const style::icon* {
switch (type) {
case Button::Type::Url: return &st::msgBotKbUrlIcon;
case Button::Type::SwitchInlineSame:
case Button::Type::SwitchInline: return &st::msgBotKbSwitchPmIcon;
case Type::Url: return &st::msgBotKbUrlIcon;
case Type::SwitchInlineSame:
case Type::SwitchInline: return &st::msgBotKbSwitchPmIcon;
}
return nullptr;
};
if (auto icon = getIcon(type)) {
if (const auto icon = getIcon(type)) {
icon->paint(p, rect.x() + rect.width() - icon->width() - st::msgBotKbIconPadding, rect.y() + st::msgBotKbIconPadding, outerWidth);
}
}
@ -105,14 +105,15 @@ void KeyboardStyle::paintButtonLoading(Painter &p, const QRect &rect) const {
int KeyboardStyle::minButtonWidth(
HistoryMessageMarkupButton::Type type) const {
using Button = HistoryMessageMarkupButton;
using Type = HistoryMessageMarkupButton::Type;
int result = 2 * buttonPadding(), iconWidth = 0;
switch (type) {
case Button::Type::Url: iconWidth = st::msgBotKbUrlIcon.width(); break;
case Button::Type::SwitchInlineSame:
case Button::Type::SwitchInline: iconWidth = st::msgBotKbSwitchPmIcon.width(); break;
case Button::Type::Callback:
case Button::Type::Game: iconWidth = st::historySendingInvertedIcon.width(); break;
case Type::Url: iconWidth = st::msgBotKbUrlIcon.width(); break;
case Type::SwitchInlineSame:
case Type::SwitchInline: iconWidth = st::msgBotKbSwitchPmIcon.width(); break;
case Type::Callback:
case Type::Game:
case Type::Auth: iconWidth = st::historySendingInvertedIcon.width(); break;
}
if (iconWidth > 0) {
result = std::max(result, 2 * iconWidth + 4 * int(st::msgBotKbIconPadding));

View File

@ -20,6 +20,13 @@ TextParseOptions _checkboxOptions = {
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _checkboxRichOptions = {
TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
} // namespace
AbstractCheckView::AbstractCheckView(int duration, bool checked, Fn<void()> updateCallback)
@ -401,7 +408,7 @@ Checkbox::Checkbox(
: RippleButton(parent, st.ripple)
, _st(st)
, _check(std::move(check))
, _text(_st.style, text, _checkboxOptions) {
, _text(_st.style, text, _checkboxOptions, (_st.width > 0) ? _st.width : QFIXED_MAX) {
_check->setUpdateCallback([=] { updateCheck(); });
resizeToText();
setCursor(style::cur_pointer);
@ -423,8 +430,8 @@ QRect Checkbox::checkRect() const {
}, size);
}
void Checkbox::setText(const QString &text) {
_text.setText(_st.style, text, _checkboxOptions);
void Checkbox::setText(const QString &text, bool rich) {
_text.setText(_st.style, text, rich ? _checkboxRichOptions : _checkboxOptions);
resizeToText();
update();
}
@ -436,6 +443,11 @@ void Checkbox::setCheckAlignment(style::align alignment) {
}
}
void Checkbox::setAllowMultiline(bool allow) {
_allowMultiline = allow;
update();
}
bool Checkbox::checked() const {
return _check->checked();
}
@ -519,19 +531,37 @@ void Checkbox::paintEvent(QPaintEvent *e) {
+ _st.textPosition.x();
auto textTop = _st.margin.top() + _st.textPosition.y();
if (_checkAlignment & Qt::AlignLeft) {
_text.drawLeftElided(
p,
textSkip,
textTop,
availableTextWidth,
width());
if (_allowMultiline) {
_text.drawLeft(
p,
textSkip,
textTop,
availableTextWidth,
width());
} else {
_text.drawLeftElided(
p,
textSkip,
textTop,
availableTextWidth,
width());
}
} else if (_checkAlignment & Qt::AlignRight) {
_text.drawRightElided(
p,
textSkip,
textTop,
availableTextWidth,
width());
if (_allowMultiline) {
_text.drawRight(
p,
textSkip,
textTop,
availableTextWidth,
width());
} else {
_text.drawRightElided(
p,
textSkip,
textTop,
availableTextWidth,
width());
}
} else {
_text.drawLeft(
p,
@ -582,13 +612,18 @@ void Checkbox::handlePress() {
int Checkbox::resizeGetHeight(int newWidth) {
const auto result = _check->getSize().height();
if (!(_checkAlignment & Qt::AlignHCenter)) {
const auto centered = ((_checkAlignment & Qt::AlignHCenter) != 0);
if (!centered && !_allowMultiline) {
return result;
}
const auto textBottom = _st.margin.top()
+ _st.textPosition.y()
+ _text.countHeight(
newWidth - _st.margin.left() - _st.margin.right());
const auto leftSkip = _st.checkPosition.x()
+ checkRect().width()
+ _st.textPosition.x();
const auto availableTextWidth = centered
? (newWidth - _st.margin.left() - _st.margin.right())
: qMax(width() - leftSkip, 1);
const auto textBottom = _st.textPosition.y()
+ _text.countHeight(availableTextWidth);
return std::max(result, textBottom);
}

View File

@ -150,8 +150,9 @@ public:
const style::Checkbox &st,
std::unique_ptr<AbstractCheckView> check);
void setText(const QString &text);
void setText(const QString &text, bool rich = false);
void setCheckAlignment(style::align alignment);
void setAllowMultiline(bool allow);
bool checked() const;
rpl::producer<bool> checkedChanges() const;
@ -198,6 +199,7 @@ private:
Text _text;
style::align _checkAlignment = style::al_left;
bool _allowMultiline = false;
};

View File

@ -78,6 +78,8 @@
<(src_loc)/boxes/sticker_set_box.h
<(src_loc)/boxes/stickers_box.cpp
<(src_loc)/boxes/stickers_box.h
<(src_loc)/boxes/url_auth_box.cpp
<(src_loc)/boxes/url_auth_box.h
<(src_loc)/boxes/username_box.cpp
<(src_loc)/boxes/username_box.h
<(src_loc)/calls/calls_box_controller.cpp