Add night mode switch to the main menu.

Also fix a bug with the default background applying.
This commit is contained in:
John Preston 2017-06-29 22:09:10 +03:00
parent 19023b4cc2
commit 61659244b7
18 changed files with 166 additions and 28 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B

View File

@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_menu_update" = "Update";
"lng_menu_restart" = "Restart";
"lng_menu_back" = "Back";
"lng_menu_night_mode" = "Night mode";
"lng_disable_notifications_from_tray" = "Disable notifications";
"lng_enable_notifications_from_tray" = "Enable notifications";
@ -368,6 +369,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_local_storage_clear_failed" = "Clear failed :(";
"lng_settings_section_advanced_settings" = "Advanced Settings";
"lng_settings_enable_night_theme" = "Enable night mode";
"lng_settings_disable_night_theme" = "Disable night mode";
"lng_passcode_remove_button" = "Remove";

Binary file not shown.

View File

@ -8,6 +8,7 @@
<file alias="art/logo_256.png">../art/logo_256.png</file>
<file alias="art/logo_256_no_margin.png">../art/logo_256_no_margin.png</file>
<file alias="art/sunrise.jpg">../art/sunrise.jpg</file>
<file alias="night.tdesktop-theme">../night.tdesktop-theme</file>
</qresource>
<qresource prefix="/sounds">
<file alias="msg_incoming.mp3">../sounds/msg_incoming.mp3</file>

View File

@ -75,9 +75,13 @@ void AdvancedWidget::createControls() {
} else {
style::margins slidedPadding(0, marginLarge.bottom() / 2, 0, marginLarge.bottom() - (marginLarge.bottom() / 2));
addChildRow(_useDefaultTheme, marginLarge, slidedPadding, lang(lng_settings_bg_use_default), SLOT(onUseDefaultTheme()));
if (!Local::hasTheme()) {
if (!Window::Theme::IsNonDefaultUsed()) {
_useDefaultTheme->hideFast();
}
addChildRow(_toggleNightTheme, marginLarge, slidedPadding, getNightThemeToggleText(), SLOT(onToggleNightTheme()));
if (Window::Theme::IsNonDefaultUsed()) {
_toggleNightTheme->hideFast();
}
}
addChildRow(_telegramFAQ, marginLarge, lang(lng_settings_faq), SLOT(onTelegramFAQ()));
if (self()) {
@ -88,7 +92,9 @@ void AdvancedWidget::createControls() {
void AdvancedWidget::checkNonDefaultTheme() {
if (self()) return;
_useDefaultTheme->toggleAnimated(Local::hasTheme());
_useDefaultTheme->toggleAnimated(Window::Theme::IsNonDefaultUsed());
_toggleNightTheme->entity()->setText(getNightThemeToggleText());
_toggleNightTheme->toggleAnimated(!Window::Theme::IsNonDefaultUsed());
}
void AdvancedWidget::onManageLocalStorage() {
@ -124,6 +130,10 @@ void AdvancedWidget::onUseDefaultTheme() {
Window::Theme::ApplyDefault();
}
void AdvancedWidget::onToggleNightTheme() {
Window::Theme::SwitchNightTheme(!Window::Theme::IsNightTheme());
}
void AdvancedWidget::onAskQuestion() {
auto box = Box<ConfirmBox>(lang(lng_settings_ask_sure), lang(lng_settings_ask_ok), lang(lng_settings_faq_button), base::lambda_guarded(this, [this] {
onAskQuestionSure();
@ -149,6 +159,10 @@ void AdvancedWidget::supportGot(const MTPhelp_Support &support) {
}
}
QString AdvancedWidget::getNightThemeToggleText() const {
return lang(Window::Theme::IsNightTheme() ? lng_settings_disable_night_theme : lng_settings_enable_night_theme);
}
void AdvancedWidget::onTelegramFAQ() {
QDesktopServices::openUrl(telegramFaqLink());
}

View File

@ -39,6 +39,7 @@ private slots:
void onAskQuestion();
void onAskQuestionSure();
void onUseDefaultTheme();
void onToggleNightTheme();
void onTelegramFAQ();
void onLogOut();
@ -49,12 +50,14 @@ private:
void connectionTypeUpdated();
#endif // !TDESKTOP_DISABLE_NETWORK_PROXY
void supportGot(const MTPhelp_Support &support);
QString getNightThemeToggleText() const;
object_ptr<Ui::LinkButton> _manageLocalStorage = { nullptr };
#ifndef TDESKTOP_DISABLE_NETWORK_PROXY
object_ptr<LabeledLink> _connectionType = { nullptr };
#endif // !TDESKTOP_DISABLE_NETWORK_PROXY
object_ptr<Ui::WidgetSlideWrap<Ui::LinkButton>> _useDefaultTheme = { nullptr };
object_ptr<Ui::WidgetSlideWrap<Ui::LinkButton>> _toggleNightTheme = { nullptr };
object_ptr<Ui::LinkButton> _askQuestion = { nullptr };
object_ptr<Ui::LinkButton> _telegramFAQ = { nullptr };
object_ptr<Ui::LinkButton> _logOut = { nullptr };

View File

@ -54,7 +54,7 @@ BackgroundRow::BackgroundRow(QWidget *parent) : TWidget(parent)
}
void BackgroundRow::checkNonDefaultTheme() {
if (Local::hasTheme()) {
if (Window::Theme::IsNonDefaultUsed()) {
if (!_useDefaultTheme) {
_useDefaultTheme.create(this, lang(lng_settings_bg_use_default), st::boxLinkButton);
_useDefaultTheme->show();

View File

@ -619,6 +619,7 @@ bool _backgroundWasRead = false;
bool _backgroundCanWrite = true;
FileKey _themeKey = 0;
QString _themeAbsolutePath;
QString _themePaletteAbsolutePath;
bool _readingUserSettings = false;
@ -3811,6 +3812,7 @@ bool readThemeUsingKey(FileKey key) {
return false;
}
_themeAbsolutePath = pathAbsolute;
_themePaletteAbsolutePath = Window::Theme::IsPaletteTestingPath(pathAbsolute) ? pathAbsolute : QString();
QFile file(pathRelative);
@ -3844,7 +3846,7 @@ bool readThemeUsingKey(FileKey key) {
void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, const Window::Theme::Cached &cache) {
if (content.isEmpty()) {
_themePaletteAbsolutePath = QString();
_themeAbsolutePath = _themePaletteAbsolutePath = QString();
if (_themeKey) {
clearKey(_themeKey);
_themeKey = 0;
@ -3853,6 +3855,7 @@ void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const
return;
}
_themeAbsolutePath = pathAbsolute;
_themePaletteAbsolutePath = Window::Theme::IsPaletteTestingPath(pathAbsolute) ? pathAbsolute : QString();
if (!_themeKey) {
_themeKey = genKey(FileOption::Safe);
@ -3916,6 +3919,10 @@ QString themePaletteAbsolutePath() {
return _themePaletteAbsolutePath;
}
QString themeAbsolutePath() {
return _themeAbsolutePath;
}
bool copyThemeColorsToPalette(const QString &path) {
if (!_themeKey) {
return false;

View File

@ -160,6 +160,7 @@ bool readBackground();
void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, const Window::Theme::Cached &cache);
void clearTheme();
bool hasTheme();
QString themeAbsolutePath();
QString themePaletteAbsolutePath();
bool copyThemeColorsToPalette(const QString &file);

View File

@ -67,7 +67,6 @@ QAction *Menu::addAction(const QString &text, base::lambda<void()> callback, con
QAction *Menu::addAction(QAction *action, const style::icon *icon, const style::icon *iconOver) {
connect(action, SIGNAL(changed()), this, SLOT(actionChanged()));
connect(action, SIGNAL(toggled(bool)), this, SLOT(actionToggled(bool)));
_actions.push_back(action);
auto createData = [icon, iconOver, action] {
@ -86,6 +85,7 @@ QAction *Menu::addAction(QAction *action, const style::icon *icon, const style::
if (_resizedCallback) {
_resizedCallback();
}
updateSelected(QCursor::pos());
update();
return action;
@ -98,6 +98,8 @@ QAction *Menu::addSeparator() {
}
void Menu::clearActions() {
setSelected(-1);
setPressed(-1);
_actionsData.clear();
for (auto action : base::take(_actions)) {
if (action->parent() == this) {
@ -110,6 +112,17 @@ void Menu::clearActions() {
}
}
void Menu::finishAnimations() {
for (auto &data : _actionsData) {
if (data.ripple) {
data.ripple.reset();
}
if (data.toggle) {
data.toggle->finishAnimation();
}
}
}
int Menu::processAction(QAction *action, int index, int width) {
auto &data = _actionsData[index];
if (action->isSeparator() || action->text().isEmpty()) {
@ -243,7 +256,7 @@ void Menu::itemPressed(TriggeredSource source) {
return;
}
if (_selected >= 0 && _selected < _actions.size() && _actions[_selected]->isEnabled()) {
_pressed = _selected;
setPressed(_selected);
if (source == TriggeredSource::Mouse) {
if (!_actionsData[_pressed].ripple) {
auto mask = RippleAnimation::rectMask(QSize(width(), _itemHeight));
@ -259,11 +272,9 @@ void Menu::itemPressed(TriggeredSource source) {
}
void Menu::itemReleased(TriggeredSource source) {
auto pressed = std::exchange(_pressed, -1);
if (pressed >= 0 && pressed < _actions.size()) {
if (pressed != _selected && _actionsData[pressed].toggle) {
_actionsData[pressed].toggle->setStyle(_st.itemToggle);
}
if (_pressed >= 0 && _pressed < _actions.size()) {
auto pressed = _pressed;
setPressed(-1);
if (source == TriggeredSource::Mouse && _actionsData[pressed].ripple) {
_actionsData[pressed].ripple->lastStop();
}
@ -308,9 +319,9 @@ void Menu::handleKeyPress(int key) {
} else if (newSelected >= _actions.size()) {
newSelected -= _actions.size();
}
} while (newSelected != start && (!_actions.at(newSelected)->isEnabled() || _actions.at(newSelected)->isSeparator()));
} while (newSelected != start && (!_actions[newSelected]->isEnabled() || _actions[newSelected]->isSeparator()));
if (_actions.at(newSelected)->isEnabled() && !_actions.at(newSelected)->isSeparator()) {
if (_actions[newSelected]->isEnabled() && !_actions[newSelected]->isSeparator()) {
_mouseSelection = false;
setSelected(newSelected);
}
@ -361,6 +372,21 @@ void Menu::setSelected(int selected) {
}
}
void Menu::setPressed(int pressed) {
if (pressed >= _actions.size()) {
pressed = -1;
}
if (_pressed != pressed) {
if (_pressed >= 0 && _pressed != _selected && _actionsData[_pressed].toggle) {
_actionsData[_pressed].toggle->setStyle(_st.itemToggle);
}
_pressed = pressed;
if (_pressed >= 0 && _actionsData[_pressed].toggle && _actions[_pressed]->isEnabled()) {
_actionsData[_pressed].toggle->setStyle(_st.itemToggleOver);
}
}
}
int Menu::itemTop(int index) {
if (index > _actions.size()) {
index = _actions.size();

View File

@ -38,6 +38,7 @@ public:
QAction *addAction(const QString &text, base::lambda<void()> callback, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr);
QAction *addSeparator();
void clearActions();
void finishAnimations();
void clearSelection();
@ -106,6 +107,7 @@ private:
QAction *addAction(QAction *a, const style::icon *icon = nullptr, const style::icon *iconOver = nullptr);
void setSelected(int selected);
void setPressed(int pressed);
void clearMouseSelection();
int itemTop(int index);

View File

@ -32,11 +32,11 @@ namespace Window {
namespace Theme {
namespace {
constexpr int kThemeFileSizeLimit = 5 * 1024 * 1024;
constexpr int kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
constexpr int kThemeSchemeSizeLimit = 1024 * 1024;
constexpr int kMinimumTiledSize = 512;
constexpr auto kThemeFileSizeLimit = 5 * 1024 * 1024;
constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
constexpr auto kThemeSchemeSizeLimit = 1024 * 1024;
constexpr auto kMinimumTiledSize = 512;
constexpr auto kNightThemeFile = str_const(":/gui/night.tdesktop-theme");
struct Data {
struct Applying {
@ -393,8 +393,15 @@ void ChatBackground::start() {
}
void ChatBackground::setImage(int32 id, QImage &&image) {
auto resetPalette = (id == kDefaultBackground && _id != kDefaultBackground && !Local::hasTheme());
if (id == kThemeBackground && _themeImage.isNull()) {
id = kDefaultBackground;
} else if (resetPalette) {
// If we had a default color theme with non-default background,
// and we switch to default background we must somehow switch from
// adjusted service colors to default (non-adjusted) service colors.
// The only way to do that right now is through full palette reset.
style::main_palette::reset();
}
_id = id;
if (_id == kThemeBackground) {
@ -425,6 +432,10 @@ void ChatBackground::setImage(int32 id, QImage &&image) {
}
t_assert(!_pixmap.isNull() && !_pixmapForTiled.isNull());
notify(BackgroundUpdate(BackgroundUpdate::Type::New, _tile));
if (resetPalette) {
notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, _tile), true);
notify(BackgroundUpdate(BackgroundUpdate::Type::ApplyingTheme, _tile), true);
}
}
void ChatBackground::setPreparedImage(QImage &&image) {
@ -432,11 +443,11 @@ void ChatBackground::setPreparedImage(QImage &&image) {
image.setDevicePixelRatio(cRetinaFactor());
auto adjustColors = [this] {
auto someCustomThemeApplied = [] {
auto someThemeApplied = [] {
if (AreTestingTheme()) {
return !instance->applying.path.isEmpty();
}
return Local::hasTheme();
return IsNonDefaultUsed() || IsNightTheme();
};
auto usingThemeBackground = [this] {
return (_id == kThemeBackground || _id == internal::kTestingThemeBackground);
@ -451,7 +462,7 @@ void ChatBackground::setPreparedImage(QImage &&image) {
return !Local::themePaletteAbsolutePath().isEmpty();
};
if (someCustomThemeApplied()) {
if (someThemeApplied()) {
return !usingThemeBackground() && !testingPalette();
}
return !usingDefaultBackground();
@ -660,6 +671,27 @@ bool Apply(const QString &filepath) {
return Apply(std::move(preview));
}
void SwitchNightTheme(bool enabled) {
if (enabled) {
auto preview = std::make_unique<Preview>();
preview->path = str_const_toString(kNightThemeFile);
if (!LoadFromFile(preview->path, &preview->instance, &preview->content)) {
return;
}
instance.createIfNull();
instance->applying.path = std::move(preview->path);
instance->applying.content = std::move(preview->content);
instance->applying.cached = std::move(preview->instance.cached);
if (instance->applying.paletteForRevert.isEmpty()) {
instance->applying.paletteForRevert = style::main_palette::save();
}
Background()->setTestingTheme(std::move(preview->instance));
} else {
Window::Theme::ApplyDefault();
}
KeepApplied();
}
bool Apply(std::unique_ptr<Preview> preview) {
instance.createIfNull();
instance->applying.path = std::move(preview->path);
@ -724,6 +756,14 @@ void Revert() {
Background()->revert();
}
bool IsNightTheme() {
return (Local::themeAbsolutePath() == str_const_toString(kNightThemeFile));
}
bool IsNonDefaultUsed() {
return Local::hasTheme() && !IsNightTheme();
}
bool LoadFromFile(const QString &path, Instance *out, QByteArray *outContent) {
*outContent = readThemeContent(path);
if (outContent->size() < 4) {

View File

@ -65,6 +65,9 @@ bool Apply(std::unique_ptr<Preview> preview);
void ApplyDefault();
bool ApplyEditedPalette(const QString &path, const QByteArray &content);
void KeepApplied();
bool IsNonDefaultUsed();
bool IsNightTheme();
void SwitchNightTheme(bool enabled);
void Revert();
bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent);

View File

@ -524,7 +524,7 @@ void Generator::paintDialogs() {
auto filterLeft = _dialogs.x() + st::dialogsFilterPadding.x() + st::dialogsMenuToggle.width + st::dialogsFilterPadding.x();
auto filterRight = st::dialogsFilterSkip + st::dialogsFilterPadding.x();
auto filterWidth = _dialogs.width() - filterLeft - filterRight;
auto filterWidth = _dialogs.x() + _dialogs.width() - filterLeft - filterRight;
auto filterAreaHeight = st::dialogsFilterPadding.y() + st::dialogsMenuToggle.height + st::dialogsFilterPadding.y();
auto filterTop = _dialogs.y() + (filterAreaHeight - st::dialogsFilter.height) / 2;
auto filter = QRect(filterLeft, filterTop, filterWidth, st::dialogsFilter.height);

View File

@ -118,6 +118,13 @@ mainMenu: Menu(defaultMenu) {
itemFont: semiboldFont;
itemIconPosition: point(28px, 10px);
itemPadding: margins(76px, 13px, 28px, 13px);
itemToggle: Toggle(defaultMenuToggle) {
toggledFg: mainMenuCoverBg;
}
itemToggleOver: Toggle(defaultMenuToggleOver) {
toggledFg: mainMenuCoverBg;
}
itemToggleShift: 11px;
}
mainMenuNewGroup: icon {{ "menu_new_group", menuIconFg }};
mainMenuNewGroupOver: icon {{ "menu_new_group", menuIconFgOver }};
@ -129,8 +136,8 @@ mainMenuCalls: icon {{ "menu_calls", menuIconFg }};
mainMenuCallsOver: icon {{ "menu_calls", menuIconFgOver }};
mainMenuSettings: icon {{ "menu_settings", menuIconFg }};
mainMenuSettingsOver: icon {{ "menu_settings", menuIconFgOver }};
mainMenuHelp: icon {{ "menu_help", menuIconFg }};
mainMenuHelpOver: icon {{ "menu_help", menuIconFgOver }};
mainMenuNightMode: icon {{ "menu_night_mode", menuIconFg }};
mainMenuNightModeOver: icon {{ "menu_night_mode", menuIconFgOver }};
mainMenuFooterLeft: 30px;
mainMenuTelegramLabel: FlatLabel(defaultFlatLabel) {
align: align(left);

View File

@ -23,10 +23,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_window.h"
#include "styles/style_dialogs.h"
#include "profile/profile_userpic_button.h"
#include "window/themes/window_theme.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/menu.h"
#include "mainwindow.h"
#include "storage/localstorage.h"
#include "boxes/contacts_box.h"
#include "boxes/about_box.h"
#include "boxes/peer_list_box.h"
@ -50,6 +52,14 @@ MainMenu::MainMenu(QWidget *parent) : TWidget(parent)
});
checkSelf();
_nightThemeSwitch.setCallback([this] {
if (auto action = *_nightThemeAction) {
if (action->isChecked() != Window::Theme::IsNightTheme()) {
Window::Theme::SwitchNightTheme(action->isChecked());
}
}
});
resize(st::mainMenuWidth, parentWidget()->height());
_menu->setTriggeredCallback([](QAction *action, int actionTop, Ui::Menu::TriggeredSource source) {
emit action->triggered();
@ -70,6 +80,11 @@ MainMenu::MainMenu(QWidget *parent) : TWidget(parent)
}
}));
subscribe(Global::RefPhoneCallsEnabledChanged(), [this] { refreshMenu(); });
subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) {
if (update.type == Window::Theme::BackgroundUpdate::Type::ApplyingTheme) {
refreshMenu();
}
});
updatePhone();
}
@ -94,9 +109,20 @@ void MainMenu::refreshMenu() {
_menu->addAction(lang(lng_menu_settings), [] {
App::wnd()->showSettings();
}, &st::mainMenuSettings, &st::mainMenuSettingsOver);
_menu->addAction(lang(lng_settings_faq), [] {
QDesktopServices::openUrl(telegramFaqLink());
}, &st::mainMenuHelp, &st::mainMenuHelpOver);
if (!Window::Theme::IsNonDefaultUsed()) {
_nightThemeAction = std::make_shared<QPointer<QAction>>(nullptr);
auto action = _menu->addAction(lang(lng_menu_night_mode), [this] {
if (auto action = *_nightThemeAction) {
action->setChecked(!action->isChecked());
_nightThemeSwitch.callOnce(st::mainMenu.itemToggle.duration);
}
}, &st::mainMenuNightMode, &st::mainMenuNightModeOver);
*_nightThemeAction = action;
action->setCheckable(true);
action->setChecked(Window::Theme::IsNightTheme());
_menu->finishAnimations();
}
updatePhone();
}
@ -133,6 +159,7 @@ void MainMenu::showFinished() {
}
void MainMenu::resizeEvent(QResizeEvent *e) {
_menu->setForceWidth(width());
updateControlsGeometry();
}
@ -143,7 +170,7 @@ void MainMenu::updateControlsGeometry() {
if (_cloudButton) {
_cloudButton->moveToRight(0, st::mainMenuCoverHeight - _cloudButton->height());
}
_menu->setGeometry(0, st::mainMenuCoverHeight + st::mainMenuSkip, width(), _menu->height());
_menu->moveToLeft(0, st::mainMenuCoverHeight + st::mainMenuSkip);
_telegram->moveToLeft(st::mainMenuFooterLeft, height() - st::mainMenuTelegramBottom - _telegram->height());
_version->moveToLeft(st::mainMenuFooterLeft, height() - st::mainMenuVersionBottom - _version->height());
}

View File

@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "base/timer.h"
namespace Ui {
class IconButton;
class FlatLabel;
@ -56,6 +58,8 @@ private:
object_ptr<Ui::Menu> _menu;
object_ptr<Ui::FlatLabel> _telegram;
object_ptr<Ui::FlatLabel> _version;
std::shared_ptr<QPointer<QAction>> _nightThemeAction;
base::Timer _nightThemeSwitch;
bool _showFinished = false;
QString _phoneText;