mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Show long-press preview of topics.
This commit is contained in:
parent
4427ae4306
commit
da31fef1ae
4 changed files with 455 additions and 13 deletions
|
@ -7,10 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "history/view/history_view_chat_preview.h"
|
||||
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_replies_list.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "history/view/reactions/history_view_reactions_button.h"
|
||||
#include "history/view/history_view_list_widget.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/elastic_scroll.h"
|
||||
#include "ui/widgets/menu/menu_item_base.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/section_widget.h"
|
||||
|
@ -19,35 +28,159 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
class Item final : public Ui::Menu::ItemBase {
|
||||
class Item final
|
||||
: public Ui::Menu::ItemBase
|
||||
, private HistoryView::ListDelegate {
|
||||
public:
|
||||
Item(not_null<Ui::RpWidget*> parent, not_null<History*> history);
|
||||
Item(not_null<Ui::RpWidget*> parent, not_null<Data::Thread*> thread);
|
||||
|
||||
not_null<QAction*> action() const override;
|
||||
bool isEnabled() const override;
|
||||
|
||||
private:
|
||||
void setupBackground();
|
||||
|
||||
int contentHeight() const override;
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
void setupBackground();
|
||||
void updateInnerVisibleArea();
|
||||
|
||||
Context listContext() override;
|
||||
bool listScrollTo(int top, bool syntetic = true) override;
|
||||
void listCancelRequest() override;
|
||||
void listDeleteRequest() override;
|
||||
void listTryProcessKeyInput(not_null<QKeyEvent*> e) override;
|
||||
rpl::producer<Data::MessagesSlice> listSource(
|
||||
Data::MessagePosition aroundId,
|
||||
int limitBefore,
|
||||
int limitAfter) override;
|
||||
bool listAllowsMultiSelect() override;
|
||||
bool listIsItemGoodForSelection(not_null<HistoryItem*> item) override;
|
||||
bool listIsLessInOrder(
|
||||
not_null<HistoryItem*> first,
|
||||
not_null<HistoryItem*> second) override;
|
||||
void listSelectionChanged(SelectedItems &&items) override;
|
||||
void listMarkReadTill(not_null<HistoryItem*> item) override;
|
||||
void listMarkContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) override;
|
||||
MessagesBarData listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) override;
|
||||
void listContentRefreshed() override;
|
||||
void listUpdateDateLink(
|
||||
ClickHandlerPtr &link,
|
||||
not_null<Element*> view) override;
|
||||
bool listElementHideReply(not_null<const Element*> view) override;
|
||||
bool listElementShownUnread(not_null<const Element*> view) override;
|
||||
bool listIsGoodForAroundPosition(
|
||||
not_null<const Element*> view) override;
|
||||
void listSendBotCommand(
|
||||
const QString &command,
|
||||
const FullMsgId &context) override;
|
||||
void listSearch(
|
||||
const QString &query,
|
||||
const FullMsgId &context) override;
|
||||
void listHandleViaClick(not_null<UserData*> bot) override;
|
||||
not_null<Ui::ChatTheme*> listChatTheme() override;
|
||||
CopyRestrictionType listCopyRestrictionType(
|
||||
HistoryItem *item) override;
|
||||
CopyRestrictionType listCopyRestrictionType() {
|
||||
return listCopyRestrictionType(nullptr);
|
||||
}
|
||||
CopyRestrictionType listCopyMediaRestrictionType(
|
||||
not_null<HistoryItem*> item) override;
|
||||
CopyRestrictionType listSelectRestrictionType() override;
|
||||
auto listAllowedReactionsValue()
|
||||
-> rpl::producer<Data::AllowedReactions> override;
|
||||
void listShowPremiumToast(not_null<DocumentData*> document) override;
|
||||
void listOpenPhoto(
|
||||
not_null<PhotoData*> photo,
|
||||
FullMsgId context) override;
|
||||
void listOpenDocument(
|
||||
not_null<DocumentData*> document,
|
||||
FullMsgId context,
|
||||
bool showInMediaView) override;
|
||||
void listPaintEmpty(
|
||||
Painter &p,
|
||||
const Ui::ChatPaintContext &context) override;
|
||||
QString listElementAuthorRank(not_null<const Element*> view) override;
|
||||
History *listTranslateHistory() override;
|
||||
void listAddTranslatedItems(
|
||||
not_null<TranslateTracker*> tracker) override;
|
||||
not_null<Window::SessionController*> listWindow() override;
|
||||
not_null<const Ui::ChatStyle*> listChatStyle() override;
|
||||
rpl::producer<bool> listChatWideValue() override;
|
||||
std::unique_ptr<Reactions::Manager> listMakeReactionsManager(
|
||||
QWidget *wheelEventsTarget,
|
||||
Fn<void(QRect)> update) override;
|
||||
void listVisibleAreaUpdated() override;
|
||||
std::shared_ptr<Ui::Show> listUiShow() override;
|
||||
void listShowPollResults(
|
||||
not_null<PollData*> poll,
|
||||
FullMsgId context) override;
|
||||
void listCancelUploadLayer(not_null<HistoryItem*> item) override;
|
||||
bool listAnimationsPaused() override;
|
||||
auto listSendingAnimation()
|
||||
-> Ui::MessageSendingAnimationController* override;
|
||||
Ui::ChatPaintContext listPreparePaintContext(
|
||||
Ui::ChatPaintContextArgs &&args) override;
|
||||
bool listMarkingContentRead() override;
|
||||
bool listIgnorePaintEvent(QWidget *w, QPaintEvent *e) override;
|
||||
bool listShowReactPremiumError(
|
||||
not_null<HistoryItem*> item,
|
||||
const Data::ReactionId &id) override;
|
||||
void listWindowSetInnerFocus() override;
|
||||
bool listAllowsDragForward() override;
|
||||
void listLaunchDrag(
|
||||
std::unique_ptr<QMimeData> data,
|
||||
Fn<void()> finished) override;
|
||||
|
||||
const not_null<QAction*> _dummyAction;
|
||||
const not_null<Main::Session*> _session;
|
||||
const not_null<Data::Thread*> _thread;
|
||||
const not_null<History*> _history;
|
||||
const not_null<PeerData*> _peer;
|
||||
const std::shared_ptr<Ui::ChatTheme> _theme;
|
||||
const std::unique_ptr<Ui::ChatStyle> _chatStyle;
|
||||
const std::unique_ptr<Ui::ElasticScroll> _scroll;
|
||||
|
||||
QPointer<HistoryView::ListWidget> _inner;
|
||||
|
||||
QImage _bg;
|
||||
|
||||
};
|
||||
|
||||
Item::Item(not_null<Ui::RpWidget*> parent, not_null<History*> history)
|
||||
Item::Item(not_null<Ui::RpWidget*> parent, not_null<Data::Thread*> thread)
|
||||
: Ui::Menu::ItemBase(parent, st::previewMenu.menu)
|
||||
, _dummyAction(new QAction(parent))
|
||||
, _theme(Window::Theme::DefaultChatThemeOn(lifetime())) {
|
||||
, _session(&thread->session())
|
||||
, _thread(thread)
|
||||
, _history(thread->owningHistory())
|
||||
, _peer(thread->peer())
|
||||
, _theme(Window::Theme::DefaultChatThemeOn(lifetime()))
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>(_session->colorIndicesValue()))
|
||||
, _scroll(std::make_unique<Ui::ElasticScroll>(this)) {
|
||||
setPointerCursor(false);
|
||||
setMinWidth(st::previewMenu.menu.widthMin);
|
||||
resize(minWidth(), contentHeight());
|
||||
setupBackground();
|
||||
|
||||
_inner = _scroll->setOwnedWidget(object_ptr<ListWidget>(
|
||||
this,
|
||||
_session,
|
||||
static_cast<ListDelegate*>(this)));
|
||||
_scroll->setGeometry(rect());
|
||||
_scroll->scrolls(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateInnerVisibleArea();
|
||||
}, lifetime());
|
||||
_scroll->setOverscrollBg(QColor(0, 0, 0, 0));
|
||||
using Type = Ui::ElasticScroll::OverscrollType;
|
||||
_scroll->setOverscrollTypes(Type::Real, Type::Real);
|
||||
|
||||
_inner->resizeToWidth(_scroll->width(), _scroll->height());
|
||||
|
||||
_inner->refreshViewer();
|
||||
|
||||
_inner->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}
|
||||
|
||||
not_null<QAction*> Item::action() const {
|
||||
|
@ -88,24 +221,328 @@ void Item::paintEvent(QPaintEvent *e) {
|
|||
p.drawImage(0, 0, _bg);
|
||||
}
|
||||
|
||||
void Item::updateInnerVisibleArea() {
|
||||
const auto scrollTop = _scroll->scrollTop();
|
||||
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
|
||||
}
|
||||
|
||||
Context Item::listContext() {
|
||||
return Context::ChatPreview;
|
||||
}
|
||||
|
||||
bool Item::listScrollTo(int top, bool syntetic) {
|
||||
top = std::clamp(top, 0, _scroll->scrollTopMax());
|
||||
if (_scroll->scrollTop() == top) {
|
||||
updateInnerVisibleArea();
|
||||
return false;
|
||||
}
|
||||
_scroll->scrollToY(top);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Item::listCancelRequest() {
|
||||
}
|
||||
|
||||
void Item::listDeleteRequest() {
|
||||
}
|
||||
|
||||
void Item::listTryProcessKeyInput(not_null<QKeyEvent*> e) {
|
||||
}
|
||||
|
||||
rpl::producer<Data::MessagesSlice> Item::listSource(
|
||||
Data::MessagePosition aroundId,
|
||||
int limitBefore,
|
||||
int limitAfter) {
|
||||
if (const auto topic = _thread->asTopic()) {
|
||||
return topic->replies()->source(
|
||||
aroundId,
|
||||
limitBefore,
|
||||
limitAfter
|
||||
) | rpl::before_next([=] { // after_next makes a copy of value.
|
||||
//if (!_loaded) {
|
||||
// _loaded = true;
|
||||
// crl::on_main(this, [=] {
|
||||
// updatePinnedVisibility();
|
||||
// });
|
||||
//}
|
||||
});
|
||||
}
|
||||
// #TODO
|
||||
//const auto messageId = aroundId.fullId.msg
|
||||
// ? aroundId.fullId.msg
|
||||
// : (ServerMaxMsgId - 1);
|
||||
|
||||
//return SharedMediaMergedViewer(
|
||||
// &_thread->session(),
|
||||
// SharedMediaMergedKey(
|
||||
// SparseIdsMergedSlice::Key(
|
||||
// _history->peer->id,
|
||||
// _thread->topicRootId(),
|
||||
// _migratedPeer ? _migratedPeer->id : 0,
|
||||
// messageId),
|
||||
// Storage::SharedMediaType::Pinned),
|
||||
// limitBefore,
|
||||
// limitAfter
|
||||
//) | rpl::map([=](SparseIdsMergedSlice &&slice) {
|
||||
// auto result = Data::MessagesSlice();
|
||||
// result.fullCount = slice.fullCount();
|
||||
// result.skippedAfter = slice.skippedAfter();
|
||||
// result.skippedBefore = slice.skippedBefore();
|
||||
// const auto count = slice.size();
|
||||
// result.ids.reserve(count);
|
||||
// if (const auto msgId = slice.nearest(messageId)) {
|
||||
// result.nearestToAround = *msgId;
|
||||
// }
|
||||
// for (auto i = 0; i != count; ++i) {
|
||||
// result.ids.push_back(slice[i]);
|
||||
// }
|
||||
// return result;
|
||||
//});
|
||||
return rpl::single(Data::MessagesSlice());
|
||||
}
|
||||
|
||||
bool Item::listAllowsMultiSelect() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Item::listIsItemGoodForSelection(not_null<HistoryItem*> item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Item::listIsLessInOrder(
|
||||
not_null<HistoryItem*> first,
|
||||
not_null<HistoryItem*> second) {
|
||||
if (first->isRegular() && second->isRegular()) {
|
||||
const auto firstPeer = first->history()->peer;
|
||||
const auto secondPeer = second->history()->peer;
|
||||
if (firstPeer == secondPeer) {
|
||||
return first->id < second->id;
|
||||
} else if (firstPeer->isChat()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else if (first->isRegular()) {
|
||||
return true;
|
||||
} else if (second->isRegular()) {
|
||||
return false;
|
||||
}
|
||||
return first->id < second->id;
|
||||
}
|
||||
|
||||
void Item::listSelectionChanged(SelectedItems &&items) {
|
||||
}
|
||||
|
||||
void Item::listMarkReadTill(not_null<HistoryItem*> item) {
|
||||
}
|
||||
|
||||
void Item::listMarkContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) {
|
||||
}
|
||||
|
||||
MessagesBarData Item::listMessagesBar(
|
||||
const std::vector<not_null<Element*>> &elements) {
|
||||
return {};// #TODO
|
||||
}
|
||||
|
||||
void Item::listContentRefreshed() {
|
||||
}
|
||||
|
||||
void Item::listUpdateDateLink(
|
||||
ClickHandlerPtr &link,
|
||||
not_null<Element*> view) {
|
||||
}
|
||||
|
||||
bool Item::listElementHideReply(not_null<const Element*> view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Item::listElementShownUnread(not_null<const Element*> view) {
|
||||
return view->data()->unread(view->data()->history());
|
||||
}
|
||||
|
||||
bool Item::listIsGoodForAroundPosition(not_null<const Element*> view) {
|
||||
return view->data()->isRegular();
|
||||
}
|
||||
|
||||
void Item::listSendBotCommand(
|
||||
const QString &command,
|
||||
const FullMsgId &context) {
|
||||
}
|
||||
|
||||
void Item::listSearch(
|
||||
const QString &query,
|
||||
const FullMsgId &context) {
|
||||
}
|
||||
|
||||
void Item::listHandleViaClick(not_null<UserData*> bot) {
|
||||
}
|
||||
|
||||
not_null<Ui::ChatTheme*> Item::listChatTheme() {
|
||||
return _theme.get();
|
||||
}
|
||||
|
||||
CopyRestrictionType Item::listCopyRestrictionType(HistoryItem *item) {
|
||||
return CopyRestrictionType::None;
|
||||
}
|
||||
|
||||
CopyRestrictionType Item::listCopyMediaRestrictionType(
|
||||
not_null<HistoryItem*> item) {
|
||||
return CopyRestrictionType::None;
|
||||
}
|
||||
|
||||
CopyRestrictionType Item::listSelectRestrictionType() {
|
||||
return CopyRestrictionType::None;
|
||||
}
|
||||
|
||||
auto Item::listAllowedReactionsValue()
|
||||
-> rpl::producer<Data::AllowedReactions> {
|
||||
return rpl::single(Data::AllowedReactions());
|
||||
}
|
||||
|
||||
void Item::listShowPremiumToast(not_null<DocumentData*> document) {
|
||||
}
|
||||
|
||||
void Item::listOpenPhoto(
|
||||
not_null<PhotoData*> photo,
|
||||
FullMsgId context) {
|
||||
}
|
||||
|
||||
void Item::listOpenDocument(
|
||||
not_null<DocumentData*> document,
|
||||
FullMsgId context,
|
||||
bool showInMediaView) {
|
||||
}
|
||||
|
||||
void Item::listPaintEmpty(
|
||||
Painter &p,
|
||||
const Ui::ChatPaintContext &context) {
|
||||
// #TODO
|
||||
}
|
||||
|
||||
QString Item::listElementAuthorRank(not_null<const Element*> view) {
|
||||
return {};
|
||||
}
|
||||
|
||||
History *Item::listTranslateHistory() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Item::listAddTranslatedItems(
|
||||
not_null<TranslateTracker*> tracker) {
|
||||
}
|
||||
|
||||
not_null<Window::SessionController*> Item::listWindow() {
|
||||
Unexpected("Item::listWindow.");
|
||||
}
|
||||
|
||||
not_null<const Ui::ChatStyle*> Item::listChatStyle() {
|
||||
return _chatStyle.get();
|
||||
}
|
||||
|
||||
rpl::producer<bool> Item::listChatWideValue() {
|
||||
return rpl::single(false);
|
||||
}
|
||||
|
||||
std::unique_ptr<Reactions::Manager> Item::listMakeReactionsManager(
|
||||
QWidget *wheelEventsTarget,
|
||||
Fn<void(QRect)> update) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Item::listVisibleAreaUpdated() {
|
||||
}
|
||||
|
||||
std::shared_ptr<Ui::Show> Item::listUiShow() {
|
||||
Unexpected("Item::listUiShow.");
|
||||
}
|
||||
|
||||
void Item::listShowPollResults(
|
||||
not_null<PollData*> poll,
|
||||
FullMsgId context) {
|
||||
}
|
||||
|
||||
void Item::listCancelUploadLayer(not_null<HistoryItem*> item) {
|
||||
}
|
||||
|
||||
bool Item::listAnimationsPaused() {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Item::listSendingAnimation()
|
||||
-> Ui::MessageSendingAnimationController* {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ui::ChatPaintContext Item::listPreparePaintContext(
|
||||
Ui::ChatPaintContextArgs &&args) {
|
||||
const auto visibleAreaTopLocal = mapFromGlobal(
|
||||
args.visibleAreaPositionGlobal).y();
|
||||
const auto viewport = QRect(
|
||||
0,
|
||||
args.visibleAreaTop - visibleAreaTopLocal,
|
||||
args.visibleAreaWidth,
|
||||
height());
|
||||
return args.theme->preparePaintContext(
|
||||
_chatStyle.get(),
|
||||
viewport,
|
||||
args.clip,
|
||||
false);
|
||||
}
|
||||
|
||||
bool Item::listMarkingContentRead() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Item::listIgnorePaintEvent(QWidget *w, QPaintEvent *e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Item::listShowReactPremiumError(
|
||||
not_null<HistoryItem*> item,
|
||||
const Data::ReactionId &id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Item::listWindowSetInnerFocus() {
|
||||
}
|
||||
|
||||
bool Item::listAllowsDragForward() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Item::listLaunchDrag(
|
||||
std::unique_ptr<QMimeData> data,
|
||||
Fn<void()> finished) {
|
||||
}
|
||||
|
||||
} // 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()) {
|
||||
const auto thread = entry->asThread();
|
||||
if (!thread) {
|
||||
return nullptr;
|
||||
} else if (const auto history = entry->asHistory()) {
|
||||
if (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));
|
||||
result->addAction(base::make_unique_q<Item>(result.get(), thread));
|
||||
if (const auto topic = thread->asTopic()) {
|
||||
const auto weak = Ui::MakeWeak(result.get());
|
||||
topic->destroyed() | rpl::start_with_next([weak] {
|
||||
if (const auto strong = weak.data()) {
|
||||
LOG(("Preview hidden for a destroyed topic."));
|
||||
strong->hideMenu(true);
|
||||
}
|
||||
}, result->lifetime());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ enum class Context : char {
|
|||
TTLViewer,
|
||||
ShortcutMessages,
|
||||
ScheduledTopic,
|
||||
ChatPreview,
|
||||
};
|
||||
|
||||
enum class OnlyEmojiAndSpaces : char {
|
||||
|
|
|
@ -2081,6 +2081,7 @@ bool Message::hasFromPhoto() const {
|
|||
case Context::AdminLog:
|
||||
return true;
|
||||
case Context::History:
|
||||
case Context::ChatPreview:
|
||||
case Context::TTLViewer:
|
||||
case Context::Pinned:
|
||||
case Context::Replies:
|
||||
|
@ -3281,6 +3282,7 @@ bool Message::hasFromName() const {
|
|||
case Context::AdminLog:
|
||||
return true;
|
||||
case Context::History:
|
||||
case Context::ChatPreview:
|
||||
case Context::TTLViewer:
|
||||
case Context::Pinned:
|
||||
case Context::Replies:
|
||||
|
|
|
@ -1084,4 +1084,6 @@ previewMenu: PopupMenu(defaultPopupMenu) {
|
|||
widthMax: 380px;
|
||||
}
|
||||
maxHeight: 420px;
|
||||
radius: boxRadius;
|
||||
shadow: boxRoundShadow;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue