Add touchscreen preview to recent / channels.

This commit is contained in:
John Preston 2024-05-31 19:12:22 +04:00
parent 0fffeac8da
commit a1049ec7ce
10 changed files with 154 additions and 24 deletions

View File

@ -1556,11 +1556,13 @@ void PeerListContent::handleMouseMove(QPoint globalPosition) {
if (_trackPressStart if (_trackPressStart
&& ((*_trackPressStart - globalPosition).manhattanLength() && ((*_trackPressStart - globalPosition).manhattanLength()
> QApplication::startDragDistance())) { > QApplication::startDragDistance())) {
_trackPressStart = std::nullopt; _trackPressStart = {};
_controller->rowTrackPressCancel(); _controller->rowTrackPressCancel();
} }
if (!_controller->rowTrackPressSkipMouseSelection()) {
selectByMouse(globalPosition); selectByMouse(globalPosition);
} }
}
void PeerListContent::pressLeftToContextMenu(bool shown) { void PeerListContent::pressLeftToContextMenu(bool shown) {
if (shown) { if (shown) {
@ -1571,13 +1573,24 @@ void PeerListContent::pressLeftToContextMenu(bool shown) {
} }
} }
bool PeerListContent::trackRowPressFromGlobal(QPoint globalPosition) {
selectByMouse(globalPosition);
if (const auto row = getRow(_selected.index)) {
if (_controller->rowTrackPress(row)) {
_trackPressStart = globalPosition;
return true;
}
}
return false;
}
void PeerListContent::mousePressEvent(QMouseEvent *e) { void PeerListContent::mousePressEvent(QMouseEvent *e) {
_pressButton = e->button(); _pressButton = e->button();
selectByMouse(e->globalPos()); selectByMouse(e->globalPos());
setPressed(_selected); setPressed(_selected);
_trackPressStart = {}; _trackPressStart = {};
if (auto row = getRow(_selected.index)) { if (const auto row = getRow(_selected.index)) {
auto updateCallback = [this, row, hint = _selected.index] { const auto updateCallback = [this, row, hint = _selected.index] {
updateRow(row, hint); updateRow(row, hint);
}; };
if (_selected.element) { if (_selected.element) {

View File

@ -347,7 +347,9 @@ public:
virtual void peerListSortRows(Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) = 0; virtual void peerListSortRows(Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) = 0;
virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0; virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0;
virtual std::shared_ptr<Main::SessionShow> peerListUiShow() = 0; virtual std::shared_ptr<Main::SessionShow> peerListUiShow() = 0;
virtual void peerListPressLeftToContextMenu(bool shown) = 0; virtual void peerListPressLeftToContextMenu(bool shown) = 0;
virtual bool peerListTrackRowPressFromGlobal(QPoint globalPosition) = 0;
template <typename PeerDataRange> template <typename PeerDataRange>
void peerListAddSelectedPeers(PeerDataRange &&range) { void peerListAddSelectedPeers(PeerDataRange &&range) {
@ -484,6 +486,9 @@ public:
} }
virtual void rowTrackPressCancel() { virtual void rowTrackPressCancel() {
} }
virtual bool rowTrackPressSkipMouseSelection() {
return false;
}
virtual void loadMoreRows() { virtual void loadMoreRows() {
} }
@ -663,6 +668,7 @@ public:
void mouseLeftGeometry(); void mouseLeftGeometry();
void pressLeftToContextMenu(bool shown); void pressLeftToContextMenu(bool shown);
bool trackRowPressFromGlobal(QPoint globalPosition);
void setSearchMode(PeerListSearchMode mode); void setSearchMode(PeerListSearchMode mode);
void changeCheckState( void changeCheckState(
@ -1000,9 +1006,13 @@ public:
not_null<PeerListRow*> row, not_null<PeerListRow*> row,
bool highlightRow, bool highlightRow,
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override; Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override;
void peerListPressLeftToContextMenu(bool shown) override { void peerListPressLeftToContextMenu(bool shown) override {
_content->pressLeftToContextMenu(shown); _content->pressLeftToContextMenu(shown);
} }
bool peerListTrackRowPressFromGlobal(QPoint globalPosition) override {
return _content->trackRowPressFromGlobal(globalPosition);
}
protected: protected:
not_null<PeerListContent*> content() const { not_null<PeerListContent*> content() const {

View File

@ -1526,7 +1526,7 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
if (alt && showChatPreview()) { if (alt && showChatPreview()) {
return; return;
} else if (!alt && isUserpicPress()) { } else if (!alt && isUserpicPress()) {
scheduleChatPreview(); scheduleChatPreview(e->globalPos());
} }
if (base::in_range(_collapsedSelected, 0, _collapsedRows.size())) { if (base::in_range(_collapsedSelected, 0, _collapsedRows.size())) {
@ -2440,12 +2440,16 @@ void InnerWidget::chatPreviewShown(bool shown, RowDescriptor row) {
} }
} }
bool InnerWidget::scheduleChatPreview() { bool InnerWidget::scheduleChatPreview(QPoint positionOverride) {
const auto row = computeChatPreviewRow(); const auto row = computeChatPreviewRow();
const auto callback = crl::guard(this, [=](bool shown) { const auto callback = crl::guard(this, [=](bool shown) {
chatPreviewShown(shown, row); chatPreviewShown(shown, row);
}); });
_chatPreviewScheduled = _controller->scheduleChatPreview(row, callback); _chatPreviewScheduled = _controller->scheduleChatPreview(
row,
callback,
nullptr,
positionOverride);
return _chatPreviewScheduled; return _chatPreviewScheduled;
} }
@ -2544,8 +2548,7 @@ bool InnerWidget::processTouchEvent(not_null<QTouchEvent*> e) {
return false; return false;
} }
selectByMouse(*point); selectByMouse(*point);
const auto onlyUserpic = true; if (isUserpicPressOnWide() && scheduleChatPreview(*point)) {
if (isUserpicPress() && scheduleChatPreview()) {
_chatPreviewTouchGlobal = point; _chatPreviewTouchGlobal = point;
} else if (!_dragging) { } else if (!_dragging) {
_touchDragStartGlobal = point; _touchDragStartGlobal = point;

View File

@ -126,7 +126,7 @@ public:
[[nodiscard]] bool isUserpicPress() const; [[nodiscard]] bool isUserpicPress() const;
[[nodiscard]] bool isUserpicPressOnWide() const; [[nodiscard]] bool isUserpicPressOnWide() const;
void cancelChatPreview(); void cancelChatPreview();
bool scheduleChatPreview(); bool scheduleChatPreview(QPoint positionOverride);
bool showChatPreview(); bool showChatPreview();
void chatPreviewShown(bool shown, RowDescriptor row = {}); void chatPreviewShown(bool shown, RowDescriptor row = {});
bool chooseRow( bool chooseRow(

View File

@ -99,10 +99,17 @@ public:
bool rowTrackPress(not_null<PeerListRow*> row) override; bool rowTrackPress(not_null<PeerListRow*> row) override;
void rowTrackPressCancel() override; void rowTrackPressCancel() override;
bool rowTrackPressSkipMouseSelection() override;
bool processTouchEvent(not_null<QTouchEvent*> e);
void setupTouchChatPreview(not_null<Ui::ElasticScroll*> scroll);
private: private:
const not_null<Window::SessionController*> _window; const not_null<Window::SessionController*> _window;
std::optional<QPoint> _chatPreviewTouchGlobal;
rpl::event_stream<> _touchCancelRequests;
}; };
class RecentsController final : public ControllerWithPreviews { class RecentsController final : public ControllerWithPreviews {
@ -426,22 +433,81 @@ bool ControllerWithPreviews::rowTrackPress(not_null<PeerListRow*> row) {
delegate()->peerListPressLeftToContextMenu(shown); delegate()->peerListPressLeftToContextMenu(shown);
}); });
if (base::IsAltPressed()) { if (base::IsAltPressed()) {
_window->showChatPreview({ history, FullMsgId() }, callback); _window->showChatPreview(
{ history, FullMsgId() },
callback,
nullptr,
_chatPreviewTouchGlobal);
return false; return false;
} }
const auto point = delegate()->peerListLastRowMousePosition(); const auto point = delegate()->peerListLastRowMousePosition();
const auto &st = computeListSt().item; const auto &st = computeListSt().item;
if (point && point->x() < st.photoPosition.x() + st.photoSize) { if (point && point->x() < st.photoPosition.x() + st.photoSize) {
_window->scheduleChatPreview({ history, FullMsgId() }, callback); _window->scheduleChatPreview(
{ history, FullMsgId() },
callback,
nullptr,
_chatPreviewTouchGlobal);
return true; return true;
} }
return false; return false;
} }
void ControllerWithPreviews::rowTrackPressCancel() { void ControllerWithPreviews::rowTrackPressCancel() {
_chatPreviewTouchGlobal = {};
_window->cancelScheduledPreview(); _window->cancelScheduledPreview();
} }
bool ControllerWithPreviews::rowTrackPressSkipMouseSelection() {
return _chatPreviewTouchGlobal.has_value();
}
bool ControllerWithPreviews::processTouchEvent(not_null<QTouchEvent*> e) {
const auto point = e->touchPoints().empty()
? std::optional<QPoint>()
: e->touchPoints().front().screenPos().toPoint();
switch (e->type()) {
case QEvent::TouchBegin: {
if (!point) {
return false;
}
_chatPreviewTouchGlobal = point;
if (!delegate()->peerListTrackRowPressFromGlobal(*point)) {
_chatPreviewTouchGlobal = {};
}
} break;
case QEvent::TouchUpdate: {
if (!point) {
return false;
}
if (_chatPreviewTouchGlobal) {
const auto delta = (*_chatPreviewTouchGlobal - *point);
if (delta.manhattanLength() > computeListSt().item.photoSize) {
rowTrackPressCancel();
}
}
} break;
case QEvent::TouchEnd:
case QEvent::TouchCancel: {
if (_chatPreviewTouchGlobal) {
rowTrackPressCancel();
}
} break;
}
return false;
}
void ControllerWithPreviews::setupTouchChatPreview(
not_null<Ui::ElasticScroll*> scroll) {
_touchCancelRequests.events() | rpl::start_with_next([=] {
QTouchEvent ev(QEvent::TouchCancel);
ev.setTimestamp(crl::now());
QGuiApplication::sendEvent(scroll, &ev);
}, lifetime());
}
RecentsController::RecentsController( RecentsController::RecentsController(
not_null<Window::SessionController*> window, not_null<Window::SessionController*> window,
RecentPeersList list) RecentPeersList list)
@ -1030,6 +1096,7 @@ void Suggestions::setupChats() {
}, _topPeers->lifetime()); }, _topPeers->lifetime());
_chatsScroll->setVisible(_tab.current() == Tab::Chats); _chatsScroll->setVisible(_tab.current() == Tab::Chats);
_chatsScroll->setCustomTouchProcess(_recentProcessTouch);
} }
void Suggestions::handlePressForChatPreview( void Suggestions::handlePressForChatPreview(
@ -1063,6 +1130,11 @@ void Suggestions::setupChannels() {
anim::type::instant); anim::type::instant);
_channelsScroll->setVisible(_tab.current() == Tab::Channels); _channelsScroll->setVisible(_tab.current() == Tab::Channels);
_channelsScroll->setCustomTouchProcess([=](not_null<QTouchEvent*> e) {
const auto myChannels = _myChannelsProcessTouch(e);
const auto recommendations = _recommendationsProcessTouch(e);
return myChannels || recommendations;
});
} }
void Suggestions::selectJump(Qt::Key direction, int pageSize) { void Suggestions::selectJump(Qt::Key direction, int pageSize) {
@ -1371,6 +1443,9 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupRecentPeers(
controller->setStyleOverrides(&st::recentPeersList); controller->setStyleOverrides(&st::recentPeersList);
_recentCount = controller->count(); _recentCount = controller->count();
_recentProcessTouch = [=](not_null<QTouchEvent*> e) {
return controller->processTouchEvent(e);
};
controller->chosen( controller->chosen(
) | rpl::start_with_next([=](not_null<PeerData*> peer) { ) | rpl::start_with_next([=](not_null<PeerData*> peer) {
@ -1419,6 +1494,7 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupRecentPeers(
delegate->setContent(raw); delegate->setContent(raw);
controller->setDelegate(delegate); controller->setDelegate(delegate);
controller->setupTouchChatPreview(_chatsScroll.get());
return object_ptr<Ui::SlideWrap<>>(this, std::move(content)); return object_ptr<Ui::SlideWrap<>>(this, std::move(content));
} }
@ -1474,6 +1550,9 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupMyChannels() {
controller->setStyleOverrides(&st::recentPeersList); controller->setStyleOverrides(&st::recentPeersList);
_myChannelsCount = controller->count(); _myChannelsCount = controller->count();
_myChannelsProcessTouch = [=](not_null<QTouchEvent*> e) {
return controller->processTouchEvent(e);
};
controller->chosen( controller->chosen(
) | rpl::start_with_next([=](not_null<PeerData*> peer) { ) | rpl::start_with_next([=](not_null<PeerData*> peer) {
@ -1535,6 +1614,7 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupMyChannels() {
delegate->setContent(raw); delegate->setContent(raw);
controller->setDelegate(delegate); controller->setDelegate(delegate);
controller->setupTouchChatPreview(_channelsScroll.get());
return object_ptr<Ui::SlideWrap<>>(this, std::move(content)); return object_ptr<Ui::SlideWrap<>>(this, std::move(content));
} }
@ -1549,6 +1629,9 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupRecommendations() {
controller->setStyleOverrides(&st::recentPeersList); controller->setStyleOverrides(&st::recentPeersList);
_recommendationsCount = controller->count(); _recommendationsCount = controller->count();
_recommendationsProcessTouch = [=](not_null<QTouchEvent*> e) {
return controller->processTouchEvent(e);
};
_tab.value() | rpl::filter( _tab.value() | rpl::filter(
rpl::mappers::_1 == Tab::Channels rpl::mappers::_1 == Tab::Channels
@ -1603,6 +1686,7 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupRecommendations() {
delegate->setContent(raw); delegate->setContent(raw);
controller->setDelegate(delegate); controller->setDelegate(delegate);
controller->setupTouchChatPreview(_channelsScroll.get());
return object_ptr<Ui::SlideWrap<>>(this, std::move(content)); return object_ptr<Ui::SlideWrap<>>(this, std::move(content));
} }

View File

@ -139,6 +139,7 @@ private:
Fn<JumpResult(Qt::Key, int)> _recentSelectJump; Fn<JumpResult(Qt::Key, int)> _recentSelectJump;
Fn<uint64(QPoint)> _recentUpdateFromParentDrag; Fn<uint64(QPoint)> _recentUpdateFromParentDrag;
Fn<void()> _recentDragLeft; Fn<void()> _recentDragLeft;
Fn<bool(not_null<QTouchEvent*>)> _recentProcessTouch;
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _recentPeers; const not_null<Ui::SlideWrap<Ui::RpWidget>*> _recentPeers;
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _emptyRecent; const not_null<Ui::SlideWrap<Ui::RpWidget>*> _emptyRecent;
@ -150,6 +151,7 @@ private:
Fn<JumpResult(Qt::Key, int)> _myChannelsSelectJump; Fn<JumpResult(Qt::Key, int)> _myChannelsSelectJump;
Fn<uint64(QPoint)> _myChannelsUpdateFromParentDrag; Fn<uint64(QPoint)> _myChannelsUpdateFromParentDrag;
Fn<void()> _myChannelsDragLeft; Fn<void()> _myChannelsDragLeft;
Fn<bool(not_null<QTouchEvent*>)> _myChannelsProcessTouch;
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _myChannels; const not_null<Ui::SlideWrap<Ui::RpWidget>*> _myChannels;
rpl::variable<int> _recommendationsCount; rpl::variable<int> _recommendationsCount;
@ -157,6 +159,7 @@ private:
Fn<JumpResult(Qt::Key, int)> _recommendationsSelectJump; Fn<JumpResult(Qt::Key, int)> _recommendationsSelectJump;
Fn<uint64(QPoint)> _recommendationsUpdateFromParentDrag; Fn<uint64(QPoint)> _recommendationsUpdateFromParentDrag;
Fn<void()> _recommendationsDragLeft; Fn<void()> _recommendationsDragLeft;
Fn<bool(not_null<QTouchEvent*>)> _recommendationsProcessTouch;
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _recommendations; const not_null<Ui::SlideWrap<Ui::RpWidget>*> _recommendations;
const not_null<Ui::SlideWrap<Ui::RpWidget>*> _emptyChannels; const not_null<Ui::SlideWrap<Ui::RpWidget>*> _emptyChannels;

View File

@ -34,7 +34,8 @@ ChatPreviewManager::ChatPreviewManager(
bool ChatPreviewManager::show( bool ChatPreviewManager::show(
Dialogs::RowDescriptor row, Dialogs::RowDescriptor row,
Fn<void(bool shown)> callback, Fn<void(bool shown)> callback,
QPointer<QWidget> parentOverride) { QPointer<QWidget> parentOverride,
std::optional<QPoint> positionOverride) {
cancelScheduled(); cancelScheduled();
_topicLifetime.destroy(); _topicLifetime.destroy();
if (const auto topic = row.key.topic()) { if (const auto topic = row.key.topic()) {
@ -94,7 +95,7 @@ bool ChatPreviewManager::show(
if (callback) { if (callback) {
callback(true); callback(true);
} }
_menu->popup(QCursor::pos()); _menu->popup(positionOverride.value_or(QCursor::pos()));
return true; return true;
} }
@ -102,7 +103,8 @@ bool ChatPreviewManager::show(
bool ChatPreviewManager::schedule( bool ChatPreviewManager::schedule(
Dialogs::RowDescriptor row, Dialogs::RowDescriptor row,
Fn<void(bool shown)> callback, Fn<void(bool shown)> callback,
QPointer<QWidget> parentOverride) { QPointer<QWidget> parentOverride,
std::optional<QPoint> positionOverride) {
cancelScheduled(); cancelScheduled();
_topicLifetime.destroy(); _topicLifetime.destroy();
if (const auto topic = row.key.topic()) { if (const auto topic = row.key.topic()) {
@ -116,17 +118,23 @@ bool ChatPreviewManager::schedule(
_scheduled = std::move(row); _scheduled = std::move(row);
_scheduledCallback = std::move(callback); _scheduledCallback = std::move(callback);
_scheduledParentOverride = std::move(parentOverride); _scheduledParentOverride = std::move(parentOverride);
_scheduledPositionOverride = positionOverride;
_timer.callOnce(kChatPreviewDelay); _timer.callOnce(kChatPreviewDelay);
return true; return true;
} }
void ChatPreviewManager::showScheduled() { void ChatPreviewManager::showScheduled() {
show(base::take(_scheduled), base::take(_scheduledCallback)); show(
base::take(_scheduled),
base::take(_scheduledCallback),
nullptr,
base::take(_scheduledPositionOverride));
} }
void ChatPreviewManager::cancelScheduled() { void ChatPreviewManager::cancelScheduled() {
_scheduled = {}; _scheduled = {};
_scheduledCallback = nullptr; _scheduledCallback = nullptr;
_scheduledPositionOverride = {};
_timer.cancel(); _timer.cancel();
} }

View File

@ -26,11 +26,13 @@ public:
bool show( bool show(
Dialogs::RowDescriptor row, Dialogs::RowDescriptor row,
Fn<void(bool shown)> callback = nullptr, Fn<void(bool shown)> callback = nullptr,
QPointer<QWidget> parentOverride = nullptr); QPointer<QWidget> parentOverride = nullptr,
std::optional<QPoint> positionOverride = {});
bool schedule( bool schedule(
Dialogs::RowDescriptor row, Dialogs::RowDescriptor row,
Fn<void(bool shown)> callback = nullptr, Fn<void(bool shown)> callback = nullptr,
QPointer<QWidget> parentOverride = nullptr); QPointer<QWidget> parentOverride = nullptr,
std::optional<QPoint> positionOverride = {});
void cancelScheduled(); void cancelScheduled();
private: private:
@ -40,6 +42,7 @@ private:
Dialogs::RowDescriptor _scheduled; Dialogs::RowDescriptor _scheduled;
Fn<void(bool)> _scheduledCallback; Fn<void(bool)> _scheduledCallback;
QPointer<QWidget> _scheduledParentOverride; QPointer<QWidget> _scheduledParentOverride;
std::optional<QPoint> _scheduledPositionOverride;
base::Timer _timer; base::Timer _timer;
rpl::lifetime _topicLifetime; rpl::lifetime _topicLifetime;

View File

@ -2979,21 +2979,25 @@ QString SessionController::premiumRef() const {
bool SessionController::showChatPreview( bool SessionController::showChatPreview(
Dialogs::RowDescriptor row, Dialogs::RowDescriptor row,
Fn<void(bool shown)> callback, Fn<void(bool shown)> callback,
QPointer<QWidget> parentOverride) { QPointer<QWidget> parentOverride,
std::optional<QPoint> positionOverride) {
return _chatPreviewManager->show( return _chatPreviewManager->show(
std::move(row), std::move(row),
std::move(callback), std::move(callback),
std::move(parentOverride)); std::move(parentOverride),
positionOverride);
} }
bool SessionController::scheduleChatPreview( bool SessionController::scheduleChatPreview(
Dialogs::RowDescriptor row, Dialogs::RowDescriptor row,
Fn<void(bool shown)> callback, Fn<void(bool shown)> callback,
QPointer<QWidget> parentOverride) { QPointer<QWidget> parentOverride,
std::optional<QPoint> positionOverride) {
return _chatPreviewManager->schedule( return _chatPreviewManager->schedule(
std::move(row), std::move(row),
std::move(callback), std::move(callback),
std::move(parentOverride)); std::move(parentOverride),
positionOverride);
} }
void SessionController::cancelScheduledPreview() { void SessionController::cancelScheduledPreview() {

View File

@ -604,11 +604,13 @@ public:
bool showChatPreview( bool showChatPreview(
Dialogs::RowDescriptor row, Dialogs::RowDescriptor row,
Fn<void(bool shown)> callback = nullptr, Fn<void(bool shown)> callback = nullptr,
QPointer<QWidget> parentOverride = nullptr); QPointer<QWidget> parentOverride = nullptr,
std::optional<QPoint> positionOverride = {});
bool scheduleChatPreview( bool scheduleChatPreview(
Dialogs::RowDescriptor row, Dialogs::RowDescriptor row,
Fn<void(bool shown)> callback = nullptr, Fn<void(bool shown)> callback = nullptr,
QPointer<QWidget> parentOverride = nullptr); QPointer<QWidget> parentOverride = nullptr,
std::optional<QPoint> positionOverride = {});
void cancelScheduledPreview(); void cancelScheduledPreview();
[[nodiscard]] bool contentOverlapped(QWidget *w, QPaintEvent *e) const; [[nodiscard]] bool contentOverlapped(QWidget *w, QPaintEvent *e) const;