Empty preview widget on long userpic press.

This commit is contained in:
John Preston 2024-05-02 19:39:36 +04:00
parent ef2aa05197
commit 4427ae4306
6 changed files with 234 additions and 7 deletions

View file

@ -781,6 +781,8 @@ PRIVATE
history/view/history_view_about_view.h
history/view/history_view_bottom_info.cpp
history/view/history_view_bottom_info.h
history/view/history_view_chat_preview.cpp
history/view/history_view_chat_preview.h
history/view/history_view_contact_status.cpp
history/view/history_view_contact_status.h
history/view/history_view_context_menu.cpp

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "dialogs/dialogs_widget.h"
#include "dialogs/dialogs_search_from_controllers.h"
#include "dialogs/dialogs_search_tags.h"
#include "history/view/history_view_chat_preview.h"
#include "history/view/history_view_context_menu.h"
#include "history/history.h"
#include "history/history_item.h"
@ -80,6 +81,7 @@ namespace {
constexpr auto kHashtagResultsLimit = 5;
constexpr auto kStartReorderThreshold = 30;
constexpr auto kChatPreviewDelay = crl::time(1000);
int FixedOnTopDialogsCount(not_null<Dialogs::IndexedList*> list) {
auto result = 0;
@ -157,6 +159,7 @@ InnerWidget::InnerWidget(
+ st::defaultDialogRow.padding.left())
, _cancelSearchInChat(this, st::dialogsCancelSearchInPeer)
, _cancelSearchFromUser(this, st::dialogsCancelSearchInPeer)
, _chatPreviewTimer([=] { showChatPreview(); })
, _childListShown(std::move(childListShown)) {
setAttribute(Qt::WA_OpaquePaintEvent, true);
@ -651,6 +654,8 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
context.active = active;
context.selected = _menuRow.key
? (row->key() == _menuRow.key)
: _chatPreviewKey
? (row->key() == _chatPreviewKey)
: selected;
context.topicJumpSelected = selected
&& _selectedTopicJump
@ -1268,6 +1273,14 @@ void InnerWidget::mouseMoveEvent(QMouseEvent *e) {
return;
}
selectByMouse(globalPosition);
if (!isUserpicPress()) {
cancelChatPreview();
}
}
void InnerWidget::cancelChatPreview() {
_chatPreviewTimer.cancel();
_chatPreviewWillBeFor = {};
}
void InnerWidget::clearIrrelevantState() {
@ -1490,11 +1503,15 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
row->repaint());
}
ClickHandler::pressed();
if (anim::Disabled()
if (pressShowsPreview()) {
_chatPreviewWillBeFor = computeChosenRow().key;
_chatPreviewTimer.callOnce(kChatPreviewDelay);
} else if (anim::Disabled()
&& (!_pressed || !_pressed->entry()->isPinnedDialog(_filterId))) {
mousePressReleased(e->globalPos(), e->button(), e->modifiers());
}
}
const std::vector<Key> &InnerWidget::pinnedChatsOrder() const {
const auto owner = &session().data();
return _savedSublists
@ -1513,6 +1530,7 @@ void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
< style::ConvertScale(kStartReorderThreshold)) {
return;
}
cancelChatPreview();
_dragging = _pressed;
if (updateReorderIndexGetCount() < 2) {
_dragging = nullptr;
@ -2288,6 +2306,33 @@ void InnerWidget::fillArchiveSearchMenu(not_null<Ui::PopupMenu*> menu) {
});
}
void InnerWidget::showChatPreview() {
const auto key = base::take(_chatPreviewWillBeFor);
cancelChatPreview();
if (!pressShowsPreview() || key != computeChosenRow().key) {
return;
}
ClickHandler::unpressed();
mousePressReleased(QCursor::pos(), Qt::NoButton, Qt::NoModifier);
_chatPreviewKey = key;
_menu = HistoryView::MakeChatPreview(this, key.entry());
if (!_menu) {
return;
}
QObject::connect(_menu.get(), &QObject::destroyed, [=] {
if (_chatPreviewKey) {
updateDialogRow(RowDescriptor(base::take(_chatPreviewKey), {}));
}
const auto globalPosition = QCursor::pos();
if (rect().contains(mapFromGlobal(globalPosition))) {
setMouseTracking(true);
selectByMouse(globalPosition);
}
});
_menu->popup(_lastMousePosition.value_or(QCursor::pos()));
}
void InnerWidget::contextMenuEvent(QContextMenuEvent *e) {
_menu = nullptr;
@ -2316,7 +2361,9 @@ void InnerWidget::contextMenuEvent(QContextMenuEvent *e) {
}
return RowDescriptor();
}();
if (!row.key) return;
if (!row.key) {
return;
}
_menuRow = row;
if (_pressButton != Qt::LeftButton) {
@ -2555,6 +2602,12 @@ void InnerWidget::trackSearchResultsHistory(not_null<History*> history) {
refresh();
clearMouseSelection(true);
}
if (_chatPreviewWillBeFor.topic() == topic) {
_chatPreviewWillBeFor = {};
}
if (_chatPreviewKey.topic() == topic) {
_chatPreviewKey = {};
}
}, _searchResultsLifetime);
}
}
@ -3499,6 +3552,23 @@ ChosenRow InnerWidget::computeChosenRow() const {
return ChosenRow();
}
bool InnerWidget::isUserpicPress() const {
return (_lastRowLocalMouseX >= 0)
&& (_lastRowLocalMouseX < _st->nameLeft)
&& (width() > _narrowWidth);
}
bool InnerWidget::pressShowsPreview() const {
if (!isUserpicPress()) {
return false;
}
const auto key = computeChosenRow().key;
if (const auto history = key.history()) {
return !history->peer->isForum();
}
return key.topic() != nullptr;;
}
bool InnerWidget::chooseRow(
Qt::KeyboardModifiers modifiers,
MsgId pressedTopicRootId) {
@ -3511,9 +3581,7 @@ bool InnerWidget::chooseRow(
ChosenRow row,
Qt::KeyboardModifiers modifiers) {
row.newWindow = (modifiers & Qt::ControlModifier);
row.userpicClick = (_lastRowLocalMouseX >= 0)
&& (_lastRowLocalMouseX < _st->nameLeft)
&& (width() > _narrowWidth);
row.userpicClick = isUserpicPress();
return row;
};
auto chosen = modifyChosenRow(computeChosenRow(), modifiers);

View file

@ -7,14 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/flags.h"
#include "base/object_ptr.h"
#include "base/timer.h"
#include "dialogs/dialogs_key.h"
#include "data/data_messages.h"
#include "ui/dragging_scroll_manager.h"
#include "ui/effects/animations.h"
#include "ui/rp_widget.h"
#include "ui/userpic_view.h"
#include "base/flags.h"
#include "base/object_ptr.h"
namespace style {
struct DialogRow;
@ -121,6 +122,10 @@ public:
void refreshEmptyLabel();
void resizeEmptyLabel();
[[nodiscard]] bool isUserpicPress() const;
[[nodiscard]] bool pressShowsPreview() const;
void cancelChatPreview();
void showChatPreview();
bool chooseRow(
Qt::KeyboardModifiers modifiers = {},
MsgId pressedTopicRootId = {});
@ -514,6 +519,10 @@ private:
rpl::event_stream<QString> _completeHashtagRequests;
rpl::event_stream<> _refreshHashtagsRequests;
base::Timer _chatPreviewTimer;
Key _chatPreviewWillBeFor;
Key _chatPreviewKey;
rpl::variable<ChildListShown> _childListShown;
float64 _narrowRatio = 0.;
bool _geometryInited = false;

View file

@ -0,0 +1,113 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/history_view_chat_preview.h"
#include "data/data_peer.h"
#include "history/history.h"
#include "ui/chat/chat_theme.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/menu/menu_item_base.h"
#include "window/themes/window_theme.h"
#include "window/section_widget.h"
#include "styles/style_chat.h"
namespace HistoryView {
namespace {
class Item final : public Ui::Menu::ItemBase {
public:
Item(not_null<Ui::RpWidget*> parent, not_null<History*> history);
not_null<QAction*> action() const override;
bool isEnabled() const override;
private:
void setupBackground();
int contentHeight() const override;
void paintEvent(QPaintEvent *e) override;
const not_null<QAction*> _dummyAction;
const std::shared_ptr<Ui::ChatTheme> _theme;
QImage _bg;
};
Item::Item(not_null<Ui::RpWidget*> parent, not_null<History*> history)
: Ui::Menu::ItemBase(parent, st::previewMenu.menu)
, _dummyAction(new QAction(parent))
, _theme(Window::Theme::DefaultChatThemeOn(lifetime())) {
setPointerCursor(false);
setMinWidth(st::previewMenu.menu.widthMin);
resize(minWidth(), contentHeight());
setupBackground();
}
not_null<QAction*> Item::action() const {
return _dummyAction;
}
bool Item::isEnabled() const {
return false;
}
int Item::contentHeight() const {
return st::previewMenu.maxHeight;
}
void Item::setupBackground() {
const auto ratio = style::DevicePixelRatio();
_bg = QImage(
size() * ratio,
QImage::Format_ARGB32_Premultiplied);
const auto paint = [=] {
auto p = QPainter(&_bg);
Window::SectionWidget::PaintBackground(
p,
_theme.get(),
QSize(width(), height() * 2),
QRect(QPoint(), size()));
};
paint();
_theme->repaintBackgroundRequests() | rpl::start_with_next([=] {
paint();
update();
}, lifetime());
}
void Item::paintEvent(QPaintEvent *e) {
auto p = QPainter(this);
p.drawImage(0, 0, _bg);
}
} // namespace
base::unique_qptr<Ui::PopupMenu> MakeChatPreview(
QWidget *parent,
not_null<Dialogs::Entry*> entry) {
if (const auto topic = entry->asTopic()) {
return nullptr;
}
const auto history = entry->asHistory();
if (!history || history->peer->isForum()) {
return nullptr;
}
auto result = base::make_unique_q<Ui::PopupMenu>(
parent,
st::previewMenu);
result->addAction(base::make_unique_q<Item>(result.get(), history));
return result;
}
} // namespace HistoryView

View file

@ -0,0 +1,26 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/unique_qptr.h"
namespace Dialogs {
class Entry;
} // namespace Dialogs
namespace Ui {
class PopupMenu;
} // namespace Ui
namespace HistoryView {
[[nodiscard]] base::unique_qptr<Ui::PopupMenu> MakeChatPreview(
QWidget *parent,
not_null<Dialogs::Entry*> entry);
} // namespace HistoryView

View file

@ -1076,3 +1076,12 @@ liveLocationLongInIconSelected: icon {{ "chat/live_location_long", msgInServiceF
liveLocationLongOutIcon: icon {{ "chat/live_location_long", msgOutServiceFg }};
liveLocationLongOutIconSelected: icon {{ "chat/live_location_long", msgOutServiceFgSelected }};
liveLocationRemainingSize: 28px;
previewMenu: PopupMenu(defaultPopupMenu) {
scrollPadding: margins(0px, 0px, 0px, 0px);
menu: Menu(defaultMenu) {
widthMin: 380px;
widthMax: 380px;
}
maxHeight: 420px;
}