mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Implement title and in-IV buttons.
This commit is contained in:
parent
f9299eee2a
commit
f508ad5e75
11 changed files with 416 additions and 42 deletions
|
@ -28,6 +28,100 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover {
|
||||||
background-color: var(--td-scroll-bar-bg-over) !important;
|
background-color: var(--td-scroll-bar-bg-over) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fixed_button {
|
||||||
|
position: fixed;
|
||||||
|
background-color: var(--td-history-to-down-bg);
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
box-shadow: 0 0 4px -2px var(--td-history-to-down-shadow);
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
z-index: 1000;
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.fixed_button:hover {
|
||||||
|
background-color: var(--td-history-to-down-bg-over);
|
||||||
|
}
|
||||||
|
.fixed_button svg {
|
||||||
|
fill: none;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.fixed_button .ripple .inner {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
transform: scale(0);
|
||||||
|
opacity: 1;
|
||||||
|
animation: ripple 650ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
|
||||||
|
background-color: var(--td-history-to-down-bg-ripple);
|
||||||
|
}
|
||||||
|
.fixed_button .ripple.hiding {
|
||||||
|
animation: fadeOut 200ms linear forwards;
|
||||||
|
}
|
||||||
|
@keyframes ripple {
|
||||||
|
to {
|
||||||
|
transform: scale(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes fadeOut {
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#top_menu svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
#top_menu circle {
|
||||||
|
fill: var(--td-history-to-down-fg);
|
||||||
|
}
|
||||||
|
#top_menu:hover circle {
|
||||||
|
fill: var(--td-history-to-down-fg-over);
|
||||||
|
}
|
||||||
|
#top_menu {
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
#top_back path,
|
||||||
|
#bottom_up path {
|
||||||
|
stroke: var(--td-history-to-down-fg);
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
}
|
||||||
|
#top_back:hover path,
|
||||||
|
#bottom_up:hover path {
|
||||||
|
stroke: var(--td-history-to-down-fg-over);
|
||||||
|
}
|
||||||
|
#top_back {
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
transition: left 200ms linear;
|
||||||
|
}
|
||||||
|
#top_back svg {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
#top_back.hidden {
|
||||||
|
left: -36px;
|
||||||
|
}
|
||||||
|
#bottom_up {
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
transition: bottom 200ms linear;
|
||||||
|
}
|
||||||
|
#bottom_up svg {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
#bottom_up.hidden {
|
||||||
|
bottom: -36px;
|
||||||
|
}
|
||||||
|
|
||||||
article {
|
article {
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -25,15 +25,15 @@ var IV = {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
},
|
},
|
||||||
frameKeyDown: function (e) {
|
frameKeyDown: function (e) {
|
||||||
let keyW = (e.key === 'w')
|
const keyW = (e.key === 'w')
|
||||||
|| (e.code === 'KeyW')
|
|| (e.code === 'KeyW')
|
||||||
|| (e.keyCode === 87);
|
|| (e.keyCode === 87);
|
||||||
let keyQ = (e.key === 'q')
|
const keyQ = (e.key === 'q')
|
||||||
|| (e.code === 'KeyQ')
|
|| (e.code === 'KeyQ')
|
||||||
|| (e.keyCode === 81);
|
|| (e.keyCode === 81);
|
||||||
let keyM = (e.key === 'm')
|
const keyM = (e.key === 'm')
|
||||||
|| (e.code === 'KeyM')
|
|| (e.code === 'KeyM')
|
||||||
|| (e.keyCode === 77);
|
|| (e.keyCode === 77);
|
||||||
if ((e.metaKey || e.ctrlKey) && (keyW || keyQ || keyM)) {
|
if ((e.metaKey || e.ctrlKey) && (keyW || keyQ || keyM)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
IV.notify({
|
IV.notify({
|
||||||
|
@ -49,13 +49,26 @@ var IV = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
frameMouseEnter: function (e) {
|
||||||
|
IV.notify({ event: 'mouseenter' });
|
||||||
|
},
|
||||||
|
frameMouseUp: function (e) {
|
||||||
|
IV.notify({ event: 'mouseup' });
|
||||||
|
},
|
||||||
|
lastScrollTop: 0,
|
||||||
|
frameScrolled: function (e) {
|
||||||
|
const now = document.documentElement.scrollTop;
|
||||||
|
if (now < 100) {
|
||||||
|
document.getElementById('bottom_up').classList.add('hidden');
|
||||||
|
} else if (now > IV.lastScrollTop && now > 200) {
|
||||||
|
document.getElementById('bottom_up').classList.remove('hidden');
|
||||||
|
}
|
||||||
|
IV.lastScrollTop = now;
|
||||||
|
},
|
||||||
updateStyles: function (styles) {
|
updateStyles: function (styles) {
|
||||||
if (IV.styles !== styles) {
|
if (IV.styles !== styles) {
|
||||||
console.log('Setting', styles);
|
|
||||||
IV.styles = styles;
|
IV.styles = styles;
|
||||||
document.getElementsByTagName('html')[0].style = styles;
|
document.getElementsByTagName('html')[0].style = styles;
|
||||||
} else {
|
|
||||||
console.log('Skipping', styles);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
slideshowSlide: function(el, next) {
|
slideshowSlide: function(el, next) {
|
||||||
|
@ -72,7 +85,9 @@ var IV = {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
initPreBlocks: function() {
|
initPreBlocks: function() {
|
||||||
if (!hljs) return;
|
if (!hljs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var pres = document.getElementsByTagName('pre');
|
var pres = document.getElementsByTagName('pre');
|
||||||
for (var i = 0; i < pres.length; i++) {
|
for (var i = 0; i < pres.length; i++) {
|
||||||
if (pres[i].hasAttribute('data-language')) {
|
if (pres[i].hasAttribute('data-language')) {
|
||||||
|
@ -102,9 +117,71 @@ var IV = {
|
||||||
}, false);
|
}, false);
|
||||||
})(iframes[i]);
|
})(iframes[i]);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
addRipple: function (button, x, y) {
|
||||||
|
const ripple = document.createElement('span');
|
||||||
|
ripple.classList.add('ripple');
|
||||||
|
|
||||||
|
const inner = document.createElement('span');
|
||||||
|
inner.classList.add('inner');
|
||||||
|
x -= button.offsetLeft;
|
||||||
|
y -= button.offsetTop;
|
||||||
|
|
||||||
|
const mx = button.clientWidth - x;
|
||||||
|
const my = button.clientHeight - y;
|
||||||
|
const sq1 = x * x + y * y;
|
||||||
|
const sq2 = mx * mx + y * y;
|
||||||
|
const sq3 = x * x + my * my;
|
||||||
|
const sq4 = mx * mx + my * my;
|
||||||
|
const radius = Math.sqrt(Math.max(sq1, sq2, sq3, sq4));
|
||||||
|
|
||||||
|
inner.style.width = inner.style.height = `${2 * radius}px`;
|
||||||
|
inner.style.left = `${x - radius}px`;
|
||||||
|
inner.style.top = `${y - radius}px`;
|
||||||
|
inner.classList.add('inner');
|
||||||
|
|
||||||
|
ripple.addEventListener('animationend', function (e) {
|
||||||
|
if (e.animationName === 'fadeOut') {
|
||||||
|
ripple.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ripple.appendChild(inner);
|
||||||
|
button.appendChild(ripple);
|
||||||
|
},
|
||||||
|
stopRipples: function (button) {
|
||||||
|
const ripples = button.getElementsByClassName('ripple');
|
||||||
|
for (var i = 0; i < ripples.length; ++i) {
|
||||||
|
const ripple = ripples[i];
|
||||||
|
if (!ripple.classList.contains('hiding')) {
|
||||||
|
ripple.classList.add('hiding');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
init: function () {
|
||||||
|
const buttons = document.getElementsByClassName('fixed_button');
|
||||||
|
for (let i = 0; i < buttons.length; ++i) {
|
||||||
|
const button = buttons[i];
|
||||||
|
button.addEventListener('mousedown', function (e) {
|
||||||
|
IV.addRipple(e.currentTarget, e.clientX, e.clientY);
|
||||||
|
});
|
||||||
|
button.addEventListener('mouseup', function (e) {
|
||||||
|
IV.stopRipples(e.currentTarget);
|
||||||
|
});
|
||||||
|
button.addEventListener('mouseleave', function (e) {
|
||||||
|
IV.stopRipples(e.currentTarget);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toTop: function () {
|
||||||
|
document.getElementById('bottom_up').classList.add('hidden');
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
document.onclick = IV.frameClickHandler;
|
document.onclick = IV.frameClickHandler;
|
||||||
document.onkeydown = IV.frameKeyDown;
|
document.onkeydown = IV.frameKeyDown;
|
||||||
|
document.onmouseenter = IV.frameMouseEnter;
|
||||||
|
document.onmouseup = IV.frameMouseUp;
|
||||||
|
document.onscroll = IV.frameScrolled;
|
||||||
window.onmessage = IV.postMessageHandler;
|
window.onmessage = IV.postMessageHandler;
|
||||||
|
|
100
Telegram/SourceFiles/iv/iv.style
Normal file
100
Telegram/SourceFiles/iv/iv.style
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
using "ui/basic.style";
|
||||||
|
using "ui/widgets/widgets.style";
|
||||||
|
|
||||||
|
ivTitleHeight: 24px;
|
||||||
|
ivTitleIconShift: point(0px, 0px);
|
||||||
|
ivTitleButton: IconButton(windowTitleButton) {
|
||||||
|
height: ivTitleHeight;
|
||||||
|
iconPosition: ivTitleIconShift;
|
||||||
|
}
|
||||||
|
ivTitleButtonClose: IconButton(windowTitleButtonClose) {
|
||||||
|
height: ivTitleHeight;
|
||||||
|
iconPosition: ivTitleIconShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
ivTitleButtonSize: size(windowTitleButtonWidth, ivTitleHeight);
|
||||||
|
ivTitle: WindowTitle(defaultWindowTitle) {
|
||||||
|
height: ivTitleHeight;
|
||||||
|
style: TextStyle(defaultTextStyle) {
|
||||||
|
font: font(semibold 12px);
|
||||||
|
}
|
||||||
|
shadow: false;
|
||||||
|
minimize: IconButton(ivTitleButton) {
|
||||||
|
icon: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBg },
|
||||||
|
{ "title_button_minimize", titleButtonFg, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
iconOver: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBgOver },
|
||||||
|
{ "title_button_minimize", titleButtonFgOver, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
minimizeIconActive: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBgActive },
|
||||||
|
{ "title_button_minimize", titleButtonFgActive, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
minimizeIconActiveOver: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBgActiveOver },
|
||||||
|
{ "title_button_minimize", titleButtonFgActiveOver, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
maximize: IconButton(windowTitleButton) {
|
||||||
|
icon: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBg },
|
||||||
|
{ "title_button_maximize", titleButtonFg, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
iconOver: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBgOver },
|
||||||
|
{ "title_button_maximize", titleButtonFgOver, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
maximizeIconActive: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBgActive },
|
||||||
|
{ "title_button_maximize", titleButtonFgActive, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
maximizeIconActiveOver: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBgActiveOver },
|
||||||
|
{ "title_button_maximize", titleButtonFgActiveOver, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
restoreIcon: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBg },
|
||||||
|
{ "title_button_restore", titleButtonFg, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
restoreIconOver: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBgOver },
|
||||||
|
{ "title_button_restore", titleButtonFgOver, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
restoreIconActive: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBgActive },
|
||||||
|
{ "title_button_restore", titleButtonFgActive, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
restoreIconActiveOver: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonBgActiveOver },
|
||||||
|
{ "title_button_restore", titleButtonFgActiveOver, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
close: IconButton(windowTitleButtonClose) {
|
||||||
|
icon: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonCloseBg },
|
||||||
|
{ "title_button_close", titleButtonCloseFg, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
iconOver: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonCloseBgOver },
|
||||||
|
{ "title_button_close", titleButtonCloseFgOver, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
closeIconActive: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonCloseBgActive },
|
||||||
|
{ "title_button_close", titleButtonCloseFgActive, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
closeIconActiveOver: icon {
|
||||||
|
{ ivTitleButtonSize, titleButtonCloseBgActiveOver },
|
||||||
|
{ "title_button_close", titleButtonCloseFgActiveOver, ivTitleIconShift },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ivTitleExpandedHeight: 76px;
|
|
@ -11,14 +11,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/invoke_queued.h"
|
#include "base/invoke_queued.h"
|
||||||
#include "iv/iv_data.h"
|
#include "iv/iv_data.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "ui/platform/ui_platform_window_title.h"
|
||||||
#include "ui/widgets/rp_window.h"
|
#include "ui/widgets/rp_window.h"
|
||||||
|
#include "ui/painter.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 "styles/style_iv.h"
|
||||||
#include "base/call_delayed.h"
|
#include "styles/style_widgets.h"
|
||||||
#include "ui/effects/animations.h"
|
#include "styles/style_window.h"
|
||||||
|
|
||||||
#include <QtCore/QRegularExpression>
|
#include <QtCore/QRegularExpression>
|
||||||
#include <QtCore/QJsonDocument>
|
#include <QtCore/QJsonDocument>
|
||||||
|
@ -39,12 +41,23 @@ namespace {
|
||||||
{ "window-bg", &st::windowBg },
|
{ "window-bg", &st::windowBg },
|
||||||
{ "window-bg-over", &st::windowBgOver },
|
{ "window-bg-over", &st::windowBgOver },
|
||||||
{ "window-bg-ripple", &st::windowBgRipple },
|
{ "window-bg-ripple", &st::windowBgRipple },
|
||||||
|
{ "window-bg-active", &st::windowBgActive },
|
||||||
{ "window-fg", &st::windowFg },
|
{ "window-fg", &st::windowFg },
|
||||||
{ "window-sub-text-fg", &st::windowSubTextFg },
|
{ "window-sub-text-fg", &st::windowSubTextFg },
|
||||||
{ "window-active-text-fg", &st::windowActiveTextFg },
|
{ "window-active-text-fg", &st::windowActiveTextFg },
|
||||||
{ "window-bg-active", &st::windowBgActive },
|
{ "window-shadow-fg", &st::windowShadowFg },
|
||||||
{ "box-divider-bg", &st::boxDividerBg },
|
{ "box-divider-bg", &st::boxDividerBg },
|
||||||
{ "box-divider-fg", &st::boxDividerFg },
|
{ "box-divider-fg", &st::boxDividerFg },
|
||||||
|
{ "menu-icon-fg", &st::menuIconFg },
|
||||||
|
{ "menu-icon-fg-over", &st::menuIconFgOver },
|
||||||
|
{ "menu-bg", &st::menuBg },
|
||||||
|
{ "menu-bg-over", &st::menuBgOver },
|
||||||
|
{ "history-to-down-fg", &st::historyToDownFg },
|
||||||
|
{ "history-to-down-fg-over", &st::historyToDownFgOver },
|
||||||
|
{ "history-to-down-bg", &st::historyToDownBg },
|
||||||
|
{ "history-to-down-bg-over", &st::historyToDownBgOver },
|
||||||
|
{ "history-to-down-bg-ripple", &st::historyToDownBgRipple },
|
||||||
|
{ "history-to-down-shadow", &st::historyToDownShadow },
|
||||||
};
|
};
|
||||||
static const auto phrases = base::flat_map<QByteArray, tr::phrase<>>{
|
static const auto phrases = base::flat_map<QByteArray, tr::phrase<>>{
|
||||||
{ "group-call-join", tr::lng_group_call_join },
|
{ "group-call-join", tr::lng_group_call_join },
|
||||||
|
@ -124,52 +137,103 @@ Controller::Controller()
|
||||||
|
|
||||||
Controller::~Controller() {
|
Controller::~Controller() {
|
||||||
_webview = nullptr;
|
_webview = nullptr;
|
||||||
|
_title = nullptr;
|
||||||
_window = nullptr;
|
_window = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::show(const QString &dataPath, Prepared page) {
|
void Controller::show(const QString &dataPath, Prepared page) {
|
||||||
createWindow();
|
createWindow();
|
||||||
|
_titleText.setText(st::ivTitle.style, page.title);
|
||||||
InvokeQueued(_container, [=, page = std::move(page)]() mutable {
|
InvokeQueued(_container, [=, page = std::move(page)]() mutable {
|
||||||
showInWindow(dataPath, std::move(page));
|
showInWindow(dataPath, std::move(page));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Controller::updateTitleGeometry() {
|
||||||
|
_title->setGeometry(0, 0, _window->width(), st::ivTitle.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Controller::paintTitle(Painter &p, QRect clip) {
|
||||||
|
const auto active = _window->isActiveWindow();
|
||||||
|
const auto full = _title->width();
|
||||||
|
p.setPen(active ? st::ivTitle.fgActive : st::ivTitle.fg);
|
||||||
|
const auto available = QRect(
|
||||||
|
_titleLeftSkip,
|
||||||
|
0,
|
||||||
|
full - _titleLeftSkip - _titleRightSkip,
|
||||||
|
_title->height());
|
||||||
|
const auto use = std::min(available.width(), _titleText.maxWidth());
|
||||||
|
const auto center = full
|
||||||
|
- 2 * std::max(_titleLeftSkip, _titleRightSkip);
|
||||||
|
const auto left = (use <= center)
|
||||||
|
? ((full - use) / 2)
|
||||||
|
: (use < available.width() && _titleLeftSkip < _titleRightSkip)
|
||||||
|
? (available.x() + available.width() - use)
|
||||||
|
: available.x();
|
||||||
|
const auto titleTextHeight = st::ivTitle.style.font->height;
|
||||||
|
const auto top = (st::ivTitle.height - titleTextHeight) / 2;
|
||||||
|
_titleText.drawLeftElided(p, left, top, available.width(), full);
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::createWindow() {
|
void Controller::createWindow() {
|
||||||
_window = std::make_unique<Ui::RpWindow>();
|
_window = std::make_unique<Ui::RpWindow>();
|
||||||
|
_window->setTitleStyle(st::ivTitle);
|
||||||
const auto window = _window.get();
|
const auto window = _window.get();
|
||||||
|
|
||||||
window->setGeometry({ 200, 200, 600, 800 });
|
_title = std::make_unique<Ui::RpWidget>(window);
|
||||||
|
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
_title->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||||
|
auto p = Painter(_title.get());
|
||||||
|
paintTitle(p, clip);
|
||||||
|
}, _title->lifetime());
|
||||||
|
window->widthValue() | rpl::start_with_next([=] {
|
||||||
|
updateTitleGeometry();
|
||||||
|
}, _title->lifetime());
|
||||||
|
|
||||||
const auto skip = window->lifetime().make_state<rpl::variable<int>>(0);
|
#ifdef Q_OS_MAC
|
||||||
|
_titleLeftSkip = 8 + 12 + 8 + 12 + 8 + 12 + 8;
|
||||||
|
_titleRightSkip = st::ivTitle.style.font->spacew;
|
||||||
|
#else // Q_OS_MAC
|
||||||
|
using namespace Ui::Platform;
|
||||||
|
TitleControlsLayoutValue(
|
||||||
|
) | rpl::start_with_next([=](TitleControls::Layout layout) {
|
||||||
|
const auto accumulate = [](const auto &list) {
|
||||||
|
auto result = 0;
|
||||||
|
for (const auto control : list) {
|
||||||
|
switch (control) {
|
||||||
|
case TitleControl::Close:
|
||||||
|
result += st::ivTitle.close.width;
|
||||||
|
break;
|
||||||
|
case TitleControl::Minimize:
|
||||||
|
result += st::ivTitle.minimize.width;
|
||||||
|
break;
|
||||||
|
case TitleControl::Maximize:
|
||||||
|
result += st::ivTitle.maximize.width;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const auto space = st::ivTitle.style.font->spacew;
|
||||||
|
_titleLeftSkip = accumulate(layout.left) + space;
|
||||||
|
_titleRightSkip = accumulate(layout.right) + space;
|
||||||
|
_title->update();
|
||||||
|
}, _title->lifetime());
|
||||||
|
#endif // Q_OS_MAC
|
||||||
|
|
||||||
|
window->setGeometry({ 200, 200, 600, 800 });
|
||||||
|
window->setMinimumSize({ st::windowMinWidth, st::windowMinHeight });
|
||||||
|
|
||||||
_container = Ui::CreateChild<Ui::RpWidget>(window->body().get());
|
_container = Ui::CreateChild<Ui::RpWidget>(window->body().get());
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
window->body()->sizeValue(),
|
window->body()->sizeValue(),
|
||||||
skip->value()
|
_title->heightValue()
|
||||||
) | rpl::start_with_next([=](QSize size, int skip) {
|
) | rpl::start_with_next([=](QSize size, int title) {
|
||||||
_container->setGeometry(QRect(QPoint(), size).marginsRemoved({ 0, skip, 0, 0 }));
|
title -= window->body()->y();
|
||||||
|
_container->setGeometry(QRect(QPoint(), size).marginsRemoved(
|
||||||
|
{ 0, title, 0, 0 }));
|
||||||
}, _container->lifetime());
|
}, _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) {
|
_container->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||||
QPainter(_container).fillRect(clip, st::windowBg);
|
QPainter(_container).fillRect(clip, st::windowBg);
|
||||||
}, _container->lifetime());
|
}, _container->lifetime());
|
||||||
|
@ -237,6 +301,10 @@ void Controller::showInWindow(const QString &dataPath, Prepared page) {
|
||||||
} else if (key == u"q"_q && modifier == ctrl) {
|
} else if (key == u"q"_q && modifier == ctrl) {
|
||||||
quit();
|
quit();
|
||||||
}
|
}
|
||||||
|
} else if (event == u"mouseenter"_q) {
|
||||||
|
window->overrideSystemButtonOver({});
|
||||||
|
} else if (event == u"mouseup"_q) {
|
||||||
|
window->overrideSystemButtonDown({});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base/invoke_queued.h"
|
#include "base/invoke_queued.h"
|
||||||
|
#include "ui/effects/animations.h"
|
||||||
|
#include "ui/text/text.h"
|
||||||
|
|
||||||
|
class Painter;
|
||||||
|
|
||||||
namespace Webview {
|
namespace Webview {
|
||||||
struct DataRequest;
|
struct DataRequest;
|
||||||
|
@ -49,6 +53,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createWindow();
|
void createWindow();
|
||||||
|
void updateTitleGeometry();
|
||||||
|
void paintTitle(Painter &p, QRect clip);
|
||||||
void showInWindow(const QString &dataPath, Prepared page);
|
void showInWindow(const QString &dataPath, Prepared page);
|
||||||
|
|
||||||
void escape();
|
void escape();
|
||||||
|
@ -56,6 +62,10 @@ private:
|
||||||
void quit();
|
void quit();
|
||||||
|
|
||||||
std::unique_ptr<Ui::RpWindow> _window;
|
std::unique_ptr<Ui::RpWindow> _window;
|
||||||
|
std::unique_ptr<Ui::RpWidget> _title;
|
||||||
|
Ui::Text::String _titleText;
|
||||||
|
int _titleLeftSkip = 0;
|
||||||
|
int _titleRightSkip = 0;
|
||||||
Ui::RpWidget *_container = nullptr;
|
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;
|
||||||
|
|
|
@ -45,6 +45,9 @@ Data::Data(const MTPDwebPage &webpage, const MTPPage &page)
|
||||||
.webpageDocument = (webpage.vdocument()
|
.webpageDocument = (webpage.vdocument()
|
||||||
? *webpage.vdocument()
|
? *webpage.vdocument()
|
||||||
: std::optional<MTPDocument>()),
|
: std::optional<MTPDocument>()),
|
||||||
|
.title = (webpage.vtitle()
|
||||||
|
? qs(*webpage.vtitle())
|
||||||
|
: qs(webpage.vauthor().value_or_empty()))
|
||||||
})) {
|
})) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ struct Options {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Prepared {
|
struct Prepared {
|
||||||
|
QString title;
|
||||||
QByteArray html;
|
QByteArray html;
|
||||||
std::vector<QByteArray> resources;
|
std::vector<QByteArray> resources;
|
||||||
base::flat_map<QByteArray, QByteArray> embeds;
|
base::flat_map<QByteArray, QByteArray> embeds;
|
||||||
|
|
|
@ -172,6 +172,7 @@ Parser::Parser(const Source &source, const Options &options)
|
||||||
: _options(options)
|
: _options(options)
|
||||||
, _rtl(source.page.data().is_rtl()) {
|
, _rtl(source.page.data().is_rtl()) {
|
||||||
process(source);
|
process(source);
|
||||||
|
_result.title = source.title;
|
||||||
_result.html = prepare(page(source.page.data()));
|
_result.html = prepare(page(source.page.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1003,9 +1004,7 @@ QByteArray Parser::prepare(QByteArray body) {
|
||||||
if (_hasEmbeds) {
|
if (_hasEmbeds) {
|
||||||
js += "IV.initEmbedBlocks();";
|
js += "IV.initEmbedBlocks();";
|
||||||
}
|
}
|
||||||
if (!js.isEmpty()) {
|
body += tag("script", js + "IV.init();");
|
||||||
body += tag("script", js);
|
|
||||||
}
|
|
||||||
return html(head, body);
|
return html(head, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1026,7 +1025,26 @@ QByteArray Parser::html(const QByteArray &head, const QByteArray &body) {
|
||||||
<link rel="stylesheet" href=")" + resource("iv/page.css") + R"(" />
|
<link rel="stylesheet" href=")" + resource("iv/page.css") + R"(" />
|
||||||
)"_q + head + R"(
|
)"_q + head + R"(
|
||||||
</head>
|
</head>
|
||||||
<body>)"_q + body + R"(</body>
|
<body>
|
||||||
|
<button class="fixed_button hidden" id="top_back" onclick="IV.back();">
|
||||||
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17 13L12 18L7 13M12 6L12 17"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="fixed_button" id="top_menu" onclick="IV.menu();">
|
||||||
|
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="8" cy="2.5" r="1.6"></circle>
|
||||||
|
<circle cx="8" cy="8" r="1.6"></circle>
|
||||||
|
<circle cx="8" cy="13.5" r="1.6"></circle>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="fixed_button hidden" id="bottom_up" onclick="IV.toTop();">
|
||||||
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17 13L12 18L7 13M12 6L12 17"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
)"_q + body + R"(
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
)"_q;
|
)"_q;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ struct Source {
|
||||||
MTPPage page;
|
MTPPage page;
|
||||||
std::optional<MTPPhoto> webpagePhoto;
|
std::optional<MTPPhoto> webpagePhoto;
|
||||||
std::optional<MTPDocument> webpageDocument;
|
std::optional<MTPDocument> webpageDocument;
|
||||||
|
QString title;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] Prepared Prepare(
|
[[nodiscard]] Prepared Prepare(
|
||||||
|
|
|
@ -38,4 +38,5 @@ PUBLIC
|
||||||
PRIVATE
|
PRIVATE
|
||||||
desktop-app::lib_webview
|
desktop-app::lib_webview
|
||||||
tdesktop::td_lang
|
tdesktop::td_lang
|
||||||
|
tdesktop::td_ui
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,6 +26,7 @@ set(style_files
|
||||||
info/boosts/giveaway/giveaway.style
|
info/boosts/giveaway/giveaway.style
|
||||||
info/userpic/info_userpic_builder.style
|
info/userpic/info_userpic_builder.style
|
||||||
intro/intro.style
|
intro/intro.style
|
||||||
|
iv/iv.style
|
||||||
media/player/media_player.style
|
media/player/media_player.style
|
||||||
passport/passport.style
|
passport/passport.style
|
||||||
payments/ui/payments.style
|
payments/ui/payments.style
|
||||||
|
|
Loading…
Add table
Reference in a new issue