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,10 +1556,12 @@ void PeerListContent::handleMouseMove(QPoint globalPosition) {
if (_trackPressStart
&& ((*_trackPressStart - globalPosition).manhattanLength()
> QApplication::startDragDistance())) {
_trackPressStart = std::nullopt;
_trackPressStart = {};
_controller->rowTrackPressCancel();
}
selectByMouse(globalPosition);
if (!_controller->rowTrackPressSkipMouseSelection()) {
selectByMouse(globalPosition);
}
}
void PeerListContent::pressLeftToContextMenu(bool 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) {
_pressButton = e->button();
selectByMouse(e->globalPos());
setPressed(_selected);
_trackPressStart = {};
if (auto row = getRow(_selected.index)) {
auto updateCallback = [this, row, hint = _selected.index] {
if (const auto row = getRow(_selected.index)) {
const auto updateCallback = [this, row, hint = _selected.index] {
updateRow(row, hint);
};
if (_selected.element) {

View File

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

View File

@ -1526,7 +1526,7 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
if (alt && showChatPreview()) {
return;
} else if (!alt && isUserpicPress()) {
scheduleChatPreview();
scheduleChatPreview(e->globalPos());
}
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 callback = crl::guard(this, [=](bool shown) {
chatPreviewShown(shown, row);
});
_chatPreviewScheduled = _controller->scheduleChatPreview(row, callback);
_chatPreviewScheduled = _controller->scheduleChatPreview(
row,
callback,
nullptr,
positionOverride);
return _chatPreviewScheduled;
}
@ -2544,8 +2548,7 @@ bool InnerWidget::processTouchEvent(not_null<QTouchEvent*> e) {
return false;
}
selectByMouse(*point);
const auto onlyUserpic = true;
if (isUserpicPress() && scheduleChatPreview()) {
if (isUserpicPressOnWide() && scheduleChatPreview(*point)) {
_chatPreviewTouchGlobal = point;
} else if (!_dragging) {
_touchDragStartGlobal = point;

View File

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

View File

@ -99,10 +99,17 @@ public:
bool rowTrackPress(not_null<PeerListRow*> row) override;
void rowTrackPressCancel() override;
bool rowTrackPressSkipMouseSelection() override;
bool processTouchEvent(not_null<QTouchEvent*> e);
void setupTouchChatPreview(not_null<Ui::ElasticScroll*> scroll);
private:
const not_null<Window::SessionController*> _window;
std::optional<QPoint> _chatPreviewTouchGlobal;
rpl::event_stream<> _touchCancelRequests;
};
class RecentsController final : public ControllerWithPreviews {
@ -426,22 +433,81 @@ bool ControllerWithPreviews::rowTrackPress(not_null<PeerListRow*> row) {
delegate()->peerListPressLeftToContextMenu(shown);
});
if (base::IsAltPressed()) {
_window->showChatPreview({ history, FullMsgId() }, callback);
_window->showChatPreview(
{ history, FullMsgId() },
callback,
nullptr,
_chatPreviewTouchGlobal);
return false;
}
const auto point = delegate()->peerListLastRowMousePosition();
const auto &st = computeListSt().item;
if (point && point->x() < st.photoPosition.x() + st.photoSize) {
_window->scheduleChatPreview({ history, FullMsgId() }, callback);
_window->scheduleChatPreview(
{ history, FullMsgId() },
callback,
nullptr,
_chatPreviewTouchGlobal);
return true;
}
return false;
}
void ControllerWithPreviews::rowTrackPressCancel() {
_chatPreviewTouchGlobal = {};
_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(
not_null<Window::SessionController*> window,
RecentPeersList list)
@ -1030,6 +1096,7 @@ void Suggestions::setupChats() {
}, _topPeers->lifetime());
_chatsScroll->setVisible(_tab.current() == Tab::Chats);
_chatsScroll->setCustomTouchProcess(_recentProcessTouch);
}
void Suggestions::handlePressForChatPreview(
@ -1063,6 +1130,11 @@ void Suggestions::setupChannels() {
anim::type::instant);
_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) {
@ -1371,6 +1443,9 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupRecentPeers(
controller->setStyleOverrides(&st::recentPeersList);
_recentCount = controller->count();
_recentProcessTouch = [=](not_null<QTouchEvent*> e) {
return controller->processTouchEvent(e);
};
controller->chosen(
) | rpl::start_with_next([=](not_null<PeerData*> peer) {
@ -1419,6 +1494,7 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupRecentPeers(
delegate->setContent(raw);
controller->setDelegate(delegate);
controller->setupTouchChatPreview(_chatsScroll.get());
return object_ptr<Ui::SlideWrap<>>(this, std::move(content));
}
@ -1474,6 +1550,9 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupMyChannels() {
controller->setStyleOverrides(&st::recentPeersList);
_myChannelsCount = controller->count();
_myChannelsProcessTouch = [=](not_null<QTouchEvent*> e) {
return controller->processTouchEvent(e);
};
controller->chosen(
) | rpl::start_with_next([=](not_null<PeerData*> peer) {
@ -1535,6 +1614,7 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupMyChannels() {
delegate->setContent(raw);
controller->setDelegate(delegate);
controller->setupTouchChatPreview(_channelsScroll.get());
return object_ptr<Ui::SlideWrap<>>(this, std::move(content));
}
@ -1549,6 +1629,9 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupRecommendations() {
controller->setStyleOverrides(&st::recentPeersList);
_recommendationsCount = controller->count();
_recommendationsProcessTouch = [=](not_null<QTouchEvent*> e) {
return controller->processTouchEvent(e);
};
_tab.value() | rpl::filter(
rpl::mappers::_1 == Tab::Channels
@ -1603,6 +1686,7 @@ object_ptr<Ui::SlideWrap<>> Suggestions::setupRecommendations() {
delegate->setContent(raw);
controller->setDelegate(delegate);
controller->setupTouchChatPreview(_channelsScroll.get());
return object_ptr<Ui::SlideWrap<>>(this, std::move(content));
}

View File

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

View File

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

View File

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

View File

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

View File

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