mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-20 08:07:09 +02:00
Implement preview top and bottom.
This commit is contained in:
parent
de73d8766c
commit
a60783eae3
6 changed files with 289 additions and 43 deletions
|
@ -2341,7 +2341,15 @@ void InnerWidget::showChatPreview(bool onlyUserpic) {
|
|||
if (const auto thread = weakThread.get()) {
|
||||
const auto itemId = action.openItemId;
|
||||
const auto owner = &thread->owner();
|
||||
if (action.openInfo) {
|
||||
if (action.markRead) {
|
||||
Window::MarkAsReadThread(thread);
|
||||
} else if (action.markUnread) {
|
||||
if (const auto history = thread->asHistory()) {
|
||||
history->owner().histories().changeDialogUnreadMark(
|
||||
history,
|
||||
true);
|
||||
}
|
||||
} else if (action.openInfo) {
|
||||
controller->showPeerInfo(thread);
|
||||
} else if (const auto item = owner->message(itemId)) {
|
||||
controller->showMessage(item);
|
||||
|
|
|
@ -7,26 +7,39 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "history/view/history_view_chat_preview.h"
|
||||
|
||||
#include "base/unixtime.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_history_messages.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "data/data_replies_list.h"
|
||||
#include "data/data_session.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 "info/profile/info_profile_cover.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "lang/lang_keys.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/controls/userpic_button.h"
|
||||
#include "ui/widgets/menu/menu_item_base.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/elastic_scroll.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/section_widget.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
@ -48,7 +61,10 @@ private:
|
|||
int contentHeight() const override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
void setupTop();
|
||||
void setupMarkRead();
|
||||
void setupBackground();
|
||||
void setupHistory();
|
||||
void updateInnerVisibleArea();
|
||||
|
||||
Context listContext() override;
|
||||
|
@ -148,7 +164,9 @@ private:
|
|||
const not_null<PeerData*> _peer;
|
||||
const std::shared_ptr<Ui::ChatTheme> _theme;
|
||||
const std::unique_ptr<Ui::ChatStyle> _chatStyle;
|
||||
const std::unique_ptr<Ui::AbstractButton> _top;
|
||||
const std::unique_ptr<Ui::ElasticScroll> _scroll;
|
||||
const std::unique_ptr<Ui::FlatButton> _markRead;
|
||||
|
||||
QPointer<HistoryView::ListWidget> _inner;
|
||||
rpl::event_stream<ChatPreviewAction> _actions;
|
||||
|
@ -157,6 +175,56 @@ private:
|
|||
|
||||
};
|
||||
|
||||
struct StatusFields {
|
||||
QString text;
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] rpl::producer<StatusFields> StatusValue(
|
||||
not_null<PeerData*> peer) {
|
||||
peer->updateFull();
|
||||
|
||||
using UpdateFlag = Data::PeerUpdate::Flag;
|
||||
return peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
UpdateFlag::OnlineStatus | UpdateFlag::Members
|
||||
) | rpl::map([=](const Data::PeerUpdate &update)
|
||||
-> StatusFields {
|
||||
const auto wrap = [](QString text) {
|
||||
return StatusFields{ .text = text };
|
||||
};
|
||||
if (const auto user = peer->asUser()) {
|
||||
const auto now = base::unixtime::now();
|
||||
return {
|
||||
.text = Data::OnlineText(user, now),
|
||||
.active = Data::OnlineTextActive(user, now),
|
||||
};
|
||||
} else if (const auto chat = peer->asChat()) {
|
||||
return wrap(!chat->amIn()
|
||||
? tr::lng_chat_status_unaccessible(tr::now)
|
||||
: (chat->count <= 0)
|
||||
? tr::lng_group_status(tr::now)
|
||||
: tr::lng_chat_status_members(
|
||||
tr::now,
|
||||
lt_count_decimal,
|
||||
chat->count));
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
return wrap((channel->membersCount() > 0)
|
||||
? ((channel->isMegagroup()
|
||||
? tr::lng_chat_status_members
|
||||
: tr::lng_chat_status_subscribers)(
|
||||
tr::now,
|
||||
lt_count_decimal,
|
||||
channel->membersCount()))
|
||||
: (channel->isMegagroup()
|
||||
? tr::lng_group_status(tr::now)
|
||||
: tr::lng_channel_status(tr::now)));
|
||||
}
|
||||
Unexpected("Peer type in ChatPreview Item.");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Item::Item(not_null<Ui::RpWidget*> parent, not_null<Data::Thread*> thread)
|
||||
: Ui::Menu::ItemBase(parent, st::previewMenu.menu)
|
||||
, _dummyAction(new QAction(parent))
|
||||
|
@ -167,17 +235,187 @@ Item::Item(not_null<Ui::RpWidget*> parent, not_null<Data::Thread*> thread)
|
|||
, _peer(thread->peer())
|
||||
, _theme(Window::Theme::DefaultChatThemeOn(lifetime()))
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>(_session->colorIndicesValue()))
|
||||
, _scroll(std::make_unique<Ui::ElasticScroll>(this)) {
|
||||
, _top(std::make_unique<Ui::AbstractButton>(this))
|
||||
, _scroll(std::make_unique<Ui::ElasticScroll>(this))
|
||||
, _markRead(
|
||||
std::make_unique<Ui::FlatButton>(
|
||||
this,
|
||||
tr::lng_context_mark_read(tr::now),
|
||||
st::previewMarkRead)) {
|
||||
setPointerCursor(false);
|
||||
setMinWidth(st::previewMenu.menu.widthMin);
|
||||
resize(minWidth(), contentHeight());
|
||||
setupTop();
|
||||
setupMarkRead();
|
||||
setupBackground();
|
||||
setupHistory();
|
||||
}
|
||||
|
||||
not_null<QAction*> Item::action() const {
|
||||
return _dummyAction;
|
||||
}
|
||||
|
||||
bool Item::isEnabled() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
int Item::contentHeight() const {
|
||||
return st::previewMenu.maxHeight;
|
||||
}
|
||||
|
||||
void Item::setupTop() {
|
||||
_top->setGeometry(0, 0, width(), st::previewTop.height);
|
||||
_top->setClickedCallback([=] {
|
||||
_actions.fire({ .openInfo = true });
|
||||
});
|
||||
_top->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||
const auto &st = st::previewTop;
|
||||
auto p = QPainter(_top.get());
|
||||
p.fillRect(clip, st::topBarBg);
|
||||
}, _top->lifetime());
|
||||
|
||||
const auto topic = _thread->asTopic();
|
||||
const auto name = Ui::CreateChild<Ui::FlatLabel>(
|
||||
_top.get(),
|
||||
(topic
|
||||
? Info::Profile::TitleValue(topic)
|
||||
: Info::Profile::NameValue(_thread->peer())),
|
||||
st::previewName);
|
||||
name->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
auto statusFields = StatusValue(
|
||||
_thread->peer()
|
||||
) | rpl::start_spawning(lifetime());
|
||||
auto statusText = rpl::duplicate(
|
||||
statusFields
|
||||
) | rpl::map([](StatusFields &&fields) {
|
||||
return fields.text;
|
||||
});
|
||||
const auto status = Ui::CreateChild<Ui::FlatLabel>(
|
||||
_top.get(),
|
||||
(topic
|
||||
? Info::Profile::NameValue(topic->channel())
|
||||
: std::move(statusText)),
|
||||
st::previewStatus);
|
||||
std::move(statusFields) | rpl::start_with_next([=](const StatusFields &fields) {
|
||||
status->setTextColorOverride(fields.active
|
||||
? st::windowActiveTextFg->c
|
||||
: std::optional<QColor>());
|
||||
}, status->lifetime());
|
||||
status->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
const auto userpic = topic
|
||||
? nullptr
|
||||
: Ui::CreateChild<Ui::UserpicButton>(
|
||||
_top.get(),
|
||||
_thread->peer(),
|
||||
st::previewUserpic);
|
||||
if (userpic) {
|
||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}
|
||||
const auto icon = topic
|
||||
? Ui::CreateChild<Info::Profile::TopicIconButton>(
|
||||
this,
|
||||
topic,
|
||||
[=] { return false; })
|
||||
: nullptr;
|
||||
if (icon) {
|
||||
icon->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}
|
||||
|
||||
const auto shadow = Ui::CreateChild<Ui::PlainShadow>(this);
|
||||
_top->geometryValue() | rpl::start_with_next([=](QRect geometry) {
|
||||
const auto &st = st::previewTop;
|
||||
name->resizeToWidth(geometry.width()
|
||||
- st.namePosition.x()
|
||||
- st.photoPosition.x());
|
||||
name->move(st::previewTop.namePosition);
|
||||
status->resizeToWidth(geometry.width()
|
||||
- st.statusPosition.x()
|
||||
- st.photoPosition.x());
|
||||
status->move(st.statusPosition);
|
||||
shadow->setGeometry(
|
||||
geometry.x(),
|
||||
geometry.y() + geometry.height(),
|
||||
geometry.width(),
|
||||
st::lineWidth);
|
||||
if (userpic) {
|
||||
userpic->move(st.photoPosition);
|
||||
} else {
|
||||
icon->move(
|
||||
st.photoPosition.x() + (st.photoSize - icon->width()) / 2,
|
||||
st.photoPosition.y() + (st.photoSize - icon->height()) / 2);
|
||||
}
|
||||
}, shadow->lifetime());
|
||||
}
|
||||
|
||||
void Item::setupMarkRead() {
|
||||
_markRead->resizeToWidth(width());
|
||||
_markRead->move(0, height() - _markRead->height());
|
||||
|
||||
rpl::single(
|
||||
rpl::empty
|
||||
) | rpl::then(
|
||||
_thread->owner().chatsListFor(_thread)->unreadStateChanges(
|
||||
) | rpl::to_empty
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto state = _thread->chatListBadgesState();
|
||||
const auto unread = (state.unreadCounter || state.unread);
|
||||
if (_thread->asTopic() && !unread) {
|
||||
_markRead->hide();
|
||||
return;
|
||||
}
|
||||
_markRead->setText(unread
|
||||
? tr::lng_context_mark_read(tr::now)
|
||||
: tr::lng_context_mark_unread(tr::now));
|
||||
_markRead->setClickedCallback([=] {
|
||||
_actions.fire({ .markRead = unread, .markUnread = !unread });
|
||||
});
|
||||
_markRead->show();
|
||||
}, _markRead->lifetime());
|
||||
|
||||
const auto shadow = Ui::CreateChild<Ui::PlainShadow>(this);
|
||||
_markRead->geometryValue() | rpl::start_with_next([=](QRect geometry) {
|
||||
shadow->setGeometry(
|
||||
geometry.x(),
|
||||
geometry.y() - st::lineWidth,
|
||||
geometry.width(),
|
||||
st::lineWidth);
|
||||
}, shadow->lifetime());
|
||||
shadow->showOn(_markRead->shownValue());
|
||||
}
|
||||
|
||||
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::setupHistory() {
|
||||
_inner = _scroll->setOwnedWidget(object_ptr<ListWidget>(
|
||||
this,
|
||||
_session,
|
||||
static_cast<ListDelegate*>(this)));
|
||||
_scroll->setGeometry(rect());
|
||||
|
||||
_markRead->shownValue() | rpl::start_with_next([=](bool shown) {
|
||||
const auto top = _top->height();
|
||||
const auto bottom = shown ? _markRead->height() : 0;
|
||||
_scroll->setGeometry(rect().marginsRemoved({ 0, top, 0, bottom }));
|
||||
}, _markRead->lifetime());
|
||||
|
||||
_scroll->scrolls(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateInnerVisibleArea();
|
||||
|
@ -209,39 +447,6 @@ Item::Item(not_null<Ui::RpWidget*> parent, not_null<Data::Thread*> thread)
|
|||
_inner->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -22,6 +22,8 @@ namespace HistoryView {
|
|||
struct ChatPreviewAction {
|
||||
FullMsgId openItemId;
|
||||
bool openInfo = false;
|
||||
bool markRead = false;
|
||||
bool markUnread = false;
|
||||
};
|
||||
|
||||
struct ChatPreview {
|
||||
|
|
|
@ -231,12 +231,17 @@ TopicIconButton::TopicIconButton(
|
|||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Data::ForumTopic*> topic)
|
||||
: TopicIconButton(parent, topic, [=] {
|
||||
return controller->isGifPausedAtLeastFor(Window::GifPauseReason::Layer);
|
||||
}) {
|
||||
}
|
||||
|
||||
TopicIconButton::TopicIconButton(
|
||||
QWidget *parent,
|
||||
not_null<Data::ForumTopic*> topic,
|
||||
Fn<bool()> paused)
|
||||
: AbstractButton(parent)
|
||||
, _view(
|
||||
topic,
|
||||
[=] { return controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Layer); },
|
||||
[=] { update(); }) {
|
||||
, _view(topic, paused, [=] { update(); }) {
|
||||
resize(st::infoTopicCover.photo.size);
|
||||
paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
|
|
|
@ -82,6 +82,10 @@ public:
|
|||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Data::ForumTopic*> topic);
|
||||
TopicIconButton(
|
||||
QWidget *parent,
|
||||
not_null<Data::ForumTopic*> topic,
|
||||
Fn<bool()> paused);
|
||||
|
||||
private:
|
||||
TopicIconView _view;
|
||||
|
|
|
@ -1090,3 +1090,25 @@ previewMenu: PopupMenu(defaultPopupMenu) {
|
|||
shadow: boxRoundShadow;
|
||||
}
|
||||
}
|
||||
previewTop: PeerListItem(defaultPeerListItem) {
|
||||
height: 52px;
|
||||
photoPosition: point(10px, 6px);
|
||||
namePosition: point(60px, 9px);
|
||||
statusPosition: point(60px, 27px);
|
||||
photoSize: 40px;
|
||||
}
|
||||
previewMarkRead: FlatButton(historyComposeButton) {
|
||||
height: 40px;
|
||||
textTop: 10px;
|
||||
}
|
||||
previewName: FlatLabel(defaultFlatLabel) {
|
||||
style: semiboldTextStyle;
|
||||
textFg: windowFg;
|
||||
}
|
||||
previewStatus: FlatLabel(defaultFlatLabel) {
|
||||
textFg: windowSubTextFg;
|
||||
}
|
||||
previewUserpic: UserpicButton(defaultUserpicButton) {
|
||||
size: size(40px, 40px);
|
||||
photoSize: 40px;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue