Improve compile time.

This commit is contained in:
John Preston 2017-11-21 13:20:56 +04:00
parent 6ca105a290
commit d93c1ccbaa
69 changed files with 692 additions and 5022 deletions

View file

@ -190,6 +190,88 @@ void AuthSessionData::constructFromSerialized(const QByteArray &serialized) {
}
}
void AuthSessionData::markItemLayoutChanged(not_null<const HistoryItem*> item) {
_itemLayoutChanged.fire_copy(item);
}
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemLayoutChanged() const {
return _itemLayoutChanged.events();
}
void AuthSessionData::requestItemRepaint(not_null<const HistoryItem*> item) {
_itemRepaintRequest.fire_copy(item);
}
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemRepaintRequest() const {
return _itemRepaintRequest.events();
}
void AuthSessionData::markItemRemoved(not_null<const HistoryItem*> item) {
_itemRemoved.fire_copy(item);
}
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemRemoved() const {
return _itemRemoved.events();
}
void AuthSessionData::markHistoryUnloaded(not_null<const History*> history) {
_historyUnloaded.fire_copy(history);
}
rpl::producer<not_null<const History*>> AuthSessionData::historyUnloaded() const {
return _historyUnloaded.events();
}
void AuthSessionData::markHistoryCleared(not_null<const History*> history) {
_historyCleared.fire_copy(history);
}
rpl::producer<not_null<const History*>> AuthSessionData::historyCleared() const {
return _historyCleared.events();
}
void AuthSessionData::removeMegagroupParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user) {
_megagroupParticipantRemoved.fire({ channel, user });
}
auto AuthSessionData::megagroupParticipantRemoved() const -> rpl::producer<MegagroupParticipant> {
return _megagroupParticipantRemoved.events();
}
rpl::producer<not_null<UserData*>> AuthSessionData::megagroupParticipantRemoved(
not_null<ChannelData*> channel) const {
return megagroupParticipantRemoved()
| rpl::filter([channel](auto updateChannel, auto user) {
return (updateChannel == channel);
})
| rpl::map([](auto updateChannel, auto user) {
return user;
});
}
void AuthSessionData::addNewMegagroupParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user) {
_megagroupParticipantAdded.fire({ channel, user });
}
auto AuthSessionData::megagroupParticipantAdded() const -> rpl::producer<MegagroupParticipant> {
return _megagroupParticipantAdded.events();
}
rpl::producer<not_null<UserData*>> AuthSessionData::megagroupParticipantAdded(
not_null<ChannelData*> channel) const {
return megagroupParticipantAdded()
| rpl::filter([channel](auto updateChannel, auto user) {
return (updateChannel == channel);
})
| rpl::map([](auto updateChannel, auto user) {
return user;
});
}
void AuthSessionData::setTabbedSelectorSectionEnabled(bool enabled) {
_variables.tabbedSelectorSectionEnabled = enabled;
if (enabled) {
@ -198,6 +280,11 @@ void AuthSessionData::setTabbedSelectorSectionEnabled(bool enabled) {
setTabbedReplacedWithInfo(false);
}
rpl::producer<bool> AuthSessionData::tabbedReplacedWithInfoValue() const {
return _tabbedReplacedWithInfoValue.events_starting_with(
tabbedReplacedWithInfo());
}
void AuthSessionData::setThirdSectionInfoEnabled(bool enabled) {
if (_variables.thirdSectionInfoEnabled != enabled) {
_variables.thirdSectionInfoEnabled = enabled;
@ -209,6 +296,11 @@ void AuthSessionData::setThirdSectionInfoEnabled(bool enabled) {
}
}
rpl::producer<bool> AuthSessionData::thirdSectionInfoEnabledValue() const {
return _thirdSectionInfoEnabledValue.events_starting_with(
thirdSectionInfoEnabled());
}
void AuthSessionData::setTabbedReplacedWithInfo(bool enabled) {
if (_tabbedReplacedWithInfo != enabled) {
_tabbedReplacedWithInfo = enabled;
@ -224,6 +316,46 @@ QString AuthSessionData::getSoundPath(const QString &key) const {
return qsl(":/sounds/") + key + qsl(".mp3");
}
void AuthSessionData::setDialogsWidthRatio(float64 ratio) {
_variables.dialogsWidthRatio = ratio;
}
float64 AuthSessionData::dialogsWidthRatio() const {
return _variables.dialogsWidthRatio.current();
}
rpl::producer<float64> AuthSessionData::dialogsWidthRatioChanges() const {
return _variables.dialogsWidthRatio.changes();
}
void AuthSessionData::setThirdColumnWidth(int width) {
_variables.thirdColumnWidth = width;
}
int AuthSessionData::thirdColumnWidth() const {
return _variables.thirdColumnWidth.current();
}
rpl::producer<int> AuthSessionData::thirdColumnWidthChanges() const {
return _variables.thirdColumnWidth.changes();
}
void AuthSessionData::markStickersUpdated() {
_stickersUpdated.fire({});
}
rpl::producer<> AuthSessionData::stickersUpdated() const {
return _stickersUpdated.events();
}
void AuthSessionData::markSavedGifsUpdated() {
_savedGifsUpdated.fire({});
}
rpl::producer<> AuthSessionData::savedGifsUpdated() const {
return _savedGifsUpdated.events();
}
AuthSession &Auth() {
auto result = Messenger::Instance().authSession();
Assert(result != nullptr);

View file

@ -70,75 +70,31 @@ public:
base::Observable<ItemVisibilityQuery> &queryItemVisibility() {
return _queryItemVisibility;
}
void markItemLayoutChanged(not_null<const HistoryItem*> item) {
_itemLayoutChanged.fire_copy(item);
}
rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const {
return _itemLayoutChanged.events();
}
void requestItemRepaint(not_null<const HistoryItem*> item) {
_itemRepaintRequest.fire_copy(item);
}
rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const {
return _itemRepaintRequest.events();
}
void markItemRemoved(not_null<const HistoryItem*> item) {
_itemRemoved.fire_copy(item);
}
rpl::producer<not_null<const HistoryItem*>> itemRemoved() const {
return _itemRemoved.events();
}
void markHistoryUnloaded(not_null<const History*> history) {
_historyUnloaded.fire_copy(history);
}
rpl::producer<not_null<const History*>> historyUnloaded() const {
return _historyUnloaded.events();
}
void markHistoryCleared(not_null<const History*> history) {
_historyCleared.fire_copy(history);
}
rpl::producer<not_null<const History*>> historyCleared() const {
return _historyCleared.events();
}
void markItemLayoutChanged(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const;
void requestItemRepaint(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const;
void markItemRemoved(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemRemoved() const;
void markHistoryUnloaded(not_null<const History*> history);
rpl::producer<not_null<const History*>> historyUnloaded() const;
void markHistoryCleared(not_null<const History*> history);
rpl::producer<not_null<const History*>> historyCleared() const;
using MegagroupParticipant = std::tuple<
not_null<ChannelData*>,
not_null<UserData*>>;
void removeMegagroupParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user) {
_megagroupParticipantRemoved.fire({ channel, user });
}
auto megagroupParticipantRemoved() const {
return _megagroupParticipantRemoved.events();
}
auto megagroupParticipantRemoved(
not_null<ChannelData*> channel) const {
return megagroupParticipantRemoved()
| rpl::filter([channel](auto updateChannel, auto user) {
return (updateChannel == channel);
})
| rpl::map([](auto updateChannel, auto user) {
return user;
});
}
not_null<ChannelData*> channel,
not_null<UserData*> user);
rpl::producer<MegagroupParticipant> megagroupParticipantRemoved() const;
rpl::producer<not_null<UserData*>> megagroupParticipantRemoved(
not_null<ChannelData*> channel) const;
void addNewMegagroupParticipant(
not_null<ChannelData*> channel,
not_null<UserData*> user) {
_megagroupParticipantAdded.fire({ channel, user });
}
auto megagroupParticipantAdded() const {
return _megagroupParticipantAdded.events();
}
auto megagroupParticipantAdded(
not_null<ChannelData*> channel) const {
return megagroupParticipantAdded()
| rpl::filter([channel](auto updateChannel, auto user) {
return (updateChannel == channel);
})
| rpl::map([](auto updateChannel, auto user) {
return user;
});
}
not_null<ChannelData*> channel,
not_null<UserData*> user);
rpl::producer<MegagroupParticipant> megagroupParticipantAdded() const;
rpl::producer<not_null<UserData*>> megagroupParticipantAdded(
not_null<ChannelData*> channel) const;
void moveFrom(AuthSessionData &&other) {
_variables = std::move(other._variables);
@ -166,10 +122,7 @@ public:
return _variables.thirdSectionInfoEnabled;
}
void setThirdSectionInfoEnabled(bool enabled);
auto thirdSectionInfoEnabledValue() const {
return _thirdSectionInfoEnabledValue.events_starting_with(
thirdSectionInfoEnabled());
}
rpl::producer<bool> thirdSectionInfoEnabledValue() const;
int thirdSectionExtendedBy() const {
return _variables.thirdSectionExtendedBy;
}
@ -180,10 +133,7 @@ public:
return _tabbedReplacedWithInfo;
}
void setTabbedReplacedWithInfo(bool enabled);
auto tabbedReplacedWithInfoValue() const {
return _tabbedReplacedWithInfoValue.events_starting_with(
tabbedReplacedWithInfo());
}
rpl::producer<bool> tabbedReplacedWithInfoValue() const;
void setSmallDialogsList(bool enabled) {
_variables.smallDialogsList = enabled;
}
@ -221,37 +171,17 @@ public:
RectPart floatPlayerCorner() const {
return _variables.floatPlayerCorner;
}
void setDialogsWidthRatio(float64 ratio) {
_variables.dialogsWidthRatio = ratio;
}
float64 dialogsWidthRatio() const {
return _variables.dialogsWidthRatio.current();
}
rpl::producer<float64> dialogsWidthRatioChanges() const {
return _variables.dialogsWidthRatio.changes();
}
void setThirdColumnWidth(int width) {
_variables.thirdColumnWidth = width;
}
int thirdColumnWidth() const {
return _variables.thirdColumnWidth.current();
}
rpl::producer<int> thirdColumnWidthChanges() const {
return _variables.thirdColumnWidth.changes();
}
void setDialogsWidthRatio(float64 ratio);
float64 dialogsWidthRatio() const;
rpl::producer<float64> dialogsWidthRatioChanges() const;
void setThirdColumnWidth(int width);
int thirdColumnWidth() const;
rpl::producer<int> thirdColumnWidthChanges() const;
void markStickersUpdated() {
_stickersUpdated.fire({});
}
rpl::producer<> stickersUpdated() const {
return _stickersUpdated.events();
}
void markSavedGifsUpdated() {
_savedGifsUpdated.fire({});
}
rpl::producer<> savedGifsUpdated() const {
return _savedGifsUpdated.events();
}
void markStickersUpdated();
rpl::producer<> stickersUpdated() const;
void markSavedGifsUpdated();
rpl::producer<> savedGifsUpdated() const;
void setGroupStickersSectionHidden(PeerId peerId) {
_variables.groupStickersSectionHidden.insert(peerId);
}

View file

@ -77,4 +77,15 @@ void HandleObservables() {
}
}
rpl::producer<> ObservableViewer(base::Observable<void> &observable) {
return [&observable](const auto &consumer) {
auto lifetime = rpl::lifetime();
lifetime.make_state<base::Subscription>(
observable.add_subscription([consumer]() {
consumer.put_next({});
}));
return lifetime;
};
}
} // namespace base

View file

@ -477,16 +477,6 @@ inline auto ObservableViewer(base::Observable<Type> &observable) {
});
}
inline auto ObservableViewer(base::Observable<void> &observable) {
return rpl::make_producer<>([&observable](
const auto &consumer) {
auto lifetime = rpl::lifetime();
lifetime.make_state<base::Subscription>(
observable.add_subscription([consumer]() {
consumer.put_next({});
}));
return lifetime;
});
}
rpl::producer<> ObservableViewer(base::Observable<void> &observable);
} // namespace base

View file

@ -103,6 +103,10 @@ int PhotoCropBox::mouseState(QPoint p) {
return 0;
}
rpl::producer<QImage> PhotoCropBox::ready() const {
return _readyImages.events();
}
void PhotoCropBox::mouseReleaseEvent(QMouseEvent *e) {
if (_downState) {
_downState = 0;

View file

@ -29,9 +29,7 @@ public:
int32 mouseState(QPoint p);
rpl::producer<QImage> ready() const {
return _readyImages.events();
}
rpl::producer<QImage> ready() const;
protected:
void prepare() override;

View file

@ -0,0 +1,126 @@
/*
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 "data/data_peer_values.h"
namespace Data {
inline auto AdminRightsValue(not_null<ChannelData*> channel) {
return channel->adminRightsValue();
}
inline auto AdminRightsValue(
not_null<ChannelData*> channel,
MTPDchannelAdminRights::Flags mask) {
return FlagsValueWithMask(AdminRightsValue(channel), mask);
}
inline auto AdminRightValue(
not_null<ChannelData*> channel,
MTPDchannelAdminRights::Flag flag) {
return SingleFlagValue(AdminRightsValue(channel), flag);
}
inline auto RestrictionsValue(not_null<ChannelData*> channel) {
return channel->restrictionsValue();
}
inline auto RestrictionsValue(
not_null<ChannelData*> channel,
MTPDchannelBannedRights::Flags mask) {
return FlagsValueWithMask(RestrictionsValue(channel), mask);
}
inline auto RestrictionValue(
not_null<ChannelData*> channel,
MTPDchannelBannedRights::Flag flag) {
return SingleFlagValue(RestrictionsValue(channel), flag);
}
rpl::producer<bool> PeerFlagValue(
ChatData *chat,
MTPDchat_ClientFlag flag) {
return PeerFlagValue(chat, static_cast<MTPDchat::Flag>(flag));
}
rpl::producer<bool> PeerFlagValue(
ChannelData *channel,
MTPDchannel_ClientFlag flag) {
return PeerFlagValue(channel, static_cast<MTPDchannel::Flag>(flag));
}
rpl::producer<bool> CanWriteValue(UserData *user) {
using namespace rpl::mappers;
return PeerFlagValue(user, MTPDuser::Flag::f_deleted)
| rpl::map(!_1);
}
rpl::producer<bool> CanWriteValue(ChatData *chat) {
using namespace rpl::mappers;
auto mask = 0
| MTPDchat::Flag::f_deactivated
| MTPDchat_ClientFlag::f_forbidden
| MTPDchat::Flag::f_left
| MTPDchat::Flag::f_kicked;
return PeerFlagsValue(chat, mask)
| rpl::map(!_1);
}
rpl::producer<bool> CanWriteValue(ChannelData *channel) {
auto mask = 0
| MTPDchannel::Flag::f_left
| MTPDchannel_ClientFlag::f_forbidden
| MTPDchannel::Flag::f_creator
| MTPDchannel::Flag::f_broadcast;
return rpl::combine(
PeerFlagsValue(channel, mask),
AdminRightValue(
channel,
MTPDchannelAdminRights::Flag::f_post_messages),
RestrictionValue(
channel,
MTPDchannelBannedRights::Flag::f_send_messages),
[](
MTPDchannel::Flags flags,
bool postMessagesRight,
bool sendMessagesRestriction) {
auto notAmInFlags = 0
| MTPDchannel::Flag::f_left
| MTPDchannel_ClientFlag::f_forbidden;
return !(flags & notAmInFlags)
&& (postMessagesRight
|| (flags & MTPDchannel::Flag::f_creator)
|| (!(flags & MTPDchannel::Flag::f_broadcast)
&& !sendMessagesRestriction));
});
}
rpl::producer<bool> CanWriteValue(not_null<PeerData*> peer) {
if (auto user = peer->asUser()) {
return CanWriteValue(user);
} else if (auto chat = peer->asChat()) {
return CanWriteValue(chat);
} else if (auto channel = peer->asChannel()) {
return CanWriteValue(channel);
}
Unexpected("Bad peer value in CanWriteValue()");
}
} // namespace Data

View file

@ -82,17 +82,13 @@ inline auto PeerFlagValue(
// return PeerFlagValue(user, static_cast<MTPDuser::Flag>(flag));
//}
inline auto PeerFlagValue(
ChatData *chat,
MTPDchat_ClientFlag flag) {
return PeerFlagValue(chat, static_cast<MTPDchat::Flag>(flag));
}
rpl::producer<bool> PeerFlagValue(
ChatData *chat,
MTPDchat_ClientFlag flag);
inline auto PeerFlagValue(
ChannelData *channel,
MTPDchannel_ClientFlag flag) {
return PeerFlagValue(channel, static_cast<MTPDchannel::Flag>(flag));
}
rpl::producer<bool> PeerFlagValue(
ChannelData *channel,
MTPDchannel_ClientFlag flag);
template <
typename PeerType,
@ -120,93 +116,9 @@ inline auto PeerFullFlagValue(
return SingleFlagValue(PeerFullFlagsValue(peer), flag);
}
inline auto AdminRightsValue(not_null<ChannelData*> channel) {
return channel->adminRightsValue();
}
inline auto AdminRightsValue(
not_null<ChannelData*> channel,
MTPDchannelAdminRights::Flags mask) {
return FlagsValueWithMask(AdminRightsValue(channel), mask);
}
inline auto AdminRightValue(
not_null<ChannelData*> channel,
MTPDchannelAdminRights::Flag flag) {
return SingleFlagValue(AdminRightsValue(channel), flag);
}
inline auto RestrictionsValue(not_null<ChannelData*> channel) {
return channel->restrictionsValue();
}
inline auto RestrictionsValue(
not_null<ChannelData*> channel,
MTPDchannelBannedRights::Flags mask) {
return FlagsValueWithMask(RestrictionsValue(channel), mask);
}
inline auto RestrictionValue(
not_null<ChannelData*> channel,
MTPDchannelBannedRights::Flag flag) {
return SingleFlagValue(RestrictionsValue(channel), flag);
}
inline auto CanWriteValue(UserData *user) {
using namespace rpl::mappers;
return PeerFlagValue(user, MTPDuser::Flag::f_deleted)
| rpl::map(!_1);
}
inline auto CanWriteValue(ChatData *chat) {
using namespace rpl::mappers;
auto mask = 0
| MTPDchat::Flag::f_deactivated
| MTPDchat_ClientFlag::f_forbidden
| MTPDchat::Flag::f_left
| MTPDchat::Flag::f_kicked;
return PeerFlagsValue(chat, mask)
| rpl::map(!_1);
}
inline auto CanWriteValue(ChannelData *channel) {
auto flagsMask = 0
| MTPDchannel::Flag::f_left
| MTPDchannel_ClientFlag::f_forbidden
| MTPDchannel::Flag::f_creator
| MTPDchannel::Flag::f_broadcast;
return rpl::combine(
PeerFlagsValue(channel, flagsMask),
AdminRightValue(
channel,
MTPDchannelAdminRights::Flag::f_post_messages),
RestrictionValue(
channel,
MTPDchannelBannedRights::Flag::f_send_messages),
[](
MTPDchannel::Flags flags,
bool postMessagesRight,
bool sendMessagesRestriction) {
auto notAmInFlags = 0
| MTPDchannel::Flag::f_left
| MTPDchannel_ClientFlag::f_forbidden;
return !(flags & notAmInFlags)
&& (postMessagesRight
|| (flags & MTPDchannel::Flag::f_creator)
|| (!(flags & MTPDchannel::Flag::f_broadcast)
&& !sendMessagesRestriction));
});
}
inline rpl::producer<bool> CanWriteValue(not_null<PeerData*> peer) {
if (auto user = peer->asUser()) {
return CanWriteValue(user);
} else if (auto chat = peer->asChat()) {
return CanWriteValue(chat);
} else if (auto channel = peer->asChannel()) {
return CanWriteValue(channel);
}
Unexpected("Bad peer value in CanWriteValue()");
}
rpl::producer<bool> CanWriteValue(UserData *user);
rpl::producer<bool> CanWriteValue(ChatData *chat);
rpl::producer<bool> CanWriteValue(ChannelData *channel);
rpl::producer<bool> CanWriteValue(not_null<PeerData*> peer);
} // namespace Data

View file

@ -208,6 +208,10 @@ void InnerWidget::restoreState(not_null<Memento*> memento) {
_listController->restoreState(memento->listState());
}
rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {
return _scrollToRequests.events();
}
int InnerWidget::desiredHeight() const {
auto desired = 0;
auto count = qMax(_user->commonChatsCount(), 1);

View file

@ -45,9 +45,7 @@ public:
return _user;
}
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const {
return _scrollToRequests.events();
}
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
int desiredHeight() const;

View file

@ -167,6 +167,14 @@ auto Controller::produceSearchQuery(
return result;
}
rpl::producer<bool> Controller::searchEnabledByContent() const {
return _seachEnabledByContent.value();
}
rpl::producer<QString> Controller::mediaSourceQueryValue() const {
return _searchController->currentQueryValue();
}
rpl::producer<SparseIdsMergedSlice> Controller::mediaSource(
SparseIdsMergedSlice::UniversalMsgId aroundId,
int limitBefore,

View file

@ -109,16 +109,12 @@ public:
void setSearchEnabledByContent(bool enabled) {
_seachEnabledByContent = enabled;
}
rpl::producer<bool> searchEnabledByContent() const {
return _seachEnabledByContent.value();
}
rpl::producer<bool> searchEnabledByContent() const;
rpl::producer<SparseIdsMergedSlice> mediaSource(
SparseIdsMergedSlice::UniversalMsgId aroundId,
int limitBefore,
int limitAfter) const;
rpl::producer<QString> mediaSourceQueryValue() const {
return _searchController->currentQueryValue();
}
rpl::producer<QString> mediaSourceQueryValue() const;
void saveSearchState(not_null<ContentMemento*> memento);

View file

@ -623,6 +623,10 @@ object_ptr<ContentWidget> WrapWidget::createContent(
// _historyStack.erase(_historyStack.begin());
//}
rpl::producer<Wrap> WrapWidget::wrapValue() const {
return _wrap.value();
}
void WrapWidget::setWrap(Wrap wrap) {
// Was done for top level tabs support.
//

View file

@ -94,9 +94,7 @@ public:
Wrap wrap() const {
return _wrap.current();
}
rpl::producer<Wrap> wrapValue() const {
return _wrap.value();
}
rpl::producer<Wrap> wrapValue() const;
void setWrap(Wrap wrap);
not_null<Controller*> controller() {

View file

@ -360,5 +360,9 @@ void InnerWidget::setScrollHeightValue(rpl::producer<int> value) {
_1 - _2));
}
rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {
return _scrollToRequests.events();
}
} // namespace Media
} // namespace Info

View file

@ -55,9 +55,7 @@ public:
void setScrollHeightValue(rpl::producer<int> value);
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const {
return _scrollToRequests.events();
}
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
rpl::producer<SelectedItems> selectedListValue() const;
void cancelSelection();

View file

@ -585,6 +585,15 @@ void ListWidget::start() {
}, lifetime());
}
rpl::producer<int> ListWidget::scrollToRequests() const {
return _scrollToRequests.events();
}
rpl::producer<SelectedItems> ListWidget::selectedListValue() const {
return _selectedListStream.events_starting_with(
collectSelectedItems());
}
void ListWidget::restart() {
mouseActionCancel();

View file

@ -55,13 +55,8 @@ public:
void restart();
rpl::producer<int> scrollToRequests() const {
return _scrollToRequests.events();
}
rpl::producer<SelectedItems> selectedListValue() const {
return _selectedListStream.events_starting_with(
collectSelectedItems());
}
rpl::producer<int> scrollToRequests() const;
rpl::producer<SelectedItems> selectedListValue() const;
void cancelSelection() {
clearSelected();
}

View file

@ -431,6 +431,11 @@ SharedMediaCover::SharedMediaCover(QWidget *parent)
createLabel();
}
SharedMediaCover *SharedMediaCover::setToggleShown(rpl::producer<bool> &&shown) {
return static_cast<SharedMediaCover*>(
SectionWithToggle::setToggleShown(std::move(shown)));
}
void SharedMediaCover::createLabel() {
using namespace rpl::mappers;
auto label = object_ptr<Ui::FlatLabel>(

View file

@ -102,10 +102,7 @@ class SharedMediaCover : public SectionWithToggle {
public:
SharedMediaCover(QWidget *parent);
SharedMediaCover *setToggleShown(rpl::producer<bool> &&shown) {
return static_cast<SharedMediaCover*>(
SectionWithToggle::setToggleShown(std::move(shown)));
}
SharedMediaCover *setToggleShown(rpl::producer<bool> &&shown);
QMargins getMargins() const override;

View file

@ -274,6 +274,14 @@ void InnerWidget::restoreState(not_null<Memento*> memento) {
}
}
rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {
return _scrollToRequests.events();
}
rpl::producer<int> InnerWidget::desiredHeightValue() const {
return _desiredHeight.events_starting_with(countDesiredHeight());
}
int InnerWidget::resizeGetHeight(int newWidth) {
_inResize = true;
auto guard = gsl::finally([&] { _inResize = false; });

View file

@ -58,13 +58,8 @@ public:
void setIsStackBottom(bool isStackBottom) {
_isStackBottom = isStackBottom;
}
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const {
return _scrollToRequests.events();
}
rpl::producer<int> desiredHeightValue() const override {
return _desiredHeight.events_starting_with(countDesiredHeight());
}
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
rpl::producer<int> desiredHeightValue() const override;
protected:
int resizeGetHeight(int newWidth) override;

View file

@ -90,6 +90,10 @@ rpl::producer<int> Members::onlineCountValue() const {
return _listController->onlineCountValue();
}
rpl::producer<Ui::ScrollToRequest> Members::scrollToRequests() const {
return _scrollToRequests.events();
}
std::unique_ptr<MembersState> Members::saveState() {
auto result = std::make_unique<MembersState>();
result->list = _listController->saveState();

View file

@ -60,9 +60,7 @@ public:
not_null<Controller*> controller,
not_null<PeerData*> peer);
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const {
return _scrollToRequests.events();
}
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
std::unique_ptr<MembersState> saveState();
void restoreState(std::unique_ptr<MembersState> state);

View file

@ -108,4 +108,35 @@ base::Observable<PeerUpdate, PeerUpdatedHandler> &PeerUpdated() {
return PeerUpdatedObservable;
}
rpl::producer<PeerUpdate> PeerUpdateViewer(
PeerUpdate::Flags flags) {
return [=](const auto &consumer) {
auto lifetime = rpl::lifetime();
lifetime.make_state<base::Subscription>(
PeerUpdated().add_subscription({ flags, [=](
const PeerUpdate &update) {
consumer.put_next_copy(update);
}}));
return lifetime;
};
}
rpl::producer<PeerUpdate> PeerUpdateViewer(
not_null<PeerData*> peer,
PeerUpdate::Flags flags) {
return PeerUpdateViewer(flags)
| rpl::filter([=](const PeerUpdate &update) {
return (update.peer == peer);
});
}
rpl::producer<PeerUpdate> PeerUpdateValue(
not_null<PeerData*> peer,
PeerUpdate::Flags flags) {
auto initial = PeerUpdate(peer);
initial.flags = flags;
return rpl::single(initial)
| rpl::then(PeerUpdateViewer(peer, flags));
}
} // namespace Notify

View file

@ -125,36 +125,15 @@ private:
};
base::Observable<PeerUpdate, PeerUpdatedHandler> &PeerUpdated();
inline auto PeerUpdateViewer(
PeerUpdate::Flags flags) {
return rpl::make_producer<PeerUpdate>([=](
const auto &consumer) {
auto lifetime = rpl::lifetime();
lifetime.make_state<base::Subscription>(
PeerUpdated().add_subscription({ flags, [=](
const PeerUpdate &update) {
consumer.put_next_copy(update);
}}));
return lifetime;
});
}
rpl::producer<PeerUpdate> PeerUpdateViewer(
PeerUpdate::Flags flags);
inline auto PeerUpdateViewer(
not_null<PeerData*> peer,
PeerUpdate::Flags flags) {
return PeerUpdateViewer(flags)
| rpl::filter([=](const PeerUpdate &update) {
return (update.peer == peer);
});
}
rpl::producer<PeerUpdate> PeerUpdateViewer(
not_null<PeerData*> peer,
PeerUpdate::Flags flags);
inline auto PeerUpdateValue(
not_null<PeerData*> peer,
PeerUpdate::Flags flags) {
auto initial = PeerUpdate(peer);
initial.flags = flags;
return rpl::single(initial)
| rpl::then(PeerUpdateViewer(peer, flags));
}
rpl::producer<PeerUpdate> PeerUpdateValue(
not_null<PeerData*> peer,
PeerUpdate::Flags flags);
} // namespace Notify

View file

@ -1,377 +0,0 @@
/*
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 "profile/profile_block_actions.h"
#include "styles/style_profile.h"
#include "styles/style_boxes.h"
#include "ui/widgets/buttons.h"
#include "boxes/confirm_box.h"
#include "boxes/report_box.h"
#include "mainwidget.h"
#include "observer_peer.h"
#include "apiwrap.h"
#include "auth_session.h"
#include "lang/lang_keys.h"
#include "profile/profile_channel_controllers.h"
#include "mainwindow.h"
namespace Profile {
constexpr auto kEnableSearchMembersAfterCount = 50;
constexpr auto kMaxChannelMembersDeleteAllowed = 1000;
using UpdateFlag = Notify::PeerUpdate::Flag;
ActionsWidget::ActionsWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_actions_section)) {
auto observeEvents = UpdateFlag::ChannelAmIn
| UpdateFlag::UserIsBlocked
| UpdateFlag::BotCommandsChanged
| UpdateFlag::MembersChanged;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
notifyPeerUpdated(update);
}));
validateBlockStatus();
refreshButtons();
}
void ActionsWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.peer != peer()) {
return;
}
auto needFullRefresh = [&update, this]() {
if (update.flags & UpdateFlag::BotCommandsChanged) {
if (_hasBotHelp != hasBotCommand(qsl("help")) || _hasBotSettings != hasBotCommand(qsl("settings"))) {
return true;
}
}
if (update.flags & UpdateFlag::MembersChanged) {
if (peer()->isMegagroup()) {
// Search members button could change.
return true;
}
}
return false;
};
if (needFullRefresh()) {
refreshButtons();
} else {
if (update.flags & UpdateFlag::MembersChanged) {
refreshDeleteChannel();
}
if (update.flags & UpdateFlag::ChannelAmIn) {
refreshLeaveChannel();
}
if (update.flags & UpdateFlag::UserIsBlocked) {
refreshBlockUser();
}
refreshVisibility();
}
contentSizeUpdated();
}
void ActionsWidget::validateBlockStatus() const {
auto needFullPeer = [this]() {
if (auto user = peer()->asUser()) {
if (user->blockStatus() == UserData::BlockStatus::Unknown) {
return true;
} else if (user->botInfo && !user->botInfo->inited) {
return true;
}
}
return false;
};
if (needFullPeer()) {
Auth().api().requestFullPeer(peer());
}
}
Ui::LeftOutlineButton *ActionsWidget::addButton(const QString &text, const char *slot, const style::OutlineButton &st, int skipHeight) {
auto result = new Ui::LeftOutlineButton(this, text, st);
connect(result, SIGNAL(clicked()), this, slot);
result->show();
int top = buttonsBottom() + skipHeight;
resizeButton(result, width(), top);
_buttons.push_back(result);
return result;
};
void ActionsWidget::resizeButton(Ui::LeftOutlineButton *button, int newWidth, int top) {
int left = defaultOutlineButtonLeft();
int availableWidth = newWidth - left - st::profileBlockMarginRight;
accumulate_min(availableWidth, st::profileBlockOneLineWidthMax);
button->resizeToWidth(availableWidth);
button->moveToLeft(left, top);
}
void ActionsWidget::refreshButtons() {
auto buttons = base::take(_buttons);
for_const (auto &button, buttons) {
delete button;
}
_blockUser = _leaveChannel = nullptr;
if (auto user = peer()->asUser()) {
if ((_hasBotHelp = hasBotCommand(qsl("help")))) {
addButton(lang(lng_profile_bot_help), SLOT(onBotHelp()));
}
if ((_hasBotSettings = hasBotCommand(qsl("settings")))) {
addButton(lang(lng_profile_bot_settings), SLOT(onBotSettings()));
}
addButton(lang(lng_profile_clear_history), SLOT(onClearHistory()));
addButton(lang(lng_profile_delete_conversation), SLOT(onDeleteConversation()));
if (user->botInfo) {
addButton(lang(lng_profile_report), SLOT(onReport()), st::defaultLeftOutlineButton, st::profileBlockOneLineSkip);
}
refreshBlockUser();
} else if (auto chat = peer()->asChat()) {
if (chat->amCreator()) {
addButton(lang(lng_profile_migrate_button), SLOT(onUpgradeToSupergroup()));
}
addButton(lang(lng_profile_clear_history), SLOT(onClearHistory()));
addButton(lang(lng_profile_clear_and_exit), SLOT(onDeleteConversation()));
} else if (auto channel = peer()->asChannel()) {
if (channel->isMegagroup() && channel->membersCount() > kEnableSearchMembersAfterCount) {
addButton(lang(lng_profile_search_members), SLOT(onSearchMembers()));
}
if (!channel->amCreator() && (!channel->isMegagroup() || channel->isPublic())) {
addButton(lang(lng_profile_report), SLOT(onReport()));
}
refreshDeleteChannel();
refreshLeaveChannel();
}
refreshVisibility();
}
void ActionsWidget::refreshVisibility() {
setVisible(!_buttons.isEmpty());
}
QString ActionsWidget::getBlockButtonText() const {
auto user = peer()->asUser();
if (!user || (user->id == Auth().userPeerId())) return QString();
if (user->blockStatus() == UserData::BlockStatus::Unknown) return QString();
if (user->isBlocked()) {
if (user->botInfo) {
return lang(lng_profile_unblock_bot);
}
return lang(lng_profile_unblock_user);
} else if (user->botInfo) {
return lang(lng_profile_block_bot);
}
return lang(lng_profile_block_user);
}
bool ActionsWidget::hasBotCommand(const QString &command) const {
auto user = peer()->asUser();
if (!user || !user->botInfo || user->botInfo->commands.isEmpty()) {
return false;
}
for_const (auto &cmd, user->botInfo->commands) {
if (!cmd.command.compare(command, Qt::CaseInsensitive)) {
return true;
}
}
return false;
}
void ActionsWidget::sendBotCommand(const QString &command) {
auto user = peer()->asUser();
if (user && user->botInfo && !user->botInfo->commands.isEmpty()) {
for_const (auto &cmd, user->botInfo->commands) {
if (!cmd.command.compare(command, Qt::CaseInsensitive)) {
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
App::sendBotCommand(user, user, '/' + cmd.command);
return;
}
}
}
// Command was not found.
refreshButtons();
contentSizeUpdated();
}
void ActionsWidget::refreshBlockUser() {
if (auto user = peer()->asUser()) {
auto blockText = getBlockButtonText();
if (_blockUser) {
if (blockText.isEmpty()) {
_buttons.removeOne(_blockUser);
delete _blockUser;
_blockUser = nullptr;
} else {
_blockUser->setText(blockText);
}
} else if (!blockText.isEmpty()) {
_blockUser = addButton(blockText, SLOT(onBlockUser()), st::attentionLeftOutlineButton, st::profileBlockOneLineSkip);
}
}
}
void ActionsWidget::refreshDeleteChannel() {
if (auto channel = peer()->asChannel()) {
if (channel->canDelete() && !_deleteChannel) {
_deleteChannel = addButton(lang(channel->isMegagroup() ? lng_profile_delete_group : lng_profile_delete_channel), SLOT(onDeleteChannel()), st::attentionLeftOutlineButton);
} else if (!channel->canDelete() && _deleteChannel) {
_buttons.removeOne(_deleteChannel);
delete _deleteChannel;
_deleteChannel = nullptr;
}
}
}
void ActionsWidget::refreshLeaveChannel() {
if (auto channel = peer()->asChannel()) {
if (!channel->amCreator()) {
if (channel->amIn() && !_leaveChannel) {
_leaveChannel = addButton(lang(channel->isMegagroup() ? lng_profile_leave_group : lng_profile_leave_channel), SLOT(onLeaveChannel()));
} else if (!channel->amIn() && _leaveChannel) {
_buttons.removeOne(_leaveChannel);
delete _leaveChannel;
_leaveChannel = nullptr;
}
}
}
}
int ActionsWidget::resizeGetHeight(int newWidth) {
for_const (auto button, _buttons) {
resizeButton(button, newWidth, button->y());
}
return buttonsBottom();
}
int ActionsWidget::buttonsBottom() const {
if (_buttons.isEmpty()) {
return contentTop();
}
auto lastButton = _buttons.back();
return lastButton->y() + lastButton->height();
}
void ActionsWidget::onBotHelp() {
sendBotCommand(qsl("help"));
}
void ActionsWidget::onBotSettings() {
sendBotCommand(qsl("settings"));
}
void ActionsWidget::onClearHistory() {
QString confirmation;
if (auto user = peer()->asUser()) {
confirmation = lng_sure_delete_history(lt_contact, App::peerName(peer()));
} else if (auto chat = peer()->asChat()) {
confirmation = lng_sure_delete_group_history(lt_group, App::peerName(peer()));
}
if (!confirmation.isEmpty()) {
Ui::show(Box<ConfirmBox>(confirmation, lang(lng_box_delete), st::attentionBoxButton, base::lambda_guarded(this, [this] {
Ui::hideLayer();
App::main()->clearHistory(peer());
Ui::showPeerHistory(peer(), ShowAtUnreadMsgId);
})));
}
}
void ActionsWidget::onDeleteConversation() {
QString confirmation, confirmButton;
if (auto user = peer()->asUser()) {
confirmation = lng_sure_delete_history(lt_contact, App::peerName(peer()));
confirmButton = lang(lng_box_delete);
} else if (auto chat = peer()->asChat()) {
confirmation = lng_sure_delete_and_exit(lt_group, App::peerName(peer()));
confirmButton = lang(lng_box_leave);
}
if (!confirmation.isEmpty()) {
Ui::show(Box<ConfirmBox>(confirmation, confirmButton, st::attentionBoxButton, base::lambda_guarded(this, [this] {
Ui::hideLayer();
Ui::showChatsList();
if (auto user = peer()->asUser()) {
App::main()->deleteConversation(peer());
} else if (auto chat = peer()->asChat()) {
App::main()->deleteAndExit(chat);
}
})));
}
}
void ActionsWidget::onBlockUser() {
if (auto user = peer()->asUser()) {
if (user->isBlocked()) {
Auth().api().unblockUser(user);
} else {
Auth().api().blockUser(user);
}
}
}
void ActionsWidget::onUpgradeToSupergroup() {
if (auto chat = peer()->asChat()) {
Ui::show(Box<ConvertToSupergroupBox>(chat));
}
}
void ActionsWidget::onDeleteChannel() {
auto text = lang(peer()->isMegagroup() ? lng_sure_delete_group : lng_sure_delete_channel);
Ui::show(Box<ConfirmBox>(text, lang(lng_box_delete), st::attentionBoxButton, base::lambda_guarded(this, [this] {
Ui::hideLayer();
Ui::showChatsList();
if (auto chat = peer()->migrateFrom()) {
App::main()->deleteAndExit(chat);
}
if (auto channel = peer()->asChannel()) {
MTP::send(MTPchannels_DeleteChannel(channel->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::deleteChannelFailed));
}
})));
}
void ActionsWidget::onLeaveChannel() {
auto channel = peer()->asChannel();
if (!channel) return;
auto text = lang(channel->isMegagroup() ? lng_sure_leave_group : lng_sure_leave_channel);
Ui::show(Box<ConfirmBox>(text, lang(lng_box_leave), base::lambda_guarded(this, [this] {
Auth().api().leaveChannel(peer()->asChannel());
})));
}
void ActionsWidget::onSearchMembers() {
if (auto channel = peer()->asChannel()) {
ParticipantsBoxController::Start(
App::wnd()->controller(),
channel,
ParticipantsBoxController::Role::Members);
}
}
void ActionsWidget::onReport() {
Ui::show(Box<ReportBox>(peer()));
}
} // namespace Profile

View file

@ -1,98 +0,0 @@
/*
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 "profile/profile_block_widget.h"
namespace style {
struct OutlineButton;
} // namespace style
namespace st {
extern const style::OutlineButton &defaultLeftOutlineButton;
} // namespace st
namespace Ui {
class LeftOutlineButton;
} // namespace Ui
namespace Notify {
struct PeerUpdate;
} // namespace Notify
namespace Profile {
class ActionsWidget : public BlockWidget {
Q_OBJECT
public:
ActionsWidget(QWidget *parent, PeerData *peer);
protected:
// Resizes content and counts natural widget height for the desired width.
int resizeGetHeight(int newWidth) override;
private slots:
void onBotHelp();
void onBotSettings();
void onClearHistory();
void onDeleteConversation();
void onBlockUser();
void onUpgradeToSupergroup();
void onSearchMembers();
void onDeleteChannel();
void onLeaveChannel();
void onReport();
private:
// Observed notifications.
void notifyPeerUpdated(const Notify::PeerUpdate &update);
void validateBlockStatus() const;
int buttonsBottom() const;
void refreshButtons();
void refreshBlockUser();
void refreshDeleteChannel();
void refreshLeaveChannel();
void refreshVisibility();
Ui::LeftOutlineButton *addButton(const QString &text, const char *slot
, const style::OutlineButton &st = st::defaultLeftOutlineButton, int skipHeight = 0);
void resizeButton(Ui::LeftOutlineButton *button, int newWidth, int top);
QString getBlockButtonText() const;
bool hasBotCommand(const QString &command) const;
void sendBotCommand(const QString &command);
QList<Ui::LeftOutlineButton*> _buttons;
// Hold some button pointers to update / toggle them.
bool _hasBotHelp = false;
bool _hasBotSettings = false;
Ui::LeftOutlineButton *_blockUser = nullptr;
Ui::LeftOutlineButton *_deleteChannel = nullptr;
Ui::LeftOutlineButton *_leaveChannel = nullptr;
};
} // namespace Profile

View file

@ -1,174 +0,0 @@
/*
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 "profile/profile_block_channel_members.h"
#include "profile/profile_channel_controllers.h"
#include "styles/style_profile.h"
#include "ui/widgets/buttons.h"
#include "observer_peer.h"
#include "mainwidget.h"
#include "history/history_admin_log_section.h"
#include "lang/lang_keys.h"
#include "mainwindow.h"
#include "window/window_controller.h"
namespace Profile {
using UpdateFlag = Notify::PeerUpdate::Flag;
ChannelMembersWidget::ChannelMembersWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_participants_section)) {
auto observeEvents = UpdateFlag::ChannelRightsChanged
| UpdateFlag::AdminsChanged
| UpdateFlag::MembersChanged;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
notifyPeerUpdated(update);
}));
refreshButtons();
}
void ChannelMembersWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.peer != peer()) {
return;
}
if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::AdminsChanged)) {
refreshAdmins();
}
if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::MembersChanged)) {
refreshMembers();
}
refreshVisibility();
contentSizeUpdated();
}
void ChannelMembersWidget::addButton(const QString &text, object_ptr<Ui::LeftOutlineButton> *button, const char *slot) {
if (text.isEmpty()) {
button->destroy();
} else if (*button) {
(*button)->setText(text);
} else {
button->create(this, text, st::defaultLeftOutlineButton);
(*button)->show();
connect(*button, SIGNAL(clicked()), this, slot);
}
}
void ChannelMembersWidget::refreshButtons() {
refreshMembers();
refreshAdmins();
refreshVisibility();
}
void ChannelMembersWidget::refreshAdmins() {
auto getAdminsText = [this] {
if (auto channel = peer()->asChannel()) {
if (!channel->isMegagroup() && channel->canViewAdmins()) {
auto adminsCount = qMax(channel->adminsCount(), 1);
return lng_channel_admins_link(lt_count, adminsCount);
}
}
return QString();
};
addButton(getAdminsText(), &_admins, SLOT(onAdmins()));
auto getRecentActionsText = [this] {
if (auto channel = peer()->asChannel()) {
if (!channel->isMegagroup() && (channel->hasAdminRights() || channel->amCreator())) {
return lang(lng_profile_recent_actions);
}
}
return QString();
};
addButton(getRecentActionsText(), &_recentActions, SLOT(onRecentActions()));
}
void ChannelMembersWidget::refreshMembers() {
auto getMembersText = [this]() -> QString {
if (auto channel = peer()->asChannel()) {
if (!channel->isMegagroup() && channel->canViewMembers()) {
int membersCount = qMax(channel->membersCount(), 1);
return lng_channel_members_link(lt_count, membersCount);
}
}
return QString();
};
addButton(getMembersText(), &_members, SLOT(onMembers()));
}
void ChannelMembersWidget::refreshVisibility() {
setVisible(_admins || _members);
}
int ChannelMembersWidget::resizeGetHeight(int newWidth) {
int newHeight = contentTop();
auto resizeButton = [this, &newHeight, newWidth](object_ptr<Ui::LeftOutlineButton> &button) {
if (!button) {
return;
}
int left = defaultOutlineButtonLeft();
int availableWidth = newWidth - left - st::profileBlockMarginRight;
accumulate_min(availableWidth, st::profileBlockOneLineWidthMax);
button->resizeToWidth(availableWidth);
button->moveToLeft(left, newHeight);
newHeight += button->height();
};
resizeButton(_members);
resizeButton(_admins);
resizeButton(_recentActions);
return newHeight;
}
void ChannelMembersWidget::onMembers() {
if (auto channel = peer()->asChannel()) {
ParticipantsBoxController::Start(
App::wnd()->controller(),
channel,
ParticipantsBoxController::Role::Members);
}
}
void ChannelMembersWidget::onAdmins() {
if (auto channel = peer()->asChannel()) {
ParticipantsBoxController::Start(
App::wnd()->controller(),
channel,
ParticipantsBoxController::Role::Admins);
}
}
void ChannelMembersWidget::onRecentActions() {
if (auto channel = peer()->asChannel()) {
if (auto main = App::main()) {
main->showSection(
AdminLog::SectionMemento(channel),
Window::SectionShow());
}
}
}
} // namespace Profile

View file

@ -1,67 +0,0 @@
/*
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 "profile/profile_block_widget.h"
namespace Ui {
class LeftOutlineButton;
} // namespace Ui
namespace Notify {
struct PeerUpdate;
} // namespace Notify
namespace Profile {
class ChannelMembersWidget : public BlockWidget {
Q_OBJECT
public:
ChannelMembersWidget(QWidget *parent, PeerData *peer);
protected:
// Resizes content and counts natural widget height for the desired width.
int resizeGetHeight(int newWidth) override;
private slots:
void onMembers();
void onAdmins();
void onRecentActions();
private:
// Observed notifications.
void notifyPeerUpdated(const Notify::PeerUpdate &update);
void refreshButtons();
void refreshAdmins();
void refreshMembers();
void refreshVisibility();
void addButton(const QString &text, object_ptr<Ui::LeftOutlineButton> *button, const char *slot);
object_ptr<Ui::LeftOutlineButton> _members = { nullptr };
object_ptr<Ui::LeftOutlineButton> _admins = { nullptr };
object_ptr<Ui::LeftOutlineButton> _recentActions = { nullptr };
};
} // namespace Profile

View file

@ -1,265 +0,0 @@
/*
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 "profile/profile_block_info.h"
#include "styles/style_profile.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/slide_wrap.h"
#include "core/click_handler_types.h"
#include "mainwidget.h"
#include "observer_peer.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "ui/toast/toast.h"
#include "messenger.h"
namespace Profile {
constexpr int kCommonGroupsLimit = 20;
using UpdateFlag = Notify::PeerUpdate::Flag;
InfoWidget::InfoWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_info_section)) {
auto observeEvents = UpdateFlag::AboutChanged
| UpdateFlag::UsernameChanged
| UpdateFlag::UserPhoneChanged
| UpdateFlag::UserCanShareContact;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
notifyPeerUpdated(update);
}));
refreshLabels();
}
void InfoWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.peer != peer()) {
return;
}
if (update.flags & UpdateFlag::AboutChanged) {
refreshAbout();
}
if (update.flags & UpdateFlag::UsernameChanged) {
refreshUsername();
refreshChannelLink();
}
if (update.flags & (UpdateFlag::UserPhoneChanged | UpdateFlag::UserCanShareContact)) {
refreshMobileNumber();
}
refreshVisibility();
contentSizeUpdated();
}
int InfoWidget::resizeGetHeight(int newWidth) {
int initialHeight = contentTop();
int newHeight = initialHeight;
int marginLeft = st::profileBlockTextPart.margin.left();
int marginRight = st::profileBlockTextPart.margin.right();
int left = st::profileBlockTitlePosition.x();
if (_about) {
int textWidth = _about->naturalWidth();
int availableWidth = newWidth - left - st::profileBlockMarginRight;
int maxWidth = st::msgMaxWidth;
accumulate_min(textWidth, availableWidth);
accumulate_min(textWidth, st::msgMaxWidth);
_about->resizeToWidth(textWidth + marginLeft + marginRight);
_about->moveToLeft(left - marginLeft, newHeight - st::profileBlockTextPart.margin.top());
newHeight += _about->height();
}
auto moveLabeledText = [&newHeight, left, newWidth, marginLeft, marginRight](Ui::FlatLabel *label, Ui::FlatLabel *text, Ui::FlatLabel *shortText) {
if (!label) return;
label->moveToLeft(left, newHeight);
int textLeft = left + label->width() + st::normalFont->spacew;
int textWidth = text->naturalWidth();
int availableWidth = newWidth - textLeft - st::profileBlockMarginRight;
bool doesNotFit = (textWidth > availableWidth);
accumulate_min(textWidth, availableWidth);
accumulate_min(textWidth, st::msgMaxWidth);
text->resizeToWidth(textWidth + marginLeft + marginRight);
text->moveToLeft(textLeft - marginLeft, newHeight - st::profileBlockOneLineTextPart.margin.top());
if (shortText) {
shortText->resizeToWidth(textWidth + marginLeft + marginRight);
shortText->moveToLeft(textLeft - marginLeft, newHeight - st::profileBlockOneLineTextPart.margin.top());
if (doesNotFit) {
shortText->show();
text->hide();
} else {
shortText->hide();
text->show();
}
}
newHeight += qMax(label->height(), text->height() - st::profileBlockTextPart.margin.top() - st::profileBlockTextPart.margin.bottom()) + st::profileBlockOneLineSkip;
};
moveLabeledText(_channelLinkLabel, _channelLink, _channelLinkShort);
moveLabeledText(_mobileNumberLabel, _mobileNumber, nullptr);
moveLabeledText(_bioLabel, _bio, nullptr);
moveLabeledText(_usernameLabel, _username, nullptr);
newHeight += st::profileBlockMarginBottom;
return newHeight;
}
void InfoWidget::leaveEventHook(QEvent *e) {
BotCommandClickHandler::setPeerForCommand(nullptr);
BotCommandClickHandler::setBotForCommand(nullptr);
}
void InfoWidget::refreshLabels() {
refreshAbout();
refreshMobileNumber();
refreshUsername();
refreshChannelLink();
refreshVisibility();
}
void InfoWidget::refreshVisibility() {
setVisible(_about || _mobileNumber || _username || _bio || _channelLink);
}
void InfoWidget::refreshAbout() {
auto getAboutText = [this]() -> QString {
if (auto user = peer()->asUser()) {
return user->about();
} else if (auto channel = peer()->asChannel()) {
return channel->about();
}
return QString();
};
_about.destroy();
_bioLabel.destroy();
_bio.destroy();
auto aboutText = TextWithEntities { TextUtilities::Clean(getAboutText()) };
auto displayAsBio = false;
if (auto user = peer()->asUser()) {
if (!user->botInfo) {
displayAsBio = true;
}
}
if (displayAsBio) {
aboutText.text = TextUtilities::SingleLine(aboutText.text);
}
if (!aboutText.text.isEmpty()) {
if (displayAsBio) {
setLabeledText(&_bioLabel, lang(lng_profile_bio), &_bio, aboutText, st::profileBioLabel, QString());
} else {
_about.create(this, st::profileBlockTextPart);
_about->show();
TextUtilities::ParseEntities(aboutText, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands);
_about->setMarkedText(aboutText);
_about->setSelectable(true);
_about->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) {
BotCommandClickHandler::setPeerForCommand(peer());
return true;
});
}
}
}
void InfoWidget::refreshMobileNumber() {
TextWithEntities phoneText;
if (auto user = peer()->asUser()) {
if (!user->phone().isEmpty()) {
phoneText.text = App::formatPhone(user->phone());
} else {
phoneText.text = App::phoneFromSharedContact(peerToUser(user->id));
}
}
setSingleLineLabeledText(&_mobileNumberLabel, lang(lng_profile_mobile_number), &_mobileNumber, phoneText, lang(lng_profile_copy_phone));
}
void InfoWidget::refreshUsername() {
TextWithEntities usernameText;
if (auto user = peer()->asUser()) {
if (!user->username.isEmpty()) {
usernameText.text = '@' + user->username;
}
}
setSingleLineLabeledText(&_usernameLabel, lang(lng_profile_username), &_username, usernameText, lang(lng_context_copy_mention));
}
void InfoWidget::refreshChannelLink() {
TextWithEntities channelLinkText;
TextWithEntities channelLinkTextShort;
if (auto channel = peer()->asChannel()) {
if (!channel->username.isEmpty()) {
channelLinkText.text = Messenger::Instance().createInternalLinkFull(channel->username);
channelLinkText.entities.push_back(EntityInText(EntityInTextUrl, 0, channelLinkText.text.size()));
channelLinkTextShort.text = Messenger::Instance().createInternalLink(channel->username);
channelLinkTextShort.entities.push_back(EntityInText(EntityInTextCustomUrl, 0, channelLinkTextShort.text.size(), Messenger::Instance().createInternalLinkFull(channel->username)));
}
}
setSingleLineLabeledText(nullptr, lang(lng_profile_link), &_channelLink, channelLinkText, QString());
setSingleLineLabeledText(&_channelLinkLabel, lang(lng_profile_link), &_channelLinkShort, channelLinkTextShort, QString());
auto copyLinkHandlerHook = [this](const ClickHandlerPtr &handler, Qt::MouseButton button) {
if (auto channel = peer()->asChannel()) {
auto link = Messenger::Instance().createInternalLinkFull(channel->username);
QGuiApplication::clipboard()->setText(link);
Ui::Toast::Show(lang(lng_create_channel_link_copied));
return false;
}
return true;
};
if (_channelLink) {
_channelLink->setClickHandlerHook(copyLinkHandlerHook);
}
if (_channelLinkShort) {
_channelLinkShort->setExpandLinksMode(ExpandLinksUrlOnly);
_channelLinkShort->setClickHandlerHook(copyLinkHandlerHook);
}
}
void InfoWidget::setLabeledText(object_ptr<Ui::FlatLabel> *labelWidget, const QString &label,
object_ptr<Ui::FlatLabel> *textWidget, const TextWithEntities &textWithEntities,
const style::FlatLabel &st, const QString &copyText) {
if (labelWidget) labelWidget->destroy();
textWidget->destroy();
if (textWithEntities.text.isEmpty()) {
return;
}
if (labelWidget) {
labelWidget->create(this, label, Ui::FlatLabel::InitType::Simple, st::profileBlockLabel);
(*labelWidget)->show();
}
textWidget->create(this, QString(), Ui::FlatLabel::InitType::Simple, st);
(*textWidget)->show();
(*textWidget)->setMarkedText(textWithEntities);
(*textWidget)->setContextCopyText(copyText);
(*textWidget)->setSelectable(true);
}
void InfoWidget::setSingleLineLabeledText(object_ptr<Ui::FlatLabel> *labelWidget, const QString &label,
object_ptr<Ui::FlatLabel> *textWidget, const TextWithEntities &textWithEntities, const QString &copyText) {
setLabeledText(labelWidget, label, textWidget, textWithEntities, st::profileBlockOneLineTextPart, copyText);
if (*textWidget) {
(*textWidget)->setDoubleClickSelectsParagraph(true);
}
}
} // namespace Profile

View file

@ -1,80 +0,0 @@
/*
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 "profile/profile_block_widget.h"
namespace style {
struct FlatLabel;
} // namespace style
namespace Ui {
class FlatLabel;
} // namespace Ui
namespace Notify {
struct PeerUpdate;
} // namespace Notify
namespace Profile {
class InfoWidget : public BlockWidget, public RPCSender {
public:
InfoWidget(QWidget *parent, PeerData *peer);
protected:
// Resizes content and counts natural widget height for the desired width.
int resizeGetHeight(int newWidth) override;
void leaveEventHook(QEvent *e) override;
private:
// Observed notifications.
void notifyPeerUpdated(const Notify::PeerUpdate &update);
void refreshLabels();
void refreshAbout();
void refreshMobileNumber();
void refreshUsername();
void refreshChannelLink();
void refreshVisibility();
// labelWidget may be nullptr.
void setLabeledText(object_ptr<Ui::FlatLabel> *labelWidget, const QString &label,
object_ptr<Ui::FlatLabel> *textWidget, const TextWithEntities &textWithEntities,
const style::FlatLabel &st, const QString &copyText);
void setSingleLineLabeledText(object_ptr<Ui::FlatLabel> *labelWidget, const QString &label,
object_ptr<Ui::FlatLabel> *textWidget, const TextWithEntities &textWithEntities, const QString &copyText);
object_ptr<Ui::FlatLabel> _about = { nullptr };
object_ptr<Ui::FlatLabel> _channelLinkLabel = { nullptr };
object_ptr<Ui::FlatLabel> _channelLink = { nullptr };
object_ptr<Ui::FlatLabel> _channelLinkShort = { nullptr };
object_ptr<Ui::FlatLabel> _mobileNumberLabel = { nullptr };
object_ptr<Ui::FlatLabel> _mobileNumber = { nullptr };
object_ptr<Ui::FlatLabel> _bioLabel = { nullptr };
object_ptr<Ui::FlatLabel> _bio = { nullptr };
object_ptr<Ui::FlatLabel> _usernameLabel = { nullptr };
object_ptr<Ui::FlatLabel> _username = { nullptr };
};
} // namespace Profile

View file

@ -1,118 +0,0 @@
/*
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 "profile/profile_block_invite_link.h"
#include "styles/style_profile.h"
#include "ui/widgets/labels.h"
#include "ui/toast/toast.h"
#include "boxes/confirm_box.h"
#include "observer_peer.h"
#include "mainwindow.h"
#include "lang/lang_keys.h"
namespace Profile {
using UpdateFlag = Notify::PeerUpdate::Flag;
InviteLinkWidget::InviteLinkWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_invite_link_section)) {
auto observeEvents = UpdateFlag::InviteLinkChanged | UpdateFlag::UsernameChanged;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
notifyPeerUpdated(update);
}));
refreshLink();
refreshVisibility();
}
void InviteLinkWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.peer != peer()) {
return;
}
if (update.flags & (UpdateFlag::InviteLinkChanged | UpdateFlag::UsernameChanged)) {
refreshLink();
refreshVisibility();
contentSizeUpdated();
}
}
int InviteLinkWidget::resizeGetHeight(int newWidth) {
int newHeight = contentTop();
int marginLeft = st::profileBlockTextPart.margin.left();
int marginRight = st::profileBlockTextPart.margin.right();
int left = st::profileBlockTitlePosition.x();
if (_link) {
int textWidth = _link->naturalWidth();
int availableWidth = newWidth - left - st::profileBlockMarginRight;
int maxWidth = st::msgMaxWidth;
accumulate_min(textWidth, availableWidth);
accumulate_min(textWidth, st::msgMaxWidth);
_link->resizeToWidth(textWidth + marginLeft + marginRight);
_link->moveToLeft(left - marginLeft, newHeight - st::profileBlockTextPart.margin.top());
newHeight += _link->height();
}
newHeight += st::profileBlockMarginBottom;
return newHeight;
}
void InviteLinkWidget::refreshVisibility() {
setVisible(_link != nullptr);
}
QString InviteLinkWidget::getInviteLink() const {
if (auto chat = peer()->asChat()) {
return chat->inviteLink();
} else if (auto channel = peer()->asChannel()) {
return channel->isPublic() ? QString() : channel->inviteLink();
}
return QString();
};
void InviteLinkWidget::refreshLink() {
_link.destroy();
TextWithEntities linkData = { getInviteLink(), EntitiesInText() };
if (linkData.text.isEmpty()) {
_link.destroy();
} else {
_link.create(this, QString(), Ui::FlatLabel::InitType::Simple, st::profileInviteLinkText);
_link->show();
linkData.entities.push_back(EntityInText(EntityInTextUrl, 0, linkData.text.size()));
_link->setMarkedText(linkData);
_link->setSelectable(true);
_link->setContextCopyText(QString());
_link->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) {
auto link = getInviteLink();
if (link.isEmpty()) {
return true;
}
QApplication::clipboard()->setText(link);
Ui::Toast::Show(lang(lng_group_invite_copied));
return false;
});
}
}
} // namespace Profile

View file

@ -1,55 +0,0 @@
/*
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 "profile/profile_block_widget.h"
namespace Ui {
class FlatLabel;
} // namespace Ui
namespace Notify {
struct PeerUpdate;
} // namespace Notify
namespace Profile {
class InviteLinkWidget : public BlockWidget {
public:
InviteLinkWidget(QWidget *parent, PeerData *peer);
protected:
// Resizes content and counts natural widget height for the desired width.
int resizeGetHeight(int newWidth) override;
private:
// Observed notifications.
void notifyPeerUpdated(const Notify::PeerUpdate &update);
QString getInviteLink() const;
void refreshLink();
void refreshVisibility();
object_ptr<Ui::FlatLabel> _link = { nullptr };
};
} // namespace Profile

View file

@ -1,270 +0,0 @@
/*
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 "profile/profile_block_settings.h"
#include "profile/profile_channel_controllers.h"
#include "history/history_admin_log_section.h"
#include "styles/style_profile.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/confirm_box.h"
#include "observer_peer.h"
#include "auth_session.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "window/window_controller.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
namespace Profile {
using UpdateFlag = Notify::PeerUpdate::Flag;
SettingsWidget::SettingsWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_settings_section))
, _enableNotifications(this, lang(lng_profile_enable_notifications), true, st::defaultCheckbox) {
subscribe(_enableNotifications->checkedChanged, [this](bool checked) { onNotificationsChange(); });
Notify::PeerUpdate::Flags observeEvents = UpdateFlag::NotificationsEnabled;
if (auto chat = peer->asChat()) {
if (chat->amCreator()) {
observeEvents |= UpdateFlag::ChatCanEdit | UpdateFlag::InviteLinkChanged;
}
} else if (auto channel = peer->asChannel()) {
observeEvents |= UpdateFlag::ChannelRightsChanged | UpdateFlag::BannedUsersChanged | UpdateFlag::UsernameChanged | UpdateFlag::InviteLinkChanged;
}
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
notifyPeerUpdated(update);
}));
refreshButtons();
_enableNotifications->finishAnimating();
show();
}
void SettingsWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.peer != peer()) {
return;
}
if (update.flags & UpdateFlag::NotificationsEnabled) {
refreshEnableNotifications();
}
if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::ChatCanEdit | UpdateFlag::UsernameChanged | UpdateFlag::InviteLinkChanged)) {
refreshInviteLinkButton();
}
if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::ChatCanEdit)) {
refreshManageAdminsButton();
}
if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::BannedUsersChanged)) {
refreshManageBannedUsersButton();
}
contentSizeUpdated();
}
int SettingsWidget::resizeGetHeight(int newWidth) {
int newHeight = contentTop() + st::profileEnableNotificationsTop;
_enableNotifications->moveToLeft(st::profileBlockTitlePosition.x(), newHeight);
newHeight += _enableNotifications->heightNoMargins() + st::profileSettingsBlockSkip;
auto moveLink = [&newHeight, newWidth](Ui::LeftOutlineButton *button) {
if (!button) return;
int left = defaultOutlineButtonLeft();
int availableWidth = newWidth - left - st::profileBlockMarginRight;
accumulate_min(availableWidth, st::profileBlockOneLineWidthMax);
button->resizeToWidth(availableWidth);
button->moveToLeft(left, newHeight);
newHeight += button->height();
};
moveLink(_manageAdmins);
moveLink(_recentActions);
moveLink(_manageBannedUsers);
moveLink(_manageRestrictedUsers);
moveLink(_inviteLink);
newHeight += st::profileBlockMarginBottom;
return newHeight;
}
void SettingsWidget::refreshButtons() {
refreshEnableNotifications();
refreshManageAdminsButton();
refreshManageBannedUsersButton();
refreshInviteLinkButton();
}
void SettingsWidget::refreshEnableNotifications() {
if (peer()->notify == UnknownNotifySettings) {
Auth().api().requestNotifySetting(peer());
} else {
auto &notifySettings = peer()->notify;
bool enabled = (notifySettings == EmptyNotifySettings || notifySettings->mute < unixtime());
_enableNotifications->setChecked(enabled, Ui::Checkbox::NotifyAboutChange::DontNotify);
}
}
void SettingsWidget::refreshManageAdminsButton() {
auto hasManageAdmins = [this] {
if (auto chat = peer()->asChat()) {
return (chat->amCreator() && chat->canEdit());
} else if (auto channel = peer()->asMegagroup()) {
return channel->hasAdminRights() || channel->amCreator();
}
return false;
};
_manageAdmins.destroy();
if (hasManageAdmins()) {
_manageAdmins.create(this, lang(lng_profile_manage_admins), st::defaultLeftOutlineButton);
_manageAdmins->show();
connect(_manageAdmins, SIGNAL(clicked()), this, SLOT(onManageAdmins()));
}
auto hasRecentActions = [this] {
if (auto channel = peer()->asMegagroup()) {
return channel->hasAdminRights() || channel->amCreator();
}
return false;
};
_recentActions.destroy();
if (hasRecentActions()) {
_recentActions.create(this, lang(lng_profile_recent_actions), st::defaultLeftOutlineButton);
_recentActions->show();
connect(_recentActions, SIGNAL(clicked()), this, SLOT(onRecentActions()));
}
}
void SettingsWidget::refreshManageBannedUsersButton() {
auto hasManageBannedUsers = [this] {
if (auto channel = peer()->asMegagroup()) {
return channel->canViewBanned() && (channel->kickedCount() > 0);
}
return false;
};
_manageBannedUsers.destroy();
if (hasManageBannedUsers()) {
_manageBannedUsers.create(this, lang(lng_profile_manage_blocklist), st::defaultLeftOutlineButton);
_manageBannedUsers->show();
connect(_manageBannedUsers, SIGNAL(clicked()), this, SLOT(onManageBannedUsers()));
}
auto hasManageRestrictedUsers = [this] {
if (auto channel = peer()->asMegagroup()) {
return channel->canViewBanned() && (channel->restrictedCount() > 0);
}
return false;
};
_manageRestrictedUsers.destroy();
if (hasManageRestrictedUsers()) {
_manageRestrictedUsers.create(this, lang(lng_profile_manage_restrictedlist), st::defaultLeftOutlineButton);
_manageRestrictedUsers->show();
connect(_manageRestrictedUsers, SIGNAL(clicked()), this, SLOT(onManageRestrictedUsers()));
}
}
void SettingsWidget::refreshInviteLinkButton() {
auto getInviteLinkText = [this]() -> QString {
if (auto chat = peer()->asChat()) {
if (chat->amCreator() && chat->canEdit()) {
return lang(chat->inviteLink().isEmpty() ? lng_group_invite_create : lng_group_invite_create_new);
}
} else if (auto channel = peer()->asChannel()) {
if (channel->canHaveInviteLink() && !channel->isPublic()) {
return lang(channel->inviteLink().isEmpty() ? lng_group_invite_create : lng_group_invite_create_new);
}
}
return QString();
};
auto inviteLinkText = getInviteLinkText();
if (inviteLinkText.isEmpty()) {
_inviteLink.destroy();
} else {
_inviteLink.create(this, inviteLinkText, st::defaultLeftOutlineButton);
_inviteLink->show();
connect(_inviteLink, SIGNAL(clicked()), this, SLOT(onInviteLink()));
}
}
void SettingsWidget::onNotificationsChange() {
App::main()->updateNotifySetting(peer(), _enableNotifications->checked() ? NotifySettingSetNotify : NotifySettingSetMuted);
}
void SettingsWidget::onManageAdmins() {
if (auto chat = peer()->asChat()) {
EditChatAdminsBoxController::Start(chat);
} else if (auto channel = peer()->asChannel()) {
ParticipantsBoxController::Start(
App::wnd()->controller(),
channel,
ParticipantsBoxController::Role::Admins);
}
}
void SettingsWidget::onRecentActions() {
if (auto channel = peer()->asChannel()) {
if (auto main = App::main()) {
main->showSection(
AdminLog::SectionMemento(channel),
Window::SectionShow());
}
}
}
void SettingsWidget::onManageBannedUsers() {
if (auto channel = peer()->asMegagroup()) {
ParticipantsBoxController::Start(
App::wnd()->controller(),
channel,
ParticipantsBoxController::Role::Kicked);
}
}
void SettingsWidget::onManageRestrictedUsers() {
if (auto channel = peer()->asMegagroup()) {
ParticipantsBoxController::Start(
App::wnd()->controller(),
channel,
ParticipantsBoxController::Role::Restricted);
}
}
void SettingsWidget::onInviteLink() {
auto getInviteLink = [this]() {
if (auto chat = peer()->asChat()) {
return chat->inviteLink();
} else if (auto channel = peer()->asChannel()) {
return channel->inviteLink();
}
return QString();
};
auto link = getInviteLink();
auto text = lang(link.isEmpty() ? lng_group_invite_about : lng_group_invite_about_new);
Ui::show(Box<ConfirmBox>(text, base::lambda_guarded(this, [this] {
Ui::hideLayer();
Auth().api().exportInviteLink(peer());
})));
}
} // namespace Profile

View file

@ -1,73 +0,0 @@
/*
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 "profile/profile_block_widget.h"
namespace Ui {
class Checkbox;
class LeftOutlineButton;
} // namespace Ui
namespace Notify {
struct PeerUpdate;
} // namespace Notify
namespace Profile {
class SettingsWidget : public BlockWidget {
Q_OBJECT
public:
SettingsWidget(QWidget *parent, PeerData *peer);
protected:
// Resizes content and counts natural widget height for the desired width.
int resizeGetHeight(int newWidth) override;
private slots:
void onNotificationsChange();
void onManageAdmins();
void onRecentActions();
void onManageBannedUsers();
void onManageRestrictedUsers();
void onInviteLink();
private:
// Observed notifications.
void notifyPeerUpdated(const Notify::PeerUpdate &update);
void refreshButtons();
void refreshEnableNotifications();
void refreshManageAdminsButton();
void refreshManageBannedUsersButton();
void refreshInviteLinkButton();
object_ptr<Ui::Checkbox> _enableNotifications;
object_ptr<Ui::LeftOutlineButton> _manageAdmins = { nullptr };
object_ptr<Ui::LeftOutlineButton> _recentActions = { nullptr };
object_ptr<Ui::LeftOutlineButton> _manageBannedUsers = { nullptr };
object_ptr<Ui::LeftOutlineButton> _manageRestrictedUsers = { nullptr };
object_ptr<Ui::LeftOutlineButton> _inviteLink = { nullptr };
};
} // namespace Profile

View file

@ -1,211 +0,0 @@
/*
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 "profile/profile_block_shared_media.h"
#include "profile/profile_common_groups_section.h"
#include "profile/profile_section_memento.h"
#include "styles/style_profile.h"
#include "observer_peer.h"
#include "ui/widgets/buttons.h"
#include "mainwidget.h"
#include "lang/lang_keys.h"
#include "window/window_controller.h"
namespace Profile {
namespace {
QString getButtonText(MediaOverviewType type, int count) {
if (count <= 0) {
return QString();
}
switch (type) {
case OverviewPhotos: return lng_profile_photos(lt_count, count);
case OverviewVideos: return lng_profile_videos(lt_count, count);
case OverviewMusicFiles: return lng_profile_songs(lt_count, count);
case OverviewFiles: return lng_profile_files(lt_count, count);
case OverviewVoiceFiles: return lng_profile_audios(lt_count, count);
case OverviewLinks: return lng_profile_shared_links(lt_count, count);
}
return QString();
}
} // namespace
SharedMediaWidget::SharedMediaWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_shared_media))
, _history(App::history(peer))
, _migrated(_history->migrateFrom()) {
auto observeEvents = Notify::PeerUpdate::Flag::SharedMediaChanged
| Notify::PeerUpdate::Flag::UserCommonChatsChanged;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
notifyPeerUpdated(update);
}));
for (auto i = 0; i != OverviewCount; ++i) {
auto type = static_cast<MediaOverviewType>(i);
if (!getButtonText(type, 1).isEmpty()) {
App::main()->preloadOverview(peer, type);
if (_migrated) {
App::main()->preloadOverview(_migrated->peer, type);
}
}
}
refreshButtons();
refreshVisibility();
}
void SharedMediaWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.peer != peer() && (!_migrated || update.peer != _migrated->peer)) {
return;
}
auto updated = false;
for (auto i = 0; i != OverviewCount; ++i) {
if (update.mediaTypesMask & (1 << i)) {
refreshButton(static_cast<MediaOverviewType>(i));
updated = true;
}
}
if (update.flags & Notify::PeerUpdate::Flag::UserCommonChatsChanged) {
refreshCommonGroups();
updated = true;
}
if (updated) {
refreshVisibility();
contentSizeUpdated();
}
}
int SharedMediaWidget::resizeGetHeight(int newWidth) {
int newHeight = contentTop();
resizeButtons(newWidth, &newHeight);
return newHeight;
}
void SharedMediaWidget::refreshButtons() {
for (int typeIndex = 0; typeIndex < OverviewCount; ++typeIndex) {
refreshButton(static_cast<MediaOverviewType>(typeIndex));
}
refreshCommonGroups();
}
void SharedMediaWidget::refreshButton(MediaOverviewType type) {
int count = _history->overviewCount(type), migrated = _migrated ? _migrated->overviewCount(type) : 0;
int finalCount = (count >= 0 && migrated >= 0) ? (count + migrated) : -1;
auto text = getButtonText(type, finalCount);
if (text.isEmpty()) {
if (_mediaButtons[type]) {
delete _mediaButtons[type];
_mediaButtons[type] = nullptr;
}
} else {
if (_mediaButtons[type]) {
_mediaButtons[type]->setText(text);
} else {
_mediaButtons[type] = new Ui::LeftOutlineButton(this, text, st::defaultLeftOutlineButton);
_mediaButtons[type]->show();
connect(_mediaButtons[type], SIGNAL(clicked()), this, SLOT(onMediaChosen()));
}
}
}
void SharedMediaWidget::refreshVisibility() {
for_const (auto button, _mediaButtons) {
if (button) {
show();
return;
}
}
setVisible(_commonGroups != nullptr);
}
void SharedMediaWidget::onMediaChosen() {
for (int i = 0; i < OverviewCount; ++i) {
auto button = _mediaButtons[i];
if (button && button == sender()) {
// SharedMediaShowOverview();
return;
}
}
}
void SharedMediaWidget::resizeButtons(int newWidth, int *top) {
Assert(top != nullptr);
int left = defaultOutlineButtonLeft();
int availableWidth = newWidth - left - st::profileBlockMarginRight;
accumulate_min(availableWidth, st::profileBlockOneLineWidthMax);
for_const (auto button, _mediaButtons) {
if (!button) continue;
button->resizeToWidth(availableWidth);
button->moveToLeft(left, *top);
*top += button->height();
}
if (_commonGroups) {
_commonGroups->resizeToWidth(availableWidth);
_commonGroups->moveToLeft(left, *top);
*top += _commonGroups->height();
}
}
int SharedMediaWidget::getCommonGroupsCount() const {
if (auto user = peer()->asUser()) {
return user->commonChatsCount();
}
return 0;
}
void SharedMediaWidget::refreshCommonGroups() {
if (auto count = getCommonGroupsCount()) {
auto text = lng_profile_common_groups(lt_count, count);
if (_commonGroups) {
_commonGroups->setText(text);
} else {
_commonGroups.create(this, text, st::defaultLeftOutlineButton);
_commonGroups->setClickedCallback([this] { onShowCommonGroups(); });
_commonGroups->show();
}
} else if (_commonGroups) {
_commonGroups.destroyDelayed();
}
}
void SharedMediaWidget::onShowCommonGroups() {
auto count = getCommonGroupsCount();
if (count <= 0) {
refreshCommonGroups();
return;
}
if (auto main = App::main()) {
main->showSection(
Profile::CommonGroups::SectionMemento(peer()->asUser()),
Window::SectionShow());
}
}
} // namespace Profile

View file

@ -1,71 +0,0 @@
/*
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 "profile/profile_block_widget.h"
namespace Ui {
class LeftOutlineButton;
} // namespace Ui
namespace Notify {
struct PeerUpdate;
} // namespace Notify
namespace Profile {
class SharedMediaWidget : public BlockWidget {
Q_OBJECT
public:
SharedMediaWidget(QWidget *parent, PeerData *peer);
protected:
// Resizes content and counts natural widget height for the desired width.
int resizeGetHeight(int newWidth) override;
private slots:
void onMediaChosen();
private:
// Observed notifications.
void notifyPeerUpdated(const Notify::PeerUpdate &update);
void refreshButtons();
void refreshButton(MediaOverviewType type);
void refreshCommonGroups();
void refreshVisibility();
int getCommonGroupsCount() const;
void onShowCommonGroups();
void slideCommonGroupsDown();
void resizeButtons(int newWidth, int *top);
Ui::LeftOutlineButton *_mediaButtons[OverviewCount] = { nullptr };
object_ptr<Ui::LeftOutlineButton> _commonGroups = { nullptr };
History *_history;
History *_migrated;
};
} // namespace Profile

View file

@ -331,6 +331,10 @@ void ParticipantsBoxController::restoreState(
}
}
rpl::producer<int> ParticipantsBoxController::onlineCountValue() const {
return _onlineCount.value();
}
template <typename Callback>
void ParticipantsBoxController::HandleParticipant(const MTPChannelParticipant &participant, Role role, not_null<Additional*> additional, Callback callback) {
if ((role == Role::Profile

View file

@ -95,9 +95,7 @@ public:
not_null<Additional*> additional,
Callback callback);
rpl::producer<int> onlineCountValue() const override {
return _onlineCount.value();
}
rpl::producer<int> onlineCountValue() const override;
protected:
virtual std::unique_ptr<PeerListRow> createRow(not_null<UserData*> user) const;

View file

@ -1,470 +0,0 @@
/*
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 "profile/profile_common_groups_section.h"
#include "profile/profile_section_memento.h"
#include "profile/profile_back_button.h"
#include "styles/style_widgets.h"
#include "styles/style_profile.h"
#include "styles/style_window.h"
#include "styles/style_settings.h"
#include "ui/effects/ripple_animation.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/shadow.h"
#include "mainwidget.h"
#include "observer_peer.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "mainwindow.h"
#include "window/window_controller.h"
namespace Profile {
namespace CommonGroups {
namespace {
constexpr int kCommonGroupsPerPage = 40;
} // namespace
object_ptr<Window::SectionWidget> SectionMemento::createWidget(
QWidget *parent,
not_null<Window::Controller*> controller,
Window::Column column,
const QRect &geometry) {
auto result = object_ptr<Widget>(parent, controller, _user);
result->setInternalState(geometry, this);
return std::move(result);
}
FixedBar::FixedBar(QWidget *parent) : TWidget(parent)
, _backButton(this, lang(lng_profile_common_groups_section)) {
_backButton->moveToLeft(0, 0);
connect(_backButton, SIGNAL(clicked()), this, SLOT(onBack()));
}
void FixedBar::onBack() {
App::main()->showBackFromStack(Window::SectionShow());
}
int FixedBar::resizeGetHeight(int newWidth) {
auto newHeight = 0;
auto buttonLeft = newWidth;
_backButton->resizeToWidth(newWidth);
_backButton->moveToLeft(0, 0);
newHeight += _backButton->height();
return newHeight;
}
void FixedBar::setAnimatingMode(bool enabled) {
if (_animatingMode != enabled) {
_animatingMode = enabled;
setCursor(_animatingMode ? style::cur_pointer : style::cur_default);
if (_animatingMode) {
setAttribute(Qt::WA_OpaquePaintEvent, false);
hideChildren();
} else {
setAttribute(Qt::WA_OpaquePaintEvent);
showChildren();
}
show();
}
}
void FixedBar::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
onBack();
} else {
TWidget::mousePressEvent(e);
}
}
InnerWidget::Item::Item(PeerData *peer) : peer(peer) {
}
InnerWidget::Item::~Item() = default;
InnerWidget::InnerWidget(QWidget *parent, not_null<UserData*> user) : TWidget(parent)
, _user(user) {
setMouseTracking(true);
setAttribute(Qt::WA_OpaquePaintEvent);
_rowHeight = st::profileCommonGroupsPadding.top() + st::profileCommonGroupsPhotoSize + st::profileCommonGroupsPadding.bottom();
_contentTop = st::profileCommonGroupsSkip;
}
void InnerWidget::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
checkPreloadMore();
}
void InnerWidget::checkPreloadMore() {
if (_visibleTop + PreloadHeightsCount * (_visibleBottom - _visibleTop) > height()) {
preloadMore();
}
}
void InnerWidget::saveState(not_null<SectionMemento*> memento) {
if (auto count = _items.size()) {
QList<not_null<PeerData*>> groups;
groups.reserve(count);
for_const (auto item, _items) {
groups.push_back(item->peer);
}
memento->setCommonGroups(groups);
}
}
void InnerWidget::restoreState(not_null<SectionMemento*> memento) {
auto list = memento->getCommonGroups();
_allLoaded = false;
if (!list.empty()) {
showInitial(list);
}
}
void InnerWidget::showInitial(const QList<not_null<PeerData*>> &list) {
for_const (auto group, list) {
if (auto item = computeItem(group)) {
_items.push_back(item);
}
_preloadGroupId = group->bareId();
}
updateSize();
}
void InnerWidget::preloadMore() {
if (_preloadRequestId || _allLoaded) {
return;
}
auto request = MTPmessages_GetCommonChats(user()->inputUser, MTP_int(_preloadGroupId), MTP_int(kCommonGroupsPerPage));
_preloadRequestId = MTP::send(request, ::rpcDone(base::lambda_guarded(this, [this](const MTPmessages_Chats &result) {
_preloadRequestId = 0;
_preloadGroupId = 0;
_allLoaded = true;
if (auto chats = Api::getChatsFromMessagesChats(result)) {
auto &list = chats->v;
if (!list.empty()) {
_items.reserve(_items.size() + list.size());
for_const (auto &chatData, list) {
if (auto chat = App::feedChat(chatData)) {
auto found = false;
for_const (auto item, _items) {
if (item->peer == chat) {
found = true;
break;
}
}
if (!found) {
if (auto item = computeItem(chat)) {
_items.push_back(item);
}
}
_preloadGroupId = chat->bareId();
_allLoaded = false;
}
}
updateSize();
}
}
})));
}
void InnerWidget::updateSize() {
TWidget::resizeToWidth(width());
checkPreloadMore();
}
int InnerWidget::resizeGetHeight(int newWidth) {
update();
auto contentLeftMin = st::profileCommonGroupsLeftMin;
auto contentLeftMax = st::profileCommonGroupsLeftMax;
auto widthWithMin = st::columnMinimalWidthMain;
auto widthWithMax = st::profileCommonGroupsWidthMax + 2 * contentLeftMax;
_contentLeft = anim::interpolate(contentLeftMax, contentLeftMin, qMax(widthWithMax - newWidth, 0) / float64(widthWithMax - widthWithMin));
_contentWidth = qMin(newWidth - 2 * _contentLeft, st::profileCommonGroupsWidthMax);
auto newHeight = _contentTop;
newHeight += _items.size() * _rowHeight;
newHeight += st::profileCommonGroupsSkip;
return qMax(newHeight, _minHeight);
}
void InnerWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
auto ms = getms();
auto clip = e->rect();
p.fillRect(clip, st::profileBg);
auto from = floorclamp(clip.y() - _contentTop, _rowHeight, 0, _items.size());
auto to = ceilclamp(clip.y() + clip.height() - _contentTop, _rowHeight, 0, _items.size());
for (auto i = from; i != to; ++i) {
paintRow(p, i, ms);
}
}
void InnerWidget::paintRow(Painter &p, int index, TimeMs ms) {
auto item = _items[index];
auto selected = (_pressed >= 0) ? (index == _pressed) : (index == _selected);
auto x = _contentLeft;
auto y = _contentTop + index * _rowHeight;
if (selected) {
p.fillRect(myrtlrect(x, y, _contentWidth, _rowHeight), st::profileCommonGroupsBgOver);
}
if (auto &ripple = item->ripple) {
ripple->paint(p, x, y, width(), ms);
if (ripple->empty()) {
ripple.reset();
}
}
x += st::profileCommonGroupsPadding.left();
y += st::profileCommonGroupsPadding.top();
item->peer->paintUserpic(p, rtl() ? (width() - x - st::profileCommonGroupsPhotoSize) : x, y, st::profileCommonGroupsPhotoSize);
p.setPen(st::profileMemberNameFg);
x += st::profileCommonGroupsPhotoSize + st::profileCommonGroupsNameLeft;
y += st::profileCommonGroupsNameTop;
auto nameWidth = _contentWidth - (x - _contentLeft) - st::profileCommonGroupsPadding.right();
if (item->name.isEmpty()) {
item->name.setText(st::msgNameStyle, App::peerName(item->peer), _textNameOptions);
}
_items[index]->name.drawLeftElided(p, x, y, nameWidth, width());
}
void InnerWidget::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
emit cancelled();
}
}
void InnerWidget::updateSelected(QPoint localPos) {
auto selected = -1;
auto selectedKick = false;
if (rtl()) localPos.setX(width() - localPos.x());
if (localPos.x() >= _contentLeft && localPos.x() < _contentLeft + _contentWidth && localPos.y() >= _contentTop) {
selected = (localPos.y() - _contentTop) / _rowHeight;
if (selected >= _items.size()) {
selected = -1;
}
}
if (_selected != selected) {
updateRow(_selected);
_selected = selected;
updateRow(_selected);
if (_pressed < 0) {
setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default);
}
}
}
void InnerWidget::updateRow(int index) {
rtlupdate(_contentLeft, _contentTop + index * _rowHeight, _contentWidth, _rowHeight);
}
void InnerWidget::mousePressEvent(QMouseEvent *e) {
_pressed = _selected;
if (_pressed >= 0) {
auto item = _items[_pressed];
if (!item->ripple) {
auto mask = Ui::RippleAnimation::rectMask(QSize(_contentWidth, _rowHeight));
item->ripple = std::make_unique<Ui::RippleAnimation>(st::profileCommonGroupsRipple, std::move(mask), [this, index = _pressed] {
updateRow(index);
});
}
auto left = _contentLeft;
auto top = _contentTop + _rowHeight * _pressed;
item->ripple->add(e->pos() - QPoint(left, top));
}
}
void InnerWidget::mouseMoveEvent(QMouseEvent *e) {
updateSelected(e->pos());
}
void InnerWidget::mouseReleaseEvent(QMouseEvent *e) {
setCursor(_selected ? style::cur_pointer : style::cur_default);
updateRow(_selected);
updateRow(_pressed);
auto pressed = std::exchange(_pressed, -1);
if (pressed >= 0 && pressed < _items.size()) {
if (auto &ripple = _items[pressed]->ripple) {
ripple->lastStop();
}
if (pressed == _selected) {
App::wnd()->controller()->showPeerHistory(
_items[pressed]->peer,
Window::SectionShow::Way::Forward);
}
}
}
InnerWidget::Item *InnerWidget::computeItem(PeerData *group) {
// Skip groups that migrated to supergroups.
if (group->migrateTo()) {
return nullptr;
}
auto it = _dataMap.constFind(group);
if (it == _dataMap.cend()) {
it = _dataMap.insert(group, new Item(group));
}
return it.value();
}
InnerWidget::~InnerWidget() {
for (auto item : base::take(_dataMap)) {
delete item;
}
}
Widget::Widget(QWidget *parent, not_null<Window::Controller*> controller, not_null<UserData*> user) : Window::SectionWidget(parent, controller)
, _scroll(this, st::settingsScroll)
, _fixedBar(this)
, _fixedBarShadow(this) {
_fixedBar->move(0, 0);
_fixedBar->resizeToWidth(width());
_fixedBar->show();
_fixedBarShadow->raise();
updateAdaptiveLayout();
subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); });
_inner = _scroll->setOwnedWidget(object_ptr<InnerWidget>(this, user));
_scroll->move(0, _fixedBar->height());
_scroll->show();
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(_inner, SIGNAL(cancelled()), _fixedBar, SLOT(onBack()));
}
void Widget::updateAdaptiveLayout() {
_fixedBarShadow->moveToLeft(Adaptive::OneColumn() ? 0 : st::lineWidth, _fixedBar->height());
}
not_null<UserData*> Widget::user() const {
return _inner->user();
}
QPixmap Widget::grabForShowAnimation(const Window::SectionSlideParams &params) {
if (params.withTopBarShadow) _fixedBarShadow->hide();
auto result = myGrab(this);
if (params.withTopBarShadow) _fixedBarShadow->show();
return result;
}
void Widget::doSetInnerFocus() {
_inner->setFocus();
}
bool Widget::showInternal(
not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) {
if (auto profileMemento = dynamic_cast<SectionMemento*>(memento.get())) {
if (profileMemento->getUser() == user()) {
restoreState(profileMemento);
return true;
}
}
return false;
}
void Widget::setInternalState(const QRect &geometry, not_null<SectionMemento*> memento) {
setGeometry(geometry);
myEnsureResized(this);
restoreState(memento);
}
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
auto result = std::make_unique<SectionMemento>(user());
saveState(result.get());
return std::move(result);
}
void Widget::saveState(not_null<SectionMemento*> memento) {
memento->setScrollTop(_scroll->scrollTop());
_inner->saveState(memento);
}
void Widget::restoreState(not_null<SectionMemento*> memento) {
_inner->restoreState(memento);
auto scrollTop = memento->getScrollTop();
_scroll->scrollToY(scrollTop);
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
}
void Widget::resizeEvent(QResizeEvent *e) {
if (!width() || !height()) {
return;
}
int newScrollTop = _scroll->scrollTop() + topDelta();
_fixedBar->resizeToWidth(width());
_fixedBarShadow->resize(width(), st::lineWidth);
QSize scrollSize(width(), height() - _fixedBar->height());
if (_scroll->size() != scrollSize) {
_scroll->resize(scrollSize);
_inner->resizeToWidth(scrollSize.width(), _scroll->height());
}
if (!_scroll->isHidden()) {
if (topDelta()) {
_scroll->scrollToY(newScrollTop);
}
int scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
}
}
void Widget::onScroll() {
int scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
}
void Widget::showAnimatedHook(
const Window::SectionSlideParams &params) {
_fixedBar->setAnimatingMode(true);
}
void Widget::showFinishedHook() {
_fixedBar->setAnimatingMode(false);
}
bool Widget::wheelEventFromFloatPlayer(QEvent *e) {
return _scroll->viewportEvent(e);
}
QRect Widget::rectForFloatPlayer() const {
return mapToGlobal(_scroll->geometry());
}
} // namespace CommonGroups
} // namespace Profile

View file

@ -1,225 +0,0 @@
/*
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 "window/section_widget.h"
#include "window/section_memento.h"
namespace Notify {
struct PeerUpdate;
} // namespace Notify
namespace Ui {
class ScrollArea;
class PlainShadow;
} // namespace Ui
namespace Profile {
class BackButton;
namespace CommonGroups {
class SectionMemento : public Window::SectionMemento {
public:
SectionMemento(not_null<UserData*> user) : _user(user) {
}
object_ptr<Window::SectionWidget> createWidget(
QWidget *parent,
not_null<Window::Controller*> controller,
Window::Column column,
const QRect &geometry) override;
not_null<UserData*> getUser() const {
return _user;
}
void setScrollTop(int scrollTop) {
_scrollTop = scrollTop;
}
int getScrollTop() const {
return _scrollTop;
}
void setCommonGroups(const QList<not_null<PeerData*>> &groups) {
_commonGroups = groups;
}
const QList<not_null<PeerData*>> &getCommonGroups() const {
return _commonGroups;
}
private:
not_null<UserData*> _user;
int _scrollTop = 0;
QList<not_null<PeerData*>> _commonGroups;
};
class FixedBar final : public TWidget, private base::Subscriber {
Q_OBJECT
public:
FixedBar(QWidget *parent);
// When animating mode is enabled the content is hidden and the
// whole fixed bar acts like a back button.
void setAnimatingMode(bool enabled);
protected:
void mousePressEvent(QMouseEvent *e) override;
int resizeGetHeight(int newWidth) override;
public slots:
void onBack();
private:
object_ptr<BackButton> _backButton;
bool _animatingMode = false;
};
class InnerWidget final : public TWidget {
Q_OBJECT
public:
InnerWidget(QWidget *parent, not_null<UserData*> user);
not_null<UserData*> user() const {
return _user;
}
void resizeToWidth(int newWidth, int minHeight) {
_minHeight = minHeight;
return TWidget::resizeToWidth(newWidth);
}
void saveState(not_null<SectionMemento*> memento);
void restoreState(not_null<SectionMemento*> memento);
~InnerWidget();
signals:
void cancelled();
protected:
int resizeGetHeight(int newWidth) override;
void visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) override;
void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
private:
void updateSelected(QPoint localPos);
void updateRow(int index);
void showInitial(const QList<not_null<PeerData*>> &list);
void checkPreloadMore();
void preloadMore();
void updateSize();
void paintRow(Painter &p, int index, TimeMs ms);
not_null<UserData*> _user;
int _minHeight = 0;
int _rowHeight = 0;
int _contentLeft = 0;
int _contentTop = 0;
int _contentWidth = 0;
int _visibleTop = 0;
int _visibleBottom = 0;
struct Item {
explicit Item(PeerData *peer);
~Item();
PeerData * const peer;
Text name;
std::unique_ptr<Ui::RippleAnimation> ripple;
};
Item *computeItem(PeerData *group);
QMap<PeerData*, Item*> _dataMap;
QList<Item*> _items;
int _selected = -1;
int _pressed = -1;
int32 _preloadGroupId = 0;
mtpRequestId _preloadRequestId = 0;
bool _allLoaded = true;
};
class Widget final : public Window::SectionWidget {
Q_OBJECT
public:
Widget(QWidget *parent, not_null<Window::Controller*> controller, not_null<UserData*> user);
not_null<UserData*> user() const;
PeerData *activePeer() const override {
return user();
}
bool hasTopBarShadow() const override {
return true;
}
QPixmap grabForShowAnimation(const Window::SectionSlideParams &params) override;
bool showInternal(
not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) override;
std::unique_ptr<Window::SectionMemento> createMemento() override;
void setInternalState(const QRect &geometry, not_null<SectionMemento*> memento);
// Float player interface.
bool wheelEventFromFloatPlayer(QEvent *e) override;
QRect rectForFloatPlayer() const override;
protected:
void resizeEvent(QResizeEvent *e) override;
void showAnimatedHook(
const Window::SectionSlideParams &params) override;
void showFinishedHook() override;
void doSetInnerFocus() override;
private slots:
void onScroll();
private:
void updateAdaptiveLayout();
void saveState(not_null<SectionMemento*> memento);
void restoreState(not_null<SectionMemento*> memento);
object_ptr<Ui::ScrollArea> _scroll;
QPointer<InnerWidget> _inner;
object_ptr<FixedBar> _fixedBar;
object_ptr<Ui::PlainShadow> _fixedBarShadow;
};
} // namespace CommonGroups
} // namespace Profile

View file

@ -1,570 +0,0 @@
/*
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 "profile/profile_cover.h"
#include "data/data_photo.h"
#include "styles/style_profile.h"
#include "styles/style_window.h"
#include "profile/profile_cover_drop_area.h"
#include "profile/profile_userpic_button.h"
#include "ui/widgets/buttons.h"
#include "core/file_utilities.h"
#include "ui/widgets/labels.h"
#include "observer_peer.h"
#include "boxes/confirm_box.h"
#include "boxes/photo_crop_box.h"
#include "boxes/peer_list_controllers.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "auth_session.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "messenger.h"
#include "platform/platform_file_utilities.h"
#include "window/main_window.h"
#include "window/window_controller.h"
namespace Profile {
namespace {
using UpdateFlag = Notify::PeerUpdate::Flag;
const auto ButtonsUpdateFlags = UpdateFlag::UserCanShareContact
| UpdateFlag::BotCanAddToGroups
| UpdateFlag::ChatCanEdit
| UpdateFlag::ChannelRightsChanged
| UpdateFlag::ChannelAmIn;
} // namespace
CoverWidget::CoverWidget(QWidget *parent, PeerData *peer) : TWidget(parent)
, _peer(peer)
, _peerUser(peer->asUser())
, _peerChat(peer->asChat())
, _peerChannel(peer->asChannel())
, _peerMegagroup(peer->isMegagroup() ? _peerChannel : nullptr)
, _userpicButton(this, peer)
, _name(this, st::profileNameLabel) {
_peer->updateFull();
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
setAttribute(Qt::WA_OpaquePaintEvent);
setAcceptDrops(true);
_name->setSelectable(true);
_name->setContextCopyText(lang(lng_profile_copy_fullname));
auto observeEvents = ButtonsUpdateFlags
| UpdateFlag::NameChanged
| UpdateFlag::UserOnlineChanged
| UpdateFlag::MembersChanged
| UpdateFlag::PhotoChanged;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
notifyPeerUpdated(update);
}));
connect(&Messenger::Instance(), SIGNAL(peerPhotoDone(PeerId)), this, SLOT(onPhotoUploadStatusChanged(PeerId)));
connect(&Messenger::Instance(), SIGNAL(peerPhotoFail(PeerId)), this, SLOT(onPhotoUploadStatusChanged(PeerId)));
connect(_userpicButton, SIGNAL(clicked()), this, SLOT(onPhotoShow()));
validatePhoto();
refreshNameText();
refreshStatusText();
refreshButtons();
}
void CoverWidget::refreshLang() {
InvokeQueued(this, [this] { moveAndToggleButtons(width()); });
}
PhotoData *CoverWidget::validatePhoto() const {
auto photo = (_peer->photoId && _peer->photoId != UnknownPeerPhotoId) ? App::photo(_peer->photoId) : nullptr;
_userpicButton->setPointerCursor(photo != nullptr && photo->date != 0);
if ((_peer->photoId == UnknownPeerPhotoId) || (_peer->photoId && (!photo || !photo->date))) {
Auth().api().requestFullPeer(_peer);
return nullptr;
}
return photo;
}
void CoverWidget::onPhotoShow() {
if (auto photo = validatePhoto()) {
Messenger::Instance().showPhoto(photo, _peer);
}
}
void CoverWidget::onCancelPhotoUpload() {
Messenger::Instance().cancelPhotoUpdate(_peer->id);
refreshStatusText();
}
int CoverWidget::countPhotoLeft(int newWidth) const {
int result = st::profilePhotoLeftMin;
result += (newWidth - st::columnMinimalWidthMain) / 2;
return qMin(result, st::profilePhotoLeftMax);
}
int CoverWidget::resizeGetHeight(int newWidth) {
int newHeight = 0;
newHeight += st::profileMarginTop;
_photoLeft = countPhotoLeft(newWidth);
_userpicButton->moveToLeft(_photoLeft, newHeight);
refreshNameGeometry(newWidth);
int infoLeft = _userpicButton->x() + _userpicButton->width();
_statusPosition = QPoint(infoLeft + st::profileStatusLeft, _userpicButton->y() + st::profileStatusTop);
if (_cancelPhotoUpload) {
_cancelPhotoUpload->moveToLeft(_statusPosition.x() + st::profileStatusFont->width(_statusText) + st::profileStatusFont->spacew, _statusPosition.y());
}
moveAndToggleButtons(newWidth);
newHeight += st::profilePhotoSize;
newHeight += st::profileMarginBottom;
_dividerTop = newHeight;
newHeight += st::profileDividerLeft.height();
newHeight += st::profileBlocksTop;
resizeDropArea(newWidth);
return newHeight;
}
void CoverWidget::refreshNameGeometry(int newWidth) {
int infoLeft = _userpicButton->x() + _userpicButton->width();
int nameLeft = infoLeft + st::profileNameLeft - st::profileNameLabel.margin.left();
int nameTop = _userpicButton->y() + st::profileNameTop - st::profileNameLabel.margin.top();
int nameWidth = newWidth - infoLeft - st::profileNameLeft;
if (_peer->isVerified()) {
nameWidth -= st::profileVerifiedCheckShift + st::profileVerifiedCheck.width();
}
int marginsAdd = st::profileNameLabel.margin.left() + st::profileNameLabel.margin.right();
_name->resizeToWidth(qMin(nameWidth - marginsAdd, _name->naturalWidth()) + marginsAdd);
_name->moveToLeft(nameLeft, nameTop);
}
// A more generic solution would be allowing an optional icon button
// for each text button. But currently we use only one, so it is done easily:
// There can be primary + secondary + icon buttons. If primary + secondary fit,
// then icon is hidden, otherwise secondary is hidden and icon is shown.
void CoverWidget::moveAndToggleButtons(int newWidth) {
int buttonLeft = _userpicButton->x() + _userpicButton->width() + st::profileButtonLeft;
int buttonsRight = newWidth - st::profileButtonSkip;
for (int i = 0, count = _buttons.size(); i < count; ++i) {
auto &button = _buttons.at(i);
button.widget->moveToLeft(buttonLeft, st::profileButtonTop);
if (button.replacement) {
button.replacement->moveToLeft(buttonLeft, st::profileButtonTop);
if (buttonLeft + button.widget->width() > buttonsRight) {
button.widget->hide();
button.replacement->show();
buttonLeft += button.replacement->width() + st::profileButtonSkip;
} else {
button.widget->show();
button.replacement->hide();
buttonLeft += button.widget->width() + st::profileButtonSkip;
}
} else if (i == 1 && (buttonLeft + button.widget->width() > buttonsRight)) {
// If second button is not fitting.
button.widget->hide();
} else {
button.widget->show();
buttonLeft += button.widget->width() + st::profileButtonSkip;
}
}
}
void CoverWidget::showFinished() {
_userpicButton->showFinished();
}
bool CoverWidget::shareContactButtonShown() const {
return _peerUser && (_buttons.size() > 1) && !(_buttons.at(1).widget->isHidden());
}
void CoverWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), st::profileBg);
p.setFont(st::profileStatusFont);
p.setPen(_statusTextIsOnline ? st::profileStatusFgActive : st::profileStatusFg);
p.drawTextLeft(_statusPosition.x(), _statusPosition.y(), width(), _statusText);
if (_peer->isVerified()) {
st::profileVerifiedCheck.paint(p, _name->x() + _name->width() + st::profileVerifiedCheckShift, _name->y(), width());
}
paintDivider(p);
}
void CoverWidget::resizeDropArea(int newWidth) {
if (_dropArea) {
_dropArea->setGeometry(0, 0, newWidth, _dividerTop);
}
}
void CoverWidget::dropAreaHidden(CoverDropArea *dropArea) {
if (_dropArea == dropArea) {
_dropArea.destroyDelayed();
}
}
bool CoverWidget::canEditPhoto() const {
if (_peerChat && _peerChat->canEdit()) {
return true;
} else if (_peerMegagroup && _peerMegagroup->canEditInformation()) {
return true;
} else if (_peerChannel && _peerChannel->canEditInformation()) {
return true;
}
return false;
}
bool CoverWidget::mimeDataHasImage(const QMimeData *mimeData) const {
if (!mimeData) return false;
if (mimeData->hasImage()) return true;
auto uriListFormat = qsl("text/uri-list");
if (!mimeData->hasFormat(uriListFormat)) return false;
const auto &urls = mimeData->urls();
if (urls.size() != 1) return false;
auto &url = urls.at(0);
if (!url.isLocalFile()) return false;
auto file = Platform::File::UrlToLocal(url);
QFileInfo info(file);
if (info.isDir()) return false;
if (info.size() > App::kImageSizeLimit) return false;
for (auto &ext : cImgExtensions()) {
if (file.endsWith(ext, Qt::CaseInsensitive)) {
return true;
}
}
return false;
}
void CoverWidget::dragEnterEvent(QDragEnterEvent *e) {
if (!canEditPhoto() || !mimeDataHasImage(e->mimeData())) {
e->ignore();
return;
}
if (!_dropArea) {
auto title = lang(lng_profile_drop_area_title);
QString subtitle;
if (_peerChat || _peerMegagroup) {
subtitle = lang(lng_profile_drop_area_subtitle);
} else {
subtitle = lang(lng_profile_drop_area_subtitle_channel);
}
_dropArea.create(this, title, subtitle);
resizeDropArea(width());
}
_dropArea->showAnimated();
e->setDropAction(Qt::CopyAction);
e->accept();
}
void CoverWidget::dragLeaveEvent(QDragLeaveEvent *e) {
if (_dropArea && !_dropArea->hiding()) {
_dropArea->hideAnimated([this](CoverDropArea *area) { dropAreaHidden(area); });
}
}
void CoverWidget::dropEvent(QDropEvent *e) {
auto mimeData = e->mimeData();
QImage img;
if (mimeData->hasImage()) {
img = qvariant_cast<QImage>(mimeData->imageData());
} else {
const auto &urls = mimeData->urls();
if (urls.size() == 1) {
auto &url = urls.at(0);
if (url.isLocalFile()) {
img = App::readImage(Platform::File::UrlToLocal(url));
}
}
}
if (!_dropArea->hiding()) {
_dropArea->hideAnimated([this](CoverDropArea *area) { dropAreaHidden(area); });
}
e->acceptProposedAction();
showSetPhotoBox(img);
}
void CoverWidget::paintDivider(Painter &p) {
auto dividerHeight = st::profileDividerLeft.height();
auto dividerLeft = Adaptive::OneColumn() ? 0 : st::lineWidth;
auto divider = rtlrect(dividerLeft, _dividerTop, width() - dividerLeft, dividerHeight, width());
p.fillRect(divider, st::profileDividerBg);
if (!Adaptive::OneColumn()) {
st::profileDividerLeft.paint(p, QPoint(dividerLeft, _dividerTop), width());
}
auto dividerFillLeft = Adaptive::OneColumn() ? 0 : (st::lineWidth + st::profileDividerLeft.width());
auto dividerFillTop = rtlrect(dividerFillLeft, _dividerTop, width() - dividerFillLeft, st::profileDividerTop.height(), width());
st::profileDividerTop.fill(p, dividerFillTop);
auto dividerFillBottom = rtlrect(dividerFillLeft, _dividerTop + dividerHeight - st::profileDividerBottom.height(), width() - dividerFillLeft, st::profileDividerBottom.height(), width());
st::profileDividerBottom.fill(p, dividerFillBottom);
}
void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.peer != _peer) {
return;
}
if ((update.flags & ButtonsUpdateFlags) != 0) {
refreshButtons();
}
if (update.flags & UpdateFlag::NameChanged) {
refreshNameText();
}
if (update.flags & UpdateFlag::PhotoChanged) {
validatePhoto();
}
if (update.flags & (UpdateFlag::UserOnlineChanged | UpdateFlag::MembersChanged)) {
refreshStatusText();
}
}
void CoverWidget::refreshNameText() {
_name->setText(App::peerName(_peer));
refreshNameGeometry(width());
}
void CoverWidget::refreshStatusText() {
if (Messenger::Instance().isPhotoUpdating(_peer->id)) {
_statusText = lang(lng_settings_uploading_photo);
_statusTextIsOnline = false;
if (!_cancelPhotoUpload) {
_cancelPhotoUpload.create(this, lang(lng_cancel), st::defaultLinkButton);
connect(_cancelPhotoUpload, SIGNAL(clicked()), this, SLOT(onCancelPhotoUpload()));
_cancelPhotoUpload->show();
_cancelPhotoUpload->moveToLeft(_statusPosition.x() + st::profileStatusFont->width(_statusText) + st::profileStatusFont->spacew, _statusPosition.y());
}
update();
return;
}
_cancelPhotoUpload.destroy();
auto currentTime = unixtime();
if (_peerUser) {
_statusText = App::onlineText(_peerUser, currentTime, true);
_statusTextIsOnline = App::onlineColorUse(_peerUser, currentTime);
} else if (_peerChat && _peerChat->amIn()) {
auto fullCount = qMax(_peerChat->count, _peerChat->participants.size());
if (_onlineCount > 0 && _onlineCount <= fullCount) {
auto membersCount = lng_chat_status_members(lt_count, fullCount);
auto onlineCount = lng_chat_status_online(lt_count, _onlineCount);
_statusText = lng_chat_status_members_online(lt_members_count, membersCount, lt_online_count, onlineCount);
} else if (_peerChat->count > 0) {
_statusText = lng_chat_status_members(lt_count, _peerChat->count);
} else {
_statusText = lang(lng_group_status);
}
} else if (_peerChannel) {
auto fullCount = _peerChannel->membersCount();
if (_onlineCount > 0 && _onlineCount <= fullCount) {
auto membersCount = lng_chat_status_members(lt_count, fullCount);
auto onlineCount = lng_chat_status_online(lt_count, _onlineCount);
_statusText = lng_chat_status_members_online(lt_members_count, membersCount, lt_online_count, onlineCount);
} else if (fullCount > 0) {
_statusText = lng_chat_status_members(lt_count, fullCount);
} else {
_statusText = lang(_peerChannel->isMegagroup() ? lng_group_status : lng_channel_status);
}
} else {
_statusText = lang(lng_chat_status_unaccessible);
}
update();
}
void CoverWidget::refreshButtons() {
clearButtons();
if (_peerUser) {
setUserButtons();
} else if (_peerChat) {
setChatButtons();
} else if (_peerMegagroup) {
setMegagroupButtons();
} else if (_peerChannel) {
setChannelButtons();
}
resizeToWidth(width());
}
void CoverWidget::setUserButtons() {
addButton(langFactory(lng_profile_send_message), SLOT(onSendMessage()));
if (_peerUser->botInfo && !_peerUser->botInfo->cantJoinGroups) {
addButton(langFactory(lng_profile_invite_to_group), SLOT(onAddBotToGroup()), &st::profileAddMemberButton);
} else if (_peerUser->canShareThisContact()) {
addButton(langFactory(lng_profile_share_contact), SLOT(onShareContact()));
}
}
void CoverWidget::setChatButtons() {
if (_peerChat->canEdit()) {
addButton(langFactory(lng_profile_set_group_photo), SLOT(onSetPhoto()));
addButton(langFactory(lng_profile_add_participant), SLOT(onAddMember()), &st::profileAddMemberButton);
}
}
void CoverWidget::setMegagroupButtons() {
if (_peerMegagroup->amIn()) {
if (canEditPhoto()) {
addButton(langFactory(lng_profile_set_group_photo), SLOT(onSetPhoto()));
}
} else {
addButton(langFactory(lng_profile_join_channel), SLOT(onJoin()));
}
if (_peerMegagroup->canAddMembers()) {
addButton(langFactory(lng_profile_add_participant), SLOT(onAddMember()), &st::profileAddMemberButton);
}
}
void CoverWidget::setChannelButtons() {
if (canEditPhoto()) {
addButton(langFactory(lng_profile_set_group_photo), SLOT(onSetPhoto()));
} else if (_peerChannel->amIn()) {
addButton(langFactory(lng_profile_view_channel), SLOT(onViewChannel()));
} else {
addButton(langFactory(lng_profile_join_channel), SLOT(onJoin()));
}
}
void CoverWidget::clearButtons() {
auto buttons = base::take(_buttons);
for_const (auto button, buttons) {
delete button.widget;
delete button.replacement;
}
}
void CoverWidget::addButton(base::lambda<QString()> textFactory, const char *slot, const style::RoundButton *replacementStyle) {
auto &buttonStyle = _buttons.isEmpty() ? st::profilePrimaryButton : st::profileSecondaryButton;
auto button = new Ui::RoundButton(this, std::move(textFactory), buttonStyle);
connect(button, SIGNAL(clicked()), this, slot);
button->show();
auto replacement = replacementStyle ? new Ui::RoundButton(this, base::lambda<QString()>(), *replacementStyle) : nullptr;
if (replacement) {
connect(replacement, SIGNAL(clicked()), this, slot);
replacement->hide();
}
_buttons.push_back({ button, replacement });
}
void CoverWidget::onOnlineCountUpdated(int onlineCount) {
_onlineCount = onlineCount;
refreshStatusText();
}
void CoverWidget::onSendMessage() {
App::wnd()->controller()->showPeerHistory(
_peer,
Window::SectionShow::Way::Forward);
}
void CoverWidget::onShareContact() {
App::main()->shareContactLayer(_peerUser);
}
void CoverWidget::onSetPhoto() {
App::CallDelayed(st::profilePrimaryButton.ripple.hideDuration, this, [this] {
auto imgExtensions = cImgExtensions();
auto filter = qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + FileDialog::AllFilesFilter();
FileDialog::GetOpenPath(lang(lng_choose_image), filter, base::lambda_guarded(this, [this](const FileDialog::OpenResult &result) {
if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
return;
}
QImage img;
if (!result.remoteContent.isEmpty()) {
img = App::readImage(result.remoteContent);
} else {
img = App::readImage(result.paths.front());
}
showSetPhotoBox(img);
}));
});
}
void CoverWidget::showSetPhotoBox(const QImage &img) {
if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
Ui::show(Box<InformBox>(lang(lng_bad_photo)));
return;
}
auto box = Ui::show(Box<PhotoCropBox>(img, _peer));
subscribe(box->boxClosing, [this] { onPhotoUploadStatusChanged(); });
}
void CoverWidget::onPhotoUploadStatusChanged(PeerId peerId) {
if (!peerId || peerId == _peer->id) {
refreshStatusText();
}
}
void CoverWidget::onAddMember() {
if (_peerChat) {
if (_peerChat->count >= Global::ChatSizeMax() && _peerChat->amCreator()) {
Ui::show(Box<ConvertToSupergroupBox>(_peerChat));
} else {
AddParticipantsBoxController::Start(_peerChat);
}
} else if (_peerChannel && _peerChannel->mgInfo) {
auto &participants = _peerChannel->mgInfo->lastParticipants;
AddParticipantsBoxController::Start(_peerChannel, { participants.cbegin(), participants.cend() });
}
}
void CoverWidget::onAddBotToGroup() {
if (_peerUser && _peerUser->botInfo) {
AddBotToGroupBoxController::Start(_peerUser);
}
}
void CoverWidget::onJoin() {
if (!_peerChannel) return;
Auth().api().joinChannel(_peerChannel);
}
void CoverWidget::onViewChannel() {
Ui::showPeerHistory(_peer, ShowAtUnreadMsgId);
}
} // namespace Profile

View file

@ -1,143 +0,0 @@
/*
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 "base/observer.h"
namespace style {
struct RoundButton;
} // namespace style
namespace Ui {
class FlatLabel;
class RoundButton;
class LinkButton;
} // namespace Ui
namespace Notify {
struct PeerUpdate;
} // namespace Notify
namespace Profile {
class BackButton;
class UserpicButton;
class CoverDropArea;
class CoverWidget final : public TWidget, private base::Subscriber {
Q_OBJECT
public:
CoverWidget(QWidget *parent, PeerData *peer);
void showFinished();
// Profile fixed top bar should use this flag to decide
// if it shows "Share contact" button or not.
// It should show it only if it is hidden in the cover.
bool shareContactButtonShown() const;
public slots:
void onOnlineCountUpdated(int onlineCount);
private slots:
void onPhotoShow();
void onPhotoUploadStatusChanged(PeerId peerId = 0);
void onCancelPhotoUpload();
void onSendMessage();
void onShareContact();
void onSetPhoto();
void onAddMember();
void onAddBotToGroup();
void onJoin();
void onViewChannel();
protected:
void paintEvent(QPaintEvent *e) override;
void dragEnterEvent(QDragEnterEvent *e) override;
void dragLeaveEvent(QDragLeaveEvent *e) override;
void dropEvent(QDropEvent *e) override;
int resizeGetHeight(int newWidth) override;
private:
void refreshLang();
// Observed notifications.
void notifyPeerUpdated(const Notify::PeerUpdate &update);
// Counts userpic button left offset for a new widget width.
int countPhotoLeft(int newWidth) const;
PhotoData *validatePhoto() const;
void refreshNameGeometry(int newWidth);
void moveAndToggleButtons(int newWidth);
void refreshNameText();
void refreshStatusText();
void refreshButtons();
void setUserButtons();
void setChatButtons();
void setMegagroupButtons();
void setChannelButtons();
void clearButtons();
void addButton(base::lambda<QString()> textFactory, const char *slot, const style::RoundButton *replacementStyle = nullptr);
void paintDivider(Painter &p);
bool canEditPhoto() const;
void showSetPhotoBox(const QImage &img);
void resizeDropArea(int newWidth);
void dropAreaHidden(CoverDropArea *dropArea);
bool mimeDataHasImage(const QMimeData *mimeData) const;
PeerData *_peer;
UserData *_peerUser;
ChatData *_peerChat;
ChannelData *_peerChannel;
ChannelData *_peerMegagroup;
object_ptr<UserpicButton> _userpicButton;
object_ptr<CoverDropArea> _dropArea = { nullptr };
object_ptr<Ui::FlatLabel> _name;
object_ptr<Ui::LinkButton> _cancelPhotoUpload = { nullptr };
QPoint _statusPosition;
QString _statusText;
bool _statusTextIsOnline = false;
struct Button {
Ui::RoundButton *widget;
Ui::RoundButton *replacement;
};
QList<Button> _buttons;
int _photoLeft = 0; // Caching countPhotoLeft() result.
int _dividerTop = 0;
int _onlineCount = 0;
};
} // namespace Profile

View file

@ -1,262 +0,0 @@
/*
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 "profile/profile_fixed_bar.h"
#include "styles/style_profile.h"
#include "styles/style_window.h"
#include "ui/widgets/buttons.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "boxes/add_contact_box.h"
#include "boxes/confirm_box.h"
#include "observer_peer.h"
#include "styles/style_boxes.h"
#include "profile/profile_back_button.h"
#include "window/window_controller.h"
namespace Profile {
namespace {
using UpdateFlag = Notify::PeerUpdate::Flag;
const auto ButtonsUpdateFlags = UpdateFlag::UserCanShareContact
| UpdateFlag::UserIsContact
| UpdateFlag::ChatCanEdit
| UpdateFlag::ChannelRightsChanged;
} // namespace
FixedBar::FixedBar(QWidget *parent, PeerData *peer) : TWidget(parent)
, _peer(peer)
, _peerUser(peer->asUser())
, _peerChat(peer->asChat())
, _peerChannel(peer->asChannel())
, _peerMegagroup(peer->isMegagroup() ? _peerChannel : nullptr)
, _backButton(this, lang(lng_menu_back)) {
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
_backButton->moveToLeft(0, 0);
connect(_backButton, SIGNAL(clicked()), this, SLOT(onBack()));
auto observeEvents = ButtonsUpdateFlags
| UpdateFlag::MigrationChanged;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
notifyPeerUpdate(update);
}));
refreshRightActions();
}
void FixedBar::notifyPeerUpdate(const Notify::PeerUpdate &update) {
if (update.peer != _peer) {
return;
}
if ((update.flags & ButtonsUpdateFlags) != 0) {
refreshRightActions();
}
if (update.flags & UpdateFlag::MigrationChanged) {
if (_peerChat && _peerChat->migrateTo()) {
auto channel = _peerChat->migrateTo();
onBack();
Ui::showPeerProfile(channel);
}
}
}
void FixedBar::refreshRightActions() {
_currentAction = 0;
if (_peerUser) {
setUserActions();
} else if (_peerChat) {
setChatActions();
} else if (_peerMegagroup) {
setMegagroupActions();
} else if (_peerChannel) {
setChannelActions();
}
while (_rightActions.size() > _currentAction) {
delete _rightActions.back().button;
_rightActions.pop_back();
}
resizeToWidth(width());
}
void FixedBar::setUserActions() {
if (_peerUser->canShareThisContact()) {
addRightAction(RightActionType::ShareContact, langFactory(lng_profile_top_bar_share_contact), SLOT(onShareContact()));
}
if (_peerUser->isContact()) {
addRightAction(RightActionType::EditContact, langFactory(lng_profile_edit_contact), SLOT(onEditContact()));
addRightAction(RightActionType::DeleteContact, langFactory(lng_profile_delete_contact), SLOT(onDeleteContact()));
} else if (_peerUser->canAddContact()) {
addRightAction(RightActionType::AddContact, langFactory(lng_profile_add_contact), SLOT(onAddContact()));
}
}
void FixedBar::setChatActions() {
if (_peerChat->canEdit()) {
addRightAction(RightActionType::EditGroup, langFactory(lng_profile_edit_contact), SLOT(onEditGroup()));
}
addRightAction(RightActionType::LeaveGroup, langFactory(lng_profile_delete_and_exit), SLOT(onLeaveGroup()));
}
void FixedBar::setMegagroupActions() {
if (_peerMegagroup->canEditInformation()) {
addRightAction(RightActionType::EditChannel, langFactory(lng_profile_edit_contact), SLOT(onEditChannel()));
}
}
void FixedBar::setChannelActions() {
if (_peerChannel->canEditInformation()) {
addRightAction(RightActionType::EditChannel, langFactory(lng_profile_edit_contact), SLOT(onEditChannel()));
}
}
void FixedBar::addRightAction(RightActionType type, base::lambda<QString()> textFactory, const char *slot) {
if (_rightActions.size() > _currentAction) {
if (_rightActions.at(_currentAction).type == type) {
++_currentAction;
return;
}
} else {
Assert(_rightActions.size() == _currentAction);
_rightActions.push_back(RightAction());
}
_rightActions[_currentAction].type = type;
delete _rightActions[_currentAction].button;
_rightActions[_currentAction].button = new Ui::RoundButton(this, std::move(textFactory), st::profileFixedBarButton);
connect(_rightActions[_currentAction].button, SIGNAL(clicked()), this, slot);
bool showButton = !_animatingMode && (type != RightActionType::ShareContact || !_hideShareContactButton);
_rightActions[_currentAction].button->setVisible(showButton);
++_currentAction;
}
void FixedBar::onBack() {
App::main()->showBackFromStack(Window::SectionShow());
}
void FixedBar::onEditChannel() {
Ui::show(Box<EditChannelBox>(_peerMegagroup ? _peerMegagroup : _peerChannel));
}
void FixedBar::onEditGroup() {
Ui::show(Box<EditNameTitleBox>(_peerChat));
}
void FixedBar::onAddContact() {
auto firstName = _peerUser->firstName;
auto lastName = _peerUser->lastName;
auto phone = _peerUser->phone().isEmpty() ? App::phoneFromSharedContact(peerToUser(_peer->id)) : _peerUser->phone();
Ui::show(Box<AddContactBox>(firstName, lastName, phone));
}
void FixedBar::onEditContact() {
Ui::show(Box<AddContactBox>(_peerUser));
}
void FixedBar::onShareContact() {
App::main()->shareContactLayer(_peerUser);
}
void FixedBar::onDeleteContact() {
auto text = lng_sure_delete_contact(lt_contact, App::peerName(_peerUser));
Ui::show(Box<ConfirmBox>(text, lang(lng_box_delete), base::lambda_guarded(this, [this] {
Ui::showChatsList();
Ui::hideLayer();
MTP::send(MTPcontacts_DeleteContact(_peerUser->inputUser), App::main()->rpcDone(&MainWidget::deletedContact, _peerUser));
})));
}
void FixedBar::onLeaveGroup() {
auto text = lng_sure_delete_and_exit(lt_group, App::peerName(_peerChat));
Ui::show(Box<ConfirmBox>(text, lang(lng_box_leave), st::attentionBoxButton, base::lambda_guarded(this, [this] {
Ui::showChatsList();
Ui::hideLayer();
App::main()->deleteAndExit(_peerChat);
})));
}
int FixedBar::resizeGetHeight(int newWidth) {
int newHeight = 0;
updateButtonsGeometry(newWidth);
_backButton->resizeToWidth(newWidth);
_backButton->moveToLeft(0, 0);
newHeight += _backButton->height();
return newHeight;
}
void FixedBar::updateButtonsGeometry(int newWidth) {
int buttonLeft = newWidth;
for (auto i = _rightActions.cend(), b = _rightActions.cbegin(); i != b;) {
--i;
buttonLeft -= i->button->width();
i->button->moveToLeft(buttonLeft, 0);
}
}
void FixedBar::refreshLang() {
InvokeQueued(this, [this] { updateButtonsGeometry(width()); });
}
void FixedBar::setAnimatingMode(bool enabled) {
if (_animatingMode != enabled) {
_animatingMode = enabled;
setCursor(_animatingMode ? style::cur_pointer : style::cur_default);
if (_animatingMode) {
setAttribute(Qt::WA_OpaquePaintEvent, false);
hideChildren();
} else {
setAttribute(Qt::WA_OpaquePaintEvent);
showChildren();
if (_hideShareContactButton) {
applyHideShareContactButton();
}
}
show();
}
}
void FixedBar::setHideShareContactButton(bool hideButton) {
_hideShareContactButton = hideButton;
if (!_animatingMode) {
applyHideShareContactButton();
}
}
void FixedBar::applyHideShareContactButton() {
for_const (auto &action, _rightActions) {
if (action.type == RightActionType::ShareContact) {
action.button->setVisible(!_hideShareContactButton);
}
}
}
void FixedBar::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
onBack();
} else {
TWidget::mousePressEvent(e);
}
}
} // namespace Profile

View file

@ -1,111 +0,0 @@
/*
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 "base/observer.h"
namespace Notify {
struct PeerUpdate;
} // namespace Notify
namespace Ui {
class RoundButton;
} // namespace Ui
namespace Profile {
class BackButton;
class FixedBar final : public TWidget, private base::Subscriber {
Q_OBJECT
public:
FixedBar(QWidget *parent, PeerData *peer);
// When animating mode is enabled the content is hidden and the
// whole fixed bar acts like a back button.
void setAnimatingMode(bool enabled);
// The "Share contact" button should be hidden if it is shown in the profile cover.
void setHideShareContactButton(bool hideButton);
protected:
void mousePressEvent(QMouseEvent *e) override;
int resizeGetHeight(int newWidth) override;
public slots:
void onBack();
private slots:
void onEditChannel();
void onEditGroup();
void onAddContact();
void onEditContact();
void onShareContact();
void onDeleteContact();
void onLeaveGroup();
private:
void refreshLang();
void updateButtonsGeometry(int newWidth);
void notifyPeerUpdate(const Notify::PeerUpdate &update);
void refreshRightActions();
void setUserActions();
void setChatActions();
void setMegagroupActions();
void setChannelActions();
enum class RightActionType {
None,
EditChannel,
EditGroup,
LeaveGroup,
AddContact,
EditContact,
DeleteContact,
ShareContact,
};
void addRightAction(RightActionType type, base::lambda<QString()> textFactory, const char *slot);
void applyHideShareContactButton();
PeerData *_peer;
UserData *_peerUser;
ChatData *_peerChat;
ChannelData *_peerChannel;
ChannelData *_peerMegagroup;
object_ptr<BackButton> _backButton;
int _currentAction = 0;
struct RightAction {
RightActionType type = RightActionType::None;
Ui::RoundButton *button = nullptr;
};
QList<RightAction> _rightActions;
bool _animatingMode = false;
bool _hideShareContactButton = false;
};
} // namespace Profile

View file

@ -1,272 +0,0 @@
/*
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 "profile/profile_inner_widget.h"
#include "styles/style_profile.h"
#include "styles/style_window.h"
#include "profile/profile_cover.h"
#include "profile/profile_block_info.h"
#include "profile/profile_block_settings.h"
#include "profile/profile_block_invite_link.h"
#include "profile/profile_block_shared_media.h"
#include "profile/profile_block_actions.h"
#include "profile/profile_block_channel_members.h"
#include "profile/profile_block_group_members.h"
#include "apiwrap.h"
namespace Profile {
InnerWidget::InnerWidget(QWidget *parent, PeerData *peer) : TWidget(parent)
, _peer(peer)
, _cover(this, peer) {
setAttribute(Qt::WA_OpaquePaintEvent);
createBlocks();
}
void InnerWidget::createBlocks() {
auto user = _peer->asUser();
auto chat = _peer->asChat();
auto channel = _peer->asChannel();
auto megagroup = _peer->isMegagroup() ? channel : nullptr;
if (user || channel || megagroup) {
_blocks.push_back({ new InfoWidget(this, _peer), RectPart::Right });
}
_blocks.push_back({ new SettingsWidget(this, _peer), RectPart::Right });
if (chat || channel || megagroup) {
_blocks.push_back({ new InviteLinkWidget(this, _peer), RectPart::Right });
}
_blocks.push_back({ new SharedMediaWidget(this, _peer), RectPart::Right });
if (channel && !megagroup) {
_blocks.push_back({ new ChannelMembersWidget(this, _peer), RectPart::Right });
}
_blocks.push_back({ new ActionsWidget(this, _peer), RectPart::Right });
if (chat || megagroup) {
auto membersWidget = new GroupMembersWidget(this, _peer);
connect(membersWidget, SIGNAL(onlineCountUpdated(int)), _cover, SLOT(onOnlineCountUpdated(int)));
_cover->onOnlineCountUpdated(membersWidget->onlineCount());
_blocks.push_back({ membersWidget, RectPart::Left });
}
for_const (auto &blockData, _blocks) {
connect(blockData.block, SIGNAL(heightUpdated()), this, SLOT(onBlockHeightUpdated()));
}
}
void InnerWidget::visibleTopBottomUpdated(int visibleTop, int visibleBottom) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
int notDisplayedAtBottom = height() - _visibleBottom;
if (notDisplayedAtBottom > 0) {
decreaseAdditionalHeight(notDisplayedAtBottom);
}
for_const (auto &blockData, _blocks) {
int blockY = blockData.block->y();
blockData.block->setVisibleTopBottom(visibleTop - blockY, visibleBottom - blockY);
}
}
bool InnerWidget::shareContactButtonShown() const {
return _cover->shareContactButtonShown();
}
void InnerWidget::saveState(not_null<SectionMemento*> memento) {
for_const (auto &blockData, _blocks) {
blockData.block->saveState(memento);
}
}
void InnerWidget::restoreState(not_null<SectionMemento*> memento) {
for_const (auto &blockData, _blocks) {
blockData.block->restoreState(memento);
}
}
void InnerWidget::showFinished() {
_cover->showFinished();
for_const (auto &blockData, _blocks) {
blockData.block->showFinished();
}
}
void InnerWidget::decreaseAdditionalHeight(int removeHeight) {
resizeToWidth(width(), height() - removeHeight);
}
void InnerWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), st::profileBg);
if (_mode == Mode::TwoColumn) {
int leftHeight = countBlocksHeight(RectPart::Left);
int rightHeight = countBlocksHeight(RectPart::Right);
int shadowHeight = rightHeight;// qMin(leftHeight, rightHeight);
int shadowLeft = _blocksLeft + _leftColumnWidth + _columnDivider;
int shadowTop = _blocksTop + st::profileBlockMarginTop;
p.fillRect(rtlrect(shadowLeft, shadowTop, st::lineWidth, shadowHeight - st::profileBlockMarginTop, width()), st::shadowFg);
}
}
void InnerWidget::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
emit cancelled();
}
}
int InnerWidget::countBlocksHeight(RectPart countSide) const {
int result = 0;
for_const (auto &blockData, _blocks) {
if (blockData.side != countSide || blockData.block->isHidden()) {
continue;
}
result += blockData.block->height();
}
return result;
}
int InnerWidget::countBlocksLeft(int newWidth) const {
int result = st::profileBlockLeftMin;
result += (newWidth - st::columnMinimalWidthMain) / 2;
return qMin(result, st::profileBlockLeftMax);
}
InnerWidget::Mode InnerWidget::countBlocksMode(int newWidth) const {
bool hasLeftWidget = false, hasRightWidget = false;
for_const (auto &blockData, _blocks) {
if (!blockData.block->isHidden()) {
if (blockData.side == RectPart::Left) {
hasLeftWidget = true;
} else {
hasRightWidget = true;
}
}
}
if (!hasLeftWidget || !hasRightWidget) {
return Mode::OneColumn;
}
int availWidth = newWidth - _blocksLeft;
if (availWidth >= st::profileBlockWideWidthMin + _columnDivider + st::profileBlockNarrowWidthMin) {
return Mode::TwoColumn;
}
return Mode::OneColumn;
}
int InnerWidget::countLeftColumnWidth(int newWidth) const {
int result = st::profileBlockWideWidthMin;
int availWidth = newWidth - _blocksLeft;
int additionalWidth = (availWidth - st::profileBlockWideWidthMin - _columnDivider - st::profileBlockNarrowWidthMin);
if (additionalWidth > 0) {
result += (additionalWidth / 2);
accumulate_min(result, st::profileBlockWideWidthMax);
}
return result;
}
void InnerWidget::refreshBlocksPositions() {
auto layoutBlocks = [this](RectPart layoutSide, int left) {
int top = _blocksTop;
for_const (auto &blockData, _blocks) {
if (_mode == Mode::TwoColumn && blockData.side != layoutSide) {
continue;
}
if (blockData.block->isHidden()) {
continue;
}
blockData.block->moveToLeft(left, top);
blockData.block->setVisibleTopBottom(_visibleTop - top, _visibleBottom - top);
top += blockData.block->height();
}
};
layoutBlocks(RectPart::Left, _blocksLeft);
if (_mode == Mode::TwoColumn) {
layoutBlocks(RectPart::Right, _blocksLeft + _leftColumnWidth + _columnDivider);
}
}
void InnerWidget::resizeBlocks(int newWidth) {
for_const (auto &blockData, _blocks) {
int blockWidth = newWidth - _blocksLeft;
if (_mode == Mode::OneColumn) {
blockWidth -= _blocksLeft;
} else {
if (blockData.side == RectPart::Left) {
blockWidth = _leftColumnWidth;
} else {
blockWidth -= _leftColumnWidth + _columnDivider;
}
}
blockData.block->resizeToWidth(blockWidth);
}
}
int InnerWidget::resizeGetHeight(int newWidth) {
_cover->resizeToWidth(newWidth);
_blocksTop = _cover->y() + _cover->height() + st::profileBlocksTop;
_blocksLeft = countBlocksLeft(newWidth);
_columnDivider = st::profileMemberPaddingLeft;
_mode = countBlocksMode(newWidth);
_leftColumnWidth = countLeftColumnWidth(newWidth);
resizeBlocks(newWidth);
refreshBlocksPositions();
update();
auto naturalHeight = countHeight();
_addedHeight = qMax(_minHeight - naturalHeight, 0);
return naturalHeight + _addedHeight;
}
int InnerWidget::countHeight() const {
auto newHeight = _cover->height();
auto leftHeight = countBlocksHeight(RectPart::Left);
auto rightHeight = countBlocksHeight(RectPart::Right);
auto blocksHeight = (_mode == Mode::OneColumn) ? (leftHeight + rightHeight) : qMax(leftHeight, rightHeight);
newHeight += st::profileBlocksTop + blocksHeight + st::profileBlocksBottom;
return newHeight;
}
void InnerWidget::onBlockHeightUpdated() {
refreshBlocksPositions();
int naturalHeight = countHeight();
int notDisplayedAtBottom = naturalHeight - _visibleBottom;
if (notDisplayedAtBottom < 0) {
_addedHeight = -notDisplayedAtBottom;
} else {
_addedHeight = 0;
}
if (naturalHeight + _addedHeight != height()) {
resize(width(), naturalHeight + _addedHeight);
}
}
} // namespace Profile

View file

@ -1,116 +0,0 @@
/*
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
namespace Profile {
class CoverWidget;
class BlockWidget;
class SectionMemento;
class InnerWidget final : public TWidget {
Q_OBJECT
public:
InnerWidget(QWidget *parent, PeerData *peer);
PeerData *peer() const {
return _peer;
}
void resizeToWidth(int newWidth, int minHeight) {
_minHeight = minHeight;
return TWidget::resizeToWidth(newWidth);
}
// Profile fixed top bar should use this flag to decide
// if it shows "Share contact" button or not.
// It should show it only if it is hidden in the cover.
bool shareContactButtonShown() const;
void saveState(not_null<SectionMemento*> memento);
void restoreState(not_null<SectionMemento*> memento);
void showFinished();
signals:
void cancelled();
private slots:
void onBlockHeightUpdated();
protected:
int resizeGetHeight(int newWidth) override;
void visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) override;
void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
private:
void createBlocks();
// Counts the natural widget height after resizing of child widgets.
int countHeight() const;
enum class Mode {
OneColumn,
TwoColumn,
};
int countBlocksLeft(int newWidth) const;
Mode countBlocksMode(int newWidth) const;
int countLeftColumnWidth(int newWidth) const;
int countBlocksHeight(RectPart countSide) const;
void resizeBlocks(int newWidth);
void refreshBlocksPositions();
// Sometimes height of this widget is larger than it is required
// so that it is allowed to scroll down to the desired position.
// When resizing with scroll moving up the additional height may be decreased.
void decreaseAdditionalHeight(int removeHeight);
PeerData *_peer;
// Height that we added to the natural height so that it is allowed
// to scroll down to the desired position.
int _addedHeight = 0;
int _minHeight = 0;
int _visibleTop = 0;
int _visibleBottom = 0;
object_ptr<CoverWidget> _cover;
int _blocksLeft = 0; // Caching countBlocksLeft() result.
int _blocksTop = 0;
int _columnDivider = 0;
int _leftColumnWidth = 0; // Caching countLeftColumnWidth() result.
struct Block {
BlockWidget *block;
RectPart side;
};
QList<Block> _blocks;
Mode _mode = Mode::OneColumn;
};
} // namespace Profile

View file

@ -1,37 +0,0 @@
/*
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 "profile/profile_section_memento.h"
#include "profile/profile_widget.h"
namespace Profile {
object_ptr<Window::SectionWidget> SectionMemento::createWidget(
QWidget *parent,
not_null<Window::Controller*> controller,
Window::Column column,
const QRect &geometry) {
auto result = object_ptr<Widget>(parent, controller, _peer);
result->setInternalState(geometry, this);
return std::move(result);
}
} // namespace Profile

View file

@ -1,56 +0,0 @@
/*
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 "window/section_memento.h"
namespace Profile {
class Widget;
class SectionMemento : public Window::SectionMemento {
public:
SectionMemento(PeerData *peer) : _peer(peer) {
}
object_ptr<Window::SectionWidget> createWidget(
QWidget *parent,
not_null<Window::Controller*> controller,
Window::Column column,
const QRect &geometry) override;
PeerData *getPeer() const {
return _peer;
}
void setScrollTop(int scrollTop) {
_scrollTop = scrollTop;
}
int getScrollTop() const {
return _scrollTop;
}
private:
PeerData *_peer;
int _scrollTop = 0;
};
} // namespace Window

View file

@ -1,122 +0,0 @@
/*
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 "profile/profile_userpic_button.h"
#include "styles/style_profile.h"
#include "observer_peer.h"
#include "auth_session.h"
namespace Profile {
UserpicButton::UserpicButton(QWidget *parent, not_null<PeerData*> peer, int size) : AbstractButton(parent)
, _size(size ? size : st::profilePhotoSize)
, _peer(peer) {
resize(_size, _size);
processPeerPhoto();
_notShownYet = _waiting;
if (!_waiting) {
_userpic = prepareUserpicPixmap();
}
auto observeEvents = Notify::PeerUpdate::Flag::PhotoChanged;
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) {
notifyPeerUpdated(update);
}));
subscribe(Auth().downloaderTaskFinished(), [this] {
if (_waiting && _peer->userpicLoaded()) {
_waiting = false;
startNewPhotoShowing();
}
});
}
void UserpicButton::showFinished() {
if (_notShownYet) {
_notShownYet = false;
if (!_waiting) {
_a_appearance.finish();
_a_appearance.start([this] { update(); }, 0, 1, st::profilePhotoDuration);
}
}
}
void UserpicButton::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_a_appearance.animating(getms())) {
p.drawPixmap(0, 0, _oldUserpic);
p.setOpacity(_a_appearance.current());
}
p.drawPixmap(0, 0, _userpic);
}
void UserpicButton::notifyPeerUpdated(const Notify::PeerUpdate &update) {
if (update.peer != _peer) {
return;
}
processNewPeerPhoto();
this->update();
}
void UserpicButton::processPeerPhoto() {
bool hasPhoto = (_peer->photoId && _peer->photoId != UnknownPeerPhotoId);
setCursor(hasPhoto ? style::cur_pointer : style::cur_default);
_waiting = !_peer->userpicLoaded();
if (_waiting) {
_peer->loadUserpic(true);
}
}
void UserpicButton::processNewPeerPhoto() {
processPeerPhoto();
if (!_waiting) {
startNewPhotoShowing();
}
}
void UserpicButton::startNewPhotoShowing() {
_oldUserpic = myGrab(this);
_userpic = prepareUserpicPixmap();
if (_notShownYet) {
return;
}
_a_appearance.finish();
_a_appearance.start([this] { update(); }, 0, 1, st::profilePhotoDuration);
update();
}
QPixmap UserpicButton::prepareUserpicPixmap() const {
auto retina = cIntRetinaFactor();
auto size = width() * retina;
QImage image(size, size, QImage::Format_ARGB32_Premultiplied);
image.setDevicePixelRatio(cRetinaFactor());
image.fill(Qt::transparent);
{
Painter p(&image);
_peer->paintUserpic(p, 0, 0, width());
}
return App::pixmapFromImageInPlace(std::move(image));
}
} // namespace Profile

View file

@ -1,61 +0,0 @@
/*
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/abstract_button.h"
#include "base/observer.h"
namespace Notify {
struct PeerUpdate;
} // namespace Notify
namespace Profile {
class UserpicButton : public Ui::AbstractButton, private base::Subscriber {
public:
UserpicButton(QWidget *parent, not_null<PeerData*> peer, int size = 0);
// If at the first moment the _userpic was not loaded,
// we need to show it animated after the profile is fully shown.
void showFinished();
protected:
void paintEvent(QPaintEvent *e);
private:
void notifyPeerUpdated(const Notify::PeerUpdate &update);
void processPeerPhoto();
void processNewPeerPhoto();
void startNewPhotoShowing();
QPixmap prepareUserpicPixmap() const;
bool _notShownYet;
int _size = 0;
not_null<PeerData*> _peer;
bool _waiting = false;
QPixmap _userpic, _oldUserpic;
Animation _a_appearance;
};
} // namespace Profile

View file

@ -1,169 +0,0 @@
/*
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 "profile/profile_widget.h"
#include "styles/style_settings.h"
#include "profile/profile_fixed_bar.h"
#include "profile/profile_inner_widget.h"
#include "profile/profile_section_memento.h"
#include "mainwindow.h"
#include "application.h"
#include "ui/widgets/scroll_area.h"
#include "ui/wrap/fade_wrap.h"
namespace Profile {
Widget::Widget(QWidget *parent, not_null<Window::Controller*> controller, PeerData *peer) : Window::SectionWidget(parent, controller)
, _scroll(this, st::settingsScroll)
, _fixedBar(this, peer)
, _fixedBarShadow(this) {
_fixedBar->move(0, 0);
_fixedBar->resizeToWidth(width());
_fixedBar->show();
_fixedBarShadow->hide(anim::type::instant);
_fixedBarShadow->raise();
updateAdaptiveLayout();
subscribe(Adaptive::Changed(), [this]() { updateAdaptiveLayout(); });
_inner = _scroll->setOwnedWidget(object_ptr<InnerWidget>(this, peer));
_scroll->move(0, _fixedBar->height());
_scroll->show();
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(_inner, SIGNAL(cancelled()), _fixedBar, SLOT(onBack()));
}
void Widget::updateAdaptiveLayout() {
_fixedBarShadow->moveToLeft(Adaptive::OneColumn() ? 0 : st::lineWidth, _fixedBar->height());
}
PeerData *Widget::peer() const {
return _inner->peer();
}
bool Widget::hasTopBarShadow() const {
return !_fixedBarShadow->isHidden() && !_fixedBarShadow->animating();
}
QPixmap Widget::grabForShowAnimation(const Window::SectionSlideParams &params) {
if (params.withTopBarShadow || !_scroll->scrollTop()) _fixedBarShadow->hide(anim::type::instant);
auto result = myGrab(this);
if (params.withTopBarShadow) _fixedBarShadow->show(anim::type::instant);
return result;
}
void Widget::doSetInnerFocus() {
_inner->setFocus();
}
bool Widget::showInternal(
not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) {
if (auto profileMemento = dynamic_cast<SectionMemento*>(memento.get())) {
if (profileMemento->getPeer() == peer()) {
restoreState(profileMemento);
return true;
}
}
return false;
}
void Widget::setInternalState(const QRect &geometry, not_null<SectionMemento*> memento) {
setGeometry(geometry);
myEnsureResized(this);
restoreState(memento);
}
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
auto result = std::make_unique<SectionMemento>(peer());
saveState(result.get());
return std::move(result);
}
void Widget::saveState(not_null<SectionMemento*> memento) {
memento->setScrollTop(_scroll->scrollTop());
_inner->saveState(memento);
}
void Widget::restoreState(not_null<SectionMemento*> memento) {
_inner->restoreState(memento);
auto scrollTop = memento->getScrollTop();
_scroll->scrollToY(scrollTop);
updateScrollState();
_fixedBarShadow->finishAnimating();
}
void Widget::resizeEvent(QResizeEvent *e) {
if (!width() || !height()) {
return;
}
int newScrollTop = _scroll->scrollTop() + topDelta();
_fixedBar->resizeToWidth(width());
QSize scrollSize(width(), height() - _fixedBar->height());
if (_scroll->size() != scrollSize) {
_scroll->resize(scrollSize);
_inner->resizeToWidth(scrollSize.width(), _scroll->height());
}
_fixedBar->setHideShareContactButton(_inner->shareContactButtonShown());
if (!_scroll->isHidden()) {
if (topDelta()) {
_scroll->scrollToY(newScrollTop);
}
updateScrollState();
}
}
void Widget::updateScrollState() {
auto scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
_fixedBarShadow->toggle(scrollTop > 0, anim::type::normal);
}
void Widget::onScroll() {
updateScrollState();
}
void Widget::showAnimatedHook(
const Window::SectionSlideParams &params) {
_fixedBar->setAnimatingMode(true);
}
void Widget::showFinishedHook() {
_fixedBar->setAnimatingMode(false);
if (!_scroll->scrollTop()) {
_fixedBarShadow->hide(anim::type::instant);
}
_inner->showFinished();
}
bool Widget::wheelEventFromFloatPlayer(QEvent *e) {
return _scroll->viewportEvent(e);
}
QRect Widget::rectForFloatPlayer() const {
return mapToGlobal(_scroll->geometry());
}
} // namespace Profile

View file

@ -1,85 +0,0 @@
/*
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 "window/section_widget.h"
namespace Ui {
class ScrollArea;
class FadeShadow;
} // namespace Ui
namespace Profile {
class SectionMemento;
class InnerWidget;
class FixedBar;
class Widget final : public Window::SectionWidget {
Q_OBJECT
public:
Widget(QWidget *parent, not_null<Window::Controller*> controller, PeerData *peer);
PeerData *peer() const;
PeerData *activePeer() const override {
return peer();
}
bool hasTopBarShadow() const override;
QPixmap grabForShowAnimation(const Window::SectionSlideParams &params) override;
bool showInternal(
not_null<Window::SectionMemento*> memento,
const Window::SectionShow &params) override;
std::unique_ptr<Window::SectionMemento> createMemento() override;
void setInternalState(const QRect &geometry, not_null<SectionMemento*> memento);
// Float player interface.
bool wheelEventFromFloatPlayer(QEvent *e) override;
QRect rectForFloatPlayer() const override;
protected:
void resizeEvent(QResizeEvent *e) override;
void showAnimatedHook(
const Window::SectionSlideParams &params) override;
void showFinishedHook() override;
void doSetInnerFocus() override;
private slots:
void onScroll();
private:
void updateScrollState();
void updateAdaptiveLayout();
void saveState(not_null<SectionMemento*> memento);
void restoreState(not_null<SectionMemento*> memento);
object_ptr<Ui::ScrollArea> _scroll;
QPointer<InnerWidget> _inner;
object_ptr<FixedBar> _fixedBar;
object_ptr<Ui::FadeShadow> _fixedBarShadow;
};
} // namespace Profile

View file

@ -24,6 +24,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/lifetime.h>
#include <rpl/details/callable.h>
// GCC 7.2 can't handle not type-erased consumers.
// It eats up 4GB RAM + 16GB swap on the unittest and dies.
// Clang and Visual C++ both handle it without such problems.
#if defined _DEBUG || defined COMPILER_GCC
#define RPL_CONSUMER_TYPE_ERASED_ALWAYS
#endif // _DEBUG || COMPILER_GCC
namespace rpl {
namespace details {
@ -609,9 +616,6 @@ inline bool operator>=(
return !(a < b);
}
// GCC 7.2 can't handle not type-erased consumers.
// It eats up 4GB RAM + 16GB swap on the unittest and dies.
// Clang and Visual C++ both handle it without such problems.
template <
typename Value,
typename Error,
@ -622,11 +626,11 @@ template <
details::is_callable_v<OnNext, Value> &&
details::is_callable_v<OnError, Error> &&
details::is_callable_v<OnDone>>>
#ifdef COMPILER_GCC
#ifdef RPL_CONSUMER_TYPE_ERASED_ALWAYS
inline consumer<Value, Error> make_consumer(
#else // COMPILER_GCC
#else // RPL_CONSUMER_TYPE_ERASED_ALWAYS
inline auto make_consumer(
#endif // COMPILER_GCC
#endif // !RPL_CONSUMER_TYPE_ERASED_ALWAYS
OnNext &&next,
OnError &&error,
OnDone &&done) {

View file

@ -26,6 +26,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/details/superset_type.h>
#include <rpl/details/callable.h>
#if defined _DEBUG
#define RPL_PRODUCER_TYPE_ERASED_ALWAYS
#endif // _DEBUG
namespace rpl {
namespace details {
@ -450,7 +454,11 @@ template <
details::const_ref_consumer<Value, Error>())),
lifetime>>>
inline auto make_producer(Generator &&generator)
#ifdef RPL_PRODUCER_TYPE_ERASED_ALWAYS
-> producer<Value, Error> {
#else // RPL_CONSUMER_TYPE_ERASED_ALWAYS
-> producer<Value, Error, std::decay_t<Generator>> {
#endif // !RPL_CONSUMER_TYPE_ERASED_ALWAYS
return std::forward<Generator>(generator);
}

View file

@ -72,10 +72,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "base/assertion.h"
#include <gsl/gsl>
#ifndef _DEBUG
#include <rpl/rpl.h>
#endif // _DEBUG
#include "base/variant.h"
#include "base/optional.h"

View file

@ -118,4 +118,16 @@ rpl::producer<SharedMediaResult> SharedMedia::query(SharedMediaQuery &&query) co
};
}
rpl::producer<SharedMediaSliceUpdate> SharedMedia::sliceUpdated() const {
return _sliceUpdated.events();
}
rpl::producer<SharedMediaRemoveOne> SharedMedia::oneRemoved() const {
return _oneRemoved.events();
}
rpl::producer<SharedMediaRemoveAll> SharedMedia::allRemoved() const {
return _allRemoved.events();
}
} // namespace Storage

View file

@ -194,16 +194,9 @@ public:
void remove(SharedMediaRemoveAll &&query);
rpl::producer<SharedMediaResult> query(SharedMediaQuery &&query) const;
rpl::producer<SharedMediaSliceUpdate> sliceUpdated() const {
return _sliceUpdated.events();
}
rpl::producer<SharedMediaRemoveOne> oneRemoved() const {
return _oneRemoved.events();
}
rpl::producer<SharedMediaRemoveAll> allRemoved() const {
return _allRemoved.events();
}
rpl::producer<SharedMediaSliceUpdate> sliceUpdated() const;
rpl::producer<SharedMediaRemoveOne> oneRemoved() const;
rpl::producer<SharedMediaRemoveAll> allRemoved() const;
private:
using Lists = std::array<SparseIdsList, kSharedMediaTypeCount>;

View file

@ -197,6 +197,10 @@ rpl::producer<SparseIdsListResult> SparseIdsList::query(
};
}
rpl::producer<SparseIdsSliceUpdate> SparseIdsList::sliceUpdated() const {
return _sliceUpdated.events();
}
SparseIdsListResult SparseIdsList::queryFromSlice(
const SparseIdsListQuery &query,
const Slice &slice) const {

View file

@ -62,10 +62,7 @@ public:
void removeOne(MsgId messageId);
void removeAll();
rpl::producer<SparseIdsListResult> query(SparseIdsListQuery &&query) const;
rpl::producer<SparseIdsSliceUpdate> sliceUpdated() const {
return _sliceUpdated.events();
}
rpl::producer<SparseIdsSliceUpdate> sliceUpdated() const;
private:
struct Slice {

View file

@ -114,8 +114,15 @@ rpl::producer<UserPhotosResult> UserPhotos::List::query(
};
}
std::map<UserId, UserPhotos::List>::iterator
UserPhotos::enforceLists(UserId user) {
auto UserPhotos::List::sliceUpdated() const -> rpl::producer<SliceUpdate> {
return _sliceUpdated.events();
}
rpl::producer<UserPhotosSliceUpdate> UserPhotos::sliceUpdated() const {
return _sliceUpdated.events();
}
std::map<UserId, UserPhotos::List>::iterator UserPhotos::enforceLists(UserId user) {
auto result = _lists.find(user);
if (result != _lists.end()) {
return result;

View file

@ -144,10 +144,7 @@ public:
void remove(UserPhotosRemoveAfter &&query);
rpl::producer<UserPhotosResult> query(UserPhotosQuery &&query) const;
rpl::producer<UserPhotosSliceUpdate> sliceUpdated() const {
return _sliceUpdated.events();
}
rpl::producer<UserPhotosSliceUpdate> sliceUpdated() const;
private:
class List {
@ -164,9 +161,7 @@ private:
const std::deque<PhotoId> *photoIds = nullptr;
base::optional<int> count;
};
rpl::producer<SliceUpdate> sliceUpdated() const {
return _sliceUpdated.events();
}
rpl::producer<SliceUpdate> sliceUpdated() const;
private:
void sendUpdate();

View file

@ -0,0 +1,134 @@
/*
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 "ui/rp_widget.h"
namespace Ui {
rpl::producer<QRect> RpWidgetMethods::geometryValue() const {
auto &stream = eventStreams().geometry;
return stream.events_starting_with_copy(callGetGeometry());
}
rpl::producer<QSize> RpWidgetMethods::sizeValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.size(); })
| rpl::distinct_until_changed();
}
rpl::producer<int> RpWidgetMethods::heightValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.height(); })
| rpl::distinct_until_changed();
}
rpl::producer<int> RpWidgetMethods::widthValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.width(); })
| rpl::distinct_until_changed();
}
rpl::producer<QPoint> RpWidgetMethods::positionValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.topLeft(); })
| rpl::distinct_until_changed();
}
rpl::producer<int> RpWidgetMethods::leftValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.left(); })
| rpl::distinct_until_changed();
}
rpl::producer<int> RpWidgetMethods::topValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.top(); })
| rpl::distinct_until_changed();
}
rpl::producer<int> RpWidgetMethods::desiredHeightValue() const {
return heightValue();
}
rpl::producer<bool> RpWidgetMethods::shownValue() const {
auto &stream = eventStreams().shown;
return stream.events_starting_with(!callIsHidden());
}
rpl::producer<QRect> RpWidgetMethods::paintRequest() const {
return eventStreams().paint.events();
}
rpl::producer<> RpWidgetMethods::alive() const {
return eventStreams().alive.events();
}
rpl::lifetime &RpWidgetMethods::lifetime() {
return _lifetime;
}
bool RpWidgetMethods::handleEvent(QEvent *event) {
switch (event->type()) {
case QEvent::Move:
case QEvent::Resize:
if (auto streams = _eventStreams.get()) {
auto that = callCreateWeak();
streams->geometry.fire_copy(callGetGeometry());
if (!that) {
return true;
}
}
break;
case QEvent::Paint:
if (auto streams = _eventStreams.get()) {
auto that = callCreateWeak();
streams->paint.fire_copy(
static_cast<QPaintEvent*>(event)->rect());
if (!that) {
return true;
}
}
break;
}
return eventHook(event);
}
RpWidgetMethods::Initer::Initer(QWidget *parent) {
parent->setGeometry(0, 0, 0, 0);
}
void RpWidgetMethods::visibilityChangedHook(bool wasVisible, bool nowVisible) {
if (nowVisible != wasVisible) {
if (auto streams = _eventStreams.get()) {
streams->shown.fire_copy(nowVisible);
}
}
}
auto RpWidgetMethods::eventStreams() const -> EventStreams& {
if (!_eventStreams) {
_eventStreams = std::make_unique<EventStreams>();
}
return *_eventStreams;
}
} // namespace Ui

View file

@ -56,9 +56,7 @@ inline Widget *CreateChild(
Parent *parent,
Args &&...args) {
Expects(parent != nullptr);
return base::make_unique_q<Widget>(
parent,
std::forward<Args>(args)...).release();
return new Widget(parent, std::forward<Args>(args)...);
}
template <typename Value>
@ -76,141 +74,102 @@ using RpWidgetParent = std::conditional_t<
TWidgetHelper<Widget>>;
template <typename Widget>
class RpWidgetWrap : public RpWidgetParent<Widget> {
using Parent = RpWidgetParent<Widget>;
class RpWidgetWrap;
class RpWidgetMethods {
public:
using Parent::Parent;
auto geometryValue() const {
auto &stream = eventStreams().geometry;
return stream.events_starting_with_copy(this->geometry());
}
auto sizeValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.size(); })
| rpl::distinct_until_changed();
}
auto heightValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.height(); })
| rpl::distinct_until_changed();
}
auto widthValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.width(); })
| rpl::distinct_until_changed();
}
auto positionValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.topLeft(); })
| rpl::distinct_until_changed();
}
auto leftValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.left(); })
| rpl::distinct_until_changed();
}
auto topValue() const {
return geometryValue()
| rpl::map([](QRect &&value) { return value.top(); })
| rpl::distinct_until_changed();
}
virtual rpl::producer<int> desiredHeightValue() const {
return heightValue();
}
auto shownValue() const {
auto &stream = eventStreams().shown;
return stream.events_starting_with(!this->isHidden());
}
auto paintRequest() const {
return eventStreams().paint.events();
}
auto alive() const {
return eventStreams().alive.events();
}
void setVisible(bool visible) final override {
auto wasVisible = !this->isHidden();
Parent::setVisible(visible);
auto nowVisible = !this->isHidden();
if (nowVisible != wasVisible) {
if (auto streams = _eventStreams.get()) {
streams->shown.fire_copy(nowVisible);
}
}
}
rpl::producer<QRect> geometryValue() const;
rpl::producer<QSize> sizeValue() const;
rpl::producer<int> heightValue() const;
rpl::producer<int> widthValue() const;
rpl::producer<QPoint> positionValue() const;
rpl::producer<int> leftValue() const;
rpl::producer<int> topValue() const;
virtual rpl::producer<int> desiredHeightValue() const;
rpl::producer<bool> shownValue() const;
rpl::producer<QRect> paintRequest() const;
rpl::producer<> alive() const;
template <typename Error, typename Generator>
void showOn(rpl::producer<bool, Error, Generator> &&shown) {
std::move(shown)
| rpl::start_with_next([this](bool visible) {
this->setVisible(visible);
callSetVisible(visible);
}, lifetime());
}
rpl::lifetime &lifetime() {
return _lifetime.data;
}
rpl::lifetime &lifetime();
protected:
bool event(QEvent *event) final override {
switch (event->type()) {
case QEvent::Move:
case QEvent::Resize:
if (auto streams = _eventStreams.get()) {
auto that = weak(this);
streams->geometry.fire_copy(this->geometry());
if (!that) {
return true;
}
}
break;
case QEvent::Paint:
if (auto streams = _eventStreams.get()) {
auto that = weak(this);
streams->paint.fire_copy(
static_cast<QPaintEvent*>(event)->rect());
if (!that) {
return true;
}
}
break;
}
return eventHook(event);
}
virtual bool eventHook(QEvent *event) {
return Parent::event(event);
}
bool handleEvent(QEvent *event);
virtual bool eventHook(QEvent *event) = 0;
private:
template <typename Widget>
friend class RpWidgetWrap;
struct EventStreams {
rpl::event_stream<QRect> geometry;
rpl::event_stream<QRect> paint;
rpl::event_stream<bool> shown;
rpl::event_stream<> alive;
};
struct LifetimeHolder {
LifetimeHolder(QWidget *parent) {
parent->setGeometry(0, 0, 0, 0);
}
rpl::lifetime data;
struct Initer {
Initer(QWidget *parent);
};
EventStreams &eventStreams() const {
if (!_eventStreams) {
_eventStreams = std::make_unique<EventStreams>();
}
return *_eventStreams;
}
virtual void callSetVisible(bool visible) = 0;
virtual QPointer<QObject> callCreateWeak() = 0;
virtual QRect callGetGeometry() const = 0;
virtual bool callIsHidden() const = 0;
void visibilityChangedHook(bool wasVisible, bool nowVisible);
EventStreams &eventStreams() const;
mutable std::unique_ptr<EventStreams> _eventStreams;
rpl::lifetime _lifetime;
LifetimeHolder _lifetime = { this };
};
template <typename Widget>
class RpWidgetWrap
: public RpWidgetParent<Widget>
, public RpWidgetMethods {
using Self = RpWidgetWrap<Widget>;
using Parent = RpWidgetParent<Widget>;
public:
using Parent::Parent;
void setVisible(bool visible) final override {
auto wasVisible = !this->isHidden();
Parent::setVisible(visible);
visibilityChangedHook(wasVisible, !this->isHidden());
}
protected:
bool event(QEvent *event) final override {
return handleEvent(event);
}
bool eventHook(QEvent *event) override {
return Parent::event(event);
}
private:
void callSetVisible(bool visible) override {
Self::setVisible(visible);
}
QPointer<QObject> callCreateWeak() override {
return QPointer<QObject>((QObject*)this);
}
QRect callGetGeometry() const override {
return this->geometry();
}
bool callIsHidden() const override {
return this->isHidden();
}
Initer _initer = { this };
};

View file

@ -97,6 +97,18 @@ base::unique_qptr<Ui::RpWidget> SearchFieldController::createRowView(
return std::move(result);
}
QString SearchFieldController::query() const {
return _query.current();
}
rpl::producer<QString> SearchFieldController::queryValue() const {
return _query.value();
}
rpl::producer<QString> SearchFieldController::queryChanges() const {
return _query.changes();
}
base::unique_qptr<Ui::InputField> SearchFieldController::createField(
QWidget *parent,
const style::InputField &st) {

View file

@ -45,15 +45,9 @@ public:
QWidget *parent,
const style::SearchFieldRow &st);
QString query() const {
return _query.current();
}
rpl::producer<QString> queryValue() const {
return _query.value();
}
rpl::producer<QString> queryChanges() const {
return _query.changes();
}
QString query() const;
rpl::producer<QString> queryValue() const;
rpl::producer<QString> queryChanges() const;
rpl::lifetime &lifetime() {
return _lifetime;

View file

@ -98,8 +98,8 @@ protected:
return heightNoMargins();
}
void visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) override {
int visibleTop,
int visibleBottom) override {
setChildVisibleTopBottom(
wrapped(),
visibleTop,

View file

@ -158,6 +158,7 @@
<(src_loc)/data/data_game.h
<(src_loc)/data/data_peer.cpp
<(src_loc)/data/data_peer.h
<(src_loc)/data/data_peer_values.cpp
<(src_loc)/data/data_peer_values.h
<(src_loc)/data/data_photo.cpp
<(src_loc)/data/data_photo.h
@ -444,42 +445,16 @@
<(src_loc)/platform/platform_window_title.h
<(src_loc)/profile/profile_back_button.cpp
<(src_loc)/profile/profile_back_button.h
<(src_loc)/profile/profile_block_actions.cpp
<(src_loc)/profile/profile_block_actions.h
<(src_loc)/profile/profile_block_channel_members.cpp
<(src_loc)/profile/profile_block_channel_members.h
<(src_loc)/profile/profile_block_info.cpp
<(src_loc)/profile/profile_block_info.h
<(src_loc)/profile/profile_block_invite_link.cpp
<(src_loc)/profile/profile_block_invite_link.h
<(src_loc)/profile/profile_block_group_members.cpp
<(src_loc)/profile/profile_block_group_members.h
<(src_loc)/profile/profile_block_peer_list.cpp
<(src_loc)/profile/profile_block_peer_list.h
<(src_loc)/profile/profile_block_settings.cpp
<(src_loc)/profile/profile_block_settings.h
<(src_loc)/profile/profile_block_shared_media.cpp
<(src_loc)/profile/profile_block_shared_media.h
<(src_loc)/profile/profile_block_widget.cpp
<(src_loc)/profile/profile_block_widget.h
<(src_loc)/profile/profile_channel_controllers.cpp
<(src_loc)/profile/profile_channel_controllers.h
<(src_loc)/profile/profile_common_groups_section.cpp
<(src_loc)/profile/profile_common_groups_section.h
<(src_loc)/profile/profile_cover_drop_area.cpp
<(src_loc)/profile/profile_cover_drop_area.h
<(src_loc)/profile/profile_cover.cpp
<(src_loc)/profile/profile_cover.h
<(src_loc)/profile/profile_fixed_bar.cpp
<(src_loc)/profile/profile_fixed_bar.h
<(src_loc)/profile/profile_inner_widget.cpp
<(src_loc)/profile/profile_inner_widget.h
<(src_loc)/profile/profile_section_memento.cpp
<(src_loc)/profile/profile_section_memento.h
<(src_loc)/profile/profile_userpic_button.cpp
<(src_loc)/profile/profile_userpic_button.h
<(src_loc)/profile/profile_widget.cpp
<(src_loc)/profile/profile_widget.h
<(src_loc)/settings/settings_advanced_widget.cpp
<(src_loc)/settings/settings_advanced_widget.h
<(src_loc)/settings/settings_background_widget.cpp
@ -617,6 +592,7 @@
<(src_loc)/ui/images.cpp
<(src_loc)/ui/images.h
<(src_loc)/ui/resize_area.h
<(src_loc)/ui/rp_widget.cpp
<(src_loc)/ui/rp_widget.h
<(src_loc)/ui/search_field_controller.cpp
<(src_loc)/ui/search_field_controller.h