mirror of https://github.com/procxx/kepka.git
Applying color themes with confirmation / reverting (15 seconds).
This commit is contained in:
parent
af9edc17d2
commit
5d10c02b5b
|
@ -292,6 +292,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
"lng_settings_adaptive_wide" = "Adaptive layout for wide screens";
|
||||
|
||||
"lng_backgrounds_header" = "Choose your new chat background";
|
||||
"lng_theme_sure_keep" = "Keep this color theme?";
|
||||
"lng_theme_reverting" = "Reverting to previous color theme in {count:_not_used_|# second|# seconds}.";
|
||||
"lng_theme_keep_changes" = "Keep changes";
|
||||
"lng_theme_revert" = "Revert";
|
||||
|
||||
"lng_download_path_dont_ask" = "Don't ask download path for each file";
|
||||
"lng_download_path_label" = "Download path:";
|
||||
|
|
|
@ -283,3 +283,9 @@ newGroupDescription: InputArea(defaultInputArea) {
|
|||
|
||||
newGroupPublicLinkPadding: margins(0px, 20px, 0px, 5px);
|
||||
newGroupLinkFadeDuration: 5000;
|
||||
|
||||
themeWarningWidth: boxWideWidth;
|
||||
themeWarningHeight: 150px;
|
||||
themeWarningShadow: boxShadow;
|
||||
themeWarningShadowShift: boxShadowShift;
|
||||
themeWarningTextTop: 60px;
|
||||
|
|
|
@ -828,7 +828,11 @@ QByteArray save() {\n\
|
|||
}\n\
|
||||
\n\
|
||||
bool load(const QByteArray &cache) {\n\
|
||||
return _palette.load(cache);\n\
|
||||
if (_palette.load(cache)) {\n\
|
||||
style::internal::resetIcons();\n\
|
||||
return true;\n\
|
||||
}\n\
|
||||
return false;\n\
|
||||
}\n\
|
||||
\n\
|
||||
bool setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a) {\n\
|
||||
|
|
|
@ -1626,7 +1626,7 @@ void _writeUserSettings() {
|
|||
|
||||
EncryptedDescriptor data(size);
|
||||
data.stream << quint32(dbiSendKey) << qint32(cCtrlEnter() ? dbiskCtrlEnter : dbiskEnter);
|
||||
data.stream << quint32(dbiTileBackground) << qint32(Window::Theme::Background()->tile() ? 1 : 0);
|
||||
data.stream << quint32(dbiTileBackground) << qint32(Window::Theme::Background()->tileForSave() ? 1 : 0);
|
||||
data.stream << quint32(dbiAdaptiveForWide) << qint32(Global::AdaptiveForWide() ? 1 : 0);
|
||||
data.stream << quint32(dbiAutoLock) << qint32(Global::AutoLock());
|
||||
data.stream << quint32(dbiReplaceEmojis) << qint32(cReplaceEmojis() ? 1 : 0);
|
||||
|
|
|
@ -43,8 +43,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "localstorage.h"
|
||||
#include "apiwrap.h"
|
||||
#include "settings/settings_widget.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "window/window_theme.h"
|
||||
#include "window/window_theme_warning.h"
|
||||
|
||||
ConnectingWidget::ConnectingWidget(QWidget *parent, const QString &text, const QString &reconnect) : QWidget(parent)
|
||||
, _shadow(st::boxShadow)
|
||||
|
@ -125,6 +127,9 @@ MainWindow::MainWindow() {
|
|||
connect(&_autoLockTimer, SIGNAL(timeout()), this, SLOT(checkAutoLock()));
|
||||
|
||||
subscribe(Global::RefSelfChanged(), [this]() { updateGlobalMenu(); });
|
||||
subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &data) {
|
||||
themeUpdated(data);
|
||||
});
|
||||
|
||||
setAttribute(Qt::WA_NoSystemBackground);
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
@ -594,6 +599,20 @@ void MainWindow::hideConnecting() {
|
|||
}
|
||||
}
|
||||
|
||||
void MainWindow::themeUpdated(const Window::Theme::BackgroundUpdate &data) {
|
||||
using Type = Window::Theme::BackgroundUpdate::Type;
|
||||
if (data.type == Type::TestingTheme) {
|
||||
if (!_testingThemeWarning) {
|
||||
_testingThemeWarning.create(this);
|
||||
_testingThemeWarning->setGeometry(rect());
|
||||
_testingThemeWarning->setHiddenCallback([this] { _testingThemeWarning.destroyDelayed(); });
|
||||
}
|
||||
_testingThemeWarning->showAnimated();
|
||||
} else if (data.type == Type::RevertingTheme || data.type == Type::ApplyingTheme) {
|
||||
_testingThemeWarning->hideAnimated();
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::doWeReadServerHistory() const {
|
||||
return isActive(false) && main && !Ui::isLayerShown() && main->doWeReadServerHistory();
|
||||
}
|
||||
|
@ -645,7 +664,9 @@ bool MainWindow::contentOverlapped(const QRect &globalRect) {
|
|||
}
|
||||
|
||||
void MainWindow::setInnerFocus() {
|
||||
if (layerBg && layerBg->canSetFocus()) {
|
||||
if (_testingThemeWarning) {
|
||||
_testingThemeWarning->setFocus();
|
||||
} else if (layerBg && layerBg->canSetFocus()) {
|
||||
layerBg->setInnerFocus();
|
||||
} else if (_passcode) {
|
||||
_passcode->setInnerFocus();
|
||||
|
@ -951,6 +972,7 @@ void MainWindow::fixOrder() {
|
|||
if (layerBg) layerBg->raise();
|
||||
if (_mediaPreview) _mediaPreview->raise();
|
||||
if (_connecting) _connecting->raise();
|
||||
if (_testingThemeWarning) _testingThemeWarning->raise();
|
||||
}
|
||||
|
||||
void MainWindow::showFromTray(QSystemTrayIcon::ActivationReason reason) {
|
||||
|
@ -1045,6 +1067,7 @@ void MainWindow::updateControlsGeometry() {
|
|||
if (layerBg) layerBg->resize(width(), height());
|
||||
if (_mediaPreview) _mediaPreview->setGeometry(0, title->height(), width(), height() - title->height());
|
||||
if (_connecting) _connecting->setGeometry(0, height() - _connecting->height(), _connecting->width(), _connecting->height());
|
||||
if (_testingThemeWarning) _testingThemeWarning->setGeometry(rect());
|
||||
}
|
||||
|
||||
MainWindow::TempDirState MainWindow::tempDirState() {
|
||||
|
|
|
@ -42,6 +42,13 @@ namespace Settings {
|
|||
class Widget;
|
||||
} // namespace Settings
|
||||
|
||||
namespace Window {
|
||||
namespace Theme {
|
||||
struct BackgroundUpdate;
|
||||
class WarningWidget;
|
||||
} // namespace Theme
|
||||
} // namespace Window
|
||||
|
||||
class ConnectingWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -228,6 +235,8 @@ private:
|
|||
void showConnecting(const QString &text, const QString &reconnect = QString());
|
||||
void hideConnecting();
|
||||
|
||||
void themeUpdated(const Window::Theme::BackgroundUpdate &data);
|
||||
|
||||
void updateControlsGeometry();
|
||||
|
||||
QPixmap grabInner();
|
||||
|
@ -253,6 +262,7 @@ private:
|
|||
bool _isActive = false;
|
||||
|
||||
ChildWidget<ConnectingWidget> _connecting = { nullptr };
|
||||
ChildWidget<Window::Theme::WarningWidget> _testingThemeWarning = { nullptr };
|
||||
|
||||
Local::ClearManager *_clearManager = nullptr;
|
||||
|
||||
|
|
|
@ -229,18 +229,7 @@ void BackgroundWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &upd
|
|||
|
||||
auto filePath = update.filePaths.front();
|
||||
if (filePath.endsWith(qstr(".tdesktop-theme"), Qt::CaseInsensitive)) {
|
||||
QByteArray themeContent;
|
||||
Window::Theme::Instance theme;
|
||||
if (Window::Theme::LoadFromFile(filePath, &theme, &themeContent)) {
|
||||
Local::writeTheme(QDir().relativeFilePath(filePath), QFileInfo(filePath).absoluteFilePath(), themeContent, theme.cached);
|
||||
if (Window::Theme::Background()->tile() != theme.cached.tiled) {
|
||||
Window::Theme::Background()->setTile(theme.cached.tiled);
|
||||
}
|
||||
if (!theme.cached.background.isEmpty()) {
|
||||
Local::writeBackground(Window::Theme::kThemeBackground, QImage());
|
||||
}
|
||||
style::main_palette::apply(theme.palette);
|
||||
}
|
||||
Window::Theme::Apply(filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "localstorage.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "window/window_theme.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -54,7 +55,7 @@ struct ColorReferenceWrap {
|
|||
};
|
||||
|
||||
ImagePtr generateUserpicImage(const style::icon &icon) {
|
||||
auto data = QImage(icon.width() * cIntRetinaFactor(), icon.height() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
auto data = QImage(icon.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
|
||||
data.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
Painter p(&data);
|
||||
|
@ -984,6 +985,15 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context,
|
|||
bool playVideo = data->isVideo() && audioPlayer();
|
||||
bool playAnimation = data->isAnimation();
|
||||
auto &location = data->location(true);
|
||||
if (auto applyTheme = data->name.endsWith(qstr(".tdesktop-theme"))) {
|
||||
if (!location.isEmpty() && location.accessEnable()) {
|
||||
if (Window::Theme::Apply(location.name())) {
|
||||
location.accessDisable();
|
||||
return;
|
||||
}
|
||||
location.accessDisable();
|
||||
}
|
||||
}
|
||||
if (!location.isEmpty() || (!data->data().isEmpty() && (playVoice || playMusic || playVideo || playAnimation))) {
|
||||
if (playVoice) {
|
||||
AudioMsgId playing;
|
||||
|
@ -1277,13 +1287,22 @@ void DocumentData::automaticLoadSettingsChanged() {
|
|||
void DocumentData::performActionOnLoad() {
|
||||
if (_actionOnLoad == ActionOnLoadNone) return;
|
||||
|
||||
const FileLocation &loc(location(true));
|
||||
QString already = loc.name();
|
||||
HistoryItem *item = _actionOnLoadMsgId.msg ? App::histItemById(_actionOnLoadMsgId) : nullptr;
|
||||
auto loc = location(true);
|
||||
auto already = loc.name();
|
||||
auto item = _actionOnLoadMsgId.msg ? App::histItemById(_actionOnLoadMsgId) : nullptr;
|
||||
bool showImage = !isVideo() && (size < MediaViewImageSizeLimit);
|
||||
bool playVoice = voice() && audioPlayer() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen);
|
||||
bool playMusic = song() && audioPlayer() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen);
|
||||
bool playAnimation = isAnimation() && (_actionOnLoad == ActionOnLoadPlayInline || _actionOnLoad == ActionOnLoadOpen) && showImage && item && item->getMedia();
|
||||
if (auto applyTheme = name.endsWith(qstr(".tdesktop-theme"))) {
|
||||
if (!loc.isEmpty() && loc.accessEnable()) {
|
||||
if (Window::Theme::Apply(loc.name())) {
|
||||
loc.accessDisable();
|
||||
return;
|
||||
}
|
||||
loc.accessDisable();
|
||||
}
|
||||
}
|
||||
if (playVoice) {
|
||||
if (loaded()) {
|
||||
AudioMsgId playing;
|
||||
|
|
|
@ -75,43 +75,49 @@ void stopManager() {
|
|||
internal::destroyIcons();
|
||||
}
|
||||
|
||||
QImage colorizeImage(const QImage &src, QColor color, const QRect &r) {
|
||||
t_assert(r.x() >= 0 && src.width() >= r.x() + r.width());
|
||||
t_assert(r.y() >= 0 && src.height() >= r.y() + r.height());
|
||||
void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect, QPoint dstPoint) {
|
||||
if (srcRect.isNull()) {
|
||||
srcRect = src.rect();
|
||||
} else {
|
||||
t_assert(src.rect().contains(srcRect));
|
||||
}
|
||||
auto width = srcRect.width();
|
||||
auto height = srcRect.height();
|
||||
t_assert(outResult && outResult->rect().contains(QRect(dstPoint, srcRect.size())));
|
||||
|
||||
auto initialAlpha = color.alpha() + 1;
|
||||
auto red = color.red() * initialAlpha;
|
||||
auto green = color.green() * initialAlpha;
|
||||
auto blue = color.blue() * initialAlpha;
|
||||
auto alpha = 255 * initialAlpha;
|
||||
auto alpha_red = static_cast<uint64>(alpha) | (static_cast<uint64>(red) << 32);
|
||||
auto green_blue = static_cast<uint64>(green) | (static_cast<uint64>(blue) << 32);
|
||||
auto initialAlpha = c.alpha() + 1;
|
||||
auto red = (c.red() * initialAlpha) >> 8;
|
||||
auto green = (c.green() * initialAlpha) >> 8;
|
||||
auto blue = (c.blue() * initialAlpha) >> 8;
|
||||
auto alpha = (255 * initialAlpha) >> 8;
|
||||
auto pattern = static_cast<uint64>(alpha)
|
||||
| (static_cast<uint64>(red) << 16)
|
||||
| (static_cast<uint64>(green) << 32)
|
||||
| (static_cast<uint64>(blue) << 48);
|
||||
|
||||
auto result = QImage(r.width(), r.height(), QImage::Format_ARGB32_Premultiplied);
|
||||
auto resultBytesPerPixel = (src.depth() >> 3);
|
||||
auto resultIntsPerPixel = 1;
|
||||
auto resultIntsPerLine = (result.bytesPerLine() >> 2);
|
||||
auto resultIntsAdded = resultIntsPerLine - r.width() * resultIntsPerPixel;
|
||||
auto resultInts = reinterpret_cast<uint32*>(result.bits());
|
||||
auto resultIntsPerLine = (outResult->bytesPerLine() >> 2);
|
||||
auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
|
||||
auto resultInts = reinterpret_cast<uint32*>(outResult->bits()) + dstPoint.y() * resultIntsPerLine + dstPoint.x() * resultIntsPerPixel;
|
||||
t_assert(resultIntsAdded >= 0);
|
||||
t_assert(result.depth() == ((resultIntsPerPixel * sizeof(uint32)) << 3));
|
||||
t_assert(result.bytesPerLine() == (resultIntsPerLine << 2));
|
||||
t_assert(outResult->depth() == ((resultIntsPerPixel * sizeof(uint32)) << 3));
|
||||
t_assert(outResult->bytesPerLine() == (resultIntsPerLine << 2));
|
||||
|
||||
auto maskBytesPerPixel = (src.depth() >> 3);
|
||||
auto maskBytesPerLine = src.bytesPerLine();
|
||||
auto maskBytesAdded = maskBytesPerLine - r.width() * maskBytesPerPixel;
|
||||
auto maskBytes = src.constBits() + r.y() * maskBytesPerLine + r.x() * maskBytesPerPixel;
|
||||
auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
|
||||
auto maskBytes = src.constBits() + srcRect.y() * maskBytesPerLine + srcRect.x() * maskBytesPerPixel;
|
||||
t_assert(maskBytesAdded >= 0);
|
||||
t_assert(src.depth() == (maskBytesPerPixel << 3));
|
||||
for (int y = 0; y != r.height(); ++y) {
|
||||
for (int x = 0; x != r.width(); ++x) {
|
||||
for (int y = 0; y != height; ++y) {
|
||||
for (int x = 0; x != width; ++x) {
|
||||
auto maskOpacity = static_cast<uint64>(*maskBytes) + 1;
|
||||
auto alpha_red_masked = (alpha_red * maskOpacity) >> 16;
|
||||
auto green_blue_masked = (green_blue * maskOpacity) >> 16;
|
||||
auto alpha = static_cast<uint32>(alpha_red_masked & 0xFF);
|
||||
auto red = static_cast<uint32>((alpha_red_masked >> 32) & 0xFF);
|
||||
auto green = static_cast<uint32>(green_blue_masked & 0xFF);
|
||||
auto blue = static_cast<uint32>((green_blue_masked >> 32) & 0xFF);
|
||||
auto masked = (pattern * maskOpacity) >> 8;
|
||||
auto alpha = static_cast<uint32>(masked & 0xFF);
|
||||
auto red = static_cast<uint32>((masked >> 16) & 0xFF);
|
||||
auto green = static_cast<uint32>((masked >> 32) & 0xFF);
|
||||
auto blue = static_cast<uint32>((masked >> 48) & 0xFF);
|
||||
*resultInts = blue | (green << 8) | (red << 16) | (alpha << 24);
|
||||
maskBytes += maskBytesPerPixel;
|
||||
resultInts += resultIntsPerPixel;
|
||||
|
@ -120,8 +126,7 @@ QImage colorizeImage(const QImage &src, QColor color, const QRect &r) {
|
|||
resultInts += resultIntsAdded;
|
||||
}
|
||||
|
||||
result.setDevicePixelRatio(src.devicePixelRatio());
|
||||
return std_::move(result);
|
||||
outResult->setDevicePixelRatio(src.devicePixelRatio());
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
|
|
@ -63,10 +63,19 @@ bool setPaletteColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);
|
|||
void startManager();
|
||||
void stopManager();
|
||||
|
||||
QImage colorizeImage(const QImage &src, QColor c, const QRect &r);
|
||||
// *outResult must be r.width() x r.height(), ARGB32_Premultiplied.
|
||||
// QRect(0, 0, src.width(), src.height()) must contain r.
|
||||
void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect = QRect(), QPoint dstPoint = QPoint(0, 0));
|
||||
|
||||
inline QImage colorizeImage(const QImage &src, const color &c, const QRect &r) {
|
||||
return colorizeImage(src, c->c, r);
|
||||
inline QImage colorizeImage(const QImage &src, QColor c, QRect srcRect = QRect()) {
|
||||
if (srcRect.isNull()) srcRect = src.rect();
|
||||
auto result = QImage(srcRect.size(), QImage::Format_ARGB32_Premultiplied);
|
||||
colorizeImage(src, c, &result, srcRect);
|
||||
return std_::move(result);
|
||||
}
|
||||
|
||||
inline QImage colorizeImage(const QImage &src, const color &c, QRect srcRect = QRect()) {
|
||||
return colorizeImage(src, c->c, srcRect);
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
|
|
@ -100,12 +100,12 @@ inline Color::operator const QPen &() const {
|
|||
} // namespace internal
|
||||
|
||||
inline QColor interpolate(QColor a, QColor b, float64 opacity_b) {
|
||||
auto bOpacity = static_cast<int>(opacity_b * 255), aOpacity = (255 - bOpacity);
|
||||
auto bOpacity = static_cast<int>(opacity_b * 255) + 1, aOpacity = (256 - bOpacity);
|
||||
return {
|
||||
(a.red() * aOpacity + b.red() * bOpacity + 1) >> 8,
|
||||
(a.green() * aOpacity + b.green() * bOpacity + 1) >> 8,
|
||||
(a.blue() * aOpacity + b.blue() * bOpacity + 1) >> 8,
|
||||
(a.alpha() * aOpacity + b.alpha() * bOpacity + 1) >> 8
|
||||
(a.red() * aOpacity + b.red() * bOpacity) >> 8,
|
||||
(a.green() * aOpacity + b.green() * bOpacity) >> 8,
|
||||
(a.blue() * aOpacity + b.blue() * bOpacity) >> 8,
|
||||
(a.alpha() * aOpacity + b.alpha() * bOpacity) >> 8
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -72,10 +72,6 @@ QImage createIconMask(const IconMask *mask) {
|
|||
return maskImage.copy(r);
|
||||
}
|
||||
|
||||
QImage createIconImage(const QImage &mask, QColor color) {
|
||||
return colorizeImage(mask, color, QRect(0, 0, mask.width(), mask.height()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MonoIcon::MonoIcon(const IconMask *mask, const Color &color, QPoint offset)
|
||||
|
@ -141,7 +137,8 @@ void MonoIcon::paint(QPainter &p, const QPoint &pos, int outerw, QColor colorOve
|
|||
if (_pixmap.isNull()) {
|
||||
p.fillRect(partPosX, partPosY, w, h, colorOverride);
|
||||
} else {
|
||||
p.drawImage(partPosX, partPosY, createIconImage(_maskImage, colorOverride));
|
||||
ensureColorizedImage(colorOverride);
|
||||
p.drawImage(partPosX, partPosY, _colorizedImage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +147,8 @@ void MonoIcon::fill(QPainter &p, const QRect &rect, QColor colorOverride) const
|
|||
if (_pixmap.isNull()) {
|
||||
p.fillRect(rect, colorOverride);
|
||||
} else {
|
||||
p.drawImage(rect, createIconImage(_maskImage, colorOverride), QRect(0, 0, _pixmap.width(), _pixmap.height()));
|
||||
ensureColorizedImage(colorOverride);
|
||||
p.drawImage(rect, _colorizedImage, _colorizedImage.rect());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,12 +204,18 @@ void MonoIcon::ensureLoaded() const {
|
|||
}
|
||||
}
|
||||
|
||||
void MonoIcon::ensureColorizedImage(QColor color) const {
|
||||
if (_colorizedImage.isNull()) _colorizedImage = QImage(_maskImage.size(), QImage::Format_ARGB32_Premultiplied);
|
||||
colorizeImage(_maskImage, color, &_colorizedImage);
|
||||
}
|
||||
|
||||
void MonoIcon::createCachedPixmap() const {
|
||||
iconPixmaps.createIfNull();
|
||||
auto key = qMakePair(_mask, colorKey(_color->c));
|
||||
auto j = iconPixmaps->constFind(key);
|
||||
if (j == iconPixmaps->cend()) {
|
||||
j = iconPixmaps->insert(key, App::pixmapFromImageInPlace(createIconImage(_maskImage, _color->c)));
|
||||
auto image = colorizeImage(_maskImage, _color);
|
||||
j = iconPixmaps->insert(key, App::pixmapFromImageInPlace(std_::move(image)));
|
||||
}
|
||||
_pixmap = j.value();
|
||||
_size = _pixmap.size() / cIntRetinaFactor();
|
||||
|
|
|
@ -68,11 +68,12 @@ public:
|
|||
private:
|
||||
void ensureLoaded() const;
|
||||
void createCachedPixmap() const;
|
||||
void ensureColorizedImage(QColor color) const;
|
||||
|
||||
const IconMask *_mask = nullptr;
|
||||
Color _color;
|
||||
QPoint _offset = { 0, 0 };
|
||||
mutable QImage _maskImage;
|
||||
mutable QImage _maskImage, _colorizedImage;
|
||||
mutable QPixmap _pixmap; // for pixmaps
|
||||
mutable QSize _size; // for rects
|
||||
|
||||
|
|
|
@ -35,7 +35,15 @@ constexpr int kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
|
|||
constexpr int kThemeSchemeSizeLimit = 1024 * 1024;
|
||||
|
||||
struct Data {
|
||||
struct Applying {
|
||||
QString path;
|
||||
QByteArray content;
|
||||
QByteArray paletteForRevert;
|
||||
Cached cached;
|
||||
};
|
||||
|
||||
ChatBackground background;
|
||||
Applying applying;
|
||||
};
|
||||
NeverFreedPointer<Data> instance;
|
||||
|
||||
|
@ -137,11 +145,11 @@ bool loadColorScheme(const QByteArray &content, Instance *out = nullptr) {
|
|||
|
||||
auto size = value.size();
|
||||
auto error = false;
|
||||
if (value[0] == '#' && (size == 7 || size == 8)) {
|
||||
if (value[0] == '#' && (size == 7 || size == 9)) {
|
||||
auto r = readHexUchar(value[1], value[2], error);
|
||||
auto g = readHexUchar(value[3], value[4], error);
|
||||
auto b = readHexUchar(value[5], value[6], error);
|
||||
auto a = (size == 8) ? readHexUchar(value[7], value[8], error) : uchar(255);
|
||||
auto a = (size == 9) ? readHexUchar(value[7], value[8], error) : uchar(255);
|
||||
if (!error) {
|
||||
if (out) {
|
||||
error = !out->palette.setColor(QLatin1String(name), r, g, b, a);
|
||||
|
@ -157,7 +165,7 @@ bool loadColorScheme(const QByteArray &content, Instance *out = nullptr) {
|
|||
}
|
||||
}
|
||||
if (error) {
|
||||
LOG(("Error: Expected a color value in #rrggbb or #rrggbbaa format in the color scheme."));
|
||||
LOG(("Error: Expected a color value in #rrggbb or #rrggbbaa format in the color scheme (while applying '%1: %2')").arg(QLatin1String(name)).arg(QLatin1String(value)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -320,6 +328,12 @@ void ChatBackground::setImage(int32 id, QImage &&image) {
|
|||
if (_id == kThemeBackground) {
|
||||
_tile = _themeTile;
|
||||
setPreparedImage(QImage(_themeImage));
|
||||
} else if (_id == internal::kTestingThemeBackground || _id == internal::kTestingDefaultBackground) {
|
||||
if (_id == internal::kTestingDefaultBackground || image.isNull()) {
|
||||
image.load(qsl(":/gui/art/bg.jpg"));
|
||||
_id = internal::kTestingDefaultBackground;
|
||||
}
|
||||
setPreparedImage(std_::move(image));
|
||||
} else {
|
||||
if (_id == kDefaultBackground) {
|
||||
image.load(qsl(":/gui/art/bg.jpg"));
|
||||
|
@ -356,19 +370,98 @@ bool ChatBackground::tile() const {
|
|||
return _tile;
|
||||
}
|
||||
|
||||
void ChatBackground::setTile(bool tile) {
|
||||
bool ChatBackground::tileForSave() const {
|
||||
if (_id == internal::kTestingThemeBackground ||
|
||||
_id == internal::kTestingDefaultBackground) {
|
||||
return _tileForRevert;
|
||||
}
|
||||
return tile();
|
||||
}
|
||||
|
||||
void ChatBackground::ensureStarted() {
|
||||
if (_image.isNull()) {
|
||||
// We should start first, otherwise the default call
|
||||
// to start() will reset this value to _themeTile.
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatBackground::setTile(bool tile) {
|
||||
ensureStarted();
|
||||
if (_tile != tile) {
|
||||
_tile = tile;
|
||||
Local::writeUserSettings();
|
||||
if (_id != internal::kTestingThemeBackground && _id != internal::kTestingDefaultBackground) {
|
||||
Local::writeUserSettings();
|
||||
}
|
||||
notify(BackgroundUpdate(BackgroundUpdate::Type::Changed, _tile));
|
||||
}
|
||||
}
|
||||
|
||||
void ChatBackground::reset() {
|
||||
if (_id == internal::kTestingThemeBackground || _id == internal::kTestingDefaultBackground) {
|
||||
if (_themeImage.isNull()) {
|
||||
_idForRevert = kDefaultBackground;
|
||||
_imageForRevert = QImage();
|
||||
_tileForRevert = false;
|
||||
} else {
|
||||
_idForRevert = kThemeBackground;
|
||||
_imageForRevert = _themeImage;
|
||||
_tileForRevert = _themeTile;
|
||||
}
|
||||
} else {
|
||||
setImage(kThemeBackground);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatBackground::saveForRevert() {
|
||||
ensureStarted();
|
||||
if (_id != internal::kTestingThemeBackground && _id != internal::kTestingDefaultBackground) {
|
||||
_idForRevert = _id;
|
||||
_imageForRevert = std_::move(_image).toImage();
|
||||
_tileForRevert = _tile;
|
||||
}
|
||||
}
|
||||
|
||||
void ChatBackground::setTestingTheme(Instance &&theme) {
|
||||
style::main_palette::apply(theme.palette);
|
||||
if (!theme.background.isNull() || _id == kThemeBackground) {
|
||||
saveForRevert();
|
||||
setImage(internal::kTestingThemeBackground, std_::move(theme.background));
|
||||
setTile(theme.tiled);
|
||||
}
|
||||
notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, _tile), true);
|
||||
}
|
||||
|
||||
void ChatBackground::keepApplied() {
|
||||
if (_id == internal::kTestingThemeBackground) {
|
||||
_id = kThemeBackground;
|
||||
_themeImage = _image.toImage();
|
||||
_themeTile = _tile;
|
||||
} else if (_id == internal::kTestingDefaultBackground) {
|
||||
_id = kDefaultBackground;
|
||||
_themeImage = QImage();
|
||||
_themeTile = false;
|
||||
writeNewBackgroundSettings();
|
||||
}
|
||||
notify(BackgroundUpdate(BackgroundUpdate::Type::ApplyingTheme, _tile), true);
|
||||
}
|
||||
|
||||
void ChatBackground::writeNewBackgroundSettings() {
|
||||
if (_tile != _tileForRevert) {
|
||||
Local::writeUserSettings();
|
||||
}
|
||||
Local::writeBackground(_id, QImage());
|
||||
}
|
||||
|
||||
void ChatBackground::revert() {
|
||||
if (_id == internal::kTestingThemeBackground || _id == internal::kTestingDefaultBackground) {
|
||||
setTile(_tileForRevert);
|
||||
setImage(_idForRevert, std_::move(_imageForRevert));
|
||||
}
|
||||
notify(BackgroundUpdate(BackgroundUpdate::Type::RevertingTheme, _tile), true);
|
||||
}
|
||||
|
||||
|
||||
ChatBackground *Background() {
|
||||
instance.createIfNull();
|
||||
return &instance->background;
|
||||
|
@ -396,6 +489,43 @@ void Unload() {
|
|||
instance.clear();
|
||||
}
|
||||
|
||||
bool Apply(const QString &filepath) {
|
||||
QByteArray content;
|
||||
Instance theme;
|
||||
if (!LoadFromFile(filepath, &theme, &content)) {
|
||||
return false;
|
||||
}
|
||||
instance.createIfNull();
|
||||
instance->applying.path = filepath;
|
||||
instance->applying.content = content;
|
||||
instance->applying.cached = theme.cached;
|
||||
if (instance->applying.paletteForRevert.isEmpty()) {
|
||||
instance->applying.paletteForRevert = style::main_palette::save();
|
||||
}
|
||||
Background()->setTestingTheme(std_::move(theme));
|
||||
return true;
|
||||
}
|
||||
|
||||
void KeepApplied() {
|
||||
auto filepath = instance ? instance->applying.path : QString();
|
||||
if (filepath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto pathRelative = QDir().relativeFilePath(filepath);
|
||||
auto pathAbsolute = QFileInfo(filepath).absoluteFilePath();
|
||||
Local::writeTheme(pathRelative, pathAbsolute, instance->applying.content, instance->applying.cached);
|
||||
instance->applying = Data::Applying();
|
||||
Background()->keepApplied();
|
||||
}
|
||||
|
||||
void Revert() {
|
||||
if (!instance->applying.paletteForRevert.isEmpty()) {
|
||||
style::main_palette::load(instance->applying.paletteForRevert);
|
||||
}
|
||||
instance->applying = Data::Applying();
|
||||
Background()->revert();
|
||||
}
|
||||
|
||||
bool LoadFromFile(const QString &path, Instance *out, QByteArray *outContent) {
|
||||
*outContent = readThemeContent(path);
|
||||
if (outContent->size() < 4) {
|
||||
|
|
|
@ -24,7 +24,9 @@ namespace Window {
|
|||
namespace Theme {
|
||||
namespace internal {
|
||||
|
||||
constexpr int32 kUninitializedBackground = -3;
|
||||
constexpr int32 kUninitializedBackground = -999;
|
||||
constexpr int32 kTestingThemeBackground = -666;
|
||||
constexpr int32 kTestingDefaultBackground = -665;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
|
@ -43,6 +45,10 @@ struct Cached {
|
|||
bool Load(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, Cached &cache);
|
||||
void Unload();
|
||||
|
||||
bool Apply(const QString &filepath);
|
||||
void KeepApplied();
|
||||
void Revert();
|
||||
|
||||
struct Instance {
|
||||
style::palette palette;
|
||||
QImage background;
|
||||
|
@ -56,6 +62,9 @@ struct BackgroundUpdate {
|
|||
New,
|
||||
Changed,
|
||||
Start,
|
||||
TestingTheme,
|
||||
RevertingTheme,
|
||||
ApplyingTheme,
|
||||
};
|
||||
|
||||
BackgroundUpdate(Type type, bool tiled) : type(type), tiled(tiled) {
|
||||
|
@ -73,16 +82,22 @@ public:
|
|||
void start();
|
||||
void setImage(int32 id, QImage &&image = QImage());
|
||||
void setTile(bool tile);
|
||||
void reset() {
|
||||
setImage(kThemeBackground);
|
||||
}
|
||||
void reset();
|
||||
|
||||
void setTestingTheme(Instance &&theme);
|
||||
void keepApplied();
|
||||
void revert();
|
||||
|
||||
int32 id() const;
|
||||
const QPixmap &image() const;
|
||||
bool tile() const;
|
||||
bool tileForSave() const;
|
||||
|
||||
private:
|
||||
void ensureStarted();
|
||||
void saveForRevert();
|
||||
void setPreparedImage(QImage &&image);
|
||||
void writeNewBackgroundSettings();
|
||||
|
||||
int32 _id = internal::kUninitializedBackground;
|
||||
QPixmap _image;
|
||||
|
@ -91,6 +106,10 @@ private:
|
|||
QImage _themeImage;
|
||||
bool _themeTile = false;
|
||||
|
||||
int32 _idForRevert = internal::kUninitializedBackground;
|
||||
QImage _imageForRevert;
|
||||
bool _tileForRevert = false;
|
||||
|
||||
};
|
||||
|
||||
ChatBackground *Background();
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "window/window_theme_warning.h"
|
||||
|
||||
#include "styles/style_boxes.h"
|
||||
#include "ui/buttons/round_button.h"
|
||||
#include "window/window_theme.h"
|
||||
#include "lang.h"
|
||||
|
||||
namespace Window {
|
||||
namespace Theme {
|
||||
namespace {
|
||||
|
||||
constexpr int kWaitBeforeRevertMs = 15999;
|
||||
|
||||
} // namespace
|
||||
|
||||
WarningWidget::WarningWidget(QWidget *parent) : TWidget(parent)
|
||||
, _secondsLeft(kWaitBeforeRevertMs / 1000)
|
||||
, _shadow(st::themeWarningShadow)
|
||||
, _keepChanges(this, lang(lng_theme_keep_changes), st::defaultBoxButton)
|
||||
, _revert(this, lang(lng_theme_revert), st::cancelBoxButton) {
|
||||
_keepChanges->setClickedCallback([] { Window::Theme::KeepApplied(); });
|
||||
_revert->setClickedCallback([] { Window::Theme::Revert(); });
|
||||
_timer.setTimeoutHandler([this] { handleTimer(); });
|
||||
updateText();
|
||||
}
|
||||
|
||||
void WarningWidget::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Escape) {
|
||||
Window::Theme::Revert();
|
||||
}
|
||||
}
|
||||
|
||||
void WarningWidget::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
if (!_cache.isNull()) {
|
||||
if (!_animation.animating(getms())) {
|
||||
if (isHidden()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
p.setOpacity(_animation.current(_hiding ? 0. : 1.));
|
||||
p.drawPixmap(_outer.topLeft(), _cache);
|
||||
if (!_animation.animating()) {
|
||||
_cache = QPixmap();
|
||||
showChildren();
|
||||
_started = getms(true);
|
||||
_timer.start(100);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_shadow.paint(p, _inner, st::themeWarningShadowShift);
|
||||
p.fillRect(_inner, st::boxBg);
|
||||
|
||||
p.setFont(st::boxTitleFont);
|
||||
p.setPen(st::boxTitleFg);
|
||||
p.drawTextLeft(_inner.x() + st::boxTitlePosition.x(), _inner.y() + st::boxTitlePosition.y(), width(), lang(lng_theme_sure_keep));
|
||||
|
||||
p.setFont(st::boxTextFont);
|
||||
p.setPen(st::boxTextFg);
|
||||
p.drawTextLeft(_inner.x() + st::boxTitlePosition.x(), _inner.y() + st::themeWarningTextTop, width(), _text);
|
||||
}
|
||||
|
||||
void WarningWidget::resizeEvent(QResizeEvent *e) {
|
||||
_inner = QRect((width() - st::themeWarningWidth) / 2, (height() - st::themeWarningHeight) / 2, st::themeWarningWidth, st::themeWarningHeight);
|
||||
_outer = _inner.marginsAdded(_shadow.getDimensions(st::themeWarningShadowShift));
|
||||
auto left = _inner.x() + _inner.width() - st::boxButtonPadding.right() - _keepChanges->width();
|
||||
_keepChanges->moveToLeft(left, _inner.y() + _inner.height() - st::boxButtonPadding.bottom() - _keepChanges->height());
|
||||
_revert->moveToLeft(left - st::boxButtonPadding.left() - _revert->width(), _keepChanges->y());
|
||||
update();
|
||||
}
|
||||
|
||||
void WarningWidget::handleTimer() {
|
||||
auto msPassed = getms(true) - _started;
|
||||
setSecondsLeft((kWaitBeforeRevertMs - msPassed) / 1000);
|
||||
}
|
||||
|
||||
void WarningWidget::setSecondsLeft(int secondsLeft) {
|
||||
if (secondsLeft <= 0) {
|
||||
Window::Theme::Revert();
|
||||
} else {
|
||||
if (_secondsLeft != secondsLeft) {
|
||||
_secondsLeft = secondsLeft;
|
||||
updateText();
|
||||
update();
|
||||
}
|
||||
_timer.start(100);
|
||||
}
|
||||
}
|
||||
|
||||
void WarningWidget::updateText() {
|
||||
_text = lng_theme_reverting(lt_count, _secondsLeft);
|
||||
}
|
||||
|
||||
void WarningWidget::showAnimated() {
|
||||
startAnimation(false);
|
||||
show();
|
||||
setFocus();
|
||||
}
|
||||
|
||||
void WarningWidget::hideAnimated() {
|
||||
startAnimation(true);
|
||||
}
|
||||
|
||||
void WarningWidget::startAnimation(bool hiding) {
|
||||
_timer.stop();
|
||||
_hiding = hiding;
|
||||
if (_cache.isNull()) {
|
||||
showChildren();
|
||||
myEnsureResized(this);
|
||||
_cache = myGrab(this, _outer);
|
||||
}
|
||||
hideChildren();
|
||||
_animation.start([this] {
|
||||
update();
|
||||
if (_hiding) {
|
||||
hide();
|
||||
if (_hiddenCallback) {
|
||||
_hiddenCallback();
|
||||
}
|
||||
}
|
||||
}, _hiding ? 1. : 0., _hiding ? 0. : 1., st::layerSlideDuration);
|
||||
}
|
||||
|
||||
} // namespace Theme
|
||||
} // namespace Window
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/effects/rect_shadow.h"
|
||||
|
||||
class BoxButton;
|
||||
|
||||
namespace Window {
|
||||
namespace Theme {
|
||||
|
||||
class WarningWidget : public TWidget {
|
||||
public:
|
||||
WarningWidget(QWidget *parent);
|
||||
|
||||
void setHiddenCallback(base::lambda_unique<void()> callback) {
|
||||
_hiddenCallback = std_::move(callback);
|
||||
}
|
||||
|
||||
void showAnimated();
|
||||
void hideAnimated();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
void setSecondsLeft(int secondsLeft);
|
||||
void startAnimation(bool hiding);
|
||||
void updateText();
|
||||
void handleTimer();
|
||||
|
||||
bool _hiding = false;
|
||||
FloatAnimation _animation;
|
||||
QPixmap _cache;
|
||||
QRect _inner, _outer;
|
||||
|
||||
SingleTimer _timer;
|
||||
uint64 _started = 0;
|
||||
int _secondsLeft = 0;
|
||||
QString _text;
|
||||
|
||||
Ui::RectShadow _shadow;
|
||||
ChildWidget<BoxButton> _keepChanges;
|
||||
ChildWidget<BoxButton> _revert;
|
||||
|
||||
base::lambda_unique<void()> _hiddenCallback;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Theme
|
||||
} // namespace Window
|
|
@ -559,6 +559,8 @@
|
|||
'<(src_loc)/window/top_bar_widget.h',
|
||||
'<(src_loc)/window/window_theme.cpp',
|
||||
'<(src_loc)/window/window_theme.h',
|
||||
'<(src_loc)/window/window_theme_warning.cpp',
|
||||
'<(src_loc)/window/window_theme_warning.h',
|
||||
|
||||
'<(sp_media_key_tap_loc)/SPMediaKeyTap.m',
|
||||
'<(sp_media_key_tap_loc)/SPMediaKeyTap.h',
|
||||
|
|
Loading…
Reference in New Issue