Show correct downloads elements context menu.
This commit is contained in:
parent
daadf7e2a1
commit
e89c95551f
|
@ -2601,7 +2601,9 @@ void GroupCall::requestCurrentTimeStart(
|
||||||
});
|
});
|
||||||
}).fail([=] {
|
}).fail([=] {
|
||||||
finish(0);
|
finish(0);
|
||||||
}).handleAllErrors().send();
|
}).handleAllErrors().toDC(
|
||||||
|
MTP::groupCallStreamDcId(_broadcastDcId)
|
||||||
|
).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupCall::requestCurrentTimeCancel(
|
void GroupCall::requestCurrentTimeCancel(
|
||||||
|
|
|
@ -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
|
auto DownloadManager::loadingList() const
|
||||||
-> ranges::any_view<const DownloadingId*, ranges::category::input> {
|
-> ranges::any_view<const DownloadingId*, ranges::category::input> {
|
||||||
return ranges::views::all(
|
return ranges::views::all(
|
||||||
|
|
|
@ -87,6 +87,8 @@ public:
|
||||||
const QString &path,
|
const QString &path,
|
||||||
DownloadDate started);
|
DownloadDate started);
|
||||||
|
|
||||||
|
void deleteFiles(const std::vector<GlobalMsgId> &ids);
|
||||||
|
|
||||||
[[nodiscard]] auto loadingList() const
|
[[nodiscard]] auto loadingList() const
|
||||||
-> ranges::any_view<const DownloadingId*, ranges::category::input>;
|
-> ranges::any_view<const DownloadingId*, ranges::category::input>;
|
||||||
[[nodiscard]] DownloadProgress loadingProgress() const;
|
[[nodiscard]] DownloadProgress loadingProgress() const;
|
||||||
|
|
|
@ -1308,20 +1308,28 @@ HistoryItem::~HistoryItem() {
|
||||||
applyTTL(0);
|
applyTTL(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryItem *MessageByGlobalId(GlobalMsgId globalId) {
|
Main::Session *SessionByUniqueId(uint64 sessionUniqueId) {
|
||||||
if (!globalId.sessionUniqueId || !globalId.itemId) {
|
if (!sessionUniqueId) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
for (const auto &[index, account] : Core::App().domain().accounts()) {
|
for (const auto &[index, account] : Core::App().domain().accounts()) {
|
||||||
if (const auto session = account->maybeSession()) {
|
if (const auto session = account->maybeSession()) {
|
||||||
if (session->uniqueId() == globalId.sessionUniqueId) {
|
if (session->uniqueId() == sessionUniqueId) {
|
||||||
return session->data().message(globalId.itemId);
|
return session;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
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) {
|
QDateTime ItemDateTime(not_null<const HistoryItem*> item) {
|
||||||
return base::unixtime::parse(item->date());
|
return base::unixtime::parse(item->date());
|
||||||
}
|
}
|
||||||
|
|
|
@ -495,6 +495,7 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] Main::Session *SessionByUniqueId(uint64 sessionUniqueId);
|
||||||
[[nodiscard]] HistoryItem *MessageByGlobalId(GlobalMsgId globalId);
|
[[nodiscard]] HistoryItem *MessageByGlobalId(GlobalMsgId globalId);
|
||||||
|
|
||||||
[[nodiscard]] QDateTime ItemDateTime(not_null<const HistoryItem*> item);
|
[[nodiscard]] QDateTime ItemDateTime(not_null<const HistoryItem*> item);
|
||||||
|
|
|
@ -148,8 +148,8 @@ rpl::producer<SelectedItems> InnerWidget::selectedListValue() const {
|
||||||
) | rpl::flatten_latest();
|
) | rpl::flatten_latest();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::cancelSelection() {
|
void InnerWidget::selectionAction(SelectionAction action) {
|
||||||
_list->cancelSelection();
|
_list->selectionAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerWidget::~InnerWidget() = default;
|
InnerWidget::~InnerWidget() = default;
|
||||||
|
|
|
@ -21,6 +21,7 @@ namespace Info {
|
||||||
|
|
||||||
class Controller;
|
class Controller;
|
||||||
struct SelectedItems;
|
struct SelectedItems;
|
||||||
|
enum class SelectionAction;
|
||||||
|
|
||||||
namespace Media {
|
namespace Media {
|
||||||
class ListWidget;
|
class ListWidget;
|
||||||
|
@ -46,7 +47,7 @@ public:
|
||||||
|
|
||||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
||||||
rpl::producer<SelectedItems> selectedListValue() const;
|
rpl::producer<SelectedItems> selectedListValue() const;
|
||||||
void cancelSelection();
|
void selectionAction(SelectionAction action);
|
||||||
|
|
||||||
~InnerWidget();
|
~InnerWidget();
|
||||||
|
|
||||||
|
|
|
@ -335,13 +335,33 @@ std::unique_ptr<BaseLayout> Provider::createLayout(
|
||||||
return nullptr;
|
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(
|
void Provider::applyDragSelection(
|
||||||
ListSelectedMap &selected,
|
ListSelectedMap &selected,
|
||||||
not_null<const HistoryItem*> fromItem,
|
not_null<const HistoryItem*> fromItem,
|
||||||
bool skipFrom,
|
bool skipFrom,
|
||||||
not_null<const HistoryItem*> tillItem,
|
not_null<const HistoryItem*> tillItem,
|
||||||
bool skipTill) {
|
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(
|
void Provider::saveState(
|
||||||
|
|
|
@ -53,6 +53,9 @@ public:
|
||||||
not_null<const HistoryItem*> a,
|
not_null<const HistoryItem*> a,
|
||||||
not_null<const HistoryItem*> b) override;
|
not_null<const HistoryItem*> b) override;
|
||||||
|
|
||||||
|
Media::ListItemSelectionData computeSelectionData(
|
||||||
|
not_null<const HistoryItem*> item,
|
||||||
|
TextSelection selection) override;
|
||||||
void applyDragSelection(
|
void applyDragSelection(
|
||||||
Media::ListSelectedMap &selected,
|
Media::ListSelectedMap &selected,
|
||||||
not_null<const HistoryItem*> fromItem,
|
not_null<const HistoryItem*> fromItem,
|
||||||
|
@ -60,6 +63,11 @@ public:
|
||||||
not_null<const HistoryItem*> tillItem,
|
not_null<const HistoryItem*> tillItem,
|
||||||
bool skipTill) override;
|
bool skipTill) override;
|
||||||
|
|
||||||
|
bool allowSaveFileAs(
|
||||||
|
not_null<const HistoryItem*> item,
|
||||||
|
not_null<DocumentData*> document) override;
|
||||||
|
std::optional<QString> deleteMenuPhrase() override;
|
||||||
|
|
||||||
void saveState(
|
void saveState(
|
||||||
not_null<Media::Memento*> memento,
|
not_null<Media::Memento*> memento,
|
||||||
Media::ListScrollTopState scrollState) override;
|
Media::ListScrollTopState scrollState) override;
|
||||||
|
|
|
@ -93,6 +93,14 @@ void Widget::restoreState(not_null<Memento*> memento) {
|
||||||
scrollTopRestore(memento->scrollTop());
|
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) {
|
std::shared_ptr<Info::Memento> Make(not_null<UserData*> self) {
|
||||||
return std::make_shared<Info::Memento>(
|
return std::make_shared<Info::Memento>(
|
||||||
std::vector<std::shared_ptr<ContentMemento>>(
|
std::vector<std::shared_ptr<ContentMemento>>(
|
||||||
|
|
|
@ -54,6 +54,9 @@ public:
|
||||||
const QRect &geometry,
|
const QRect &geometry,
|
||||||
not_null<Memento*> memento);
|
not_null<Memento*> memento);
|
||||||
|
|
||||||
|
rpl::producer<SelectedItems> selectedListValue() const override;
|
||||||
|
void selectionAction(SelectionAction action) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void saveState(not_null<Memento*> memento);
|
void saveState(not_null<Memento*> memento);
|
||||||
void restoreState(not_null<Memento*> memento);
|
void restoreState(not_null<Memento*> memento);
|
||||||
|
|
|
@ -70,7 +70,7 @@ public:
|
||||||
QRect floatPlayerAvailableRect() const;
|
QRect floatPlayerAvailableRect() const;
|
||||||
|
|
||||||
virtual rpl::producer<SelectedItems> selectedListValue() const;
|
virtual rpl::producer<SelectedItems> selectedListValue() const;
|
||||||
virtual void cancelSelection() {
|
virtual void selectionAction(SelectionAction action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void saveChanges(FnMut<void()> done);
|
virtual void saveChanges(FnMut<void()> done);
|
||||||
|
|
|
@ -303,7 +303,7 @@ rpl::producer<bool> Controller::searchEnabledByContent() const {
|
||||||
rpl::producer<QString> Controller::mediaSourceQueryValue() const {
|
rpl::producer<QString> Controller::mediaSourceQueryValue() const {
|
||||||
return _searchController
|
return _searchController
|
||||||
? _searchController->currentQueryValue()
|
? _searchController->currentQueryValue()
|
||||||
: rpl::single(QString()); // #TODO downloads
|
: rpl::single(QString()); // #TODO downloads search
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<SparseIdsMergedSlice> Controller::mediaSource(
|
rpl::producer<SparseIdsMergedSlice> Controller::mediaSource(
|
||||||
|
|
|
@ -434,8 +434,8 @@ SelectedItems TopBar::takeSelectedItems() {
|
||||||
return std::move(_selectedItems);
|
return std::move(_selectedItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<> TopBar::cancelSelectionRequests() const {
|
rpl::producer<SelectionAction> TopBar::selectionActionRequests() const {
|
||||||
return _cancelSelectionClicks.events();
|
return _selectionActionRequests.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TopBar::updateSelectionState() {
|
void TopBar::updateSelectionState() {
|
||||||
|
@ -466,9 +466,10 @@ void TopBar::createSelectionControls() {
|
||||||
st::infoTopBarScale));
|
st::infoTopBarScale));
|
||||||
_cancelSelection->setDuration(st::infoTopBarDuration);
|
_cancelSelection->setDuration(st::infoTopBarDuration);
|
||||||
_cancelSelection->entity()->clicks(
|
_cancelSelection->entity()->clicks(
|
||||||
) | rpl::to_empty
|
) | rpl::map_to(
|
||||||
| rpl::start_to_stream(
|
SelectionAction::Clear
|
||||||
_cancelSelectionClicks,
|
) | rpl::start_to_stream(
|
||||||
|
_selectionActionRequests,
|
||||||
_cancelSelection->lifetime());
|
_cancelSelection->lifetime());
|
||||||
_selectionText = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::LabelWithNumbers>>(
|
_selectionText = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::LabelWithNumbers>>(
|
||||||
this,
|
this,
|
||||||
|
@ -488,7 +489,12 @@ void TopBar::createSelectionControls() {
|
||||||
_forward.data(),
|
_forward.data(),
|
||||||
[this] { return selectionMode() && _canForward; });
|
[this] { return selectionMode() && _canForward; });
|
||||||
_forward->setDuration(st::infoTopBarDuration);
|
_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);
|
_forward->entity()->setVisible(_canForward);
|
||||||
_delete = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
|
_delete = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
|
||||||
this,
|
this,
|
||||||
|
@ -498,7 +504,12 @@ void TopBar::createSelectionControls() {
|
||||||
_delete.data(),
|
_delete.data(),
|
||||||
[this] { return selectionMode() && _canDelete; });
|
[this] { return selectionMode() && _canDelete; });
|
||||||
_delete->setDuration(st::infoTopBarDuration);
|
_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);
|
_delete->entity()->setVisible(_canDelete);
|
||||||
|
|
||||||
updateControlsGeometry(width());
|
updateControlsGeometry(width());
|
||||||
|
@ -541,50 +552,12 @@ bool TopBar::searchMode() const {
|
||||||
return _searchModeAvailable && _searchModeEnabled;
|
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() {
|
void TopBar::performForward() {
|
||||||
auto items = collectItems();
|
_selectionActionRequests.fire(SelectionAction::Forward);
|
||||||
if (items.empty()) {
|
|
||||||
_cancelSelectionClicks.fire({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Window::ShowForwardMessagesBox(
|
|
||||||
_navigation,
|
|
||||||
std::move(items),
|
|
||||||
[weak = Ui::MakeWeak(this)] {
|
|
||||||
if (weak) {
|
|
||||||
weak->_cancelSelectionClicks.fire({});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TopBar::performDelete() {
|
void TopBar::performDelete() {
|
||||||
// #TODO downloads
|
_selectionActionRequests.fire(SelectionAction::Delete);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<QString> TitleValue(
|
rpl::producer<QString> TitleValue(
|
||||||
|
|
|
@ -83,7 +83,8 @@ public:
|
||||||
void setSelectedItems(SelectedItems &&items);
|
void setSelectedItems(SelectedItems &&items);
|
||||||
SelectedItems takeSelectedItems();
|
SelectedItems takeSelectedItems();
|
||||||
|
|
||||||
rpl::producer<> cancelSelectionRequests() const;
|
[[nodiscard]] auto selectionActionRequests() const
|
||||||
|
-> rpl::producer<SelectionAction>;
|
||||||
|
|
||||||
void finishAnimating() {
|
void finishAnimating() {
|
||||||
updateControlsVisibility(anim::type::instant);
|
updateControlsVisibility(anim::type::instant);
|
||||||
|
@ -115,9 +116,7 @@ private:
|
||||||
[[nodiscard]] bool computeCanForward() const;
|
[[nodiscard]] bool computeCanForward() const;
|
||||||
void updateSelectionState();
|
void updateSelectionState();
|
||||||
void createSelectionControls();
|
void createSelectionControls();
|
||||||
void clearSelectionControls();
|
|
||||||
|
|
||||||
MessageIdsList collectItems() const;
|
|
||||||
void performForward();
|
void performForward();
|
||||||
void performDelete();
|
void performDelete();
|
||||||
|
|
||||||
|
@ -161,7 +160,7 @@ private:
|
||||||
QPointer<Ui::FadeWrap<Ui::LabelWithNumbers>> _selectionText;
|
QPointer<Ui::FadeWrap<Ui::LabelWithNumbers>> _selectionText;
|
||||||
QPointer<Ui::FadeWrap<Ui::IconButton>> _forward;
|
QPointer<Ui::FadeWrap<Ui::IconButton>> _forward;
|
||||||
QPointer<Ui::FadeWrap<Ui::IconButton>> _delete;
|
QPointer<Ui::FadeWrap<Ui::IconButton>> _delete;
|
||||||
rpl::event_stream<> _cancelSelectionClicks;
|
rpl::event_stream<SelectionAction> _selectionActionRequests;
|
||||||
|
|
||||||
using UpdateCallback = Fn<bool(anim::type)>;
|
using UpdateCallback = Fn<bool(anim::type)>;
|
||||||
std::map<QObject*, UpdateCallback> _updateControlCallbacks;
|
std::map<QObject*, UpdateCallback> _updateControlCallbacks;
|
||||||
|
|
|
@ -343,9 +343,9 @@ void WrapWidget::createTopBar() {
|
||||||
_controller.get(),
|
_controller.get(),
|
||||||
TopBarStyle(wrapValue),
|
TopBarStyle(wrapValue),
|
||||||
std::move(selectedItems));
|
std::move(selectedItems));
|
||||||
_topBar->cancelSelectionRequests(
|
_topBar->selectionActionRequests(
|
||||||
) | rpl::start_with_next([this] {
|
) | rpl::start_with_next([=](SelectionAction action) {
|
||||||
_content->cancelSelection();
|
_content->selectionAction(action);
|
||||||
}, _topBar->lifetime());
|
}, _topBar->lifetime());
|
||||||
|
|
||||||
_topBar->setTitle(TitleValue(
|
_topBar->setTitle(TitleValue(
|
||||||
|
|
|
@ -66,7 +66,12 @@ struct SelectedItems {
|
||||||
|
|
||||||
Storage::SharedMediaType type;
|
Storage::SharedMediaType type;
|
||||||
std::vector<SelectedItem> list;
|
std::vector<SelectedItem> list;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SelectionAction {
|
||||||
|
Clear,
|
||||||
|
Forward,
|
||||||
|
Delete,
|
||||||
};
|
};
|
||||||
|
|
||||||
class WrapWidget final : public Window::SectionWidget {
|
class WrapWidget final : public Window::SectionWidget {
|
||||||
|
|
|
@ -28,22 +28,19 @@ UniversalMsgId GetUniversalId(not_null<const BaseLayout*> layout) {
|
||||||
bool ChangeItemSelection(
|
bool ChangeItemSelection(
|
||||||
ListSelectedMap &selected,
|
ListSelectedMap &selected,
|
||||||
not_null<const HistoryItem*> item,
|
not_null<const HistoryItem*> item,
|
||||||
TextSelection selection) {
|
ListItemSelectionData selectionData) {
|
||||||
const auto changeExisting = [&](auto it) {
|
const auto changeExisting = [&](auto it) {
|
||||||
if (it == selected.cend()) {
|
if (it == selected.cend()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (it->second.text != selection) {
|
} else if (it->second != selectionData) {
|
||||||
it->second.text = selection;
|
it->second = selectionData;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if (selected.size() < MaxSelectedItems) {
|
if (selected.size() < MaxSelectedItems) {
|
||||||
const auto [i, ok] = selected.try_emplace(item, selection);
|
const auto [i, ok] = selected.try_emplace(item, selectionData);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
// #TODO downloads
|
|
||||||
i->second.canDelete = item->canDelete();
|
|
||||||
i->second.canForward = item->allowsForward();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return changeExisting(i);
|
return changeExisting(i);
|
||||||
|
|
|
@ -32,6 +32,14 @@ struct ListItemSelectionData {
|
||||||
bool canForward = false;
|
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<
|
using ListSelectedMap = base::flat_map<
|
||||||
not_null<const HistoryItem*>,
|
not_null<const HistoryItem*>,
|
||||||
ListItemSelectionData,
|
ListItemSelectionData,
|
||||||
|
@ -83,7 +91,7 @@ using UniversalMsgId = MsgId;
|
||||||
bool ChangeItemSelection(
|
bool ChangeItemSelection(
|
||||||
ListSelectedMap &selected,
|
ListSelectedMap &selected,
|
||||||
not_null<const HistoryItem*> item,
|
not_null<const HistoryItem*> item,
|
||||||
TextSelection selection);
|
ListItemSelectionData selectionData);
|
||||||
|
|
||||||
class ListSectionDelegate {
|
class ListSectionDelegate {
|
||||||
public:
|
public:
|
||||||
|
@ -132,6 +140,9 @@ public:
|
||||||
not_null<const HistoryItem*> a,
|
not_null<const HistoryItem*> a,
|
||||||
not_null<const HistoryItem*> b) = 0;
|
not_null<const HistoryItem*> b) = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual ListItemSelectionData computeSelectionData(
|
||||||
|
not_null<const HistoryItem*> item,
|
||||||
|
TextSelection selection) = 0;
|
||||||
virtual void applyDragSelection(
|
virtual void applyDragSelection(
|
||||||
ListSelectedMap &selected,
|
ListSelectedMap &selected,
|
||||||
not_null<const HistoryItem*> fromItem,
|
not_null<const HistoryItem*> fromItem,
|
||||||
|
@ -139,6 +150,11 @@ public:
|
||||||
not_null<const HistoryItem*> tillItem,
|
not_null<const HistoryItem*> tillItem,
|
||||||
bool skipTill) = 0;
|
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(
|
virtual void saveState(
|
||||||
not_null<Memento*> memento,
|
not_null<Memento*> memento,
|
||||||
ListScrollTopState scrollState) = 0;
|
ListScrollTopState scrollState) = 0;
|
||||||
|
|
|
@ -281,8 +281,8 @@ rpl::producer<SelectedItems> InnerWidget::selectedListValue() const {
|
||||||
) | rpl::flatten_latest();
|
) | rpl::flatten_latest();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::cancelSelection() {
|
void InnerWidget::selectionAction(SelectionAction action) {
|
||||||
_list->cancelSelection();
|
_list->selectionAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerWidget::~InnerWidget() = default;
|
InnerWidget::~InnerWidget() = default;
|
||||||
|
|
|
@ -48,7 +48,7 @@ public:
|
||||||
|
|
||||||
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
|
||||||
rpl::producer<SelectedItems> selectedListValue() const;
|
rpl::producer<SelectedItems> selectedListValue() const;
|
||||||
void cancelSelection();
|
void selectionAction(SelectionAction action);
|
||||||
|
|
||||||
~InnerWidget();
|
~InnerWidget();
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_file_click_handler.h"
|
#include "data/data_file_click_handler.h"
|
||||||
#include "data/data_file_origin.h"
|
#include "data/data_file_origin.h"
|
||||||
|
#include "data/data_download_manager.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/view/history_view_cursor_state.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_session_controller.h"
|
||||||
#include "window/window_peer_menu.h"
|
#include "window/window_peer_menu.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/controls/delete_message_context_action.h"
|
#include "ui/controls/delete_message_context_action.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
#include "ui/cached_round_corners.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/delete_messages_box.h"
|
||||||
#include "boxes/peer_list_controllers.h"
|
#include "boxes/peer_list_controllers.h"
|
||||||
#include "core/file_utilities.h"
|
#include "core/file_utilities.h"
|
||||||
|
#include "core/application.h"
|
||||||
#include "facades.h"
|
#include "facades.h"
|
||||||
#include "styles/style_overview.h"
|
#include "styles/style_overview.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
|
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
|
@ -215,6 +219,14 @@ rpl::producer<SelectedItems> ListWidget::selectedListValue() const {
|
||||||
collectSelectedItems());
|
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() {
|
QRect ListWidget::getCurrentSongGeometry() {
|
||||||
const auto type = AudioMsgId::Type::Song;
|
const auto type = AudioMsgId::Type::Song;
|
||||||
const auto current = ::Media::Player::instance()->current(type);
|
const auto current = ::Media::Player::instance()->current(type);
|
||||||
|
@ -307,11 +319,21 @@ auto ListWidget::collectSelectedItems() const -> SelectedItems {
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageIdsList ListWidget::collectSelectedIds() const {
|
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(
|
return ranges::views::all(
|
||||||
selected.list
|
items.list
|
||||||
) | ranges::views::transform([](const SelectedItem &item) {
|
) | ranges::views::transform([](auto &&item) {
|
||||||
return item.globalId.itemId; // #TODO downloads
|
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;
|
}) | ranges::to_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -806,19 +828,20 @@ void ListWidget::showContextMenu(
|
||||||
|
|
||||||
auto link = ClickHandler::getActive();
|
auto link = ClickHandler::getActive();
|
||||||
|
|
||||||
const auto itemFullId = item->fullId();
|
|
||||||
const auto owner = &session().data();
|
const auto owner = &session().data();
|
||||||
_contextMenu = base::make_unique_q<Ui::PopupMenu>(
|
_contextMenu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
this,
|
this,
|
||||||
st::popupMenuWithIcons);
|
st::popupMenuWithIcons);
|
||||||
_contextMenu->addAction(
|
if (item->isHistoryEntry()) {
|
||||||
tr::lng_context_to_msg(tr::now),
|
_contextMenu->addAction(
|
||||||
[=] {
|
tr::lng_context_to_msg(tr::now),
|
||||||
if (const auto item = owner->message(itemFullId)) {
|
[=] {
|
||||||
_controller->parentController()->showPeerHistoryAtItem(item);
|
if (const auto item = MessageByGlobalId(globalId)) {
|
||||||
}
|
goToMessageClickHandler(item)->onClick({});
|
||||||
},
|
}
|
||||||
&st::menuIconShowInChat);
|
},
|
||||||
|
&st::menuIconShowInChat);
|
||||||
|
}
|
||||||
|
|
||||||
const auto lnkPhoto = link
|
const auto lnkPhoto = link
|
||||||
? reinterpret_cast<PhotoData*>(
|
? reinterpret_cast<PhotoData*>(
|
||||||
|
@ -870,12 +893,11 @@ void ListWidget::showContextMenu(
|
||||||
this,
|
this,
|
||||||
[=] {
|
[=] {
|
||||||
DocumentSaveClickHandler::Save(
|
DocumentSaveClickHandler::Save(
|
||||||
itemFullId,
|
globalId.itemId,
|
||||||
lnkDocument,
|
lnkDocument,
|
||||||
DocumentSaveClickHandler::Mode::ToNewFile);
|
DocumentSaveClickHandler::Mode::ToNewFile);
|
||||||
});
|
});
|
||||||
if (item->history()->peer->allowsForwarding()
|
if (_provider->allowSaveFileAs(item, lnkDocument)) {
|
||||||
&& !item->forbidsForward()) {
|
|
||||||
_contextMenu->addAction(
|
_contextMenu->addAction(
|
||||||
(isVideo
|
(isVideo
|
||||||
? tr::lng_context_save_video(tr::now)
|
? tr::lng_context_save_video(tr::now)
|
||||||
|
@ -911,7 +933,9 @@ void ListWidget::showContextMenu(
|
||||||
}
|
}
|
||||||
if (canDeleteAll()) {
|
if (canDeleteAll()) {
|
||||||
_contextMenu->addAction(
|
_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] {
|
crl::guard(this, [this] {
|
||||||
deleteSelected();
|
deleteSelected();
|
||||||
}),
|
}),
|
||||||
|
@ -925,18 +949,28 @@ void ListWidget::showContextMenu(
|
||||||
&st::menuIconSelect);
|
&st::menuIconSelect);
|
||||||
} else {
|
} else {
|
||||||
if (overSelected != SelectionState::NotOverSelectedItems) {
|
if (overSelected != SelectionState::NotOverSelectedItems) {
|
||||||
if (item->allowsForward()) {
|
const auto selectionData = _provider->computeSelectionData(
|
||||||
|
item,
|
||||||
|
FullSelection);
|
||||||
|
if (selectionData.canForward) {
|
||||||
_contextMenu->addAction(
|
_contextMenu->addAction(
|
||||||
tr::lng_context_forward_msg(tr::now),
|
tr::lng_context_forward_msg(tr::now),
|
||||||
crl::guard(this, [=] { forwardItem(globalId); }),
|
crl::guard(this, [=] { forwardItem(globalId); }),
|
||||||
&st::menuIconForward);
|
&st::menuIconForward);
|
||||||
}
|
}
|
||||||
if (item->canDelete()) {
|
if (selectionData.canDelete) {
|
||||||
_contextMenu->addAction(Ui::DeleteMessageContextAction(
|
if (_controller->isDownloads()) {
|
||||||
_contextMenu->menu(),
|
_contextMenu->addAction(
|
||||||
crl::guard(this, [=] { deleteItem(globalId); }),
|
u"Delete from disk"_q,
|
||||||
item->ttlDestroyAt(),
|
crl::guard(this, [=] { deleteItem(globalId); }),
|
||||||
[=] { _contextMenu = nullptr; }));
|
&st::menuIconDelete);
|
||||||
|
} else {
|
||||||
|
_contextMenu->addAction(Ui::DeleteMessageContextAction(
|
||||||
|
_contextMenu->menu(),
|
||||||
|
crl::guard(this, [=] { deleteItem(globalId); }),
|
||||||
|
item->ttlDestroyAt(),
|
||||||
|
[=] { _contextMenu = nullptr; }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!_provider->hasSelectRestriction()) {
|
if (!_provider->hasSelectRestriction()) {
|
||||||
|
@ -1005,36 +1039,62 @@ void ListWidget::forwardItems(MessageIdsList &&items) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::deleteSelected() {
|
void ListWidget::deleteSelected() {
|
||||||
if (const auto box = deleteItems(collectSelectedIds())) {
|
deleteItems(collectSelectedItems(), crl::guard(this, [=]{
|
||||||
box->setDeleteConfirmedCallback(crl::guard(this, [=]{
|
clearSelected();
|
||||||
clearSelected();
|
}));
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListWidget::deleteItem(GlobalMsgId globalId) {
|
void ListWidget::deleteItem(GlobalMsgId globalId) {
|
||||||
const auto session = &_controller->session();
|
if (const auto item = MessageByGlobalId(globalId)) {
|
||||||
if (globalId.sessionUniqueId == session->uniqueId()) {
|
auto items = SelectedItems(_provider->type());
|
||||||
if (const auto item = session->data().message(globalId.itemId)) {
|
items.list.push_back(SelectedItem(item->globalId()));
|
||||||
deleteItems({ 1, item->fullId() });
|
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) {
|
void ListWidget::setActionBoxWeak(QPointer<Ui::BoxContent> box) {
|
||||||
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) {
|
|
||||||
if ((_actionBoxWeak = box)) {
|
if ((_actionBoxWeak = box)) {
|
||||||
_actionBoxWeakLifetime = _actionBoxWeak->alive(
|
_actionBoxWeakLifetime = _actionBoxWeak->alive(
|
||||||
) | rpl::start_with_done([weak = Ui::MakeWeak(this)]{
|
) | rpl::start_with_done([weak = Ui::MakeWeak(this)]{
|
||||||
|
@ -1087,7 +1147,11 @@ void ListWidget::switchToWordSelection() {
|
||||||
void ListWidget::applyItemSelection(
|
void ListWidget::applyItemSelection(
|
||||||
HistoryItem *item,
|
HistoryItem *item,
|
||||||
TextSelection selection) {
|
TextSelection selection) {
|
||||||
if (item && ChangeItemSelection(_selected, item, selection)) {
|
if (item
|
||||||
|
&& ChangeItemSelection(
|
||||||
|
_selected,
|
||||||
|
item,
|
||||||
|
_provider->computeSelectionData(item, selection))) {
|
||||||
repaintItem(item);
|
repaintItem(item);
|
||||||
pushSelectedItems();
|
pushSelectedItems();
|
||||||
}
|
}
|
||||||
|
@ -1624,7 +1688,10 @@ void ListWidget::applyDragSelection() {
|
||||||
void ListWidget::applyDragSelection(SelectedMap &applyTo) const {
|
void ListWidget::applyDragSelection(SelectedMap &applyTo) const {
|
||||||
if (_dragSelectAction == DragSelectAction::Selecting) {
|
if (_dragSelectAction == DragSelectAction::Selecting) {
|
||||||
for (auto &[item, data] : _dragSelected) {
|
for (auto &[item, data] : _dragSelected) {
|
||||||
ChangeItemSelection(applyTo, item, FullSelection);
|
ChangeItemSelection(
|
||||||
|
applyTo,
|
||||||
|
item,
|
||||||
|
_provider->computeSelectionData(item, FullSelection));
|
||||||
}
|
}
|
||||||
} else if (_dragSelectAction == DragSelectAction::Deselecting) {
|
} else if (_dragSelectAction == DragSelectAction::Deselecting) {
|
||||||
for (auto &[item, data] : _dragSelected) {
|
for (auto &[item, data] : _dragSelected) {
|
||||||
|
|
|
@ -27,6 +27,7 @@ enum class PointState : char;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class PopupMenu;
|
class PopupMenu;
|
||||||
|
class BoxContent;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Overview {
|
namespace Overview {
|
||||||
|
@ -65,9 +66,7 @@ public:
|
||||||
|
|
||||||
rpl::producer<int> scrollToRequests() const;
|
rpl::producer<int> scrollToRequests() const;
|
||||||
rpl::producer<SelectedItems> selectedListValue() const;
|
rpl::producer<SelectedItems> selectedListValue() const;
|
||||||
void cancelSelection() {
|
void selectionAction(SelectionAction action);
|
||||||
clearSelected();
|
|
||||||
}
|
|
||||||
|
|
||||||
QRect getCurrentSongGeometry();
|
QRect getCurrentSongGeometry();
|
||||||
rpl::producer<> checkForHide() const {
|
rpl::producer<> checkForHide() const {
|
||||||
|
@ -152,7 +151,6 @@ private:
|
||||||
void setupSelectRestriction();
|
void setupSelectRestriction();
|
||||||
|
|
||||||
QMargins padding() const;
|
QMargins padding() const;
|
||||||
bool isMyItem(not_null<const HistoryItem*> item) const;
|
|
||||||
bool isItemLayout(
|
bool isItemLayout(
|
||||||
not_null<const HistoryItem*> item,
|
not_null<const HistoryItem*> item,
|
||||||
BaseLayout *layout) const;
|
BaseLayout *layout) const;
|
||||||
|
@ -168,6 +166,8 @@ private:
|
||||||
|
|
||||||
[[nodiscard]] SelectedItems collectSelectedItems() const;
|
[[nodiscard]] SelectedItems collectSelectedItems() const;
|
||||||
[[nodiscard]] MessageIdsList collectSelectedIds() const;
|
[[nodiscard]] MessageIdsList collectSelectedIds() const;
|
||||||
|
[[nodiscard]] MessageIdsList collectSelectedIds(
|
||||||
|
const SelectedItems &items) const;
|
||||||
void pushSelectedItems();
|
void pushSelectedItems();
|
||||||
[[nodiscard]] bool hasSelected() const;
|
[[nodiscard]] bool hasSelected() const;
|
||||||
[[nodiscard]] bool isSelectedItem(
|
[[nodiscard]] bool isSelectedItem(
|
||||||
|
@ -182,7 +182,7 @@ private:
|
||||||
void forwardItems(MessageIdsList &&items);
|
void forwardItems(MessageIdsList &&items);
|
||||||
void deleteSelected();
|
void deleteSelected();
|
||||||
void deleteItem(GlobalMsgId globalId);
|
void deleteItem(GlobalMsgId globalId);
|
||||||
DeleteMessagesBox *deleteItems(MessageIdsList &&items);
|
void deleteItems(SelectedItems &&items, Fn<void()> confirmed = nullptr);
|
||||||
void applyItemSelection(
|
void applyItemSelection(
|
||||||
HistoryItem *item,
|
HistoryItem *item,
|
||||||
TextSelection selection);
|
TextSelection selection);
|
||||||
|
@ -254,7 +254,7 @@ private:
|
||||||
void checkMoveToOtherViewer();
|
void checkMoveToOtherViewer();
|
||||||
void clearHeavyItems();
|
void clearHeavyItems();
|
||||||
|
|
||||||
void setActionBoxWeak(QPointer<Ui::RpWidget> box);
|
void setActionBoxWeak(QPointer<Ui::BoxContent> box);
|
||||||
|
|
||||||
const not_null<AbstractController*> _controller;
|
const not_null<AbstractController*> _controller;
|
||||||
const std::unique_ptr<ListProvider> _provider;
|
const std::unique_ptr<ListProvider> _provider;
|
||||||
|
@ -290,7 +290,7 @@ private:
|
||||||
|
|
||||||
base::unique_qptr<Ui::PopupMenu> _contextMenu;
|
base::unique_qptr<Ui::PopupMenu> _contextMenu;
|
||||||
rpl::event_stream<> _checkForHide;
|
rpl::event_stream<> _checkForHide;
|
||||||
QPointer<Ui::RpWidget> _actionBoxWeak;
|
QPointer<Ui::BoxContent> _actionBoxWeak;
|
||||||
rpl::lifetime _actionBoxWeakLifetime;
|
rpl::lifetime _actionBoxWeakLifetime;
|
||||||
|
|
||||||
QPoint _trippleClickPoint;
|
QPoint _trippleClickPoint;
|
||||||
|
|
|
@ -461,6 +461,25 @@ std::unique_ptr<BaseLayout> Provider::createLayout(
|
||||||
Unexpected("Type in ListWidget::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(
|
void Provider::applyDragSelection(
|
||||||
ListSelectedMap &selected,
|
ListSelectedMap &selected,
|
||||||
not_null<const HistoryItem*> fromItem,
|
not_null<const HistoryItem*> fromItem,
|
||||||
|
@ -480,10 +499,11 @@ void Provider::applyDragSelection(
|
||||||
for (auto &layoutItem : _layouts) {
|
for (auto &layoutItem : _layouts) {
|
||||||
auto &&universalId = layoutItem.first;
|
auto &&universalId = layoutItem.first;
|
||||||
if (universalId <= fromId && universalId > tillId) {
|
if (universalId <= fromId && universalId > tillId) {
|
||||||
|
const auto item = layoutItem.second.item->getItem();
|
||||||
ChangeItemSelection(
|
ChangeItemSelection(
|
||||||
selected,
|
selected,
|
||||||
layoutItem.second.item->getItem(),
|
item,
|
||||||
FullSelection);
|
computeSelectionData(item, FullSelection));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,9 @@ public:
|
||||||
not_null<const HistoryItem*> a,
|
not_null<const HistoryItem*> a,
|
||||||
not_null<const HistoryItem*> b) override;
|
not_null<const HistoryItem*> b) override;
|
||||||
|
|
||||||
|
ListItemSelectionData computeSelectionData(
|
||||||
|
not_null<const HistoryItem*> item,
|
||||||
|
TextSelection selection) override;
|
||||||
void applyDragSelection(
|
void applyDragSelection(
|
||||||
ListSelectedMap &selected,
|
ListSelectedMap &selected,
|
||||||
not_null<const HistoryItem*> fromItem,
|
not_null<const HistoryItem*> fromItem,
|
||||||
|
@ -53,6 +56,11 @@ public:
|
||||||
not_null<const HistoryItem*> tillItem,
|
not_null<const HistoryItem*> tillItem,
|
||||||
bool skipTill) override;
|
bool skipTill) override;
|
||||||
|
|
||||||
|
bool allowSaveFileAs(
|
||||||
|
not_null<const HistoryItem*> item,
|
||||||
|
not_null<DocumentData*> document) override;
|
||||||
|
std::optional<QString> deleteMenuPhrase() override;
|
||||||
|
|
||||||
void saveState(
|
void saveState(
|
||||||
not_null<Memento*> memento,
|
not_null<Memento*> memento,
|
||||||
ListScrollTopState scrollState) override;
|
ListScrollTopState scrollState) override;
|
||||||
|
|
|
@ -93,8 +93,8 @@ rpl::producer<SelectedItems> Widget::selectedListValue() const {
|
||||||
return _inner->selectedListValue();
|
return _inner->selectedListValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::cancelSelection() {
|
void Widget::selectionAction(SelectionAction action) {
|
||||||
_inner->cancelSelection();
|
_inner->selectionAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::setIsStackBottom(bool isStackBottom) {
|
void Widget::setIsStackBottom(bool isStackBottom) {
|
||||||
|
|
|
@ -99,7 +99,7 @@ public:
|
||||||
not_null<Memento*> memento);
|
not_null<Memento*> memento);
|
||||||
|
|
||||||
rpl::producer<SelectedItems> selectedListValue() const override;
|
rpl::producer<SelectedItems> selectedListValue() const override;
|
||||||
void cancelSelection() override;
|
void selectionAction(SelectionAction action) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void saveState(not_null<Memento*> memento);
|
void saveState(not_null<Memento*> memento);
|
||||||
|
|
|
@ -679,7 +679,7 @@ void InnerWidget::setupContent() {
|
||||||
st::boxRowPadding.right(),
|
st::boxRowPadding.right(),
|
||||||
st::boxMediumSkip });
|
st::boxMediumSkip });
|
||||||
for (const auto &answer : _poll->answers) {
|
for (const auto &answer : _poll->answers) {
|
||||||
const auto session = &_controller->parentController()->session();
|
const auto session = &_controller->session();
|
||||||
const auto controller = CreateAnswerRows(
|
const auto controller = CreateAnswerRows(
|
||||||
_content,
|
_content,
|
||||||
_visibleTop.value(),
|
_visibleTop.value(),
|
||||||
|
|
|
@ -924,7 +924,7 @@ Document::Document(
|
||||||
const style::OverviewFileLayout &st)
|
const style::OverviewFileLayout &st)
|
||||||
: RadialProgressItem(delegate, parent)
|
: RadialProgressItem(delegate, parent)
|
||||||
, _data(fields.document)
|
, _data(fields.document)
|
||||||
, _msgl(goToMessageClickHandler(parent))
|
, _msgl(parent->isHistoryEntry() ? goToMessageClickHandler(parent) : nullptr)
|
||||||
, _namel(std::make_shared<DocumentOpenClickHandler>(
|
, _namel(std::make_shared<DocumentOpenClickHandler>(
|
||||||
_data,
|
_data,
|
||||||
crl::guard(this, [=](FullMsgId id) {
|
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());
|
p.drawTextLeft(nameleft, statustop, _width, _status.text());
|
||||||
}
|
}
|
||||||
if (datetop >= 0 && clip.intersects(style::rtlrect(nameleft, datetop, _datew, st::normalFont->height, _width))) {
|
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.setPen(st::mediaInFg);
|
||||||
p.drawTextLeft(nameleft, datetop, _width, _date, _datew);
|
p.drawTextLeft(nameleft, datetop, _width, _date, _datew);
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,14 @@ void ActionWithTimer::paint(Painter &p) {
|
||||||
paintRipple(p, 0, 0);
|
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);
|
p.setPen(selected ? _st.itemFgOver : _st.itemFg);
|
||||||
_text.drawLeftElided(
|
_text.drawLeftElided(
|
||||||
|
|
|
@ -1083,7 +1083,7 @@ void BlockSenderFromRepliesBox(
|
||||||
Window::ClearReply{ id });
|
Window::ClearReply{ id });
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
not_null<Window::SessionNavigation*> navigation,
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
Data::ForwardDraft &&draft,
|
Data::ForwardDraft &&draft,
|
||||||
FnMut<void()> &&successCallback) {
|
FnMut<void()> &&successCallback) {
|
||||||
|
@ -1130,7 +1130,7 @@ QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
||||||
return weak->data();
|
return weak->data();
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
not_null<Window::SessionNavigation*> navigation,
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
MessageIdsList &&items,
|
MessageIdsList &&items,
|
||||||
FnMut<void()> &&successCallback) {
|
FnMut<void()> &&successCallback) {
|
||||||
|
@ -1140,7 +1140,7 @@ QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
||||||
std::move(successCallback));
|
std::move(successCallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointer<Ui::RpWidget> ShowSendNowMessagesBox(
|
QPointer<Ui::BoxContent> ShowSendNowMessagesBox(
|
||||||
not_null<Window::SessionNavigation*> navigation,
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
MessageIdsList &&items,
|
MessageIdsList &&items,
|
||||||
|
|
|
@ -15,6 +15,7 @@ class History;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class RpWidget;
|
class RpWidget;
|
||||||
|
class BoxContent;
|
||||||
class GenericBox;
|
class GenericBox;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
@ -98,16 +99,16 @@ void ToggleHistoryArchived(not_null<History*> history, bool archived);
|
||||||
Fn<void()> ClearHistoryHandler(not_null<PeerData*> peer);
|
Fn<void()> ClearHistoryHandler(not_null<PeerData*> peer);
|
||||||
Fn<void()> DeleteAndLeaveHandler(not_null<PeerData*> peer);
|
Fn<void()> DeleteAndLeaveHandler(not_null<PeerData*> peer);
|
||||||
|
|
||||||
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
not_null<Window::SessionNavigation*> navigation,
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
Data::ForwardDraft &&draft,
|
Data::ForwardDraft &&draft,
|
||||||
FnMut<void()> &&successCallback = nullptr);
|
FnMut<void()> &&successCallback = nullptr);
|
||||||
QPointer<Ui::RpWidget> ShowForwardMessagesBox(
|
QPointer<Ui::BoxContent> ShowForwardMessagesBox(
|
||||||
not_null<Window::SessionNavigation*> navigation,
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
MessageIdsList &&items,
|
MessageIdsList &&items,
|
||||||
FnMut<void()> &&successCallback = nullptr);
|
FnMut<void()> &&successCallback = nullptr);
|
||||||
|
|
||||||
QPointer<Ui::RpWidget> ShowSendNowMessagesBox(
|
QPointer<Ui::BoxContent> ShowSendNowMessagesBox(
|
||||||
not_null<Window::SessionNavigation*> navigation,
|
not_null<Window::SessionNavigation*> navigation,
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
MessageIdsList &&items,
|
MessageIdsList &&items,
|
||||||
|
|
Loading…
Reference in New Issue