mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-04-01 23:00:58 +00:00
Create and edit proxy box.
This commit is contained in:
parent
a7c77682d7
commit
9935a36c3d
@ -424,6 +424,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_proxy_settings" = "Proxy settings";
|
||||
"lng_proxy_use" = "Use proxy";
|
||||
"lng_proxy_add" = "Add proxy";
|
||||
"lng_proxy_share" = "Share";
|
||||
"lng_proxy_online" = "online";
|
||||
"lng_proxy_checking" = "checking";
|
||||
"lng_proxy_connecting" = "connecting";
|
||||
@ -431,10 +432,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_proxy_unavailable" = "unavailable";
|
||||
"lng_proxy_edit" = "Edit proxy";
|
||||
"lng_proxy_undo_delete" = "Undo";
|
||||
"lng_proxy_type_http" = "HTTP";
|
||||
"lng_proxy_type_socks5" = "SOCKS5";
|
||||
"lng_proxy_type_mtproto" = "MTPROTO";
|
||||
"lng_proxy_address_label" = "Socket address";
|
||||
"lng_proxy_credentials_optional" = "Credentials (optional)";
|
||||
"lng_proxy_credentials" = "Credentials";
|
||||
"lng_proxy_edit_share" = "Share";
|
||||
"lng_proxy_description" = "Your saved proxy list will be here.";
|
||||
|
||||
"lng_settings_blocked_users" = "Blocked users";
|
||||
"lng_settings_last_seen_privacy" = "Last seen privacy";
|
||||
|
@ -747,3 +747,20 @@ proxyRowEdit: IconButton(defaultIconButton) {
|
||||
color: windowBgOver;
|
||||
}
|
||||
}
|
||||
|
||||
proxyEditTitle: FlatLabel(defaultFlatLabel) {
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: autoDownloadTitleFont;
|
||||
}
|
||||
textFg: boxTitleFg;
|
||||
}
|
||||
proxyEditTitlePadding: margins(22px, 16px, 22px, 0px);
|
||||
proxyEditTypePadding: margins(22px, 4px, 22px, 8px);
|
||||
proxyEditInputPadding: margins(22px, 0px, 22px, 0px);
|
||||
proxyEditSkip: 16px;
|
||||
|
||||
proxyEmptyListLabel: FlatLabel(defaultFlatLabel) {
|
||||
align: align(top);
|
||||
textFg: windowSubTextFg;
|
||||
}
|
||||
proxyEmptyListPadding: margins(22px, 48px, 22px, 0px);
|
||||
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "auth_session.h"
|
||||
@ -19,9 +20,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/text_options.h"
|
||||
#include "history/history_location_manager.h"
|
||||
#include "application.h"
|
||||
@ -75,17 +79,20 @@ protected:
|
||||
|
||||
private:
|
||||
void setupContent();
|
||||
void createNoRowsLabel();
|
||||
void addNewProxy();
|
||||
void applyView(View &&view);
|
||||
void setupButtons(int id, not_null<ProxyRow*> button);
|
||||
int rowHeight() const;
|
||||
|
||||
not_null<ProxiesBoxController*> _controller;
|
||||
object_ptr<Ui::PaddingWrap<Ui::Checkbox>> _useProxy;
|
||||
object_ptr<Ui::PaddingWrap<Ui::Checkbox>> _tryIPv6;
|
||||
base::unique_qptr<Ui::RpWidget> _noRows;
|
||||
object_ptr<Ui::VerticalLayout> _initialWrap;
|
||||
QPointer<Ui::VerticalLayout> _wrap;
|
||||
|
||||
base::flat_map<int, QPointer<ProxyRow>> _rows;
|
||||
base::flat_map<int, base::unique_qptr<ProxyRow>> _rows;
|
||||
|
||||
};
|
||||
|
||||
@ -100,8 +107,37 @@ protected:
|
||||
void prepare() override;
|
||||
|
||||
private:
|
||||
using Type = ProxyData::Type;
|
||||
|
||||
void refreshButtons();
|
||||
ProxyData collectData();
|
||||
void save();
|
||||
void share();
|
||||
void setupControls(const ProxyData &data);
|
||||
void setupTypes();
|
||||
void setupSocketAddress(const ProxyData &data);
|
||||
void setupCredentials(const ProxyData &data);
|
||||
void setupMtprotoCredentials(const ProxyData &data);
|
||||
|
||||
void addLabel(
|
||||
not_null<Ui::VerticalLayout*> parent,
|
||||
const QString &text) const;
|
||||
|
||||
base::lambda<void(ProxyData)> _callback;
|
||||
|
||||
object_ptr<Ui::VerticalLayout> _content;
|
||||
|
||||
std::shared_ptr<Ui::RadioenumGroup<Type>> _type;
|
||||
|
||||
QPointer<Ui::InputField> _host;
|
||||
QPointer<Ui::PortInput> _port;
|
||||
QPointer<Ui::InputField> _user;
|
||||
QPointer<Ui::PasswordInput> _password;
|
||||
QPointer<Ui::HexInput> _secret;
|
||||
|
||||
QPointer<Ui::SlideWrap<Ui::VerticalLayout>> _credentials;
|
||||
QPointer<Ui::SlideWrap<Ui::VerticalLayout>> _mtprotoCredentials;
|
||||
|
||||
};
|
||||
|
||||
ProxyRow::ProxyRow(QWidget *parent, View &&view)
|
||||
@ -283,9 +319,13 @@ void ProxiesBox::setupContent() {
|
||||
_useProxy->moveToLeft(0, 0);
|
||||
subscribe(_useProxy->entity()->checkedChanged, [=](bool checked) {
|
||||
if (!_controller->setProxyEnabled(checked)) {
|
||||
_useProxy->entity()->setChecked(false);
|
||||
addNewProxy();
|
||||
}
|
||||
});
|
||||
subscribe(_tryIPv6->entity()->checkedChanged, [=](bool checked) {
|
||||
_controller->setTryIPv6(checked);
|
||||
});
|
||||
subscribe(Global::RefConnectionTypeChanged(), [=] {
|
||||
_useProxy->entity()->setChecked(Global::UseProxy());
|
||||
});
|
||||
@ -306,12 +346,16 @@ void ProxiesBox::setupContent() {
|
||||
inner,
|
||||
st::proxyRowPadding.bottom()));
|
||||
|
||||
if (_rows.empty()) {
|
||||
createNoRowsLabel();
|
||||
}
|
||||
|
||||
inner->resizeToWidth(st::boxWideWidth);
|
||||
|
||||
inner->heightValue(
|
||||
) | rpl::map([=](int height) {
|
||||
return std::min(
|
||||
topSkip + height + bottomSkip,
|
||||
topSkip + std::max(height, 3 * rowHeight()) + bottomSkip,
|
||||
st::boxMaxListHeight);
|
||||
}) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](int height) {
|
||||
@ -324,6 +368,14 @@ void ProxiesBox::setupContent() {
|
||||
}, _tryIPv6->lifetime());
|
||||
}
|
||||
|
||||
int ProxiesBox::rowHeight() const {
|
||||
return st::proxyRowPadding.top()
|
||||
+ st::semiboldFont->height
|
||||
+ st::proxyRowSkip
|
||||
+ st::normalFont->height
|
||||
+ st::proxyRowPadding.bottom();
|
||||
}
|
||||
|
||||
void ProxiesBox::addNewProxy() {
|
||||
Ui::show(_controller->addNewItemBox(), LayerOption::KeepOther);
|
||||
}
|
||||
@ -335,17 +387,44 @@ void ProxiesBox::applyView(View &&view) {
|
||||
const auto wrap = _wrap
|
||||
? _wrap.data()
|
||||
: _initialWrap.data();
|
||||
const auto [i, ok] = _rows.emplace(id, wrap->insert(
|
||||
const auto [i, ok] = _rows.emplace(id, nullptr);
|
||||
i->second.reset(wrap->insert(
|
||||
0,
|
||||
object_ptr<ProxyRow>(
|
||||
wrap,
|
||||
std::move(view))));
|
||||
setupButtons(id, i->second);
|
||||
setupButtons(id, i->second.get());
|
||||
_noRows.reset();
|
||||
} else if (view.host.isEmpty()) {
|
||||
_rows.erase(i);
|
||||
} else {
|
||||
i->second->updateFields(std::move(view));
|
||||
}
|
||||
}
|
||||
|
||||
void ProxiesBox::createNoRowsLabel() {
|
||||
_noRows.reset(_wrap->add(
|
||||
object_ptr<Ui::FixedHeightWidget>(
|
||||
_wrap,
|
||||
rowHeight()),
|
||||
st::proxyEmptyListPadding));
|
||||
_noRows->resize(
|
||||
(st::boxWideWidth
|
||||
- st::proxyEmptyListPadding.left()
|
||||
- st::proxyEmptyListPadding.right()),
|
||||
_noRows->height());
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
_noRows.get(),
|
||||
lang(lng_proxy_description),
|
||||
Ui::FlatLabel::InitType::Simple,
|
||||
st::proxyEmptyListLabel);
|
||||
_noRows->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
label->resizeToWidth(width);
|
||||
label->moveToLeft(0, 0);
|
||||
});
|
||||
}
|
||||
|
||||
void ProxiesBox::setupButtons(int id, not_null<ProxyRow*> button) {
|
||||
button->deleteClicks(
|
||||
) | rpl::start_with_next([=] {
|
||||
@ -372,11 +451,233 @@ ProxyBox::ProxyBox(
|
||||
QWidget*,
|
||||
const ProxyData &data,
|
||||
base::lambda<void(ProxyData)> callback)
|
||||
: _callback(std::move(callback)) {
|
||||
: _callback(std::move(callback))
|
||||
, _content(this) {
|
||||
setupControls(data);
|
||||
}
|
||||
|
||||
void ProxyBox::prepare() {
|
||||
setTitle(langFactory(lng_proxy_edit));
|
||||
|
||||
refreshButtons();
|
||||
|
||||
_content->heightValue(
|
||||
) | rpl::start_with_next([=](int height) {
|
||||
setDimensions(st::boxWidth, height);
|
||||
}, _content->lifetime());
|
||||
}
|
||||
|
||||
void ProxyBox::refreshButtons() {
|
||||
clearButtons();
|
||||
addButton(langFactory(lng_settings_save), [=] { save(); });
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
|
||||
const auto type = _type->value();
|
||||
if (type == Type::Socks5 || type == Type::Mtproto) {
|
||||
addLeftButton(langFactory(lng_proxy_share), [=] { share(); });
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyBox::save() {
|
||||
if (const auto data = collectData()) {
|
||||
_callback(data);
|
||||
closeBox();
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyBox::share() {
|
||||
if (const auto data = collectData()) {
|
||||
if (data.type == Type::Http) {
|
||||
return;
|
||||
}
|
||||
const auto link = qsl("https://t.me/")
|
||||
+ (data.type == Type::Socks5 ? "socks" : "proxy")
|
||||
+ "?server=" + data.host + "&port=" + QString::number(data.port)
|
||||
+ ((data.type == Type::Socks5 && !data.user.isEmpty())
|
||||
? "&user=" + qthelp::url_encode(data.user) : "")
|
||||
+ ((data.type == Type::Socks5 && !data.password.isEmpty())
|
||||
? "&pass=" + qthelp::url_encode(data.password) : "")
|
||||
+ ((data.type == Type::Mtproto && !data.password.isEmpty())
|
||||
? "&secret=" + data.password : "");
|
||||
Application::clipboard()->setText(link);
|
||||
Ui::Toast::Show(lang(lng_username_copied));
|
||||
}
|
||||
}
|
||||
|
||||
ProxyData ProxyBox::collectData() {
|
||||
auto result = ProxyData();
|
||||
result.type = _type->value();
|
||||
result.host = _host->getLastText().trimmed();
|
||||
result.port = _port->getLastText().trimmed().toInt();
|
||||
result.user = (result.type == Type::Mtproto)
|
||||
? QString()
|
||||
: _user->getLastText();
|
||||
result.password = (result.type == Type::Mtproto)
|
||||
? _secret->getLastText()
|
||||
: _password->getLastText();
|
||||
if (result.host.isEmpty()) {
|
||||
_host->showError();
|
||||
} else if (!result.port) {
|
||||
_port->showError();
|
||||
} else if ((result.type == Type::Http || result.type == Type::Socks5)
|
||||
&& !result.password.isEmpty() && result.user.isEmpty()) {
|
||||
_user->showError();
|
||||
} else if (result.type == Type::Mtproto
|
||||
&& result.password.size() != 32) {
|
||||
_secret->showError();
|
||||
} else if (!result) {
|
||||
_host->showError();
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
return ProxyData();
|
||||
}
|
||||
|
||||
void ProxyBox::setupTypes() {
|
||||
const auto types = std::map<Type, QString>{
|
||||
{ Type::Http, "HTTP" },
|
||||
{ Type::Socks5, "SOCKS5" },
|
||||
{ Type::Mtproto, "MTPROTO" },
|
||||
};
|
||||
for (const auto [type, label] : types) {
|
||||
_content->add(
|
||||
object_ptr<Ui::Radioenum<Type>>(
|
||||
_content,
|
||||
_type,
|
||||
type,
|
||||
label),
|
||||
st::proxyEditTypePadding);
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyBox::setupSocketAddress(const ProxyData &data) {
|
||||
addLabel(_content, lang(lng_proxy_address_label));
|
||||
const auto address = _content->add(
|
||||
object_ptr<Ui::FixedHeightWidget>(
|
||||
_content,
|
||||
st::connectionHostInputField.heightMin),
|
||||
st::proxyEditInputPadding);
|
||||
_host = Ui::CreateChild<Ui::InputField>(
|
||||
address,
|
||||
st::connectionHostInputField,
|
||||
langFactory(lng_connection_host_ph),
|
||||
data.host);
|
||||
_port = Ui::CreateChild<Ui::PortInput>(
|
||||
address,
|
||||
st::connectionPortInputField,
|
||||
langFactory(lng_connection_port_ph),
|
||||
data.port ? QString::number(data.port) : QString());
|
||||
address->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
_port->moveToRight(0, 0);
|
||||
_host->resize(
|
||||
width - _port->width() - st::proxyEditSkip,
|
||||
_host->height());
|
||||
_host->moveToLeft(0, 0);
|
||||
}, address->lifetime());
|
||||
}
|
||||
|
||||
void ProxyBox::setupCredentials(const ProxyData &data) {
|
||||
_credentials = _content->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
_content,
|
||||
object_ptr<Ui::VerticalLayout>(_content)));
|
||||
const auto credentials = _credentials->entity();
|
||||
addLabel(credentials, lang(lng_proxy_credentials_optional));
|
||||
_user = credentials->add(
|
||||
object_ptr<Ui::InputField>(
|
||||
credentials,
|
||||
st::connectionUserInputField,
|
||||
langFactory(lng_connection_user_ph),
|
||||
data.user),
|
||||
st::proxyEditInputPadding);
|
||||
|
||||
auto passwordWrap = object_ptr<Ui::RpWidget>(credentials);
|
||||
_password = Ui::CreateChild<Ui::PasswordInput>(
|
||||
passwordWrap.data(),
|
||||
st::connectionPasswordInputField,
|
||||
langFactory(lng_connection_password_ph),
|
||||
(data.type == Type::Mtproto) ? QString() : data.password);
|
||||
_password->move(0, 0);
|
||||
_password->heightValue(
|
||||
) | rpl::start_with_next([=, wrap = passwordWrap.data()](int height) {
|
||||
wrap->resize(wrap->width(), height);
|
||||
}, _password->lifetime());
|
||||
passwordWrap->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
_password->resize(width, _password->height());
|
||||
}, _password->lifetime());
|
||||
credentials->add(std::move(passwordWrap), st::proxyEditInputPadding);
|
||||
}
|
||||
|
||||
void ProxyBox::setupMtprotoCredentials(const ProxyData &data) {
|
||||
_mtprotoCredentials = _content->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
_content,
|
||||
object_ptr<Ui::VerticalLayout>(_content)));
|
||||
const auto mtproto = _mtprotoCredentials->entity();
|
||||
addLabel(mtproto, lang(lng_proxy_credentials));
|
||||
|
||||
auto secretWrap = object_ptr<Ui::RpWidget>(mtproto);
|
||||
_secret = Ui::CreateChild<Ui::HexInput>(
|
||||
secretWrap.data(),
|
||||
st::connectionUserInputField,
|
||||
langFactory(lng_connection_proxy_secret_ph),
|
||||
(data.type == Type::Mtproto) ? data.password : QString());
|
||||
_secret->setMaxLength(32);
|
||||
_secret->move(0, 0);
|
||||
_secret->heightValue(
|
||||
) | rpl::start_with_next([=, wrap = secretWrap.data()](int height) {
|
||||
wrap->resize(wrap->width(), height);
|
||||
}, _secret->lifetime());
|
||||
secretWrap->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
_secret->resize(width, _secret->height());
|
||||
}, _secret->lifetime());
|
||||
mtproto->add(std::move(secretWrap), st::proxyEditInputPadding);
|
||||
}
|
||||
|
||||
void ProxyBox::setupControls(const ProxyData &data) {
|
||||
_type = std::make_shared<Ui::RadioenumGroup<Type>>(
|
||||
(data.type == Type::None
|
||||
? Type::Socks5
|
||||
: data.type));
|
||||
_content.create(this);
|
||||
_content->resizeToWidth(st::boxWidth);
|
||||
_content->moveToLeft(0, 0);
|
||||
|
||||
setupTypes();
|
||||
setupSocketAddress(data);
|
||||
setupCredentials(data);
|
||||
setupMtprotoCredentials(data);
|
||||
|
||||
_content->resizeToWidth(st::boxWidth);
|
||||
|
||||
const auto handleType = [=](Type type) {
|
||||
_credentials->toggle(
|
||||
type == Type::Http || type == Type::Socks5,
|
||||
anim::type::instant);
|
||||
_mtprotoCredentials->toggle(
|
||||
type == Type::Mtproto,
|
||||
anim::type::instant);
|
||||
};
|
||||
_type->setChangedCallback([=](Type type) {
|
||||
handleType(type);
|
||||
refreshButtons();
|
||||
});
|
||||
handleType(_type->value());
|
||||
}
|
||||
|
||||
void ProxyBox::addLabel(
|
||||
not_null<Ui::VerticalLayout*> parent,
|
||||
const QString &text) const {
|
||||
parent->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
parent,
|
||||
text,
|
||||
Ui::FlatLabel::InitType::Simple,
|
||||
st::proxyEditTitle),
|
||||
st::proxyEditTitlePadding);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -844,22 +1145,46 @@ object_ptr<BoxContent> ProxiesBoxController::editItemBox(int id) {
|
||||
result,
|
||||
[](const Item &item) { return item.data; });
|
||||
if (j != end(_list) && j != i) {
|
||||
_views.fire({ i->id });
|
||||
_list.erase(i);
|
||||
if (j->deleted) {
|
||||
restoreItem(j->id);
|
||||
}
|
||||
replaceItemWith(i, j);
|
||||
} else {
|
||||
i->data = result;
|
||||
if (i->deleted) {
|
||||
restoreItem(i->id);
|
||||
} else {
|
||||
updateView(*i);
|
||||
}
|
||||
replaceItemValue(i, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ProxiesBoxController::replaceItemWith(
|
||||
std::vector<Item>::iterator which,
|
||||
std::vector<Item>::iterator with) {
|
||||
auto &proxies = Global::RefProxiesList();
|
||||
proxies.erase(ranges::remove(proxies, which->data), end(proxies));
|
||||
|
||||
_views.fire({ which->id });
|
||||
_list.erase(which);
|
||||
|
||||
if (with->deleted) {
|
||||
restoreItem(with->id);
|
||||
}
|
||||
applyItem(with->id);
|
||||
saveDelayed();
|
||||
}
|
||||
|
||||
void ProxiesBoxController::replaceItemValue(
|
||||
std::vector<Item>::iterator which,
|
||||
const ProxyData &proxy) {
|
||||
if (which->deleted) {
|
||||
restoreItem(which->id);
|
||||
}
|
||||
|
||||
auto &proxies = Global::RefProxiesList();
|
||||
const auto i = ranges::find(proxies, which->data);
|
||||
Assert(i != end(proxies));
|
||||
*i = proxy;
|
||||
which->data = proxy;
|
||||
|
||||
applyItem(which->id);
|
||||
saveDelayed();
|
||||
}
|
||||
|
||||
object_ptr<BoxContent> ProxiesBoxController::addNewItemBox() {
|
||||
return Box<ProxyBox>(ProxyData(), [=](const ProxyData &result) {
|
||||
auto j = ranges::find(
|
||||
@ -870,13 +1195,21 @@ object_ptr<BoxContent> ProxiesBoxController::addNewItemBox() {
|
||||
if (j->deleted) {
|
||||
restoreItem(j->id);
|
||||
}
|
||||
applyItem(j->id);
|
||||
} else {
|
||||
_list.push_back({ ++_idCounter, result });
|
||||
updateView(_list.back());
|
||||
addNewItem(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ProxiesBoxController::addNewItem(const ProxyData &proxy) {
|
||||
auto &proxies = Global::RefProxiesList();
|
||||
proxies.push_back(proxy);
|
||||
|
||||
_list.push_back({ ++_idCounter, proxy });
|
||||
applyItem(_list.back().id);
|
||||
}
|
||||
|
||||
bool ProxiesBoxController::setProxyEnabled(bool enabled) {
|
||||
if (enabled) {
|
||||
if (Global::ProxiesList().empty()) {
|
||||
@ -894,6 +1227,14 @@ bool ProxiesBoxController::setProxyEnabled(bool enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProxiesBoxController::setTryIPv6(bool enabled) {
|
||||
if (Global::TryIPv6() == enabled) {
|
||||
return;
|
||||
}
|
||||
Global::SetTryIPv6(enabled);
|
||||
applyChanges();
|
||||
}
|
||||
|
||||
void ProxiesBoxController::applyChanges() {
|
||||
Sandbox::refreshGlobalProxy();
|
||||
Global::RefConnectionTypeChanged().notify();
|
||||
|
@ -124,6 +124,7 @@ public:
|
||||
object_ptr<BoxContent> editItemBox(int id);
|
||||
object_ptr<BoxContent> addNewItemBox();
|
||||
bool setProxyEnabled(bool enabled);
|
||||
void setTryIPv6(bool enabled);
|
||||
|
||||
rpl::producer<ItemView> views() const;
|
||||
|
||||
@ -143,6 +144,14 @@ private:
|
||||
void applyChanges();
|
||||
void saveDelayed();
|
||||
|
||||
void replaceItemWith(
|
||||
std::vector<Item>::iterator which,
|
||||
std::vector<Item>::iterator with);
|
||||
void replaceItemValue(
|
||||
std::vector<Item>::iterator which,
|
||||
const ProxyData &proxy);
|
||||
void addNewItem(const ProxyData &proxy);
|
||||
|
||||
int _idCounter = 0;
|
||||
int _selected = -1;
|
||||
std::vector<Item> _list;
|
||||
|
@ -3944,6 +3944,33 @@ void PortInput::correctValue(
|
||||
setCorrectedText(now, nowCursor, newText, newPos);
|
||||
}
|
||||
|
||||
HexInput::HexInput(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory, const QString &val) : MaskedInputField(parent, st, std::move(placeholderFactory), val) {
|
||||
if (!QRegularExpression("^[a-fA-F0-9]+$").match(val).hasMatch()) {
|
||||
setText(QString());
|
||||
}
|
||||
}
|
||||
|
||||
void HexInput::correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) {
|
||||
QString newText;
|
||||
newText.reserve(now.size());
|
||||
auto newPos = nowCursor;
|
||||
for (auto i = 0, l = now.size(); i < l; ++i) {
|
||||
const auto ch = now[i];
|
||||
if ((ch >= '0' && ch <= '9')
|
||||
|| (ch >= 'a' && ch <= 'f')
|
||||
|| (ch >= 'A' && ch <= 'F')) {
|
||||
newText.append(ch);
|
||||
} else if (i < nowCursor) {
|
||||
--newPos;
|
||||
}
|
||||
}
|
||||
setCorrectedText(now, nowCursor, newText, newPos);
|
||||
}
|
||||
|
||||
UsernameInput::UsernameInput(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory, const QString &val, bool isLink) : MaskedInputField(parent, st, std::move(placeholderFactory), val) {
|
||||
setLinkPlaceholder(isLink ? Messenger::Instance().createInternalLink(QString()) : QString());
|
||||
}
|
||||
|
@ -906,6 +906,19 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
class HexInput : public MaskedInputField {
|
||||
public:
|
||||
HexInput(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory, const QString &val);
|
||||
|
||||
protected:
|
||||
void correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) override;
|
||||
|
||||
};
|
||||
|
||||
class UsernameInput : public MaskedInputField {
|
||||
public:
|
||||
UsernameInput(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory, const QString &val, bool isLink);
|
||||
|
@ -164,8 +164,8 @@ void VerticalLayout::removeChild(RpWidget *child) {
|
||||
auto prev = it - 1;
|
||||
return prev->widget->bottomNoMargins() + prev->margin.bottom();
|
||||
}() - margins.top();
|
||||
for (auto next = it + 1; it != end; ++it) {
|
||||
auto &row = *it;
|
||||
for (auto next = it + 1; next != end; ++next) {
|
||||
auto &row = *next;
|
||||
auto margin = row.margin;
|
||||
auto widget = row.widget.data();
|
||||
widget->moveToLeft(
|
||||
@ -175,6 +175,7 @@ void VerticalLayout::removeChild(RpWidget *child) {
|
||||
+ widget->heightNoMargins()
|
||||
+ margin.bottom();
|
||||
}
|
||||
it->widget = nullptr;
|
||||
_rows.erase(it);
|
||||
|
||||
resize(width(), margins.top() + top + margins.bottom());
|
||||
|
Loading…
Reference in New Issue
Block a user