Apply, delete and restore proxies in the box.

This commit is contained in:
John Preston 2018-04-28 18:06:59 +04:00
parent 8bbea976ea
commit a7c77682d7
11 changed files with 318 additions and 60 deletions

View File

@ -19,12 +19,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "mainwindow.h"
QPointer<Ui::RoundButton> BoxContent::addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback) {
return addButton(std::move(textFactory), std::move(clickCallback), st::defaultBoxButton);
QPointer<Ui::RoundButton> BoxContent::addButton(
base::lambda<QString()> textFactory,
base::lambda<void()> clickCallback) {
return addButton(
std::move(textFactory),
std::move(clickCallback),
st::defaultBoxButton);
}
QPointer<Ui::RoundButton> BoxContent::addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback) {
return getDelegate()->addLeftButton(std::move(textFactory), std::move(clickCallback), st::defaultBoxButton);
QPointer<Ui::RoundButton> BoxContent::addLeftButton(
base::lambda<QString()> textFactory,
base::lambda<void()> clickCallback) {
return getDelegate()->addLeftButton(
std::move(textFactory),
std::move(clickCallback),
st::defaultBoxButton);
}
void BoxContent::setInner(object_ptr<TWidget> inner) {
@ -117,7 +127,7 @@ void BoxContent::updateShadowsVisibility() {
(top > 0 || _innerTopSkip > 0),
anim::type::normal);
_bottomShadow->toggle(
(top < _scroll->scrollTopMax()),
(top < _scroll->scrollTopMax() || _innerBottomSkip > 0),
anim::type::normal);
}
@ -145,6 +155,16 @@ void BoxContent::setInnerTopSkip(int innerTopSkip, bool scrollBottomFixed) {
}
}
void BoxContent::setInnerBottomSkip(int innerBottomSkip) {
if (_innerBottomSkip != innerBottomSkip) {
auto delta = innerBottomSkip - _innerBottomSkip;
_innerBottomSkip = innerBottomSkip;
if (_scroll && width() > 0) {
updateScrollAreaGeometry();
}
}
}
void BoxContent::setInnerVisible(bool scrollAreaVisible) {
if (_scroll) {
_scroll->setVisible(scrollAreaVisible);
@ -169,13 +189,15 @@ void BoxContent::resizeEvent(QResizeEvent *e) {
}
void BoxContent::updateScrollAreaGeometry() {
auto newScrollHeight = height() - _innerTopSkip;
auto newScrollHeight = height() - _innerTopSkip - _innerBottomSkip;
auto changed = (_scroll->height() != newScrollHeight);
_scroll->setGeometryToLeft(0, _innerTopSkip, width(), newScrollHeight);
_topShadow->entity()->resize(width(), st::lineWidth);
_topShadow->moveToLeft(0, _innerTopSkip);
_bottomShadow->entity()->resize(width(), st::lineWidth);
_bottomShadow->moveToLeft(0, height() - st::lineWidth);
_bottomShadow->moveToLeft(
0,
height() - _innerBottomSkip - st::lineWidth);
if (changed) {
updateInnerVisibleTopBottom();
@ -184,7 +206,7 @@ void BoxContent::updateScrollAreaGeometry() {
(top > 0 || _innerTopSkip > 0),
anim::type::instant);
_bottomShadow->toggle(
(top < _scroll->scrollTopMax()),
(top < _scroll->scrollTopMax() || _innerBottomSkip > 0),
anim::type::instant);
}
}

View File

@ -130,19 +130,29 @@ protected:
getDelegate()->setDimensions(newWidth, maxHeight);
}
void setInnerTopSkip(int topSkip, bool scrollBottomFixed = false);
void setInnerBottomSkip(int bottomSkip);
template <typename Widget>
QPointer<Widget> setInnerWidget(object_ptr<Widget> inner, const style::ScrollArea &st, int topSkip = 0) {
QPointer<Widget> setInnerWidget(
object_ptr<Widget> inner,
const style::ScrollArea &st,
int topSkip = 0,
int bottomSkip = 0) {
auto result = QPointer<Widget>(inner.data());
setInnerTopSkip(topSkip);
setInnerBottomSkip(bottomSkip);
setInner(std::move(inner), st);
return result;
}
template <typename Widget>
QPointer<Widget> setInnerWidget(object_ptr<Widget> inner, int topSkip = 0) {
QPointer<Widget> setInnerWidget(
object_ptr<Widget> inner,
int topSkip = 0,
int bottomSkip = 0) {
auto result = QPointer<Widget>(inner.data());
setInnerTopSkip(topSkip);
setInnerBottomSkip(bottomSkip);
setInner(std::move(inner));
return result;
}
@ -183,6 +193,7 @@ private:
bool _preparing = false;
bool _noContentMargin = false;
int _innerTopSkip = 0;
int _innerBottomSkip = 0;
object_ptr<Ui::ScrollArea> _scroll = { nullptr };
object_ptr<Ui::FadeShadow> _topShadow = { nullptr };
object_ptr<Ui::FadeShadow> _bottomShadow = { nullptr };

View File

@ -712,8 +712,10 @@ sendMediaFileThumbSkip: 10px;
sendMediaFileNameTop: 7px;
sendMediaFileStatusTop: 37px;
proxyUsePadding: margins(22px, 0px, 22px, 12px);
proxyTryIPv6Padding: margins(22px, 12px, 22px, 0px);
proxyRowPadding: margins(22px, 8px, 8px, 8px);
proxyRowIconSkip: 16px;
proxyRowIconSkip: 32px;
proxyRowSkip: 2px;
proxyRowRipple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;

View File

@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/text_options.h"
#include "history/history_location_manager.h"
@ -29,6 +30,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
constexpr auto kSaveSettingsDelayedTimeout = TimeMs(1000);
class ProxyRow : public Ui::RippleButton {
public:
using View = ProxiesBoxController::ItemView;
@ -72,12 +75,16 @@ protected:
private:
void setupContent();
void addNewProxy();
void applyView(View &&view);
void setupButtons(int id, not_null<ProxyRow*> button);
not_null<ProxiesBoxController*> _controller;
object_ptr<Ui::VerticalLayout> _initialInner;
QPointer<Ui::VerticalLayout> _inner;
object_ptr<Ui::PaddingWrap<Ui::Checkbox>> _useProxy;
object_ptr<Ui::PaddingWrap<Ui::Checkbox>> _tryIPv6;
object_ptr<Ui::VerticalLayout> _initialWrap;
QPointer<Ui::VerticalLayout> _wrap;
base::flat_map<int, QPointer<ProxyRow>> _rows;
};
@ -145,6 +152,8 @@ void ProxyRow::updateFields(View &&view) {
_edit->toggle(!_view.deleted, anim::type::instant);
_restore->toggle(_view.deleted, anim::type::instant);
setPointerCursor(!_view.deleted);
update();
}
@ -171,7 +180,6 @@ int ProxyRow::resizeGetHeight(int newWidth) {
right -= _edit->width();
_skipRight = right;
_skipLeft = st::proxyRowPadding.left()
+ st::proxyRowSelectedIcon.width()
+ st::proxyRowIconSkip;
return result;
}
@ -240,7 +248,21 @@ ProxiesBox::ProxiesBox(
QWidget*,
not_null<ProxiesBoxController*> controller)
: _controller(controller)
, _initialInner(this) {
, _useProxy(
this,
object_ptr<Ui::Checkbox>(
this,
lang(lng_proxy_use),
Global::UseProxy()),
st::proxyUsePadding)
, _tryIPv6(
this,
object_ptr<Ui::Checkbox>(
this,
lang(lng_connection_try_ipv6),
Global::TryIPv6()),
st::proxyTryIPv6Padding)
, _initialWrap(this) {
_controller->views(
) | rpl::start_with_next([=](View &&view) {
applyView(std::move(view));
@ -250,40 +272,73 @@ ProxiesBox::ProxiesBox(
void ProxiesBox::prepare() {
setTitle(langFactory(lng_proxy_settings));
addButton(langFactory(lng_proxy_add), [=] {
Ui::show(_controller->addNewItemBox(), LayerOption::KeepOther);
});
addButton(langFactory(lng_close), [=] {
closeBox();
});
addButton(langFactory(lng_proxy_add), [=] { addNewProxy(); });
addButton(langFactory(lng_close), [=] { closeBox(); });
setupContent();
}
void ProxiesBox::setupContent() {
_inner = setInnerWidget(std::move(_initialInner));
_useProxy->resizeToWidth(st::boxWideWidth);
_useProxy->moveToLeft(0, 0);
subscribe(_useProxy->entity()->checkedChanged, [=](bool checked) {
if (!_controller->setProxyEnabled(checked)) {
addNewProxy();
}
});
subscribe(Global::RefConnectionTypeChanged(), [=] {
_useProxy->entity()->setChecked(Global::UseProxy());
});
_inner->resizeToWidth(st::boxWideWidth);
_tryIPv6->resizeToWidth(st::boxWideWidth);
_inner->heightValue(
) | rpl::map([](int height) {
return std::min(height, st::boxMaxListHeight);
const auto topSkip = _useProxy->heightNoMargins();
const auto bottomSkip = _tryIPv6->heightNoMargins();
const auto inner = setInnerWidget(
object_ptr<Ui::VerticalLayout>(this),
topSkip,
bottomSkip);
inner->add(object_ptr<Ui::FixedHeightWidget>(
inner,
st::proxyRowPadding.top()));
_wrap = inner->add(std::move(_initialWrap));
inner->add(object_ptr<Ui::FixedHeightWidget>(
inner,
st::proxyRowPadding.bottom()));
inner->resizeToWidth(st::boxWideWidth);
inner->heightValue(
) | rpl::map([=](int height) {
return std::min(
topSkip + height + bottomSkip,
st::boxMaxListHeight);
}) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](int height) {
setDimensions(st::boxWideWidth, height);
}, lifetime());
}, inner->lifetime());
heightValue(
) | rpl::start_with_next([=](int height) {
_tryIPv6->moveToLeft(0, height - _tryIPv6->heightNoMargins());
}, _tryIPv6->lifetime());
}
void ProxiesBox::addNewProxy() {
Ui::show(_controller->addNewItemBox(), LayerOption::KeepOther);
}
void ProxiesBox::applyView(View &&view) {
const auto id = view.id;
const auto i = _rows.find(id);
if (i == _rows.end()) {
const auto inner = _inner
? _inner.data()
: _initialInner.data();
const auto [i, ok] = _rows.emplace(id, inner->add(
const auto wrap = _wrap
? _wrap.data()
: _initialWrap.data();
const auto [i, ok] = _rows.emplace(id, wrap->insert(
0,
object_ptr<ProxyRow>(
inner,
wrap,
std::move(view))));
setupButtons(id, i->second);
} else {
@ -306,6 +361,11 @@ void ProxiesBox::setupButtons(int id, not_null<ProxyRow*> button) {
) | rpl::start_with_next([=] {
Ui::show(_controller->editItemBox(id), LayerOption::KeepOther);
}, button->lifetime());
button->clicks(
) | rpl::start_with_next([=] {
_controller->applyItem(id);
}, button->lifetime());
}
ProxyBox::ProxyBox(
@ -346,7 +406,7 @@ void ConnectionBox::ShowApplyProxyConfirmation(
*box = Ui::show(Box<ConfirmBox>(text, lang(lng_sure_enable), [=] {
auto &proxies = Global::RefProxiesList();
if (ranges::find(proxies, proxy) == end(proxies)) {
proxies.insert(begin(proxies), proxy);
proxies.push_back(proxy);
}
Global::SetSelectedProxy(proxy);
Global::SetUseProxy(true);
@ -662,7 +722,8 @@ void AutoDownloadBox::onSave() {
closeBox();
}
ProxiesBoxController::ProxiesBoxController() {
ProxiesBoxController::ProxiesBoxController()
: _saveTimer([] { Local::writeSettings(); }) {
_list = ranges::view::all(
Global::ProxiesList()
) | ranges::view::transform([&](const ProxyData &proxy) {
@ -679,7 +740,7 @@ object_ptr<BoxContent> ProxiesBoxController::CreateOwningBox() {
object_ptr<BoxContent> ProxiesBoxController::create() {
auto result = Box<ProxiesBox>(this);
for (const auto &item : _list) {
for (const auto &item : base::reversed(_list)) {
updateView(item);
}
return std::move(result);
@ -694,6 +755,14 @@ auto ProxiesBoxController::findById(int id) -> std::vector<Item>::iterator {
return result;
}
auto ProxiesBoxController::findByProxy(const ProxyData &proxy)
->std::vector<Item>::iterator {
return ranges::find(
_list,
proxy,
[](const Item &item) { return item.data; });
}
void ProxiesBoxController::deleteItem(int id) {
setDeleted(id, true);
}
@ -702,9 +771,68 @@ void ProxiesBoxController::restoreItem(int id) {
setDeleted(id, false);
}
void ProxiesBoxController::applyItem(int id) {
auto item = findById(id);
if (Global::UseProxy() && Global::SelectedProxy() == item->data) {
return;
} else if (item->deleted) {
return;
}
auto j = findByProxy(Global::SelectedProxy());
Global::SetSelectedProxy(item->data);
Global::SetUseProxy(true);
applyChanges();
if (j != end(_list)) {
updateView(*j);
}
updateView(*item);
}
void ProxiesBoxController::setDeleted(int id, bool deleted) {
auto item = findById(id);
item->deleted = deleted;
if (deleted) {
auto &proxies = Global::RefProxiesList();
proxies.erase(ranges::remove(proxies, item->data), end(proxies));
if (item->data == Global::SelectedProxy()) {
_lastSelectedProxy = base::take(Global::RefSelectedProxy());
if (Global::UseProxy()) {
_lastSelectedProxyUsed = true;
Global::SetUseProxy(false);
applyChanges();
} else {
_lastSelectedProxyUsed = false;
}
}
} else {
auto &proxies = Global::RefProxiesList();
if (ranges::find(proxies, item->data) == end(proxies)) {
auto insertBefore = item + 1;
while (insertBefore != end(_list) && insertBefore->deleted) {
++insertBefore;
}
auto insertBeforeIt = (insertBefore == end(_list))
? end(proxies)
: ranges::find(proxies, insertBefore->data);
proxies.insert(insertBeforeIt, item->data);
}
if (!Global::SelectedProxy() && _lastSelectedProxy == item->data) {
Assert(!Global::UseProxy());
Global::SetSelectedProxy(base::take(_lastSelectedProxy));
if (base::take(_lastSelectedProxyUsed)) {
Global::SetUseProxy(true);
applyChanges();
}
}
}
saveDelayed();
updateView(*item);
}
@ -749,6 +877,34 @@ object_ptr<BoxContent> ProxiesBoxController::addNewItemBox() {
});
}
bool ProxiesBoxController::setProxyEnabled(bool enabled) {
if (enabled) {
if (Global::ProxiesList().empty()) {
return false;
} else if (!Global::SelectedProxy()) {
Global::SetSelectedProxy(Global::ProxiesList().back());
auto j = findByProxy(Global::SelectedProxy());
if (j != end(_list)) {
updateView(*j);
}
}
}
Global::SetUseProxy(enabled);
applyChanges();
return true;
}
void ProxiesBoxController::applyChanges() {
Sandbox::refreshGlobalProxy();
Global::RefConnectionTypeChanged().notify();
MTP::restart();
saveDelayed();
}
void ProxiesBoxController::saveDelayed() {
_saveTimer.callOnce(kSaveSettingsDelayedTimeout);
}
auto ProxiesBoxController::views() const -> rpl::producer<ItemView> {
return _views.events();
}
@ -779,3 +935,12 @@ void ProxiesBoxController::updateView(const Item &item) {
deleted,
state });
}
ProxiesBoxController::~ProxiesBoxController() {
if (_saveTimer.isActive()) {
App::CallDelayed(
kSaveSettingsDelayedTimeout,
QApplication::instance(),
[] { Local::writeSettings(); });
}
}

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "boxes/abstract_box.h"
#include "base/timer.h"
namespace Ui {
class InputField;
@ -119,11 +120,15 @@ public:
void deleteItem(int id);
void restoreItem(int id);
void applyItem(int id);
object_ptr<BoxContent> editItemBox(int id);
object_ptr<BoxContent> addNewItemBox();
bool setProxyEnabled(bool enabled);
rpl::producer<ItemView> views() const;
~ProxiesBoxController();
private:
struct Item {
int id = 0;
@ -132,12 +137,19 @@ private:
};
std::vector<Item>::iterator findById(int id);
std::vector<Item>::iterator findByProxy(const ProxyData &proxy);
void setDeleted(int id, bool deleted);
void updateView(const Item &item);
void applyChanges();
void saveDelayed();
int _idCounter = 0;
int _selected = -1;
std::vector<Item> _list;
rpl::event_stream<ItemView> _views;
base::Timer _saveTimer;
ProxyData _lastSelectedProxy;
bool _lastSelectedProxyUsed = false;
};

View File

@ -32,6 +32,7 @@ constexpr int kErrorAlreadyDefined = 805;
constexpr int kErrorBadString = 806;
constexpr int kErrorIconDuplicate = 807;
constexpr int kErrorBadIconModifier = 808;
constexpr int kErrorCyclicDependency = 809;
QString findInputFile(const Options &options) {
for (const auto &dir : options.includePaths) {
@ -148,14 +149,21 @@ Modifier GetModifier(const QString &name) {
return modifiers.value(name);
}
ParsedFile::ParsedFile(const Options &options)
ParsedFile::ParsedFile(
const Options &options,
std::vector<QString> includeStack)
: filePath_(findInputFile(options))
, file_(filePath_)
, options_(options) {
, options_(options)
, includeStack_(includeStack) {
}
bool ParsedFile::read() {
if (!file_.read()) {
if (std::find(begin(includeStack_), end(includeStack_), filePath_)
!= end(includeStack_)) {
logError(kErrorCyclicDependency) << "include cycle detected.";
return false;
} else if (!file_.read()) {
return false;
}
@ -205,7 +213,11 @@ common::LogStream ParsedFile::logErrorTypeMismatch() {
ParsedFile::ModulePtr ParsedFile::readIncluded() {
if (auto usingFile = assertNextToken(BasicType::String)) {
if (assertNextToken(BasicType::Semicolon)) {
ParsedFile included(includedOptions(tokenValue(usingFile)));
auto includeStack = includeStack_;
includeStack.push_back(filePath_);
ParsedFile included(
includedOptions(tokenValue(usingFile)),
includeStack);
if (included.read()) {
return included.getResult();
} else {

View File

@ -24,7 +24,9 @@ Modifier GetModifier(const QString &name);
// Parses an input file to the internal struct.
class ParsedFile {
public:
explicit ParsedFile(const Options &options);
explicit ParsedFile(
const Options &options,
std::vector<QString> includeStack = {});
ParsedFile(const ParsedFile &other) = delete;
ParsedFile &operator=(const ParsedFile &other) = delete;
@ -108,6 +110,8 @@ private:
bool failed_ = false;
ModulePtr module_;
std::vector<QString> includeStack_;
QMap<std::string, structure::Type> typeNames_ = {
{ "int" , { structure::TypeTag::Int } },
{ "double" , { structure::TypeTag::Double } },

View File

@ -1184,6 +1184,8 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
Global::SetUseProxy(proxy ? true : false);
if (proxy) {
Global::SetProxiesList({ 1, proxy });
} else {
Global::SetProxiesList({});
}
Sandbox::refreshGlobalProxy();
} break;
@ -1221,16 +1223,20 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
const auto proxy = readProxy();
if (proxy) {
list.push_back(proxy);
} else if (index < -list.size()) {
++index;
} else if (index > list.size()) {
--index;
}
}
if (!_checkStreamStatus(stream)) {
return false;
}
Global::SetProxiesList(list);
Global::SetUseProxy(index >= 0 && index < count);
Global::SetUseProxy(index > 0 && index <= list.size());
index = std::abs(index);
if (index >= 0 && index < count) {
Global::SetSelectedProxy(list[index]);
if (index > 0 && index <= list.size()) {
Global::SetSelectedProxy(list[index - 1]);
} else {
Global::SetSelectedProxy(ProxyData());
}
@ -1239,12 +1245,17 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
if (!_checkStreamStatus(stream)) {
return false;
}
Global::SetProxiesList({ 1, proxy });
if (connectionType == dbictTcpProxy
|| connectionType == dbictHttpProxy) {
if (proxy) {
Global::SetProxiesList({ 1, proxy });
Global::SetSelectedProxy(proxy);
Global::SetUseProxy(true);
if (connectionType == dbictTcpProxy
|| connectionType == dbictHttpProxy) {
Global::SetUseProxy(true);
} else {
Global::SetUseProxy(false);
}
} else {
Global::SetProxiesList({});
Global::SetSelectedProxy(ProxyData());
Global::SetUseProxy(false);
}
@ -2438,8 +2449,8 @@ void writeSettings() {
auto proxyIt = ranges::find(proxies, proxy);
if (proxy.type != ProxyData::Type::None
&& proxyIt == end(proxies)) {
proxies.insert(begin(proxies), proxy);
proxyIt = begin(proxies);
proxies.push_back(proxy);
proxyIt = end(proxies) - 1;
}
size += sizeof(quint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32);
for (const auto &proxy : proxies) {
@ -2472,7 +2483,7 @@ void writeSettings() {
data.stream << quint32(dbiConnectionType) << qint32(dbictProxiesList);
data.stream << qint32(proxies.size());
const auto index = qint32(proxyIt - begin(proxies));
const auto index = qint32(proxyIt - begin(proxies)) + 1;
data.stream << (Global::UseProxy() ? index : -index);
for (const auto &proxy : proxies) {
data.stream << qint32(kProxyTypeShift + int(proxy.type));

View File

@ -58,6 +58,7 @@ int PaddingWrap<RpWidget>::resizeGetHeight(int newWidth) {
weak->resizeToWidth(newWidth
- _padding.left()
- _padding.right());
SendPendingMoveResizeEvents(weak);
} else {
resize(QSize(
_padding.left() + newWidth + _padding.right(),

View File

@ -92,12 +92,17 @@ void VerticalLayout::updateChildGeometry(
width);
}
RpWidget *VerticalLayout::addChild(
RpWidget *VerticalLayout::insertChild(
int atPosition,
object_ptr<RpWidget> child,
const style::margins &margin) {
if (auto weak = AttachParentChild(this, child)) {
_rows.push_back({ std::move(child), margin });
auto margins = getMargins();
Expects(atPosition >= 0 && atPosition <= _rows.size());
if (const auto weak = AttachParentChild(this, child)) {
_rows.insert(
begin(_rows) + atPosition,
{ std::move(child), margin });
const auto margins = getMargins();
updateChildGeometry(
margins,
weak,
@ -105,11 +110,11 @@ RpWidget *VerticalLayout::addChild(
width() - margins.left() - margins.right(),
height() - margins.top() - margins.bottom());
weak->heightValue(
) | rpl::start_with_next_done([this, weak] {
) | rpl::start_with_next_done([=] {
if (!_inResize) {
childHeightUpdated(weak);
}
}, [this, weak] {
}, [=] {
removeChild(weak);
}, lifetime());
return weak;

View File

@ -15,6 +15,20 @@ class VerticalLayout : public RpWidget {
public:
using RpWidget::RpWidget;
template <
typename Widget,
typename = std::enable_if_t<
std::is_base_of_v<RpWidget, Widget>>>
Widget *insert(
int atPosition,
object_ptr<Widget> &&child,
const style::margins &margin = style::margins()) {
return static_cast<Widget*>(insertChild(
atPosition,
std::move(child),
margin));
}
template <
typename Widget,
typename = std::enable_if_t<
@ -22,9 +36,7 @@ public:
Widget *add(
object_ptr<Widget> &&child,
const style::margins &margin = style::margins()) {
return static_cast<Widget*>(addChild(
std::move(child),
margin));
return insert(_rows.size(), std::move(child), margin);
}
QMargins getMargins() const override;
@ -37,7 +49,8 @@ protected:
int visibleBottom) override;
private:
RpWidget *addChild(
RpWidget *insertChild(
int addPosition,
object_ptr<RpWidget> child,
const style::margins &margin);
void childHeightUpdated(RpWidget *child);