Improve scrolling limits in chat select boxes.

This commit is contained in:
John Preston 2023-04-04 17:17:07 +04:00
parent 9b1c5b1050
commit 7ff0659e91
5 changed files with 128 additions and 35 deletions

View file

@ -55,14 +55,18 @@ public:
[[nodiscard]] auto selectedValue() const [[nodiscard]] auto selectedValue() const
-> rpl::producer<base::flat_set<not_null<PeerData*>>>; -> rpl::producer<base::flat_set<not_null<PeerData*>>>;
void setAddedTopHeight(int addedTopHeight); void adjust(int minHeight, int maxHeight, int addedTopHeight);
void setRealContentHeight(rpl::producer<int> value);
rpl::producer<int> boxHeightValue() const override;
private: private:
void setupAboveWidget(); void setupAboveWidget();
void setupBelowWidget(); void setupBelowWidget();
void initDesiredHeightValue();
const not_null<Window::SessionController*> _window; const not_null<Window::SessionController*> _window;
Ui::RpWidget *_addedTopWidget = nullptr; Ui::RpWidget *_addedTopWidget = nullptr;
Ui::RpWidget *_addedBottomWidget = nullptr;
ToggleAction _action = ToggleAction::Adding; ToggleAction _action = ToggleAction::Adding;
QString _filterTitle; QString _filterTitle;
@ -70,6 +74,12 @@ private:
std::vector<not_null<PeerData*>> _additional; std::vector<not_null<PeerData*>> _additional;
rpl::variable<base::flat_set<not_null<PeerData*>>> _selected; rpl::variable<base::flat_set<not_null<PeerData*>>> _selected;
int _minTopHeight = 0;
rpl::variable<int> _maxTopHeight;
rpl::variable<int> _aboveHeight;
rpl::variable<int> _belowHeight;
rpl::variable<int> _desiredHeight;
base::unique_qptr<Ui::PopupMenu> _menu; base::unique_qptr<Ui::PopupMenu> _menu;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;
@ -126,7 +136,7 @@ private:
void InitFilterLinkHeader( void InitFilterLinkHeader(
not_null<PeerListBox*> box, not_null<PeerListBox*> box,
Fn<void(int)> setAddedTopHeight, Fn<void(int minHeight, int maxHeight, int addedTopHeight)> adjust,
Ui::FilterLinkHeaderType type, Ui::FilterLinkHeaderType type,
const QString &title, const QString &title,
const QString &iconEmoji, const QString &iconEmoji,
@ -159,6 +169,12 @@ void InitFilterLinkHeader(
box->sendScrollViewportEvent(e); box->sendScrollViewportEvent(e);
}, widget->lifetime()); }, widget->lifetime());
std::move(
header.closeRequests
) | rpl::start_with_next([=] {
box->closeBox();
}, widget->lifetime());
struct State { struct State {
bool processing = false; bool processing = false;
int addedTopHeight = 0; int addedTopHeight = 0;
@ -178,17 +194,18 @@ void InitFilterLinkHeader(
const auto addedTopHeight = max - headerHeight; const auto addedTopHeight = max - headerHeight;
widget->resize(widget->width(), headerHeight); widget->resize(widget->width(), headerHeight);
if (state->addedTopHeight < addedTopHeight) { if (state->addedTopHeight < addedTopHeight) {
setAddedTopHeight(addedTopHeight); adjust(min, max, addedTopHeight);
box->setAddedTopScrollSkip(headerHeight); box->setAddedTopScrollSkip(headerHeight);
} else { } else {
box->setAddedTopScrollSkip(headerHeight); box->setAddedTopScrollSkip(headerHeight);
setAddedTopHeight(addedTopHeight); adjust(min, max, addedTopHeight);
} }
state->addedTopHeight = addedTopHeight; state->addedTopHeight = addedTopHeight;
box->peerListRefreshRows(); box->peerListRefreshRows();
}, widget->lifetime()); }, widget->lifetime());
box->setNoContentMargin(true); box->setNoContentMargin(true);
adjust(min, max, 0);
} }
void ImportInvite( void ImportInvite(
@ -260,6 +277,7 @@ void ToggleChatsController::prepare() {
} }
if (!additional) { if (!additional) {
delegate()->peerListSetRowChecked(raw, true); delegate()->peerListSetRowChecked(raw, true);
raw->finishCheckedAnimation();
selected.emplace(peer); selected.emplace(peer);
} else if (_action == ToggleAction::Adding) { } else if (_action == ToggleAction::Adding) {
raw->setDisabledState(PeerListRow::State::DisabledChecked); raw->setDisabledState(PeerListRow::State::DisabledChecked);
@ -274,6 +292,7 @@ void ToggleChatsController::prepare() {
for (const auto &peer : _additional) { for (const auto &peer : _additional) {
add(peer, true); add(peer, true);
} }
initDesiredHeightValue();
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
_selected = std::move(selected); _selected = std::move(selected);
} }
@ -298,7 +317,9 @@ void ToggleChatsController::setupAboveWidget() {
const auto container = wrap.data(); const auto container = wrap.data();
_addedTopWidget = container->add(object_ptr<Ui::RpWidget>(container)); _addedTopWidget = container->add(object_ptr<Ui::RpWidget>(container));
AddDivider(container); const auto realAbove = container->add(
object_ptr<Ui::VerticalLayout>(container));
AddDivider(realAbove);
const auto totalCount = [&] { const auto totalCount = [&] {
if (_chats.empty()) { if (_chats.empty()) {
return _additional.size(); return _additional.size();
@ -319,7 +340,7 @@ void ToggleChatsController::setupAboveWidget() {
? _additional.size() ? _additional.size()
: _chats.size(); : _chats.size();
AddSubsectionTitle( AddSubsectionTitle(
container, realAbove,
(_action == ToggleAction::Removing (_action == ToggleAction::Removing
? tr::lng_filters_by_link_quit ? tr::lng_filters_by_link_quit
: _chats.empty() : _chats.empty()
@ -329,23 +350,34 @@ void ToggleChatsController::setupAboveWidget() {
rpl::single(float64(count))), rpl::single(float64(count))),
st::filterLinkSubsectionTitlePadding); st::filterLinkSubsectionTitlePadding);
_aboveHeight = realAbove->heightValue();
delegate()->peerListSetAboveWidget(std::move(wrap)); delegate()->peerListSetAboveWidget(std::move(wrap));
} }
void ToggleChatsController::setupBelowWidget() { void ToggleChatsController::setupBelowWidget() {
if (_chats.empty()) { if (_chats.empty()) {
auto widget = object_ptr<Ui::RpWidget>((QWidget*)nullptr);
_addedBottomWidget = widget.data();
delegate()->peerListSetBelowWidget(std::move(widget));
return; return;
} }
delegate()->peerListSetBelowWidget( auto layout = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
object_ptr<Ui::DividerLabel>( const auto raw = layout.data();
(QWidget*)nullptr, auto widget = object_ptr<Ui::DividerLabel>(
object_ptr<Ui::FlatLabel>( (QWidget*)nullptr,
(QWidget*)nullptr, std::move(layout),
(_action == ToggleAction::Removing st::settingsDividerLabelPadding);
? tr::lng_filters_by_link_about_quit raw->add(object_ptr<Ui::FlatLabel>(
: tr::lng_filters_by_link_about)(tr::now), raw,
st::boxDividerLabel), (_action == ToggleAction::Removing
st::settingsDividerLabelPadding)); ? tr::lng_filters_by_link_about_quit
: tr::lng_filters_by_link_about)(tr::now),
st::boxDividerLabel));
_addedBottomWidget = raw->add(object_ptr<Ui::RpWidget>(raw));
_belowHeight = widget->heightValue() | rpl::map([=](int value) {
return value - _addedBottomWidget->height();
});
delegate()->peerListSetBelowWidget(std::move(widget));
} }
Main::Session &ToggleChatsController::session() const { Main::Session &ToggleChatsController::session() const {
@ -357,10 +389,56 @@ auto ToggleChatsController::selectedValue() const
return _selected.value(); return _selected.value();
} }
void ToggleChatsController::setAddedTopHeight(int addedTopHeight) { void ToggleChatsController::adjust(
int minHeight,
int maxHeight,
int addedTopHeight) {
Expects(addedTopHeight >= 0); Expects(addedTopHeight >= 0);
_addedTopWidget->resize(_addedTopWidget->width(), addedTopHeight); _addedTopWidget->resize(_addedTopWidget->width(), addedTopHeight);
_minTopHeight = minHeight;
_maxTopHeight = maxHeight;
}
void ToggleChatsController::setRealContentHeight(rpl::producer<int> value) {
std::move(
value
) | rpl::start_with_next([=](int height) {
const auto desired = _desiredHeight.current();
if (height <= computeListSt().item.height) {
return;
} else if (height >= desired) {
_addedBottomWidget->resize(_addedBottomWidget->width(), 0);
} else {
const auto available = desired - height;
const auto required = _maxTopHeight.current() - _minTopHeight;
const auto added = required - available;
_addedBottomWidget->resize(
_addedBottomWidget->width(),
std::max(added, 0));
}
}, _lifetime);
}
void ToggleChatsController::initDesiredHeightValue() {
using namespace rpl::mappers;
const auto &st = computeListSt();
const auto count = int(delegate()->peerListFullRowsCount());
const auto middle = st.padding.top()
+ (count * st.item.height)
+ st.padding.bottom();
_desiredHeight = rpl::combine(
_maxTopHeight.value(),
_aboveHeight.value(),
_belowHeight.value(),
_1 + _2 + middle + _3);
}
rpl::producer<int> ToggleChatsController::boxHeightValue() const {
return _desiredHeight.value() | rpl::map([=](int value) {
return std::min(value, st::boxMaxListHeight);
});
} }
void ShowImportError( void ShowImportError(
@ -454,10 +532,12 @@ void ProcessFilterInvite(
) | rpl::map([=](const base::flat_set<not_null<PeerData*>> &peers) { ) | rpl::map([=](const base::flat_set<not_null<PeerData*>> &peers) {
return int(peers.size()); return int(peers.size());
}); });
InitFilterLinkHeader(box, [=](int addedTopHeight) { InitFilterLinkHeader(box, [=](int min, int max, int addedTop) {
raw->setAddedTopHeight(addedTopHeight); raw->adjust(min, max, addedTop);
}, type, title, iconEmoji, rpl::duplicate(badge)); }, type, title, iconEmoji, rpl::duplicate(badge));
raw->setRealContentHeight(box->heightValue());
auto owned = Ui::FilterLinkProcessButton( auto owned = Ui::FilterLinkProcessButton(
box, box,
type, type,
@ -677,8 +757,8 @@ void ProcessFilterRemove(
) | rpl::map([=](const base::flat_set<not_null<PeerData*>> &peers) { ) | rpl::map([=](const base::flat_set<not_null<PeerData*>> &peers) {
return int(peers.size()); return int(peers.size());
}); });
InitFilterLinkHeader(box, [=](int addedTopHeight) { InitFilterLinkHeader(box, [=](int min, int max, int addedTop) {
raw->setAddedTopHeight(addedTopHeight); raw->adjust(min, max, addedTop);
}, type, title, iconEmoji, rpl::single(0)); }, type, title, iconEmoji, rpl::single(0));
auto owned = Ui::FilterLinkProcessButton( auto owned = Ui::FilterLinkProcessButton(

View file

@ -217,16 +217,18 @@ Ui::FlatLabel *EditPrivacyBox::addLabel(
if (!text) { if (!text) {
return nullptr; return nullptr;
} }
return container->add( auto label = object_ptr<Ui::FlatLabel>(
container,
rpl::duplicate(text),
st::boxDividerLabel);
const auto result = label.data();
container->add(
object_ptr<Ui::DividerLabel>( object_ptr<Ui::DividerLabel>(
container, container,
object_ptr<Ui::FlatLabel>( std::move(label),
container,
rpl::duplicate(text),
st::boxDividerLabel),
st::settingsDividerLabelPadding), st::settingsDividerLabelPadding),
{ 0, topSkip, 0, 0 } { 0, topSkip, 0, 0 });
)->entity(); return result;
} }
Ui::FlatLabel *EditPrivacyBox::addLabelOrDivider( Ui::FlatLabel *EditPrivacyBox::addLabelOrDivider(

View file

@ -695,6 +695,7 @@ void LinkController::prepare() {
const auto raw = row.get(); const auto raw = row.get();
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
delegate()->peerListSetRowChecked(raw, true); delegate()->peerListSetRowChecked(raw, true);
raw->finishCheckedAnimation();
_initial.emplace(peer); _initial.emplace(peer);
} }
for (const auto &history : _filterChats) { for (const auto &history : _filterChats) {

View file

@ -31,9 +31,9 @@ public:
FilterLinkHeaderDescriptor &&descriptor); FilterLinkHeaderDescriptor &&descriptor);
void setTitlePosition(int x, int y); void setTitlePosition(int x, int y);
void updateDimensions(int newWidth);
[[nodiscard]] rpl::producer<not_null<QWheelEvent*>> wheelEvents() const; [[nodiscard]] rpl::producer<not_null<QWheelEvent*>> wheelEvents() const;
[[nodiscard]] rpl::producer<> closeRequests() const;
private: private:
void resizeEvent(QResizeEvent *e) override; void resizeEvent(QResizeEvent *e) override;
@ -46,6 +46,7 @@ private:
void refreshTitleText(); void refreshTitleText();
const not_null<FlatLabel*> _about; const not_null<FlatLabel*> _about;
const not_null<IconButton*> _close;
QMargins _aboutPadding; QMargins _aboutPadding;
struct { struct {
@ -176,10 +177,11 @@ Widget::Widget(
not_null<QWidget*> parent, not_null<QWidget*> parent,
FilterLinkHeaderDescriptor &&descriptor) FilterLinkHeaderDescriptor &&descriptor)
: RpWidget(parent) : RpWidget(parent)
, _about(CreateChild<Ui::FlatLabel>( , _about(CreateChild<FlatLabel>(
this, this,
rpl::single(descriptor.about.value()), rpl::single(descriptor.about.value()),
st::filterLinkAbout)) st::filterLinkAbout))
, _close(CreateChild<IconButton>(this, st::boxTitleClose))
, _aboutPadding(st::boxRowPadding) , _aboutPadding(st::boxRowPadding)
, _badge(std::move(descriptor.badge)) , _badge(std::move(descriptor.badge))
, _titleText(descriptor.title) , _titleText(descriptor.title)
@ -216,6 +218,10 @@ rpl::producer<not_null<QWheelEvent*>> Widget::wheelEvents() const {
return _wheelEvents.events(); return _wheelEvents.events();
} }
rpl::producer<> Widget::closeRequests() const {
return _close->clicks() | rpl::to_empty;
}
void Widget::resizeEvent(QResizeEvent *e) { void Widget::resizeEvent(QResizeEvent *e) {
const auto &padding = _aboutPadding; const auto &padding = _aboutPadding;
const auto availableWidth = width() - padding.left() - padding.right(); const auto availableWidth = width() - padding.left() - padding.right();
@ -258,13 +264,15 @@ void Widget::resizeEvent(QResizeEvent *e) {
_about->moveToLeft(_aboutPadding.left(), aboutTop); _about->moveToLeft(_aboutPadding.left(), aboutTop);
_about->setOpacity(_progress.body); _about->setOpacity(_progress.body);
_close->moveToRight(0, 0);
update(); update();
} }
QRectF Widget::previewRect( QRectF Widget::previewRect(
float64 topProgress, float64 topProgress,
float64 sizeProgress) const { float64 sizeProgress) const {
const auto size = st::filterLinkPreview; const auto size = st::filterLinkPreview * sizeProgress;
return QRectF( return QRectF(
(width() - size) / 2., (width() - size) / 2.,
st::filterLinkPreviewTop * topProgress, st::filterLinkPreviewTop * topProgress,
@ -276,9 +284,6 @@ void Widget::paintEvent(QPaintEvent *e) {
auto p = QPainter(this); auto p = QPainter(this);
p.setOpacity(_progress.body); p.setOpacity(_progress.body);
p.translate(_previewRect.center());
p.scale(_progress.body, _progress.body);
p.translate(-_previewRect.center());
if (_progress.top) { if (_progress.top) {
auto hq = PainterHighQualityEnabler(p); auto hq = PainterHighQualityEnabler(p);
if (_preview.isNull()) { if (_preview.isNull()) {
@ -327,7 +332,11 @@ void Widget::wheelEvent(QWheelEvent *e) {
const auto result = CreateChild<Widget>( const auto result = CreateChild<Widget>(
parent.get(), parent.get(),
std::move(descriptor)); std::move(descriptor));
return { .widget = result, .wheelEvents = result->wheelEvents() }; return {
.widget = result,
.wheelEvents = result->wheelEvents(),
.closeRequests = result->closeRequests(),
};
} }
object_ptr<RoundButton> FilterLinkProcessButton( object_ptr<RoundButton> FilterLinkProcessButton(

View file

@ -34,6 +34,7 @@ struct FilterLinkHeaderDescriptor {
struct FilterLinkHeader { struct FilterLinkHeader {
not_null<RpWidget*> widget; not_null<RpWidget*> widget;
rpl::producer<not_null<QWheelEvent*>> wheelEvents; rpl::producer<not_null<QWheelEvent*>> wheelEvents;
rpl::producer<> closeRequests;
}; };
[[nodiscard]] FilterLinkHeader MakeFilterLinkHeader( [[nodiscard]] FilterLinkHeader MakeFilterLinkHeader(