Skip to content

Commit 99567d3

Browse files
committed
Added second implementation of flexible scroll with event filter.
1 parent eddad6b commit 99567d3

File tree

2 files changed

+131
-1
lines changed

2 files changed

+131
-1
lines changed

Telegram/SourceFiles/info/info_flexible_scroll.cpp

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ For license and copyright information please follow this link:
88
#include "info/info_flexible_scroll.h"
99

1010
#include "ui/widgets/scroll_area.h"
11+
#include "base/event_filter.h"
1112
#include "styles/style_info.h"
1213

1314
#include <QtWidgets/QApplication>
@@ -29,7 +30,8 @@ FlexibleScrollHelper::FlexibleScrollHelper(
2930
, _setViewport(setViewport)
3031
, _data(data) {
3132
setupScrollAnimation();
32-
setupScrollHandling();
33+
// setupScrollHandling();
34+
setupScrollHandlingWithFilter();
3335
}
3436

3537
void FlexibleScrollHelper::setupScrollAnimation() {
@@ -195,4 +197,130 @@ void FlexibleScrollHelper::setupScrollHandling() {
195197
}));
196198
}
197199

200+
void FlexibleScrollHelper::setupScrollHandlingWithFilter() {
201+
const auto heightDiff = [=] {
202+
return _pinnedToTop->maximumHeight()
203+
- _pinnedToTop->minimumHeight();
204+
};
205+
206+
rpl::combine(
207+
_pinnedToTop->heightValue(),
208+
_inner->heightValue()
209+
) | rpl::start_with_next([=](int, int h) {
210+
_data.contentHeightValue.fire(h + heightDiff());
211+
}, _pinnedToTop->lifetime());
212+
213+
const auto singleStep = _scroll->verticalScrollBar()->singleStep()
214+
* QApplication::wheelScrollLines();
215+
const auto step1 = (_pinnedToTop->maximumHeight()
216+
< st::infoProfileTopBarHeightMax)
217+
? (st::infoProfileTopBarStep2 + st::lineWidth)
218+
: st::infoProfileTopBarStep1;
219+
const auto step2 = st::infoProfileTopBarStep2;
220+
221+
base::install_event_filter(_scroll->verticalScrollBar(), [=](
222+
not_null<QEvent*> e) {
223+
if (e->type() != QEvent::Wheel) {
224+
return base::EventFilterResult::Continue;
225+
}
226+
const auto wheel = static_cast<QWheelEvent*>(e.get());
227+
const auto delta = wheel->angleDelta().y();
228+
if (std::abs(delta) != 120) {
229+
return base::EventFilterResult::Continue;
230+
}
231+
const auto top = _scroll->scrollTop();
232+
const auto diff = (delta > 0) ? -singleStep : singleStep;
233+
const auto previousValue = top;
234+
const auto targetTop = top + diff;
235+
const auto nextStep = (diff > 0)
236+
? ((previousValue == 0)
237+
? step1
238+
: (previousValue == step1)
239+
? step2
240+
: -1)
241+
: ((targetTop < step1)
242+
? 0
243+
: (targetTop < step2)
244+
? step1
245+
: -1);
246+
if (_scrollAnimation.animating()
247+
&& ((_scrollTopTo > _scrollTopFrom) != (diff > 0))) {
248+
auto overriddenDirection = true;
249+
if (_scrollTopTo > _scrollTopFrom) {
250+
if (_scrollTopTo == step1) {
251+
_scrollTopTo = 0;
252+
} else if (_scrollTopTo == step2) {
253+
_scrollTopTo = step1;
254+
} else {
255+
overriddenDirection = false;
256+
}
257+
} else {
258+
if (_scrollTopTo == 0) {
259+
_scrollTopTo = step1;
260+
} else if (_scrollTopTo == step1) {
261+
_scrollTopTo = step2;
262+
} else {
263+
overriddenDirection = false;
264+
}
265+
}
266+
if (overriddenDirection) {
267+
_timeOffset = crl::now() - _scrollAnimation.started();
268+
_scrollTopFrom = _lastScrollApplied
269+
? _lastScrollApplied
270+
: top;
271+
return base::EventFilterResult::Cancel;
272+
} else {
273+
_scrollAnimation.stop();
274+
_scrollTopFrom = 0;
275+
_scrollTopTo = 0;
276+
_timeOffset = 0;
277+
_lastScrollApplied = 0;
278+
}
279+
}
280+
_scrollTopFrom = _lastScrollApplied ? _lastScrollApplied : top;
281+
if (!_scrollAnimation.animating()) {
282+
_scrollTopTo = (nextStep != -1) ? nextStep : targetTop;
283+
_scrollAnimation.start();
284+
} else {
285+
if (_scrollTopTo > _scrollTopFrom) {
286+
if (_scrollTopTo == step1) {
287+
_scrollTopTo = step2;
288+
} else {
289+
_scrollTopTo += diff;
290+
}
291+
} else {
292+
if (_scrollTopTo == step2) {
293+
_scrollTopTo = step1;
294+
} else if (_scrollTopTo == step1) {
295+
_scrollTopTo = 0;
296+
} else {
297+
_scrollTopTo += diff;
298+
}
299+
}
300+
_timeOffset = crl::now() - _scrollAnimation.started();
301+
}
302+
return base::EventFilterResult::Cancel;
303+
}, _filterLifetime);
304+
305+
_scroll->scrollTopValue(
306+
) | rpl::start_with_next([=](int top) {
307+
const auto current = heightDiff() - top;
308+
_inner->moveToLeft(0, std::min(0, current));
309+
_pinnedToTop->resize(
310+
_pinnedToTop->width(),
311+
std::max(current + _pinnedToTop->minimumHeight(), 0));
312+
}, _inner->lifetime());
313+
314+
_data.fillerWidthValue.events(
315+
) | rpl::start_with_next([=](int w) {
316+
_inner->resizeToWidth(w);
317+
}, _inner->lifetime());
318+
319+
_setPaintPadding({ 0, _pinnedToTop->minimumHeight(), 0, 0 });
320+
_setViewport(_pinnedToTop->events(
321+
) | rpl::filter([](not_null<QEvent*> e) {
322+
return e->type() == QEvent::Wheel;
323+
}));
324+
}
325+
198326
} // namespace Info

Telegram/SourceFiles/info/info_flexible_scroll.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class FlexibleScrollHelper final {
3131
private:
3232
void setupScrollAnimation();
3333
void setupScrollHandling();
34+
void setupScrollHandlingWithFilter();
3435

3536
const not_null<Ui::ScrollArea*> _scroll;
3637
const not_null<Ui::RpWidget*> _inner;
@@ -46,6 +47,7 @@ class FlexibleScrollHelper final {
4647
int _lastScrollApplied = 0;
4748
int _scrollTopPrevious = 0;
4849
bool _applyingFakeScrollState = false;
50+
rpl::lifetime _filterLifetime;
4951
};
5052

5153
} // namespace Info

0 commit comments

Comments
 (0)