Start new Info section (profile + shared media).

This commit is contained in:
John Preston 2017-09-13 20:01:23 +03:00
parent fbcd5e2f1e
commit 088d23d557
80 changed files with 3969 additions and 63 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -553,16 +553,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_recent_actions" = "Recent actions";
"lng_profile_common_groups#one" = "{count} group in common";
"lng_profile_common_groups#other" = "{count} groups in common";
"lng_profile_common_groups_section" = "Groups in common";
"lng_profile_participants_section" = "Members";
"lng_profile_info_section" = "Info";
"lng_profile_mobile_number" = "Mobile:";
"lng_profile_username" = "Username:";
"lng_profile_link" = "Link:";
"lng_profile_bio" = "Bio:";
"lng_profile_add_contact" = "Add Contact";
"lng_profile_edit_contact" = "Edit";
"lng_profile_enable_notifications" = "Notifications";
"lng_profile_clear_history" = "Clear history";
"lng_profile_delete_conversation" = "Delete conversation";
"lng_profile_clear_and_exit" = "Delete and exit";
@ -576,7 +573,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_unblock_user" = "Unblock user";
"lng_profile_block_bot" = "Stop and block bot";
"lng_profile_unblock_bot" = "Unblock bot";
"lng_profile_send_message" = "Send Message";
"lng_profile_share_contact" = "Share Contact";
"lng_profile_invite_to_group" = "Add to Group";
"lng_profile_delete_contact" = "Delete";
@ -589,7 +585,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_sure_kick" = "Remove {user} from the group?";
"lng_profile_sure_kick_channel" = "Remove {user} from the channel?";
"lng_profile_loading" = "Loading...";
"lng_profile_shared_media" = "Shared media";
"lng_profile_photos#one" = "{count} photo";
"lng_profile_photos#other" = "{count} photos";
"lng_profile_photos_header" = "Photos";
@ -615,6 +610,30 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_drop_area_subtitle_channel" = "to set it as a channel photo";
"lng_profile_top_bar_share_contact" = "Share";
"lng_profile_info_section" = "Info";
"lng_info_tab_media" = "Media";
"lng_info_mobile_label" = "Mobile";
"lng_info_username_label" = "Username";
"lng_info_bio_label" = "Bio";
"lng_info_link_label" = "Link";
"lng_info_about_label" = "About";
"lng_info_user_title" = "User Info";
"lng_info_bot_title" = "Bot Info";
"lng_info_group_title" = "Group Info";
"lng_info_channel_title" = "Channel Info";
"lng_profile_enable_notifications" = "Notifications";
"lng_profile_send_message" = "Send Message";
"lng_info_add_as_contact" = "Add as contact";
"lng_profile_shared_media" = "Shared media";
"lng_media_type_photos" = "Photos";
"lng_media_type_videos" = "Videos";
"lng_media_type_songs" = "Audio files";
"lng_media_type_files" = "Files";
"lng_media_type_audios" = "Voice messages";
"lng_media_type_links" = "Shared links";
"lng_media_type_rounds" = "Video messages";
"lng_profile_common_groups_section" = "Groups in common";
"lng_report_title" = "Report channel";
"lng_report_group_title" = "Report group";
"lng_report_bot_title" = "Report bot";
@ -812,12 +831,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_attach_photo" = "Photo";
"lng_media_type" = "Media type";
"lng_media_type_photos" = "Photos";
"lng_media_type_videos" = "Videos";
"lng_media_type_songs" = "Audio files";
"lng_media_type_files" = "Files";
"lng_media_type_audios" = "Voice messages";
"lng_media_type_links" = "Shared links";
"lng_media_open_with" = "Open With";
"lng_media_download" = "Download";

View file

@ -1928,15 +1928,26 @@ void ApiWrap::requestSharedMedia(
auto filter = [&] {
using Type = SharedMediaType;
switch (type) {
case Type::Photo: return MTP_inputMessagesFilterPhotos();
case Type::Video: return MTP_inputMessagesFilterVideo();
case Type::MusicFile: return MTP_inputMessagesFilterMusic();
case Type::File: return MTP_inputMessagesFilterDocument();
case Type::VoiceFile: return MTP_inputMessagesFilterVoice();
case Type::RoundVoiceFile: return MTP_inputMessagesFilterRoundVoice();
case Type::GIF: return MTP_inputMessagesFilterGif();
case Type::Link: return MTP_inputMessagesFilterUrl();
case Type::ChatPhoto: return MTP_inputMessagesFilterChatPhotos();
case Type::Photo:
return MTP_inputMessagesFilterPhotos();
case Type::Video:
return MTP_inputMessagesFilterVideo();
case Type::MusicFile:
return MTP_inputMessagesFilterMusic();
case Type::File:
return MTP_inputMessagesFilterDocument();
case Type::VoiceFile:
return MTP_inputMessagesFilterVoice();
case Type::RoundVoiceFile:
return MTP_inputMessagesFilterRoundVoice();
case Type::RoundFile:
return MTP_inputMessagesFilterRoundVideo();
case Type::GIF:
return MTP_inputMessagesFilterGif();
case Type::Link:
return MTP_inputMessagesFilterUrl();
case Type::ChatPhoto:
return MTP_inputMessagesFilterChatPhotos();
}
return MTP_inputMessagesFilterEmpty();
}();

View file

@ -38,4 +38,20 @@ inline const T *get_if(const variant<Types...> *v) {
return (v && v->template is<T>()) ? &v->template get_unchecked<T>() : nullptr;
}
// Simplified visit
template <typename Method, typename... Types>
inline auto visit(Method &&method, const variant<Types...> &value) {
return value.match(std::forward<Method>(method));
}
template <typename Method, typename... Types>
inline auto visit(Method &&method, variant<Types...> &value) {
return value.match(std::forward<Method>(method));
}
template <typename Method, typename... Types>
inline auto visit(Method &&method, variant<Types...> &&value) {
return value.match(std::forward<Method>(method));
}
} // namespace base

View file

@ -424,7 +424,7 @@ void AbstractBox::keyPressEvent(QKeyEvent *e) {
BoxLayerTitleShadow::BoxLayerTitleShadow(QWidget *parent) : Ui::PlainShadow(parent, st::boxLayerTitleShadow) {
}
BoxContentDivider::BoxContentDivider(QWidget *parent) : TWidget(parent) {
BoxContentDivider::BoxContentDivider(QWidget *parent) : RpWidget(parent) {
}
int BoxContentDivider::resizeGetHeight(int newWidth) {

View file

@ -290,7 +290,7 @@ public:
};
class BoxContentDivider : public TWidget {
class BoxContentDivider : public Ui::RpWidget {
public:
BoxContentDivider(QWidget *parent);

View file

@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "facades.h"
#include "profile/profile_section_memento.h"
#include "info/info_memento.h"
#include "core/click_handler_types.h"
#include "media/media_clip_reader.h"
#include "observer_peer.h"
@ -256,8 +257,17 @@ void autoplayMediaInlineAsync(const FullMsgId &msgId) {
}
void showPeerProfile(const PeerId &peer) {
if (auto main = App::main()) {
main->showWideSection(Profile::SectionMemento(App::peer(peer)));
//if (auto main = App::main()) {
// main->showWideSection(Profile::SectionMemento(App::peer(peer)));
//}
if (auto window = App::wnd()) {
auto memento = Info::Memento(peer);
if (auto layer = memento.createLayer(window->controller())) {
window->showSpecialLayer(std::move(layer));
} else {
App::main()->showWideSection(std::move(memento));
}
}
}

View file

@ -1616,8 +1616,9 @@ void HistoryDocument::eraseFromOverview() {
Storage::SharedMediaTypesMask HistoryDocument::sharedMediaTypes() const {
using Type = Storage::SharedMediaType;
if (_data->voice()) {
using Mask = Storage::SharedMediaTypesMask;
return Mask {}.added(Type::VoiceFile).added(Type::RoundVoiceFile);
return Storage::SharedMediaTypesMask{}
.added(Type::VoiceFile)
.added(Type::RoundVoiceFile);
} else if (_data->song()) {
if (_data->isMusic()) {
return Type::MusicFile;
@ -2456,7 +2457,9 @@ int32 HistoryGif::addToOverview(AddToOverviewMethod method) {
Storage::SharedMediaTypesMask HistoryGif::sharedMediaTypes() const {
using Type = Storage::SharedMediaType;
if (_data->isRoundVideo()) {
return Type::RoundVoiceFile;
return Storage::SharedMediaTypesMask{}
.added(Type::RoundFile)
.added(Type::RoundVoiceFile);
} else if (_data->isGifv()) {
return Type::GIF;
}

View file

@ -0,0 +1,166 @@
/*
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
*/
using "basic.style";
using "boxes/boxes.style";
using "ui/widgets/widgets.style";
infoScroll: ScrollArea(defaultScrollArea) {
bottomsh: 0px;
topsh: 0px;
}
infoTopBarBackIcon: icon {{ "info_back", boxTitleCloseFg }};
infoTopBarBackIconOver: icon {{ "info_back", boxTitleCloseFgOver }};
infoTopBarHeight: boxLayerTitleHeight;
infoTopBarBack: IconButton(defaultIconButton) {
width: infoTopBarHeight;
height: infoTopBarHeight;
icon: infoTopBarBackIcon;
iconOver: infoTopBarBackIconOver;
rippleAreaPosition: point(6px, 6px);
rippleAreaSize: 44px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}
infoLayerTopBarHeight: boxLayerTitleHeight;
infoLayerTopBarBackIcon: icon {{ "info_back", boxTitleCloseFg }};
infoLayerTopBarBackIconOver: icon {{ "info_back", boxTitleCloseFgOver }};
infoLayerTopBarBack: IconButton(infoTopBarBack) {
width: infoLayerTopBarHeight;
height: infoLayerTopBarHeight;
icon: infoLayerTopBarBackIcon;
iconOver: infoLayerTopBarBackIconOver;
}
infoLayerTopBarCloseIcon: icon {{ "info_close", boxTitleCloseFg }};
infoLayerTopBarCloseIconOver: icon {{ "info_close", boxTitleCloseFgOver }};
infoLayerTopBarClose: IconButton(infoLayerTopBarBack) {
icon: infoLayerTopBarCloseIcon;
iconOver: infoLayerTopBarCloseIconOver;
}
infoLayerTopBar: InfoTopBar {
height: infoLayerTopBarHeight;
back: infoLayerTopBarBack;
title: boxTitle;
titlePosition: boxLayerTitlePosition;
bg: boxBg;
}
infoMinimalWidth: 320px;
infoDesiredWidth: 360px;
infoMinimalLayerMargin: 48px;
infoTabs: SettingsSlider(defaultTabsSlider) {
height: 55px;
barTop: 52px;
labelTop: 19px;
}
infoProfilePhotoSize: 72px;
infoProfilePhotoLeft: 27px;
infoProfilePhotoTop: 22px;
infoProfilePhotoBottom: 22px;
infoProfileNameLeft: 111px;
infoProfileNameRight: 20px;
infoProfileNameTop: 32px;
infoProfileNameLabel: FlatLabel(defaultFlatLabel) {
margin: margins(10px, 5px, 10px, 5px);
width: 160px;
maxHeight: 24px;
textFg: windowBoldFg;
style: TextStyle(defaultTextStyle) {
font: font(16px semibold);
linkFont: font(16px semibold);
linkFontOver: font(16px semibold underline);
}
}
infoProfileStatusLeft: infoProfileNameLeft;
infoProfileStatusRight: infoProfileNameRight;
infoProfileStatusTop: 62px;
infoProfileStatusLabel: FlatLabel(infoProfileNameLabel) {
margin: margins(10px, 5px, 10px, 5px);
width: 160px;
maxHeight: 18px;
textFg: windowSubTextFg;
style: TextStyle(defaultTextStyle) {
font: normalFont;
linkFont: normalFont;
linkFontOver: normalFont;
}
palette: TextPalette(defaultTextPalette) {
linkFg: windowActiveTextFg;
}
}
infoProfileToggleRight: 12px;
infoProfileToggleTop: 40px;
infoProfileSkip: 12px;
infoProfileLabeledPadding: margins(77px, 10px, 10px, 10px);
infoProfileSeparatorPadding: margins(
73px,
infoProfileSkip,
0px,
infoProfileSkip);
infoIconFg: menuIconFg;
infoIconPosition: point(28px, 8px);
infoIconInformation: icon {{ "info_information", infoIconFg }};
infoIconMembers: icon {{ "info_members", infoIconFg }};
infoIconNotifications: icon {{ "info_notifications", infoIconFg }};
infoIconActions: icon {{ "info_actions", infoIconFg }};
infoLabel: FlatLabel(defaultFlatLabel) {
}
infoLabeled: FlatLabel(defaultFlatLabel) {
}
infoProfileButton: InfoProfileButton {
textFg: windowBoldFg;
textFgOver: windowBoldFgOver;
textBg: windowBg;
textBgOver: windowBgOver;
font: semiboldFont;
height: 22px;
padding: margins(80px, 8px, 8px, 8px);
toggle: defaultMenuToggle;
toggleOver: defaultMenuToggleOver;
ripple: defaultRippleAnimation;
}
infoNotificationsButton: InfoProfileButton(infoProfileButton) {
padding: margins(80px, 11px, 8px, 9px);
}
infoMainButton: InfoProfileButton(infoProfileButton) {
textFg: lightButtonFg;
textFgOver: lightButtonFgOver;
}

View file

@ -0,0 +1,63 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_common_groups_inner_widget.h"
#include "info/info_common_groups_widget.h"
#include "lang/lang_keys.h"
#include "styles/style_info.h"
namespace Info {
namespace CommonGroups {
InnerWidget::InnerWidget(QWidget *parent, not_null<UserData*> user)
: RpWidget(parent)
, _user(user) {
base::lambda<void(int)> launch = [this, &launch](int counter) {
QTimer::singleShot(500, this, [this, launch, counter] {
_rowsHeightFake += 300;
resizeToWidth(width(), _minHeight);
launch(counter - 1);
});
};
launch(10);
}
void InnerWidget::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
}
void InnerWidget::saveState(not_null<Memento*> memento) {
}
void InnerWidget::restoreState(not_null<Memento*> memento) {
}
int InnerWidget::resizeGetHeight(int newWidth) {
auto rowsHeight = _rowsHeightFake;
return qMax(rowsHeight, _minHeight);
}
} // namespace CommonGroups
} // namespace Info

View file

@ -0,0 +1,65 @@
/*
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 <rpl/producer.h>
#include "ui/rp_widget.h"
namespace Info {
namespace CommonGroups {
class Memento;
class InnerWidget final : public Ui::RpWidget {
public:
InnerWidget(QWidget *parent, not_null<UserData*> user);
not_null<UserData*> user() const {
return _user;
}
void resizeToWidth(int newWidth, int minHeight) {
_minHeight = minHeight;
return RpWidget::resizeToWidth(newWidth);
}
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
protected:
int resizeGetHeight(int newWidth) override;
void visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) override;
private:
not_null<UserData*> _user;
int _rowsHeightFake = 0;
int _visibleTop = 0;
int _visibleBottom = 0;
int _minHeight = 0;
};
} // namespace CommonGroups
} // namespace Info

View file

@ -0,0 +1,92 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_common_groups_widget.h"
#include "info/info_common_groups_inner_widget.h"
#include "ui/widgets/scroll_area.h"
namespace Info {
namespace CommonGroups {
object_ptr<ContentWidget> Memento::createWidget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
const QRect &geometry) {
auto result = object_ptr<Widget>(
parent,
wrap,
controller,
App::user(_userId));
result->setInternalState(geometry, this);
return std::move(result);
}
Widget::Widget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
not_null<UserData*> user)
: ContentWidget(parent, wrap, controller) {
_inner = setInnerWidget(object_ptr<InnerWidget>(this, user));
}
not_null<UserData*> Widget::user() const {
return _inner->user();
}
bool Widget::showInternal(not_null<ContentMemento*> memento) {
if (auto groupsMemento = dynamic_cast<Memento*>(memento.get())) {
if (groupsMemento->userId() == user()->bareId()) {
restoreState(groupsMemento);
return true;
}
}
return false;
}
void Widget::setInternalState(const QRect &geometry, not_null<Memento*> memento) {
setGeometry(geometry);
myEnsureResized(this);
restoreState(memento);
}
std::unique_ptr<ContentMemento> Widget::createMemento() {
auto result = std::make_unique<Memento>(user()->bareId());
saveState(result.get());
return std::move(result);
}
void Widget::saveState(not_null<Memento*> memento) {
memento->setScrollTop(scrollTopSave());
_inner->saveState(memento);
}
void Widget::restoreState(not_null<Memento*> memento) {
_inner->restoreState(memento);
auto scrollTop = memento->scrollTop();
scrollTopRestore(memento->scrollTop());
// TODO is setVisibleTopBottom called?
}
} // namespace CommonGroups
} // namespace Info

View file

@ -0,0 +1,79 @@
/*
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 <rpl/producer.h>
#include "info/info_memento.h"
namespace Info {
namespace CommonGroups {
class InnerWidget;
class Memento final : public ContentMemento {
public:
Memento(UserId userId) : _userId(userId) {
}
object_ptr<ContentWidget> createWidget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
const QRect &geometry) override;
UserId userId() const {
return _userId;
}
private:
UserId _userId = 0;
};
class Widget final : public ContentWidget {
public:
Widget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
not_null<UserData*> user);
not_null<UserData*> user() const;
bool showInternal(
not_null<ContentMemento*> memento) override;
std::unique_ptr<ContentMemento> createMemento() override;
void setInternalState(
const QRect &geometry,
not_null<Memento*> memento);
private:
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
InnerWidget *_inner = nullptr;
};
} // namespace CommonGroups
} // namespace Info

View file

@ -0,0 +1,164 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_layer_wrap.h"
#include "info/info_memento.h"
#include "info/info_top_bar.h"
#include "ui/rp_widget.h"
#include "ui/widgets/buttons.h"
#include "window/section_widget.h"
#include "window/window_controller.h"
#include "window/main_window.h"
#include "styles/style_info.h"
#include "styles/style_settings.h"
#include "styles/style_window.h"
#include "styles/style_boxes.h"
namespace Info {
LayerWrap::LayerWrap(
not_null<Window::Controller*> controller,
not_null<Memento*> memento)
: _topBar(createTopBar(controller, memento))
, _content(createContent(controller, memento)) {
_content->desiredHeightValue()
| rpl::on_next([this](int height) {
_desiredHeight = height;
resizeToDesiredHeight();
})
| rpl::start(lifetime());
}
object_ptr<TopBar> LayerWrap::createTopBar(
not_null<Window::Controller*> controller,
not_null<Memento*> memento) {
auto result = object_ptr<TopBar>(
this,
st::infoLayerTopBar);
result->addButton(object_ptr<Ui::IconButton>(
result.data(),
st::infoLayerTopBarClose));
result->setTitle(TitleValue(
memento->section(),
App::peer(memento->peerId())));
return result;
}
object_ptr<ContentWidget> LayerWrap::createContent(
not_null<Window::Controller*> controller,
not_null<Memento*> memento) {
return memento->content()->createWidget(
this,
Wrap::Layer,
controller,
controller->window()->rect());
}
void LayerWrap::showFinished() {
}
void LayerWrap::parentResized() {
auto parentSize = parentWidget()->size();
auto windowWidth = parentSize.width();
auto newWidth = st::settingsMaxWidth;
auto newContentLeft = st::settingsMaxPadding;
if (windowWidth <= st::settingsMaxWidth) {
newWidth = windowWidth;
newContentLeft = st::settingsMinPadding;
if (windowWidth > st::windowMinWidth) {
// Width changes from st::windowMinWidth to st::settingsMaxWidth.
// Padding changes from st::settingsMinPadding to st::settingsMaxPadding.
newContentLeft += ((newWidth - st::windowMinWidth) * (st::settingsMaxPadding - st::settingsMinPadding)) / (st::settingsMaxWidth - st::windowMinWidth);
}
} else if (windowWidth < st::settingsMaxWidth + 2 * st::settingsMargin) {
newWidth = windowWidth - 2 * st::settingsMargin;
newContentLeft = st::settingsMinPadding;
if (windowWidth > st::windowMinWidth) {
// Width changes from st::windowMinWidth to st::settingsMaxWidth.
// Padding changes from st::settingsMinPadding to st::settingsMaxPadding.
newContentLeft += ((newWidth - st::windowMinWidth) * (st::settingsMaxPadding - st::settingsMinPadding)) / (st::settingsMaxWidth - st::windowMinWidth);
}
}
resizeToWidth(newWidth, newContentLeft);
}
void LayerWrap::resizeToWidth(int newWidth, int newContentLeft) {
resize(newWidth, height());
_topBar->resizeToWidth(newWidth);
_topBar->moveToLeft(0, 0, newWidth);
// Widget height depends on content height, so we
// resize it here, not in the resizeEvent() handler.
_content->resizeToWidth(newWidth);
_content->moveToLeft(0, _topBar->height(), newWidth);
resizeToDesiredHeight();
}
void LayerWrap::resizeToDesiredHeight() {
if (!parentWidget()) return;
auto parentSize = parentWidget()->size();
auto windowWidth = parentSize.width();
auto windowHeight = parentSize.height();
auto maxHeight = _topBar->height() + _desiredHeight;
auto newHeight = maxHeight + st::boxRadius;
if (newHeight > windowHeight || width() >= windowWidth) {
newHeight = windowHeight;
}
setRoundedCorners(newHeight < windowHeight);
setGeometry((windowWidth - width()) / 2, (windowHeight - newHeight) / 2, width(), newHeight);
update();
}
void LayerWrap::setRoundedCorners(bool rounded) {
_roundedCorners = rounded;
setAttribute(Qt::WA_OpaquePaintEvent, !_roundedCorners);
}
void LayerWrap::paintEvent(QPaintEvent *e) {
if (_roundedCorners) {
Painter p(this);
auto clip = e->rect();
auto r = st::boxRadius;
auto parts = RectPart::None | 0;
if (clip.intersects({ 0, 0, width(), r })) {
parts |= RectPart::FullTop;
}
if (clip.intersects({ 0, height() - r, width(), r })) {
parts |= RectPart::FullBottom;
}
if (parts) {
App::roundRect(
p,
rect(),
st::boxBg,
BoxCorners,
nullptr,
parts);
}
}
}
} // namespace Info

View file

@ -0,0 +1,68 @@
/*
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 "layerwidget.h"
namespace Window {
class Controller;
} // namespace Window
namespace Info {
class Memento;
class ContentWidget;
class TopBar;
class LayerWrap : public LayerWidget {
public:
LayerWrap(
not_null<Window::Controller*> controller,
not_null<Memento*> memento);
void showFinished() override;
void parentResized() override;
protected:
void paintEvent(QPaintEvent *e) override;
void setRoundedCorners(bool roundedCorners);
private:
object_ptr<TopBar> createTopBar(
not_null<Window::Controller*> controller,
not_null<Memento*> memento);
object_ptr<ContentWidget> createContent(
not_null<Window::Controller*> controller,
not_null<Memento*> memento);
void resizeToWidth(int newWidth, int newContentLeft);
void resizeToDesiredHeight();
object_ptr<TopBar> _topBar;
object_ptr<ContentWidget> _content;
int _desiredHeight = 0;
bool _roundedCorners = false;
};
} // namespace Info

View file

@ -0,0 +1,62 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_media_inner_widget.h"
namespace Info {
namespace Media {
InnerWidget::InnerWidget(
QWidget *parent,
not_null<PeerData*> peer,
Type type)
: RpWidget(parent)
, _peer(peer)
, _type(type) {
base::lambda<void(int)> launch = [this, &launch](int counter) {
QTimer::singleShot(500, this, [this, launch, counter] {
_rowsHeightFake += 300;
resizeToWidth(width(), _minHeight);
launch(counter - 1);
});
};
launch(10);
}
void InnerWidget::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
}
void InnerWidget::saveState(not_null<Memento*> memento) {
}
void InnerWidget::restoreState(not_null<Memento*> memento) {
}
int InnerWidget::resizeGetHeight(int newWidth) {
auto rowsHeight = _rowsHeightFake;
return qMax(rowsHeight, _minHeight);
}
} // namespace Media
} // namespace Info

View file

@ -0,0 +1,70 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/rp_widget.h"
#include "info/info_media_widget.h"
namespace Info {
namespace Media {
class InnerWidget final : public Ui::RpWidget {
public:
using Type = Widget::Type;
InnerWidget(
QWidget *parent,
not_null<PeerData*> peer,
Type type);
not_null<PeerData*> peer() const {
return _peer;
}
Type type() const {
return _type;
}
void resizeToWidth(int newWidth, int minHeight) {
_minHeight = minHeight;
return RpWidget::resizeToWidth(newWidth);
}
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
protected:
int resizeGetHeight(int newWidth) override;
void visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) override;
private:
not_null<PeerData*> _peer;
Type _type = Type::Photo;
int _rowsHeightFake = 0;
int _visibleTop = 0;
int _visibleBottom = 0;
int _minHeight = 0;
};
} // namespace Media
} // namespace Info

View file

@ -0,0 +1,95 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_media_widget.h"
#include "info/info_media_inner_widget.h"
#include "ui/widgets/scroll_area.h"
namespace Info {
namespace Media {
object_ptr<ContentWidget> Memento::createWidget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
const QRect &geometry) {
auto result = object_ptr<Widget>(
parent,
wrap,
controller,
App::peer(_peerId),
_type);
result->setInternalState(geometry, this);
return std::move(result);
}
Widget::Widget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
not_null<PeerData*> peer,
Type type)
: ContentWidget(parent, wrap, controller) {
_inner = setInnerWidget(object_ptr<InnerWidget>(this, peer, type));
}
not_null<PeerData*> Widget::peer() const {
return _inner->peer();
}
Widget::Type Widget::type() const {
return _inner->type();
}
bool Widget::showInternal(not_null<ContentMemento*> memento) {
if (auto mediaMemento = dynamic_cast<Memento*>(memento.get())) {
if (mediaMemento->peerId() == peer()->id) {
restoreState(mediaMemento);
return true;
}
}
return false;
}
void Widget::setInternalState(const QRect &geometry, not_null<Memento*> memento) {
setGeometry(geometry);
myEnsureResized(this);
restoreState(memento);
}
std::unique_ptr<ContentMemento> Widget::createMemento() {
auto result = std::make_unique<Memento>(peer()->id, type());
saveState(result.get());
return std::move(result);
}
void Widget::saveState(not_null<Memento*> memento) {
memento->setScrollTop(scrollTopSave());
_inner->saveState(memento);
}
void Widget::restoreState(not_null<Memento*> memento) {
_inner->restoreState(memento);
scrollTopRestore(memento->scrollTop());
}
} // namespace Media
} // namespace Info

View file

@ -0,0 +1,91 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include <rpl/producer.h>
#include "info/info_memento.h"
#include "storage/storage_shared_media.h"
namespace Info {
namespace Media {
class InnerWidget;
class Memento final : public ContentMemento {
public:
using Type = Storage::SharedMediaType;
Memento(PeerId peerId, Type type)
: _peerId(peerId)
, _type(type) {
}
object_ptr<ContentWidget> createWidget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
const QRect &geometry) override;
PeerId peerId() const {
return _peerId;
}
Type type() const {
return _type;
}
private:
PeerId _peerId = 0;
Type _type = Type::Photo;
};
class Widget final : public ContentWidget {
public:
using Type = Memento::Type;
Widget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
not_null<PeerData*> peer,
Type type);
not_null<PeerData*> peer() const;
Type type() const;
bool showInternal(
not_null<ContentMemento*> memento) override;
std::unique_ptr<ContentMemento> createMemento() override;
void setInternalState(
const QRect &geometry,
not_null<Memento*> memento);
private:
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
InnerWidget *_inner = nullptr;
};
} // namespace Media
} // namespace Info

View file

@ -0,0 +1,227 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_memento.h"
#include <rpl/never.h>
#include "window/window_controller.h"
#include "ui/widgets/scroll_area.h"
#include "lang/lang_keys.h"
#include "info/info_profile_widget.h"
#include "info/info_media_widget.h"
#include "info/info_common_groups_widget.h"
#include "info/info_layer_wrap.h"
#include "info/info_narrow_wrap.h"
#include "info/info_side_wrap.h"
#include "styles/style_info.h"
#include "styles/style_profile.h"
namespace Info {
ContentWidget::ContentWidget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller)
: RpWidget(parent)
, _controller(controller)
, _wrap(wrap)
, _scroll(this, st::infoScroll) {
setAttribute(Qt::WA_OpaquePaintEvent);
}
void ContentWidget::resizeEvent(QResizeEvent *e) {
auto newScrollTop = _scroll->scrollTop() + _topDelta;
auto scrollGeometry = rect().marginsRemoved(
QMargins(0, _scrollTopSkip, 0, 0));
if (_scroll->geometry() != scrollGeometry) {
_scroll->setGeometry(scrollGeometry);
_inner->setMinimumHeight(_scroll->height());
_inner->resizeToWidth(_scroll->width());
}
if (!_scroll->isHidden()) {
if (_topDelta) {
_scroll->scrollToY(newScrollTop);
}
auto scrollTop = _scroll->scrollTop();
_inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height());
}
}
void ContentWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), (_wrap == Wrap::Layer)
? st::boxBg
: st::profileBg);
}
void ContentWidget::setGeometryWithTopMoved(
const QRect &newGeometry,
int topDelta) {
_topDelta = topDelta;
auto willBeResized = (size() != newGeometry.size());
if (geometry() != newGeometry) {
setGeometry(newGeometry);
}
if (!willBeResized) {
QResizeEvent fake(size(), size());
QApplication::sendEvent(this, &fake);
}
_topDelta = 0;
}
Ui::RpWidget *ContentWidget::doSetInnerWidget(
object_ptr<RpWidget> inner,
int scrollTopSkip) {
_inner = _scroll->setOwnedWidget(std::move(inner));
_inner->move(0, 0);
scrollTopValue()
| rpl::on_next([this, inner = _inner](int value) {
inner->setVisibleTopBottom(
value,
value + _scroll->height()); // TODO rpl::combine_latest
})
| rpl::start(_inner->lifetime());
return _inner;
}
rpl::producer<Section> ContentWidget::sectionRequest() const {
return rpl::never<Section>();
}
rpl::producer<int> ContentWidget::desiredHeightValue() const {
return _inner->desiredHeightValue()
| rpl::map([this](int value) {
return value + _scrollTopSkip;
});
}
rpl::producer<int> ContentWidget::scrollTopValue() const {
return _scroll->scrollTopValue();
}
void ContentWidget::setWrap(Wrap wrap) {
_wrap = wrap;
update();
}
int ContentWidget::scrollTopSave() const {
return _scroll->scrollTop();
}
void ContentWidget::scrollTopRestore(int scrollTop) {
_scroll->scrollToY(scrollTop);
}
bool ContentWidget::wheelEventFromFloatPlayer(QEvent *e) {
return _scroll->viewportEvent(e);
}
QRect ContentWidget::rectForFloatPlayer() const {
return mapToGlobal(_scroll->geometry());
}
std::unique_ptr<ContentMemento> Memento::Default(
PeerId peerId,
Section section) {
switch (section.type()) {
case Section::Type::Profile:
return std::make_unique<Profile::Memento>(peerId);
case Section::Type::Media:
return std::make_unique<Media::Memento>(
peerId,
section.mediaType());
case Section::Type::CommonGroups:
Assert(peerIsUser(peerId));
return std::make_unique<CommonGroups::Memento>(
peerToUser(peerId));
}
Unexpected("Wrong section type in Info::Memento::Default()");
}
object_ptr<Window::SectionWidget> Memento::createWidget(
QWidget *parent,
not_null<Window::Controller*> controller,
const QRect &geometry) {
return object_ptr<NarrowWrap>(
parent,
controller,
this);
}
object_ptr<LayerWidget> Memento::createLayer(
not_null<Window::Controller*> controller) {
auto layout = controller->computeColumnLayout();
auto minimalWidthForLayer = st::infoMinimalWidth
+ 2 * st::infoMinimalLayerMargin;
if (layout.bodyWidth < minimalWidthForLayer) {
return nullptr;
}
return object_ptr<LayerWrap>(controller, this);
}
rpl::producer<QString> TitleValue(
const Section &section,
not_null<PeerData*> peer) {
return Lang::Viewer([&] {
switch (section.type()) {
case Section::Type::Profile:
if (auto user = peer->asUser()) {
return user->botInfo
? lng_info_bot_title
: lng_info_user_title;
} else if (auto channel = peer->asChannel()) {
return channel->isMegagroup()
? lng_info_group_title
: lng_info_channel_title;
} else if (peer->isChat()) {
return lng_info_group_title;
}
Unexpected("Bad peer type in Info::TitleValue()");
case Section::Type::Media:
switch (section.mediaType()) {
case Section::MediaType::Photo:
return lng_media_type_photos;
case Section::MediaType::Video:
return lng_media_type_videos;
case Section::MediaType::MusicFile:
return lng_media_type_songs;
case Section::MediaType::File:
return lng_media_type_files;
case Section::MediaType::VoiceFile:
return lng_media_type_audios;
case Section::MediaType::Link:
return lng_media_type_links;
case Section::MediaType::RoundFile:
return lng_media_type_rounds;
}
Unexpected("Bad media type in Info::TitleValue()");
case Section::Type::CommonGroups:
return lng_profile_common_groups_section;
}
Unexpected("Bad section type in Info::TitleValue()");
}());
}
} // namespace Info

View file

@ -0,0 +1,223 @@
/*
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"
#include "ui/rp_widget.h"
namespace Storage {
enum class SharedMediaType : char;
} // namespace Storage
namespace Ui {
class ScrollArea;
} // namespace Ui
namespace Info {
enum class Wrap {
Layer,
Narrow,
Side,
};
class Section final {
public:
enum class Type {
Profile,
Media,
CommonGroups,
};
using MediaType = Storage::SharedMediaType;
Section(Type type) : _type(type) {
Expects(type != Type::Media);
}
Section(MediaType mediaType)
: _type(Type::Media)
, _mediaType(mediaType) {
}
Type type() const {
return _type;
}
MediaType mediaType() const {
Expects(_type == Type::Media);
return _mediaType;
}
private:
Type _type;
Storage::SharedMediaType _mediaType;
};
class ContentMemento;
class ContentWidget : public Ui::RpWidget {
public:
ContentWidget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller);
virtual bool showInternal(
not_null<ContentMemento*> memento) = 0;
virtual std::unique_ptr<ContentMemento> createMemento() = 0;
virtual rpl::producer<Section> sectionRequest() const;
virtual void setWrap(Wrap wrap);
rpl::producer<int> desiredHeightValue() const override;
virtual void setInnerFocus() {
_inner->setFocus();
}
// When resizing the widget with top edge moved up or down and we
// want to add this top movement to the scroll position, so inner
// content will not move.
void setGeometryWithTopMoved(
const QRect &newGeometry,
int topDelta);
// Float player interface.
bool wheelEventFromFloatPlayer(QEvent *e);
QRect rectForFloatPlayer() const;
protected:
template <typename Widget>
Widget *setInnerWidget(
object_ptr<Widget> inner,
int scrollTopSkip = 0) {
return static_cast<Widget*>(
doSetInnerWidget(std::move(inner), scrollTopSkip));
}
not_null<Window::Controller*> controller() const {
return _controller;
}
Wrap wrap() const {
return _wrap;
}
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
rpl::producer<int> scrollTopValue() const;
int scrollTopSave() const;
void scrollTopRestore(int scrollTop);
private:
RpWidget *doSetInnerWidget(
object_ptr<RpWidget> inner,
int scrollTopSkip);
not_null<Window::Controller*> _controller;
Wrap _wrap = Wrap::Layer;
int _scrollTopSkip = 0;
object_ptr<Ui::ScrollArea> _scroll;
Ui::RpWidget *_inner = nullptr;
// Saving here topDelta in setGeometryWithTopMoved() to get it passed to resizeEvent().
int _topDelta = 0;
};
class ContentMemento {
public:
virtual object_ptr<ContentWidget> createWidget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
const QRect &geometry) = 0;
virtual ~ContentMemento() = default;
void setScrollTop(int scrollTop) {
_scrollTop = scrollTop;
}
int scrollTop() const {
return _scrollTop;
}
private:
int _scrollTop = 0;
};
class Memento final : public Window::SectionMemento {
public:
Memento(PeerId peerId)
: Memento(peerId, Section::Type::Profile) {
}
Memento(PeerId peerId, Section section)
: Memento(peerId, section, Default(peerId, section)) {
}
Memento(
PeerId peerId,
Section section,
std::unique_ptr<ContentMemento> content)
: _peerId(peerId)
, _section(section)
, _content(std::move(content)) {
}
object_ptr<Window::SectionWidget> createWidget(
QWidget *parent,
not_null<Window::Controller*> controller,
const QRect &geometry) override;
object_ptr<LayerWidget> createLayer(
not_null<Window::Controller*> controller) override;
void setInner(std::unique_ptr<ContentMemento> content) {
_content = std::move(content);
}
not_null<ContentMemento*> content() {
return _content.get();
}
PeerId peerId() const {
return _peerId;
}
Section section() const {
return _section;
}
private:
static std::unique_ptr<ContentMemento> Default(
PeerId peerId,
Section section);
PeerId _peerId = 0;
Section _section = Section::Type::Profile;
std::unique_ptr<ContentMemento> _content;
};
rpl::producer<QString> TitleValue(
const Section &section,
not_null<PeerData*> peer);
} // namespace Info

View file

@ -0,0 +1,158 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_narrow_wrap.h"
#include <rpl/flatten_latest.h>
#include "info/info_profile_widget.h"
#include "info/info_media_widget.h"
#include "info/info_memento.h"
#include "ui/widgets/discrete_sliders.h"
#include "ui/widgets/shadow.h"
#include "lang/lang_keys.h"
#include "styles/style_info.h"
#include "styles/style_profile.h"
namespace Info {
NarrowWrap::NarrowWrap(
QWidget *parent,
not_null<Window::Controller*> controller,
not_null<Memento*> memento)
: Window::SectionWidget(parent, controller)
, _peer(App::peer(memento->peerId())) {
setInternalState(geometry(), memento);
}
void NarrowWrap::showInner(object_ptr<ContentWidget> inner) {
_inner = std::move(inner);
_inner->setGeometry(innerGeometry());
_inner->show();
_desiredHeights.fire(desiredHeightForInner());
}
rpl::producer<int> NarrowWrap::desiredHeightForInner() const {
return _inner->desiredHeightValue();
}
object_ptr<Profile::Widget> NarrowWrap::createProfileWidget() {
auto result = object_ptr<Profile::Widget>(
this,
Wrap::Narrow,
controller(),
_peer);
return result;
}
object_ptr<Media::Widget> NarrowWrap::createMediaWidget() {
auto result = object_ptr<Media::Widget>(
this,
Wrap::Narrow,
controller(),
_peer,
Media::Widget::Type::Photo);
return result;
}
QPixmap NarrowWrap::grabForShowAnimation(
const Window::SectionSlideParams &params) {
// if (params.withTopBarShadow) _tabsShadow->hide();
auto result = myGrab(this);
// if (params.withTopBarShadow) _tabsShadow->show();
return result;
}
void NarrowWrap::doSetInnerFocus() {
_inner->setInnerFocus();
}
bool NarrowWrap::showInternal(
not_null<Window::SectionMemento*> memento) {
if (auto infoMemento = dynamic_cast<Memento*>(memento.get())) {
if (infoMemento->peerId() == peer()->id) {
restoreState(infoMemento);
return true;
}
}
return false;
}
void NarrowWrap::setInternalState(
const QRect &geometry,
not_null<Memento*> memento) {
setGeometry(geometry);
restoreState(memento);
}
std::unique_ptr<Window::SectionMemento> NarrowWrap::createMemento() {
auto result = std::make_unique<Memento>(peer()->id);
saveState(result.get());
return std::move(result);
}
rpl::producer<int> NarrowWrap::desiredHeight() const {
return
rpl::single(desiredHeightForInner())
| rpl::then(_desiredHeights.events())
| rpl::flatten_latest();
}
void NarrowWrap::saveState(not_null<Memento*> memento) {
memento->setInner(_inner->createMemento());
}
QRect NarrowWrap::innerGeometry() const {
return rect();
}
void NarrowWrap::restoreState(not_null<Memento*> memento) {
showInner(memento->content()->createWidget(
this,
Wrap::Narrow,
controller(),
innerGeometry()));
}
void NarrowWrap::resizeEvent(QResizeEvent *e) {
if (_inner) {
_inner->setGeometry(innerGeometry());
}
}
void NarrowWrap::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), st::profileBg);
}
bool NarrowWrap::wheelEventFromFloatPlayer(
QEvent *e,
Window::Column myColumn,
Window::Column playerColumn) {
return _inner->wheelEventFromFloatPlayer(e);
}
QRect NarrowWrap::rectForFloatPlayer(
Window::Column myColumn,
Window::Column playerColumn) const {
return _inner->rectForFloatPlayer();
}
} // namespace Info

View file

@ -0,0 +1,112 @@
/*
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 <rpl/event_stream.h>
#include "window/section_widget.h"
namespace Ui {
class PlainShadow;
class SettingsSlider;
} // namespace Ui
namespace Info {
namespace Profile {
class Widget;
} // namespace Profile
namespace Media {
class Widget;
} // namespace Media
class Memento;
class ContentWidget;
class NarrowWrap final : public Window::SectionWidget {
public:
NarrowWrap(
QWidget *parent,
not_null<Window::Controller*> controller,
not_null<Memento*> memento);
not_null<PeerData*> peer() const {
return _peer;
}
PeerData *peerForDialogs() const override {
return _peer;
}
bool hasTopBarShadow() const override {
return true;
}
QPixmap grabForShowAnimation(
const Window::SectionSlideParams &params) override;
bool showInternal(
not_null<Window::SectionMemento*> memento) override;
std::unique_ptr<Window::SectionMemento> createMemento() override;
rpl::producer<int> desiredHeight() const override;
void setInternalState(
const QRect &geometry,
not_null<Memento*> memento);
// Float player interface.
bool wheelEventFromFloatPlayer(
QEvent *e,
Window::Column myColumn,
Window::Column playerColumn) override;
QRect rectForFloatPlayer(
Window::Column myColumn,
Window::Column playerColumn) const override;
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void doSetInnerFocus() override;
private:
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
QRect innerGeometry() const;
rpl::producer<int> desiredHeightForInner() const;
void showInner(object_ptr<ContentWidget> inner);
object_ptr<Profile::Widget> createProfileWidget();
object_ptr<Media::Widget> createMediaWidget();
not_null<PeerData*> _peer;
object_ptr<Ui::PlainShadow> _tabsShadow = { nullptr };
object_ptr<ContentWidget> _inner = { nullptr };
rpl::event_stream<rpl::producer<int>> _desiredHeights;
rpl::lifetime _lifetime;
};
} // namespace Info

View file

@ -0,0 +1,211 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_profile_inner_widget.h"
#include <rpl/combine_latest.h>
#include "boxes/abstract_box.h"
#include "mainwidget.h"
#include "info/info_profile_widget.h"
#include "info/info_profile_lines.h"
#include "lang/lang_keys.h"
#include "styles/style_info.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
namespace Info {
namespace Profile {
InnerWidget::InnerWidget(QWidget *parent, not_null<PeerData*> peer)
: RpWidget(parent)
, _peer(peer)
, _content(this) {
setupContent();
}
void InnerWidget::setupContent() {
auto hideDetails = (_peer->isChat() || _peer->isMegagroup());
auto cover = _content->add(object_ptr<CoverLine>(this, _peer));
if (hideDetails) {
auto hiddenDetailsContent = setupDetailsContent(_content);
auto hiddenDetails = _content->add(object_ptr<Ui::SlideWrap<>>(
this,
std::move(hiddenDetailsContent)));
cover->setHasToggle(true);
cover->toggled()
| rpl::on_next([=](bool expanded) {
hiddenDetails->toggleAnimated(expanded);
}) | rpl::start(_lifetime);
hiddenDetails->hideFast();
} else {
_content->add(setupDetailsContent(_content));
}
_content->add(object_ptr<BoxContentDivider>(this));
_content->heightValue()
| rpl::on_next([this](int height) {
TWidget::resizeToWidth(width());
}) | rpl::start(_lifetime);
}
object_ptr<Ui::RpWidget> InnerWidget::setupDetailsContent(
RpWidget *parent) const {
auto result = object_ptr<Ui::VerticalLayout>(parent);
result->add(object_ptr<BoxContentDivider>(result));
auto skipPadding = QMargins(0, 0, 0, st::infoProfileSkip);
result->add(object_ptr<Ui::PaddingWrap<>>(result, skipPadding));
result->add(setupInfoLines(result));
result->add(setupMuteToggle(result));
if (auto user = _peer->asUser()) {
setupMainUserButtons(result, user);
}
result->add(object_ptr<Ui::PaddingWrap<>>(result, skipPadding));
return result;
}
object_ptr<Ui::RpWidget> InnerWidget::setupMuteToggle(
RpWidget *parent) const {
auto result = object_ptr<Ui::VerticalLayout>(parent);
auto button = result->add(object_ptr<Button>(
result,
Lang::Viewer(lng_profile_enable_notifications),
st::infoNotificationsButton));
NotificationsEnabledViewer(_peer)
| rpl::on_next([button](bool enabled) {
button->setToggled(enabled);
})
| rpl::start(button->lifetime());
button->clicks()
| rpl::on_next([this](auto) {
App::main()->updateNotifySetting(
_peer,
_peer->isMuted()
? NotifySettingSetNotify
: NotifySettingSetMuted);
})
| rpl::start(button->lifetime());
object_ptr<FloatingIcon>(
result,
st::infoIconNotifications);
return result;
}
void InnerWidget::setupMainUserButtons(
Ui::VerticalLayout *wrap,
not_null<UserData*> user) const {
auto sendMessage = wrap->add(object_ptr<Button>(
wrap,
Lang::Viewer(lng_profile_send_message) | ToUpperValue(),
st::infoMainButton));
sendMessage->clicks()
| rpl::on_next([user](auto&&) {
Ui::showPeerHistory(
user,
ShowAtUnreadMsgId,
Ui::ShowWay::Forward);
})
| rpl::start(sendMessage->lifetime());
auto addContact = wrap->add(object_ptr<Ui::SlideWrap<Button>>(
wrap,
object_ptr<Button>(
wrap,
Lang::Viewer(lng_info_add_as_contact) | ToUpperValue(),
st::infoMainButton)));
CanAddContactViewer(user)
| rpl::on_next([addContact](bool canAdd) {
addContact->toggleAnimated(canAdd);
})
| rpl::start(addContact->lifetime());
addContact->finishAnimations();
addContact->entity()->clicks()
| rpl::on_next([user](auto&&) {
App::main()->shareContactLayer(user);
})
| rpl::start(addContact->lifetime());
}
object_ptr<Ui::RpWidget> InnerWidget::setupInfoLines(
RpWidget *parent) const {
auto result = object_ptr<Ui::VerticalLayout>(parent);
auto infoPartsShown = std::vector<rpl::producer<bool>>();
auto addInfoLine = [&](
LangKey label,
rpl::producer<TextWithEntities> &&text) {
auto line = result->add(object_ptr<LabeledLine>(
result,
Lang::Viewer(label) | WithEmptyEntities(),
std::move(text)));
infoPartsShown.push_back(line->shownValue());
};
if (auto user = _peer->asUser()) {
addInfoLine(lng_info_mobile_label, PhoneViewer(user));
addInfoLine(lng_info_bio_label, BioViewer(user));
addInfoLine(lng_info_username_label, UsernameViewer(user));
} else {
addInfoLine(lng_info_link_label, LinkViewer(_peer));
addInfoLine(lng_info_about_label, AboutViewer(_peer));
}
auto separator = result->add(object_ptr<Ui::SlideWrap<>>(
result,
object_ptr<Ui::PlainShadow>(result, st::shadowFg),
st::infoProfileSeparatorPadding));
rpl::combine_latest(std::move(infoPartsShown))
| rpl::map([](const std::vector<bool> &values) {
return base::find(values, true) != values.end();
})
| rpl::distinct_until_changed()
| rpl::on_next([separator](bool someShown) {
separator->toggleAnimated(someShown);
})
| rpl::start(separator->lifetime());
separator->finishAnimations();
object_ptr<FloatingIcon>(result, st::infoIconInformation);
return result;
}
void InnerWidget::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
}
void InnerWidget::saveState(not_null<Memento*> memento) {
}
void InnerWidget::restoreState(not_null<Memento*> memento) {
}
int InnerWidget::resizeGetHeight(int newWidth) {
_content->resizeToWidth(newWidth);
return qMax(_content->height(), _minHeight);
}
} // namespace Profile
} // namespace Info

View file

@ -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
*/
#pragma once
#include <rpl/producer.h>
#include "ui/rp_widget.h"
#include "ui/wrap/vertical_layout.h"
namespace Info {
namespace Profile {
class Memento;
class InnerWidget final : public Ui::RpWidget {
public:
InnerWidget(QWidget *parent, not_null<PeerData*> peer);
not_null<PeerData*> peer() const {
return _peer;
}
void resizeToWidth(int newWidth, int minHeight) {
_minHeight = minHeight;
return RpWidget::resizeToWidth(newWidth);
}
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
protected:
int resizeGetHeight(int newWidth) override;
void visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) override;
private:
void setupContent();
object_ptr<RpWidget> setupDetailsContent(RpWidget *parent) const;
object_ptr<RpWidget> setupMuteToggle(RpWidget *parent) const;
object_ptr<RpWidget> setupInfoLines(RpWidget *parent) const;
void setupMainUserButtons(
Ui::VerticalLayout *wrap,
not_null<UserData*> user) const;
not_null<PeerData*> _peer;
int _visibleTop = 0;
int _visibleBottom = 0;
int _minHeight = 0;
object_ptr<Ui::VerticalLayout> _content;
rpl::lifetime _lifetime;
};
} // namespace Profile
} // namespace Info

View file

@ -0,0 +1,555 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_profile_lines.h"
#include <rpl/filter.h>
#include <rpl/never.h>
#include <rpl/before_next.h>
#include <rpl/combine_latest.h>
#include "styles/style_info.h"
#include "profile/profile_userpic_button.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 "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<Notify::PeerUpdate> PeerUpdateViewer(
Notify::PeerUpdate::Flags flags) {
return [=](const rpl::consumer<Notify::PeerUpdate> &consumer) {
auto lifetime = rpl::lifetime();
lifetime.make_state<base::Subscription>(
Notify::PeerUpdated().add_subscription({ flags, [=](
const Notify::PeerUpdate &update) {
consumer.put_next_copy(update);
}}));
return lifetime;
};
}
rpl::producer<Notify::PeerUpdate> PeerUpdateViewer(
not_null<PeerData*> peer,
Notify::PeerUpdate::Flags flags) {
return PeerUpdateViewer(flags)
| rpl::filter([=](const Notify::PeerUpdate &update) {
return (update.peer == peer);
});
}
rpl::producer<Notify::PeerUpdate> PeerUpdateValue(
not_null<PeerData*> peer,
Notify::PeerUpdate::Flags flags) {
return rpl::single(Notify::PeerUpdate())
| then(PeerUpdateViewer(peer, flags));
}
rpl::producer<TextWithEntities> PhoneViewer(
not_null<UserData*> user) {
return PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::UserPhoneChanged)
| rpl::map([user](auto&&) {
return App::formatPhone(user->phone());
})
| WithEmptyEntities();
}
rpl::producer<TextWithEntities> BioViewer(
not_null<UserData*> user) {
return PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::AboutChanged)
| rpl::map([user](auto&&) { return user->about(); })
| WithEmptyEntities();
}
rpl::producer<QString> PlainUsernameViewer(
not_null<PeerData*> peer) {
return PeerUpdateValue(
peer,
Notify::PeerUpdate::Flag::UsernameChanged)
| rpl::map([peer](auto&&) {
return peer->userName();
});
}
rpl::producer<TextWithEntities> UsernameViewer(
not_null<UserData*> user) {
return PlainUsernameViewer(user)
| rpl::map([](QString &&username) {
return username.isEmpty()
? QString()
: ('@' + username);
})
| WithEmptyEntities();
}
rpl::producer<TextWithEntities> AboutViewer(
not_null<PeerData*> 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<TextWithEntities> LinkViewer(
not_null<PeerData*> peer) {
return PlainUsernameViewer(peer)
| rpl::map([](QString &&username) {
return username.isEmpty()
? QString()
: Messenger::Instance().createInternalLink(username);
})
| WithEmptyEntities();
}
rpl::producer<bool> NotificationsEnabledViewer(
not_null<PeerData*> peer) {
return PeerUpdateValue(
peer,
Notify::PeerUpdate::Flag::NotificationsEnabled)
| rpl::map([peer](auto&&) { return !peer->isMuted(); });
}
rpl::producer<bool> IsContactViewer(
not_null<UserData*> user) {
return PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::UserIsContact)
| rpl::map([user](auto&&) { return user->isContact(); });
}
rpl::producer<bool> CanShareContactViewer(
not_null<UserData*> user) {
return PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::UserCanShareContact)
| rpl::map([user](auto&&) {
return user->canShareThisContact();
});
}
rpl::producer<bool> CanAddContactViewer(
not_null<UserData*> user) {
return rpl::combine_latest(
IsContactViewer(user),
CanShareContactViewer(user))
| rpl::map([](auto &&value) {
return !std::get<0>(value) && std::get<1>(value);
})
| rpl::map_error([](auto &&error) {
return *base::get_if<rpl::no_error>(&error);
});
}
FloatingIcon::FloatingIcon(
QWidget *parent,
not_null<RpWidget*> above,
const style::icon &icon)
: FloatingIcon(parent, above, icon, st::infoIconPosition, Tag{}) {
}
FloatingIcon::FloatingIcon(
QWidget *parent,
not_null<RpWidget*> above,
const style::icon &icon,
QPoint position)
: FloatingIcon(parent, above, icon, position, Tag{}) {
}
FloatingIcon::FloatingIcon(
QWidget *parent,
const style::icon &icon)
: FloatingIcon(parent, nullptr, icon, st::infoIconPosition, Tag{}) {
}
FloatingIcon::FloatingIcon(
QWidget *parent,
const style::icon &icon,
QPoint position)
: FloatingIcon(parent, nullptr, icon, position, Tag{}) {
}
FloatingIcon::FloatingIcon(
QWidget *parent,
RpWidget *above,
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);
if (above) {
above->geometryValue()
| rpl::on_next([this](QRect &&geometry) {
auto topLeft = rtlpoint(
geometry.topLeft(),
parentWidget()->width());
moveToLeft(topLeft.x(), topLeft.y() + geometry.height());
})
| rpl::start(lifetime());
} else {
moveToLeft(0, 0);
}
}
void FloatingIcon::paintEvent(QPaintEvent *e) {
Painter p(this);
_icon->paint(p, _point, width());
}
LabeledLine::LabeledLine(
QWidget *parent,
rpl::producer<TextWithEntities> &&label,
rpl::producer<TextWithEntities> &&text)
: LabeledLine(
parent,
std::move(label),
std::move(text),
st::infoLabeled,
st::infoProfileLabeledPadding) {
}
LabeledLine::LabeledLine(
QWidget *parent,
rpl::producer<TextWithEntities> &&label,
rpl::producer<TextWithEntities> &&text,
const style::FlatLabel &textSt,
const style::margins &padding)
: SlideWrap<Ui::VerticalLayout>(
parent,
object_ptr<Ui::VerticalLayout>(parent),
padding
) {
auto layout = entity();
auto nonEmptyText = std::move(text)
| rpl::before_next([this](const TextWithEntities &value) {
toggleAnimated(!value.text.isEmpty());
}) | rpl::filter([this](const TextWithEntities &value) {
return !value.text.isEmpty();
});
layout->add(object_ptr<Ui::FlatLabel>(
this,
std::move(nonEmptyText),
textSt));
layout->add(object_ptr<Ui::FlatLabel>(
this,
std::move(label),
st::infoLabel));
finishAnimations();
};
CoverLine::CoverLine(QWidget *parent, not_null<PeerData*> peer)
: RpWidget(parent)
, _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();
}
void CoverLine::setOnlineCount(int onlineCount) {
_onlineCount = onlineCount;
refreshStatusText();
}
void CoverLine::setHasToggle(bool hasToggle) {
if (hasToggle && !_toggle) {
_toggle.create(this, QString());
} else if (!hasToggle && _toggle) {
_toggle.destroy();
}
}
void CoverLine::initViewers() {
using Flag = Notify::PeerUpdate::Flag;
PeerUpdateViewer(_peer, Flag::PhotoChanged)
| rpl::on_next([this](auto&&) { refreshUserpicLink(); })
| rpl::start(_lifetime);
PeerUpdateViewer(_peer, Flag::NameChanged)
| rpl::on_next([this](auto&&) { refreshNameText(); })
| rpl::start(_lifetime);
PeerUpdateViewer(_peer,
Flag::UserOnlineChanged | Flag::MembersChanged)
| rpl::on_next([this](auto&&) { refreshStatusText(); })
| rpl::start(_lifetime);
}
void CoverLine::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 CoverLine::refreshUserpicLink() {
auto hasPhoto = (_peer->photoId != 0);
auto knownPhoto = (_peer->photoId != UnknownPeerPhotoId);
_userpic->setPointerCursor(hasPhoto && knownPhoto);
if (!knownPhoto) {
Auth().api().requestFullPeer(_peer);
}
}
void CoverLine::refreshNameText() {
_name->setText(App::peerName(_peer));
refreshNameGeometry(width());
}
void CoverLine::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 CoverLine::refreshNameGeometry(int newWidth) {
_name->resizeToWidth(newWidth
- st::infoProfileNameLeft
- st::infoProfileNameRight);
_name->moveToLeft(
st::infoProfileNameLeft,
st::infoProfileNameTop,
newWidth);
}
void CoverLine::refreshStatusGeometry(int newWidth) {
_status->resizeToWidth(newWidth
- st::infoProfileStatusLeft
- st::infoProfileStatusRight);
_status->moveToLeft(
st::infoProfileStatusLeft,
st::infoProfileStatusTop,
newWidth);
}
int CoverLine::resizeGetHeight(int newWidth) {
_userpic->moveToLeft(
st::infoProfilePhotoLeft,
st::infoProfilePhotoTop,
newWidth);
refreshNameGeometry(newWidth);
refreshStatusGeometry(newWidth);
if (_toggle) {
_toggle->moveToRight(
st::infoProfileToggleRight,
st::infoProfileToggleTop,
newWidth);
}
return st::infoProfilePhotoTop
+ _userpic->height()
+ st::infoProfilePhotoBottom;
}
rpl::producer<bool> CoverLine::toggled() const {
return _toggle
? base::ObservableViewer(_toggle->checkedChanged)
: rpl::never<bool>();
}
Button::Button(
QWidget *parent,
rpl::producer<QString> &&text)
: Button(parent, std::move(text), st::infoProfileButton) {
}
Button::Button(
QWidget *parent,
rpl::producer<QString> &&text,
const style::InfoProfileButton &st)
: RippleButton(parent, st.ripple)
, _st(st) {
std::move(text)
| rpl::on_next([this](QString &&value) {
setText(std::move(value));
})
| rpl::start(lifetime());
}
void Button::setToggled(bool toggled) {
if (!_toggle) {
_toggle = std::make_unique<Ui::ToggleView>(
isOver() ? _st.toggleOver : _st.toggle,
toggled,
[this] { rtlupdate(toggleRect()); });
clicks()
| rpl::on_next([this](auto) {
_toggle->setCheckedAnimated(!_toggle->checked());
})
| rpl::start(lifetime());
} else {
_toggle->setCheckedAnimated(toggled);
}
}
rpl::producer<bool> Button::toggledValue() const {
return _toggle ? _toggle->checkedValue() : rpl::never<bool>();
}
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.padding.right() - 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 -= _toggle->getSize().width()
+ _st.padding.right();
}
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

View file

@ -0,0 +1,211 @@
/*
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 <rpl/producer.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;
} // namespace style
namespace Lang {
rpl::producer<QString> 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<TextWithEntities> PhoneViewer(
not_null<UserData*> user);
rpl::producer<TextWithEntities> BioViewer(
not_null<UserData*> user);
rpl::producer<TextWithEntities> UsernameViewer(
not_null<UserData*> user);
rpl::producer<TextWithEntities> AboutViewer(
not_null<PeerData*> peer);
rpl::producer<TextWithEntities> LinkViewer(
not_null<PeerData*> peer);
rpl::producer<bool> NotificationsEnabledViewer(
not_null<PeerData*> peer);
rpl::producer<bool> IsContactViewer(
not_null<UserData*> user);
rpl::producer<bool> CanShareContactViewer(
not_null<UserData*> user);
rpl::producer<bool> CanAddContactViewer(
not_null<UserData*> user);
class FloatingIcon : public Ui::RpWidget {
public:
FloatingIcon(
QWidget *parent,
not_null<RpWidget*> above,
const style::icon &icon);
FloatingIcon(
QWidget *parent,
not_null<RpWidget*> above,
const style::icon &icon,
QPoint position);
FloatingIcon(
QWidget *parent,
const style::icon &icon);
FloatingIcon(
QWidget *parent,
const style::icon &icon,
QPoint position);
protected:
void paintEvent(QPaintEvent *e) override;
private:
struct Tag {
};
FloatingIcon(
QWidget *parent,
RpWidget *above,
const style::icon &icon,
QPoint position,
const Tag &);
not_null<const style::icon*> _icon;
QPoint _point;
};
class LabeledLine : public Ui::SlideWrap<Ui::VerticalLayout> {
public:
LabeledLine(
QWidget *parent,
rpl::producer<TextWithEntities> &&label,
rpl::producer<TextWithEntities> &&text);
LabeledLine(
QWidget *parent,
rpl::producer<TextWithEntities> &&label,
rpl::producer<TextWithEntities> &&text,
const style::FlatLabel &textSt,
const style::margins &padding);
};
class CoverLine : public Ui::RpWidget {
public:
CoverLine(QWidget *parent, not_null<PeerData*> peer);
void setOnlineCount(int onlineCount);
void setHasToggle(bool hasToggle);
rpl::producer<bool> toggled() const;
protected:
int resizeGetHeight(int newWidth) override;
private:
void initViewers();
void initUserpicButton();
void refreshUserpicLink();
void refreshNameText();
void refreshStatusText();
void refreshNameGeometry(int newWidth);
void refreshStatusGeometry(int newWidth);
not_null<PeerData*> _peer;
int _onlineCount = 0;
object_ptr<::Profile::UserpicButton> _userpic;
object_ptr<Ui::FlatLabel> _name = { nullptr };
object_ptr<Ui::FlatLabel> _status = { nullptr };
object_ptr<Ui::Checkbox> _toggle = { nullptr };
//object_ptr<CoverDropArea> _dropArea = { nullptr };
rpl::lifetime _lifetime;
};
class Button : public Ui::RippleButton {
public:
Button(
QWidget *parent,
rpl::producer<QString> &&text);
Button(
QWidget *parent,
rpl::producer<QString> &&text,
const style::InfoProfileButton &st);
void setToggled(bool toggled);
rpl::producer<bool> 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<Ui::ToggleView> _toggle;
};
} // namespace Profile
} // namespace Info

View file

@ -0,0 +1,94 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_profile_widget.h"
#include "info/info_profile_inner_widget.h"
#include "ui/widgets/scroll_area.h"
namespace Info {
namespace Profile {
object_ptr<ContentWidget> Memento::createWidget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
const QRect &geometry) {
auto result = object_ptr<Widget>(
parent,
wrap,
controller,
App::peer(_peerId));
result->setInternalState(geometry, this);
return std::move(result);
}
Widget::Widget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
not_null<PeerData*> peer)
: ContentWidget(parent, wrap, controller) {
_inner = setInnerWidget(object_ptr<InnerWidget>(this, peer));
_inner->move(0, 0);
}
not_null<PeerData*> Widget::peer() const {
return _inner->peer();
}
void Widget::setInnerFocus() {
_inner->setFocus();
}
bool Widget::showInternal(not_null<ContentMemento*> memento) {
if (auto profileMemento = dynamic_cast<Memento*>(memento.get())) {
if (profileMemento->peerId() == peer()->id) {
restoreState(profileMemento);
return true;
}
}
return false;
}
void Widget::setInternalState(const QRect &geometry, not_null<Memento*> memento) {
setGeometry(geometry);
myEnsureResized(this);
restoreState(memento);
}
std::unique_ptr<ContentMemento> Widget::createMemento() {
auto result = std::make_unique<Memento>(peer()->id);
saveState(result.get());
return std::move(result);
}
void Widget::saveState(not_null<Memento*> memento) {
memento->setScrollTop(scrollTopSave());
_inner->saveState(memento);
}
void Widget::restoreState(not_null<Memento*> memento) {
_inner->restoreState(memento);
scrollTopRestore(memento->scrollTop());
}
} // namespace Profile
} // namespace Info

View file

@ -0,0 +1,92 @@
/*
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 <rpl/producer.h>
#include "info/info_memento.h"
namespace Info {
namespace Profile {
class InnerWidget;
class Memento final : public ContentMemento {
public:
Memento(PeerId peerId) : _peerId(peerId) {
}
object_ptr<ContentWidget> createWidget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
const QRect &geometry) override;
PeerId peerId() const {
return _peerId;
}
void setScrollTop(int scrollTop) {
_scrollTop = scrollTop;
}
int scrollTop() const {
return _scrollTop;
}
private:
PeerId _peerId = 0;
int _scrollTop = 0;
};
class Widget final : public ContentWidget {
public:
Widget(
QWidget *parent,
Wrap wrap,
not_null<Window::Controller*> controller,
not_null<PeerData*> peer);
not_null<PeerData*> peer() const;
bool showInternal(
not_null<ContentMemento*> memento) override;
std::unique_ptr<ContentMemento> createMemento() override;
void setInternalState(
const QRect &geometry,
not_null<Memento*> memento);
void setInnerFocus() override;
private:
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
void scrollTopUpdated(int scrollTop);
InnerWidget *_inner = nullptr;
rpl::lifetime _lifetime;
};
} // namespace Profile
} // namespace Info

View file

@ -0,0 +1,235 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_side_wrap.h"
#include <rpl/flatten_latest.h>
#include "info/info_profile_widget.h"
#include "info/info_media_widget.h"
#include "info/info_memento.h"
#include "ui/widgets/discrete_sliders.h"
#include "ui/widgets/shadow.h"
#include "lang/lang_keys.h"
#include "styles/style_info.h"
#include "styles/style_profile.h"
namespace Info {
SideWrap::SideWrap(
QWidget *parent,
not_null<Window::Controller*> controller,
not_null<PeerData*> peer)
: Window::SectionWidget(parent, controller)
, _peer(peer) {
}
void SideWrap::setupTabs() {
_tabsShadow.create(this, st::shadowFg);
_tabs.create(this, st::infoTabs);
auto sections = QStringList();
sections.push_back(lang(lng_profile_info_section));
sections.push_back(lang(lng_info_tab_media));
_tabs->setSections(sections);
_tabs->sectionActivated()
| rpl::map([](int index) { return static_cast<Tab>(index); })
| rpl::on_next([this](Tab tab) { showTab(tab); })
| rpl::start(_lifetime);
_tabs->move(0, 0);
_tabs->resizeToWidth(width());
_tabs->show();
_tabsShadow->setGeometry(
0,
_tabs->height() - st::lineWidth,
width(),
st::lineWidth);
_tabsShadow->show();
}
void SideWrap::showTab(Tab tab) {
showInner(createInner(tab));
}
void SideWrap::showInner(object_ptr<ContentWidget> inner) {
_inner = std::move(inner);
_inner->setGeometry(innerGeometry());
_inner->show();
_desiredHeights.fire(desiredHeightForInner());
}
rpl::producer<int> SideWrap::desiredHeightForInner() const {
auto result = _inner->desiredHeightValue();
if (_tabs) {
result = std::move(result)
| rpl::map(func::add(_tabs->height()));
}
return result;
}
object_ptr<ContentWidget> SideWrap::createInner(Tab tab) {
switch (tab) {
case Tab::Profile: return createProfileWidget();
case Tab::Media: return createMediaWidget();
}
Unexpected("Tab value in Info::SideWrap::createInner()");
}
object_ptr<Profile::Widget> SideWrap::createProfileWidget() {
auto result = object_ptr<Profile::Widget>(
this,
Wrap::Side,
controller(),
_peer);
return result;
}
object_ptr<Media::Widget> SideWrap::createMediaWidget() {
auto result = object_ptr<Media::Widget>(
this,
Wrap::Side,
controller(),
_peer,
Media::Widget::Type::Photo);
return result;
}
QPixmap SideWrap::grabForShowAnimation(
const Window::SectionSlideParams &params) {
if (params.withTopBarShadow) _tabsShadow->hide();
auto result = myGrab(this);
if (params.withTopBarShadow) _tabsShadow->show();
return result;
}
void SideWrap::doSetInnerFocus() {
_inner->setInnerFocus();
}
bool SideWrap::showInternal(
not_null<Window::SectionMemento*> memento) {
if (auto infoMemento = dynamic_cast<Memento*>(memento.get())) {
if (infoMemento->peerId() == peer()->id) {
restoreState(infoMemento);
return true;
}
}
return false;
}
void SideWrap::setInternalState(
const QRect &geometry,
not_null<Memento*> memento) {
setGeometry(geometry);
restoreState(memento);
if (_tabs) {
_tabs->finishAnimations();
}
}
std::unique_ptr<Window::SectionMemento> SideWrap::createMemento() {
auto result = std::make_unique<Memento>(peer()->id);
saveState(result.get());
return std::move(result);
}
rpl::producer<int> SideWrap::desiredHeightValue() const {
return
rpl::single(desiredHeightForInner())
| rpl::then(_desiredHeights.events())
| rpl::flatten_latest();
}
void SideWrap::saveState(not_null<Memento*> memento) {
memento->setInner(_inner->createMemento());
}
QRect SideWrap::innerGeometry() const {
return (_tab == Tab::None)
? rect()
: rect().marginsRemoved({ 0, _tabs->height(), 0, 0 });
}
void SideWrap::restoreState(not_null<Memento*> memento) {
switch (memento->section().type()) {
case Section::Type::Profile:
setCurrentTab(Tab::Profile);
break;
case Section::Type::Media:
switch (memento->section().mediaType()) {
case Section::MediaType::Photo:
case Section::MediaType::Video:
case Section::MediaType::File:
setCurrentTab(Tab::Media);
break;
default:
setCurrentTab(Tab::None);
break;
}
break;
}
showInner(memento->content()->createWidget(
this,
Wrap::Side,
controller(),
innerGeometry()));
}
void SideWrap::setCurrentTab(Tab tab) {
_tab = tab;
if (_tab == Tab::None) {
_tabs.destroy();
} else if (!_tabs) {
setupTabs();
} else {
_tabs->setActiveSection(static_cast<int>(tab));
}
}
void SideWrap::resizeEvent(QResizeEvent *e) {
if (_tabs) {
_tabs->resizeToWidth(width());
}
if (_inner) {
_inner->setGeometry(innerGeometry());
}
}
void SideWrap::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), st::profileBg);
}
bool SideWrap::wheelEventFromFloatPlayer(
QEvent *e,
Window::Column myColumn,
Window::Column playerColumn) {
return _inner->wheelEventFromFloatPlayer(e);
}
QRect SideWrap::rectForFloatPlayer(
Window::Column myColumn,
Window::Column playerColumn) const {
return _inner->rectForFloatPlayer();
}
} // namespace Info

View file

@ -0,0 +1,122 @@
/*
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 <rpl/event_stream.h>
#include "window/section_widget.h"
namespace Ui {
class PlainShadow;
class SettingsSlider;
} // namespace Ui
namespace Info {
namespace Profile {
class Widget;
} // namespace Profile
namespace Media {
class Widget;
} // namespace Media
class Memento;
class ContentWidget;
class SideWrap final : public Window::SectionWidget {
public:
SideWrap(
QWidget *parent,
not_null<Window::Controller*> controller,
not_null<PeerData*> peer);
not_null<PeerData*> peer() const {
return _peer;
}
PeerData *peerForDialogs() const override {
return _peer;
}
bool hasTopBarShadow() const override {
return true;
}
QPixmap grabForShowAnimation(
const Window::SectionSlideParams &params) override;
bool showInternal(
not_null<Window::SectionMemento*> memento) override;
std::unique_ptr<Window::SectionMemento> createMemento() override;
rpl::producer<int> desiredHeightValue() const override;
void setInternalState(
const QRect &geometry,
not_null<Memento*> memento);
// Float player interface.
bool wheelEventFromFloatPlayer(
QEvent *e,
Window::Column myColumn,
Window::Column playerColumn) override;
QRect rectForFloatPlayer(
Window::Column myColumn,
Window::Column playerColumn) const override;
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void doSetInnerFocus() override;
private:
enum class Tab {
Profile,
Media,
None,
};
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
QRect innerGeometry() const;
rpl::producer<int> desiredHeightForInner() const;
void setupTabs();
void showTab(Tab tab);
void setCurrentTab(Tab tab);
void showInner(object_ptr<ContentWidget> inner);
object_ptr<ContentWidget> createInner(Tab tab);
object_ptr<Profile::Widget> createProfileWidget();
object_ptr<Media::Widget> createMediaWidget();
not_null<PeerData*> _peer;
object_ptr<Ui::PlainShadow> _tabsShadow = { nullptr };
object_ptr<Ui::SettingsSlider> _tabs = { nullptr };
object_ptr<ContentWidget> _inner = { nullptr };
Tab _tab = Tab::Profile;
rpl::event_stream<rpl::producer<int>> _desiredHeights;
rpl::lifetime _lifetime;
};
} // namespace Info

View file

@ -0,0 +1,84 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "info/info_top_bar.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
namespace Info {
TopBar::TopBar(QWidget *parent, const style::InfoTopBar &st)
: RpWidget(parent)
, _st(st) {
setAttribute(Qt::WA_OpaquePaintEvent);
}
void TopBar::setTitle(rpl::producer<QString> &&title) {
_title.create(this, std::move(title), _st.title);
}
void TopBar::enableBackButton(bool enable) {
if (enable) {
_back.create(this, _st.back);
_back->clicks()
| rpl::to_stream(_backClicks)
| rpl::start(_lifetime);
} else {
_back.destroy();
}
if (_title) {
_title->setAttribute(Qt::WA_TransparentForMouseEvents, enable);
}
updateControlsGeometry(width());
}
void TopBar::pushButton(object_ptr<Ui::RpWidget> button) {
auto weak = Ui::AttachParentChild(this, button);
_buttons.push_back(std::move(button));
weak->widthValue()
| rpl::on_next([this](auto) {
updateControlsGeometry(width());
})
| rpl::start(_lifetime);
}
int TopBar::resizeGetHeight(int newWidth) {
updateControlsGeometry(newWidth);
return _st.height;
}
void TopBar::updateControlsGeometry(int newWidth) {
auto right = 0;
for (auto &button : _buttons) {
button->moveToRight(right, 0, newWidth);
right += button->width();
}
if (_back) {
_back->setGeometryToLeft(0, 0, newWidth - right, _back->height(), newWidth);
}
}
void TopBar::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), _st.bg);
}
} // namespace Info

View file

@ -0,0 +1,72 @@
/*
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 style {
struct InfoTopBar;
} // namespace style
namespace Ui {
class IconButton;
class FlatLabel;
} // namespace Ui
namespace Info {
class TopBar : public Ui::RpWidget {
public:
TopBar(QWidget *parent, const style::InfoTopBar &st);
rpl::producer<> backRequest() const {
return _backClicks.events();
}
void setTitle(rpl::producer<QString> &&title);
void enableBackButton(bool enable);
template <typename ButtonWidget>
ButtonWidget *addButton(object_ptr<ButtonWidget> button) {
auto result = button.data();
pushButton(std::move(button));
return result;
}
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
void updateControlsGeometry(int newWidth);
void pushButton(object_ptr<Ui::RpWidget> button);
const style::InfoTopBar &_st;
object_ptr<Ui::IconButton> _back = { nullptr };
std::vector<object_ptr<Ui::RpWidget>> _buttons;
object_ptr<Ui::FlatLabel> _title = { nullptr };
rpl::event_stream<> _backClicks;
rpl::lifetime _lifetime;
};
} // namespace Info

View file

@ -0,0 +1,155 @@
/*
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 <rpl/producer.h>
#include <rpl/complete.h>
#include "base/optional.h"
namespace rpl {
namespace details {
template <typename Value>
struct combine_latest_vector_state {
std::vector<base::optional<Value>> accumulated;
std::vector<Value> latest;
int invalid = 0;
int working = 0;
};
} // namespace details
template <typename Value, typename Error>
producer<std::vector<Value>, Error> combine_latest(
std::vector<producer<Value, Error>> &&producers) {
if (producers.empty()) {
return complete<std::vector<Value>, Error>();
}
using state_type = details::combine_latest_vector_state<Value>;
using consumer_type = consumer<std::vector<Value>, Error>;
return [producers = std::move(producers)](
const consumer_type &consumer) mutable {
auto count = producers.size();
auto state = consumer.make_state<state_type>();
state->accumulated.resize(count);
state->invalid = count;
state->working = count;
for (auto index = 0; index != count; ++index) {
auto &producer = producers[index];
consumer.add_lifetime(std::move(producer).start(
[consumer, state, index](Value &&value) {
if (state->accumulated.empty()) {
state->latest[index] = std::move(value);
consumer.put_next_copy(state->latest);
} else if (state->accumulated[index]) {
state->accumulated[index] = std::move(value);
} else {
state->accumulated[index] = std::move(value);
if (!--state->invalid) {
state->latest.reserve(
state->accumulated.size());
for (auto &&value : state->accumulated) {
state->latest.push_back(
std::move(*value));
}
base::take(state->accumulated);
consumer.put_next_copy(state->latest);
}
}
}, [consumer](Error &&error) {
consumer.put_error(std::move(error));
}, [consumer, state] {
if (!--state->working) {
consumer.put_done();
}
}));
}
return lifetime();
};
}
namespace details {
template <typename Value, typename ...Values>
struct combine_latest_tuple_state {
base::optional<Value> first;
base::optional<std::tuple<Values...>> others;
int working = 2;
};
} // namespace details
template <
typename Value,
typename Error,
typename ...Values,
typename ...Errors>
producer<std::tuple<Value, Values...>, base::variant<Error, Errors...>> combine_latest(
producer<Value, Error> &&first,
producer<Values, Errors> &&...others) {
auto others_combined = combine_latest(std::move(others)...);
return [
first = std::move(first),
others = std::move(others_combined)
](const consumer<std::tuple<Value, Values...>, base::variant<Error, Errors...>> &consumer) mutable {
auto state = consumer.make_state<details::combine_latest_tuple_state<Value, Values...>>();
consumer.add_lifetime(std::move(first).start([consumer, state](Value &&value) {
state->first = std::move(value);
if (state->others) {
consumer.put_next(std::tuple_cat(std::make_tuple(*state->first), *state->others));
}
}, [consumer](Error &&error) {
consumer.put_error(std::move(error));
}, [consumer, state] {
if (!--state->working) {
consumer.put_done();
}
}));
consumer.add_lifetime(std::move(others).start([consumer, state](std::tuple<Values...> &&value) {
state->others = std::move(value);
if (state->first) {
consumer.put_next(std::tuple_cat(std::make_tuple(*state->first), *state->others));
}
}, [consumer](base::variant<Errors...> &&error) {
base::visit([&](auto &&errorValue) {
consumer.put_error(std::move(errorValue));
}, std::move(error));
}, [consumer, state] {
if (!--state->working) {
consumer.put_done();
}
}));
return lifetime();
};
}
template <
typename Value,
typename Error>
producer<std::tuple<Value>, Error> combine_latest(
producer<Value, Error> &&producer) {
return std::move(producer) | map([](auto &&value) {
return std::make_tuple(std::forward<decltype(value)>(value));
});
}
} // namespace rpl

View file

@ -28,13 +28,12 @@ namespace details {
template <
typename Transform,
typename NewValue,
typename Error,
typename Value>
typename Error>
class map_transform_helper {
public:
map_transform_helper(
const consumer<NewValue, Error> &consumer,
Transform &&transform)
Transform &&transform,
const consumer<NewValue, Error> &consumer)
: _transform(std::move(transform))
, _consumer(consumer) {
}
@ -59,6 +58,18 @@ private:
};
template <
typename Transform,
typename NewValue,
typename Error,
typename = std::enable_if_t<
std::is_rvalue_reference_v<Transform&&>>>
map_transform_helper<Transform, NewValue, Error> map_transform(
Transform &&transform,
const consumer<NewValue, Error> &consumer) {
return { std::move(transform), consumer };
}
template <typename Transform>
class map_helper {
public:
@ -80,11 +91,12 @@ public:
transform = std::move(_transform)
](const consumer<NewValue, Error> &consumer) mutable {
return std::move(initial).start(
map_transform_helper<Transform, NewValue, Error, Value>(
consumer,
std::move(transform)
map_transform(
std::move(transform),
consumer
), [consumer](auto &&error) {
consumer.put_error_forward(std::forward<decltype(error)>(error));
consumer.put_error_forward(
std::forward<decltype(error)>(error));
}, [consumer] {
consumer.put_done();
});
@ -105,4 +117,99 @@ auto map(Transform &&transform)
std::forward<Transform>(transform));
}
namespace details {
template <
typename Transform,
typename Value,
typename NewError>
class map_error_transform_helper {
public:
map_error_transform_helper(
Transform &&transform,
const consumer<Value, NewError> &consumer)
: _transform(std::move(transform))
, _consumer(consumer) {
}
template <
typename OtherError,
typename = std::enable_if_t<
std::is_rvalue_reference_v<OtherError&&>>>
void operator()(OtherError &&error) const {
_consumer.put_error_forward(_transform(std::move(error)));
}
template <
typename OtherError,
typename = decltype(
std::declval<Transform>()(const_ref_val<OtherError>()))>
void operator()(const OtherError &error) const {
_consumer.put_error_forward(_transform(error));
}
private:
consumer<Value, NewError> _consumer;
Transform _transform;
};
template <
typename Transform,
typename Value,
typename NewError,
typename = std::enable_if_t<
std::is_rvalue_reference_v<Transform&&>>>
map_error_transform_helper<Transform, Value, NewError>
map_error_transform(
Transform &&transform,
const consumer<Value, NewError> &consumer) {
return { std::move(transform), consumer };
}
template <typename Transform>
class map_error_helper {
public:
template <typename OtherTransform>
map_error_helper(OtherTransform &&transform)
: _transform(std::forward<OtherTransform>(transform)) {
}
template <
typename Value,
typename Error,
typename NewError = decltype(
std::declval<Transform>()(std::declval<Error>())
)>
rpl::producer<Value, NewError> operator()(
rpl::producer<Value, Error> &&initial) {
return [
initial = std::move(initial),
transform = std::move(_transform)
](const consumer<Value, NewError> &consumer) mutable {
return std::move(initial).start(
[consumer](auto &&value) {
consumer.put_next_forward(
std::forward<decltype(value)>(value));
}, map_error_transform(
std::move(transform),
consumer
), [consumer] {
consumer.put_done();
});
};
}
private:
Transform _transform;
};
} // namespace details
template <typename Transform>
auto map_error(Transform &&transform)
-> details::map_error_helper<std::decay_t<Transform>> {
return details::map_error_helper<std::decay_t<Transform>>(
std::forward<Transform>(transform));
}
} // namespace rpl

View file

@ -36,5 +36,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/filter.h>
#include <rpl/distinct_until_changed.h>
#include <rpl/flatten_latest.h>
#include <rpl/combine_latest.h>
#include <rpl/before_next.h>

View file

@ -36,8 +36,9 @@ enum class SharedMediaType : char {
ChatPhoto = 6,
RoundVoiceFile = 7,
GIF = 8,
RoundFile = 9,
kCount = 9,
kCount = 10,
};
constexpr auto kSharedMediaTypeCount = static_cast<int>(SharedMediaType::kCount);
constexpr bool IsValidSharedMediaType(SharedMediaType type) {

View file

@ -595,7 +595,11 @@ public:
auto &link = links[lnkIndex - maxLnkIndex - 1];
ClickHandlerPtr handler;
switch (link.type) {
case EntityInTextCustomUrl: handler = MakeShared<HiddenUrlClickHandler>(link.data); break;
case EntityInTextCustomUrl: {
if (!link.data.isEmpty()) {
handler = MakeShared<HiddenUrlClickHandler>(link.data);
}
} break;
case EntityInTextEmail:
case EntityInTextUrl: handler = MakeShared<UrlClickHandler>(link.data, link.displayStatus == LinkDisplayedFull); break;
case EntityInTextBotCommand: handler = MakeShared<BotCommandClickHandler>(link.data); break;

View file

@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/buttons.h"
#include "styles/style_widgets.h"
#include <rpl/event_stream.h>
namespace Ui {
@ -38,6 +39,10 @@ public:
}
float64 currentAnimationValue(TimeMs ms);
rpl::producer<bool> checkedValue() const {
return _checks.events_starting_with(checked());
}
virtual QSize getSize() const = 0;
// Zero instead of ms value means that animation was already updated for this time.
@ -59,6 +64,8 @@ private:
base::lambda<void()> _updateCallback;
Animation _toggleAnimation;
rpl::event_stream<bool> _checks;
};
class CheckView : public AbstractCheckView {

View file

@ -18,17 +18,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once
#include "styles/style_widgets.h"
#include "ui/rp_widget.h"
namespace Ui {
class PlainShadow : public TWidget {
class PlainShadow : public RpWidget {
public:
PlainShadow(QWidget *parent, style::color color) : TWidget(parent), _color(color) {
PlainShadow(QWidget *parent, style::color color)
: RpWidget(parent), _color(color) {
resize(st::lineWidth, st::lineWidth);
}
protected:
void paintEvent(QPaintEvent *e) override {
Painter(this).fillRect(e->rect(), _color->b);
Painter(this).fillRect(e->rect(), _color);
}
private:

View file

@ -496,6 +496,23 @@ PeerAvatarButton {
photoSize: pixels;
}
InfoProfileButton {
textFg: color;
textFgOver: color;
textBg: color;
textBgOver: color;
font: font;
height: pixels;
padding: margins;
toggle: Toggle;
toggleOver: Toggle;
ripple: RippleAnimation;
}
defaultLabelSimple: LabelSimple {
font: normalFont;
maxWidth: 0px;
@ -1041,3 +1058,11 @@ ProfilePeerListItem {
statusFgOver: color;
statusFgActive: color;
}
InfoTopBar {
height: pixels;
back: IconButton;
title: FlatLabel;
titlePosition: point;
bg: color;
}

View file

@ -66,16 +66,16 @@ void SlideWrap<RpWidget>::animationStep() {
weak->moveToLeft(margins.left(), margins.top());
newWidth = weak->width();
}
auto current = _slideAnimation.current(_visible ? 1. : 0.);
auto current = _slideAnimation.current(_shown ? 1. : 0.);
auto newHeight = wrapped()
? (_slideAnimation.animating()
? anim::interpolate(0, wrapped()->heightNoMargins(), current)
: (_visible ? wrapped()->height() : 0))
: (_shown ? wrapped()->height() : 0))
: 0;
if (newWidth != width() || newHeight != height()) {
resize(newWidth, newHeight);
}
auto shouldBeHidden = !_visible && !_slideAnimation.animating();
auto shouldBeHidden = !_shown && !_slideAnimation.animating();
if (shouldBeHidden != isHidden()) {
setVisible(!shouldBeHidden);
if (shouldBeHidden) {
@ -84,23 +84,28 @@ void SlideWrap<RpWidget>::animationStep() {
}
}
void SlideWrap<RpWidget>::toggleAnimated(bool visible) {
if (_visible == visible) {
void SlideWrap<RpWidget>::setShown(bool shown) {
_shown = shown;
_shownUpdated.fire_copy(_shown);
}
void SlideWrap<RpWidget>::toggleAnimated(bool shown) {
if (_shown == shown) {
animationStep();
return;
}
_visible = visible;
setShown(shown);
_slideAnimation.start(
[this] { animationStep(); },
_visible ? 0. : 1.,
_visible ? 1. : 0.,
_shown ? 0. : 1.,
_shown ? 1. : 0.,
_duration,
anim::linear);
animationStep();
}
void SlideWrap<RpWidget>::toggleFast(bool visible) {
_visible = visible;
void SlideWrap<RpWidget>::toggleFast(bool shown) {
setShown(shown);
finishAnimations();
}
@ -111,7 +116,7 @@ void SlideWrap<RpWidget>::finishAnimations() {
QMargins SlideWrap<RpWidget>::getMargins() const {
auto result = wrapped()->getMargins();
return (animating() || !_visible)
return (animating() || !_shown)
? QMargins(result.left(), 0, result.right(), 0)
: result;
}
@ -126,7 +131,7 @@ int SlideWrap<RpWidget>::resizeGetHeight(int newWidth) {
void SlideWrap<RpWidget>::wrappedSizeUpdated(QSize size) {
if (_slideAnimation.animating()) {
animationStep();
} else if (_visible) {
} else if (_shown) {
resize(size);
}
}

View file

@ -47,8 +47,8 @@ public:
const style::margins &padding,
int duration);
void toggleAnimated(bool visible);
void toggleFast(bool visible);
void toggleAnimated(bool shown);
void toggleFast(bool shown);
void showAnimated() {
toggleAnimated(true);
@ -72,7 +72,11 @@ public:
QMargins getMargins() const override;
bool isHiddenOrHiding() const {
return !_visible;
return !_shown;
}
rpl::producer<bool> shownValue() const {
return _shownUpdated.events_starting_with_copy(_shown);
}
protected:
@ -81,8 +85,10 @@ protected:
private:
void animationStep();
void setShown(bool shown);
bool _visible = true;
bool _shown = true;
rpl::event_stream<bool> _shownUpdated;
Animation _slideAnimation;
int _duration = 0;

View file

@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
class LayerWidget;
namespace Window {
class Controller;
@ -27,10 +29,18 @@ class SectionWidget;
class SectionMemento {
public:
virtual object_ptr<Window::SectionWidget> createWidget(QWidget *parent, not_null<Window::Controller*> controller, const QRect &geometry) = 0;
virtual ~SectionMemento() {
virtual object_ptr<Window::SectionWidget> createWidget(
QWidget *parent,
not_null<Window::Controller*> controller,
const QRect &geometry) = 0;
virtual object_ptr<LayerWidget> createLayer(
not_null<Window::Controller*> controller) {
return nullptr;
}
virtual ~SectionMemento() = default;
};
} // namespace Window

View file

@ -21,13 +21,19 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "window/section_widget.h"
#include "application.h"
#include <rpl/single.h>
namespace Window {
SectionWidget::SectionWidget(QWidget *parent, not_null<Window::Controller*> controller) : AbstractSectionWidget(parent, controller) {
SectionWidget::SectionWidget(
QWidget *parent,
not_null<Window::Controller*> controller)
: AbstractSectionWidget(parent, controller) {
}
void SectionWidget::setGeometryWithTopMoved(const QRect &newGeometry, int topDelta) {
void SectionWidget::setGeometryWithTopMoved(
const QRect &newGeometry,
int topDelta) {
_topDelta = topDelta;
bool willBeResized = (size() != newGeometry.size());
if (geometry() != newGeometry) {
@ -39,7 +45,9 @@ void SectionWidget::setGeometryWithTopMoved(const QRect &newGeometry, int topDel
_topDelta = 0;
}
void SectionWidget::showAnimated(SlideDirection direction, const SectionSlideParams &params) {
void SectionWidget::showAnimated(
SlideDirection direction,
const SectionSlideParams &params) {
if (_showAnimation) return;
showChildren();
@ -51,7 +59,9 @@ void SectionWidget::showAnimated(SlideDirection direction, const SectionSlidePar
_showAnimation->setDirection(direction);
_showAnimation->setRepaintCallback([this] { update(); });
_showAnimation->setFinishedCallback([this] { showFinished(); });
_showAnimation->setPixmaps(params.oldContentCache, myContentCache);
_showAnimation->setPixmaps(
params.oldContentCache,
myContentCache);
_showAnimation->setTopBarShadow(params.withTopBarShadow);
_showAnimation->start();
@ -82,4 +92,8 @@ void SectionWidget::showFinished() {
setInnerFocus();
}
rpl::producer<int> SectionWidget::desiredHeight() const {
return rpl::single(height());
}
} // namespace Window

View file

@ -115,6 +115,8 @@ public:
doSetInnerFocus();
}
virtual rpl::producer<int> desiredHeight() const;
// Global shortcut handler. For now that ugly :(
virtual bool cmd_search() {
return false;
@ -150,7 +152,7 @@ private:
std::unique_ptr<SlideAnimation> _showAnimation;
// Saving here topDelta in resizeWithTopMoved() to get it passed to resizeEvent().
// Saving here topDelta in setGeometryWithTopMoved() to get it passed to resizeEvent().
int _topDelta = 0;
};

View file

@ -41,6 +41,7 @@
'<(src_loc)/calls/calls.style',
'<(src_loc)/dialogs/dialogs.style',
'<(src_loc)/history/history.style',
'<(src_loc)/info/info.style',
'<(src_loc)/intro/intro.style',
'<(src_loc)/media/view/mediaview.style',
'<(src_loc)/media/player/media_player.style',

View file

@ -193,6 +193,30 @@
<(src_loc)/history/history_user_photos.h
<(src_loc)/history/history_widget.cpp
<(src_loc)/history/history_widget.h
<(src_loc)/info/info_common_groups_inner_widget.cpp
<(src_loc)/info/info_common_groups_inner_widget.h
<(src_loc)/info/info_common_groups_widget.cpp
<(src_loc)/info/info_common_groups_widget.h
<(src_loc)/info/info_layer_wrap.cpp
<(src_loc)/info/info_layer_wrap.h
<(src_loc)/info/info_media_inner_widget.cpp
<(src_loc)/info/info_media_inner_widget.h
<(src_loc)/info/info_media_widget.cpp
<(src_loc)/info/info_media_widget.h
<(src_loc)/info/info_memento.cpp
<(src_loc)/info/info_memento.h
<(src_loc)/info/info_narrow_wrap.cpp
<(src_loc)/info/info_narrow_wrap.h
<(src_loc)/info/info_profile_inner_widget.cpp
<(src_loc)/info/info_profile_inner_widget.h
<(src_loc)/info/info_profile_lines.cpp
<(src_loc)/info/info_profile_lines.h
<(src_loc)/info/info_profile_widget.cpp
<(src_loc)/info/info_profile_widget.h
<(src_loc)/info/info_side_wrap.cpp
<(src_loc)/info/info_side_wrap.h
<(src_loc)/info/info_top_bar.cpp
<(src_loc)/info/info_top_bar.h
<(src_loc)/inline_bots/inline_bot_layout_internal.cpp
<(src_loc)/inline_bots/inline_bot_layout_internal.h
<(src_loc)/inline_bots/inline_bot_layout_item.cpp

View file

@ -100,6 +100,7 @@
'<(src_loc)/rpl/before_next.h',
'<(src_loc)/rpl/combine_latest.h',
'<(src_loc)/rpl/complete.h',
'<(src_loc)/rpl/combine_latest.h',
'<(src_loc)/rpl/consumer.h',
'<(src_loc)/rpl/deferred.h',
'<(src_loc)/rpl/distinct_until_changed.h',