From 229bc56cc873f1fd6d3b9ea10f3de3d53ff95740 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 3 Sep 2019 11:25:19 +0300 Subject: [PATCH] Upload saved theme to the cloud. --- Telegram/Resources/langs/lang.strings | 1 + .../SourceFiles/settings/settings_chat.cpp | 8 +- Telegram/SourceFiles/storage/file_upload.cpp | 20 +- .../SourceFiles/storage/localimageloader.cpp | 62 -- .../SourceFiles/storage/localimageloader.h | 3 +- .../window/themes/window_theme.cpp | 63 ++ .../window/themes/window_theme_create_box.cpp | 148 ---- .../window/themes/window_theme_editor.cpp | 233 ++---- .../window/themes/window_theme_editor.h | 9 +- .../window/themes/window_theme_editor_box.cpp | 664 ++++++++++++++++++ ...create_box.h => window_theme_editor_box.h} | 17 +- Telegram/SourceFiles/window/window.style | 15 + .../SourceFiles/window/window_controller.cpp | 6 +- .../SourceFiles/window/window_controller.h | 2 + Telegram/gyp/telegram_sources.txt | 4 +- 15 files changed, 834 insertions(+), 421 deletions(-) delete mode 100644 Telegram/SourceFiles/window/themes/window_theme_create_box.cpp create mode 100644 Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp rename Telegram/SourceFiles/window/themes/{window_theme_create_box.h => window_theme_editor_box.h} (57%) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index e194ae897..bad6a2883 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1603,6 +1603,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_theme_editor_done" = "Theme exported successfully!"; "lng_theme_editor_title" = "Edit color palette"; "lng_theme_editor_export_button" = "Export theme"; +"lng_theme_editor_save_button" = "Save theme"; "lng_theme_editor_create_title" = "Create theme"; "lng_theme_editor_name" = "Theme name"; diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 2838dd2d1..fb0681f9a 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -27,10 +27,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/image/image_source.h" #include "lang/lang_keys.h" -#include "window/themes/window_theme_editor.h" #include "window/themes/window_theme.h" #include "window/themes/window_themes_embedded.h" -#include "window/themes/window_theme_create_box.h" +#include "window/themes/window_theme_editor_box.h" #include "window/window_session_controller.h" #include "info/profile/info_profile_button.h" #include "storage/localstorage.h" @@ -43,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "support/support_templates.h" #include "main/main_session.h" #include "mainwidget.h" +#include "mainwindow.h" #include "styles/style_settings.h" #include "styles/style_boxes.h" @@ -1264,7 +1264,9 @@ void SetupThemeOptions( &st::settingsIconThemes, st::settingsChatIconLeft )->addClickHandler([=] { - Ui::show(Box(Window::Theme::CreateBox, &controller->session())); + Ui::show(Box( + Window::Theme::CreateBox, + &controller->window()->controller())); }); AddSkip(container); diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index a8ff66f94..81aa63777 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -76,7 +76,7 @@ struct Uploader::File { Uploader::File::File(const SendMediaReady &media) : media(media) { partsCount = media.parts.size(); if (type() == SendMediaType::File - || type() == SendMediaType::WallPaper + || type() == SendMediaType::ThemeFile || type() == SendMediaType::Audio) { setDocSize(media.file.isEmpty() ? media.data.size() @@ -92,7 +92,7 @@ Uploader::File::File(const std::shared_ptr &file) ? file->fileparts.size() : file->thumbparts.size(); if (type() == SendMediaType::File - || type() == SendMediaType::WallPaper + || type() == SendMediaType::ThemeFile || type() == SendMediaType::Audio) { setDocSize(file->filesize); } else { @@ -154,7 +154,7 @@ void Uploader::uploadMedia( if (media.type == SendMediaType::Photo) { Auth().data().processPhoto(media.photo, media.photoThumbs); } else if (media.type == SendMediaType::File - || media.type == SendMediaType::WallPaper + || media.type == SendMediaType::ThemeFile || media.type == SendMediaType::Audio) { const auto document = media.photoThumbs.empty() ? Auth().data().processDocument(media.document) @@ -163,7 +163,7 @@ void Uploader::uploadMedia( base::duplicate(media.photoThumbs.front().second)); if (!media.data.isEmpty()) { document->setData(media.data); - if (media.type == SendMediaType::WallPaper) { + if (media.type == SendMediaType::ThemeFile) { document->checkWallPaperProperties(); } if (document->saveToCache() @@ -193,7 +193,7 @@ void Uploader::upload( photo->uploadingData = std::make_unique( file->partssize); } else if (file->type == SendMediaType::File - || file->type == SendMediaType::WallPaper + || file->type == SendMediaType::ThemeFile || file->type == SendMediaType::Audio) { const auto document = file->thumb.isNull() ? Auth().data().processDocument(file->document) @@ -207,7 +207,7 @@ void Uploader::upload( std::move(file->goodThumbnailBytes)); if (!file->content.isEmpty()) { document->setData(file->content); - if (file->type == SendMediaType::WallPaper) { + if (file->type == SendMediaType::ThemeFile) { document->checkWallPaperProperties(); } if (document->saveToCache() @@ -233,7 +233,7 @@ void Uploader::currentFailed() { if (j->second.type() == SendMediaType::Photo) { _photoFailed.fire_copy(j->first); } else if (j->second.type() == SendMediaType::File - || j->second.type() == SendMediaType::WallPaper + || j->second.type() == SendMediaType::ThemeFile || j->second.type() == SendMediaType::Audio) { const auto document = Auth().data().document(j->second.id()); if (document->uploading()) { @@ -335,7 +335,7 @@ void Uploader::sendNext() { MTP_bytes(md5)); _photoReady.fire({ uploadingId, options, file, edit }); } else if (uploadingData.type() == SendMediaType::File - || uploadingData.type() == SendMediaType::WallPaper + || uploadingData.type() == SendMediaType::ThemeFile || uploadingData.type() == SendMediaType::Audio) { QByteArray docMd5(32, Qt::Uninitialized); hashMd5Hex(uploadingData.md5Hash.result(), docMd5.data()); @@ -412,7 +412,7 @@ void Uploader::sendNext() { * uploadingData.docPartSize; toSend = content.mid(offset, uploadingData.docPartSize); if ((uploadingData.type() == SendMediaType::File - || uploadingData.type() == SendMediaType::WallPaper + || uploadingData.type() == SendMediaType::ThemeFile || uploadingData.type() == SendMediaType::Audio) && uploadingData.docSentParts <= kUseBigFilesFrom) { uploadingData.md5Hash.feed(toSend.constData(), toSend.size()); @@ -554,7 +554,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { } _photoProgress.fire_copy(fullId); } else if (file.type() == SendMediaType::File - || file.type() == SendMediaType::WallPaper + || file.type() == SendMediaType::ThemeFile || file.type() == SendMediaType::Audio) { const auto document = Auth().data().document(file.id()); if (document->uploading()) { diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 930d635e1..ce611eae4 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -243,68 +243,6 @@ SendMediaReady PreparePeerPhoto(PeerId peerId, QImage &&image) { 0); } -SendMediaReady PrepareWallPaper(const QImage &image) { - PreparedPhotoThumbs thumbnails; - QVector sizes; - - QByteArray jpeg; - QBuffer jpegBuffer(&jpeg); - image.save(&jpegBuffer, "JPG", 87); - - const auto scaled = [&](int size) { - return image.scaled( - size, - size, - Qt::KeepAspectRatio, - Qt::SmoothTransformation); - }; - const auto push = [&](const char *type, QImage &&image) { - sizes.push_back(MTP_photoSize( - MTP_string(type), - MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), - MTP_int(image.width()), - MTP_int(image.height()), MTP_int(0))); - thumbnails.emplace(type[0], std::move(image)); - }; - push("s", scaled(320)); - - const auto filename = qsl("wallpaper.jpg"); - auto attributes = QVector( - 1, - MTP_documentAttributeFilename(MTP_string(filename))); - attributes.push_back(MTP_documentAttributeImageSize( - MTP_int(image.width()), - MTP_int(image.height()))); - const auto id = rand_value(); - const auto document = MTP_document( - MTP_flags(0), - MTP_long(id), - MTP_long(0), - MTP_bytes(), - MTP_int(base::unixtime::now()), - MTP_string("image/jpeg"), - MTP_int(jpeg.size()), - MTP_vector(sizes), - MTP_int(MTP::maindc()), - MTP_vector(attributes)); - - return SendMediaReady( - SendMediaType::WallPaper, - QString(), // filepath - filename, - jpeg.size(), - jpeg, - id, - 0, - QString(), - PeerId(), - MTP_photoEmpty(MTP_long(0)), - thumbnails, - document, - QByteArray(), - 0); -} - TaskQueue::TaskQueue(crl::time stopTimeoutMs) { if (stopTimeoutMs > 0) { _stopTimer = new QTimer(this); diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index 3954f0d47..05ae9d4e8 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -21,7 +21,7 @@ enum class SendMediaType { Photo, Audio, File, - WallPaper, + ThemeFile, Secure, }; @@ -85,7 +85,6 @@ struct SendMediaReady { }; SendMediaReady PreparePeerPhoto(PeerId peerId, QImage &&image); -SendMediaReady PrepareWallPaper(const QImage &image); using TaskId = void*; // no interface, just id diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index bb86159b7..7046039c9 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/file_upload.h" #include "base/parse_helper.h" #include "base/zlib_help.h" +#include "base/unixtime.h" #include "data/data_session.h" #include "main/main_account.h" // Account::sessionValue. #include "ui/image/image.h" @@ -395,6 +396,68 @@ void ClearApplying() { GlobalApplying = Applying(); } +SendMediaReady PrepareWallPaper(const QImage &image) { + PreparedPhotoThumbs thumbnails; + QVector sizes; + + QByteArray jpeg; + QBuffer jpegBuffer(&jpeg); + image.save(&jpegBuffer, "JPG", 87); + + const auto scaled = [&](int size) { + return image.scaled( + size, + size, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + }; + const auto push = [&](const char *type, QImage &&image) { + sizes.push_back(MTP_photoSize( + MTP_string(type), + MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), + MTP_int(image.width()), + MTP_int(image.height()), MTP_int(0))); + thumbnails.emplace(type[0], std::move(image)); + }; + push("s", scaled(320)); + + const auto filename = qsl("wallpaper.jpg"); + auto attributes = QVector( + 1, + MTP_documentAttributeFilename(MTP_string(filename))); + attributes.push_back(MTP_documentAttributeImageSize( + MTP_int(image.width()), + MTP_int(image.height()))); + const auto id = rand_value(); + const auto document = MTP_document( + MTP_flags(0), + MTP_long(id), + MTP_long(0), + MTP_bytes(), + MTP_int(base::unixtime::now()), + MTP_string("image/jpeg"), + MTP_int(jpeg.size()), + MTP_vector(sizes), + MTP_int(MTP::maindc()), + MTP_vector(attributes)); + + return SendMediaReady( + SendMediaType::ThemeFile, + QString(), // filepath + filename, + jpeg.size(), + jpeg, + id, + 0, + QString(), + PeerId(), + MTP_photoEmpty(MTP_long(0)), + thumbnails, + document, + QByteArray(), + 0); +} + } // namespace ChatBackground::AdjustableColor::AdjustableColor(style::color data) diff --git a/Telegram/SourceFiles/window/themes/window_theme_create_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_create_box.cpp deleted file mode 100644 index 6fa32e3ea..000000000 --- a/Telegram/SourceFiles/window/themes/window_theme_create_box.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "window/themes/window_theme_create_box.h" - -#include "window/themes/window_theme.h" -#include "window/themes/window_theme_editor.h" -#include "boxes/generic_box.h" -#include "boxes/confirm_box.h" -#include "ui/text/text_utilities.h" -#include "ui/widgets/input_fields.h" -#include "ui/widgets/labels.h" -#include "info/profile/info_profile_button.h" -#include "main/main_session.h" -#include "core/file_utilities.h" -#include "lang/lang_keys.h" -#include "storage/localstorage.h" -#include "mainwindow.h" -#include "styles/style_widgets.h" -#include "styles/style_window.h" -#include "styles/style_boxes.h" - -namespace Window { -namespace Theme { -namespace { - -void ImportFromFile( - not_null session, - not_null parent) { - const auto &imgExtensions = cImgExtensions(); - auto filters = QStringList( - qsl("Theme files (*.tdesktop-theme *.tdesktop-palette)")); - filters.push_back(FileDialog::AllFilesFilter()); - const auto callback = crl::guard(session, [=]( - const FileDialog::OpenResult &result) { - if (result.paths.isEmpty()) { - return; - } - Window::Theme::Apply(result.paths.front()); - }); - FileDialog::GetOpenPath( - parent.get(), - tr::lng_choose_image(tr::now), - filters.join(qsl(";;")), - crl::guard(parent, callback)); -} - -QString BytesToUTF8(QLatin1String string) { - return QString::fromUtf8(string.data(), string.size()); -} - -void WriteDefaultPalette(const QString &path) { - QFile f(path); - if (!f.open(QIODevice::WriteOnly)) { - LOG(("Theme Error: could not open '%1' for writing.").arg(path)); - return; - } - - QTextStream stream(&f); - stream.setCodec("UTF-8"); - - auto rows = style::main_palette::data(); - for (const auto &row : std::as_const(rows)) { - stream - << BytesToUTF8(row.name) - << ": " - << BytesToUTF8(row.value) - << "; // " - << BytesToUTF8( - row.description - ).replace( - '\n', - ' ' - ).replace( - '\r', - ' ') - << "\n"; - } -} - -void StartEditor( - not_null session, - const QString &title) { - const auto path = EditingPalettePath(); - if (!Local::copyThemeColorsToPalette(path)) { - WriteDefaultPalette(path); - } - if (!Apply(path)) { - Ui::show(Box(tr::lng_theme_editor_error(tr::now))); - return; - } - KeepApplied(); - if (const auto window = App::wnd()) { - window->showRightColumn(Box()); - } -} - -} // namespace - -void CreateBox(not_null box, not_null session) { - box->setTitle(tr::lng_theme_editor_create_title(Ui::Text::WithEntities)); - - const auto name = box->addRow(object_ptr( - box, - st::defaultInputField, - tr::lng_theme_editor_name())); - - box->addRow( - object_ptr( - box, - tr::lng_theme_editor_create_description(), - st::boxDividerLabel), - style::margins( - st::boxRowPadding.left(), - st::boxRowPadding.left(), - st::boxRowPadding.right(), - st::boxRowPadding.right())); - - box->addRow( - object_ptr( - box, - tr::lng_theme_editor_import_existing() | Ui::Text::ToUpper(), - st::createThemeImportButton), - style::margins() - )->addClickHandler([=] { - ImportFromFile(session, box); - }); - - box->setFocusCallback([=] { name->setFocusFast(); }); - - box->addButton(tr::lng_box_done(), [=] { - const auto title = name->getLastText().trimmed(); - if (title.isEmpty()) { - name->showError(); - return; - } - box->closeBox(); - StartEditor(session, title); - }); - box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); -} - -} // namespace Theme -} // namespace Window diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp index ed911a20a..73e6c8dcb 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp @@ -9,9 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/themes/window_theme.h" #include "window/themes/window_theme_editor_block.h" +#include "window/themes/window_theme_editor_box.h" #include "window/themes/window_themes_embedded.h" +#include "window/window_controller.h" +#include "main/main_account.h" #include "mainwindow.h" -#include "layout.h" #include "storage/localstorage.h" #include "boxes/confirm_box.h" #include "styles/style_window.h" @@ -20,9 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/scroll_area.h" #include "ui/widgets/shadow.h" #include "ui/widgets/buttons.h" -#include "ui/widgets/checkbox.h" #include "ui/widgets/multi_select.h" -#include "ui/image/image_prepare.h" #include "ui/toast/toast.h" #include "base/parse_helper.h" #include "base/zlib_help.h" @@ -247,8 +247,9 @@ public: } void prepare(); - - Fn exportCallback(); + [[nodiscard]] QByteArray paletteContent() const { + return _paletteContent; + } void filterRows(const QString &query); void chooseRow(); @@ -293,34 +294,6 @@ private: }; -class ThemeExportBox : public BoxContent { -public: - ThemeExportBox(QWidget*, const QByteArray &paletteContent, const QImage &background, const QByteArray &backgroundContent, bool tileBackground); - -protected: - void prepare() override; - - void paintEvent(QPaintEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - -private: - void updateThumbnail(); - void chooseBackgroundFromFile(); - void exportTheme(); - - QByteArray _paletteContent; - - QImage _background; - QByteArray _backgroundContent; - bool _isPng = false; - QString _imageText; - QPixmap _thumbnail; - - object_ptr _chooseFromFile; - object_ptr _tileBackground; - -}; - bool CopyColorsToPalette( const QString &destination, const QString &themePath, @@ -402,19 +375,6 @@ void Editor::Inner::prepare() { } } -Fn Editor::Inner::exportCallback() { - return App::LambdaDelayed(st::defaultRippleAnimation.hideDuration, this, [=] { - auto background = Background()->createCurrentImage(); - auto backgroundContent = QByteArray(); - auto tiled = Background()->tile(); - { - QBuffer buffer(&backgroundContent); - background.save(&buffer, "JPG", 87); - } - Ui::show(Box(_paletteContent, background, backgroundContent, tiled)); - }); -} - void Editor::Inner::filterRows(const QString &query) { if (query == ":sort-for-accent") { sortByAccentDistance(); @@ -637,150 +597,45 @@ void Editor::Inner::applyEditing(const QString &name, const QString ©Of, QCo _paletteContent = newContent; } -ThemeExportBox::ThemeExportBox(QWidget*, const QByteArray &paletteContent, const QImage &background, const QByteArray &backgroundContent, bool tileBackground) : BoxContent() -, _paletteContent(paletteContent) -, _background(background) -, _backgroundContent(backgroundContent) -, _chooseFromFile(this, tr::lng_settings_bg_from_file(tr::now), st::boxLinkButton) -, _tileBackground(this, tr::lng_settings_bg_tile(tr::now), tileBackground, st::defaultBoxCheckbox) { - _imageText = tr::lng_theme_editor_saved_to_jpg(tr::now, lt_size, formatSizeText(_backgroundContent.size())); - _chooseFromFile->setClickedCallback([this] { chooseBackgroundFromFile(); }); -} +//void ThemeExportBox::exportTheme() { +// App::CallDelayed(st::defaultRippleAnimation.hideDuration, this, [this] { +// auto caption = tr::lng_theme_editor_choose_name(tr::now); +// auto filter = "Themes (*.tdesktop-theme)"; +// auto name = "awesome.tdesktop-theme"; +// FileDialog::GetWritePath(this, caption, filter, name, crl::guard(this, [this](const QString &path) { +// QFile f(path); +// if (!f.open(QIODevice::WriteOnly)) { +// LOG(("Theme Error: could not open zip-ed theme file '%1' for writing").arg(path)); +// Ui::show(Box(tr::lng_theme_editor_error(tr::now))); +// return; +// } +// if (f.write(result) != result.size()) { +// LOG(("Theme Error: could not write zip-ed theme to file '%1'").arg(path)); +// Ui::show(Box(tr::lng_theme_editor_error(tr::now))); +// return; +// } +// Ui::hideLayer(); +// Ui::Toast::Show(tr::lng_theme_editor_done(tr::now)); +// })); +// }); +//} -void ThemeExportBox::prepare() { - setTitle(tr::lng_theme_editor_background_image()); - - addButton(tr::lng_theme_editor_export(), [this] { exportTheme(); }); - addButton(tr::lng_cancel(), [this] { closeBox(); }); - - auto height = st::themesSmallSkip + st::themesBackgroundSize + st::themesSmallSkip + _tileBackground->height(); - - setDimensions(st::boxWideWidth, height); - - updateThumbnail(); -} - -void ThemeExportBox::paintEvent(QPaintEvent *e) { - BoxContent::paintEvent(e); - - Painter p(this); - - auto linkLeft = st::boxPadding.left() + st::themesBackgroundSize + st::themesSmallSkip; - - p.setPen(st::boxTextFg); - p.setFont(st::boxTextFont); - p.drawTextLeft(linkLeft, st::themesSmallSkip, width(), _imageText); - - p.drawPixmapLeft(st::boxPadding.left(), st::themesSmallSkip, width(), _thumbnail); -} - -void ThemeExportBox::resizeEvent(QResizeEvent *e) { - auto linkLeft = st::boxPadding.left() + st::themesBackgroundSize + st::themesSmallSkip; - _chooseFromFile->moveToLeft(linkLeft, st::themesSmallSkip + st::boxTextFont->height + st::themesSmallSkip); - _tileBackground->moveToLeft(st::boxPadding.left(), st::themesSmallSkip + st::themesBackgroundSize + 2 * st::themesSmallSkip); -} - -void ThemeExportBox::updateThumbnail() { - int32 size = st::themesBackgroundSize * cIntRetinaFactor(); - QImage back(size, size, QImage::Format_ARGB32_Premultiplied); - back.setDevicePixelRatio(cRetinaFactor()); - { - Painter p(&back); - PainterHighQualityEnabler hq(p); - - auto &pix = _background; - int sx = (pix.width() > pix.height()) ? ((pix.width() - pix.height()) / 2) : 0; - int sy = (pix.height() > pix.width()) ? ((pix.height() - pix.width()) / 2) : 0; - int s = (pix.width() > pix.height()) ? pix.height() : pix.width(); - p.drawImage(QRect(0, 0, st::themesBackgroundSize, st::themesBackgroundSize), pix, QRect(sx, sy, s, s)); - } - Images::prepareRound(back, ImageRoundRadius::Small); - _thumbnail = App::pixmapFromImageInPlace(std::move(back)); - _thumbnail.setDevicePixelRatio(cRetinaFactor()); - update(); -} - -void ThemeExportBox::chooseBackgroundFromFile() { - FileDialog::GetOpenPath(this, tr::lng_theme_editor_choose_image(tr::now), "Image files (*.jpeg *.jpg *.png)", crl::guard(this, [this](const FileDialog::OpenResult &result) { - auto content = result.remoteContent; - if (!result.paths.isEmpty()) { - QFile f(result.paths.front()); - if (f.open(QIODevice::ReadOnly)) { - content = f.readAll(); - f.close(); - } - } - if (!content.isEmpty()) { - auto format = QByteArray(); - auto image = App::readImage(content, &format); - if (!image.isNull() && (format == "jpeg" || format == "jpg" || format == "png")) { - _background = image; - _backgroundContent = content; - _isPng = (format == "png"); - auto sizeText = formatSizeText(_backgroundContent.size()); - _imageText = _isPng ? tr::lng_theme_editor_read_from_png(tr::now, lt_size, sizeText) : tr::lng_theme_editor_read_from_jpg(tr::now, lt_size, sizeText); - _tileBackground->setChecked(false); - updateThumbnail(); - } - } - })); -} - -void ThemeExportBox::exportTheme() { - App::CallDelayed(st::defaultRippleAnimation.hideDuration, this, [this] { - auto caption = tr::lng_theme_editor_choose_name(tr::now); - auto filter = "Themes (*.tdesktop-theme)"; - auto name = "awesome.tdesktop-theme"; - FileDialog::GetWritePath(this, caption, filter, name, crl::guard(this, [this](const QString &path) { - zlib::FileToWrite zip; - - zip_fileinfo zfi = { { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 }; - auto background = std::string(_tileBackground->checked() ? "tiled" : "background") + (_isPng ? ".png" : ".jpg"); - zip.openNewFile(background.c_str(), &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION); - zip.writeInFile(_backgroundContent.constData(), _backgroundContent.size()); - zip.closeFile(); - auto scheme = "colors.tdesktop-theme"; - zip.openNewFile(scheme, &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION); - zip.writeInFile(_paletteContent.constData(), _paletteContent.size()); - zip.closeFile(); - zip.close(); - - if (zip.error() != ZIP_OK) { - LOG(("Theme Error: could not export zip-ed theme, status: %1").arg(zip.error())); - Ui::show(Box(tr::lng_theme_editor_error(tr::now))); - return; - } - auto result = zip.result(); - - QFile f(path); - if (!f.open(QIODevice::WriteOnly)) { - LOG(("Theme Error: could not open zip-ed theme file '%1' for writing").arg(path)); - Ui::show(Box(tr::lng_theme_editor_error(tr::now))); - return; - } - if (f.write(result) != result.size()) { - LOG(("Theme Error: could not write zip-ed theme to file '%1'").arg(path)); - Ui::show(Box(tr::lng_theme_editor_error(tr::now))); - return; - } - Ui::hideLayer(); - Ui::Toast::Show(tr::lng_theme_editor_done(tr::now)); - })); - }); -} - -Editor::Editor(QWidget*) -: _scroll(this, st::themesScroll) +Editor::Editor(QWidget*, not_null window) +: _window(window) +, _scroll(this, st::themesScroll) , _close(this, st::contactsMultiSelect.fieldCancel) , _select(this, st::contactsMultiSelect, tr::lng_country_ph()) , _leftShadow(this) , _topShadow(this) -, _export(this, tr::lng_theme_editor_export_button(tr::now).toUpper(), st::dialogsUpdateButton) { +, _save(this, tr::lng_theme_editor_save_button(tr::now).toUpper(), st::dialogsUpdateButton) { const auto path = EditingPalettePath(); _inner = _scroll->setOwnedWidget(object_ptr(this, path)); - _export->setClickedCallback(_inner->exportCallback()); + _save->setClickedCallback(App::LambdaDelayed( + st::defaultRippleAnimation.hideDuration, + this, + [=] { save(); })); _inner->setErrorCallback([this] { Ui::show(Box(tr::lng_theme_editor_error(tr::now))); @@ -808,8 +663,16 @@ Editor::Editor(QWidget*) resizeToWidth(st::windowMinWidth); } +void Editor::save() { + if (!_window->account().sessionExists()) { + //_window->show(Box()) + return; + } + Ui::show(Box(SaveThemeBox, _window, _inner->paletteContent())); +} + void Editor::resizeEvent(QResizeEvent *e) { - _export->resizeToWidth(width()); + _save->resizeToWidth(width()); _close->moveToRight(0, 0); _select->resizeToWidth(width()); @@ -821,7 +684,7 @@ void Editor::resizeEvent(QResizeEvent *e) { _topShadow->moveToLeft(st::lineWidth, shadowTop); _leftShadow->resize(st::lineWidth, height()); _leftShadow->moveToLeft(0, 0); - auto scrollSize = QSize(width(), height() - shadowTop - _export->height()); + auto scrollSize = QSize(width(), height() - shadowTop - _save->height()); if (_scroll->size() != scrollSize) { _scroll->resize(scrollSize); } @@ -831,7 +694,7 @@ void Editor::resizeEvent(QResizeEvent *e) { auto scrollTop = _scroll->scrollTop(); _inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height()); } - _export->moveToLeft(0, _scroll->y() + _scroll->height()); + _save->moveToLeft(0, _scroll->y() + _scroll->height()); } void Editor::keyPressEvent(QKeyEvent *e) { @@ -894,7 +757,7 @@ void Editor::paintEvent(QPaintEvent *e) { //} void Editor::closeEditor() { - if (auto window = App::wnd()) { + if (const auto window = App::wnd()) { window->showRightColumn(nullptr); } } diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.h b/Telegram/SourceFiles/window/themes/window_theme_editor.h index e710760c5..ae1c6cee7 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor.h +++ b/Telegram/SourceFiles/window/themes/window_theme_editor.h @@ -16,6 +16,9 @@ class PlainShadow; } // namespace Ui namespace Window { + +class Controller; + namespace Theme { bool CopyColorsToPalette( @@ -25,7 +28,7 @@ bool CopyColorsToPalette( class Editor : public TWidget { public: - explicit Editor(QWidget*); + Editor(QWidget*, not_null window); protected: void paintEvent(QPaintEvent *e) override; @@ -35,8 +38,10 @@ protected: void focusInEvent(QFocusEvent *e) override; private: + void save(); void closeEditor(); + not_null _window; object_ptr _scroll; class Inner; QPointer _inner; @@ -44,7 +49,7 @@ private: object_ptr _select; object_ptr _leftShadow; object_ptr _topShadow; - object_ptr _export; + object_ptr _save; }; diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp new file mode 100644 index 000000000..c73a610f3 --- /dev/null +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -0,0 +1,664 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "window/themes/window_theme_editor_box.h" + +#include "window/themes/window_theme.h" +#include "window/themes/window_theme_editor.h" +#include "window/window_controller.h" +#include "boxes/confirm_box.h" +#include "ui/text/text_utilities.h" +#include "ui/widgets/input_fields.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/labels.h" +#include "ui/image/image_prepare.h" +#include "ui/toast/toast.h" +#include "info/profile/info_profile_button.h" +#include "main/main_account.h" +#include "main/main_session.h" +#include "storage/localstorage.h" +#include "core/file_utilities.h" +#include "core/application.h" +#include "lang/lang_keys.h" +#include "base/zlib_help.h" +#include "base/unixtime.h" +#include "data/data_session.h" +#include "data/data_document.h" +#include "storage/file_upload.h" +#include "mainwindow.h" +#include "layout.h" +#include "apiwrap.h" +#include "styles/style_widgets.h" +#include "styles/style_window.h" +#include "styles/style_settings.h" +#include "styles/style_boxes.h" + +namespace Window { +namespace Theme { +namespace { + +constexpr auto kRandomSlugSize = 16; +constexpr auto kMinSlugSize = 5; +constexpr auto kMaxSlugSize = 64; + +enum class SaveErrorType { + Other, + Name, + Link, +}; + +struct PreparedBackground { + QByteArray content; + bool tile = false; + bool isPng = false; +}; + +class BackgroundSelector : public Ui::RpWidget { +public: + BackgroundSelector( + QWidget *parent, + const QImage &background, + const PreparedBackground &data); + + [[nodiscard]] PreparedBackground result() const; + + int resizeGetHeight(int newWidth) override; + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + void updateThumbnail(); + void chooseBackgroundFromFile(); + + object_ptr _chooseFromFile; + object_ptr _tileBackground; + + QImage _background; + QByteArray _backgroundContent; + bool _isPng = false; + QString _imageText; + QPixmap _thumbnail; + +}; + +BackgroundSelector::BackgroundSelector( + QWidget *parent, + const QImage &background, + const PreparedBackground &data) +: RpWidget(parent) +, _chooseFromFile( + this, + tr::lng_settings_bg_from_file(tr::now), + st::boxLinkButton) +, _tileBackground( + this, + tr::lng_settings_bg_tile(tr::now), + data.tile, + st::defaultBoxCheckbox) +, _background(background) +, _backgroundContent(data.content) { + _imageText = tr::lng_theme_editor_saved_to_jpg( + tr::now, + lt_size, + formatSizeText(_backgroundContent.size())); + _chooseFromFile->setClickedCallback([=] { chooseBackgroundFromFile(); }); + + const auto height = st::boxTextFont->height + + st::themesSmallSkip + + _chooseFromFile->heightNoMargins() + + st::themesSmallSkip + + _tileBackground->heightNoMargins(); + resize(width(), height); + + updateThumbnail(); +} + +void BackgroundSelector::paintEvent(QPaintEvent *e) { + Painter p(this); + + const auto left = height() + st::themesSmallSkip; + + p.setPen(st::boxTextFg); + p.setFont(st::boxTextFont); + p.drawTextLeft(left, 0, width(), _imageText); + + p.drawPixmapLeft(0, 0, width(), _thumbnail); +} + +int BackgroundSelector::resizeGetHeight(int newWidth) { + const auto left = height() + st::themesSmallSkip; + _chooseFromFile->moveToLeft(left, st::boxTextFont->height + st::themesSmallSkip); + _tileBackground->moveToLeft(left, st::boxTextFont->height + st::themesSmallSkip + _chooseFromFile->height() + st::themesSmallSkip); + return height(); +} + +void BackgroundSelector::updateThumbnail() { + const auto size = height(); + auto back = QImage( + QSize(size, size) * cIntRetinaFactor(), + QImage::Format_ARGB32_Premultiplied); + back.setDevicePixelRatio(cRetinaFactor()); + { + Painter p(&back); + PainterHighQualityEnabler hq(p); + + auto &pix = _background; + int sx = (pix.width() > pix.height()) ? ((pix.width() - pix.height()) / 2) : 0; + int sy = (pix.height() > pix.width()) ? ((pix.height() - pix.width()) / 2) : 0; + int s = (pix.width() > pix.height()) ? pix.height() : pix.width(); + p.drawImage(QRect(0, 0, size, size), pix, QRect(sx, sy, s, s)); + } + Images::prepareRound(back, ImageRoundRadius::Small); + _thumbnail = App::pixmapFromImageInPlace(std::move(back)); + _thumbnail.setDevicePixelRatio(cRetinaFactor()); + update(); +} + +void BackgroundSelector::chooseBackgroundFromFile() { + const auto callback = [=](const FileDialog::OpenResult &result) { + auto content = result.remoteContent; + if (!result.paths.isEmpty()) { + QFile f(result.paths.front()); + if (f.open(QIODevice::ReadOnly)) { + content = f.readAll(); + f.close(); + } + } + if (!content.isEmpty()) { + auto format = QByteArray(); + auto image = App::readImage(content, &format); + if (!image.isNull() + && (format == "jpeg" + || format == "jpg" + || format == "png")) { + _background = image; + _backgroundContent = content; + _isPng = (format == "png"); + const auto phrase = _isPng + ? tr::lng_theme_editor_read_from_png + : tr::lng_theme_editor_read_from_jpg; + _imageText = phrase( + tr::now, + lt_size, + formatSizeText(_backgroundContent.size())); + _tileBackground->setChecked(false); + updateThumbnail(); + } + } + }; + FileDialog::GetOpenPath( + this, + tr::lng_theme_editor_choose_image(tr::now), + "Image files (*.jpeg *.jpg *.png)", + crl::guard(this, callback)); +} + +PreparedBackground BackgroundSelector::result() const { + return { + _backgroundContent, + _tileBackground->checked(), + _isPng, + }; +} + +void ImportFromFile( + not_null session, + not_null parent) { + const auto &imgExtensions = cImgExtensions(); + auto filters = QStringList( + qsl("Theme files (*.tdesktop-theme *.tdesktop-palette)")); + filters.push_back(FileDialog::AllFilesFilter()); + const auto callback = crl::guard(session, [=]( + const FileDialog::OpenResult &result) { + if (result.paths.isEmpty()) { + return; + } + Window::Theme::Apply(result.paths.front()); + }); + FileDialog::GetOpenPath( + parent.get(), + tr::lng_choose_image(tr::now), + filters.join(qsl(";;")), + crl::guard(parent, callback)); +} + +QString BytesToUTF8(QLatin1String string) { + return QString::fromUtf8(string.data(), string.size()); +} + +void WriteDefaultPalette(const QString &path) { + QFile f(path); + if (!f.open(QIODevice::WriteOnly)) { + LOG(("Theme Error: could not open '%1' for writing.").arg(path)); + return; + } + + QTextStream stream(&f); + stream.setCodec("UTF-8"); + + auto rows = style::main_palette::data(); + for (const auto &row : std::as_const(rows)) { + stream + << BytesToUTF8(row.name) + << ": " + << BytesToUTF8(row.value) + << "; // " + << BytesToUTF8( + row.description + ).replace( + '\n', + ' ' + ).replace( + '\r', + ' ') + << "\n"; + } +} + +void StartEditor( + not_null window, + const QString &title) { + const auto path = EditingPalettePath(); + if (!Local::copyThemeColorsToPalette(path)) { + WriteDefaultPalette(path); + } + if (!Apply(path)) { + window->show(Box(tr::lng_theme_editor_error(tr::now))); + return; + } + KeepApplied(); + window->showRightColumn(Box(window)); +} + +[[nodiscard]] QString GenerateSlug() { + const auto letters = uint8('Z' + 1 - 'A'); + const auto digits = uint8('9' + 1 - '0'); + const auto values = uint8(2 * letters + digits); + + auto result = QString(); + result.reserve(kRandomSlugSize); + for (auto i = 0; i != kRandomSlugSize; ++i) { + const auto value = rand_value() % values; + if (value < letters) { + result.append(char('A' + value)); + } else if (value < 2 * letters) { + result.append(char('a' + (value - letters))); + } else { + result.append(char('0' + (value - 2 * letters))); + } + } + return result; +} + +[[nodiscard]] QByteArray PrepareTheme( + const QByteArray &palette, + const PreparedBackground &background) { + zlib::FileToWrite zip; + + zip_fileinfo zfi = { { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 }; + const auto back = std::string(background.tile ? "tiled" : "background") + + (background.isPng ? ".png" : ".jpg"); + zip.openNewFile( + back.c_str(), + &zfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + zip.writeInFile( + background.content.constData(), + background.content.size()); + zip.closeFile(); + const auto scheme = "colors.tdesktop-theme"; + zip.openNewFile( + scheme, + &zfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + zip.writeInFile(palette.constData(), palette.size()); + zip.closeFile(); + zip.close(); + + if (zip.error() != ZIP_OK) { + LOG(("Theme Error: could not export zip-ed theme, status: %1" + ).arg(zip.error())); + return QByteArray(); + } + return zip.result(); +} + +[[nodiscard]] bool IsGoodSlug(const QString &slug) { + if (slug.size() < kMinSlugSize || slug.size() > kMaxSlugSize) { + return false; + } + const auto i = ranges::find_if(slug, [](QChar ch) { + return (ch < 'A' || ch > 'Z') + && (ch < 'a' || ch > 'z') + && (ch < '0' || ch > '9') + && (ch != '_'); + }); + return (i == slug.end()); +} + +SendMediaReady PrepareThemeMedia( + const QString &name, + const QByteArray &content) { + PreparedPhotoThumbs thumbnails; + QVector sizes; + + //const auto push = [&](const char *type, QImage &&image) { + // sizes.push_back(MTP_photoSize( + // MTP_string(type), + // MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), + // MTP_int(image.width()), + // MTP_int(image.height()), MTP_int(0))); + // thumbnails.emplace(type[0], std::move(image)); + //}; + //push("s", scaled(320)); + + const auto filename = QString(name).replace(' ', '_') + + qsl(".tdesktop-theme"); // #TODO themes + auto attributes = QVector( + 1, + MTP_documentAttributeFilename(MTP_string(filename))); + const auto id = rand_value(); + const auto document = MTP_document( + MTP_flags(0), + MTP_long(id), + MTP_long(0), + MTP_bytes(), + MTP_int(base::unixtime::now()), + MTP_string("application/x-tgtheme-tdesktop"), + MTP_int(content.size()), + MTP_vector(sizes), + MTP_int(MTP::maindc()), + MTP_vector(attributes)); + + return SendMediaReady( + SendMediaType::ThemeFile, + QString(), // filepath + filename, + content.size(), + content, + id, + 0, + QString(), + PeerId(), + MTP_photoEmpty(MTP_long(0)), + thumbnails, + document, + QByteArray(), + 0); +} + +Fn SaveTheme( + not_null window, + const QByteArray &palette, + const PreparedBackground &background, + const QString &name, + const QString &link, + Fn done, + Fn fail) { + using Storage::UploadedDocument; + struct State { + FullMsgId id; + bool generating = false; + mtpRequestId requestId = 0; + rpl::lifetime lifetime; + }; + + if (name.isEmpty()) { + fail(SaveErrorType::Name, {}); + return nullptr; + } else if (!IsGoodSlug(link)) { + fail(SaveErrorType::Link, {}); + return nullptr; + } + const auto session = &window->account().session(); + const auto api = &session->api(); + const auto state = std::make_shared(); + state->id = FullMsgId( + 0, + session->data().nextLocalMessageId()); + + const auto createTheme = [=](const MTPDocument &data) { + const auto document = session->data().processDocument(data); + state->requestId = api->request(MTPaccount_CreateTheme( + MTP_string(link), + MTP_string(name), + document->mtpInput() + )).done([=](const MTPTheme &result) { + done(); + }).fail([=](const RPCError &error) { + fail(SaveErrorType::Other, error.type()); + }).send(); + }; + + const auto uploadTheme = [=](const UploadedDocument &data) { + state->requestId = api->request(MTPaccount_UploadTheme( + MTP_flags(0), + data.file, + MTPInputFile(), // thumb + MTP_string(name + ".tdesktop-theme"), // #TODO themes + MTP_string("application/x-tgtheme-tdesktop") + )).done([=](const MTPDocument &result) { + createTheme(result); + }).fail([=](const RPCError &error) { + fail(SaveErrorType::Other, error.type()); + }).send(); + }; + + const auto uploadFile = [=](const QByteArray &theme) { + session->uploader().documentReady( + ) | rpl::filter([=](const UploadedDocument &data) { + return data.fullId == state->id; + }) | rpl::start_with_next([=](const UploadedDocument &data) { + uploadTheme(data); + }, state->lifetime); + + session->uploader().uploadMedia( + state->id, + PrepareThemeMedia(name, theme)); + }; + + state->generating = true; + crl::async([=] { + crl::on_main([=, ready = PrepareTheme(palette, background)]{ + if (!state->generating) { + return; + } + state->generating = false; + uploadFile(ready); + }); + }); + + return [=] { + if (state->generating) { + state->generating = false; + } else { + api->request(base::take(state->requestId)).cancel(); + session->uploader().cancel(state->id); + state->lifetime.destroy(); + } + }; +} + +} // namespace + +void CreateBox( + not_null box, + not_null window) { + Expects(window->account().sessionExists()); + + box->setTitle(tr::lng_theme_editor_create_title(Ui::Text::WithEntities)); + + const auto name = box->addRow(object_ptr( + box, + st::defaultInputField, + tr::lng_theme_editor_name())); + + box->addRow( + object_ptr( + box, + tr::lng_theme_editor_create_description(), + st::boxDividerLabel), + style::margins( + st::boxRowPadding.left(), + st::boxRowPadding.left(), + st::boxRowPadding.right(), + st::boxRowPadding.right())); + + box->addRow( + object_ptr( + box, + tr::lng_theme_editor_import_existing() | Ui::Text::ToUpper(), + st::createThemeImportButton), + style::margins() + )->addClickHandler([=] { + ImportFromFile(&window->account().session(), box); + }); + + box->setFocusCallback([=] { name->setFocusFast(); }); + + box->addButton(tr::lng_box_done(), [=] { + const auto title = name->getLastText().trimmed(); + if (title.isEmpty()) { + name->showError(); + return; + } + box->closeBox(); + StartEditor(window, title); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} + +void SaveThemeBox( + not_null box, + not_null window, + const QByteArray &palette) { + Expects(window->account().sessionExists()); + + const auto background = Background()->createCurrentImage(); + auto backgroundContent = QByteArray(); + const auto tiled = Background()->tile(); + { + QBuffer buffer(&backgroundContent); + background.save(&buffer, "JPG", 87); + } + + box->setTitle(tr::lng_theme_editor_save_title(Ui::Text::WithEntities)); + + const auto name = box->addRow(object_ptr( + box, + st::defaultInputField, + tr::lng_theme_editor_name())); + const auto linkWrap = box->addRow( + object_ptr(box), + style::margins( + st::boxRowPadding.left(), + st::themesSmallSkip, + st::boxRowPadding.right(), + st::boxRowPadding.bottom())); + const auto link = Ui::CreateChild( + linkWrap, + st::createThemeLink, + rpl::single(qsl("link")), + GenerateSlug(), + true); + linkWrap->widthValue( + ) | rpl::start_with_next([=](int width) { + link->resize(width, link->height()); + link->moveToLeft(0, 0, width); + }, link->lifetime()); + link->heightValue( + ) | rpl::start_with_next([=](int height) { + linkWrap->resize(linkWrap->width(), height); + }, link->lifetime()); + link->setLinkPlaceholder( + Core::App().createInternalLink(qsl("addtheme/"))); + link->setPlaceholderHidden(false); + link->setMaxLength(kMaxSlugSize); + + box->addRow( + object_ptr( + box, + tr::lng_theme_editor_link_about(), + st::boxDividerLabel), + style::margins( + st::boxRowPadding.left(), + st::themesSmallSkip, + st::boxRowPadding.right(), + st::boxRowPadding.bottom())); + + box->addRow( + object_ptr( + box, + tr::lng_theme_editor_background_image(), + st::settingsSubsectionTitle), + st::settingsSubsectionTitlePadding); + const auto back = box->addRow( + object_ptr( + box, + background, + PreparedBackground{ backgroundContent, tiled }), + style::margins( + st::boxRowPadding.left(), + st::themesSmallSkip, + st::boxRowPadding.right(), + st::boxRowPadding.bottom())); + + box->setFocusCallback([=] { name->setFocusFast(); }); + + box->setWidth(st::boxWideWidth); + + const auto saving = box->lifetime().make_state(); + const auto cancel = std::make_shared>(nullptr); + box->lifetime().add([=] { if (*cancel) (*cancel)(); }); + box->addButton(tr::lng_settings_save(), [=] { + if (*saving) { + return; + } + *saving = true; + const auto done = crl::guard(box, [=] { + box->closeBox(); + window->showRightColumn(nullptr); + }); + const auto fail = crl::guard(box, [=]( + SaveErrorType type, + const QString &text) { + if (!text.isEmpty()) { + Ui::Toast::Show(text); + } + if (type == SaveErrorType::Name) { + name->showError(); + } else if (type == SaveErrorType::Link) { + link->showError(); + } + }); + *cancel = SaveTheme( + window, + palette, + back->result(), + name->getLastText().trimmed(), + link->getLastText().trimmed(), + done, + fail); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} + +} // namespace Theme +} // namespace Window diff --git a/Telegram/SourceFiles/window/themes/window_theme_create_box.h b/Telegram/SourceFiles/window/themes/window_theme_editor_box.h similarity index 57% rename from Telegram/SourceFiles/window/themes/window_theme_create_box.h rename to Telegram/SourceFiles/window/themes/window_theme_editor_box.h index 75e4b0072..75a106e22 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_create_box.h +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.h @@ -7,16 +7,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -class GenericBox; - -namespace Main { -class Session; -} // namespace Main +#include "boxes/generic_box.h" namespace Window { + +class Controller; + namespace Theme { -void CreateBox(not_null box, not_null session); +void CreateBox( + not_null box, + not_null window); +void SaveThemeBox( + not_null box, + not_null window, + const QByteArray &palette); } // namespace Theme } // namespace Window diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index ba2a14c67..c64e99d8c 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -309,6 +309,21 @@ createThemeImportButton: InfoProfileButton { ripple: defaultRippleAnimation; } +createThemeLink: InputField(defaultInputField) { + textMargins: margins(0px, 7px, 0px, 0px); + textBg: boxBg; + + placeholderFg: placeholderFg; + placeholderFgActive: placeholderFgActive; + placeholderFgError: placeholderFgActive; + placeholderMargins: margins(0px, 0px, 0px, 0px); + placeholderScale: 0.; + placeholderFont: boxTextFont; + + heightMin: 34px; + + font: boxTextFont; +} // Mac specific diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index 92b4da43c..5dbd206be 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -43,7 +43,7 @@ void Controller::firstShow() { void Controller::checkThemeEditor() { if (Window::Theme::Background()->isEditingTheme()) { - _widget.showRightColumn(Box()); + showRightColumn(Box(this)); } } @@ -74,6 +74,10 @@ void Controller::showBox( _widget.ui_showBox(std::move(content), options, animated); } +void Controller::showRightColumn(object_ptr widget) { + _widget.showRightColumn(std::move(widget)); +} + void Controller::activate() { _widget.activate(); } diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index cf448fa7a..eae0f4670 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -52,6 +52,8 @@ public: return result; } + void showRightColumn(object_ptr widget); + void activate(); void reActivate(); void updateIsActive(int timeout); diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 9a98f7e2c..aff0f8619 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -889,12 +889,12 @@ <(src_loc)/window/window_top_bar_wrap.h <(src_loc)/window/themes/window_theme.cpp <(src_loc)/window/themes/window_theme.h -<(src_loc)/window/themes/window_theme_create_box.cpp -<(src_loc)/window/themes/window_theme_create_box.h <(src_loc)/window/themes/window_theme_editor.cpp <(src_loc)/window/themes/window_theme_editor.h <(src_loc)/window/themes/window_theme_editor_block.cpp <(src_loc)/window/themes/window_theme_editor_block.h +<(src_loc)/window/themes/window_theme_editor_box.cpp +<(src_loc)/window/themes/window_theme_editor_box.h <(src_loc)/window/themes/window_theme_preview.cpp <(src_loc)/window/themes/window_theme_preview.h <(src_loc)/window/themes/window_theme_warning.cpp