Apply app color scheme, test dynamic header.
This commit is contained in:
parent
212259aae3
commit
f9299eee2a
5 changed files with 727 additions and 549 deletions
File diff suppressed because it is too large
Load diff
|
@ -49,15 +49,13 @@ var IV = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
postMessageHandler: function(event) {
|
updateStyles: function (styles) {
|
||||||
if (event.source !== window.parent ||
|
if (IV.styles !== styles) {
|
||||||
event.origin != window.parentOrigin) {
|
console.log('Setting', styles);
|
||||||
return;
|
IV.styles = styles;
|
||||||
}
|
document.getElementsByTagName('html')[0].style = styles;
|
||||||
try {
|
} else {
|
||||||
var data = JSON.parse(event.data);
|
console.log('Skipping', styles);
|
||||||
} catch(e) {
|
|
||||||
var data = {};
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
slideshowSlide: function(el, next) {
|
slideshowSlide: function(el, next) {
|
||||||
|
|
|
@ -8,13 +8,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "iv/iv_controller.h"
|
#include "iv/iv_controller.h"
|
||||||
|
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
|
#include "base/invoke_queued.h"
|
||||||
#include "iv/iv_data.h"
|
#include "iv/iv_data.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/widgets/rp_window.h"
|
#include "ui/widgets/rp_window.h"
|
||||||
#include "webview/webview_data_stream_memory.h"
|
#include "webview/webview_data_stream_memory.h"
|
||||||
#include "webview/webview_embed.h"
|
#include "webview/webview_embed.h"
|
||||||
#include "webview/webview_interface.h"
|
#include "webview/webview_interface.h"
|
||||||
#include "styles/palette.h"
|
#include "styles/palette.h"
|
||||||
|
|
||||||
|
#include "base/call_delayed.h"
|
||||||
|
#include "ui/effects/animations.h"
|
||||||
|
|
||||||
#include <QtCore/QRegularExpression>
|
#include <QtCore/QRegularExpression>
|
||||||
#include <QtCore/QJsonDocument>
|
#include <QtCore/QJsonDocument>
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
|
@ -23,8 +28,99 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <QtGui/QPainter>
|
#include <QtGui/QPainter>
|
||||||
|
|
||||||
namespace Iv {
|
namespace Iv {
|
||||||
|
namespace {
|
||||||
|
|
||||||
Controller::Controller() = default;
|
[[nodiscard]] QByteArray ComputeStyles() {
|
||||||
|
static const auto map = base::flat_map<QByteArray, const style::color*>{
|
||||||
|
{ "scroll-bg", &st::scrollBg },
|
||||||
|
{ "scroll-bg-over", &st::scrollBgOver },
|
||||||
|
{ "scroll-bar-bg", &st::scrollBarBg },
|
||||||
|
{ "scroll-bar-bg-over", &st::scrollBarBgOver },
|
||||||
|
{ "window-bg", &st::windowBg },
|
||||||
|
{ "window-bg-over", &st::windowBgOver },
|
||||||
|
{ "window-bg-ripple", &st::windowBgRipple },
|
||||||
|
{ "window-fg", &st::windowFg },
|
||||||
|
{ "window-sub-text-fg", &st::windowSubTextFg },
|
||||||
|
{ "window-active-text-fg", &st::windowActiveTextFg },
|
||||||
|
{ "window-bg-active", &st::windowBgActive },
|
||||||
|
{ "box-divider-bg", &st::boxDividerBg },
|
||||||
|
{ "box-divider-fg", &st::boxDividerFg },
|
||||||
|
};
|
||||||
|
static const auto phrases = base::flat_map<QByteArray, tr::phrase<>>{
|
||||||
|
{ "group-call-join", tr::lng_group_call_join },
|
||||||
|
};
|
||||||
|
static const auto serialize = [](const style::color *color) {
|
||||||
|
const auto qt = (*color)->c;
|
||||||
|
if (qt.alpha() == 255) {
|
||||||
|
return '#'
|
||||||
|
+ QByteArray::number(qt.red(), 16).right(2)
|
||||||
|
+ QByteArray::number(qt.green(), 16).right(2)
|
||||||
|
+ QByteArray::number(qt.blue(), 16).right(2);
|
||||||
|
}
|
||||||
|
return "rgba("
|
||||||
|
+ QByteArray::number(qt.red()) + ","
|
||||||
|
+ QByteArray::number(qt.green()) + ","
|
||||||
|
+ QByteArray::number(qt.blue()) + ","
|
||||||
|
+ QByteArray::number(qt.alpha() / 255.) + ")";
|
||||||
|
};
|
||||||
|
static const auto escape = [](tr::phrase<> phrase) {
|
||||||
|
const auto text = phrase(tr::now);
|
||||||
|
|
||||||
|
auto result = QByteArray();
|
||||||
|
for (auto i = 0; i != text.size(); ++i) {
|
||||||
|
uint ucs4 = text[i].unicode();
|
||||||
|
if (QChar::isHighSurrogate(ucs4) && i + 1 != text.size()) {
|
||||||
|
ushort low = text[i + 1].unicode();
|
||||||
|
if (QChar::isLowSurrogate(low)) {
|
||||||
|
ucs4 = QChar::surrogateToUcs4(ucs4, low);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ucs4 == '\'' || ucs4 == '\"' || ucs4 == '\\') {
|
||||||
|
result.append('\\').append(char(ucs4));
|
||||||
|
} else if (ucs4 < 32 || ucs4 > 127) {
|
||||||
|
result.append('\\' + QByteArray::number(ucs4, 16) + ' ');
|
||||||
|
} else {
|
||||||
|
result.append(char(ucs4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
auto result = QByteArray();
|
||||||
|
for (const auto &[name, phrase] : phrases) {
|
||||||
|
result += "--td-lng-" + name + ":'" + escape(phrase) + "'; ";
|
||||||
|
}
|
||||||
|
for (const auto &[name, color] : map) {
|
||||||
|
result += "--td-" + name + ':' + serialize(color) + ';';
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QByteArray EscapeForAttribute(QByteArray value) {
|
||||||
|
return value
|
||||||
|
.replace('&', "&")
|
||||||
|
.replace('"', """)
|
||||||
|
.replace('\'', "'")
|
||||||
|
.replace('<', "<")
|
||||||
|
.replace('>', ">");
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QByteArray EscapeForScriptString(QByteArray value) {
|
||||||
|
return value
|
||||||
|
.replace('\\', "\\\\")
|
||||||
|
.replace('"', "\\\"")
|
||||||
|
.replace('\'', "\\\'");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
Controller::Controller()
|
||||||
|
: _updateStyles([=] {
|
||||||
|
const auto str = EscapeForScriptString(ComputeStyles());
|
||||||
|
if (_webview) {
|
||||||
|
_webview->eval("IV.updateStyles(\"" + str + "\");");
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
}
|
||||||
|
|
||||||
Controller::~Controller() {
|
Controller::~Controller() {
|
||||||
_webview = nullptr;
|
_webview = nullptr;
|
||||||
|
@ -32,21 +128,66 @@ Controller::~Controller() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::show(const QString &dataPath, Prepared page) {
|
void Controller::show(const QString &dataPath, Prepared page) {
|
||||||
|
createWindow();
|
||||||
|
InvokeQueued(_container, [=, page = std::move(page)]() mutable {
|
||||||
|
showInWindow(dataPath, std::move(page));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::createWindow() {
|
||||||
_window = std::make_unique<Ui::RpWindow>();
|
_window = std::make_unique<Ui::RpWindow>();
|
||||||
const auto window = _window.get();
|
const auto window = _window.get();
|
||||||
|
|
||||||
window->setGeometry({ 200, 200, 600, 800 });
|
window->setGeometry({ 200, 200, 600, 800 });
|
||||||
|
|
||||||
const auto container = Ui::CreateChild<Ui::RpWidget>(
|
const auto skip = window->lifetime().make_state<rpl::variable<int>>(0);
|
||||||
window->body().get());
|
|
||||||
window->sizeValue() | rpl::start_with_next([=](QSize size) {
|
|
||||||
container->setGeometry(QRect(QPoint(), size));
|
|
||||||
}, container->lifetime());
|
|
||||||
container->show();
|
|
||||||
|
|
||||||
|
_container = Ui::CreateChild<Ui::RpWidget>(window->body().get());
|
||||||
|
rpl::combine(
|
||||||
|
window->body()->sizeValue(),
|
||||||
|
skip->value()
|
||||||
|
) | rpl::start_with_next([=](QSize size, int skip) {
|
||||||
|
_container->setGeometry(QRect(QPoint(), size).marginsRemoved({ 0, skip, 0, 0 }));
|
||||||
|
}, _container->lifetime());
|
||||||
|
|
||||||
|
base::call_delayed(5000, window, [=] {
|
||||||
|
const auto animation = window->lifetime().make_state<Ui::Animations::Simple>();
|
||||||
|
animation->start([=] {
|
||||||
|
*skip = animation->value(64);
|
||||||
|
if (!animation->animating()) {
|
||||||
|
base::call_delayed(4000, window, [=] {
|
||||||
|
animation->start([=] {
|
||||||
|
*skip = animation->value(0);
|
||||||
|
}, 64, 0, 200, anim::easeOutCirc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 0, 64, 200, anim::easeOutCirc);
|
||||||
|
});
|
||||||
|
|
||||||
|
window->body()->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||||
|
auto p = QPainter(window->body());
|
||||||
|
p.fillRect(clip, st::windowBg);
|
||||||
|
p.fillRect(clip, QColor(0, 128, 0, 128));
|
||||||
|
}, window->body()->lifetime());
|
||||||
|
|
||||||
|
_container->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||||
|
QPainter(_container).fillRect(clip, st::windowBg);
|
||||||
|
}, _container->lifetime());
|
||||||
|
|
||||||
|
_container->show();
|
||||||
|
window->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::showInWindow(const QString &dataPath, Prepared page) {
|
||||||
|
Expects(_container != nullptr);
|
||||||
|
|
||||||
|
const auto window = _window.get();
|
||||||
_webview = std::make_unique<Webview::Window>(
|
_webview = std::make_unique<Webview::Window>(
|
||||||
container,
|
_container,
|
||||||
Webview::WindowConfig{ .userDataPath = dataPath });
|
Webview::WindowConfig{
|
||||||
|
.opaqueBg = st::windowBg->c,
|
||||||
|
.userDataPath = dataPath,
|
||||||
|
});
|
||||||
const auto raw = _webview.get();
|
const auto raw = _webview.get();
|
||||||
|
|
||||||
window->lifetime().add([=] {
|
window->lifetime().add([=] {
|
||||||
|
@ -69,14 +210,10 @@ void Controller::show(const QString &dataPath, Prepared page) {
|
||||||
}, window->lifetime());
|
}, window->lifetime());
|
||||||
raw->widget()->show();
|
raw->widget()->show();
|
||||||
|
|
||||||
container->geometryValue(
|
_container->sizeValue(
|
||||||
) | rpl::start_with_next([=](QRect geometry) {
|
) | rpl::start_with_next([=](QSize size) {
|
||||||
raw->widget()->setGeometry(geometry);
|
raw->widget()->setGeometry(QRect(QPoint(), size));
|
||||||
}, container->lifetime());
|
}, _container->lifetime());
|
||||||
|
|
||||||
container->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
|
||||||
QPainter(container).fillRect(clip, st::windowBg);
|
|
||||||
}, container->lifetime());
|
|
||||||
|
|
||||||
raw->setNavigationStartHandler([=](const QString &uri, bool newWindow) {
|
raw->setNavigationStartHandler([=](const QString &uri, bool newWindow) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -113,12 +250,27 @@ void Controller::show(const QString &dataPath, Prepared page) {
|
||||||
.stream = std::make_unique<Webview::DataStreamFromMemory>(
|
.stream = std::make_unique<Webview::DataStreamFromMemory>(
|
||||||
std::move(data),
|
std::move(data),
|
||||||
std::move(mime)),
|
std::move(mime)),
|
||||||
});
|
});
|
||||||
return Webview::DataResult::Done;
|
return Webview::DataResult::Done;
|
||||||
};
|
};
|
||||||
const auto id = std::string_view(request.id).substr(3);
|
const auto id = std::string_view(request.id).substr(3);
|
||||||
if (id == "page.html") {
|
if (id == "page.html") {
|
||||||
return finishWith(page.html, "text/html");
|
const auto i = page.html.indexOf("<html"_q);
|
||||||
|
Assert(i >= 0);
|
||||||
|
const auto colored = page.html.mid(0, i + 5)
|
||||||
|
+ " style=\"" + EscapeForAttribute(ComputeStyles()) + "\""
|
||||||
|
+ page.html.mid(i + 5);
|
||||||
|
if (!_subscribedToColors) {
|
||||||
|
_subscribedToColors = true;
|
||||||
|
|
||||||
|
rpl::merge(
|
||||||
|
Lang::Updated(),
|
||||||
|
style::PaletteChanged()
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_updateStyles.call();
|
||||||
|
}, _webview->lifetime());
|
||||||
|
}
|
||||||
|
return finishWith(colored, "text/html");
|
||||||
}
|
}
|
||||||
const auto css = id.ends_with(".css");
|
const auto css = id.ends_with(".css");
|
||||||
const auto js = !css && id.ends_with(".js");
|
const auto js = !css && id.ends_with(".js");
|
||||||
|
@ -140,8 +292,6 @@ void Controller::show(const QString &dataPath, Prepared page) {
|
||||||
raw->init(R"(
|
raw->init(R"(
|
||||||
)");
|
)");
|
||||||
raw->navigateToData("iv/page.html");
|
raw->navigateToData("iv/page.html");
|
||||||
|
|
||||||
window->show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::active() const {
|
bool Controller::active() const {
|
||||||
|
|
|
@ -7,12 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/invoke_queued.h"
|
||||||
|
|
||||||
namespace Webview {
|
namespace Webview {
|
||||||
struct DataRequest;
|
struct DataRequest;
|
||||||
class Window;
|
class Window;
|
||||||
} // namespace Webview
|
} // namespace Webview
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
class RpWidget;
|
||||||
class RpWindow;
|
class RpWindow;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
@ -45,14 +48,21 @@ public:
|
||||||
[[nodiscard]] rpl::lifetime &lifetime();
|
[[nodiscard]] rpl::lifetime &lifetime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void createWindow();
|
||||||
|
void showInWindow(const QString &dataPath, Prepared page);
|
||||||
|
|
||||||
void escape();
|
void escape();
|
||||||
void close();
|
void close();
|
||||||
void quit();
|
void quit();
|
||||||
|
|
||||||
std::unique_ptr<Ui::RpWindow> _window;
|
std::unique_ptr<Ui::RpWindow> _window;
|
||||||
|
Ui::RpWidget *_container = nullptr;
|
||||||
std::unique_ptr<Webview::Window> _webview;
|
std::unique_ptr<Webview::Window> _webview;
|
||||||
rpl::event_stream<Webview::DataRequest> _dataRequests;
|
rpl::event_stream<Webview::DataRequest> _dataRequests;
|
||||||
rpl::event_stream<Event> _events;
|
rpl::event_stream<Event> _events;
|
||||||
|
SingleQueuedInvokation _updateStyles;
|
||||||
|
bool _subscribedToColors = false;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "iv/iv_data.h"
|
#include "iv/iv_data.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/image/image_prepare.h"
|
#include "ui/image/image_prepare.h"
|
||||||
|
#include "styles/palette.h"
|
||||||
|
|
||||||
#include <QtCore/QSize>
|
#include <QtCore/QSize>
|
||||||
|
|
||||||
|
@ -1009,8 +1010,14 @@ QByteArray Parser::prepare(QByteArray body) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray Parser::html(const QByteArray &head, const QByteArray &body) {
|
QByteArray Parser::html(const QByteArray &head, const QByteArray &body) {
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
const auto classAttribute = ""_q;
|
||||||
|
#else // Q_OS_MAC
|
||||||
|
const auto classAttribute = " class=\"custom_scroll\""_q;
|
||||||
|
#endif // Q_OS_MAC
|
||||||
|
|
||||||
return R"(<!DOCTYPE html>
|
return R"(<!DOCTYPE html>
|
||||||
<html>
|
<html)"_q + classAttribute + R"(">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="robots" content="noindex, nofollow">
|
<meta name="robots" content="noindex, nofollow">
|
||||||
|
|
Loading…
Reference in a new issue