Start new Info section (profile + shared media).
After Width: | Height: | Size: 136 B |
After Width: | Height: | Size: 242 B |
After Width: | Height: | Size: 572 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 224 B |
After Width: | Height: | Size: 556 B |
After Width: | Height: | Size: 275 B |
After Width: | Height: | Size: 575 B |
After Width: | Height: | Size: 551 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 593 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 588 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 398 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 337 B |
After Width: | Height: | Size: 486 B |
After Width: | Height: | Size: 664 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 476 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 512 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 570 B |
After Width: | Height: | Size: 1005 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 194 B |
After Width: | Height: | Size: 350 B |
After Width: | Height: | Size: 459 B |
After Width: | Height: | Size: 1.2 KiB |
|
@ -553,16 +553,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
"lng_profile_recent_actions" = "Recent actions";
|
"lng_profile_recent_actions" = "Recent actions";
|
||||||
"lng_profile_common_groups#one" = "{count} group in common";
|
"lng_profile_common_groups#one" = "{count} group in common";
|
||||||
"lng_profile_common_groups#other" = "{count} groups 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_participants_section" = "Members";
|
||||||
"lng_profile_info_section" = "Info";
|
|
||||||
"lng_profile_mobile_number" = "Mobile:";
|
"lng_profile_mobile_number" = "Mobile:";
|
||||||
"lng_profile_username" = "Username:";
|
"lng_profile_username" = "Username:";
|
||||||
"lng_profile_link" = "Link:";
|
"lng_profile_link" = "Link:";
|
||||||
"lng_profile_bio" = "Bio:";
|
"lng_profile_bio" = "Bio:";
|
||||||
"lng_profile_add_contact" = "Add Contact";
|
"lng_profile_add_contact" = "Add Contact";
|
||||||
"lng_profile_edit_contact" = "Edit";
|
"lng_profile_edit_contact" = "Edit";
|
||||||
"lng_profile_enable_notifications" = "Notifications";
|
|
||||||
"lng_profile_clear_history" = "Clear history";
|
"lng_profile_clear_history" = "Clear history";
|
||||||
"lng_profile_delete_conversation" = "Delete conversation";
|
"lng_profile_delete_conversation" = "Delete conversation";
|
||||||
"lng_profile_clear_and_exit" = "Delete and exit";
|
"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_unblock_user" = "Unblock user";
|
||||||
"lng_profile_block_bot" = "Stop and block bot";
|
"lng_profile_block_bot" = "Stop and block bot";
|
||||||
"lng_profile_unblock_bot" = "Unblock bot";
|
"lng_profile_unblock_bot" = "Unblock bot";
|
||||||
"lng_profile_send_message" = "Send Message";
|
|
||||||
"lng_profile_share_contact" = "Share Contact";
|
"lng_profile_share_contact" = "Share Contact";
|
||||||
"lng_profile_invite_to_group" = "Add to Group";
|
"lng_profile_invite_to_group" = "Add to Group";
|
||||||
"lng_profile_delete_contact" = "Delete";
|
"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" = "Remove {user} from the group?";
|
||||||
"lng_profile_sure_kick_channel" = "Remove {user} from the channel?";
|
"lng_profile_sure_kick_channel" = "Remove {user} from the channel?";
|
||||||
"lng_profile_loading" = "Loading...";
|
"lng_profile_loading" = "Loading...";
|
||||||
"lng_profile_shared_media" = "Shared media";
|
|
||||||
"lng_profile_photos#one" = "{count} photo";
|
"lng_profile_photos#one" = "{count} photo";
|
||||||
"lng_profile_photos#other" = "{count} photos";
|
"lng_profile_photos#other" = "{count} photos";
|
||||||
"lng_profile_photos_header" = "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_drop_area_subtitle_channel" = "to set it as a channel photo";
|
||||||
"lng_profile_top_bar_share_contact" = "Share";
|
"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_title" = "Report channel";
|
||||||
"lng_report_group_title" = "Report group";
|
"lng_report_group_title" = "Report group";
|
||||||
"lng_report_bot_title" = "Report bot";
|
"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_attach_photo" = "Photo";
|
||||||
|
|
||||||
"lng_media_type" = "Media type";
|
"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_open_with" = "Open With";
|
||||||
"lng_media_download" = "Download";
|
"lng_media_download" = "Download";
|
||||||
|
|
|
@ -1928,15 +1928,26 @@ void ApiWrap::requestSharedMedia(
|
||||||
auto filter = [&] {
|
auto filter = [&] {
|
||||||
using Type = SharedMediaType;
|
using Type = SharedMediaType;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type::Photo: return MTP_inputMessagesFilterPhotos();
|
case Type::Photo:
|
||||||
case Type::Video: return MTP_inputMessagesFilterVideo();
|
return MTP_inputMessagesFilterPhotos();
|
||||||
case Type::MusicFile: return MTP_inputMessagesFilterMusic();
|
case Type::Video:
|
||||||
case Type::File: return MTP_inputMessagesFilterDocument();
|
return MTP_inputMessagesFilterVideo();
|
||||||
case Type::VoiceFile: return MTP_inputMessagesFilterVoice();
|
case Type::MusicFile:
|
||||||
case Type::RoundVoiceFile: return MTP_inputMessagesFilterRoundVoice();
|
return MTP_inputMessagesFilterMusic();
|
||||||
case Type::GIF: return MTP_inputMessagesFilterGif();
|
case Type::File:
|
||||||
case Type::Link: return MTP_inputMessagesFilterUrl();
|
return MTP_inputMessagesFilterDocument();
|
||||||
case Type::ChatPhoto: return MTP_inputMessagesFilterChatPhotos();
|
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();
|
return MTP_inputMessagesFilterEmpty();
|
||||||
}();
|
}();
|
||||||
|
|
|
@ -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;
|
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
|
} // namespace base
|
||||||
|
|
|
@ -424,7 +424,7 @@ void AbstractBox::keyPressEvent(QKeyEvent *e) {
|
||||||
BoxLayerTitleShadow::BoxLayerTitleShadow(QWidget *parent) : Ui::PlainShadow(parent, st::boxLayerTitleShadow) {
|
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) {
|
int BoxContentDivider::resizeGetHeight(int newWidth) {
|
||||||
|
|
|
@ -290,7 +290,7 @@ public:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class BoxContentDivider : public TWidget {
|
class BoxContentDivider : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
BoxContentDivider(QWidget *parent);
|
BoxContentDivider(QWidget *parent);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include "facades.h"
|
#include "facades.h"
|
||||||
|
|
||||||
#include "profile/profile_section_memento.h"
|
#include "profile/profile_section_memento.h"
|
||||||
|
#include "info/info_memento.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
#include "media/media_clip_reader.h"
|
#include "media/media_clip_reader.h"
|
||||||
#include "observer_peer.h"
|
#include "observer_peer.h"
|
||||||
|
@ -256,8 +257,17 @@ void autoplayMediaInlineAsync(const FullMsgId &msgId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void showPeerProfile(const PeerId &peer) {
|
void showPeerProfile(const PeerId &peer) {
|
||||||
if (auto main = App::main()) {
|
//if (auto main = App::main()) {
|
||||||
main->showWideSection(Profile::SectionMemento(App::peer(peer)));
|
// 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1616,8 +1616,9 @@ void HistoryDocument::eraseFromOverview() {
|
||||||
Storage::SharedMediaTypesMask HistoryDocument::sharedMediaTypes() const {
|
Storage::SharedMediaTypesMask HistoryDocument::sharedMediaTypes() const {
|
||||||
using Type = Storage::SharedMediaType;
|
using Type = Storage::SharedMediaType;
|
||||||
if (_data->voice()) {
|
if (_data->voice()) {
|
||||||
using Mask = Storage::SharedMediaTypesMask;
|
return Storage::SharedMediaTypesMask{}
|
||||||
return Mask {}.added(Type::VoiceFile).added(Type::RoundVoiceFile);
|
.added(Type::VoiceFile)
|
||||||
|
.added(Type::RoundVoiceFile);
|
||||||
} else if (_data->song()) {
|
} else if (_data->song()) {
|
||||||
if (_data->isMusic()) {
|
if (_data->isMusic()) {
|
||||||
return Type::MusicFile;
|
return Type::MusicFile;
|
||||||
|
@ -2456,7 +2457,9 @@ int32 HistoryGif::addToOverview(AddToOverviewMethod method) {
|
||||||
Storage::SharedMediaTypesMask HistoryGif::sharedMediaTypes() const {
|
Storage::SharedMediaTypesMask HistoryGif::sharedMediaTypes() const {
|
||||||
using Type = Storage::SharedMediaType;
|
using Type = Storage::SharedMediaType;
|
||||||
if (_data->isRoundVideo()) {
|
if (_data->isRoundVideo()) {
|
||||||
return Type::RoundVoiceFile;
|
return Storage::SharedMediaTypesMask{}
|
||||||
|
.added(Type::RoundFile)
|
||||||
|
.added(Type::RoundVoiceFile);
|
||||||
} else if (_data->isGifv()) {
|
} else if (_data->isGifv()) {
|
||||||
return Type::GIF;
|
return Type::GIF;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 §ion,
|
||||||
|
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
|
|
@ -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 §ion,
|
||||||
|
not_null<PeerData*> peer);
|
||||||
|
|
||||||
|
} // namespace Info
|
|
@ -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 ¶ms) {
|
||||||
|
// 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
|
|
@ -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 ¶ms) 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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 ¶ms) {
|
||||||
|
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
|
|
@ -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 ¶ms) 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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -28,13 +28,12 @@ namespace details {
|
||||||
template <
|
template <
|
||||||
typename Transform,
|
typename Transform,
|
||||||
typename NewValue,
|
typename NewValue,
|
||||||
typename Error,
|
typename Error>
|
||||||
typename Value>
|
|
||||||
class map_transform_helper {
|
class map_transform_helper {
|
||||||
public:
|
public:
|
||||||
map_transform_helper(
|
map_transform_helper(
|
||||||
const consumer<NewValue, Error> &consumer,
|
Transform &&transform,
|
||||||
Transform &&transform)
|
const consumer<NewValue, Error> &consumer)
|
||||||
: _transform(std::move(transform))
|
: _transform(std::move(transform))
|
||||||
, _consumer(consumer) {
|
, _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>
|
template <typename Transform>
|
||||||
class map_helper {
|
class map_helper {
|
||||||
public:
|
public:
|
||||||
|
@ -80,11 +91,12 @@ public:
|
||||||
transform = std::move(_transform)
|
transform = std::move(_transform)
|
||||||
](const consumer<NewValue, Error> &consumer) mutable {
|
](const consumer<NewValue, Error> &consumer) mutable {
|
||||||
return std::move(initial).start(
|
return std::move(initial).start(
|
||||||
map_transform_helper<Transform, NewValue, Error, Value>(
|
map_transform(
|
||||||
consumer,
|
std::move(transform),
|
||||||
std::move(transform)
|
consumer
|
||||||
), [consumer](auto &&error) {
|
), [consumer](auto &&error) {
|
||||||
consumer.put_error_forward(std::forward<decltype(error)>(error));
|
consumer.put_error_forward(
|
||||||
|
std::forward<decltype(error)>(error));
|
||||||
}, [consumer] {
|
}, [consumer] {
|
||||||
consumer.put_done();
|
consumer.put_done();
|
||||||
});
|
});
|
||||||
|
@ -105,4 +117,99 @@ auto map(Transform &&transform)
|
||||||
std::forward<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
|
} // namespace rpl
|
||||||
|
|
|
@ -36,5 +36,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include <rpl/filter.h>
|
#include <rpl/filter.h>
|
||||||
#include <rpl/distinct_until_changed.h>
|
#include <rpl/distinct_until_changed.h>
|
||||||
#include <rpl/flatten_latest.h>
|
#include <rpl/flatten_latest.h>
|
||||||
|
#include <rpl/combine_latest.h>
|
||||||
|
|
||||||
#include <rpl/before_next.h>
|
#include <rpl/before_next.h>
|
||||||
|
|
|
@ -36,8 +36,9 @@ enum class SharedMediaType : char {
|
||||||
ChatPhoto = 6,
|
ChatPhoto = 6,
|
||||||
RoundVoiceFile = 7,
|
RoundVoiceFile = 7,
|
||||||
GIF = 8,
|
GIF = 8,
|
||||||
|
RoundFile = 9,
|
||||||
|
|
||||||
kCount = 9,
|
kCount = 10,
|
||||||
};
|
};
|
||||||
constexpr auto kSharedMediaTypeCount = static_cast<int>(SharedMediaType::kCount);
|
constexpr auto kSharedMediaTypeCount = static_cast<int>(SharedMediaType::kCount);
|
||||||
constexpr bool IsValidSharedMediaType(SharedMediaType type) {
|
constexpr bool IsValidSharedMediaType(SharedMediaType type) {
|
||||||
|
|
|
@ -595,7 +595,11 @@ public:
|
||||||
auto &link = links[lnkIndex - maxLnkIndex - 1];
|
auto &link = links[lnkIndex - maxLnkIndex - 1];
|
||||||
ClickHandlerPtr handler;
|
ClickHandlerPtr handler;
|
||||||
switch (link.type) {
|
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 EntityInTextEmail:
|
||||||
case EntityInTextUrl: handler = MakeShared<UrlClickHandler>(link.data, link.displayStatus == LinkDisplayedFull); break;
|
case EntityInTextUrl: handler = MakeShared<UrlClickHandler>(link.data, link.displayStatus == LinkDisplayedFull); break;
|
||||||
case EntityInTextBotCommand: handler = MakeShared<BotCommandClickHandler>(link.data); break;
|
case EntityInTextBotCommand: handler = MakeShared<BotCommandClickHandler>(link.data); break;
|
||||||
|
|
|
@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
|
#include <rpl/event_stream.h>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
||||||
|
@ -38,6 +39,10 @@ public:
|
||||||
}
|
}
|
||||||
float64 currentAnimationValue(TimeMs ms);
|
float64 currentAnimationValue(TimeMs ms);
|
||||||
|
|
||||||
|
rpl::producer<bool> checkedValue() const {
|
||||||
|
return _checks.events_starting_with(checked());
|
||||||
|
}
|
||||||
|
|
||||||
virtual QSize getSize() const = 0;
|
virtual QSize getSize() const = 0;
|
||||||
|
|
||||||
// Zero instead of ms value means that animation was already updated for this time.
|
// Zero instead of ms value means that animation was already updated for this time.
|
||||||
|
@ -59,6 +64,8 @@ private:
|
||||||
base::lambda<void()> _updateCallback;
|
base::lambda<void()> _updateCallback;
|
||||||
Animation _toggleAnimation;
|
Animation _toggleAnimation;
|
||||||
|
|
||||||
|
rpl::event_stream<bool> _checks;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CheckView : public AbstractCheckView {
|
class CheckView : public AbstractCheckView {
|
||||||
|
|
|
@ -18,17 +18,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
|
#include "ui/rp_widget.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
||||||
class PlainShadow : public TWidget {
|
class PlainShadow : public RpWidget {
|
||||||
public:
|
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:
|
protected:
|
||||||
void paintEvent(QPaintEvent *e) override {
|
void paintEvent(QPaintEvent *e) override {
|
||||||
Painter(this).fillRect(e->rect(), _color->b);
|
Painter(this).fillRect(e->rect(), _color);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -496,6 +496,23 @@ PeerAvatarButton {
|
||||||
photoSize: pixels;
|
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 {
|
defaultLabelSimple: LabelSimple {
|
||||||
font: normalFont;
|
font: normalFont;
|
||||||
maxWidth: 0px;
|
maxWidth: 0px;
|
||||||
|
@ -1041,3 +1058,11 @@ ProfilePeerListItem {
|
||||||
statusFgOver: color;
|
statusFgOver: color;
|
||||||
statusFgActive: color;
|
statusFgActive: color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InfoTopBar {
|
||||||
|
height: pixels;
|
||||||
|
back: IconButton;
|
||||||
|
title: FlatLabel;
|
||||||
|
titlePosition: point;
|
||||||
|
bg: color;
|
||||||
|
}
|
||||||
|
|
|
@ -66,16 +66,16 @@ void SlideWrap<RpWidget>::animationStep() {
|
||||||
weak->moveToLeft(margins.left(), margins.top());
|
weak->moveToLeft(margins.left(), margins.top());
|
||||||
newWidth = weak->width();
|
newWidth = weak->width();
|
||||||
}
|
}
|
||||||
auto current = _slideAnimation.current(_visible ? 1. : 0.);
|
auto current = _slideAnimation.current(_shown ? 1. : 0.);
|
||||||
auto newHeight = wrapped()
|
auto newHeight = wrapped()
|
||||||
? (_slideAnimation.animating()
|
? (_slideAnimation.animating()
|
||||||
? anim::interpolate(0, wrapped()->heightNoMargins(), current)
|
? anim::interpolate(0, wrapped()->heightNoMargins(), current)
|
||||||
: (_visible ? wrapped()->height() : 0))
|
: (_shown ? wrapped()->height() : 0))
|
||||||
: 0;
|
: 0;
|
||||||
if (newWidth != width() || newHeight != height()) {
|
if (newWidth != width() || newHeight != height()) {
|
||||||
resize(newWidth, newHeight);
|
resize(newWidth, newHeight);
|
||||||
}
|
}
|
||||||
auto shouldBeHidden = !_visible && !_slideAnimation.animating();
|
auto shouldBeHidden = !_shown && !_slideAnimation.animating();
|
||||||
if (shouldBeHidden != isHidden()) {
|
if (shouldBeHidden != isHidden()) {
|
||||||
setVisible(!shouldBeHidden);
|
setVisible(!shouldBeHidden);
|
||||||
if (shouldBeHidden) {
|
if (shouldBeHidden) {
|
||||||
|
@ -84,23 +84,28 @@ void SlideWrap<RpWidget>::animationStep() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlideWrap<RpWidget>::toggleAnimated(bool visible) {
|
void SlideWrap<RpWidget>::setShown(bool shown) {
|
||||||
if (_visible == visible) {
|
_shown = shown;
|
||||||
|
_shownUpdated.fire_copy(_shown);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlideWrap<RpWidget>::toggleAnimated(bool shown) {
|
||||||
|
if (_shown == shown) {
|
||||||
animationStep();
|
animationStep();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_visible = visible;
|
setShown(shown);
|
||||||
_slideAnimation.start(
|
_slideAnimation.start(
|
||||||
[this] { animationStep(); },
|
[this] { animationStep(); },
|
||||||
_visible ? 0. : 1.,
|
_shown ? 0. : 1.,
|
||||||
_visible ? 1. : 0.,
|
_shown ? 1. : 0.,
|
||||||
_duration,
|
_duration,
|
||||||
anim::linear);
|
anim::linear);
|
||||||
animationStep();
|
animationStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlideWrap<RpWidget>::toggleFast(bool visible) {
|
void SlideWrap<RpWidget>::toggleFast(bool shown) {
|
||||||
_visible = visible;
|
setShown(shown);
|
||||||
finishAnimations();
|
finishAnimations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +116,7 @@ void SlideWrap<RpWidget>::finishAnimations() {
|
||||||
|
|
||||||
QMargins SlideWrap<RpWidget>::getMargins() const {
|
QMargins SlideWrap<RpWidget>::getMargins() const {
|
||||||
auto result = wrapped()->getMargins();
|
auto result = wrapped()->getMargins();
|
||||||
return (animating() || !_visible)
|
return (animating() || !_shown)
|
||||||
? QMargins(result.left(), 0, result.right(), 0)
|
? QMargins(result.left(), 0, result.right(), 0)
|
||||||
: result;
|
: result;
|
||||||
}
|
}
|
||||||
|
@ -126,7 +131,7 @@ int SlideWrap<RpWidget>::resizeGetHeight(int newWidth) {
|
||||||
void SlideWrap<RpWidget>::wrappedSizeUpdated(QSize size) {
|
void SlideWrap<RpWidget>::wrappedSizeUpdated(QSize size) {
|
||||||
if (_slideAnimation.animating()) {
|
if (_slideAnimation.animating()) {
|
||||||
animationStep();
|
animationStep();
|
||||||
} else if (_visible) {
|
} else if (_shown) {
|
||||||
resize(size);
|
resize(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,8 @@ public:
|
||||||
const style::margins &padding,
|
const style::margins &padding,
|
||||||
int duration);
|
int duration);
|
||||||
|
|
||||||
void toggleAnimated(bool visible);
|
void toggleAnimated(bool shown);
|
||||||
void toggleFast(bool visible);
|
void toggleFast(bool shown);
|
||||||
|
|
||||||
void showAnimated() {
|
void showAnimated() {
|
||||||
toggleAnimated(true);
|
toggleAnimated(true);
|
||||||
|
@ -72,7 +72,11 @@ public:
|
||||||
QMargins getMargins() const override;
|
QMargins getMargins() const override;
|
||||||
|
|
||||||
bool isHiddenOrHiding() const {
|
bool isHiddenOrHiding() const {
|
||||||
return !_visible;
|
return !_shown;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<bool> shownValue() const {
|
||||||
|
return _shownUpdated.events_starting_with_copy(_shown);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -81,8 +85,10 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void animationStep();
|
void animationStep();
|
||||||
|
void setShown(bool shown);
|
||||||
|
|
||||||
bool _visible = true;
|
bool _shown = true;
|
||||||
|
rpl::event_stream<bool> _shownUpdated;
|
||||||
Animation _slideAnimation;
|
Animation _slideAnimation;
|
||||||
int _duration = 0;
|
int _duration = 0;
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
class LayerWidget;
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
|
||||||
class Controller;
|
class Controller;
|
||||||
|
@ -27,10 +29,18 @@ class SectionWidget;
|
||||||
|
|
||||||
class SectionMemento {
|
class SectionMemento {
|
||||||
public:
|
public:
|
||||||
virtual object_ptr<Window::SectionWidget> createWidget(QWidget *parent, not_null<Window::Controller*> controller, const QRect &geometry) = 0;
|
virtual object_ptr<Window::SectionWidget> createWidget(
|
||||||
virtual ~SectionMemento() {
|
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
|
} // namespace Window
|
||||||
|
|
|
@ -21,13 +21,19 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include "window/section_widget.h"
|
#include "window/section_widget.h"
|
||||||
|
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
|
#include <rpl/single.h>
|
||||||
|
|
||||||
namespace Window {
|
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;
|
_topDelta = topDelta;
|
||||||
bool willBeResized = (size() != newGeometry.size());
|
bool willBeResized = (size() != newGeometry.size());
|
||||||
if (geometry() != newGeometry) {
|
if (geometry() != newGeometry) {
|
||||||
|
@ -39,7 +45,9 @@ void SectionWidget::setGeometryWithTopMoved(const QRect &newGeometry, int topDel
|
||||||
_topDelta = 0;
|
_topDelta = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SectionWidget::showAnimated(SlideDirection direction, const SectionSlideParams ¶ms) {
|
void SectionWidget::showAnimated(
|
||||||
|
SlideDirection direction,
|
||||||
|
const SectionSlideParams ¶ms) {
|
||||||
if (_showAnimation) return;
|
if (_showAnimation) return;
|
||||||
|
|
||||||
showChildren();
|
showChildren();
|
||||||
|
@ -51,7 +59,9 @@ void SectionWidget::showAnimated(SlideDirection direction, const SectionSlidePar
|
||||||
_showAnimation->setDirection(direction);
|
_showAnimation->setDirection(direction);
|
||||||
_showAnimation->setRepaintCallback([this] { update(); });
|
_showAnimation->setRepaintCallback([this] { update(); });
|
||||||
_showAnimation->setFinishedCallback([this] { showFinished(); });
|
_showAnimation->setFinishedCallback([this] { showFinished(); });
|
||||||
_showAnimation->setPixmaps(params.oldContentCache, myContentCache);
|
_showAnimation->setPixmaps(
|
||||||
|
params.oldContentCache,
|
||||||
|
myContentCache);
|
||||||
_showAnimation->setTopBarShadow(params.withTopBarShadow);
|
_showAnimation->setTopBarShadow(params.withTopBarShadow);
|
||||||
_showAnimation->start();
|
_showAnimation->start();
|
||||||
|
|
||||||
|
@ -82,4 +92,8 @@ void SectionWidget::showFinished() {
|
||||||
setInnerFocus();
|
setInnerFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<int> SectionWidget::desiredHeight() const {
|
||||||
|
return rpl::single(height());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
|
@ -115,6 +115,8 @@ public:
|
||||||
doSetInnerFocus();
|
doSetInnerFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual rpl::producer<int> desiredHeight() const;
|
||||||
|
|
||||||
// Global shortcut handler. For now that ugly :(
|
// Global shortcut handler. For now that ugly :(
|
||||||
virtual bool cmd_search() {
|
virtual bool cmd_search() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -150,7 +152,7 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<SlideAnimation> _showAnimation;
|
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;
|
int _topDelta = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
'<(src_loc)/calls/calls.style',
|
'<(src_loc)/calls/calls.style',
|
||||||
'<(src_loc)/dialogs/dialogs.style',
|
'<(src_loc)/dialogs/dialogs.style',
|
||||||
'<(src_loc)/history/history.style',
|
'<(src_loc)/history/history.style',
|
||||||
|
'<(src_loc)/info/info.style',
|
||||||
'<(src_loc)/intro/intro.style',
|
'<(src_loc)/intro/intro.style',
|
||||||
'<(src_loc)/media/view/mediaview.style',
|
'<(src_loc)/media/view/mediaview.style',
|
||||||
'<(src_loc)/media/player/media_player.style',
|
'<(src_loc)/media/player/media_player.style',
|
||||||
|
|
|
@ -193,6 +193,30 @@
|
||||||
<(src_loc)/history/history_user_photos.h
|
<(src_loc)/history/history_user_photos.h
|
||||||
<(src_loc)/history/history_widget.cpp
|
<(src_loc)/history/history_widget.cpp
|
||||||
<(src_loc)/history/history_widget.h
|
<(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.cpp
|
||||||
<(src_loc)/inline_bots/inline_bot_layout_internal.h
|
<(src_loc)/inline_bots/inline_bot_layout_internal.h
|
||||||
<(src_loc)/inline_bots/inline_bot_layout_item.cpp
|
<(src_loc)/inline_bots/inline_bot_layout_item.cpp
|
||||||
|
|
|
@ -100,6 +100,7 @@
|
||||||
'<(src_loc)/rpl/before_next.h',
|
'<(src_loc)/rpl/before_next.h',
|
||||||
'<(src_loc)/rpl/combine_latest.h',
|
'<(src_loc)/rpl/combine_latest.h',
|
||||||
'<(src_loc)/rpl/complete.h',
|
'<(src_loc)/rpl/complete.h',
|
||||||
|
'<(src_loc)/rpl/combine_latest.h',
|
||||||
'<(src_loc)/rpl/consumer.h',
|
'<(src_loc)/rpl/consumer.h',
|
||||||
'<(src_loc)/rpl/deferred.h',
|
'<(src_loc)/rpl/deferred.h',
|
||||||
'<(src_loc)/rpl/distinct_until_changed.h',
|
'<(src_loc)/rpl/distinct_until_changed.h',
|
||||||
|
|