From df91b2bfeb7395762cc6079021574fe1e48c6332 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 15 Jun 2018 21:56:17 +0300 Subject: [PATCH] Export settings layout ready. --- Telegram/Resources/langs/lang.strings | 17 ++ .../SourceFiles/export/export_api_wrap.cpp | 11 +- Telegram/SourceFiles/export/export_settings.h | 15 +- Telegram/SourceFiles/export/view/export.style | 25 ++ .../export/view/export_view_settings.cpp | 217 +++++++++++++++++- .../export/view/export_view_settings.h | 6 + .../ui/widgets/continuous_sliders.cpp | 9 +- .../ui/widgets/continuous_sliders.h | 16 +- Telegram/SourceFiles/ui/widgets/labels.cpp | 6 +- Telegram/SourceFiles/ui/widgets/labels.h | 7 +- 10 files changed, 299 insertions(+), 30 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 6d35c0574d..4177a4af58 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1657,6 +1657,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_export_option_info" = "Personal info"; "lng_export_option_contacts" = "Contacts list"; "lng_export_option_sessions" = "Sessions list"; +"lng_export_header_chats" = "Chats export settings"; +"lng_export_option_personal_chats" = "Personal chats"; +"lng_export_option_bot_chats" = "Bot chats"; +"lng_export_option_private_groups" = "Private groups"; +"lng_export_option_private_channels" = "Private channels"; +"lng_export_option_public_groups" = "Public groups"; +"lng_export_option_public_channels" = "Public channels"; +"lng_export_option_only_my" = "Only my messages"; +"lng_export_header_media" = "Media export settings"; +"lng_export_option_photos" = "Photos"; +"lng_export_option_video_files" = "Video files"; +"lng_export_option_voice_messages" = "Voice messages"; +"lng_export_option_video_messages" = "Round video messages"; +"lng_export_option_stickers" = "Stickers"; +"lng_export_option_gifs" = "Animated GIFs"; +"lng_export_option_files" = "Files"; +"lng_export_option_size_limit" = "Size limit: {size}"; "lng_export_start" = "Export"; // Wnd specific diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index b47bd2aeed..d21d312723 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -147,15 +147,8 @@ void ApiWrap::startExport( } void ApiWrap::startMainSession(FnMut done) { - auto sizeLimit = _settings->defaultMedia.sizeLimit; - auto hasFiles = _settings->defaultMedia.types != 0; - for (const auto &item : _settings->customMedia) { - sizeLimit = std::max(sizeLimit, item.second.sizeLimit); - hasFiles = hasFiles || (item.second.types != 0); - } - if (!sizeLimit) { - hasFiles = false; - } + const auto sizeLimit = _settings->media.sizeLimit; + const auto hasFiles = (_settings->media.types != 0) && (sizeLimit > 0); using Type = Settings::Type; using Flag = MTPaccount_InitTakeoutSession::Flag; diff --git a/Telegram/SourceFiles/export/export_settings.h b/Telegram/SourceFiles/export/export_settings.h index e1a923d805..13803b16e2 100644 --- a/Telegram/SourceFiles/export/export_settings.h +++ b/Telegram/SourceFiles/export/export_settings.h @@ -17,11 +17,13 @@ enum class Format; struct MediaSettings { enum class Type { - Photo = 0x01, - Video = 0x02, - Sticker = 0x04, - GIF = 0x08, - File = 0x10, + Photo = 0x01, + Video = 0x02, + VoiceMessage = 0x04, + VideoMessage = 0x08, + Sticker = 0x10, + GIF = 0x20, + File = 0x40, }; using Types = base::flags; friend inline constexpr auto is_flag_type(Type) { return true; }; @@ -57,8 +59,7 @@ struct Settings { Types types = DefaultTypes(); Types fullChats = DefaultFullChats(); - MediaSettings defaultMedia; - base::flat_map customMedia; + MediaSettings media; static inline Types DefaultTypes() { return Type::PersonalInfo diff --git a/Telegram/SourceFiles/export/view/export.style b/Telegram/SourceFiles/export/view/export.style index ecbcc27631..96ba03d9ab 100644 --- a/Telegram/SourceFiles/export/view/export.style +++ b/Telegram/SourceFiles/export/view/export.style @@ -12,3 +12,28 @@ using "boxes/boxes.style"; exportPanelSize: size(364px, 480px); exportSettingPadding: margins(22px, 8px, 22px, 8px); +exportSubSettingPadding: margins(56px, 4px, 22px, 12px); +exportHeaderLabel: FlatLabel(boxTitle) { + style: TextStyle(defaultTextStyle) { + font: font(15px semibold); + linkFont: font(15px semibold); + linkFontOver: font(15px semibold underline); + } +} +exportHeaderPadding: margins(22px, 20px, 22px, 9px); +exportFileSizeSlider: MediaSlider { + width: 3px; + activeFg: mediaPlayerActiveFg; + inactiveFg: mediaPlayerInactiveFg; + activeFgOver: mediaPlayerActiveFg; + inactiveFgOver: mediaPlayerInactiveFg; + activeFgDisabled: mediaPlayerInactiveFg; + inactiveFgDisabled: windowBg; + seekSize: size(15px, 15px); + duration: 150; +} +exportFileSizeLabel: LabelSimple(defaultLabelSimple) { + font: boxTextFont; +} +exportFileSizePadding: margins(22px, 8px, 22px, 8px); +exportFileSizeLabelBottom: 18px; diff --git a/Telegram/SourceFiles/export/view/export_view_settings.cpp b/Telegram/SourceFiles/export/view/export_view_settings.cpp index b43b677bfd..ea4a4b7749 100644 --- a/Telegram/SourceFiles/export/view/export_view_settings.cpp +++ b/Telegram/SourceFiles/export/view/export_view_settings.cpp @@ -10,8 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/scroll_area.h" +#include "ui/widgets/continuous_sliders.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/padding_wrap.h" +#include "ui/wrap/slide_wrap.h" +#include "ui/wrap/fade_wrap.h" #include "platform/platform_specific.h" #include "styles/style_widgets.h" #include "styles/style_export.h" @@ -19,6 +24,36 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Export { namespace View { +namespace { + +constexpr auto kSizeValueCount = 80; +constexpr auto kMegabyte = 1024 * 1024; + +int SizeLimitByIndex(int index) { + Expects(index >= 0 && index <= kSizeValueCount); + + const auto megabytes = [&] { + if (index <= 10) { + return index; + } else if (index <= 30) { + return 10 + (index - 10) * 2; + } else if (index <= 40) { + return 50 + (index - 30) * 5; + } else if (index <= 60) { + return 100 + (index - 40) * 10; + } else if (index <= 70) { + return 300 + (index - 60) * 20; + } else { + return 500 + (index - 70) * 100; + } + }; + if (!index) { + return kMegabyte / 2; + } + return megabytes() * kMegabyte; +} + +} // namespace SettingsWidget::SettingsWidget(QWidget *parent) : RpWidget(parent) { @@ -35,7 +70,15 @@ SettingsWidget::SettingsWidget(QWidget *parent) } void SettingsWidget::setupContent() { - const auto content = Ui::CreateChild(this); + using namespace rpl::mappers; + + const auto scroll = Ui::CreateChild( + this, + st::boxLayerScroll); + const auto wrap = scroll->setOwnedWidget(object_ptr( + scroll, + object_ptr(scroll))); + const auto content = static_cast(wrap->entity()); const auto buttonsPadding = st::boxButtonPadding; const auto buttonsHeight = buttonsPadding.top() @@ -44,9 +87,32 @@ void SettingsWidget::setupContent() { const auto buttons = Ui::CreateChild( this, buttonsHeight); + const auto topShadow = Ui::CreateChild(this); + const auto bottomShadow = Ui::CreateChild(this); + topShadow->toggleOn(scroll->scrollTopValue( + ) | rpl::map(_1 > 0)); + bottomShadow->toggleOn(rpl::combine( + scroll->heightValue(), + scroll->scrollTopValue(), + wrap->heightValue(), + _2 + ) | rpl::map([=](int top) { + return top < scroll->scrollTopMax(); + })); const auto refreshButtonsCallback = [=] { refreshButtons(buttons); }; + const auto addHeader = [&]( + not_null container, + LangKey key) { + container->add( + object_ptr( + container, + lang(key), + Ui::FlatLabel::InitType::Simple, + st::exportHeaderLabel), + st::exportHeaderPadding); + }; const auto addOption = [&](LangKey key, Types types) { const auto checkbox = content->add( @@ -64,22 +130,171 @@ void SettingsWidget::setupContent() { } else { _data.types &= ~types; } + _dataTypesChanges.fire_copy(_data.types); refreshButtonsCallback(); }, lifetime()); + return checkbox; + }; + const auto addBigOption = [&](LangKey key, Types types) { + const auto checkbox = addOption(key, types); + const auto onlyMy = content->add( + object_ptr>( + content, + object_ptr( + content, + lang(lng_export_option_only_my), + ((_data.fullChats & types) != types), + st::defaultBoxCheckbox), + st::exportSubSettingPadding)); + + base::ObservableViewer( + onlyMy->entity()->checkedChanged + ) | rpl::start_with_next([=](bool checked) { + if (checked) { + _data.fullChats &= ~types; + } else { + _data.fullChats |= types; + } + }, checkbox->lifetime()); + + onlyMy->toggleOn(base::ObservableViewer( + checkbox->checkedChanged + )); + + onlyMy->toggle(checkbox->checked(), anim::type::instant); + + if (types & (Type::PublicGroups | Type::PublicChannels)) { + onlyMy->entity()->setChecked(true); + onlyMy->entity()->setDisabled(true); + } }; addOption(lng_export_option_info, Type::PersonalInfo | Type::Userpics); addOption(lng_export_option_contacts, Type::Contacts); addOption(lng_export_option_sessions, Type::Sessions); + addHeader(content, lng_export_header_chats); + addOption(lng_export_option_personal_chats, Type::PersonalChats); + addOption(lng_export_option_bot_chats, Type::BotChats); + addBigOption(lng_export_option_private_groups, Type::PrivateGroups); + addBigOption(lng_export_option_private_channels, Type::PrivateChannels); + addBigOption(lng_export_option_public_groups, Type::PublicGroups); + addBigOption(lng_export_option_public_channels, Type::PublicChannels); + const auto mediaWrap = content->add( + object_ptr>( + content, + object_ptr(content))); + const auto media = mediaWrap->entity(); + const auto addSubOption = [&](LangKey key, MediaType type) { + const auto checkbox = media->add( + object_ptr( + media, + lang(key), + ((_data.media.types & type) == type), + st::defaultBoxCheckbox), + st::exportSettingPadding); + base::ObservableViewer( + checkbox->checkedChanged + ) | rpl::start_with_next([=](bool checked) { + if (checked) { + _data.media.types |= type; + } else { + _data.media.types &= ~type; + } + refreshButtonsCallback(); + }, lifetime()); + }; + addHeader(media, lng_export_header_media); + addSubOption(lng_export_option_photos, MediaType::Photo); + addSubOption(lng_export_option_video_files, MediaType::Video); + addSubOption(lng_export_option_voice_messages, MediaType::VoiceMessage); + addSubOption(lng_export_option_video_messages, MediaType::VideoMessage); + addSubOption(lng_export_option_stickers, MediaType::Sticker); + addSubOption(lng_export_option_gifs, MediaType::GIF); + addSubOption(lng_export_option_files, MediaType::File); + createSizeSlider(media); + + _dataTypesChanges.events_starting_with_copy( + _data.types + ) | rpl::start_with_next([=](Settings::Types types) { + mediaWrap->toggle((types & (Type::PersonalChats + | Type::BotChats + | Type::PrivateGroups + | Type::PrivateChannels + | Type::PublicGroups + | Type::PublicChannels)) != 0, anim::type::normal); + }, mediaWrap->lifetime()); + refreshButtonsCallback(); + topShadow->raise(); + bottomShadow->raise(); + sizeValue( ) | rpl::start_with_next([=](QSize size) { + scroll->resize(size.width(), size.height() - buttons->height()); + wrap->resizeToWidth(size.width()); content->resizeToWidth(size.width()); buttons->resizeToWidth(size.width()); + topShadow->resizeToWidth(size.width()); + mediaWrap->resizeToWidth(size.width()); + topShadow->moveToLeft(0, 0); + bottomShadow->resizeToWidth(size.width()); + bottomShadow->moveToLeft(0, scroll->height() - st::lineWidth); buttons->moveToLeft(0, size.height() - buttons->height()); }, lifetime()); } +void SettingsWidget::createSizeSlider( + not_null container) { + using namespace rpl::mappers; + + const auto slider = container->add( + object_ptr(container, st::exportFileSizeSlider), + st::exportFileSizePadding); + slider->resize(st::exportFileSizeSlider.seekSize); + slider->setAlwaysDisplayMarker(true); + slider->setMoveByWheel(true); + slider->setDirection(Ui::ContinuousSlider::Direction::Horizontal); + for (auto i = 0; i != kSizeValueCount + 1; ++i) { + if (_data.media.sizeLimit <= SizeLimitByIndex(i)) { + slider->setValue(i / float64(kSizeValueCount)); + break; + } + } + + const auto label = Ui::CreateChild( + container.get(), + st::exportFileSizeLabel); + const auto refreshSizeLimit = [=] { + const auto limit = _data.media.sizeLimit / kMegabyte; + const auto size = ((limit > 0) + ? QString::number(limit) + : QString::number(float64(_data.media.sizeLimit) / kMegabyte)) + + " MB"; + const auto text = lng_export_option_size_limit(lt_size, size); + label->setText(text); + }; + slider->setAdjustCallback([=](float64 value) { + return std::round(value * kSizeValueCount) / kSizeValueCount; + }); + slider->setChangeProgressCallback([=](float64 value) { + const auto index = int(std::round(value * kSizeValueCount)); + _data.media.sizeLimit = SizeLimitByIndex(index); + refreshSizeLimit(); + }); + refreshSizeLimit(); + + rpl::combine( + label->widthValue(), + slider->geometryValue(), + _2 + ) | rpl::start_with_next([=](QRect geometry) { + label->moveToRight( + st::exportFileSizePadding.right(), + geometry.y() - label->height() - st::exportFileSizeLabelBottom); + }, label->lifetime()); + +} + void SettingsWidget::refreshButtons(not_null container) { container->hideChildren(); const auto children = container->children(); diff --git a/Telegram/SourceFiles/export/view/export_view_settings.h b/Telegram/SourceFiles/export/view/export_view_settings.h index b0ec348231..e7c0ba79b2 100644 --- a/Telegram/SourceFiles/export/view/export_view_settings.h +++ b/Telegram/SourceFiles/export/view/export_view_settings.h @@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "export/export_settings.h" #include "ui/rp_widget.h" +namespace Ui { +class VerticalLayout; +} // namespace Ui + namespace Export { namespace View { @@ -28,6 +32,7 @@ private: void setupContent(); void refreshButtons(not_null container); + void createSizeSlider(not_null container); Settings _data; struct Wrap { @@ -40,6 +45,7 @@ private: }; rpl::variable _startClicks; rpl::variable _cancelClicks; + rpl::event_stream _dataTypesChanges; }; diff --git a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp index 691461c4e5..27d3abe495 100644 --- a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp @@ -14,7 +14,7 @@ constexpr auto kByWheelFinishedTimeout = 1000; } // namespace -ContinuousSlider::ContinuousSlider(QWidget *parent) : TWidget(parent) { +ContinuousSlider::ContinuousSlider(QWidget *parent) : RpWidget(parent) { setCursor(style::cur_pointer); } @@ -58,11 +58,12 @@ void ContinuousSlider::mouseMoveEvent(QMouseEvent *e) { } float64 ContinuousSlider::computeValue(const QPoint &pos) const { - auto seekRect = myrtlrect(getSeekRect()); - auto result = isHorizontal() ? + const auto seekRect = myrtlrect(getSeekRect()); + const auto result = isHorizontal() ? (pos.x() - seekRect.x()) / float64(seekRect.width()) : (1. - (pos.y() - seekRect.y()) / float64(seekRect.height())); - return snap(result, 0., 1.); + const auto snapped = snap(result, 0., 1.); + return _adjustCallback ? _adjustCallback(snapped) : snapped; } void ContinuousSlider::mousePressEvent(QMouseEvent *e) { diff --git a/Telegram/SourceFiles/ui/widgets/continuous_sliders.h b/Telegram/SourceFiles/ui/widgets/continuous_sliders.h index 92d9ca6de4..f85b988073 100644 --- a/Telegram/SourceFiles/ui/widgets/continuous_sliders.h +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.h @@ -8,10 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "styles/style_widgets.h" +#include "ui/rp_widget.h" namespace Ui { -class ContinuousSlider : public TWidget { +class ContinuousSlider : public RpWidget { public: ContinuousSlider(QWidget *parent); @@ -32,11 +33,13 @@ public: return _disabled; } - using Callback = Fn; - void setChangeProgressCallback(Callback &&callback) { + void setAdjustCallback(Fn callback) { + _adjustCallback = std::move(callback); + } + void setChangeProgressCallback(Fn callback) { _changeProgressCallback = std::move(callback); } - void setChangeFinishedCallback(Callback &&callback) { + void setChangeFinishedCallback(Fn callback) { _changeFinishedCallback = std::move(callback); } bool isChanging() const { @@ -86,8 +89,9 @@ private: std::unique_ptr _byWheelFinished; - Callback _changeProgressCallback; - Callback _changeFinishedCallback; + Fn _adjustCallback; + Fn _changeProgressCallback; + Fn _changeFinishedCallback; bool _over = false; Animation _a_over; diff --git a/Telegram/SourceFiles/ui/widgets/labels.cpp b/Telegram/SourceFiles/ui/widgets/labels.cpp index d03180c2c5..75160fa6c6 100644 --- a/Telegram/SourceFiles/ui/widgets/labels.cpp +++ b/Telegram/SourceFiles/ui/widgets/labels.cpp @@ -86,7 +86,11 @@ void CrossFadeAnimation::paintLine(Painter &p, const Line &line, float64 positio } } -LabelSimple::LabelSimple(QWidget *parent, const style::LabelSimple &st, const QString &value) : TWidget(parent) +LabelSimple::LabelSimple( + QWidget *parent, + const style::LabelSimple &st, + const QString &value) +: RpWidget(parent) , _st(st) { setText(value); } diff --git a/Telegram/SourceFiles/ui/widgets/labels.h b/Telegram/SourceFiles/ui/widgets/labels.h index 7419c43d74..030cd7ddd2 100644 --- a/Telegram/SourceFiles/ui/widgets/labels.h +++ b/Telegram/SourceFiles/ui/widgets/labels.h @@ -47,9 +47,12 @@ private: }; -class LabelSimple : public TWidget { +class LabelSimple : public RpWidget { public: - LabelSimple(QWidget *parent, const style::LabelSimple &st = st::defaultLabelSimple, const QString &value = QString()); + LabelSimple( + QWidget *parent, + const style::LabelSimple &st = st::defaultLabelSimple, + const QString &value = QString()); // This method also resizes the label. void setText(const QString &newText, bool *outTextChanged = nullptr);