Add a box for new theme creating.

This commit is contained in:
John Preston 2019-09-02 19:10:18 +03:00
parent 1e3b72ab74
commit 4b045a602c
16 changed files with 322 additions and 72 deletions

View File

@ -338,6 +338,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_bg_from_gallery" = "Choose from gallery";
"lng_settings_bg_from_file" = "Choose from file";
"lng_settings_bg_edit_theme" = "Launch theme editor";
"lng_settings_bg_create_theme" = "Create new theme";
"lng_settings_bg_tile" = "Tile background";
"lng_settings_adaptive_wide" = "Adaptive layout for wide screens";
@ -1603,6 +1604,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_theme_editor_title" = "Edit color palette";
"lng_theme_editor_export_button" = "Export theme";
"lng_theme_editor_create_title" = "Create theme";
"lng_theme_editor_name" = "Theme name";
"lng_theme_editor_create_description" = "New theme will be based on your currently selected colors and wallpaper. Alternatively, you can import existing theme or color palette from file.";
"lng_theme_editor_import_existing" = "Import existing theme";
"lng_theme_editor_save_title" = "Save theme";
"lng_theme_editor_link_about" = "Your theme will be updated for all users each time you change it. Anyone can install it using this link.\n\nTheme links must be longer than 5 characters and use a-z, 0-9 and underscores.";
"lng_payments_not_supported" = "Sorry, Telegram Desktop doesn't support payments yet. Please use one of our mobile apps to do this.";
"lng_payments_receipt_label" = "Receipt";
"lng_payments_receipt_label_test" = "Test receipt";

View File

@ -0,0 +1,52 @@
/*
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
*/
#pragma once
#include "base/optional.h"
#include "base/variant.h"
#include <rpl/map.h>
#include <rpl/producer.h>
namespace rpl {
template <
typename Value,
typename Error,
typename GeneratorTest,
typename GeneratorA,
typename GeneratorB>
inline auto conditional(
rpl::producer<bool, Error, GeneratorTest> &&test,
rpl::producer<Value, Error, GeneratorA> &&a,
rpl::producer<Value, Error, GeneratorB> &&b) {
return rpl::combine(
std::move(test),
std::move(a),
std::move(b)
) | rpl::map([](bool test, Value &&a, Value &&b) {
return test ? std::move(a) : std::move(b);
});
//struct conditional_state {
// std::optional<Value> a;
// std::optional<Value> b;
// char state = -1;
// int working = 3;
//};
//return rpl::make_producer<Value, Error>([
// test = std::move(test),
// a = std::move(a),
// b = std::move(b)
//](const auto &consumer) mutable {
// auto result = lifetime();
// const auto state = result.make_state<conditional_state>();
// result.add(std::move(test).start())
// return result;
//});
}
} // namespace rpl

View File

@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/flatten_latest.h>
#include <rpl/combine.h>
#include <rpl/combine_previous.h>
#include <rpl/conditional.h>
#include <rpl/variable.h>
#include <rpl/before_next.h>

View File

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/auto_download_box.h"
#include "boxes/stickers_box.h"
#include "boxes/background_box.h"
#include "boxes/generic_box.h"
#include "boxes/background_preview_box.h"
#include "boxes/download_path_box.h"
#include "boxes/local_storage_box.h"
@ -29,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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/window_session_controller.h"
#include "info/profile/info_profile_button.h"
#include "storage/localstorage.h"
@ -727,7 +729,6 @@ void ChooseFromFile(
tr::lng_choose_image(tr::now),
filters.join(qsl(";;")),
crl::guard(parent, callback));
}
QString DownloadPathText() {
@ -1242,7 +1243,9 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
AddSkip(container);
}
void SetupThemeOptions(not_null<Ui::VerticalLayout*> container) {
void SetupThemeOptions(
not_null<Window::SessionController*> controller,
not_null<Ui::VerticalLayout*> container) {
AddSkip(container, st::settingsPrivacySkip);
AddSubsectionTitle(container, tr::lng_settings_themes());
@ -1250,17 +1253,19 @@ void SetupThemeOptions(not_null<Ui::VerticalLayout*> container) {
AddSkip(container, st::settingsThemesTopSkip);
SetupDefaultThemes(container);
AddSkip(container, st::settingsThemesBottomSkip);
auto canEdit = rpl::single(false);
AddButton(
container,
tr::lng_settings_bg_edit_theme(),
rpl::conditional(
std::move(canEdit),
tr::lng_settings_bg_edit_theme(),
tr::lng_settings_bg_create_theme()),
st::settingsChatButton,
&st::settingsIconThemes,
st::settingsChatIconLeft
)->addClickHandler(App::LambdaDelayed(
st::settingsChatButton.ripple.hideDuration,
container,
[] { Window::Theme::Editor::Start(); }));
)->addClickHandler([=] {
Ui::show(Box(Window::Theme::CreateBox, &controller->session()));
});
AddSkip(container);
}
@ -1385,7 +1390,7 @@ Chat::Chat(QWidget *parent, not_null<Window::SessionController*> controller)
void Chat::setupContent(not_null<Window::SessionController*> controller) {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
SetupThemeOptions(content);
SetupThemeOptions(controller, content);
SetupChatBackground(controller, content);
SetupStickersEmoji(controller, content);
SetupMessages(controller, content);

View File

@ -94,9 +94,6 @@ auto GenerateCodes() {
}
});
});
codes.emplace(qsl("edittheme"), [](::Main::Session *session) {
Window::Theme::Editor::Start();
});
codes.emplace(qsl("videoplayer"), [](::Main::Session *session) {
auto text = cUseExternalVideoPlayer() ? qsl("Use internal video player?") : qsl("Use external video player?");
Ui::show(Box<ConfirmBox>(text, [] {

View File

@ -51,6 +51,14 @@ inline bool AreTestingTheme() {
return !GlobalApplying.paletteForRevert.isEmpty();
};
[[nodiscard]] bool IsEditingTheme(const QString &path) {
static const auto kEditingPath = QFileInfo(
EditingPalettePath()
).absoluteFilePath();
return !path.compare(kEditingPath, Qt::CaseInsensitive)
&& QFileInfo(path).exists();
}
bool CalculateIsMonoColorImage(const QImage &image) {
if (!image.isNull()) {
const auto bits = reinterpret_cast<const uint32*>(image.constBits());
@ -441,7 +449,7 @@ void ChatBackground::checkUploadWallPaper() {
}
if (!Data::IsCustomWallPaper(_paper)
|| _original.isNull()
|| testingPalette()) {
|| isEditingTheme()) {
return;
}
@ -631,7 +639,7 @@ bool ChatBackground::adjustPaletteRequired() {
|| Data::details::IsTestingDefaultWallPaper(_paper);
};
if (testingPalette()) {
if (isEditingTheme()) {
return false;
} else if (isNonDefaultThemeOrBackground() || nightMode()) {
return !usingThemeBackground();
@ -639,11 +647,11 @@ bool ChatBackground::adjustPaletteRequired() {
return !usingDefaultBackground();
}
bool ChatBackground::testingPalette() const {
bool ChatBackground::isEditingTheme() const {
const auto path = AreTestingTheme()
? GlobalApplying.pathAbsolute
: _themeAbsolutePath;
return IsPaletteTestingPath(path);
return IsEditingTheme(path);
}
void ChatBackground::adjustPaletteUsingBackground(const QImage &image) {
@ -801,8 +809,7 @@ void ChatBackground::setTestingTheme(Instance &&theme) {
|| (Data::IsDefaultWallPaper(_paper)
&& !nightMode()
&& _themeAbsolutePath.isEmpty());
if (AreTestingTheme()
&& IsPaletteTestingPath(GlobalApplying.pathAbsolute)) {
if (AreTestingTheme() && isEditingTheme()) {
// Grab current background image if it is not already custom
// Use prepared pixmap, not original image, because we're
// for sure switching to a non-pattern wall-paper (testing editor).
@ -1137,11 +1144,8 @@ bool LoadFromFile(
return loadTheme(*outContent, out->cached, colorizer, out);
}
bool IsPaletteTestingPath(const QString &path) {
if (path.endsWith(qstr(".tdesktop-palette"), Qt::CaseInsensitive)) {
return QFileInfo(path).exists();
}
return false;
QString EditingPalettePath() {
return cWorkingDir() + "tdata/editing-theme.tdesktop-palette";
}
QColor CountAverageColor(const QImage &image) {

View File

@ -62,11 +62,12 @@ void ToggleNightMode(const QString &themePath);
[[nodiscard]] bool IsNonDefaultBackground();
void Revert();
[[nodiscard]] QString EditingPalettePath();
bool LoadFromFile(
const QString &file,
Instance *out,
QByteArray *outContent);
bool IsPaletteTestingPath(const QString &path);
QColor CountAverageColor(const QImage &image);
QColor AdjustedColor(QColor original, QColor background);
QImage ProcessBackgroundImage(QImage image);
@ -83,7 +84,7 @@ struct BackgroundUpdate {
BackgroundUpdate(Type type, bool tiled) : type(type), tiled(tiled) {
}
bool paletteChanged() const {
[[nodiscard]] bool paletteChanged() const {
return (type == Type::TestingTheme || type == Type::RevertingTheme);
}
Type type;
@ -107,6 +108,7 @@ public:
void setTileNightValue(bool tile);
void setThemeAbsolutePath(const QString &path);
[[nodiscard]] QString themeAbsolutePath() const;
[[nodiscard]] bool isEditingTheme() const;
void reset();
void setTestingTheme(Instance &&theme);
@ -160,7 +162,6 @@ private:
[[nodiscard]] bool isNonDefaultThemeOrBackground();
[[nodiscard]] bool isNonDefaultBackground();
void checkUploadWallPaper();
[[nodiscard]] bool testingPalette() const;
friend bool IsNightMode();
friend void SetNightModeValue(bool nightMode);

View File

@ -0,0 +1,148 @@
/*
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<Main::Session*> session,
not_null<QWidget*> 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<Main::Session*> session,
const QString &title) {
const auto path = EditingPalettePath();
if (!Local::copyThemeColorsToPalette(path)) {
WriteDefaultPalette(path);
}
if (!Apply(path)) {
Ui::show(Box<InformBox>(tr::lng_theme_editor_error(tr::now)));
return;
}
KeepApplied();
if (const auto window = App::wnd()) {
window->showRightColumn(Box<Editor>());
}
}
} // namespace
void CreateBox(not_null<GenericBox*> box, not_null<Main::Session*> session) {
box->setTitle(tr::lng_theme_editor_create_title(Ui::Text::WithEntities));
const auto name = box->addRow(object_ptr<Ui::InputField>(
box,
st::defaultInputField,
tr::lng_theme_editor_name()));
box->addRow(
object_ptr<Ui::FlatLabel>(
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<Info::Profile::Button>(
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

View File

@ -0,0 +1,22 @@
/*
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
*/
#pragma once
class GenericBox;
namespace Main {
class Session;
} // namespace Main
namespace Window {
namespace Theme {
void CreateBox(not_null<GenericBox*> box, not_null<Main::Session*> session);
} // namespace Theme
} // namespace Window

View File

@ -637,22 +637,6 @@ void Editor::Inner::applyEditing(const QString &name, const QString &copyOf, QCo
_paletteContent = newContent;
}
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, rows) {
stream << bytesToUtf8(row.name) << ": " << bytesToUtf8(row.value) << "; // " << bytesToUtf8(row.description).replace('\n', ' ').replace('\r', ' ') << "\n";
}
}
ThemeExportBox::ThemeExportBox(QWidget*, const QByteArray &paletteContent, const QImage &background, const QByteArray &backgroundContent, bool tileBackground) : BoxContent()
, _paletteContent(paletteContent)
, _background(background)
@ -785,13 +769,15 @@ void ThemeExportBox::exportTheme() {
});
}
Editor::Editor(QWidget*, const QString &path)
Editor::Editor(QWidget*)
: _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) {
const auto path = EditingPalettePath();
_inner = _scroll->setOwnedWidget(object_ptr<Inner>(this, path));
_export->setClickedCallback(_inner->exportCallback());
@ -880,32 +866,32 @@ void Editor::paintEvent(QPaintEvent *e) {
p.drawTextLeft(st::themeEditorMargin.left(), st::themeEditorMargin.top(), width(), tr::lng_theme_editor_title(tr::now));
}
void Editor::Start() {
const auto path = Background()->themeAbsolutePath();
if (path.isEmpty() || !Window::Theme::IsPaletteTestingPath(path)) {
const auto start = [](const QString &path) {
if (!Local::copyThemeColorsToPalette(path)) {
writeDefaultPalette(path);
}
if (!Apply(path)) {
Ui::show(Box<InformBox>(tr::lng_theme_editor_error(tr::now)));
return;
}
KeepApplied();
if (auto window = App::wnd()) {
window->showRightColumn(Box<Editor>(path));
}
};
FileDialog::GetWritePath(
App::wnd(),
tr::lng_theme_editor_save_palette(tr::now),
"Palette (*.tdesktop-palette)",
"colors.tdesktop-palette",
start);
} else if (auto window = App::wnd()) {
window->showRightColumn(Box<Editor>(path));
}
}
//void Editor::Start() {
// const auto path = Background()->themeAbsolutePath();
// if (!Window::Theme::IsPaletteTestingPath(path)) {
// const auto start = [](const QString &path) {
// if (!Local::copyThemeColorsToPalette(path)) {
// writeDefaultPalette(path);
// }
// if (!Apply(path)) {
// Ui::show(Box<InformBox>(tr::lng_theme_editor_error(tr::now)));
// return;
// }
// KeepApplied();
// if (auto window = App::wnd()) {
// window->showRightColumn(Box<Editor>(path));
// }
// };
// FileDialog::GetWritePath(
// App::wnd(),
// tr::lng_theme_editor_save_palette(tr::now),
// "Palette (*.tdesktop-palette)",
// "colors.tdesktop-palette",
// start);
// } else if (auto window = App::wnd()) {
// window->showRightColumn(Box<Editor>(path));
// }
//}
void Editor::closeEditor() {
if (auto window = App::wnd()) {

View File

@ -25,9 +25,7 @@ bool CopyColorsToPalette(
class Editor : public TWidget {
public:
Editor(QWidget*, const QString &path);
static void Start();
explicit Editor(QWidget*);
protected:
void paintEvent(QPaintEvent *e) override;

View File

@ -296,6 +296,20 @@ windowOutdatedClose: IconButton(defaultIconButton) {
iconPosition: point(-1px, -1px);
}
createThemeImportButton: InfoProfileButton {
textFg: lightButtonFg;
textFgOver: lightButtonFgOver;
textBg: windowBg;
textBgOver: windowBgOver;
font: semiboldFont;
height: 20px;
padding: margins(23px, 10px, 23px, 8px);
ripple: defaultRippleAnimation;
}
// Mac specific
macAccessoryWidth: 450.;

View File

@ -9,7 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "main/main_account.h"
#include "window/layer_widget.h"
#include "window/window_session_controller.h"
#include "window/themes/window_theme.h"
#include "window/themes/window_theme_editor.h"
#include "mainwindow.h"
namespace Window {
@ -35,6 +38,13 @@ Controller::~Controller() {
void Controller::firstShow() {
_widget.firstShow();
checkThemeEditor();
}
void Controller::checkThemeEditor() {
if (Window::Theme::Background()->isEditingTheme()) {
_widget.showRightColumn(Box<Window::Theme::Editor>());
}
}
void Controller::setupPasscodeLock() {

View File

@ -67,6 +67,7 @@ private:
object_ptr<BoxContent> content,
LayerOptions options,
anim::type animated);
void checkThemeEditor();
not_null<Main::Account*> _account;
::MainWindow _widget;

View File

@ -889,6 +889,8 @@
<(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

View File

@ -93,6 +93,7 @@
'<(src_loc)/rpl/combine.h',
'<(src_loc)/rpl/combine_previous.h',
'<(src_loc)/rpl/complete.h',
'<(src_loc)/rpl/conditional.h',
'<(src_loc)/rpl/consumer.h',
'<(src_loc)/rpl/deferred.h',
'<(src_loc)/rpl/distinct_until_changed.h',