diff --git a/Telegram/SourceFiles/info/profile/info_profile_button.cpp b/Telegram/SourceFiles/info/profile/info_profile_button.cpp new file mode 100644 index 000000000..4bebe050a --- /dev/null +++ b/Telegram/SourceFiles/info/profile/info_profile_button.cpp @@ -0,0 +1,142 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "info/profile/info_profile_button.h" + +#include +#include "ui/widgets/checkbox.h" +#include "styles/style_info.h" + +namespace Info { +namespace Profile { + +Button::Button( + QWidget *parent, + rpl::producer &&text) +: Button(parent, std::move(text), st::infoProfileButton) { +} + +Button::Button( + QWidget *parent, + rpl::producer &&text, + const style::InfoProfileButton &st) +: RippleButton(parent, st.ripple) +, _st(st) { + std::move(text) + | rpl::start([this](QString &&value) { + setText(std::move(value)); + }, lifetime()); +} + +Button *Button::toggleOn(rpl::producer &&toggled) { + _toggleOnLifetime.destroy(); + _toggle = std::make_unique( + isOver() ? _st.toggleOver : _st.toggle, + false, + [this] { rtlupdate(toggleRect()); }); + clicks() + | rpl::start([this](auto) { + _toggle->setCheckedAnimated(!_toggle->checked()); + }, _toggleOnLifetime); + std::move(toggled) + | rpl::start([this](bool toggled) { + _toggle->setCheckedAnimated(toggled); + }, _toggleOnLifetime); + _toggle->finishAnimation(); + return this; +} + +rpl::producer Button::toggledValue() const { + return _toggle ? _toggle->checkedValue() : rpl::never(); +} + +void Button::paintEvent(QPaintEvent *e) { + Painter p(this); + + auto ms = getms(); + auto paintOver = (isOver() || isDown()); + p.fillRect(e->rect(), paintOver ? _st.textBgOver : _st.textBg); + + paintRipple(p, 0, 0, ms); + + auto outerw = width(); + p.setFont(_st.font); + p.setPen(paintOver ? _st.textFgOver : _st.textFg); + p.drawTextLeft( + _st.padding.left(), + _st.padding.top(), + outerw, + _text, + _textWidth); + + if (_toggle) { + auto rect = toggleRect(); + _toggle->paint(p, rect.left(), rect.top(), outerw, ms); + } +} + +QRect Button::toggleRect() const { + Expects(_toggle != nullptr); + auto size = _toggle->getSize(); + auto left = width() - _st.toggleSkip - size.width(); + auto top = (height() - size.height()) / 2; + return { QPoint(left, top), size }; +} + +int Button::resizeGetHeight(int newWidth) { + updateVisibleText(newWidth); + return _st.padding.top() + _st.height + _st.padding.bottom(); +} + +void Button::onStateChanged( + State was, + StateChangeSource source) { + RippleButton::onStateChanged(was, source); + if (_toggle) { + _toggle->setStyle(isOver() ? _st.toggleOver : _st.toggle); + } +} + +void Button::setText(QString &&text) { + _original = std::move(text); + _originalWidth = _st.font->width(_original); + updateVisibleText(width()); +} + +void Button::updateVisibleText(int newWidth) { + auto availableWidth = newWidth + - _st.padding.left() + - _st.padding.right(); + if (_toggle) { + availableWidth -= (width() - toggleRect().x()); + } + accumulate_max(availableWidth, 0); + if (availableWidth < _originalWidth) { + _text = _st.font->elided(_original, availableWidth); + _textWidth = _st.font->width(_text); + } else { + _text = _original; + _textWidth = _originalWidth; + } + update(); +} + +} // namespace Profile +} // namespace Info diff --git a/Telegram/SourceFiles/info/profile/info_profile_button.h b/Telegram/SourceFiles/info/profile/info_profile_button.h new file mode 100644 index 000000000..f14625239 --- /dev/null +++ b/Telegram/SourceFiles/info/profile/info_profile_button.h @@ -0,0 +1,69 @@ +/* +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/widgets/buttons.h" + +namespace Ui { +class ToggleView; +} // namespace Ui + +namespace Info { +namespace Profile { + +class Button : public Ui::RippleButton { +public: + Button( + QWidget *parent, + rpl::producer &&text); + Button( + QWidget *parent, + rpl::producer &&text, + const style::InfoProfileButton &st); + + Button *toggleOn(rpl::producer &&toggled); + rpl::producer toggledValue() const; + +protected: + int resizeGetHeight(int newWidth) override; + void onStateChanged( + State was, + StateChangeSource source) override; + + void paintEvent(QPaintEvent *e) override; + +private: + void setText(QString &&text); + QRect toggleRect() const; + void updateVisibleText(int newWidth); + + const style::InfoProfileButton &_st; + QString _original; + QString _text; + int _originalWidth = 0; + int _textWidth = 0; + std::unique_ptr _toggle; + rpl::lifetime _toggleOnLifetime; + +}; + +} // namespace Profile +} // namespace Info diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp new file mode 100644 index 000000000..b8e6ed552 --- /dev/null +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp @@ -0,0 +1,372 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "info/profile/info_profile_cover.h" + +#include +#include "info/profile/info_profile_values.h" +#include "lang/lang_keys.h" +#include "styles/style_info.h" +#include "ui/widgets/labels.h" +#include "ui/effects/ripple_animation.h" +#include "observer_peer.h" +#include "messenger.h" +#include "auth_session.h" +#include "apiwrap.h" +#include "profile/profile_userpic_button.h" + +namespace Info { +namespace Profile { +namespace { + +auto MembersStatusText(int count) { + return lng_chat_status_members(lt_count, count); +}; + +auto OnlineStatusText(int count) { + return lng_chat_status_online(lt_count, count); +}; + +auto ChatStatusText(int fullCount, int onlineCount, bool isGroup) { + if (onlineCount > 0 && onlineCount <= fullCount) { + return lng_chat_status_members_online( + lt_members_count, MembersStatusText(fullCount), + lt_online_count, OnlineStatusText(onlineCount)); + } else if (fullCount > 0) { + return lng_chat_status_members(lt_count, fullCount); + } + return lang(isGroup + ? lng_group_status + : lng_channel_status); +}; + +} // namespace + +Cover::Cover(QWidget *parent, not_null peer) +: FixedHeightWidget( + parent, + st::infoProfilePhotoTop + + st::infoProfilePhotoSize + + st::infoProfilePhotoBottom) +, _peer(peer) +, _userpic(this, _peer, st::infoProfilePhotoSize) +, _name(this, st::infoProfileNameLabel) +, _status(this, st::infoProfileStatusLabel) { + _peer->updateFull(); + + _name->setSelectable(true); + _status->setAttribute(Qt::WA_TransparentForMouseEvents); + + initUserpicButton(); + initViewers(); + setupChildGeometry(); +} + +void Cover::setupChildGeometry() { + widthValue() + | rpl::start([this](int newWidth) { + _userpic->moveToLeft( + st::infoProfilePhotoLeft, + st::infoProfilePhotoTop, + newWidth); + refreshNameGeometry(newWidth); + refreshStatusGeometry(newWidth); + }, lifetime()); +} + +Cover *Cover::setOnlineCount(rpl::producer &&count) { + std::move(count) + | rpl::start([this](int count) { + _onlineCount = count; + refreshStatusText(); + }, lifetime()); + return this; +} + +Cover *Cover::setToggleShown(rpl::producer &&shown) { + _toggle.create( + this, + QString(), + st::infoToggleCheckbox, + std::make_unique( + st::infoToggle, + false, + [this] { _toggle->updateCheck(); })); + _toggle->lower(); + _toggle->setCheckAlignment(style::al_right); + widthValue() + | rpl::start([this](int newValue) { + _toggle->setGeometry(0, 0, newValue, height()); + }, _toggle->lifetime()); + std::move(shown) + | rpl::start([this](bool shown) { + if (_toggle->isHidden() == shown) { + _toggle->setVisible(shown); + } + }, lifetime()); + return this; +} + +void Cover::initViewers() { + using Flag = Notify::PeerUpdate::Flag; + PeerUpdateValue(_peer, Flag::PhotoChanged) + | rpl::start( + [this](auto&&) { this->refreshUserpicLink(); }, + lifetime()); + PeerUpdateValue(_peer, Flag::NameChanged) + | rpl::start( + [this](auto&&) { this->refreshNameText(); }, + lifetime()); + PeerUpdateValue(_peer, + Flag::UserOnlineChanged | Flag::MembersChanged) + | rpl::start( + [this](auto&&) { this->refreshStatusText(); }, + lifetime()); +} + +void Cover::initUserpicButton() { + _userpic->setClickedCallback([this] { + auto hasPhoto = (_peer->photoId != 0); + auto knownPhoto = (_peer->photoId != UnknownPeerPhotoId); + if (hasPhoto && knownPhoto) { + if (auto photo = App::photo(_peer->photoId)) { + if (photo->date) { + Messenger::Instance().showPhoto(photo, _peer); + } + } + } + }); +} + +void Cover::refreshUserpicLink() { + auto hasPhoto = (_peer->photoId != 0); + auto knownPhoto = (_peer->photoId != UnknownPeerPhotoId); + _userpic->setPointerCursor(hasPhoto && knownPhoto); + if (!knownPhoto) { + Auth().api().requestFullPeer(_peer); + } +} + +void Cover::refreshNameText() { + _name->setText(App::peerName(_peer)); + refreshNameGeometry(width()); +} + +void Cover::refreshStatusText() { + auto statusText = [this] { + auto currentTime = unixtime(); + if (auto user = _peer->asUser()) { + auto result = App::onlineText(user, currentTime, true); + return App::onlineColorUse(user, currentTime) + ? textcmdLink(1, result) + : result; + } else if (auto chat = _peer->asChat()) { + if (!chat->amIn()) { + return lang(lng_chat_status_unaccessible); + } + auto fullCount = qMax( + chat->count, + chat->participants.size()); + return ChatStatusText(fullCount, _onlineCount, true); + } else if (auto channel = _peer->asChannel()) { + auto fullCount = qMax(channel->membersCount(), 1); + return ChatStatusText( + fullCount, + _onlineCount, + channel->isMegagroup()); + } + return lang(lng_chat_status_unaccessible); + }(); + _status->setRichText(statusText); + refreshStatusGeometry(width()); +} + +void Cover::refreshNameGeometry(int newWidth) { + auto nameWidth = newWidth + - st::infoProfileNameLeft + - st::infoProfileNameRight; + if (_toggle) { + nameWidth -= st::infoToggleCheckbox.checkPosition.x() + + _toggle->checkRect().width(); + } + _name->resizeToWidth(nameWidth); + _name->moveToLeft( + st::infoProfileNameLeft, + st::infoProfileNameTop, + newWidth); +} + +void Cover::refreshStatusGeometry(int newWidth) { + auto statusWidth = newWidth + - st::infoProfileStatusLeft + - st::infoProfileStatusRight; + if (_toggle) { + statusWidth -= st::infoToggleCheckbox.checkPosition.x() + + _toggle->checkRect().width(); + } + _status->resizeToWidth(statusWidth); + _status->moveToLeft( + st::infoProfileStatusLeft, + st::infoProfileStatusTop, + newWidth); +} + +rpl::producer Cover::toggledValue() const { + return _toggle + ? (rpl::single(_toggle->checked()) + | rpl::then( + base::ObservableViewer(_toggle->checkedChanged))) + : rpl::never(); +} + +QMargins SharedMediaCover::getMargins() const { + return QMargins(0, 0, 0, st::infoSharedMediaBottomSkip); +} + +SharedMediaCover::SharedMediaCover(QWidget *parent) +: FixedHeightWidget(parent, st::infoSharedMediaCoverHeight) { + createLabel(); +} + +void SharedMediaCover::createLabel() { + auto label = object_ptr( + this, + Lang::Viewer(lng_profile_shared_media) | ToUpperValue(), + st::infoSharedMediaLabel); + label->setAttribute(Qt::WA_TransparentForMouseEvents); + widthValue() + | rpl::start([weak = label.data()](int newWidth) { + weak->resizeToNaturalWidth(newWidth + - st::infoSharedMediaLabelPosition.x() + - st::infoSharedMediaButton.padding.right()); + weak->moveToLeft( + st::infoSharedMediaLabelPosition.x(), + st::infoSharedMediaLabelPosition.y(), + newWidth); + }, label->lifetime()); +} + +SharedMediaCover *SharedMediaCover::setToggleShown( + rpl::producer &&shown) { + _toggle.create( + this, + QString(), + st::infoToggleCheckbox, + std::make_unique( + st::infoToggle, + false, + [this] { _toggle->updateCheck(); })); + _toggle->lower(); + _toggle->setCheckAlignment(style::al_right); + widthValue() + | rpl::start([this](int newValue) { + _toggle->setGeometry(0, 0, newValue, height()); + }, _toggle->lifetime()); + std::move(shown) + | rpl::start([this](bool shown) { + if (_toggle->isHidden() == shown) { + _toggle->setVisible(shown); + } + }, lifetime()); + return this; +} + +rpl::producer SharedMediaCover::toggledValue() const { + return _toggle + ? (rpl::single(_toggle->checked()) + | rpl::then( + base::ObservableViewer(_toggle->checkedChanged))) + : rpl::never(); +} + +SectionToggle::SectionToggle( + const style::InfoToggle &st, + bool checked, + base::lambda updateCallback) +: AbstractCheckView(st.duration, checked, std::move(updateCallback)) +, _st(st) { +} + +QSize SectionToggle::getSize() const { + return QSize(_st.size, _st.size); +} + +void SectionToggle::paint( + Painter &p, + int left, + int top, + int outerWidth, + TimeMs ms) { + auto sqrt2 = sqrt(2.); + auto vLeft = rtlpoint(left + _st.skip, 0, outerWidth).x() + 0.; + auto vTop = top + _st.skip + 0.; + auto vWidth = _st.size - 2 * _st.skip; + auto vHeight = _st.size - 2 * _st.skip; + auto vStroke = _st.stroke / sqrt2; + constexpr auto kPointCount = 6; + std::array pathV = { { + { vLeft, vTop + (vHeight / 4.) + vStroke }, + { vLeft + vStroke, vTop + (vHeight / 4.) }, + { vLeft + (vWidth / 2.), vTop + (vHeight * 3. / 4.) - vStroke }, + { vLeft + vWidth - vStroke, vTop + (vHeight / 4.) }, + { vLeft + vWidth, vTop + (vHeight / 4.) + vStroke }, + { vLeft + (vWidth / 2.), vTop + (vHeight * 3. / 4.) + vStroke }, + } }; + + auto toggled = currentAnimationValue(ms); + auto alpha = (toggled - 1.) * M_PI_2; + auto cosalpha = cos(alpha); + auto sinalpha = sin(alpha); + auto shiftx = vLeft + (vWidth / 2.); + auto shifty = vTop + (vHeight / 2.); + for (auto &point : pathV) { + auto x = point.x() - shiftx; + auto y = point.y() - shifty; + point.setX(shiftx + x * cosalpha - y * sinalpha); + point.setY(shifty + y * cosalpha + x * sinalpha); + } + QPainterPath path; + path.moveTo(pathV[0]); + for (int i = 1; i != kPointCount; ++i) { + path.lineTo(pathV[i]); + } + path.lineTo(pathV[0]); + + PainterHighQualityEnabler hq(p); + p.fillPath(path, _st.color); +} + +QImage SectionToggle::prepareRippleMask() const { + return Ui::RippleAnimation::ellipseMask(rippleSize()); +} + +QSize SectionToggle::rippleSize() const { + return getSize() + 2 * QSize( + _st.rippleAreaPadding, + _st.rippleAreaPadding); +} + +bool SectionToggle::checkRippleStartPosition(QPoint position) const { + return QRect(QPoint(0, 0), rippleSize()).contains(position); + +} + +} // namespace Profile +} // namespace Info diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.h b/Telegram/SourceFiles/info/profile/info_profile_cover.h new file mode 100644 index 000000000..ff58e66dd --- /dev/null +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.h @@ -0,0 +1,113 @@ +/* +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/wrap/padding_wrap.h" +#include "ui/widgets/checkbox.h" + +namespace style { +struct InfoToggle; +} // namespace style + +namespace Profile { +class UserpicButton; +} // namespace Profile + +namespace Ui { +class FlatLabel; +template +class SlideWrap; +} // namespace Ui + +namespace Info { +namespace Profile { + +class Cover : public Ui::FixedHeightWidget { +public: + Cover(QWidget *parent, not_null peer); + + Cover *setOnlineCount(rpl::producer &&count); + Cover *setToggleShown(rpl::producer &&shown); + rpl::producer toggledValue() const; + +private: + void setupChildGeometry(); + void initViewers(); + void initUserpicButton(); + void refreshUserpicLink(); + void refreshNameText(); + void refreshStatusText(); + void refreshNameGeometry(int newWidth); + void refreshStatusGeometry(int newWidth); + + not_null _peer; + int _onlineCount = 0; + + object_ptr<::Profile::UserpicButton> _userpic; + object_ptr _name = { nullptr }; + object_ptr _status = { nullptr }; + object_ptr _toggle = { nullptr }; + //object_ptr _dropArea = { nullptr }; + +}; + +class SharedMediaCover : public Ui::FixedHeightWidget { +public: + SharedMediaCover(QWidget *parent); + + SharedMediaCover *setToggleShown(rpl::producer &&shown); + rpl::producer toggledValue() const; + + QMargins getMargins() const override; + +private: + void createLabel(); + + object_ptr _toggle = { nullptr }; + +}; + +class SectionToggle : public Ui::AbstractCheckView { +public: + SectionToggle( + const style::InfoToggle &st, + bool checked, + base::lambda updateCallback); + + QSize getSize() const override; + void paint( + Painter &p, + int left, + int top, + int outerWidth, + TimeMs ms) override; + QImage prepareRippleMask() const override; + bool checkRippleStartPosition(QPoint position) const override; + +private: + QSize rippleSize() const; + + const style::InfoToggle &_st; + +}; + +} // namespace Profile +} // namespace Info diff --git a/Telegram/SourceFiles/info/profile/info_profile_icon.cpp b/Telegram/SourceFiles/info/profile/info_profile_icon.cpp new file mode 100644 index 000000000..938ebe8a3 --- /dev/null +++ b/Telegram/SourceFiles/info/profile/info_profile_icon.cpp @@ -0,0 +1,57 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "info/profile/info_profile_icon.h" + +namespace Info { +namespace Profile { + +FloatingIcon::FloatingIcon( + RpWidget *parent, + const style::icon &icon, + QPoint position) +: FloatingIcon(parent, icon, position, Tag{}) { +} + +FloatingIcon::FloatingIcon( + RpWidget *parent, + const style::icon &icon, + QPoint position, + const Tag &) +: RpWidget(parent) +, _icon(&icon) +, _point(position) { + resize( + _point.x() + _icon->width(), + _point.y() + _icon->height()); + setAttribute(Qt::WA_TransparentForMouseEvents); + parent->widthValue() + | rpl::start( + [this](auto&&) { moveToLeft(0, 0); }, + lifetime()); +} + +void FloatingIcon::paintEvent(QPaintEvent *e) { + Painter p(this); + _icon->paint(p, _point, width()); +} + +} // namespace Profile +} // namespace Info diff --git a/Telegram/SourceFiles/info/profile/info_profile_icon.h b/Telegram/SourceFiles/info/profile/info_profile_icon.h new file mode 100644 index 000000000..5049f5c00 --- /dev/null +++ b/Telegram/SourceFiles/info/profile/info_profile_icon.h @@ -0,0 +1,53 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "ui/rp_widget.h" + +namespace Info { +namespace Profile { + +class FloatingIcon : public Ui::RpWidget { +public: + FloatingIcon( + RpWidget *parent, + const style::icon &icon, + QPoint position); + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + struct Tag { + }; + FloatingIcon( + RpWidget *parent, + const style::icon &icon, + QPoint position, + const Tag &); + + not_null _icon; + QPoint _point; + +}; + +} // namespace Profile +} // namespace Info diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index db6158bd6..674661f4b 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -21,19 +21,23 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "info/profile/info_profile_inner_widget.h" #include -#include -#include +#include "info/profile/info_profile_button.h" +#include "info/profile/info_profile_widget.h" +#include "info/profile/info_profile_text.h" +#include "info/profile/info_profile_values.h" +#include "info/profile/info_profile_cover.h" +#include "info/profile/info_profile_icon.h" #include "boxes/abstract_box.h" #include "boxes/add_contact_box.h" #include "mainwidget.h" -#include "info/profile/info_profile_widget.h" -#include "info/profile/info_profile_lines.h" #include "window/window_controller.h" #include "storage/storage_shared_media.h" #include "lang/lang_keys.h" #include "styles/style_info.h" #include "ui/widgets/buttons.h" #include "ui/widgets/checkbox.h" +#include "ui/wrap/slide_wrap.h" +#include "ui/wrap/vertical_layout.h" namespace Info { namespace Profile { @@ -45,8 +49,11 @@ InnerWidget::InnerWidget( : RpWidget(parent) , _controller(controller) , _peer(peer) -, _content(this) { - setupContent(); +, _content(setupContent(this)) { + _content->heightValue() + | rpl::start([this](int height) { + TWidget::resizeToWidth(width()); + }, lifetime()); } bool InnerWidget::canHideDetailsEver() const { @@ -55,36 +62,38 @@ bool InnerWidget::canHideDetailsEver() const { rpl::producer InnerWidget::canHideDetails() const { using namespace rpl::mappers; - return MembersCountViewer(_peer) + return MembersCountValue(_peer) | rpl::map($1 > 0); } -void InnerWidget::setupContent() { - auto cover = _content->add(object_ptr( - this, +object_ptr InnerWidget::setupContent( + RpWidget *parent) const { + auto result = object_ptr(parent); + auto cover = result->add(object_ptr( + result, _peer) ); cover->setOnlineCount(rpl::single(0)); - auto details = setupDetails(_content); + auto details = setupDetails(parent); if (canHideDetailsEver()) { cover->setToggleShown(canHideDetails()); - _content->add(object_ptr>( - this, + result->add(object_ptr>( + result, std::move(details)) )->toggleOn(cover->toggledValue()); } else { - _content->add(std::move(details)); + result->add(std::move(details)); } - _content->add(setupSharedMedia(_content)); - _content->add(object_ptr(this)); + result->add(setupSharedMedia(result)); + result->add(object_ptr(result)); if (auto user = _peer->asUser()) { - _content->add(setupUserActions(_content, user)); + result->add(setupUserActions(result, user)); + //} else if (auto channel = _peer->asChannel()) { + // if (!channel->isMegagroup()) { + // setupChannelActions(result, channel); + // } } - - _content->heightValue() - | rpl::start([this](int height) { - TWidget::resizeToWidth(width()); - }, _lifetime); + return std::move(result); } object_ptr InnerWidget::setupDetails( @@ -114,7 +123,7 @@ object_ptr InnerWidget::setupInfo( rpl::producer &&text, bool selectByDoubleClick = false, const style::FlatLabel &textSt = st::infoLabeled) { - auto line = result->add(object_ptr( + auto line = result->add(CreateTextWithLabel( result, Lang::Viewer(label) | WithEmptyEntities(), std::move(text), @@ -134,12 +143,12 @@ object_ptr InnerWidget::setupInfo( st::infoLabeledOneLine); }; if (auto user = _peer->asUser()) { - addInfoOneLine(lng_info_mobile_label, PhoneViewer(user)); - addInfoLine(lng_info_bio_label, BioViewer(user)); - addInfoOneLine(lng_info_username_label, UsernameViewer(user)); + addInfoOneLine(lng_info_mobile_label, PhoneValue(user)); + addInfoLine(lng_info_bio_label, BioValue(user)); + addInfoOneLine(lng_info_username_label, UsernameValue(user)); } else { - addInfoOneLine(lng_info_link_label, LinkViewer(_peer)); - addInfoLine(lng_info_about_label, AboutViewer(_peer)); + addInfoOneLine(lng_info_link_label, LinkValue(_peer)); + addInfoLine(lng_info_about_label, AboutValue(_peer)); } result->add(object_ptr>( result, @@ -160,7 +169,7 @@ object_ptr InnerWidget::setupMuteToggle( Lang::Viewer(lng_profile_enable_notifications), st::infoNotificationsButton); result->toggleOn( - NotificationsEnabledViewer(_peer) + NotificationsEnabledValue(_peer) )->clicks() | rpl::start([this](auto) { App::main()->updateNotifySetting( @@ -207,7 +216,7 @@ void InnerWidget::setupUserButtons( addButton( Lang::Viewer(lng_info_add_as_contact) | ToUpperValue() )->toggleOn( - CanAddContactViewer(user) + CanAddContactValue(user) )->entity()->clicks() | rpl::start([user](auto&&) { auto firstName = user->firstName; @@ -230,8 +239,7 @@ object_ptr InnerWidget::setupSharedMedia( auto addButton = [&]( rpl::producer &&count, auto textFromCount) { - auto forked = rpl::single(0) - | rpl::then(std::move(count)) + auto forked = std::move(count) | start_spawning(content->lifetime()); auto button = content->add(object_ptr>( content, @@ -265,14 +273,14 @@ object_ptr InnerWidget::setupSharedMedia( }; auto addMediaButton = [&](MediaType type) { return addButton( - SharedMediaCountViewer(_peer, type), + SharedMediaCountValue(_peer, type), [phrase = mediaText(type)](int count) { return phrase(lt_count, count); }); }; auto addCommonGroupsButton = [&](not_null user) { return addButton( - CommonGroupsCountViewer(user), + CommonGroupsCountValue(user), [](int count) { return lng_profile_common_groups(lt_count, count); }); diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h index 5a2878053..4edf08b83 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.h @@ -64,7 +64,7 @@ class InnerWidget final : public Ui::RpWidget { int visibleBottom) override; private: - void setupContent(); + object_ptr setupContent(RpWidget *parent) const; object_ptr setupDetails(RpWidget *parent) const; object_ptr setupSharedMedia(RpWidget *parent) const; object_ptr setupMuteToggle(RpWidget *parent) const; @@ -90,9 +90,7 @@ class InnerWidget final : public Ui::RpWidget { int _visibleBottom = 0; int _minHeight = 0; - object_ptr _content; - - rpl::lifetime _lifetime; + object_ptr _content; }; diff --git a/Telegram/SourceFiles/info/profile/info_profile_lines.cpp b/Telegram/SourceFiles/info/profile/info_profile_lines.cpp deleted file mode 100644 index 160cecdcd..000000000 --- a/Telegram/SourceFiles/info/profile/info_profile_lines.cpp +++ /dev/null @@ -1,771 +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 "info/profile/info_profile_lines.h" - -#include -#include -#include -#include -#include -#include "styles/style_info.h" -#include "profile/profile_userpic_button.h" -#include "history/history_shared_media.h" -#include "observer_peer.h" -#include "auth_session.h" -#include "apiwrap.h" -#include "messenger.h" -#include "ui/widgets/labels.h" -#include "ui/widgets/checkbox.h" -#include "ui/effects/ripple_animation.h" -#include "lang/lang_keys.h" - -namespace Info { -namespace Profile { -namespace { - -auto MembersStatusText(int count) { - return lng_chat_status_members(lt_count, count); -}; - -auto OnlineStatusText(int count) { - return lng_chat_status_online(lt_count, count); -}; - -auto ChatStatusText(int fullCount, int onlineCount, bool isGroup) { - if (onlineCount > 0 && onlineCount <= fullCount) { - return lng_chat_status_members_online( - lt_members_count, MembersStatusText(fullCount), - lt_online_count, OnlineStatusText(onlineCount)); - } else if (fullCount > 0) { - return lng_chat_status_members(lt_count, fullCount); - } - return lang(isGroup - ? lng_group_status - : lng_channel_status); -}; - -} // namespace - -rpl::producer PeerUpdateViewer( - Notify::PeerUpdate::Flags flags) { - return [=](const rpl::consumer &consumer) { - auto lifetime = rpl::lifetime(); - lifetime.make_state( - Notify::PeerUpdated().add_subscription({ flags, [=]( - const Notify::PeerUpdate &update) { - consumer.put_next_copy(update); - }})); - return lifetime; - }; -} - -rpl::producer PeerUpdateViewer( - not_null peer, - Notify::PeerUpdate::Flags flags) { - return PeerUpdateViewer(flags) - | rpl::filter([=](const Notify::PeerUpdate &update) { - return (update.peer == peer); - }); -} - -rpl::producer PeerUpdateValue( - not_null peer, - Notify::PeerUpdate::Flags flags) { - return rpl::single(Notify::PeerUpdate()) - | then(PeerUpdateViewer(peer, flags)); -} - -rpl::producer PhoneViewer( - not_null user) { - return PeerUpdateValue( - user, - Notify::PeerUpdate::Flag::UserPhoneChanged) - | rpl::map([user](auto&&) { - return App::formatPhone(user->phone()); - }) - | WithEmptyEntities(); -} - -rpl::producer BioViewer( - not_null user) { - return PeerUpdateValue( - user, - Notify::PeerUpdate::Flag::AboutChanged) - | rpl::map([user](auto&&) { return user->about(); }) - | WithEmptyEntities(); -} - -rpl::producer PlainUsernameViewer( - not_null peer) { - return PeerUpdateValue( - peer, - Notify::PeerUpdate::Flag::UsernameChanged) - | rpl::map([peer](auto&&) { - return peer->userName(); - }); -} - -rpl::producer UsernameViewer( - not_null user) { - return PlainUsernameViewer(user) - | rpl::map([](QString &&username) { - return username.isEmpty() - ? QString() - : ('@' + username); - }) - | WithEmptyEntities(); -} - -rpl::producer AboutViewer( - not_null peer) { - if (auto channel = peer->asChannel()) { - return PeerUpdateValue( - channel, - Notify::PeerUpdate::Flag::AboutChanged) - | rpl::map([channel](auto&&) { return channel->about(); }) - | WithEmptyEntities(); - } - return rpl::single(TextWithEntities{}); -} - -rpl::producer LinkViewer( - not_null peer) { - return PlainUsernameViewer(peer) - | rpl::map([](QString &&username) { - return username.isEmpty() - ? QString() - : Messenger::Instance().createInternalLink(username); - }) - | WithEmptyEntities(); -} - -rpl::producer NotificationsEnabledViewer( - not_null peer) { - return PeerUpdateValue( - peer, - Notify::PeerUpdate::Flag::NotificationsEnabled) - | rpl::map([peer](auto&&) { return !peer->isMuted(); }); -} - -rpl::producer IsContactViewer( - not_null user) { - return PeerUpdateValue( - user, - Notify::PeerUpdate::Flag::UserIsContact) - | rpl::map([user](auto&&) { return user->isContact(); }); -} - -rpl::producer CanShareContactViewer( - not_null user) { - return PeerUpdateValue( - user, - Notify::PeerUpdate::Flag::UserCanShareContact) - | rpl::map([user](auto&&) { - return user->canShareThisContact(); - }); -} - -rpl::producer CanAddContactViewer( - not_null user) { - using namespace rpl::mappers; - return rpl::combine( - IsContactViewer(user), - CanShareContactViewer(user), - !$1 && $2); -} - -rpl::producer MembersCountViewer( - not_null peer) { - if (auto chat = peer->asChat()) { - return PeerUpdateValue( - peer, - Notify::PeerUpdate::Flag::MembersChanged) - | rpl::map([chat](auto&&) { - return chat->amIn() - ? qMax(chat->count, chat->participants.size()) - : 0; - }); - } else if (auto channel = peer->asChannel()) { - return PeerUpdateValue( - peer, - Notify::PeerUpdate::Flag::MembersChanged) - | rpl::map([channel](auto &&) { - auto canViewCount = channel->canViewMembers() - || !channel->isMegagroup(); - return canViewCount - ? qMax(channel->membersCount(), 1) - : 0; - }); - } - Unexpected("User in MembersCountViewer()."); -} - -rpl::producer SharedMediaCountViewer( - not_null peer, - Storage::SharedMediaType type) { - auto initial = peer->migrateFrom() ? peer->migrateFrom() : peer; - auto migrated = initial->migrateTo(); - auto aroundId = 0; - auto limit = 0; - return SharedMediaMergedViewer( - SharedMediaMergedSlice::Key( - peer->id, - migrated ? migrated->id : 0, - type, - aroundId), - limit, - limit) - | rpl::map([](const SharedMediaMergedSlice &slice) { - return slice.fullCount(); - }) - | rpl::filter_optional(); -} - -rpl::producer CommonGroupsCountViewer( - not_null user) { - return PeerUpdateValue( - user, - Notify::PeerUpdate::Flag::UserCommonChatsChanged) - | rpl::map([user](auto&&) { - return user->commonChatsCount(); - }); -} - -FloatingIcon::FloatingIcon( - RpWidget *parent, - const style::icon &icon, - QPoint position) -: FloatingIcon(parent, icon, position, Tag{}) { -} - -FloatingIcon::FloatingIcon( - RpWidget *parent, - const style::icon &icon, - QPoint position, - const Tag &) -: RpWidget(parent) -, _icon(&icon) -, _point(position) { - resize( - _point.x() + _icon->width(), - _point.y() + _icon->height()); - setAttribute(Qt::WA_TransparentForMouseEvents); - parent->widthValue() - | rpl::start( - [this](auto&&) { moveToLeft(0, 0); }, - lifetime()); -} - -void FloatingIcon::paintEvent(QPaintEvent *e) { - Painter p(this); - _icon->paint(p, _point, width()); -} - -LabeledLine::LabeledLine( - QWidget *parent, - rpl::producer &&label, - rpl::producer &&text) -: LabeledLine( - parent, - std::move(label), - std::move(text), - st::infoLabeledOneLine, - st::infoProfileLabeledPadding, - true) { -} - -LabeledLine::LabeledLine( - QWidget *parent, - rpl::producer &&label, - rpl::producer &&text, - const style::FlatLabel &textSt, - const style::margins &padding, - bool doubleClickSelects) -: SlideWrap( - parent, - object_ptr(parent), - padding -) { - auto layout = entity(); - auto nonEmptyText = std::move(text) - | rpl::before_next([this](const TextWithEntities &value) { - if (value.text.isEmpty()) { - hideAnimated(); - } - }) - | rpl::filter([this](const TextWithEntities &value) { - return !value.text.isEmpty(); - }) - | rpl::after_next([this](const TextWithEntities &value) { - showAnimated(); - }); - auto labeled = layout->add(object_ptr( - this, - std::move(nonEmptyText), - textSt)); - labeled->setSelectable(true); - labeled->setDoubleClickSelectsParagraph(doubleClickSelects); - layout->add(Ui::CreateSkipWidget(this, st::infoLabelSkip)); - layout->add(object_ptr( - this, - std::move(label), - st::infoLabel)); - finishAnimations(); -}; - -Cover::Cover(QWidget *parent, not_null peer) -: FixedHeightWidget( - parent, - st::infoProfilePhotoTop - + st::infoProfilePhotoSize - + st::infoProfilePhotoBottom) -, _peer(peer) -, _userpic(this, _peer, st::infoProfilePhotoSize) -, _name(this, st::infoProfileNameLabel) -, _status(this, st::infoProfileStatusLabel) { - _peer->updateFull(); - - _name->setSelectable(true); - _status->setAttribute(Qt::WA_TransparentForMouseEvents); - - initViewers(); - initUserpicButton(); - refreshNameText(); - refreshStatusText(); - setupChildGeometry(); -} - -void Cover::setupChildGeometry() { - widthValue() - | rpl::start([this](int newWidth) { - _userpic->moveToLeft( - st::infoProfilePhotoLeft, - st::infoProfilePhotoTop, - newWidth); - refreshNameGeometry(newWidth); - refreshStatusGeometry(newWidth); - }, lifetime()); -} - -Cover *Cover::setOnlineCount(rpl::producer &&count) { - std::move(count) - | rpl::start([this](int count) { - _onlineCount = count; - refreshStatusText(); - }, lifetime()); - return this; -} - -Cover *Cover::setToggleShown(rpl::producer &&shown) { - _toggle.create( - this, - QString(), - st::infoToggleCheckbox, - std::make_unique( - st::infoToggle, - false, - [this] { _toggle->updateCheck(); })); - _toggle->lower(); - _toggle->setCheckAlignment(style::al_right); - widthValue() - | rpl::start([this](int newValue) { - _toggle->setGeometry(0, 0, newValue, height()); - }, _toggle->lifetime()); - std::move(shown) - | rpl::start([this](bool shown) { - if (_toggle->isHidden() == shown) { - _toggle->setVisible(shown); - } - }, lifetime()); - return this; -} - -void Cover::initViewers() { - using Flag = Notify::PeerUpdate::Flag; - PeerUpdateViewer(_peer, Flag::PhotoChanged) - | rpl::start( - [this](auto&&) { this->refreshUserpicLink(); }, - lifetime()); - PeerUpdateViewer(_peer, Flag::NameChanged) - | rpl::start( - [this](auto&&) { this->refreshNameText(); }, - lifetime()); - PeerUpdateViewer(_peer, - Flag::UserOnlineChanged | Flag::MembersChanged) - | rpl::start( - [this](auto&&) { this->refreshStatusText(); }, - lifetime()); -} - -void Cover::initUserpicButton() { - _userpic->setClickedCallback([this] { - auto hasPhoto = (_peer->photoId != 0); - auto knownPhoto = (_peer->photoId != UnknownPeerPhotoId); - if (hasPhoto && knownPhoto) { - if (auto photo = App::photo(_peer->photoId)) { - if (photo->date) { - Messenger::Instance().showPhoto(photo, _peer); - } - } - } - }); - refreshUserpicLink(); -} - -void Cover::refreshUserpicLink() { - auto hasPhoto = (_peer->photoId != 0); - auto knownPhoto = (_peer->photoId != UnknownPeerPhotoId); - _userpic->setPointerCursor(hasPhoto && knownPhoto); - if (!knownPhoto) { - Auth().api().requestFullPeer(_peer); - } -} - -void Cover::refreshNameText() { - _name->setText(App::peerName(_peer)); - refreshNameGeometry(width()); -} - -void Cover::refreshStatusText() { - auto statusText = [this] { - auto currentTime = unixtime(); - if (auto user = _peer->asUser()) { - auto result = App::onlineText(user, currentTime, true); - return App::onlineColorUse(user, currentTime) - ? textcmdLink(1, result) - : result; - } else if (auto chat = _peer->asChat()) { - if (!chat->amIn()) { - return lang(lng_chat_status_unaccessible); - } - auto fullCount = qMax( - chat->count, - chat->participants.size()); - return ChatStatusText(fullCount, _onlineCount, true); - } else if (auto channel = _peer->asChannel()) { - auto fullCount = qMax(channel->membersCount(), 1); - return ChatStatusText( - fullCount, - _onlineCount, - channel->isMegagroup()); - } - return lang(lng_chat_status_unaccessible); - }(); - _status->setRichText(statusText); - refreshStatusGeometry(width()); -} - -void Cover::refreshNameGeometry(int newWidth) { - auto nameWidth = newWidth - - st::infoProfileNameLeft - - st::infoProfileNameRight; - if (_toggle) { - nameWidth -= st::infoToggleCheckbox.checkPosition.x() - + _toggle->checkRect().width(); - } - _name->resizeToWidth(nameWidth); - _name->moveToLeft( - st::infoProfileNameLeft, - st::infoProfileNameTop, - newWidth); -} - -void Cover::refreshStatusGeometry(int newWidth) { - auto statusWidth = newWidth - - st::infoProfileStatusLeft - - st::infoProfileStatusRight; - if (_toggle) { - statusWidth -= st::infoToggleCheckbox.checkPosition.x() - + _toggle->checkRect().width(); - } - _status->resizeToWidth(statusWidth); - _status->moveToLeft( - st::infoProfileStatusLeft, - st::infoProfileStatusTop, - newWidth); -} - -rpl::producer Cover::toggledValue() const { - return _toggle - ? (rpl::single(_toggle->checked()) - | rpl::then( - base::ObservableViewer(_toggle->checkedChanged))) - : rpl::never(); -} - -QMargins SharedMediaCover::getMargins() const { - return QMargins(0, 0, 0, st::infoSharedMediaBottomSkip); -} - -SharedMediaCover::SharedMediaCover(QWidget *parent) -: FixedHeightWidget(parent, st::infoSharedMediaCoverHeight) { - createLabel(); -} - -void SharedMediaCover::createLabel() { - auto label = object_ptr( - this, - Lang::Viewer(lng_profile_shared_media) | ToUpperValue(), - st::infoSharedMediaLabel); - label->setAttribute(Qt::WA_TransparentForMouseEvents); - widthValue() - | rpl::start([weak = label.data()](int newWidth) { - weak->resizeToNaturalWidth(newWidth - - st::infoSharedMediaLabelPosition.x() - - st::infoSharedMediaButton.padding.right()); - weak->moveToLeft( - st::infoSharedMediaLabelPosition.x(), - st::infoSharedMediaLabelPosition.y(), - newWidth); - }, label->lifetime()); -} - -SharedMediaCover *SharedMediaCover::setToggleShown( - rpl::producer &&shown) { - _toggle.create( - this, - QString(), - st::infoToggleCheckbox, - std::make_unique( - st::infoToggle, - false, - [this] { _toggle->updateCheck(); })); - _toggle->lower(); - _toggle->setCheckAlignment(style::al_right); - widthValue() - | rpl::start([this](int newValue) { - _toggle->setGeometry(0, 0, newValue, height()); - }, _toggle->lifetime()); - std::move(shown) - | rpl::start([this](bool shown) { - if (_toggle->isHidden() == shown) { - _toggle->setVisible(shown); - } - }, lifetime()); - return this; -} - -rpl::producer SharedMediaCover::toggledValue() const { - return _toggle - ? (rpl::single(_toggle->checked()) - | rpl::then( - base::ObservableViewer(_toggle->checkedChanged))) - : rpl::never(); -} - -Button::Button( - QWidget *parent, - rpl::producer &&text) -: Button(parent, std::move(text), st::infoProfileButton) { -} - -Button::Button( - QWidget *parent, - rpl::producer &&text, - const style::InfoProfileButton &st) -: RippleButton(parent, st.ripple) -, _st(st) { - std::move(text) - | rpl::start([this](QString &&value) { - setText(std::move(value)); - }, lifetime()); -} - -Button *Button::toggleOn(rpl::producer &&toggled) { - _toggleOnLifetime.destroy(); - _toggle = std::make_unique( - isOver() ? _st.toggleOver : _st.toggle, - false, - [this] { rtlupdate(toggleRect()); }); - clicks() - | rpl::start([this](auto) { - _toggle->setCheckedAnimated(!_toggle->checked()); - }, _toggleOnLifetime); - std::move(toggled) - | rpl::start([this](bool toggled) { - _toggle->setCheckedAnimated(toggled); - }, _toggleOnLifetime); - _toggle->finishAnimation(); - return this; -} - -rpl::producer Button::toggledValue() const { - return _toggle ? _toggle->checkedValue() : rpl::never(); -} - -void Button::paintEvent(QPaintEvent *e) { - Painter p(this); - - auto ms = getms(); - auto paintOver = (isOver() || isDown()); - p.fillRect(e->rect(), paintOver ? _st.textBgOver : _st.textBg); - - paintRipple(p, 0, 0, ms); - - auto outerw = width(); - p.setFont(_st.font); - p.setPen(paintOver ? _st.textFgOver : _st.textFg); - p.drawTextLeft( - _st.padding.left(), - _st.padding.top(), - outerw, - _text, - _textWidth); - - if (_toggle) { - auto rect = toggleRect(); - _toggle->paint(p, rect.left(), rect.top(), outerw, ms); - } -} - -QRect Button::toggleRect() const { - Expects(_toggle != nullptr); - auto size = _toggle->getSize(); - auto left = width() - _st.toggleSkip - size.width(); - auto top = (height() - size.height()) / 2; - return { QPoint(left, top), size }; -} - -int Button::resizeGetHeight(int newWidth) { - updateVisibleText(newWidth); - return _st.padding.top() + _st.height + _st.padding.bottom(); -} - -void Button::onStateChanged( - State was, - StateChangeSource source) { - RippleButton::onStateChanged(was, source); - if (_toggle) { - _toggle->setStyle(isOver() ? _st.toggleOver : _st.toggle); - } -} - -void Button::setText(QString &&text) { - _original = std::move(text); - _originalWidth = _st.font->width(_original); - updateVisibleText(width()); -} - -void Button::updateVisibleText(int newWidth) { - auto availableWidth = newWidth - - _st.padding.left() - - _st.padding.right(); - if (_toggle) { - availableWidth -= (width() - toggleRect().x()); - } - accumulate_max(availableWidth, 0); - if (availableWidth < _originalWidth) { - _text = _st.font->elided(_original, availableWidth); - _textWidth = _st.font->width(_text); - } else { - _text = _original; - _textWidth = _originalWidth; - } - update(); -} - -rpl::producer MultiLineTracker::atLeastOneShownValue() const { - auto shown = std::vector>(); - shown.reserve(_widgets.size()); - for (auto &widget : _widgets) { - shown.push_back(widget->shownValue()); - } - return rpl::combine( - std::move(shown), - [](const std::vector &values) { - return base::find(values, true) != values.end(); - }); -} - -SectionToggle::SectionToggle( - const style::InfoToggle &st, - bool checked, - base::lambda updateCallback) -: AbstractCheckView(st.duration, checked, std::move(updateCallback)) -, _st(st) { -} - -QSize SectionToggle::getSize() const { - return QSize(_st.size, _st.size); -} - -void SectionToggle::paint( - Painter &p, - int left, - int top, - int outerWidth, - TimeMs ms) { - auto sqrt2 = sqrt(2.); - auto vLeft = rtlpoint(left + _st.skip, 0, outerWidth).x() + 0.; - auto vTop = top + _st.skip + 0.; - auto vWidth = _st.size - 2 * _st.skip; - auto vHeight = _st.size - 2 * _st.skip; - auto vStroke = _st.stroke / sqrt2; - constexpr auto kPointCount = 6; - std::array pathV = { { - { vLeft, vTop + (vHeight / 4.) + vStroke }, - { vLeft + vStroke, vTop + (vHeight / 4.) }, - { vLeft + (vWidth / 2.), vTop + (vHeight * 3. / 4.) - vStroke }, - { vLeft + vWidth - vStroke, vTop + (vHeight / 4.) }, - { vLeft + vWidth, vTop + (vHeight / 4.) + vStroke }, - { vLeft + (vWidth / 2.), vTop + (vHeight * 3. / 4.) + vStroke }, - } }; - - auto toggled = currentAnimationValue(ms); - auto alpha = (toggled - 1.) * M_PI_2; - auto cosalpha = cos(alpha); - auto sinalpha = sin(alpha); - auto shiftx = vLeft + (vWidth / 2.); - auto shifty = vTop + (vHeight / 2.); - for (auto &point : pathV) { - auto x = point.x() - shiftx; - auto y = point.y() - shifty; - point.setX(shiftx + x * cosalpha - y * sinalpha); - point.setY(shifty + y * cosalpha + x * sinalpha); - } - QPainterPath path; - path.moveTo(pathV[0]); - for (int i = 1; i != kPointCount; ++i) { - path.lineTo(pathV[i]); - } - path.lineTo(pathV[0]); - - PainterHighQualityEnabler hq(p); - p.fillPath(path, _st.color); -} - -QImage SectionToggle::prepareRippleMask() const { - return Ui::RippleAnimation::ellipseMask(rippleSize()); -} - -QSize SectionToggle::rippleSize() const { - return getSize() + 2 * QSize( - _st.rippleAreaPadding, - _st.rippleAreaPadding); -} - -bool SectionToggle::checkRippleStartPosition(QPoint position) const { - return QRect(QPoint(0, 0), rippleSize()).contains(position); - -} - -} // namespace Profile -} // namespace Info diff --git a/Telegram/SourceFiles/info/profile/info_profile_lines.h b/Telegram/SourceFiles/info/profile/info_profile_lines.h deleted file mode 100644 index 4dc4c609c..000000000 --- a/Telegram/SourceFiles/info/profile/info_profile_lines.h +++ /dev/null @@ -1,255 +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 -#include "ui/widgets/checkbox.h" -#include "ui/wrap/padding_wrap.h" -#include "ui/wrap/slide_wrap.h" -#include "ui/wrap/vertical_layout.h" -#include "ui/widgets/buttons.h" - -enum LangKey : int; - -namespace style { -struct FlatLabel; -struct InfoProfileButton; -struct InfoToggle; -} // namespace style - -namespace Lang { -rpl::producer Viewer(LangKey key); -} // namespace Lang - -namespace Ui { -class FlatLabel; -class Checkbox; -class IconButton; -class ToggleView; -} // namespace Ui - -namespace Profile { -class UserpicButton; -} // namespace Profile - -namespace Info { -namespace Profile { - -inline auto WithEmptyEntities() { - return rpl::map([](QString &&text) { - return TextWithEntities{ std::move(text), {} }; - }); -} - -inline auto ToUpperValue() { - return rpl::map([](QString &&text) { - return std::move(text).toUpper(); - }); -} - -rpl::producer PhoneViewer( - not_null user); -rpl::producer BioViewer( - not_null user); -rpl::producer UsernameViewer( - not_null user); -rpl::producer AboutViewer( - not_null peer); -rpl::producer LinkViewer( - not_null peer); -rpl::producer NotificationsEnabledViewer( - not_null peer); -rpl::producer IsContactViewer( - not_null user); -rpl::producer CanShareContactViewer( - not_null user); -rpl::producer CanAddContactViewer( - not_null user); -rpl::producer MembersCountViewer( - not_null peer); -rpl::producer SharedMediaCountViewer( - not_null peer, - Storage::SharedMediaType type); -rpl::producer CommonGroupsCountViewer( - not_null user); - -class FloatingIcon : public Ui::RpWidget { -public: - FloatingIcon( - RpWidget *parent, - const style::icon &icon, - QPoint position); - -protected: - void paintEvent(QPaintEvent *e) override; - -private: - struct Tag { - }; - FloatingIcon( - RpWidget *parent, - const style::icon &icon, - QPoint position, - const Tag &); - - not_null _icon; - QPoint _point; - -}; - -class LabeledLine : public Ui::SlideWrap { -public: - LabeledLine( - QWidget *parent, - rpl::producer &&label, - rpl::producer &&text); - - LabeledLine( - QWidget *parent, - rpl::producer &&label, - rpl::producer &&text, - const style::FlatLabel &textSt, - const style::margins &padding, - bool doubleClickSelects); - -}; - -class Cover : public Ui::FixedHeightWidget { -public: - Cover(QWidget *parent, not_null peer); - - Cover *setOnlineCount(rpl::producer &&count); - Cover *setToggleShown(rpl::producer &&shown); - rpl::producer toggledValue() const; - -private: - void setupChildGeometry(); - void initViewers(); - void initUserpicButton(); - void refreshUserpicLink(); - void refreshNameText(); - void refreshStatusText(); - void refreshNameGeometry(int newWidth); - void refreshStatusGeometry(int newWidth); - - not_null _peer; - int _onlineCount = 0; - - object_ptr<::Profile::UserpicButton> _userpic; - object_ptr _name = { nullptr }; - object_ptr _status = { nullptr }; - object_ptr _toggle = { nullptr }; - //object_ptr _dropArea = { nullptr }; - -}; - -class SharedMediaCover : public Ui::FixedHeightWidget { -public: - SharedMediaCover(QWidget *parent); - - SharedMediaCover *setToggleShown(rpl::producer &&shown); - rpl::producer toggledValue() const; - - QMargins getMargins() const override; - -private: - void createLabel(); - - object_ptr _toggle = { nullptr }; - -}; - -class Button : public Ui::RippleButton { -public: - Button( - QWidget *parent, - rpl::producer &&text); - Button( - QWidget *parent, - rpl::producer &&text, - const style::InfoProfileButton &st); - - Button *toggleOn(rpl::producer &&toggled); - rpl::producer toggledValue() const; - -protected: - int resizeGetHeight(int newWidth) override; - void onStateChanged( - State was, - StateChangeSource source) override; - - void paintEvent(QPaintEvent *e) override; - -private: - void setText(QString &&text); - QRect toggleRect() const; - void updateVisibleText(int newWidth); - - const style::InfoProfileButton &_st; - QString _original; - QString _text; - int _originalWidth = 0; - int _textWidth = 0; - std::unique_ptr _toggle; - rpl::lifetime _toggleOnLifetime; - -}; - -class MultiLineTracker { -public: - template - void track(const Ui::SlideWrap *wrap) { - _widgets.push_back(wrap); - } - - rpl::producer atLeastOneShownValue() const; - -private: - std::vector*> _widgets; - -}; - -class SectionToggle : public Ui::AbstractCheckView { -public: - SectionToggle( - const style::InfoToggle &st, - bool checked, - base::lambda updateCallback); - - QSize getSize() const override; - void paint( - Painter &p, - int left, - int top, - int outerWidth, - TimeMs ms) override; - QImage prepareRippleMask() const override; - bool checkRippleStartPosition(QPoint position) const override; - -private: - QSize rippleSize() const; - - const style::InfoToggle &_st; - -}; - -} // namespace Profile -} // namespace Info diff --git a/Telegram/SourceFiles/info/profile/info_profile_text.cpp b/Telegram/SourceFiles/info/profile/info_profile_text.cpp new file mode 100644 index 000000000..81709c28c --- /dev/null +++ b/Telegram/SourceFiles/info/profile/info_profile_text.cpp @@ -0,0 +1,76 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "info/profile/info_profile_text.h" + +#include +#include +#include +#include "ui/widgets/labels.h" +#include "ui/wrap/slide_wrap.h" +#include "ui/wrap/vertical_layout.h" +#include "styles/style_info.h" + +namespace Info { +namespace Profile { + +object_ptr> CreateTextWithLabel( + QWidget *parent, + rpl::producer &&label, + rpl::producer &&text, + const style::FlatLabel &textSt, + const style::margins &padding, + bool doubleClickSelects) { + auto result = object_ptr>( + parent, + object_ptr(parent), + padding); + auto layout = result->entity(); + auto nonEmptyText = std::move(text) + | rpl::before_next([slide = result.data()]( + const TextWithEntities &value) { + if (value.text.isEmpty()) { + slide->hideAnimated(); + } + }) + | rpl::filter([](const TextWithEntities &value) { + return !value.text.isEmpty(); + }) + | rpl::after_next([slide = result.data()]( + const TextWithEntities &value) { + slide->showAnimated(); + }); + auto labeled = layout->add(object_ptr( + layout, + std::move(nonEmptyText), + textSt)); + labeled->setSelectable(true); + labeled->setDoubleClickSelectsParagraph(doubleClickSelects); + layout->add(Ui::CreateSkipWidget(layout, st::infoLabelSkip)); + layout->add(object_ptr( + layout, + std::move(label), + st::infoLabel)); + result->finishAnimations(); + return result; +} + +} // namespace Profile +} // namespace Info diff --git a/Telegram/SourceFiles/info/profile/info_profile_text.h b/Telegram/SourceFiles/info/profile/info_profile_text.h new file mode 100644 index 000000000..99d181adc --- /dev/null +++ b/Telegram/SourceFiles/info/profile/info_profile_text.h @@ -0,0 +1,47 @@ +/* +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 + +namespace style { +struct FlatLabel; +} // namespace style + +namespace Ui { +class VerticalLayout; +template +class SlideWrap; +} // namespace Ui + +namespace Info { +namespace Profile { + +object_ptr> CreateTextWithLabel( + QWidget *parent, + rpl::producer &&label, + rpl::producer &&text, + const style::FlatLabel &textSt, + const style::margins &padding, + bool doubleClickSelects); + +} // namespace Profile +} // namespace Info diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp new file mode 100644 index 000000000..f57c76e7a --- /dev/null +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -0,0 +1,237 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "info/profile/info_profile_values.h" + +#include +#include +#include +#include +#include "observer_peer.h" +#include "messenger.h" +#include "ui/wrap/slide_wrap.h" +#include "history/history_shared_media.h" + +namespace Info { +namespace Profile { + +rpl::producer PeerUpdateViewer( + Notify::PeerUpdate::Flags flags) { + return [=](const rpl::consumer &consumer) { + auto lifetime = rpl::lifetime(); + lifetime.make_state( + Notify::PeerUpdated().add_subscription({ flags, [=]( + const Notify::PeerUpdate &update) { + consumer.put_next_copy(update); + }})); + return lifetime; + }; +} + +rpl::producer PeerUpdateViewer( + not_null peer, + Notify::PeerUpdate::Flags flags) { + return PeerUpdateViewer(flags) + | rpl::filter([=](const Notify::PeerUpdate &update) { + return (update.peer == peer); + }); +} + +rpl::producer PeerUpdateValue( + not_null peer, + Notify::PeerUpdate::Flags flags) { + auto initial = Notify::PeerUpdate(peer); + initial.flags = flags; + return rpl::single(initial) + | then(PeerUpdateViewer(peer, flags)); +} + +rpl::producer PhoneValue( + not_null user) { + return PeerUpdateValue( + user, + Notify::PeerUpdate::Flag::UserPhoneChanged) + | rpl::map([user](auto&&) { + return App::formatPhone(user->phone()); + }) + | WithEmptyEntities(); +} + +rpl::producer BioValue( + not_null user) { + return PeerUpdateValue( + user, + Notify::PeerUpdate::Flag::AboutChanged) + | rpl::map([user](auto&&) { return user->about(); }) + | WithEmptyEntities(); +} + +rpl::producer PlainUsernameViewer( + not_null peer) { + return PeerUpdateValue( + peer, + Notify::PeerUpdate::Flag::UsernameChanged) + | rpl::map([peer](auto&&) { + return peer->userName(); + }); +} + +rpl::producer UsernameValue( + not_null user) { + return PlainUsernameViewer(user) + | rpl::map([](QString &&username) { + return username.isEmpty() + ? QString() + : ('@' + username); + }) + | WithEmptyEntities(); +} + +rpl::producer AboutValue( + not_null peer) { + if (auto channel = peer->asChannel()) { + return PeerUpdateValue( + channel, + Notify::PeerUpdate::Flag::AboutChanged) + | rpl::map([channel](auto&&) { return channel->about(); }) + | WithEmptyEntities(); + } + return rpl::single(TextWithEntities{}); +} + +rpl::producer LinkValue( + not_null peer) { + return PlainUsernameViewer(peer) + | rpl::map([](QString &&username) { + return username.isEmpty() + ? QString() + : Messenger::Instance().createInternalLink(username); + }) + | WithEmptyEntities(); +} + +rpl::producer NotificationsEnabledValue( + not_null peer) { + return PeerUpdateValue( + peer, + Notify::PeerUpdate::Flag::NotificationsEnabled) + | rpl::map([peer](auto&&) { return !peer->isMuted(); }); +} + +rpl::producer IsContactValue( + not_null user) { + return PeerUpdateValue( + user, + Notify::PeerUpdate::Flag::UserIsContact) + | rpl::map([user](auto&&) { return user->isContact(); }); +} + +rpl::producer CanShareContactValue( + not_null user) { + return PeerUpdateValue( + user, + Notify::PeerUpdate::Flag::UserCanShareContact) + | rpl::map([user](auto&&) { + return user->canShareThisContact(); + }); +} + +rpl::producer CanAddContactValue( + not_null user) { + using namespace rpl::mappers; + return rpl::combine( + IsContactValue(user), + CanShareContactValue(user), + !$1 && $2); +} + +rpl::producer MembersCountValue( + not_null peer) { + if (auto chat = peer->asChat()) { + return PeerUpdateValue( + peer, + Notify::PeerUpdate::Flag::MembersChanged) + | rpl::map([chat](auto&&) { + return chat->amIn() + ? qMax(chat->count, chat->participants.size()) + : 0; + }); + } else if (auto channel = peer->asChannel()) { + return PeerUpdateValue( + peer, + Notify::PeerUpdate::Flag::MembersChanged) + | rpl::map([channel](auto &&) { + auto canViewCount = channel->canViewMembers() + || !channel->isMegagroup(); + return canViewCount + ? qMax(channel->membersCount(), 1) + : 0; + }); + } + Unexpected("User in MembersCountViewer()."); +} + +rpl::producer SharedMediaCountValue( + not_null peer, + Storage::SharedMediaType type) { + auto initial = peer->migrateFrom() ? peer->migrateFrom() : peer; + auto migrated = initial->migrateTo(); + auto aroundId = 0; + auto limit = 0; + auto updated = SharedMediaMergedViewer( + SharedMediaMergedSlice::Key( + peer->id, + migrated ? migrated->id : 0, + type, + aroundId), + limit, + limit) + | rpl::map([](const SharedMediaMergedSlice &slice) { + return slice.fullCount(); + }) + | rpl::filter_optional(); + return rpl::single(0) | rpl::then(std::move(updated)); +} + +rpl::producer CommonGroupsCountValue( + not_null user) { + return PeerUpdateValue( + user, + Notify::PeerUpdate::Flag::UserCommonChatsChanged) + | rpl::map([user](auto&&) { + return user->commonChatsCount(); + }); +} + +rpl::producer MultiLineTracker::atLeastOneShownValue() const { + auto shown = std::vector>(); + shown.reserve(_widgets.size()); + for (auto &widget : _widgets) { + shown.push_back(widget->shownValue()); + } + return rpl::combine( + std::move(shown), + [](const std::vector &values) { + return base::find(values, true) != values.end(); + }); +} + +} // namespace Profile +} // namespace Info diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.h b/Telegram/SourceFiles/info/profile/info_profile_values.h new file mode 100644 index 000000000..0440e3e41 --- /dev/null +++ b/Telegram/SourceFiles/info/profile/info_profile_values.h @@ -0,0 +1,93 @@ +/* +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 +#include +#include "observer_peer.h" + +namespace Ui { +class RpWidget; +template +class SlideWrap; +} // namespace Ui + +namespace Info { +namespace Profile { + +inline auto WithEmptyEntities() { + return rpl::map([](QString &&text) { + return TextWithEntities{ std::move(text), {} }; + }); +} + +inline auto ToUpperValue() { + return rpl::map([](QString &&text) { + return std::move(text).toUpper(); + }); +} + +rpl::producer PeerUpdateValue( + not_null peer, + Notify::PeerUpdate::Flags flags); + +rpl::producer PhoneValue( + not_null user); +rpl::producer BioValue( + not_null user); +rpl::producer UsernameValue( + not_null user); +rpl::producer AboutValue( + not_null peer); +rpl::producer LinkValue( + not_null peer); +rpl::producer NotificationsEnabledValue( + not_null peer); +rpl::producer IsContactValue( + not_null user); +rpl::producer CanShareContactValue( + not_null user); +rpl::producer CanAddContactValue( + not_null user); +rpl::producer MembersCountValue( + not_null peer); +rpl::producer SharedMediaCountValue( + not_null peer, + Storage::SharedMediaType type); +rpl::producer CommonGroupsCountValue( + not_null user); + +class MultiLineTracker { +public: + template + void track(const Ui::SlideWrap *wrap) { + _widgets.push_back(wrap); + } + + rpl::producer atLeastOneShownValue() const; + +private: + std::vector*> _widgets; + +}; + +} // namespace Profile +} // namespace Info diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index f9e17edf2..888b3d28b 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -211,10 +211,20 @@ <(src_loc)/info/info_side_wrap.h <(src_loc)/info/info_top_bar.cpp <(src_loc)/info/info_top_bar.h +<(src_loc)/info/profile/info_profile_button.cpp +<(src_loc)/info/profile/info_profile_button.h +<(src_loc)/info/profile/info_profile_cover.cpp +<(src_loc)/info/profile/info_profile_cover.h +<(src_loc)/info/profile/info_profile_icon.cpp +<(src_loc)/info/profile/info_profile_icon.h <(src_loc)/info/profile/info_profile_inner_widget.cpp <(src_loc)/info/profile/info_profile_inner_widget.h -<(src_loc)/info/profile/info_profile_lines.cpp -<(src_loc)/info/profile/info_profile_lines.h +<(src_loc)/info/profile/info_profile_members.cpp +<(src_loc)/info/profile/info_profile_members.h +<(src_loc)/info/profile/info_profile_text.cpp +<(src_loc)/info/profile/info_profile_text.h +<(src_loc)/info/profile/info_profile_values.cpp +<(src_loc)/info/profile/info_profile_values.h <(src_loc)/info/profile/info_profile_widget.cpp <(src_loc)/info/profile/info_profile_widget.h <(src_loc)/inline_bots/inline_bot_layout_internal.cpp