diff --git a/Telegram/Resources/icons/info_media_audio_empty.png b/Telegram/Resources/icons/info_media_audio_empty.png new file mode 100644 index 000000000..92af8ab50 Binary files /dev/null and b/Telegram/Resources/icons/info_media_audio_empty.png differ diff --git a/Telegram/Resources/icons/info_media_audio_empty@2x.png b/Telegram/Resources/icons/info_media_audio_empty@2x.png new file mode 100644 index 000000000..65b7506ab Binary files /dev/null and b/Telegram/Resources/icons/info_media_audio_empty@2x.png differ diff --git a/Telegram/Resources/icons/info_media_file_empty.png b/Telegram/Resources/icons/info_media_file_empty.png new file mode 100644 index 000000000..bcff0554f Binary files /dev/null and b/Telegram/Resources/icons/info_media_file_empty.png differ diff --git a/Telegram/Resources/icons/info_media_file_empty@2x.png b/Telegram/Resources/icons/info_media_file_empty@2x.png new file mode 100644 index 000000000..b08f2b7cd Binary files /dev/null and b/Telegram/Resources/icons/info_media_file_empty@2x.png differ diff --git a/Telegram/Resources/icons/info_media_link_empty.png b/Telegram/Resources/icons/info_media_link_empty.png new file mode 100644 index 000000000..393e34da8 Binary files /dev/null and b/Telegram/Resources/icons/info_media_link_empty.png differ diff --git a/Telegram/Resources/icons/info_media_link_empty@2x.png b/Telegram/Resources/icons/info_media_link_empty@2x.png new file mode 100644 index 000000000..b99bbecf2 Binary files /dev/null and b/Telegram/Resources/icons/info_media_link_empty@2x.png differ diff --git a/Telegram/Resources/icons/info_media_photo_empty.png b/Telegram/Resources/icons/info_media_photo_empty.png new file mode 100644 index 000000000..a4b1e92ae Binary files /dev/null and b/Telegram/Resources/icons/info_media_photo_empty.png differ diff --git a/Telegram/Resources/icons/info_media_photo_empty@2x.png b/Telegram/Resources/icons/info_media_photo_empty@2x.png new file mode 100644 index 000000000..098a6adf7 Binary files /dev/null and b/Telegram/Resources/icons/info_media_photo_empty@2x.png differ diff --git a/Telegram/Resources/icons/info_media_video_empty.png b/Telegram/Resources/icons/info_media_video_empty.png new file mode 100644 index 000000000..8fbf73143 Binary files /dev/null and b/Telegram/Resources/icons/info_media_video_empty.png differ diff --git a/Telegram/Resources/icons/info_media_video_empty@2x.png b/Telegram/Resources/icons/info_media_video_empty@2x.png new file mode 100644 index 000000000..d45d799d1 Binary files /dev/null and b/Telegram/Resources/icons/info_media_video_empty@2x.png differ diff --git a/Telegram/Resources/icons/info_media_voice_empty.png b/Telegram/Resources/icons/info_media_voice_empty.png new file mode 100644 index 000000000..83f007578 Binary files /dev/null and b/Telegram/Resources/icons/info_media_voice_empty.png differ diff --git a/Telegram/Resources/icons/info_media_voice_empty@2x.png b/Telegram/Resources/icons/info_media_voice_empty@2x.png new file mode 100644 index 000000000..7f55bb83c Binary files /dev/null and b/Telegram/Resources/icons/info_media_voice_empty@2x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index a4ef8b1c2..b9d82c96a 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -590,8 +590,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_profile_files_header" = "Files"; "lng_profile_audios#one" = "{count} voice message"; "lng_profile_audios#other" = "{count} voice messages"; -"lng_profile_rounds#one" = "{count} video message"; -"lng_profile_rounds#other" = "{count} video messages"; +//"lng_profile_rounds#one" = "{count} video message"; +//"lng_profile_rounds#other" = "{count} video messages"; "lng_profile_audios_header" = "Voice messages"; "lng_profile_shared_links#one" = "{count} shared link"; "lng_profile_shared_links#other" = "{count} shared links"; @@ -645,10 +645,19 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_media_selected_file#other" = "{count} Files"; "lng_media_selected_audio#one" = "{count} Voice message"; "lng_media_selected_audio#other" = "{count} Voice messages"; -"lng_media_selected_round#one" = "{count} Video message"; -"lng_media_selected_round#other" = "{count} video messages"; +//"lng_media_selected_round#one" = "{count} Video message"; +//"lng_media_selected_round#other" = "{count} Video messages"; "lng_media_selected_link#one" = "{count} Shared link"; "lng_media_selected_link#other" = "{count} Shared links"; +"lng_media_photo_empty" = "No photos here yet"; +"lng_media_video_empty" = "No videos here yet"; +"lng_media_song_empty" = "No music files here yet"; +"lng_media_file_empty" = "No files here yet"; +"lng_media_audio_empty" = "No voice messages here yet"; +"lng_media_link_empty" = "No shared links here yet"; +"lng_media_song_empty_search" = "No music files found"; +"lng_media_file_empty_search" = "No files found"; +"lng_media_link_empty_search" = "No shared links found"; "lng_manage_group_title" = "Manage Group"; "lng_manage_channel_title" = "Manage Channel"; diff --git a/Telegram/SourceFiles/data/data_search_controller.cpp b/Telegram/SourceFiles/data/data_search_controller.cpp index 66a944196..37cfcee75 100644 --- a/Telegram/SourceFiles/data/data_search_controller.cpp +++ b/Telegram/SourceFiles/data/data_search_controller.cpp @@ -390,7 +390,7 @@ void DelayedSearchController::setQuery( void DelayedSearchController::setQueryFast(const Query &query) { _controller.setQuery(query); - _sourceChanges.fire({}); + _currentQueryChanges.fire_copy(query.query); } } // namespace Api diff --git a/Telegram/SourceFiles/data/data_search_controller.h b/Telegram/SourceFiles/data/data_search_controller.h index 7577f1124..d7ccf43b4 100644 --- a/Telegram/SourceFiles/data/data_search_controller.h +++ b/Telegram/SourceFiles/data/data_search_controller.h @@ -162,8 +162,9 @@ public: limitAfter); } - rpl::producer<> sourceChanged() const { - return _sourceChanges.events(); + rpl::producer currentQueryValue() const { + return _currentQueryChanges.events_starting_with( + currentQuery().query); } SavedState saveState() { @@ -178,7 +179,7 @@ private: SearchController _controller; Query _nextQuery; base::Timer _timer; - rpl::event_stream<> _sourceChanges; + rpl::event_stream _currentQueryChanges; }; diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index ec5cc41f7..07018fa77 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -548,6 +548,21 @@ managePeerButtonLabel: FlatLabel(defaultFlatLabel) { } managePeerButtonLabelPosition: point(25px, 10px); +infoEmptyFg: windowSubTextFg; +infoEmptyPhoto: icon {{ "info_media_photo_empty", infoEmptyFg }}; +infoEmptyVideo: icon {{ "info_media_video_empty", infoEmptyFg }}; +infoEmptyAudio: icon {{ "info_media_audio_empty", infoEmptyFg }}; +infoEmptyFile: icon {{ "info_media_file_empty", infoEmptyFg }}; +infoEmptyVoice: icon {{ "info_media_voice_empty", infoEmptyFg }}; +infoEmptyLink: icon {{ "info_media_link_empty", infoEmptyFg }}; +infoEmptyIconTop: 120px; +infoEmptyLabelTop: 40px; +infoEmptyLabelSkip: 20px; +infoEmptyLabel: FlatLabel(defaultFlatLabel) { + minWidth: 220px; + textFg: windowSubTextFg; +} + editPeerDeleteButtonMargins: margins(23px, 16px, 23px, 16px); editPeerDeleteButton: sessionTerminateAllButton; editPeerPhotoMargins: margins(23px, 16px, 23px, 8px); diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index daf8dfbbb..32af22711 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -150,6 +150,10 @@ rpl::producer
ContentWidget::sectionRequest() const { return rpl::never
(); } +rpl::producer ContentWidget::scrollHeightValue() const { + return _scroll->heightValue(); +} + rpl::producer ContentWidget::desiredHeightValue() const { using namespace rpl::mappers; return rpl::combine( diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h index 4ac7ec6cb..df8ae301f 100644 --- a/Telegram/SourceFiles/info/info_content_widget.h +++ b/Telegram/SourceFiles/info/info_content_widget.h @@ -52,6 +52,7 @@ public: virtual void setIsStackBottom(bool isStackBottom) { } + rpl::producer scrollHeightValue() const; rpl::producer desiredHeightValue() const override; rpl::producer desiredShadowVisibility() const; bool hasTopBarShadow() const; diff --git a/Telegram/SourceFiles/info/info_controller.cpp b/Telegram/SourceFiles/info/info_controller.cpp index 0b9ba9338..3ed5b6bb4 100644 --- a/Telegram/SourceFiles/info/info_controller.cpp +++ b/Telegram/SourceFiles/info/info_controller.cpp @@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #include "info/info_controller.h" +#include +#include #include "ui/search_field_controller.h" #include "data/data_shared_media.h" #include "info/info_content_widget.h" diff --git a/Telegram/SourceFiles/info/info_controller.h b/Telegram/SourceFiles/info/info_controller.h index 309cf9d00..108f3b4dc 100644 --- a/Telegram/SourceFiles/info/info_controller.h +++ b/Telegram/SourceFiles/info/info_controller.h @@ -116,8 +116,8 @@ public: SparseIdsMergedSlice::UniversalMsgId aroundId, int limitBefore, int limitAfter) const; - rpl::producer<> mediaSourceChanged() const { - return _searchController->sourceChanged(); + rpl::producer mediaSourceQueryValue() const { + return _searchController->currentQueryValue(); } void saveSearchState(not_null memento); diff --git a/Telegram/SourceFiles/info/info_top_bar_override.cpp b/Telegram/SourceFiles/info/info_top_bar_override.cpp index caddf5ac9..06d6ce576 100644 --- a/Telegram/SourceFiles/info/info_top_bar_override.cpp +++ b/Telegram/SourceFiles/info/info_top_bar_override.cpp @@ -65,7 +65,7 @@ QString TopBarOverride::generateText() const { case Type::MusicFile: return lng_media_selected_song; case Type::Link: return lng_media_selected_link; case Type::VoiceFile: return lng_media_selected_audio; - case Type::RoundFile: return lng_media_selected_round; +// case Type::RoundFile: return lng_media_selected_round; } Unexpected("Type in TopBarOverride::generateText()"); }(); diff --git a/Telegram/SourceFiles/info/media/info_media_buttons.h b/Telegram/SourceFiles/info/media/info_media_buttons.h index 5711985b5..c6a5262ea 100644 --- a/Telegram/SourceFiles/info/media/info_media_buttons.h +++ b/Telegram/SourceFiles/info/media/info_media_buttons.h @@ -46,7 +46,7 @@ inline auto MediaTextPhrase(Type type) { case Type::MusicFile: return lng_profile_songs; case Type::Link: return lng_profile_shared_links; case Type::VoiceFile: return lng_profile_audios; - case Type::RoundFile: return lng_profile_rounds; +// case Type::RoundFile: return lng_profile_rounds; } Unexpected("Type in MediaTextPhrase()"); }; diff --git a/Telegram/SourceFiles/info/media/info_media_empty_widget.cpp b/Telegram/SourceFiles/info/media/info_media_empty_widget.cpp new file mode 100644 index 000000000..c3987a09a --- /dev/null +++ b/Telegram/SourceFiles/info/media/info_media_empty_widget.cpp @@ -0,0 +1,117 @@ +/* +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/media/info_media_empty_widget.h" + +#include "ui/widgets/labels.h" +#include "styles/style_info.h" +#include "lang/lang_keys.h" + +namespace Info { +namespace Media { + +EmptyWidget::EmptyWidget(QWidget *parent) +: RpWidget(parent) +, _text(this, st::infoEmptyLabel) { +} + +void EmptyWidget::setFullHeight(rpl::producer fullHeightValue) { + std::move(fullHeightValue) + | rpl::start_with_next([this](int fullHeight) { + // Make icon center be on 1/3 height. + auto iconCenter = fullHeight / 3; + auto iconHeight = st::infoEmptyFile.height(); + auto iconTop = iconCenter - iconHeight / 2; + _height = iconTop + st::infoEmptyIconTop; + resizeToWidth(width()); + }, lifetime()); +} + +void EmptyWidget::setType(Type type) { + _type = type; + using Data = std::pair; + _icon = [&] { + switch (_type) { + case Type::Photo: return &st::infoEmptyPhoto; + case Type::Video: return &st::infoEmptyVideo; + case Type::MusicFile: return &st::infoEmptyAudio; + case Type::File: return &st::infoEmptyFile; + case Type::Link: return &st::infoEmptyLink; + case Type::VoiceFile: return &st::infoEmptyVoice; + } + Unexpected("Bad type in EmptyWidget::setType()"); + }(); + update(); +} + +void EmptyWidget::setSearchQuery(const QString &query) { + auto key = [&] { + switch (_type) { + case Type::Photo: + return lng_media_photo_empty; + case Type::Video: + return lng_media_video_empty; + case Type::MusicFile: + return query.isEmpty() + ? lng_media_song_empty + : lng_media_song_empty_search; + case Type::File: + return query.isEmpty() + ? lng_media_file_empty + : lng_media_file_empty_search; + case Type::Link: + return query.isEmpty() + ? lng_media_link_empty + : lng_media_link_empty_search; + case Type::VoiceFile: + return lng_media_audio_empty; + } + Unexpected("Bad type in EmptyWidget::setSearchQuery()"); + }(); + _text->setText(lang(key)); + resizeToWidth(width()); +} + +void EmptyWidget::paintEvent(QPaintEvent *e) { + if (!_icon) { + return; + } + + Painter p(this); + + auto iconLeft = (width() - _icon->width()) / 2; + auto iconTop = height() - st::infoEmptyIconTop; + _icon->paint(p, iconLeft, iconTop, width()); +} + +int EmptyWidget::resizeGetHeight(int newWidth) { + auto labelTop = _height - st::infoEmptyLabelTop; + auto labelWidth = newWidth - 2 * st::infoEmptyLabelSkip; + _text->resizeToNaturalWidth(labelWidth); + + auto labelLeft = (newWidth - _text->width()) / 2; + _text->moveToLeft(labelLeft, labelTop, newWidth); + + update(); + return _height; +} + +} // namespace Media +} // namespace Info diff --git a/Telegram/SourceFiles/info/media/info_media_empty_widget.h b/Telegram/SourceFiles/info/media/info_media_empty_widget.h new file mode 100644 index 000000000..66b64ce2a --- /dev/null +++ b/Telegram/SourceFiles/info/media/info_media_empty_widget.h @@ -0,0 +1,55 @@ +/* +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/media/info_media_widget.h" + +namespace Ui { +class FlatLabel; +} // namespace Ui + +namespace Info { +namespace Media { + +class EmptyWidget : public Ui::RpWidget { +public: + EmptyWidget(QWidget *parent); + + void setFullHeight(rpl::producer fullHeightValue); + void setType(Type type); + void setSearchQuery(const QString &query); + +protected: + void paintEvent(QPaintEvent *e) override; + + int resizeGetHeight(int newWidth) override; + +private: + object_ptr _text; + Type _type = Type::kCount; + const style::icon *_icon = nullptr; + int _height = 0; + +}; + +} // namespace Media +} // namespace Info diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp index 42ec30b8a..c442d336d 100644 --- a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "boxes/abstract_box.h" #include "info/media/info_media_list_widget.h" #include "info/media/info_media_buttons.h" +#include "info/media/info_media_empty_widget.h" #include "info/profile/info_profile_button.h" #include "info/profile/info_profile_icon.h" #include "info/info_controller.h" @@ -41,7 +42,8 @@ InnerWidget::InnerWidget( QWidget *parent, not_null controller) : RpWidget(parent) -, _controller(controller) { +, _controller(controller) +, _empty(this) { _list = setupList(); setupOtherTypes(); } @@ -253,6 +255,12 @@ object_ptr InnerWidget::setupList() { _scrollToRequests, result->lifetime()); _selectedLists.fire(result->selectedListValue()); + _listTops.fire(result->topValue()); + _empty->setType(_controller->section().mediaType()); + _controller->mediaSourceQueryValue() + | rpl::start_with_next([this](const QString &query) { + _empty->setSearchQuery(query); + }, result->lifetime()); return result; } @@ -287,6 +295,7 @@ int InnerWidget::resizeGetHeight(int newWidth) { _searchField->resizeToWidth(newWidth); } _list->resizeToWidth(newWidth); + _empty->resizeToWidth(newWidth); return recountHeight(); } @@ -308,12 +317,30 @@ int InnerWidget::recountHeight() { _searchField->moveToLeft(0, top); top += _searchField->heightNoMargins() - st::lineWidth; } + auto listHeight = 0; if (_list) { _list->moveToLeft(0, top); - top += _list->heightNoMargins(); + listHeight = _list->heightNoMargins(); + top += listHeight; + } + if (listHeight > 0) { + _empty->hide(); + } else { + _empty->show(); + _empty->moveToLeft(0, top); + top += _empty->heightNoMargins(); } return top; } +void InnerWidget::setScrollHeightValue(rpl::producer value) { + using namespace rpl::mappers; + _empty->setFullHeight(rpl::combine( + std::move(value), + _listTops.events_starting_with(_list->topValue()) + | rpl::flatten_latest(), + $1 - $2)); +} + } // namespace Media } // namespace Info diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.h b/Telegram/SourceFiles/info/media/info_media_inner_widget.h index 1cfaec6e7..7d9cd2ae4 100644 --- a/Telegram/SourceFiles/info/media/info_media_inner_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.h @@ -40,6 +40,7 @@ namespace Media { class Memento; class ListWidget; +class EmptyWidget; class InnerWidget final : public Ui::RpWidget { public: @@ -52,6 +53,8 @@ public: void saveState(not_null memento); void restoreState(not_null memento); + void setScrollHeightValue(rpl::producer value); + rpl::producer scrollToRequests() const { return _scrollToRequests.events(); } @@ -88,12 +91,14 @@ private: object_ptr _otherTabsShadow = { nullptr }; base::unique_qptr _searchField = nullptr; object_ptr _list = { nullptr }; + object_ptr _empty; bool _searchEnabled = false; bool _inResize = false; rpl::event_stream _scrollToRequests; rpl::event_stream> _selectedLists; + rpl::event_stream> _listTops; }; diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 2a32ee02a..c1ce488a6 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -549,7 +549,6 @@ ListWidget::ListWidget( , _slice(sliceKey(_universalAroundId)) { setAttribute(Qt::WA_MouseTracking); start(); - refreshViewer(); } void ListWidget::start() { @@ -574,7 +573,7 @@ void ListWidget::start() { | rpl::start_with_next([this](auto item) { repaintItem(item); }, lifetime()); - _controller->mediaSourceChanged() + _controller->mediaSourceQueryValue() | rpl::start_with_next([this]{ restart(); }, lifetime()); @@ -2014,8 +2013,9 @@ int ListWidget::recountHeight() { section.setTop(result); result += section.height(); } - return result - + cachedPadding.bottom(); + return (result > cachedPadding.top()) + ? (result + cachedPadding.bottom()) + : 0; } void ListWidget::mouseActionUpdate() { diff --git a/Telegram/SourceFiles/info/media/info_media_widget.cpp b/Telegram/SourceFiles/info/media/info_media_widget.cpp index 4351cfd9a..8a28fcd1f 100644 --- a/Telegram/SourceFiles/info/media/info_media_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_widget.cpp @@ -87,6 +87,7 @@ Widget::Widget( _inner = setInnerWidget(object_ptr( this, controller)); + _inner->setScrollHeightValue(scrollHeightValue()); _inner->scrollToRequests() | rpl::start_with_next([this](Ui::ScrollToRequest request) { scrollTo(request); diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 68ca49811..6940dc6b7 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -240,6 +240,8 @@ <(src_loc)/info/common_groups/info_common_groups_widget.cpp <(src_loc)/info/common_groups/info_common_groups_widget.h <(src_loc)/info/media/info_media_buttons.h +<(src_loc)/info/media/info_media_empty_widget.cpp +<(src_loc)/info/media/info_media_empty_widget.h <(src_loc)/info/media/info_media_inner_widget.cpp <(src_loc)/info/media/info_media_inner_widget.h <(src_loc)/info/media/info_media_list_widget.cpp