Add initial scheduled list implementation.

This commit is contained in:
John Preston 2019-08-08 18:07:55 +01:00
parent 3814b0833d
commit 815a18be94
13 changed files with 692 additions and 25 deletions

View File

@ -77,7 +77,7 @@ ScheduledMessages::~ScheduledMessages() {
}
}
MsgId ScheduledMessages::scheduledId(not_null<HistoryItem*> item) {
MsgId ScheduledMessages::lookupId(not_null<HistoryItem*> item) const {
if (const auto i = _data.find(item->history()); i != end(_data)) {
const auto &list = i->second.idByItem;
if (const auto j = list.find(item); j != end(list)) {
@ -87,16 +87,9 @@ MsgId ScheduledMessages::scheduledId(not_null<HistoryItem*> item) {
return MsgId(0);
}
HistoryItem *ScheduledMessages::scheduledMessage(
not_null<History*> history,
MsgId id) {
if (const auto i = _data.find(history); i != end(_data)) {
const auto &list = i->second.itemById;
if (const auto j = list.find(id); j != end(list)) {
return j->second;
}
}
return nullptr;
int ScheduledMessages::count(not_null<History*> history) const {
const auto i = _data.find(history);
return (i != end(_data)) ? i->second.items.size() : 0;
}
void ScheduledMessages::apply(const MTPDupdateNewScheduledMessage &update) {
@ -152,6 +145,28 @@ rpl::producer<> ScheduledMessages::updates(not_null<History*> history) {
});
}
Data::MessagesSlice ScheduledMessages::list(not_null<History*> history) {
auto result = Data::MessagesSlice();
const auto i = _data.find(history);
if (i == end(_data)) {
const auto i = _requests.find(history);
if (i == end(_requests)) {
return result;
}
result.fullCount = result.skippedAfter = result.skippedBefore = 0;
return result;
}
const auto &list = i->second.items;
result.skippedAfter = result.skippedBefore = 0;
result.fullCount = int(list.size());
result.ids = ranges::view::all(
list
) | ranges::view::transform(
&HistoryItem::fullId
) | ranges::view::reverse | ranges::to_vector;
return result;
}
void ScheduledMessages::request(not_null<History*> history) {
auto &request = _requests[history];
if (request.requestId) {

View File

@ -18,6 +18,7 @@ class Session;
namespace Data {
class Session;
struct MessagesSlice;
class ScheduledMessages final {
public:
@ -26,15 +27,14 @@ public:
ScheduledMessages &operator=(const ScheduledMessages &other) = delete;
~ScheduledMessages();
[[nodiscard]] MsgId scheduledId(not_null<HistoryItem*> item);
[[nodiscard]] HistoryItem *scheduledMessage(
not_null<History*> history,
MsgId id);
[[nodiscard]] MsgId lookupId(not_null<HistoryItem*> item) const;
[[nodiscard]] int count(not_null<History*> history) const;
void apply(const MTPDupdateNewScheduledMessage &update);
void apply(const MTPDupdateDeleteScheduledMessages &update);
[[nodiscard]] rpl::producer<> updates(not_null<History*> history);
[[nodiscard]] Data::MessagesSlice list(not_null<History*> history);
private:
using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;

View File

@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_web_page.h"
#include "data/data_game.h"
#include "data/data_poll.h"
#include "data/data_scheduled_messages.h"
#include "base/unixtime.h"
#include "styles/style_boxes.h" // st::backgroundSize
@ -207,7 +208,7 @@ Session::Session(not_null<Main::Session*> session)
})
, _unmuteByFinishedTimer([=] { unmuteByFinished(); })
, _groups(this)
, _scheduledMessages(this) {
, _scheduledMessages(std::make_unique<ScheduledMessages>(this)) {
_cache->open(Local::cacheKey());
_bigFileCache->open(Local::cacheBigFileKey());
@ -231,6 +232,7 @@ void Session::clear() {
for (const auto &[peerId, history] : _histories) {
history->clear(History::ClearType::Unload);
}
_scheduledMessages = nullptr;
_dependentMessages.clear();
base::take(_messages);
base::take(_channelMessages);

View File

@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "dialogs/dialogs_main_list.h"
#include "data/data_groups.h"
#include "data/data_notify_settings.h"
#include "data/data_scheduled_messages.h"
#include "history/history_location_manager.h"
#include "base/timer.h"
#include "base/flags.h"
@ -89,11 +88,8 @@ public:
return _groups;
}
[[nodiscard]] ScheduledMessages &scheduledMessages() {
return _scheduledMessages;
}
[[nodiscard]] const ScheduledMessages &scheduledMessages() const {
return _scheduledMessages;
[[nodiscard]] ScheduledMessages &scheduledMessages() const {
return *_scheduledMessages;
}
void clear();
@ -989,7 +985,7 @@ private:
int32 _wallpapersHash = 0;
Groups _groups;
ScheduledMessages _scheduledMessages;
std::unique_ptr<ScheduledMessages> _scheduledMessages;
rpl::lifetime _lifetime;

View File

@ -82,6 +82,7 @@ Widget::Widget(
_topBar->move(0, 0);
_topBar->resizeToWidth(width());
_topBar->show();
_topBar->forwardSelectionRequest(
) | rpl::start_with_next([=] {
forwardSelected();

View File

@ -267,6 +267,10 @@ historyBotCommandStart: IconButton(historyAttach) {
icon: icon {{ "send_control_bot_command", historyComposeIconFg }};
iconOver: icon {{ "send_control_bot_command", historyComposeIconFgOver }};
}
historyScheduledToggle: IconButton(historyAttach) {
icon: icon {{ "dialogs_calendar", historyComposeIconFg }};
iconOver: icon {{ "dialogs_calendar", historyComposeIconFgOver }};
}
historyRecordVoiceFg: historyComposeIconFg;
historyRecordVoiceFgOver: historyComposeIconFgOver;
historyRecordVoiceFgActive: windowBgActive;

View File

@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_scheduled_messages.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_message.h"
@ -46,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
//#include "history/feed/history_feed_section.h" // #feed
#include "history/view/history_view_service_message.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_scheduled_section.h"
#include "profile/profile_block_group_members.h"
#include "info/info_memento.h"
#include "core/click_handler_types.h"
@ -644,6 +646,7 @@ HistoryWidget::HistoryWidget(
}
}, lifetime());
setupScheduledToggle();
orderWidgets();
setupShortcuts();
}
@ -1766,6 +1769,7 @@ void HistoryWidget::showHistory(
session().data().requestNotifySettings(_peer);
refreshSilentToggle();
}
refreshScheduledToggle();
if (_showAtMsgId == ShowAtUnreadMsgId) {
if (_history->scrollTopItem) {
@ -1902,6 +1906,37 @@ void HistoryWidget::refreshSilentToggle() {
}
}
void HistoryWidget::setupScheduledToggle() {
controller()->activeChatValue(
) | rpl::map([=](const Dialogs::Key &key) -> rpl::producer<> {
if (const auto history = key.history()) {
return session().data().scheduledMessages().updates(history);
}
return rpl::never<rpl::empty_value>();
}) | rpl::flatten_latest(
) | rpl::start_with_next([=] {
refreshScheduledToggle();
updateControlsVisibility();
updateControlsGeometry();
}, lifetime());
}
void HistoryWidget::refreshScheduledToggle() {
const auto has = _history
&& _peer->canWrite()
&& (session().data().scheduledMessages().count(_history) > 0);
if (!_scheduled && has) {
_scheduled.create(this, st::historyScheduledToggle);
_scheduled->show();
_scheduled->addClickHandler([=] {
controller()->showSection(
HistoryView::ScheduledMemento(_history));
});
} else if (_scheduled && !has) {
_scheduled.destroy();
}
}
bool HistoryWidget::contentOverlapped(const QRect &globalRect) {
return (_attachDragDocument->overlaps(globalRect)
|| _attachDragPhoto->overlaps(globalRect)
@ -2004,6 +2039,9 @@ void HistoryWidget::updateControlsVisibility() {
if (_silent) {
_silent->hide();
}
if (_scheduled) {
_scheduled->hide();
}
_kbScroll->hide();
_fieldBarCancel->hide();
_attachToggle->hide();
@ -2041,6 +2079,9 @@ void HistoryWidget::updateControlsVisibility() {
if (_silent) {
_silent->hide();
}
if (_scheduled) {
_scheduled->hide();
}
if (_kbShown) {
_kbScroll->show();
} else {
@ -2080,6 +2121,9 @@ void HistoryWidget::updateControlsVisibility() {
if (_silent) {
_silent->show();
}
if (_scheduled) {
_scheduled->show();
}
updateFieldPlaceholder();
}
if (_editMsgId || _replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) {
@ -2106,6 +2150,9 @@ void HistoryWidget::updateControlsVisibility() {
if (_silent) {
_silent->hide();
}
if (_scheduled) {
_scheduled->hide();
}
_kbScroll->hide();
_fieldBarCancel->hide();
_attachToggle->hide();
@ -3841,7 +3888,7 @@ void HistoryWidget::moveFieldControls() {
}
// _attachToggle --------- _inlineResults -------------------------------------- _tabbedPanel --------- _fieldBarCancel
// (_attachDocument|_attachPhoto) _field (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) [_broadcast] _send
// (_attachDocument|_attachPhoto) _field (_scheduled) (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) [_broadcast] _send
// (_botStart|_unblock|_joinChannel|{_muteUnmute&_discuss})
auto buttonsBottom = bottom - _attachToggle->height();
@ -3858,6 +3905,13 @@ void HistoryWidget::moveFieldControls() {
if (_silent) {
_silent->moveToRight(right, buttonsBottom);
}
const auto kbShowShown = _history && !_kbShown && _keyboard->hasMarkup();
if (kbShowShown || _cmdStartShown || _silent) {
right += _botCommandStart->width();
}
if (_scheduled) {
_scheduled->moveToRight(right, buttonsBottom);
}
_fieldBarCancel->moveToRight(0, _field->y() - st::historySendPadding - _fieldBarCancel->height());
if (_inlineResults) {
@ -3915,6 +3969,7 @@ void HistoryWidget::updateFieldSize() {
if (kbShowShown) fieldWidth -= _botKeyboardShow->width();
if (_cmdStartShown) fieldWidth -= _botCommandStart->width();
if (_silent) fieldWidth -= _silent->width();
if (_scheduled) fieldWidth -= _scheduled->width();
if (_field->width() != fieldWidth) {
_field->resize(fieldWidth, _field->height());
@ -6089,6 +6144,7 @@ void HistoryWidget::fullPeerUpdated(PeerData *peer) {
if (!_canSendMessages) {
cancelReply();
}
refreshScheduledToggle();
refreshSilentToggle();
refresh = true;
}
@ -6135,6 +6191,7 @@ void HistoryWidget::handlePeerUpdate() {
if (!_canSendMessages) {
cancelReply();
}
refreshScheduledToggle();
refreshSilentToggle();
resize = true;
}

View File

@ -711,6 +711,9 @@ private:
bool showInlineBotCancel() const;
void refreshSilentToggle();
void setupScheduledToggle();
void refreshScheduledToggle();
std::unique_ptr<HistoryView::ContactStatus> _contactStatus;
object_ptr<Ui::SendButton> _send;
@ -728,6 +731,7 @@ private:
object_ptr<Ui::IconButton> _botKeyboardHide;
object_ptr<Ui::IconButton> _botCommandStart;
object_ptr<Ui::SilentToggle> _silent = { nullptr };
object_ptr<Ui::IconButton> _scheduled = { nullptr };
bool _cmdStartShown = false;
object_ptr<Ui::InputField> _field;
bool _recording = false;

View File

@ -92,7 +92,8 @@ public:
int shift = 0;
};
explicit ListMemento(Data::MessagePosition position)
explicit ListMemento(
Data::MessagePosition position = Data::UnreadMessagePosition)
: _aroundPosition(position) {
}
void setAroundPosition(Data::MessagePosition position) {

View File

@ -0,0 +1,423 @@
/*
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_scheduled_section.h"
#include "history/view/history_view_top_bar_widget.h"
#include "history/view/history_view_list_widget.h"
#include "history/history.h"
#include "history/history_item.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/shadow.h"
#include "ui/special_buttons.h"
#include "window/window_session_controller.h"
#include "core/event_filter.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_scheduled_messages.h"
#include "lang/lang_keys.h"
#include "styles/style_history.h"
#include "styles/style_window.h"
#include "styles/style_info.h"
namespace HistoryView {
object_ptr<Window::SectionWidget> ScheduledMemento::createWidget(
QWidget *parent,
not_null<Window::SessionController*> controller,
Window::Column column,
const QRect &geometry) {
if (column == Window::Column::Third) {
return nullptr;
}
auto result = object_ptr<ScheduledWidget>(parent, controller, _history);
result->setInternalState(geometry, this);
return std::move(result);
}
ScheduledWidget::ScheduledWidget(
QWidget *parent,
not_null<Window::SessionController*> controller,
not_null<History*> history)
: Window::SectionWidget(parent, controller)
, _history(history)
, _scroll(this, st::historyScroll, false)
, _topBar(this, controller)
, _topBarShadow(this)
, _scrollDown(_scroll, st::historyToDown) {
_topBar->setActiveChat(_history);
_topBar->move(0, 0);
_topBar->resizeToWidth(width());
_topBar->show();
_topBar->deleteSelectionRequest(
) | rpl::start_with_next([=] {
confirmDeleteSelected();
}, _topBar->lifetime());
_topBar->clearSelectionRequest(
) | rpl::start_with_next([=] {
clearSelected();
}, _topBar->lifetime());
_topBarShadow->raise();
updateAdaptiveLayout();
subscribe(Adaptive::Changed(), [=] { updateAdaptiveLayout(); });
_inner = _scroll->setOwnedWidget(object_ptr<ListWidget>(
this,
controller,
static_cast<ListDelegate*>(this)));
_scroll->move(0, _topBar->height());
_scroll->show();
connect(_scroll, &Ui::ScrollArea::scrolled, [=] { onScroll(); });
setupScrollDownButton();
}
void ScheduledWidget::setupScrollDownButton() {
_scrollDown->setClickedCallback([=] {
scrollDownClicked();
});
Core::InstallEventFilter(_scrollDown, [=](not_null<QEvent*> event) {
if (event->type() == QEvent::Wheel) {
return _scroll->viewportEvent(event);
}
return false;
});
updateScrollDownVisibility();
}
void ScheduledWidget::scrollDownClicked() {
showAtPosition(Data::MaxMessagePosition);
}
void ScheduledWidget::showAtPosition(Data::MessagePosition position) {
if (showAtPositionNow(position)) {
if (const auto highlight = base::take(_highlightMessageId)) {
_inner->highlightMessage(highlight);
}
} else {
_nextAnimatedScrollPosition = position;
_nextAnimatedScrollDelta = _inner->isBelowPosition(position)
? -_scroll->height()
: _inner->isAbovePosition(position)
? _scroll->height()
: 0;
auto memento = HistoryView::ListMemento(position);
_inner->restoreState(&memento);
}
}
bool ScheduledWidget::showAtPositionNow(Data::MessagePosition position) {
if (const auto scrollTop = _inner->scrollTopForPosition(position)) {
const auto currentScrollTop = _scroll->scrollTop();
const auto wanted = snap(*scrollTop, 0, _scroll->scrollTopMax());
const auto fullDelta = (wanted - currentScrollTop);
const auto limit = _scroll->height();
const auto scrollDelta = snap(fullDelta, -limit, limit);
_inner->animatedScrollTo(
wanted,
position,
scrollDelta,
(std::abs(fullDelta) > limit
? HistoryView::ListWidget::AnimatedScroll::Part
: HistoryView::ListWidget::AnimatedScroll::Full));
return true;
}
return false;
}
void ScheduledWidget::updateScrollDownVisibility() {
if (animating()) {
return;
}
const auto scrollDownIsVisible = [&]() -> std::optional<bool> {
const auto top = _scroll->scrollTop() + st::historyToDownShownAfter;
if (top < _scroll->scrollTopMax()) {
return true;
}
if (_inner->loadedAtBottomKnown()) {
return !_inner->loadedAtBottom();
}
return std::nullopt;
};
const auto scrollDownIsShown = scrollDownIsVisible();
if (!scrollDownIsShown) {
return;
}
if (_scrollDownIsShown != *scrollDownIsShown) {
_scrollDownIsShown = *scrollDownIsShown;
_scrollDownShown.start(
[=] { updateScrollDownPosition(); },
_scrollDownIsShown ? 0. : 1.,
_scrollDownIsShown ? 1. : 0.,
st::historyToDownDuration);
}
}
void ScheduledWidget::updateScrollDownPosition() {
// _scrollDown is a child widget of _scroll, not me.
auto top = anim::interpolate(
0,
_scrollDown->height() + st::historyToDownPosition.y(),
_scrollDownShown.value(_scrollDownIsShown ? 1. : 0.));
_scrollDown->moveToRight(
st::historyToDownPosition.x(),
_scroll->height() - top);
auto shouldBeHidden = !_scrollDownIsShown && !_scrollDownShown.animating();
if (shouldBeHidden != _scrollDown->isHidden()) {
_scrollDown->setVisible(!shouldBeHidden);
}
}
void ScheduledWidget::scrollDownAnimationFinish() {
_scrollDownShown.stop();
updateScrollDownPosition();
}
void ScheduledWidget::updateAdaptiveLayout() {
_topBarShadow->moveToLeft(
Adaptive::OneColumn() ? 0 : st::lineWidth,
_topBar->height());
}
not_null<History*> ScheduledWidget::history() const {
return _history;
}
Dialogs::RowDescriptor ScheduledWidget::activeChat() const {
return {
_history,
FullMsgId(_history->channelId(), ShowAtUnreadMsgId)
};
}
QPixmap ScheduledWidget::grabForShowAnimation(const Window::SectionSlideParams &params) {
if (params.withTopBarShadow) _topBarShadow->hide();
auto result = Ui::GrabWidget(this);
if (params.withTopBarShadow) _topBarShadow->show();
return result;
}
void ScheduledWidget::doSetInnerFocus() {
_inner->setFocus();
}
bool ScheduledWidget::showInternal(
not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) {
if (auto logMemento = dynamic_cast<ScheduledMemento*>(memento.get())) {
if (logMemento->getHistory() == history()) {
restoreState(logMemento);
return true;
}
}
return false;
}
void ScheduledWidget::setInternalState(
const QRect &geometry,
not_null<ScheduledMemento*> memento) {
setGeometry(geometry);
Ui::SendPendingMoveResizeEvents(this);
restoreState(memento);
}
std::unique_ptr<Window::SectionMemento> ScheduledWidget::createMemento() {
auto result = std::make_unique<ScheduledMemento>(history());
saveState(result.get());
return std::move(result);
}
void ScheduledWidget::saveState(not_null<ScheduledMemento*> memento) {
_inner->saveState(memento->list());
}
void ScheduledWidget::restoreState(not_null<ScheduledMemento*> memento) {
_inner->restoreState(memento->list());
}
void ScheduledWidget::resizeEvent(QResizeEvent *e) {
if (!width() || !height()) {
return;
}
updateControlsGeometry();
}
void ScheduledWidget::updateControlsGeometry() {
const auto contentWidth = width();
const auto newScrollTop = _scroll->isHidden()
? std::nullopt
: base::make_optional(_scroll->scrollTop() + topDelta());
_topBar->resizeToWidth(contentWidth);
_topBarShadow->resize(contentWidth, st::lineWidth);
const auto bottom = height();
const auto scrollHeight = bottom
- _topBar->height();
// - _showNext->height();
const auto scrollSize = QSize(contentWidth, scrollHeight);
if (_scroll->size() != scrollSize) {
_skipScrollEvent = true;
_scroll->resize(scrollSize);
_inner->resizeToWidth(scrollSize.width(), _scroll->height());
_skipScrollEvent = false;
}
if (!_scroll->isHidden()) {
if (newScrollTop) {
_scroll->scrollToY(*newScrollTop);
}
updateInnerVisibleArea();
}
updateScrollDownPosition();
}
void ScheduledWidget::paintEvent(QPaintEvent *e) {
if (animating()) {
SectionWidget::paintEvent(e);
return;
}
if (Ui::skipPaintEvent(this, e)) {
return;
}
//if (hasPendingResizedItems()) {
// updateListSize();
//}
//auto ms = crl::now();
//_historyDownShown.step(ms);
SectionWidget::PaintBackground(this, e->rect());
}
void ScheduledWidget::onScroll() {
if (_skipScrollEvent) {
return;
}
updateInnerVisibleArea();
}
void ScheduledWidget::updateInnerVisibleArea() {
const auto scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
updateScrollDownVisibility();
}
void ScheduledWidget::showAnimatedHook(
const Window::SectionSlideParams &params) {
_topBar->setAnimatingMode(true);
if (params.withTopBarShadow) _topBarShadow->show();
}
void ScheduledWidget::showFinishedHook() {
_topBar->setAnimatingMode(false);
}
bool ScheduledWidget::wheelEventFromFloatPlayer(QEvent *e) {
return _scroll->viewportEvent(e);
}
QRect ScheduledWidget::rectForFloatPlayer() const {
return mapToGlobal(_scroll->geometry());
}
Context ScheduledWidget::listContext() {
return Context::History;
}
void ScheduledWidget::listScrollTo(int top) {
if (_scroll->scrollTop() != top) {
_scroll->scrollToY(top);
} else {
updateInnerVisibleArea();
}
}
void ScheduledWidget::listCancelRequest() {
controller()->showBackFromStack();
}
void ScheduledWidget::listDeleteRequest() {
confirmDeleteSelected();
}
rpl::producer<Data::MessagesSlice> ScheduledWidget::listSource(
Data::MessagePosition aroundId,
int limitBefore,
int limitAfter) {
const auto data = &controller()->session().data();
return rpl::single(
rpl::empty_value()
) | rpl::then(
data->scheduledMessages().updates(_history)
) | rpl::map([=] {
return data->scheduledMessages().list(_history);
});
}
bool ScheduledWidget::listAllowsMultiSelect() {
return true;
}
bool ScheduledWidget::listIsLessInOrder(
not_null<HistoryItem*> first,
not_null<HistoryItem*> second) {
return first->position() < second->position();
}
void ScheduledWidget::listSelectionChanged(SelectedItems &&items) {
HistoryView::TopBarWidget::SelectedState state;
state.count = items.size();
for (const auto item : items) {
if (item.canForward) {
++state.canForwardCount;
}
if (item.canDelete) {
++state.canDeleteCount;
}
}
_topBar->showSelected(state);
}
void ScheduledWidget::listVisibleItemsChanged(HistoryItemsList &&items) {
}
std::optional<int> ScheduledWidget::listUnreadBarView(
const std::vector<not_null<Element*>> &elements) {
return std::nullopt;
}
void ScheduledWidget::listContentRefreshed() {
}
ClickHandlerPtr ScheduledWidget::listDateLink(not_null<Element*> view) {
return nullptr;
}
void ScheduledWidget::confirmDeleteSelected() {
auto items = _inner->getSelectedItems();
if (items.empty()) {
return;
}
//const auto weak = make_weak(this);
//const auto box = Ui::show(Box<DeleteMessagesBox>(std::move(items)));
//box->setDeleteConfirmedCallback([=] {
// if (const auto strong = weak.data()) {
// strong->clearSelected();
// }
//});
}
void ScheduledWidget::clearSelected() {
_inner->cancelSelection();
}
} // namespace HistoryView

View File

@ -0,0 +1,161 @@
/*
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 "window/section_widget.h"
#include "window/section_memento.h"
#include "history/view/history_view_list_widget.h"
class History;
namespace Notify {
struct PeerUpdate;
} // namespace Notify
namespace Ui {
class ScrollArea;
class PlainShadow;
class FlatButton;
class HistoryDownButton;
} // namespace Ui
namespace Profile {
class BackButton;
} // namespace Profile
namespace HistoryView {
class Element;
class TopBarWidget;
class ScheduledMemento;
class ScheduledWidget final
: public Window::SectionWidget
, private ListDelegate {
public:
ScheduledWidget(
QWidget *parent,
not_null<Window::SessionController*> controller,
not_null<History*> history);
not_null<History*> history() const;
Dialogs::RowDescriptor activeChat() const override;
bool hasTopBarShadow() const override {
return true;
}
QPixmap grabForShowAnimation(
const Window::SectionSlideParams &params) override;
bool showInternal(
not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) override;
std::unique_ptr<Window::SectionMemento> createMemento() override;
void setInternalState(
const QRect &geometry,
not_null<ScheduledMemento*> memento);
// Float player interface.
bool wheelEventFromFloatPlayer(QEvent *e) override;
QRect rectForFloatPlayer() const override;
// ListDelegate interface.
Context listContext() override;
void listScrollTo(int top) override;
void listCancelRequest() override;
void listDeleteRequest() override;
rpl::producer<Data::MessagesSlice> listSource(
Data::MessagePosition aroundId,
int limitBefore,
int limitAfter) override;
bool listAllowsMultiSelect() override;
bool listIsLessInOrder(
not_null<HistoryItem*> first,
not_null<HistoryItem*> second) override;
void listSelectionChanged(SelectedItems &&items) override;
void listVisibleItemsChanged(HistoryItemsList &&items) override;
std::optional<int> listUnreadBarView(
const std::vector<not_null<Element*>> &elements) override;
void listContentRefreshed() override;
ClickHandlerPtr listDateLink(not_null<Element*> view) override;
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void showAnimatedHook(
const Window::SectionSlideParams &params) override;
void showFinishedHook() override;
void doSetInnerFocus() override;
private:
void onScroll();
void updateInnerVisibleArea();
void updateControlsGeometry();
void updateAdaptiveLayout();
void saveState(not_null<ScheduledMemento*> memento);
void restoreState(not_null<ScheduledMemento*> memento);
void showAtPosition(Data::MessagePosition position);
bool showAtPositionNow(Data::MessagePosition position);
void setupScrollDownButton();
void scrollDownClicked();
void scrollDownAnimationFinish();
void updateScrollDownVisibility();
void updateScrollDownPosition();
void confirmDeleteSelected();
void clearSelected();
const not_null<History*> _history;
object_ptr<Ui::ScrollArea> _scroll;
QPointer<ListWidget> _inner;
object_ptr<TopBarWidget> _topBar;
object_ptr<Ui::PlainShadow> _topBarShadow;
bool _skipScrollEvent = false;
FullMsgId _highlightMessageId;
std::optional<Data::MessagePosition> _nextAnimatedScrollPosition;
int _nextAnimatedScrollDelta = 0;
Ui::Animations::Simple _scrollDownShown;
bool _scrollDownIsShown = false;
object_ptr<Ui::HistoryDownButton> _scrollDown;
};
class ScheduledMemento : public Window::SectionMemento {
public:
ScheduledMemento(not_null<History*> history)
: _history(history)
, _list({}) {
}
object_ptr<Window::SectionWidget> createWidget(
QWidget *parent,
not_null<Window::SessionController*> controller,
Window::Column column,
const QRect &geometry) override;
not_null<History*> getHistory() const {
return _history;
}
not_null<ListMemento*> list() {
return &_list;
}
private:
const not_null<History*> _history;
ListMemento _list;
};
} // namespace HistoryScheduled

View File

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "data/data_scheduled_messages.h"
#include "ui/special_buttons.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"

View File

@ -330,6 +330,8 @@
<(src_loc)/history/view/history_view_message.cpp
<(src_loc)/history/view/history_view_message.h
<(src_loc)/history/view/history_view_object.h
<(src_loc)/history/view/history_view_scheduled_section.cpp
<(src_loc)/history/view/history_view_scheduled_section.h
<(src_loc)/history/view/history_view_service_message.cpp
<(src_loc)/history/view/history_view_service_message.h
<(src_loc)/history/view/history_view_top_bar_widget.cpp