Apply app color scheme, test dynamic header.

This commit is contained in:
John Preston 2023-12-02 22:43:16 +04:00
parent 212259aae3
commit f9299eee2a
5 changed files with 727 additions and 549 deletions

View file

@ -4,7 +4,30 @@ body {
line-height: 25px; line-height: 25px;
padding: 0; padding: 0;
margin: 0; margin: 0;
background-color: var(--td-window-bg);
color: var(--td-window-fg);
} }
html.custom_scroll ::-webkit-scrollbar {
border-radius: 5px !important;
border: 3px solid transparent !important;
background-color: var(--td-scroll-bg) !important;
background-clip: content-box !important;
width: 10px !important;
}
html.custom_scroll ::-webkit-scrollbar:hover {
background-color: var(--td-scroll-bg-over) !important;
}
html.custom_scroll ::-webkit-scrollbar-thumb {
border-radius: 5px !important;
border: 3px solid transparent !important;
background-color: var(--td-scroll-bar-bg) !important;
background-clip: content-box !important;
}
html.custom_scroll ::-webkit-scrollbar-thumb:hover {
background-color: var(--td-scroll-bar-bg-over) !important;
}
article { article {
padding-bottom: 12px; padding-bottom: 12px;
overflow: hidden; overflow: hidden;
@ -25,22 +48,11 @@ article h2 {
font-size: 24px; font-size: 24px;
line-height: 30px; line-height: 30px;
margin: -6px 18px 12px; margin: -6px 18px 12px;
color: #777; color: var(--td-window-sub-text-fg);
}
article h6.kicker {
font-family: 'Helvetica Neue';
font-size: 14px;
line-height: 17px;
margin: 21px 18px 12px;
font-weight: 500;
color: #79828B;
}
article h6.kicker + h1 {
margin-top: 12px;
} }
article address { article address {
font-size: 15px; font-size: 15px;
color: #79828B; color: var(--td-window-sub-text-fg);
margin: 12px 18px 21px; margin: 12px 18px 21px;
font-style: normal; font-style: normal;
} }
@ -59,17 +71,17 @@ article address figure {
} }
article address a, article address a,
article address a[href] { article address a[href] {
color: #79828B; color: var(--td-window-sub-text-fg);
} }
article address a[href] { article address a[href] {
text-decoration: underline; text-decoration: underline;
} }
article a[href] { article a[href] {
color: #007EE5; color: var(--td-window-active-text-fg);
text-decoration: none; text-decoration: none;
} }
article span.reference { article span.reference {
border: dotted #ddd; border: dotted var(--td-window-sub-text-fg);
border-width: 1px 1px 1px 2px; border-width: 1px 1px 1px 2px;
background: rgba(255, 255, 255, 0.7); background: rgba(255, 255, 255, 0.7);
margin: 0 1px; margin: 0 1px;
@ -81,12 +93,13 @@ article.rtl span.reference {
} }
article code { article code {
font-size: 0.94em; font-size: 0.94em;
background: #eee; background: var(--td-box-divider-bg);
border-radius: 2px; border-radius: 2px;
padding: 0 3px 1px; padding: 0 3px 1px;
} }
article mark { article mark {
background: #fcf8e3; background: var(--td-window-bg-over);
color: var(--td-window-fg);
border-radius: 2px; border-radius: 2px;
padding: 0 3px 1px; padding: 0 3px 1px;
} }
@ -166,7 +179,7 @@ article blockquote:before {
top: 3px; top: 3px;
bottom: 3px; bottom: 3px;
width: 3px; width: 3px;
background-color: #000; background-color: var(--td-window-bg-active);
border-radius: 3px; border-radius: 3px;
} }
article.rtl blockquote { article.rtl blockquote {
@ -197,7 +210,7 @@ article .iv-pullquote cite {
font-family: 'Helvetica Neue'; font-family: 'Helvetica Neue';
font-size: 15px; font-size: 15px;
display: block; display: block;
color: #79828B; color: var(--td-window-sub-text-fg);
line-height: 19px; line-height: 19px;
padding: 5px 0 2px; padding: 5px 0 2px;
font-style: normal; font-style: normal;
@ -215,7 +228,7 @@ article ol hr {
article hr:before { article hr:before {
content: ''; content: '';
display: block; display: block;
border-top: 1px solid #c9cdd1; border-top: 1px solid var(--td-window-sub-text-fg);
padding: 0 0 2px; padding: 0 0 2px;
} }
article footer hr { article footer hr {
@ -272,17 +285,17 @@ article table {
article table.bordered, article table.bordered,
article table.bordered td, article table.bordered td,
article table.bordered th { article table.bordered th {
border: 1px solid #ddd; border: 1px solid var(--td-box-divider-fg);
} }
article table.striped tr:nth-child(odd) td { article table.striped tr:nth-child(odd) td {
background-color: #f7f7f7; background-color: var(--td-box-divider-bg);
} }
article table caption { article table caption {
font-size: 15px; font-size: 15px;
line-height: 18px; line-height: 18px;
margin: 4px 0 7px; margin: 4px 0 7px;
text-align: left; text-align: left;
color: #79828B; color: var(--td-window-sub-text-fg);
} }
article.rtl table caption { article.rtl table caption {
text-align: right; text-align: right;
@ -292,13 +305,13 @@ article th {
font-size: 15px; font-size: 15px;
line-height: 21px; line-height: 21px;
padding: 6px 5px 5px; padding: 6px 5px 5px;
background-color: #fff; background-color: var(--td-window-bg);
vertical-align: middle; vertical-align: middle;
font-weight: normal; font-weight: normal;
text-align: left; text-align: left;
} }
article th { article th {
background-color: #efefef; background-color: var(--td-box-divider-bg);
} }
article.rtl table td, article.rtl table td,
article.rtl table th { article.rtl table th {
@ -312,7 +325,7 @@ article details {
article details:before { article details:before {
content: ''; content: '';
display: block; display: block;
border-bottom: 1px solid #c8c7cb; border-bottom: 1px solid var(--td-box-divider-fg);
position: absolute; position: absolute;
left: 18px; left: 18px;
right: 0; right: 0;
@ -401,7 +414,7 @@ audio {
img { img {
font-size: 12px; font-size: 12px;
line-height: 14px; line-height: 14px;
color: #999; color: var(--td-window-sub-text-fg);
} }
img.pic { img.pic {
max-height: none; max-height: none;
@ -455,7 +468,7 @@ figure > figure {
} }
figcaption { figcaption {
font-size: 15px; font-size: 15px;
color: #79828B; color: var(--td-window-sub-text-fg);
padding: 6px 18px 0; padding: 6px 18px 0;
line-height: 19px; line-height: 19px;
text-align: left; text-align: left;
@ -478,7 +491,7 @@ figcaption > cite {
} }
footer { footer {
margin: 12px 18px; margin: 12px 18px;
color: #79828B; color: var(--td-window-sub-text-fg);
} }
figure.slideshow-wrap { figure.slideshow-wrap {
@ -658,7 +671,7 @@ blockquote.embed-post address a {
padding-top: 2px; padding-top: 2px;
font-size: 17px; font-size: 17px;
font-weight: 600; font-weight: 600;
color: #000; color: var(--td-window-fg);
} }
blockquote.embed-post address time { blockquote.embed-post address time {
display: block; display: block;
@ -689,7 +702,7 @@ section.embed-post {
display: block; display: block;
width: auto; width: auto;
height: auto; height: auto;
background: #f7f7f7; background: var(--td-box-divider-bg);
margin: 0 18px 12px; margin: 0 18px 12px;
padding: 24px 18px; padding: 24px 18px;
text-align: center; text-align: center;
@ -698,11 +711,11 @@ section.embed-post strong {
font-size: 21px; font-size: 21px;
font-weight: normal; font-weight: normal;
display: block; display: block;
color: #777; color: var(--td-window-sub-text-fg);
} }
section.embed-post small { section.embed-post small {
display: block; display: block;
color: #777; color: var(--td-window-sub-text-fg);
} }
section.related { section.related {
@ -715,21 +728,21 @@ section.related h4 {
font-weight: 500; font-weight: 500;
display: block; display: block;
padding: 7px 18px; padding: 7px 18px;
background: #f7f7f7; background: var(--td-box-divider-bg);
margin: 0; margin: 0;
color: #000; color: var(--td-window-fg);
} }
section.related a.related-link { section.related a.related-link {
display: block; display: block;
padding: 15px 18px 16px; padding: 15px 18px 16px;
background: #fff; background: var(--td-window-bg);
position: relative; position: relative;
overflow: hidden; overflow: hidden;
} }
section.related a.related-link:after { section.related a.related-link:after {
content: ''; content: '';
display: block; display: block;
border-bottom: 1px solid #c8c7cb; border-bottom: 1px solid var(--td-box-divider-fg);
position: absolute; position: absolute;
left: 18px; left: 18px;
right: 0; right: 0;
@ -740,7 +753,7 @@ section.related .related-link-url {
font-size: 15px; font-size: 15px;
line-height: 18px; line-height: 18px;
padding: 7px 0; padding: 7px 0;
color: #999; color: var(--td-window-sub-text-fg);
word-break: break-word; word-break: break-word;
} }
section.related .related-link-thumb { section.related .related-link-thumb {
@ -770,7 +783,7 @@ section.related .related-link-title {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: pre-wrap; white-space: pre-wrap;
color: #000; color: var(--td-window-fg);
} }
section.related .related-link-desc { section.related .related-link-desc {
font-size: 14px; font-size: 14px;
@ -783,7 +796,7 @@ section.related .related-link-desc {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: pre-wrap; white-space: pre-wrap;
color: #000; color: var(--td-window-fg);
} }
section.related .related-link-source { section.related .related-link-source {
font-size: 13px; font-size: 13px;
@ -793,7 +806,7 @@ section.related .related-link-source {
margin-top: 4px; margin-top: 4px;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
color: #818181; color: var(--td-window-sub-text-fg);
} }
section.message { section.message {
@ -811,7 +824,7 @@ section.message > aside {
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
text-align: center; text-align: center;
color: #999; color: var(--td-window-sub-text-fg);
font-size: 24px; font-size: 24px;
pointer-events: none; pointer-events: none;
} }
@ -820,7 +833,7 @@ section.message > aside > cite {
font-size: 14px; font-size: 14px;
padding: 10px 0 0; padding: 10px 0 0;
font-style: normal; font-style: normal;
color: #ccc; color: var(--td-window-sub-text-fg);
} }
section.channel { section.channel {
@ -833,11 +846,11 @@ section.channel:first-child {
section.channel > a { section.channel > a {
display: block; display: block;
padding: 7px 18px; padding: 7px 18px;
background: #f7f7f7; background: var(--td-box-divider-bg);
} }
section.channel > a:before { section.channel > a:before {
content: 'Join'; content: var(--td-lng-group-call-join);
color: #3196e8; color: var(--td-window-active-text-fg);
font-weight: 500; font-weight: 500;
margin-left: 7px; margin-left: 7px;
float: right; float: right;
@ -848,7 +861,7 @@ section.channel > a > h4 {
line-height: 26px; line-height: 26px;
font-weight: 500; font-weight: 500;
margin: 0; margin: 0;
color: #000; color: var(--td-window-fg);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;

View file

@ -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) {

View file

@ -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('&', "&amp;")
.replace('"', "&quot;")
.replace('\'', "&#039;")
.replace('<', "&lt;")
.replace('>', "&gt;");
}
[[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;
@ -118,7 +255,22 @@ void Controller::show(const QString &dataPath, Prepared page) {
}; };
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 {

View file

@ -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;
}; };

View file

@ -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">