Allow delete / forward selected in shared media.
Also use PeerListBox with a chats list with global search controller instead of HistoryHider for forward / share contact.
This commit is contained in:
parent
7b69282c7e
commit
be5f4c9a71
Binary file not shown.
After Width: | Height: | Size: 224 B |
Binary file not shown.
After Width: | Height: | Size: 398 B |
Binary file not shown.
After Width: | Height: | Size: 204 B |
Binary file not shown.
After Width: | Height: | Size: 377 B |
|
@ -100,6 +100,7 @@ void PeerListBox::prepare() {
|
|||
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
|
||||
if (_select) {
|
||||
_select->finishAnimating();
|
||||
myEnsureResized(_select);
|
||||
_scrollBottomFixed = true;
|
||||
onScrollToY(0);
|
||||
}
|
||||
|
|
|
@ -80,12 +80,31 @@ infoTopBarTitle: FlatLabel(defaultFlatLabel) {
|
|||
linkFontOver: font(14px semibold);
|
||||
}
|
||||
}
|
||||
infoTopBarClose: IconButton(infoTopBarBack) {
|
||||
icon: icon {{ "info_close", boxTitleCloseFg }};
|
||||
iconOver: icon {{ "info_close", boxTitleCloseFgOver }};
|
||||
}
|
||||
infoTopBarForward: IconButton(infoTopBarBack) {
|
||||
width: 46px;
|
||||
icon: icon {{ "info_media_forward", boxTitleCloseFg }};
|
||||
iconOver: icon {{ "info_media_forward", boxTitleCloseFgOver }};
|
||||
iconPosition: point(6px, -1px);
|
||||
rippleAreaPosition: point(1px, 6px);
|
||||
}
|
||||
infoTopBarDelete: IconButton(infoTopBarForward) {
|
||||
icon: icon {{ "info_media_delete", boxTitleCloseFg }};
|
||||
iconOver: icon {{ "info_media_delete", boxTitleCloseFgOver }};
|
||||
}
|
||||
infoTopBar: InfoTopBar {
|
||||
height: infoTopBarHeight;
|
||||
back: infoTopBarBack;
|
||||
title: infoTopBarTitle;
|
||||
titlePosition: point(23px, 18px);
|
||||
bg: windowBg;
|
||||
mediaCancel: infoTopBarClose;
|
||||
mediaActionsSkip: 4px;
|
||||
mediaForward: infoTopBarForward;
|
||||
mediaDelete: infoTopBarDelete;
|
||||
}
|
||||
|
||||
infoLayerTopBarHeight: boxLayerTitleHeight;
|
||||
|
@ -107,12 +126,27 @@ infoLayerTopBarClose: IconButton(infoLayerTopBarBack) {
|
|||
icon: infoLayerTopBarCloseIcon;
|
||||
iconOver: infoLayerTopBarCloseIconOver;
|
||||
}
|
||||
infoLayerTopBarForward: IconButton(infoLayerTopBarBack) {
|
||||
width: 45px;
|
||||
icon: icon {{ "info_media_forward", boxTitleCloseFg }};
|
||||
iconOver: icon {{ "info_media_forward", boxTitleCloseFgOver }};
|
||||
iconPosition: point(6px, -1px);
|
||||
rippleAreaPosition: point(1px, 6px);
|
||||
}
|
||||
infoLayerTopBarDelete: IconButton(infoLayerTopBarForward) {
|
||||
icon: icon {{ "info_media_delete", boxTitleCloseFg }};
|
||||
iconOver: icon {{ "info_media_delete", boxTitleCloseFgOver }};
|
||||
}
|
||||
infoLayerTopBar: InfoTopBar {
|
||||
height: infoLayerTopBarHeight;
|
||||
back: infoLayerTopBarBack;
|
||||
title: boxTitle;
|
||||
titlePosition: boxLayerTitlePosition;
|
||||
bg: boxBg;
|
||||
mediaCancel: infoLayerTopBarClose;
|
||||
mediaActionsSkip: 6px;
|
||||
mediaForward: infoLayerTopBarForward;
|
||||
mediaDelete: infoLayerTopBarDelete;
|
||||
}
|
||||
|
||||
infoMinimalWidth: 324px;
|
||||
|
|
|
@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include <rpl/never.h>
|
||||
#include <rpl/combine.h>
|
||||
#include <rpl/range.h>
|
||||
#include "window/window_controller.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
@ -156,4 +157,8 @@ QRect ContentWidget::rectForFloatPlayer() const {
|
|||
return mapToGlobal(_scroll->geometry());
|
||||
}
|
||||
|
||||
rpl::producer<SelectedItems> ContentWidget::selectedListValue() const {
|
||||
return rpl::single(SelectedItems(Storage::SharedMediaType::Photo));
|
||||
}
|
||||
|
||||
} // namespace Info
|
||||
|
|
|
@ -79,6 +79,10 @@ public:
|
|||
bool wheelEventFromFloatPlayer(QEvent *e);
|
||||
QRect rectForFloatPlayer() const;
|
||||
|
||||
virtual rpl::producer<SelectedItems> selectedListValue() const;
|
||||
virtual void cancelSelection() {
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename Widget>
|
||||
Widget *setInnerWidget(
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "info/info_top_bar_override.h"
|
||||
|
||||
#include <rpl/merge.h>
|
||||
#include "styles/style_info.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "info/info_wrap_widget.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
|
||||
namespace Info {
|
||||
|
||||
ChooseRecipientBoxController::ChooseRecipientBoxController(
|
||||
base::lambda<void(not_null<PeerData*>)> callback)
|
||||
: _callback(std::move(callback)) {
|
||||
}
|
||||
|
||||
void ChooseRecipientBoxController::prepareViewHook() {
|
||||
delegate()->peerListSetTitle(langFactory(lng_forward_choose));
|
||||
}
|
||||
|
||||
void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
_callback(row->peer());
|
||||
}
|
||||
|
||||
auto ChooseRecipientBoxController::createRow(
|
||||
not_null<History*> history) -> std::unique_ptr<Row> {
|
||||
return std::make_unique<Row>(history);
|
||||
}
|
||||
|
||||
TopBarOverride::TopBarOverride(
|
||||
QWidget *parent,
|
||||
const style::InfoTopBar &st,
|
||||
SelectedItems &&items)
|
||||
: RpWidget(parent)
|
||||
, _st(st)
|
||||
, _items(std::move(items))
|
||||
, _canDelete(computeCanDelete())
|
||||
, _cancel(this, _st.mediaCancel)
|
||||
, _text(this, generateText(), Ui::FlatLabel::InitType::Simple, _st.title)
|
||||
, _forward(this, _st.mediaForward)
|
||||
, _delete(this, _st.mediaDelete) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
||||
updateControlsVisibility();
|
||||
|
||||
_forward->addClickHandler([this] { performForward(); });
|
||||
_delete->addClickHandler([this] { performDelete(); });
|
||||
}
|
||||
|
||||
QString TopBarOverride::generateText() const {
|
||||
using Type = Storage::SharedMediaType;
|
||||
auto phrase = [&] {
|
||||
switch (_items.type) {
|
||||
case Type::Photo: return lng_profile_photos;
|
||||
case Type::Video: return lng_profile_videos;
|
||||
case Type::File: return lng_profile_files;
|
||||
case Type::MusicFile: return lng_profile_songs;
|
||||
case Type::Link: return lng_profile_shared_links;
|
||||
case Type::VoiceFile: return lng_profile_audios;
|
||||
case Type::RoundFile: return lng_profile_rounds;
|
||||
}
|
||||
Unexpected("Type in TopBarOverride::generateText()");
|
||||
}();
|
||||
return phrase(lt_count, _items.list.size());
|
||||
}
|
||||
|
||||
bool TopBarOverride::computeCanDelete() const {
|
||||
return base::find_if(_items.list, [](const SelectedItem &item) {
|
||||
return !item.canDelete;
|
||||
}) == _items.list.end();
|
||||
}
|
||||
|
||||
void TopBarOverride::setItems(SelectedItems &&items) {
|
||||
_items = std::move(items);
|
||||
_canDelete = computeCanDelete();
|
||||
|
||||
_text->setText(generateText());
|
||||
updateControlsVisibility();
|
||||
updateControlsGeometry(width());
|
||||
}
|
||||
|
||||
rpl::producer<> TopBarOverride::cancelRequests() const {
|
||||
return rpl::merge(
|
||||
_cancel->clicks(),
|
||||
_correctionCancelRequests.events());
|
||||
}
|
||||
|
||||
int TopBarOverride::resizeGetHeight(int newWidth) {
|
||||
updateControlsGeometry(newWidth);
|
||||
return _st.height;
|
||||
}
|
||||
|
||||
void TopBarOverride::updateControlsGeometry(int newWidth) {
|
||||
auto right = _st.mediaActionsSkip;
|
||||
if (_canDelete) {
|
||||
_delete->moveToRight(right, 0, newWidth);
|
||||
right += _delete->width();
|
||||
}
|
||||
_forward->moveToRight(right, 0, newWidth);
|
||||
_cancel->moveToLeft(0, 0);
|
||||
_text->moveToLeft(_cancel->width(), _st.titlePosition.y());
|
||||
}
|
||||
|
||||
void TopBarOverride::updateControlsVisibility() {
|
||||
_delete->setVisible(_canDelete);
|
||||
}
|
||||
|
||||
void TopBarOverride::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
p.fillRect(e->rect(), _st.bg);
|
||||
}
|
||||
|
||||
SelectedItemSet TopBarOverride::collectItems() const {
|
||||
auto result = SelectedItemSet();
|
||||
for (auto value : _items.list) {
|
||||
if (auto item = App::histItemById(value.msgId)) {
|
||||
result.insert(result.size(), item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void TopBarOverride::performForward() {
|
||||
auto items = collectItems();
|
||||
if (items.empty()) {
|
||||
_correctionCancelRequests.fire({});
|
||||
return;
|
||||
}
|
||||
auto callback = [items = std::move(items)](not_null<PeerData*> peer) {
|
||||
App::main()->setForwardDraft(peer->id, items);
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(
|
||||
std::make_unique<ChooseRecipientBoxController>(std::move(callback)),
|
||||
[](not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_cancel), [box] {
|
||||
box->closeBox();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
void TopBarOverride::performDelete() {
|
||||
auto items = collectItems();
|
||||
if (items.empty()) {
|
||||
_correctionCancelRequests.fire({});
|
||||
} else {
|
||||
Ui::show(Box<DeleteMessagesBox>(items));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Info
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/rp_widget.h"
|
||||
#include "info/info_wrap_widget.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
|
||||
namespace style {
|
||||
struct InfoTopBar;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
class IconButton;
|
||||
class FlatLabel;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Info {
|
||||
|
||||
class ChooseRecipientBoxController : public ChatsListBoxController {
|
||||
public:
|
||||
ChooseRecipientBoxController(
|
||||
base::lambda<void(not_null<PeerData*>)> callback);
|
||||
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
||||
protected:
|
||||
void prepareViewHook() override;
|
||||
std::unique_ptr<Row> createRow(
|
||||
not_null<History*> history) override;
|
||||
|
||||
base::lambda<void(not_null<PeerData*>)> _callback;
|
||||
|
||||
};
|
||||
|
||||
class TopBarOverride : public Ui::RpWidget {
|
||||
public:
|
||||
TopBarOverride(
|
||||
QWidget *parent,
|
||||
const style::InfoTopBar &st,
|
||||
SelectedItems &&items);
|
||||
|
||||
void setItems(SelectedItems &&items);
|
||||
|
||||
rpl::producer<> cancelRequests() const;
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
void updateControlsVisibility();
|
||||
void updateControlsGeometry(int newWidth);
|
||||
QString generateText() const;
|
||||
[[nodiscard]] bool computeCanDelete() const;
|
||||
[[nodiscard]] SelectedItemSet collectItems() const;
|
||||
|
||||
void performForward();
|
||||
void performDelete();
|
||||
|
||||
const style::InfoTopBar &_st;
|
||||
SelectedItems _items;
|
||||
bool _canDelete = false;
|
||||
object_ptr<Ui::IconButton> _cancel;
|
||||
object_ptr<Ui::FlatLabel> _text;
|
||||
object_ptr<Ui::IconButton> _forward;
|
||||
object_ptr<Ui::IconButton> _delete;
|
||||
rpl::event_stream<> _correctionCancelRequests;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace Info
|
|
@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "info/info_content_widget.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "info/info_top_bar.h"
|
||||
#include "info/info_top_bar_override.h"
|
||||
#include "ui/widgets/discrete_sliders.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
|
@ -39,6 +40,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "styles/style_profile.h"
|
||||
|
||||
namespace Info {
|
||||
namespace {
|
||||
|
||||
const style::InfoTopBar &TopBarStyle(Wrap wrap) {
|
||||
return (wrap == Wrap::Layer)
|
||||
? st::infoLayerTopBar
|
||||
: st::infoTopBar;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct WrapWidget::StackItem {
|
||||
std::unique_ptr<ContentMemento> section;
|
||||
|
@ -54,6 +64,12 @@ WrapWidget::WrapWidget(
|
|||
, _wrap(wrap)
|
||||
, _topShadow(this) {
|
||||
_topShadow->toggleOn(topShadowToggledValue());
|
||||
selectedListValue()
|
||||
| rpl::start_with_next([this](SelectedItems &&items) {
|
||||
InvokeQueued(this, [this, items = std::move(items)]() mutable {
|
||||
refreshTopBarOverride(std::move(items));
|
||||
});
|
||||
}, lifetime());
|
||||
showNewContent(memento->content());
|
||||
}
|
||||
|
||||
|
@ -182,11 +198,7 @@ void WrapWidget::setupTop(
|
|||
void WrapWidget::createTopBar(
|
||||
const Section §ion,
|
||||
PeerId peerId) {
|
||||
_topBar.create(
|
||||
this,
|
||||
(wrap() == Wrap::Layer)
|
||||
? st::infoLayerTopBar
|
||||
: st::infoTopBar);
|
||||
_topBar.create(this, TopBarStyle(wrap()));
|
||||
|
||||
_topBar->setTitle(TitleValue(
|
||||
section,
|
||||
|
@ -212,6 +224,52 @@ void WrapWidget::createTopBar(
|
|||
_topBar->show();
|
||||
}
|
||||
|
||||
void WrapWidget::refreshTopBarOverride(SelectedItems &&items) {
|
||||
if (items.list.empty()) {
|
||||
destroyTopBarOverride();
|
||||
} else if (_topBarOverride) {
|
||||
_topBarOverride->setItems(std::move(items));
|
||||
} else {
|
||||
createTopBarOverride(std::move(items));
|
||||
}
|
||||
}
|
||||
|
||||
void WrapWidget::destroyTopBarOverride() {
|
||||
if (!_topBarOverride) {
|
||||
return;
|
||||
}
|
||||
auto widget = std::exchange(_topBarOverride, nullptr);
|
||||
auto handle = weak(widget.data());
|
||||
_topBarOverrideAnimation.start([this, handle] {
|
||||
}, 1., 0., st::slideWrapDuration);
|
||||
widget.destroy();
|
||||
if (_topTabs) {
|
||||
_topTabs->show();
|
||||
} else if (_topBar) {
|
||||
_topBar->show();
|
||||
}
|
||||
}
|
||||
|
||||
void WrapWidget::createTopBarOverride(SelectedItems &&items) {
|
||||
Expects(_topBarOverride == nullptr);
|
||||
_topBarOverride.create(
|
||||
this,
|
||||
TopBarStyle(wrap()),
|
||||
std::move(items));
|
||||
if (_topTabs) {
|
||||
_topTabs->hide();
|
||||
} else if (_topBar) {
|
||||
_topBar->hide();
|
||||
}
|
||||
_topBarOverride->cancelRequests()
|
||||
| rpl::start_with_next([this](auto) {
|
||||
_content->cancelSelection();
|
||||
}, _topBarOverride->lifetime());
|
||||
_topBarOverride->moveToLeft(0, 0);
|
||||
_topBarOverride->resizeToWidth(width());
|
||||
_topBarOverride->show();
|
||||
}
|
||||
|
||||
void WrapWidget::showBackFromStack() {
|
||||
auto params = Window::SectionShow(
|
||||
Window::SectionShow::Way::Backward);
|
||||
|
@ -245,6 +303,7 @@ void WrapWidget::finishShowContent() {
|
|||
updateContentGeometry();
|
||||
_desiredHeights.fire(desiredHeightForContent());
|
||||
_desiredShadowVisibilities.fire(_content->desiredShadowVisibility());
|
||||
_selectedLists.fire(_content->selectedListValue());
|
||||
_topShadow->raise();
|
||||
_topShadow->finishAnimating();
|
||||
if (_topTabs) {
|
||||
|
@ -268,6 +327,10 @@ rpl::producer<int> WrapWidget::desiredHeightForContent() const {
|
|||
$1 + $2);
|
||||
}
|
||||
|
||||
rpl::producer<SelectedItems> WrapWidget::selectedListValue() const {
|
||||
return _selectedLists.events() | rpl::flatten_latest();
|
||||
}
|
||||
|
||||
object_ptr<ContentWidget> WrapWidget::createContent(Tab tab) {
|
||||
switch (tab) {
|
||||
case Tab::Profile: return createProfileWidget();
|
||||
|
@ -450,6 +513,9 @@ void WrapWidget::resizeEvent(QResizeEvent *e) {
|
|||
} else if (_topBar) {
|
||||
_topBar->resizeToWidth(width());
|
||||
}
|
||||
if (_topBarOverride) {
|
||||
_topBarOverride->resizeToWidth(width());
|
||||
}
|
||||
updateContentGeometry();
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ class MoveMemento;
|
|||
class ContentMemento;
|
||||
class ContentWidget;
|
||||
class TopBar;
|
||||
class TopBarOverride;
|
||||
|
||||
enum class Wrap {
|
||||
Layer,
|
||||
|
@ -87,6 +88,25 @@ private:
|
|||
|
||||
};
|
||||
|
||||
struct SelectedItem {
|
||||
explicit SelectedItem(FullMsgId msgId) : msgId(msgId) {
|
||||
}
|
||||
|
||||
FullMsgId msgId;
|
||||
bool canDelete = false;
|
||||
bool canForward = false;
|
||||
};
|
||||
|
||||
struct SelectedItems {
|
||||
explicit SelectedItems(Storage::SharedMediaType type)
|
||||
: type(type) {
|
||||
}
|
||||
|
||||
Storage::SharedMediaType type;
|
||||
std::vector<SelectedItem> list;
|
||||
|
||||
};
|
||||
|
||||
class WrapWidget final : public Window::SectionWidget {
|
||||
public:
|
||||
WrapWidget(
|
||||
|
@ -171,11 +191,18 @@ private:
|
|||
object_ptr<ContentWidget> createContent(
|
||||
not_null<ContentMemento*> memento);
|
||||
|
||||
rpl::producer<SelectedItems> selectedListValue() const;
|
||||
void refreshTopBarOverride(SelectedItems &&items);
|
||||
void createTopBarOverride(SelectedItems &&items);
|
||||
void destroyTopBarOverride();
|
||||
|
||||
rpl::variable<Wrap> _wrap;
|
||||
object_ptr<ContentWidget> _content = { nullptr };
|
||||
object_ptr<Ui::PlainShadow> _topTabsBackground = { nullptr };
|
||||
object_ptr<Ui::SettingsSlider> _topTabs = { nullptr };
|
||||
object_ptr<TopBar> _topBar = { nullptr };
|
||||
object_ptr<TopBarOverride> _topBarOverride = { nullptr };
|
||||
Animation _topBarOverrideAnimation;
|
||||
object_ptr<Ui::FadeShadow> _topShadow;
|
||||
Tab _tab = Tab::Profile;
|
||||
std::unique_ptr<ContentMemento> _anotherTabMemento;
|
||||
|
@ -183,6 +210,7 @@ private:
|
|||
|
||||
rpl::event_stream<rpl::producer<int>> _desiredHeights;
|
||||
rpl::event_stream<rpl::producer<bool>> _desiredShadowVisibilities;
|
||||
rpl::event_stream<rpl::producer<SelectedItems>> _selectedLists;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ inline auto MediaTextPhrase(Type type) {
|
|||
case Type::VoiceFile: return lng_profile_audios;
|
||||
case Type::RoundFile: return lng_profile_rounds;
|
||||
}
|
||||
Unexpected("Type in setupSharedMedia()");
|
||||
Unexpected("Type in MediaTextPhrase()");
|
||||
};
|
||||
|
||||
inline auto MediaText(Type type) {
|
||||
|
|
|
@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
*/
|
||||
#include "info/media/info_media_inner_widget.h"
|
||||
|
||||
#include <rpl/flatten_latest.h>
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "info/media/info_media_list_widget.h"
|
||||
#include "info/media/info_media_buttons.h"
|
||||
|
@ -246,6 +247,7 @@ object_ptr<ListWidget> InnerWidget::setupList(
|
|||
| rpl::start_to_stream(
|
||||
_scrollToRequests,
|
||||
result->lifetime());
|
||||
_selectedLists.fire(result->selectedListValue());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -255,6 +257,15 @@ void InnerWidget::saveState(not_null<Memento*> memento) {
|
|||
void InnerWidget::restoreState(not_null<Memento*> memento) {
|
||||
}
|
||||
|
||||
rpl::producer<SelectedItems> InnerWidget::selectedListValue() const {
|
||||
return _selectedLists.events_starting_with(_list->selectedListValue())
|
||||
| rpl::flatten_latest();
|
||||
}
|
||||
|
||||
void InnerWidget::cancelSelection() {
|
||||
_list->cancelSelection();
|
||||
}
|
||||
|
||||
int InnerWidget::resizeGetHeight(int newWidth) {
|
||||
_inResize = true;
|
||||
auto guard = gsl::finally([this] { _inResize = false; });
|
||||
|
|
|
@ -55,6 +55,8 @@ public:
|
|||
rpl::producer<int> scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
rpl::producer<SelectedItems> selectedListValue() const;
|
||||
void cancelSelection();
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
@ -86,6 +88,7 @@ private:
|
|||
object_ptr<ListWidget> _list = { nullptr };
|
||||
|
||||
rpl::event_stream<int> _scrollToRequests;
|
||||
rpl::event_stream<rpl::producer<SelectedItems>> _selectedLists;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -159,21 +159,14 @@ bool ListWidget::IsAfter(
|
|||
|
||||
bool ListWidget::SkipSelectFromItem(const CursorState &state) {
|
||||
if (state.cursor.y() >= state.size.height()
|
||||
&& state.cursor.x() >= 0) {
|
||||
return true;
|
||||
} else if (state.cursor.x() >= state.size.width()
|
||||
&& state.cursor.y() >= 0) {
|
||||
|| state.cursor.x() >= state.size.width()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ListWidget::SkipSelectTillItem(const CursorState &state) {
|
||||
if (state.cursor.y() < state.size.height()
|
||||
&& state.cursor.x() < 0) {
|
||||
return true;
|
||||
} else if (state.cursor.x() < state.size.width()
|
||||
&& state.cursor.y() < 0) {
|
||||
if (state.cursor.x() < 0 || state.cursor.y() < 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -600,7 +593,6 @@ void ListWidget::itemRemoved(not_null<const HistoryItem*> item) {
|
|||
auto i = _selected.find(universalId);
|
||||
if (i != _selected.cend()) {
|
||||
removeItemSelection(i);
|
||||
pushSelectedItems();
|
||||
}
|
||||
|
||||
mouseActionUpdate(_mousePosition);
|
||||
|
@ -630,18 +622,20 @@ auto ListWidget::collectSelectedItems() const -> SelectedItems {
|
|||
auto transformation = [&](const auto &item) {
|
||||
return convert(item.first, item.second);
|
||||
};
|
||||
auto items = SelectedItems();
|
||||
items.reserve(_selected.size());
|
||||
std::transform(
|
||||
_selected.begin(),
|
||||
_selected.end(),
|
||||
std::back_inserter(items),
|
||||
transformation);
|
||||
auto items = SelectedItems(_type);
|
||||
if (hasSelectedItems()) {
|
||||
items.list.reserve(_selected.size());
|
||||
std::transform(
|
||||
_selected.begin(),
|
||||
_selected.end(),
|
||||
std::back_inserter(items.list),
|
||||
transformation);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
void ListWidget::pushSelectedItems() {
|
||||
_selectedItemsStream.fire(collectSelectedItems());
|
||||
_selectedListStream.fire(collectSelectedItems());
|
||||
}
|
||||
|
||||
bool ListWidget::hasSelected() const {
|
||||
|
@ -661,6 +655,7 @@ void ListWidget::removeItemSelection(
|
|||
if (_selected.empty()) {
|
||||
update();
|
||||
}
|
||||
pushSelectedItems();
|
||||
}
|
||||
|
||||
bool ListWidget::hasSelectedText() const {
|
||||
|
@ -854,8 +849,8 @@ void ListWidget::refreshRows() {
|
|||
clearStaleLayouts();
|
||||
|
||||
resizeToWidth(width());
|
||||
|
||||
restoreScrollState();
|
||||
mouseActionUpdate();
|
||||
}
|
||||
|
||||
void ListWidget::markLayoutsStale() {
|
||||
|
@ -1025,7 +1020,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
|||
fromSectionIt,
|
||||
clip.y() + clip.height());
|
||||
auto context = Context {
|
||||
Layout::PaintContext(ms, !_selected.empty()),
|
||||
Layout::PaintContext(ms, hasSelectedItems()),
|
||||
&_selected,
|
||||
&_dragSelected,
|
||||
_dragSelectAction
|
||||
|
@ -1113,6 +1108,7 @@ void ListWidget::applyItemSelection(
|
|||
universalId,
|
||||
selection)) {
|
||||
repaintItem(universalId);
|
||||
pushSelectedItems();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1203,10 +1199,12 @@ void ListWidget::clearSelected() {
|
|||
}
|
||||
if (hasSelectedText()) {
|
||||
repaintItem(_selected.begin()->first);
|
||||
_selected.clear();
|
||||
} else {
|
||||
_selected.clear();
|
||||
pushSelectedItems();
|
||||
update();
|
||||
}
|
||||
_selected.clear();
|
||||
}
|
||||
|
||||
void ListWidget::validateTrippleClickStartTime() {
|
||||
|
@ -1246,7 +1244,7 @@ QPoint ListWidget::clampMousePosition(QPoint position) const {
|
|||
}
|
||||
|
||||
void ListWidget::mouseActionUpdate(const QPoint &screenPos) {
|
||||
if (_sections.empty()) {
|
||||
if (_sections.empty() || _visibleBottom <= _visibleTop) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1657,6 +1655,7 @@ void ListWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton butt
|
|||
void ListWidget::applyDragSelection() {
|
||||
applyDragSelection(_selected);
|
||||
clearDragSelection();
|
||||
pushSelectedItems();
|
||||
}
|
||||
|
||||
void ListWidget::applyDragSelection(SelectedMap &applyTo) const {
|
||||
|
@ -1693,6 +1692,9 @@ void ListWidget::mouseActionUpdate() {
|
|||
void ListWidget::clearStaleLayouts() {
|
||||
for (auto i = _layouts.begin(); i != _layouts.end();) {
|
||||
if (i->second.stale) {
|
||||
if (i->second.item.get() == _overLayout) {
|
||||
_overLayout = nullptr;
|
||||
}
|
||||
i = _layouts.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
|
|
|
@ -66,17 +66,12 @@ public:
|
|||
rpl::producer<int> scrollToRequests() const {
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
struct SelectedItem {
|
||||
explicit SelectedItem(FullMsgId msgId) : msgId(msgId) {
|
||||
}
|
||||
|
||||
FullMsgId msgId;
|
||||
bool canDelete = false;
|
||||
bool canForward = false;
|
||||
};
|
||||
using SelectedItems = std::vector<SelectedItem>;
|
||||
rpl::producer<SelectedItems> selectedItemsValue() const {
|
||||
return _selectedItemsStream.events();
|
||||
rpl::producer<SelectedItems> selectedListValue() const {
|
||||
return _selectedListStream.events_starting_with(
|
||||
collectSelectedItems());
|
||||
}
|
||||
void cancelSelection() {
|
||||
clearSelected();
|
||||
}
|
||||
|
||||
~ListWidget();
|
||||
|
@ -246,10 +241,6 @@ private:
|
|||
|
||||
void updateDragSelection();
|
||||
void clearDragSelection();
|
||||
void setDragSelection(
|
||||
BaseLayout *dragSelectFrom,
|
||||
BaseLayout *dragSelectTill,
|
||||
DragSelectAction action);
|
||||
|
||||
void trySwitchToWordSelection();
|
||||
void switchToWordSelection();
|
||||
|
@ -285,7 +276,7 @@ private:
|
|||
bool _pressWasInactive = false;
|
||||
SelectedMap _selected;
|
||||
SelectedMap _dragSelected;
|
||||
rpl::event_stream<SelectedItems> _selectedItemsStream;
|
||||
rpl::event_stream<SelectedItems> _selectedListStream;
|
||||
style::cursor _cursor = style::cur_default;
|
||||
DragSelectAction _dragSelectAction = DragSelectAction::None;
|
||||
bool _wasSelectedText = false; // was some text selected in current drag action
|
||||
|
|
|
@ -60,6 +60,14 @@ Widget::Widget(
|
|||
}, _inner->lifetime());
|
||||
}
|
||||
|
||||
rpl::producer<SelectedItems> Widget::selectedListValue() const {
|
||||
return _inner->selectedListValue();
|
||||
}
|
||||
|
||||
void Widget::cancelSelection() {
|
||||
_inner->cancelSelection();
|
||||
}
|
||||
|
||||
Section Widget::section() const {
|
||||
return Section(type());
|
||||
}
|
||||
|
|
|
@ -79,6 +79,9 @@ public:
|
|||
const QRect &geometry,
|
||||
not_null<Memento*> memento);
|
||||
|
||||
rpl::producer<SelectedItems> selectedListValue() const override;
|
||||
void cancelSelection() override;
|
||||
|
||||
private:
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
|
|
@ -31,6 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
#include "info/profile/info_profile_icon.h"
|
||||
#include "info/profile/info_profile_members.h"
|
||||
#include "info/media/info_media_buttons.h"
|
||||
#include "info/info_top_bar_override.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
|
@ -351,7 +352,7 @@ object_ptr<Ui::RpWidget> InnerWidget::setupUserActions(
|
|||
addButton(
|
||||
Lang::Viewer(lng_profile_share_contact),
|
||||
CanShareContactValue(user),
|
||||
[user] { App::main()->shareContactLayer(user); });
|
||||
[this, user] { shareContact(user); });
|
||||
addButton(
|
||||
Lang::Viewer(lng_info_edit_contact),
|
||||
IsContactValue(user),
|
||||
|
@ -422,6 +423,36 @@ object_ptr<Ui::RpWidget> InnerWidget::setupUserActions(
|
|||
return std::move(result);
|
||||
}
|
||||
|
||||
void InnerWidget::shareContact(not_null<UserData*> user) const {
|
||||
auto callback = [user](not_null<PeerData*> peer) {
|
||||
if (!peer->canWrite()) {
|
||||
Ui::show(Box<InformBox>(
|
||||
lang(lng_forward_share_cant)),
|
||||
LayerOption::KeepOther);
|
||||
return;
|
||||
}
|
||||
auto recipient = peer->isUser()
|
||||
? peer->name
|
||||
: '\xAB' + peer->name + '\xBB';
|
||||
Ui::show(Box<ConfirmBox>(
|
||||
lng_forward_share_contact(lt_recipient, recipient),
|
||||
lang(lng_forward_send),
|
||||
[peer, user] {
|
||||
App::main()->onShareContact(
|
||||
peer->id,
|
||||
user);
|
||||
Ui::hideLayer();
|
||||
}), LayerOption::KeepOther);
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(
|
||||
std::make_unique<ChooseRecipientBoxController>(std::move(callback)),
|
||||
[](not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_cancel), [box] {
|
||||
box->closeBox();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
object_ptr<Ui::RpWidget> InnerWidget::createSkipWidget(
|
||||
RpWidget *parent) const {
|
||||
return Ui::CreateSkipWidget(parent, st::infoProfileSkip);
|
||||
|
|
|
@ -88,6 +88,7 @@ private:
|
|||
object_ptr<RpWidget> setupUserActions(
|
||||
RpWidget *parent,
|
||||
not_null<UserData*> user) const;
|
||||
void shareContact(not_null<UserData*> user) const;
|
||||
|
||||
object_ptr<RpWidget> createSkipWidget(RpWidget *parent) const;
|
||||
object_ptr<Ui::SlideWrap<RpWidget>> createSlideSkipWidget(
|
||||
|
|
|
@ -677,7 +677,7 @@ bool MainWidget::setForwardDraft(PeerId peerId, const SelectedItemSet &items) {
|
|||
auto peer = App::peer(peerId);
|
||||
auto error = GetErrorTextForForward(peer, items);
|
||||
if (!error.isEmpty()) {
|
||||
Ui::show(Box<InformBox>(error));
|
||||
Ui::show(Box<InformBox>(error), LayerOption::KeepOther);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1117,4 +1117,8 @@ InfoTopBar {
|
|||
title: FlatLabel;
|
||||
titlePosition: point;
|
||||
bg: color;
|
||||
mediaCancel: IconButton;
|
||||
mediaActionsSkip: pixels;
|
||||
mediaForward: IconButton;
|
||||
mediaDelete: IconButton;
|
||||
}
|
||||
|
|
|
@ -219,6 +219,8 @@
|
|||
<(src_loc)/info/info_section_widget.h
|
||||
<(src_loc)/info/info_top_bar.cpp
|
||||
<(src_loc)/info/info_top_bar.h
|
||||
<(src_loc)/info/info_top_bar_override.cpp
|
||||
<(src_loc)/info/info_top_bar_override.h
|
||||
<(src_loc)/info/info_wrap_widget.cpp
|
||||
<(src_loc)/info/info_wrap_widget.h
|
||||
<(src_loc)/info/media/info_media_buttons.h
|
||||
|
|
Loading…
Reference in New Issue