Show correct downloads elements context menu.

This commit is contained in:
John Preston 2022-02-27 14:14:39 +03:00
parent daadf7e2a1
commit e89c95551f
32 changed files with 362 additions and 147 deletions

View File

@ -2601,7 +2601,9 @@ void GroupCall::requestCurrentTimeStart(
});
}).fail([=] {
finish(0);
}).handleAllErrors().send();
}).handleAllErrors().toDC(
MTP::groupCallStreamDcId(_broadcastDcId)
).send();
}
void GroupCall::requestCurrentTimeCancel(

View File

@ -242,6 +242,73 @@ void DownloadManager::addLoaded(
}
}
void DownloadManager::deleteFiles(const std::vector<GlobalMsgId> &ids) {
struct DocumentDescriptor {
uint64 sessionUniqueId = 0;
DocumentId documentId = 0;
FullMsgId itemId;
};
auto sessions = base::flat_set<not_null<Main::Session*>>();
auto files = base::flat_map<QString, DocumentDescriptor>();
for (const auto &id : ids) {
if (const auto item = MessageByGlobalId(id)) {
const auto session = &item->history()->session();
const auto i = _sessions.find(session);
if (i == end(_sessions)) {
continue;
}
auto &data = i->second;
const auto j = ranges::find(
data.downloading,
not_null{ item },
ByItem);
if (j != end(data.downloading)) {
cancel(data, j);
}
const auto k = ranges::find(data.downloaded, item, ByItem);
if (k != end(data.downloaded)) {
const auto document = k->object->document;
files.emplace(k->path, DocumentDescriptor{
.sessionUniqueId = id.sessionUniqueId,
.documentId = document ? document->id : DocumentId(),
.itemId = id.itemId,
});
_loaded.remove(item);
_generated.remove(item);
if (document) {
_generatedDocuments.remove(document);
}
data.downloaded.erase(k);
_loadedRemoved.fire_copy(item);
sessions.emplace(session);
}
}
}
for (const auto session : sessions) {
writePostponed(session);
}
crl::async([files = std::move(files)] {
for (const auto &[path, descriptor] : files) {
QFile(path).remove();
crl::on_main([descriptor] {
if (const auto session = SessionByUniqueId(
descriptor.sessionUniqueId)) {
if (const auto id = descriptor.documentId) {
[[maybe_unused]] const auto location
= session->data().document(id)->location(true);
}
const auto itemId = descriptor.itemId;
if (const auto item = session->data().message(itemId)) {
session->data().requestItemRepaint(item);
}
}
});
}
});
}
auto DownloadManager::loadingList() const
-> ranges::any_view<const DownloadingId*, ranges::category::input> {
return ranges::views::all(

View File

@ -87,6 +87,8 @@ public:
const QString &path,
DownloadDate started);
void deleteFiles(const std::vector<GlobalMsgId> &ids);
[[nodiscard]] auto loadingList() const
-> ranges::any_view<const DownloadingId*, ranges::category::input>;
[[nodiscard]] DownloadProgress loadingProgress() const;

View File

@ -1308,20 +1308,28 @@ HistoryItem::~HistoryItem() {
applyTTL(0);
}
HistoryItem *MessageByGlobalId(GlobalMsgId globalId) {
if (!globalId.sessionUniqueId || !globalId.itemId) {
Main::Session *SessionByUniqueId(uint64 sessionUniqueId) {
if (!sessionUniqueId) {
return nullptr;
}
for (const auto &[index, account] : Core::App().domain().accounts()) {
if (const auto session = account->maybeSession()) {
if (session->uniqueId() == globalId.sessionUniqueId) {
return session->data().message(globalId.itemId);
if (session->uniqueId() == sessionUniqueId) {
return session;
}
}
}
return nullptr;
}
HistoryItem *MessageByGlobalId(GlobalMsgId globalId) {
const auto sessionId = globalId.itemId ? globalId.sessionUniqueId : 0;
if (const auto session = SessionByUniqueId(sessionId)) {
return session->data().message(globalId.itemId);
}
return nullptr;
}
QDateTime ItemDateTime(not_null<const HistoryItem*> item) {
return base::unixtime::parse(item->date());
}

View File

@ -495,6 +495,7 @@ private:
};
[[nodiscard]] Main::Session *SessionByUniqueId(uint64 sessionUniqueId);
[[nodiscard]] HistoryItem *MessageByGlobalId(GlobalMsgId globalId);
[[nodiscard]] QDateTime ItemDateTime(not_null<const HistoryItem*> item);

View File

@ -148,8 +148,8 @@ rpl::producer<SelectedItems> InnerWidget::selectedListValue() const {
) | rpl::flatten_latest();
}
void InnerWidget::cancelSelection() {
_list->cancelSelection();
void InnerWidget::selectionAction(SelectionAction action) {
_list->selectionAction(action);
}
InnerWidget::~InnerWidget() = default;

View File

@ -21,6 +21,7 @@ namespace Info {
class Controller;
struct SelectedItems;
enum class SelectionAction;
namespace Media {
class ListWidget;
@ -46,7 +47,7 @@ public:
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
rpl::producer<SelectedItems> selectedListValue() const;
void cancelSelection();
void selectionAction(SelectionAction action);
~InnerWidget();

View File

@ -335,13 +335,33 @@ std::unique_ptr<BaseLayout> Provider::createLayout(
return nullptr;
}
ListItemSelectionData Provider::computeSelectionData(
not_null<const HistoryItem*> item,
TextSelection selection) {
auto result = ListItemSelectionData(selection);
result.canDelete = true;
result.canForward = item->allowsForward()
&& (&item->history()->session() == &_controller->session());
return result;
}
void Provider::applyDragSelection(
ListSelectedMap &selected,
not_null<const HistoryItem*> fromItem,
bool skipFrom,
not_null<const HistoryItem*> tillItem,
bool skipTill) {
// #TODO downloads
// #TODO downloads selection
}
bool Provider::allowSaveFileAs(
not_null<const HistoryItem*> item,
not_null<DocumentData*> document) {
return false;
}
std::optional<QString> Provider::deleteMenuPhrase() {
return u"Delete from disk"_q;
}
void Provider::saveState(

View File

@ -53,6 +53,9 @@ public:
not_null<const HistoryItem*> a,
not_null<const HistoryItem*> b) override;
Media::ListItemSelectionData computeSelectionData(
not_null<const HistoryItem*> item,
TextSelection selection) override;
void applyDragSelection(
Media::ListSelectedMap &selected,
not_null<const HistoryItem*> fromItem,
@ -60,6 +63,11 @@ public:
not_null<const HistoryItem*> tillItem,
bool skipTill) override;
bool allowSaveFileAs(
not_null<const HistoryItem*> item,
not_null<DocumentData*> document) override;
std::optional<QString> deleteMenuPhrase() override;
void saveState(
not_null<Media::Memento*> memento,
Media::ListScrollTopState scrollState) override;

View File

@ -93,6 +93,14 @@ void Widget::restoreState(not_null<Memento*> memento) {
scrollTopRestore(memento->scrollTop());
}
rpl::producer<SelectedItems> Widget::selectedListValue() const {
return _inner->selectedListValue();
}
void Widget::selectionAction(SelectionAction action) {
_inner->selectionAction(action);
}
std::shared_ptr<Info::Memento> Make(not_null<UserData*> self) {
return std::make_shared<Info::Memento>(
std::vector<std::shared_ptr<ContentMemento>>(

View File

@ -54,6 +54,9 @@ public:
const QRect &geometry,
not_null<Memento*> memento);
rpl::producer<SelectedItems> selectedListValue() const override;
void selectionAction(SelectionAction action) override;
private:
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);

View File

@ -70,7 +70,7 @@ public:
QRect floatPlayerAvailableRect() const;
virtual rpl::producer<SelectedItems> selectedListValue() const;
virtual void cancelSelection() {
virtual void selectionAction(SelectionAction action) {
}
virtual void saveChanges(FnMut<void()> done);

View File

@ -303,7 +303,7 @@ rpl::producer<bool> Controller::searchEnabledByContent() const {
rpl::producer<QString> Controller::mediaSourceQueryValue() const {
return _searchController
? _searchController->currentQueryValue()
: rpl::single(QString()); // #TODO downloads
: rpl::single(QString()); // #TODO downloads search
}
rpl::producer<SparseIdsMergedSlice> Controller::mediaSource(

View File

@ -434,8 +434,8 @@ SelectedItems TopBar::takeSelectedItems() {
return std::move(_selectedItems);
}
rpl::producer<> TopBar::cancelSelectionRequests() const {
return _cancelSelectionClicks.events();
rpl::producer<SelectionAction> TopBar::selectionActionRequests() const {
return _selectionActionRequests.events();
}
void TopBar::updateSelectionState() {
@ -466,9 +466,10 @@ void TopBar::createSelectionControls() {
st::infoTopBarScale));
_cancelSelection->setDuration(st::infoTopBarDuration);
_cancelSelection->entity()->clicks(
) | rpl::to_empty
| rpl::start_to_stream(
_cancelSelectionClicks,
) | rpl::map_to(
SelectionAction::Clear
) | rpl::start_to_stream(
_selectionActionRequests,
_cancelSelection->lifetime());
_selectionText = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::LabelWithNumbers>>(
this,
@ -488,7 +489,12 @@ void TopBar::createSelectionControls() {
_forward.data(),
[this] { return selectionMode() && _canForward; });
_forward->setDuration(st::infoTopBarDuration);
_forward->entity()->addClickHandler([this] { performForward(); });
_forward->entity()->clicks(
) | rpl::map_to(
SelectionAction::Forward
) | rpl::start_to_stream(
_selectionActionRequests,
_cancelSelection->lifetime());
_forward->entity()->setVisible(_canForward);
_delete = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
this,
@ -498,7 +504,12 @@ void TopBar::createSelectionControls() {
_delete.data(),
[this] { return selectionMode() && _canDelete; });
_delete->setDuration(st::infoTopBarDuration);
_delete->entity()->addClickHandler([this] { performDelete(); });
_delete->entity()->clicks(
) | rpl::map_to(
SelectionAction::Delete
) | rpl::start_to_stream(
_selectionActionRequests,
_cancelSelection->lifetime());
_delete->entity()->setVisible(_canDelete);
updateControlsGeometry(width());
@ -541,50 +552,12 @@ bool TopBar::searchMode() const {
return _searchModeAvailable && _searchModeEnabled;
}
MessageIdsList TopBar::collectItems() const {
return ranges::views::all(
_selectedItems.list
) | ranges::views::transform([](auto &&item) {
return item.globalId;
}) | ranges::views::filter([&](const GlobalMsgId &globalId) {
const auto session = &_navigation->session();
return (globalId.sessionUniqueId == session->uniqueId())
&& (session->data().message(globalId.itemId) != nullptr);
}) | ranges::views::transform([](const GlobalMsgId &globalId) {
return globalId.itemId;
}) | ranges::to_vector;
}
void TopBar::performForward() {
auto items = collectItems();
if (items.empty()) {
_cancelSelectionClicks.fire({});
return;
}
Window::ShowForwardMessagesBox(
_navigation,
std::move(items),
[weak = Ui::MakeWeak(this)] {
if (weak) {
weak->_cancelSelectionClicks.fire({});
}
});
_selectionActionRequests.fire(SelectionAction::Forward);
}
void TopBar::performDelete() {
// #TODO downloads
auto items = collectItems();
if (items.empty()) {
_cancelSelectionClicks.fire({});
} else {
auto box = Box<DeleteMessagesBox>(
&_navigation->session(),
std::move(items));
box->setDeleteConfirmedCallback(crl::guard(this, [=] {
_cancelSelectionClicks.fire({});
}));
_navigation->parentController()->show(std::move(box));
}
_selectionActionRequests.fire(SelectionAction::Delete);
}
rpl::producer<QString> TitleValue(

View File

@ -83,7 +83,8 @@ public:
void setSelectedItems(SelectedItems &&items);
SelectedItems takeSelectedItems();
rpl::producer<> cancelSelectionRequests() const;
[[nodiscard]] auto selectionActionRequests() const
-> rpl::producer<SelectionAction>;
void finishAnimating() {
updateControlsVisibility(anim::type::instant);
@ -115,9 +116,7 @@ private:
[[nodiscard]] bool computeCanForward() const;
void updateSelectionState();
void createSelectionControls();
void clearSelectionControls();
MessageIdsList collectItems() const;
void performForward();
void performDelete();
@ -161,7 +160,7 @@ private:
QPointer<Ui::FadeWrap<Ui::LabelWithNumbers>> _selectionText;
QPointer<Ui::FadeWrap<Ui::IconButton>> _forward;
QPointer<Ui::FadeWrap<Ui::IconButton>> _delete;
rpl::event_stream<> _cancelSelectionClicks;
rpl::event_stream<SelectionAction> _selectionActionRequests;
using UpdateCallback = Fn<bool(anim::type)>;
std::map<QObject*, UpdateCallback> _updateControlCallbacks;

View File

@ -343,9 +343,9 @@ void WrapWidget::createTopBar() {
_controller.get(),
TopBarStyle(wrapValue),
std::move(selectedItems));
_topBar->cancelSelectionRequests(
) | rpl::start_with_next([this] {
_content->cancelSelection();
_topBar->selectionActionRequests(
) | rpl::start_with_next([=](SelectionAction action) {
_content->selectionAction(action);
}, _topBar->lifetime());
_topBar->setTitle(TitleValue(

View File

@ -66,7 +66,12 @@ struct SelectedItems {
Storage::SharedMediaType type;
std::vector<SelectedItem> list;
};
enum class SelectionAction {
Clear,
Forward,
Delete,
};
class WrapWidget final : public Window::SectionWidget {

View File

@ -28,22 +28,19 @@ UniversalMsgId GetUniversalId(not_null<const BaseLayout*> layout) {
bool ChangeItemSelection(
ListSelectedMap &selected,
not_null<const HistoryItem*> item,
TextSelection selection) {
ListItemSelectionData selectionData) {
const auto changeExisting = [&](auto it) {
if (it == selected.cend()) {
return false;
} else if (it->second.text != selection) {
it->second.text = selection;
} else if (it->second != selectionData) {
it->second = selectionData;
return true;
}
return false;
};
if (selected.size() < MaxSelectedItems) {
const auto [i, ok] = selected.try_emplace(item, selection);
const auto [i, ok] = selected.try_emplace(item, selectionData);
if (ok) {
// #TODO downloads
i->second.canDelete = item->canDelete();
i->second.canForward = item->allowsForward();
return true;
}
return changeExisting(i);

View File

@ -32,6 +32,14 @@ struct ListItemSelectionData {
bool canForward = false;
};
inline bool operator==(
ListItemSelectionData a,
ListItemSelectionData b) {
return (a.text == b.text)
&& (a.canDelete == b.canDelete)
&& (a.canForward == b.canForward);
}
using ListSelectedMap = base::flat_map<
not_null<const HistoryItem*>,
ListItemSelectionData,
@ -83,7 +91,7 @@ using UniversalMsgId = MsgId;
bool ChangeItemSelection(
ListSelectedMap &selected,
not_null<const HistoryItem*> item,
TextSelection selection);
ListItemSelectionData selectionData);
class ListSectionDelegate {
public:
@ -132,6 +140,9 @@ public:
not_null<const HistoryItem*> a,
not_null<const HistoryItem*> b) = 0;
[[nodiscard]] virtual ListItemSelectionData computeSelectionData(
not_null<const HistoryItem*> item,
TextSelection selection) = 0;
virtual void applyDragSelection(
ListSelectedMap &selected,
not_null<const HistoryItem*> fromItem,
@ -139,6 +150,11 @@ public:
not_null<const HistoryItem*> tillItem,
bool skipTill) = 0;
[[nodiscard]] virtual bool allowSaveFileAs(
not_null<const HistoryItem*> item,
not_null<DocumentData*> document) = 0;
[[nodiscard]] virtual std::optional<QString> deleteMenuPhrase() = 0;
virtual void saveState(
not_null<Memento*> memento,
ListScrollTopState scrollState) = 0;

View File

@ -281,8 +281,8 @@ rpl::producer<SelectedItems> InnerWidget::selectedListValue() const {
) | rpl::flatten_latest();
}
void InnerWidget::cancelSelection() {
_list->cancelSelection();
void InnerWidget::selectionAction(SelectionAction action) {
_list->selectionAction(action);
}
InnerWidget::~InnerWidget() = default;

View File

@ -48,7 +48,7 @@ public:
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
rpl::producer<SelectedItems> selectedListValue() const;
void cancelSelection();
void selectionAction(SelectionAction action);
~InnerWidget();

View File

@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "data/data_download_manager.h"
#include "history/history_item.h"
#include "history/history.h"
#include "history/view/history_view_cursor_state.h"
@ -30,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "window/window_peer_menu.h"
#include "ui/widgets/popup_menu.h"
#include "ui/boxes/confirm_box.h"
#include "ui/controls/delete_message_context_action.h"
#include "ui/chat/chat_style.h"
#include "ui/cached_round_corners.h"
@ -46,9 +48,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/delete_messages_box.h"
#include "boxes/peer_list_controllers.h"
#include "core/file_utilities.h"
#include "core/application.h"
#include "facades.h"
#include "styles/style_overview.h"
#include "styles/style_info.h"
#include "styles/style_layers.h"
#include "styles/style_menu_icons.h"
#include <QtWidgets/QApplication>
@ -215,6 +219,14 @@ rpl::producer<SelectedItems> ListWidget::selectedListValue() const {
collectSelectedItems());
}
void ListWidget::selectionAction(SelectionAction action) {
switch (action) {
case SelectionAction::Clear: clearSelected(); return;
case SelectionAction::Forward: forwardSelected(); return;
case SelectionAction::Delete: deleteSelected(); return;
}
}
QRect ListWidget::getCurrentSongGeometry() {
const auto type = AudioMsgId::Type::Song;
const auto current = ::Media::Player::instance()->current(type);
@ -307,11 +319,21 @@ auto ListWidget::collectSelectedItems() const -> SelectedItems {
}
MessageIdsList ListWidget::collectSelectedIds() const {
const auto selected = collectSelectedItems();
return collectSelectedIds(collectSelectedItems());
}
MessageIdsList ListWidget::collectSelectedIds(
const SelectedItems &items) const {
const auto session = &_controller->session();
return ranges::views::all(
selected.list
) | ranges::views::transform([](const SelectedItem &item) {
return item.globalId.itemId; // #TODO downloads
items.list
) | ranges::views::transform([](auto &&item) {
return item.globalId;
}) | ranges::views::filter([&](const GlobalMsgId &globalId) {
return (globalId.sessionUniqueId == session->uniqueId())
&& (session->data().message(globalId.itemId) != nullptr);
}) | ranges::views::transform([](const GlobalMsgId &globalId) {
return globalId.itemId;
}) | ranges::to_vector;
}
@ -806,19 +828,20 @@ void ListWidget::showContextMenu(
auto link = ClickHandler::getActive();
const auto itemFullId = item->fullId();
const auto owner = &session().data();
_contextMenu = base::make_unique_q<Ui::PopupMenu>(
this,
st::popupMenuWithIcons);
_contextMenu->addAction(
tr::lng_context_to_msg(tr::now),
[=] {
if (const auto item = owner->message(itemFullId)) {
_controller->parentController()->showPeerHistoryAtItem(item);
}
},
&st::menuIconShowInChat);
if (item->isHistoryEntry()) {
_contextMenu->addAction(
tr::lng_context_to_msg(tr::now),
[=] {
if (const auto item = MessageByGlobalId(globalId)) {
goToMessageClickHandler(item)->onClick({});
}
},
&st::menuIconShowInChat);
}
const auto lnkPhoto = link
? reinterpret_cast<PhotoData*>(
@ -870,12 +893,11 @@ void ListWidget::showContextMenu(
this,
[=] {
DocumentSaveClickHandler::Save(
itemFullId,
globalId.itemId,
lnkDocument,
DocumentSaveClickHandler::Mode::ToNewFile);
});
if (item->history()->peer->allowsForwarding()
&& !item->forbidsForward()) {
if (_provider->allowSaveFileAs(item, lnkDocument)) {
_contextMenu->addAction(
(isVideo
? tr::lng_context_save_video(tr::now)
@ -911,7 +933,9 @@ void ListWidget::showContextMenu(
}
if (canDeleteAll()) {
_contextMenu->addAction(
tr::lng_context_delete_selected(tr::now),
(_controller->isDownloads()
? u"Delete from disk"_q
: tr::lng_context_delete_selected(tr::now)),
crl::guard(this, [this] {
deleteSelected();
}),
@ -925,18 +949,28 @@ void ListWidget::showContextMenu(
&st::menuIconSelect);
} else {
if (overSelected != SelectionState::NotOverSelectedItems) {
if (item->allowsForward()) {
const auto selectionData = _provider->computeSelectionData(
item,
FullSelection);
if (selectionData.canForward) {
_contextMenu->addAction(
tr::lng_context_forward_msg(tr::now),
crl::guard(this, [=] { forwardItem(globalId); }),
&st::menuIconForward);
}
if (item->canDelete()) {
_contextMenu->addAction(Ui::DeleteMessageContextAction(
_contextMenu->menu(),
crl::guard(this, [=] { deleteItem(globalId); }),
item->ttlDestroyAt(),
[=] { _contextMenu = nullptr; }));
if (selectionData.canDelete) {
if (_controller->isDownloads()) {
_contextMenu->addAction(
u"Delete from disk"_q,
crl::guard(this, [=] { deleteItem(globalId); }),
&st::menuIconDelete);
} else {
_contextMenu->addAction(Ui::DeleteMessageContextAction(
_contextMenu->menu(),
crl::guard(this, [=] { deleteItem(globalId); }),
item->ttlDestroyAt(),
[=] { _contextMenu = nullptr; }));
}
}
}
if (!_provider->hasSelectRestriction()) {
@ -1005,36 +1039,62 @@ void ListWidget::forwardItems(MessageIdsList &&items) {
}
void ListWidget::deleteSelected() {
if (const auto box = deleteItems(collectSelectedIds())) {
box->setDeleteConfirmedCallback(crl::guard(this, [=]{
clearSelected();
}));
}
deleteItems(collectSelectedItems(), crl::guard(this, [=]{
clearSelected();
}));
}
void ListWidget::deleteItem(GlobalMsgId globalId) {
const auto session = &_controller->session();
if (globalId.sessionUniqueId == session->uniqueId()) {
if (const auto item = session->data().message(globalId.itemId)) {
deleteItems({ 1, item->fullId() });
if (const auto item = MessageByGlobalId(globalId)) {
auto items = SelectedItems(_provider->type());
items.list.push_back(SelectedItem(item->globalId()));
const auto selectionData = _provider->computeSelectionData(
item,
FullSelection);
items.list.back().canDelete = selectionData.canDelete;
items.list.back().canForward = selectionData.canForward;
deleteItems(std::move(items));
}
}
void ListWidget::deleteItems(SelectedItems &&items, Fn<void()> confirmed) {
const auto window = _controller->parentController();
if (items.list.empty()) {
return;
} else if (_controller->isDownloads()) {
const auto phrase = (items.list.size() == 1)
? u"Do you want to delete this file?"_q
: u"Do you want to delete X files?"_q;
const auto deleteSure = [=] {
const auto ids = ranges::views::all(
items.list
) | ranges::views::transform([](const SelectedItem &item) {
return item.globalId;
}) | ranges::to_vector;
Core::App().downloadManager().deleteFiles(ids);
if (const auto box = _actionBoxWeak.data()) {
box->closeBox();
}
};
setActionBoxWeak(window->show(Box<Ui::ConfirmBox>(
phrase,
tr::lng_box_delete(tr::now),
st::attentionBoxButton,
deleteSure)));
} else if (auto list = collectSelectedIds(items); !list.empty()) {
auto box = Box<DeleteMessagesBox>(
&_controller->session(),
std::move(list));
const auto weak = box.data();
window->show(std::move(box));
setActionBoxWeak(weak);
if (confirmed) {
weak->setDeleteConfirmedCallback(std::move(confirmed));
}
}
// #TODO downloads
}
DeleteMessagesBox *ListWidget::deleteItems(MessageIdsList &&items) {
if (!items.empty()) {
const auto box = Ui::show(
Box<DeleteMessagesBox>(
&_controller->session(),
std::move(items))).data();
setActionBoxWeak(box);
return box;
}
return nullptr;
}
void ListWidget::setActionBoxWeak(QPointer<Ui::RpWidget> box) {
void ListWidget::setActionBoxWeak(QPointer<Ui::BoxContent> box) {
if ((_actionBoxWeak = box)) {
_actionBoxWeakLifetime = _actionBoxWeak->alive(
) | rpl::start_with_done([weak = Ui::MakeWeak(this)]{
@ -1087,7 +1147,11 @@ void ListWidget::switchToWordSelection() {
void ListWidget::applyItemSelection(
HistoryItem *item,
TextSelection selection) {
if (item && ChangeItemSelection(_selected, item, selection)) {
if (item
&& ChangeItemSelection(
_selected,
item,
_provider->computeSelectionData(item, selection))) {
repaintItem(item);
pushSelectedItems();
}
@ -1624,7 +1688,10 @@ void ListWidget::applyDragSelection() {
void ListWidget::applyDragSelection(SelectedMap &applyTo) const {
if (_dragSelectAction == DragSelectAction::Selecting) {
for (auto &[item, data] : _dragSelected) {
ChangeItemSelection(applyTo, item, FullSelection);
ChangeItemSelection(
applyTo,
item,
_provider->computeSelectionData(item, FullSelection));
}
} else if (_dragSelectAction == DragSelectAction::Deselecting) {
for (auto &[item, data] : _dragSelected) {

View File

@ -27,6 +27,7 @@ enum class PointState : char;
namespace Ui {
class PopupMenu;
class BoxContent;
} // namespace Ui
namespace Overview {
@ -65,9 +66,7 @@ public:
rpl::producer<int> scrollToRequests() const;
rpl::producer<SelectedItems> selectedListValue() const;
void cancelSelection() {
clearSelected();
}
void selectionAction(SelectionAction action);
QRect getCurrentSongGeometry();
rpl::producer<> checkForHide() const {
@ -152,7 +151,6 @@ private:
void setupSelectRestriction();
QMargins padding() const;
bool isMyItem(not_null<const HistoryItem*> item) const;
bool isItemLayout(
not_null<const HistoryItem*> item,
BaseLayout *layout) const;
@ -168,6 +166,8 @@ private:
[[nodiscard]] SelectedItems collectSelectedItems() const;
[[nodiscard]] MessageIdsList collectSelectedIds() const;
[[nodiscard]] MessageIdsList collectSelectedIds(
const SelectedItems &items) const;
void pushSelectedItems();
[[nodiscard]] bool hasSelected() const;
[[nodiscard]] bool isSelectedItem(
@ -182,7 +182,7 @@ private:
void forwardItems(MessageIdsList &&items);
void deleteSelected();
void deleteItem(GlobalMsgId globalId);
DeleteMessagesBox *deleteItems(MessageIdsList &&items);
void deleteItems(SelectedItems &&items, Fn<void()> confirmed = nullptr);
void applyItemSelection(
HistoryItem *item,
TextSelection selection);
@ -254,7 +254,7 @@ private:
void checkMoveToOtherViewer();
void clearHeavyItems();
void setActionBoxWeak(QPointer<Ui::RpWidget> box);
void setActionBoxWeak(QPointer<Ui::BoxContent> box);
const not_null<AbstractController*> _controller;
const std::unique_ptr<ListProvider> _provider;
@ -290,7 +290,7 @@ private:
base::unique_qptr<Ui::PopupMenu> _contextMenu;
rpl::event_stream<> _checkForHide;
QPointer<Ui::RpWidget> _actionBoxWeak;
QPointer<Ui::BoxContent> _actionBoxWeak;
rpl::lifetime _actionBoxWeakLifetime;
QPoint _trippleClickPoint;

View File

@ -461,6 +461,25 @@ std::unique_ptr<BaseLayout> Provider::createLayout(
Unexpected("Type in ListWidget::createLayout()");
}
ListItemSelectionData Provider::computeSelectionData(
not_null<const HistoryItem*> item,
TextSelection selection) {
auto result = ListItemSelectionData(selection);
result.canDelete = item->canDelete();
result.canForward = item->allowsForward();
return result;
}
bool Provider::allowSaveFileAs(
not_null<const HistoryItem*> item,
not_null<DocumentData*> document) {
return item->allowsForward();
}
std::optional<QString> Provider::deleteMenuPhrase() {
return std::nullopt;
}
void Provider::applyDragSelection(
ListSelectedMap &selected,
not_null<const HistoryItem*> fromItem,
@ -480,10 +499,11 @@ void Provider::applyDragSelection(
for (auto &layoutItem : _layouts) {
auto &&universalId = layoutItem.first;
if (universalId <= fromId && universalId > tillId) {
const auto item = layoutItem.second.item->getItem();
ChangeItemSelection(
selected,
layoutItem.second.item->getItem(),
FullSelection);
item,
computeSelectionData(item, FullSelection));
}
}
}

View File

@ -46,6 +46,9 @@ public:
not_null<const HistoryItem*> a,
not_null<const HistoryItem*> b) override;
ListItemSelectionData computeSelectionData(
not_null<const HistoryItem*> item,
TextSelection selection) override;
void applyDragSelection(
ListSelectedMap &selected,
not_null<const HistoryItem*> fromItem,
@ -53,6 +56,11 @@ public:
not_null<const HistoryItem*> tillItem,
bool skipTill) override;
bool allowSaveFileAs(
not_null<const HistoryItem*> item,
not_null<DocumentData*> document) override;
std::optional<QString> deleteMenuPhrase() override;
void saveState(
not_null<Memento*> memento,
ListScrollTopState scrollState) override;

View File

@ -93,8 +93,8 @@ rpl::producer<SelectedItems> Widget::selectedListValue() const {
return _inner->selectedListValue();
}
void Widget::cancelSelection() {
_inner->cancelSelection();
void Widget::selectionAction(SelectionAction action) {
_inner->selectionAction(action);
}
void Widget::setIsStackBottom(bool isStackBottom) {

View File

@ -99,7 +99,7 @@ public:
not_null<Memento*> memento);
rpl::producer<SelectedItems> selectedListValue() const override;
void cancelSelection() override;
void selectionAction(SelectionAction action) override;
private:
void saveState(not_null<Memento*> memento);

View File

@ -679,7 +679,7 @@ void InnerWidget::setupContent() {
st::boxRowPadding.right(),
st::boxMediumSkip });
for (const auto &answer : _poll->answers) {
const auto session = &_controller->parentController()->session();
const auto session = &_controller->session();
const auto controller = CreateAnswerRows(
_content,
_visibleTop.value(),

View File

@ -924,7 +924,7 @@ Document::Document(
const style::OverviewFileLayout &st)
: RadialProgressItem(delegate, parent)
, _data(fields.document)
, _msgl(goToMessageClickHandler(parent))
, _msgl(parent->isHistoryEntry() ? goToMessageClickHandler(parent) : nullptr)
, _namel(std::make_shared<DocumentOpenClickHandler>(
_data,
crl::guard(this, [=](FullMsgId id) {
@ -1189,7 +1189,9 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con
p.drawTextLeft(nameleft, statustop, _width, _status.text());
}
if (datetop >= 0 && clip.intersects(style::rtlrect(nameleft, datetop, _datew, st::normalFont->height, _width))) {
p.setFont(ClickHandler::showAsActive(_msgl) ? st::normalFont->underline() : st::normalFont);
p.setFont((_msgl && ClickHandler::showAsActive(_msgl))
? st::normalFont->underline()
: st::normalFont);
p.setPen(st::mediaInFg);
p.drawTextLeft(nameleft, datetop, _width, _date, _datew);
}

View File

@ -106,7 +106,14 @@ void ActionWithTimer::paint(Painter &p) {
paintRipple(p, 0, 0);
}
st::menuIconDelete.paint(p, _st.itemIconPosition, width());
const auto normalHeight = _st.itemPadding.top()
+ _st.itemStyle.font->height
+ _st.itemPadding.bottom();
const auto deltaHeight = _height - normalHeight;
st::menuIconDelete.paint(
p,
_st.itemIconPosition + QPoint(0, deltaHeight / 2),
width());
p.setPen(selected ? _st.itemFgOver : _st.itemFg);
_text.drawLeftElided(

View File

@ -1083,7 +1083,7 @@ void BlockSenderFromRepliesBox(
Window::ClearReply{ id });
}
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
not_null<Window::SessionNavigation*> navigation,
Data::ForwardDraft &&draft,
FnMut<void()> &&successCallback) {
@ -1130,7 +1130,7 @@ QPointer<Ui::RpWidget> ShowForwardMessagesBox(
return weak->data();
}
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
not_null<Window::SessionNavigation*> navigation,
MessageIdsList &&items,
FnMut<void()> &&successCallback) {
@ -1140,7 +1140,7 @@ QPointer<Ui::RpWidget> ShowForwardMessagesBox(
std::move(successCallback));
}
QPointer<Ui::RpWidget> ShowSendNowMessagesBox(
QPointer<Ui::BoxContent> ShowSendNowMessagesBox(
not_null<Window::SessionNavigation*> navigation,
not_null<History*> history,
MessageIdsList &&items,

View File

@ -15,6 +15,7 @@ class History;
namespace Ui {
class RpWidget;
class BoxContent;
class GenericBox;
} // namespace Ui
@ -98,16 +99,16 @@ void ToggleHistoryArchived(not_null<History*> history, bool archived);
Fn<void()> ClearHistoryHandler(not_null<PeerData*> peer);
Fn<void()> DeleteAndLeaveHandler(not_null<PeerData*> peer);
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
not_null<Window::SessionNavigation*> navigation,
Data::ForwardDraft &&draft,
FnMut<void()> &&successCallback = nullptr);
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
not_null<Window::SessionNavigation*> navigation,
MessageIdsList &&items,
FnMut<void()> &&successCallback = nullptr);
QPointer<Ui::RpWidget> ShowSendNowMessagesBox(
QPointer<Ui::BoxContent> ShowSendNowMessagesBox(
not_null<Window::SessionNavigation*> navigation,
not_null<History*> history,
MessageIdsList &&items,