Ask export path with other export options.

This commit is contained in:
John Preston 2018-06-22 20:46:20 +01:00
parent 10a0c6a086
commit ae18ece549
12 changed files with 310 additions and 184 deletions

View File

@ -1676,7 +1676,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_export_option_gifs" = "Animated GIFs";
"lng_export_option_files" = "Files";
"lng_export_option_size_limit" = "Size limit: {size}";
"lng_export_header_format" = "Format";
"lng_export_header_format" = "Location and format";
"lng_export_option_location" = "Download path: {path}";
"lng_export_option_text" = "Human-readable text";
"lng_export_option_json" = "Machine-readable JSON";
"lng_export_start" = "Export";

View File

@ -31,7 +31,7 @@ void AboutBox::prepare() {
addButton(langFactory(lng_close), [this] { closeBox(); });
const auto linkHook = [](const ClickHandlerPtr &link, auto button) {
const auto linkFilter = [](const ClickHandlerPtr &link, auto button) {
if (const auto url = dynamic_cast<UrlClickHandler*>(link.get())) {
url->UrlClickHandler::onClick(button);
return false;
@ -40,9 +40,9 @@ void AboutBox::prepare() {
};
_text3->setRichText(lng_about_text_3(lt_faq_open, qsl("[a href=\"%1\"]").arg(telegramFaqLink()), lt_faq_close, qsl("[/a]")));
_text1->setClickHandlerHook(linkHook);
_text2->setClickHandlerHook(linkHook);
_text3->setClickHandlerHook(linkHook);
_text1->setClickHandlerFilter(linkFilter);
_text2->setClickHandlerFilter(linkFilter);
_text3->setClickHandlerFilter(linkFilter);
_version->setClickedCallback([this] { showVersionHistory(); });

View File

@ -710,7 +710,7 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() {
_controls.inviteLink->setSelectable(true);
_controls.inviteLink->setContextCopyText(QString());
_controls.inviteLink->setBreakEverywhere(true);
_controls.inviteLink->setClickHandlerHook([this](auto&&...) {
_controls.inviteLink->setClickHandlerFilter([=](auto&&...) {
Application::clipboard()->setText(inviteLinkText());
Ui::Toast::Show(lang(lng_group_invite_copied));
return false;

View File

@ -37,6 +37,12 @@ exportFileSizeLabel: LabelSimple(defaultLabelSimple) {
}
exportFileSizePadding: margins(22px, 8px, 22px, 8px);
exportFileSizeLabelBottom: 18px;
exportLocationLabel: FlatLabel(boxLabel) {
maxHeight: 21px;
}
exportLocationPadding: margins(22px, 8px, 22px, 8px);
exportErrorLabel: FlatLabel(boxLabel) {
minWidth: 175px;
align: align(top);

View File

@ -59,21 +59,13 @@ int SizeLimitByIndex(int index) {
SettingsWidget::SettingsWidget(QWidget *parent)
: RpWidget(parent) {
if (Global::DownloadPath().isEmpty()) {
_data.path = psDownloadPath();
} else if (Global::DownloadPath() == qsl("tmp")) {
_data.path = cTempDir();
} else {
_data.path = Global::DownloadPath();
}
_data.path = psDownloadPath();
_data.internalLinksDomain = Global::InternalLinksDomain();
setupContent();
}
void SettingsWidget::setupContent() {
using namespace rpl::mappers;
const auto scroll = Ui::CreateChild<Ui::ScrollArea>(
this,
st::boxLayerScroll);
@ -82,6 +74,148 @@ void SettingsWidget::setupContent() {
object_ptr<Ui::VerticalLayout>(scroll)));
const auto content = static_cast<Ui::VerticalLayout*>(wrap->entity());
const auto buttons = setupButtons(scroll, wrap);
setupOptions(content);
setupPathAndFormat(content);
_refreshButtons.fire({});
sizeValue(
) | rpl::start_with_next([=](QSize size) {
scroll->resize(size.width(), size.height() - buttons->height());
wrap->resizeToWidth(size.width());
content->resizeToWidth(size.width());
}, lifetime());
}
void SettingsWidget::setupOptions(not_null<Ui::VerticalLayout*> container) {
addOption(
container,
lng_export_option_info,
Type::PersonalInfo | Type::Userpics);
addOption(container, lng_export_option_contacts, Type::Contacts);
addOption(container, lng_export_option_sessions, Type::Sessions);
addHeader(container, lng_export_header_chats);
addOption(
container,
lng_export_option_personal_chats,
Type::PersonalChats);
addOption(container, lng_export_option_bot_chats, Type::BotChats);
addChatOption(
container,
lng_export_option_private_groups,
Type::PrivateGroups);
addChatOption(
container,
lng_export_option_private_channels,
Type::PrivateChannels);
addChatOption(
container,
lng_export_option_public_groups,
Type::PublicGroups);
addChatOption(
container,
lng_export_option_public_channels,
Type::PublicChannels);
setupMediaOptions(container);
}
void SettingsWidget::setupMediaOptions(
not_null<Ui::VerticalLayout*> container) {
const auto mediaWrap = container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)));
const auto media = mediaWrap->entity();
addHeader(media, lng_export_header_media);
addMediaOption(media, lng_export_option_photos, MediaType::Photo);
addMediaOption(media, lng_export_option_video_files, MediaType::Video);
addMediaOption(media, lng_export_option_voice_messages, MediaType::VoiceMessage);
addMediaOption(media, lng_export_option_video_messages, MediaType::VideoMessage);
addMediaOption(media, lng_export_option_stickers, MediaType::Sticker);
addMediaOption(media, lng_export_option_gifs, MediaType::GIF);
addMediaOption(media, lng_export_option_files, MediaType::File);
addSizeSlider(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());
widthValue(
) | rpl::start_with_next([=](int width) {
mediaWrap->resizeToWidth(width);
}, mediaWrap->lifetime());
}
void SettingsWidget::setupPathAndFormat(
not_null<Ui::VerticalLayout*> container) {
const auto formatGroup = std::make_shared<Ui::RadioenumGroup<Format>>(
_data.format);
formatGroup->setChangedCallback([=](Format format) {
_data.format = format;
});
const auto addFormatOption = [&](LangKey key, Format format) {
const auto radio = container->add(
object_ptr<Ui::Radioenum<Format>>(
container,
formatGroup,
format,
lang(key),
st::defaultBoxCheckbox),
st::exportSettingPadding);
};
addHeader(container, lng_export_header_format);
addLocationLabel(container);
addFormatOption(lng_export_option_text, Format::Text);
addFormatOption(lng_export_option_json, Format::Json);
}
void SettingsWidget::addLocationLabel(
not_null<Ui::VerticalLayout*> container) {
auto pathLabel = _locationChanges.events_starting_with_copy(
_data.path
) | rpl::map([](const QString &path) {
const auto text = (path == psDownloadPath())
? QString("Downloads/Telegram Desktop")
: path;
auto pathLink = TextWithEntities{
QDir::toNativeSeparators(text),
EntitiesInText()
};
pathLink.entities.push_back(EntityInText(
EntityInTextCustomUrl,
0,
text.size(),
"internal:edit_export_path"));
return lng_export_option_location__generic<TextWithEntities>(
lt_path,
pathLink);
});
const auto label = container->add(
object_ptr<Ui::FlatLabel>(
container,
std::move(pathLabel),
st::exportLocationLabel),
st::exportLocationPadding);
label->setClickHandlerFilter([=](auto&&...) {
chooseFolder();
return false;
});
}
not_null<Ui::RpWidget*> SettingsWidget::setupButtons(
not_null<Ui::ScrollArea*> scroll,
not_null<Ui::RpWidget*> wrap) {
using namespace rpl::mappers;
const auto buttonsPadding = st::boxButtonPadding;
const auto buttonsHeight = buttonsPadding.top()
+ st::defaultBoxButton.height
@ -101,170 +235,125 @@ void SettingsWidget::setupContent() {
) | rpl::map([=](int top) {
return top < scroll->scrollTopMax();
}));
const auto refreshButtonsCallback = [=] {
_refreshButtons.events(
) | rpl::start_with_next([=] {
refreshButtons(buttons);
};
const auto addHeader = [&](
not_null<Ui::VerticalLayout*> container,
LangKey key) {
container->add(
object_ptr<Ui::FlatLabel>(
container,
lang(key),
Ui::FlatLabel::InitType::Simple,
st::exportHeaderLabel),
st::exportHeaderPadding);
};
const auto addOption = [&](LangKey key, Types types) {
const auto checkbox = content->add(
object_ptr<Ui::Checkbox>(
content,
lang(key),
((_data.types & types) == types),
st::defaultBoxCheckbox),
st::exportSettingPadding);
base::ObservableViewer(
checkbox->checkedChanged
) | rpl::start_with_next([=](bool checked) {
if (checked) {
_data.types |= types;
} 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<Ui::SlideWrap<Ui::Checkbox>>(
content,
object_ptr<Ui::Checkbox>(
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<Ui::SlideWrap<Ui::VerticalLayout>>(
content,
object_ptr<Ui::VerticalLayout>(content)));
const auto media = mediaWrap->entity();
const auto addSubOption = [&](LangKey key, MediaType type) {
const auto checkbox = media->add(
object_ptr<Ui::Checkbox>(
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);
const auto formatGroup = std::make_shared<Ui::RadioenumGroup<Format>>(
_data.format);
formatGroup->setChangedCallback([=](Format format) {
_data.format = format;
});
const auto addFormatOption = [&](LangKey key, Format format) {
const auto radio = content->add(
object_ptr<Ui::Radioenum<Format>>(
content,
formatGroup,
format,
lang(key),
st::defaultBoxCheckbox),
st::exportSettingPadding);
};
addHeader(content, lng_export_header_format);
addFormatOption(lng_export_option_text, Format::Text);
addFormatOption(lng_export_option_json, Format::Json);
_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();
topShadow->raise();
bottomShadow->raise();
}, buttons->lifetime());
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());
buttons->moveToLeft(0, size.height() - buttons->height());
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());
bottomShadow->moveToLeft(0, buttons->y() - st::lineWidth);
}, buttons->lifetime());
return buttons;
}
void SettingsWidget::addHeader(
not_null<Ui::VerticalLayout*> container,
LangKey key) {
container->add(
object_ptr<Ui::FlatLabel>(
container,
lang(key),
Ui::FlatLabel::InitType::Simple,
st::exportHeaderLabel),
st::exportHeaderPadding);
}
not_null<Ui::Checkbox*> SettingsWidget::addOption(
not_null<Ui::VerticalLayout*> container,
LangKey key,
Types types) {
const auto checkbox = container->add(
object_ptr<Ui::Checkbox>(
container,
lang(key),
((_data.types & types) == types),
st::defaultBoxCheckbox),
st::exportSettingPadding);
base::ObservableViewer(
checkbox->checkedChanged
) | rpl::start_with_next([=](bool checked) {
if (checked) {
_data.types |= types;
} else {
_data.types &= ~types;
}
_dataTypesChanges.fire_copy(_data.types);
_refreshButtons.fire({});
}, lifetime());
return checkbox;
}
void SettingsWidget::addChatOption(
not_null<Ui::VerticalLayout*> container,
LangKey key,
Types types) {
const auto checkbox = addOption(container, key, types);
const auto onlyMy = container->add(
object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
container,
object_ptr<Ui::Checkbox>(
container,
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);
}
}
void SettingsWidget::addMediaOption(
not_null<Ui::VerticalLayout*> container,
LangKey key,
MediaType type) {
const auto checkbox = container->add(
object_ptr<Ui::Checkbox>(
container,
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;
}
_refreshButtons.fire({});
}, lifetime());
}
void SettingsWidget::createSizeSlider(
void SettingsWidget::addSizeSlider(
not_null<Ui::VerticalLayout*> container) {
using namespace rpl::mappers;
@ -273,7 +362,6 @@ void SettingsWidget::createSizeSlider(
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)) {
@ -332,7 +420,9 @@ void SettingsWidget::refreshButtons(not_null<Ui::RpWidget*> container) {
: nullptr;
if (start) {
start->show();
start->addClickHandler([=] { chooseFolder(); });
start->addClickHandler([=] {
_startClicks.fire(base::duplicate(_data));
});
container->sizeValue(
) | rpl::start_with_next([=](QSize size) {
@ -363,7 +453,7 @@ void SettingsWidget::refreshButtons(not_null<Ui::RpWidget*> container) {
void SettingsWidget::chooseFolder() {
const auto ready = [=](QString &&result) {
_data.path = result;
_startClicks.fire(base::duplicate(_data));
_locationChanges.fire(std::move(result));
};
FileDialog::GetFolder(this, lang(lng_export_folder), _data.path, ready);
}

View File

@ -10,8 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "export/export_settings.h"
#include "ui/rp_widget.h"
enum LangKey : int;
namespace Ui {
class VerticalLayout;
class Checkbox;
class ScrollArea;
} // namespace Ui
namespace Export {
@ -32,9 +36,32 @@ private:
using Format = Output::Format;
void setupContent();
not_null<Ui::RpWidget*> setupButtons(
not_null<Ui::ScrollArea*> scroll,
not_null<Ui::RpWidget*> wrap);
void setupOptions(not_null<Ui::VerticalLayout*> container);
void setupMediaOptions(not_null<Ui::VerticalLayout*> container);
void setupPathAndFormat(not_null<Ui::VerticalLayout*> container);
void addHeader(
not_null<Ui::VerticalLayout*> container,
LangKey key);
not_null<Ui::Checkbox*> addOption(
not_null<Ui::VerticalLayout*> container,
LangKey key,
Types types);
void addChatOption(
not_null<Ui::VerticalLayout*> container,
LangKey key,
Types types);
void addMediaOption(
not_null<Ui::VerticalLayout*> container,
LangKey key,
MediaType type);
void addSizeSlider(not_null<Ui::VerticalLayout*> container);
void addLocationLabel(
not_null<Ui::VerticalLayout*> container);
void chooseFolder();
void refreshButtons(not_null<Ui::RpWidget*> container);
void createSizeSlider(not_null<Ui::VerticalLayout*> container);
Settings _data;
struct Wrap {
@ -43,11 +70,12 @@ private:
}
rpl::producer<> value;
};
rpl::event_stream<Settings> _startClicks;
rpl::variable<Wrap> _cancelClicks;
rpl::event_stream<Settings::Types> _dataTypesChanges;
rpl::event_stream<> _refreshButtons;
rpl::event_stream<QString> _locationChanges;
};

View File

@ -271,7 +271,7 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
lng_info_link_label,
std::move(linkText),
QString());
link->setClickHandlerHook([peer = _peer](auto&&...) {
link->setClickHandlerFilter([peer = _peer](auto&&...) {
auto link = Messenger::Instance().createInternalLinkFull(
peer->userName());
if (!link.isEmpty()) {

View File

@ -331,7 +331,7 @@ void GroupMembersWidget::refreshLimitReached() {
QString link = TextUtilities::EscapeForRichParsing(lang(lng_profile_migrate_learn_more));
QString text = qsl("%1%2%3\n%4 [a href=\"https://telegram.org/blog/supergroups5k\"]%5[/a]").arg(textcmdStartSemibold()).arg(title).arg(textcmdStopSemibold()).arg(body).arg(link);
_limitReachedInfo->setRichText(text);
_limitReachedInfo->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) {
_limitReachedInfo->setClickHandlerFilter([=](auto&&...) {
Ui::show(Box<ConvertToSupergroupBox>(peer()->asChat()));
return false;
});

View File

@ -88,7 +88,7 @@ void InfoWidget::refreshUsername() {
TextWithEntities(),
copyText);
if (auto text = _username->entity()->textLabel()) {
text->setClickHandlerHook([](const ClickHandlerPtr &handler, Qt::MouseButton button) {
text->setClickHandlerFilter([](auto&&...) {
Ui::show(Box<UsernameBox>());
return false;
});
@ -115,7 +115,7 @@ void InfoWidget::refreshBio() {
TextWithEntities(),
QString());
if (auto text = _bio->entity()->textLabel()) {
text->setClickHandlerHook([](const ClickHandlerPtr &handler, Qt::MouseButton button) {
text->setClickHandlerFilter([](auto&&...) {
Ui::show(Box<EditBioBox>(App::self()));
return false;
});

View File

@ -272,8 +272,8 @@ void FlatLabel::setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk) {
_text.setLink(lnkIndex, lnk);
}
void FlatLabel::setClickHandlerHook(ClickHandlerHook &&hook) {
_clickHandlerHook = std::move(hook);
void FlatLabel::setClickHandlerFilter(ClickHandlerFilter &&filter) {
_clickHandlerFilter = std::move(filter);
}
void FlatLabel::mouseMoveEvent(QMouseEvent *e) {
@ -358,7 +358,8 @@ Text::StateResult FlatLabel::dragActionFinish(const QPoint &p, Qt::MouseButton b
_selectionType = TextSelectType::Letters;
if (activated) {
if (!_clickHandlerHook || _clickHandlerHook(activated, button)) {
if (!_clickHandlerFilter
|| _clickHandlerFilter(activated, button)) {
App::activateClickHandler(activated, button);
}
}

View File

@ -112,8 +112,8 @@ public:
void setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk);
using ClickHandlerHook = Fn<bool(const ClickHandlerPtr&, Qt::MouseButton)>;
void setClickHandlerHook(ClickHandlerHook &&hook);
using ClickHandlerFilter = Fn<bool(const ClickHandlerPtr&, Qt::MouseButton)>;
void setClickHandlerFilter(ClickHandlerFilter &&filter);
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) override;
@ -207,7 +207,7 @@ private:
QString _contextCopyText;
ExpandLinksMode _contextExpandLinksMode = ExpandLinksAll;
ClickHandlerHook _clickHandlerHook;
ClickHandlerFilter _clickHandlerFilter;
// text selection and context menu by touch support (at least Windows Surface tablets)
bool _touchSelect = false;

View File

@ -243,7 +243,7 @@ void TermsBox::prepare() {
st::termsPadding),
0,
age ? age->height() : 0);
content->entity()->setClickHandlerHook([=](
content->entity()->setClickHandlerFilter([=](
const ClickHandlerPtr &handler,
Qt::MouseButton button) {
const auto link = handler