mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Handle shortcuts in IV.
This commit is contained in:
parent
6d733bb566
commit
212259aae3
7 changed files with 261 additions and 95 deletions
|
@ -1,86 +1,112 @@
|
|||
var IV = {
|
||||
sendPostMessage: function(data) {
|
||||
try {
|
||||
window.parent.postMessage(JSON.stringify(data), window.parentOrigin);
|
||||
} catch(e) {}
|
||||
},
|
||||
frameClickHandler: function(e) {
|
||||
var target = e.target, href;
|
||||
do {
|
||||
if (target.tagName == 'SUMMARY') return;
|
||||
if (target.tagName == 'DETAILS') return;
|
||||
if (target.tagName == 'LABEL') return;
|
||||
if (target.tagName == 'AUDIO') return;
|
||||
if (target.tagName == 'A') break;
|
||||
} while (target = target.parentNode);
|
||||
if (target && target.hasAttribute('href')) {
|
||||
var base_loc = document.createElement('A');
|
||||
base_loc.href = window.currentUrl;
|
||||
if (base_loc.origin != target.origin ||
|
||||
base_loc.pathname != target.pathname ||
|
||||
base_loc.search != target.search) {
|
||||
IV.sendPostMessage({event: 'link_click', url: target.href});
|
||||
}
|
||||
}
|
||||
e.preventDefault();
|
||||
},
|
||||
postMessageHandler: function(event) {
|
||||
if (event.source !== window.parent ||
|
||||
event.origin != window.parentOrigin) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var data = JSON.parse(event.data);
|
||||
} catch(e) {
|
||||
var data = {};
|
||||
}
|
||||
},
|
||||
slideshowSlide: function(el, next) {
|
||||
var dir = window.getComputedStyle(el, null).direction || 'ltr';
|
||||
var marginProp = dir == 'rtl' ? 'marginRight' : 'marginLeft';
|
||||
if (next) {
|
||||
var s = el.previousSibling.s;
|
||||
s.value = (+s.value + 1 == s.length) ? 0 : +s.value + 1;
|
||||
s.forEach(function(el){ el.checked && el.parentNode.scrollIntoView && el.parentNode.scrollIntoView({behavior: 'smooth', block: 'center', inline: 'center'}); });
|
||||
el.firstChild.style[marginProp] = (-100 * s.value) + '%';
|
||||
} else {
|
||||
el.form.nextSibling.firstChild.style[marginProp] = (-100 * el.value) + '%';
|
||||
}
|
||||
return false;
|
||||
},
|
||||
initPreBlocks: function() {
|
||||
if (!hljs) return;
|
||||
var pres = document.getElementsByTagName('pre');
|
||||
for (var i = 0; i < pres.length; i++) {
|
||||
if (pres[i].hasAttribute('data-language')) {
|
||||
hljs.highlightBlock(pres[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
initEmbedBlocks: function() {
|
||||
var iframes = document.getElementsByTagName('iframe');
|
||||
for (var i = 0; i < iframes.length; i++) {
|
||||
(function(iframe) {
|
||||
window.addEventListener('message', function(event) {
|
||||
if (event.source !== iframe.contentWindow ||
|
||||
event.origin != window.origin) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var data = JSON.parse(event.data);
|
||||
} catch(e) {
|
||||
var data = {};
|
||||
}
|
||||
if (data.eventType == 'resize_frame') {
|
||||
if (data.eventData.height) {
|
||||
iframe.style.height = data.eventData.height + 'px';
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
})(iframes[i]);
|
||||
}
|
||||
}
|
||||
notify: function(message) {
|
||||
if (window.external && window.external.invoke) {
|
||||
window.external.invoke(JSON.stringify(message));
|
||||
}
|
||||
},
|
||||
frameClickHandler: function(e) {
|
||||
var target = e.target, href;
|
||||
do {
|
||||
if (target.tagName == 'SUMMARY') return;
|
||||
if (target.tagName == 'DETAILS') return;
|
||||
if (target.tagName == 'LABEL') return;
|
||||
if (target.tagName == 'AUDIO') return;
|
||||
if (target.tagName == 'A') break;
|
||||
} while (target = target.parentNode);
|
||||
if (target && target.hasAttribute('href')) {
|
||||
var base_loc = document.createElement('A');
|
||||
base_loc.href = window.currentUrl;
|
||||
if (base_loc.origin != target.origin ||
|
||||
base_loc.pathname != target.pathname ||
|
||||
base_loc.search != target.search) {
|
||||
IV.notify({ event: 'link_click', url: target.href });
|
||||
}
|
||||
}
|
||||
e.preventDefault();
|
||||
},
|
||||
frameKeyDown: function (e) {
|
||||
let keyW = (e.key === 'w')
|
||||
|| (e.code === 'KeyW')
|
||||
|| (e.keyCode === 87);
|
||||
let keyQ = (e.key === 'q')
|
||||
|| (e.code === 'KeyQ')
|
||||
|| (e.keyCode === 81);
|
||||
let keyM = (e.key === 'm')
|
||||
|| (e.code === 'KeyM')
|
||||
|| (e.keyCode === 77);
|
||||
if ((e.metaKey || e.ctrlKey) && (keyW || keyQ || keyM)) {
|
||||
e.preventDefault();
|
||||
IV.notify({
|
||||
event: 'keydown',
|
||||
modifier: e.ctrlKey ? 'ctrl' : 'cmd',
|
||||
key: keyW ? 'w' : keyQ ? 'q' : 'm',
|
||||
});
|
||||
} else if (e.key === 'Escape' || e.keyCode === 27) {
|
||||
e.preventDefault();
|
||||
IV.notify({
|
||||
event: 'keydown',
|
||||
key: 'escape',
|
||||
});
|
||||
}
|
||||
},
|
||||
postMessageHandler: function(event) {
|
||||
if (event.source !== window.parent ||
|
||||
event.origin != window.parentOrigin) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var data = JSON.parse(event.data);
|
||||
} catch(e) {
|
||||
var data = {};
|
||||
}
|
||||
},
|
||||
slideshowSlide: function(el, next) {
|
||||
var dir = window.getComputedStyle(el, null).direction || 'ltr';
|
||||
var marginProp = dir == 'rtl' ? 'marginRight' : 'marginLeft';
|
||||
if (next) {
|
||||
var s = el.previousSibling.s;
|
||||
s.value = (+s.value + 1 == s.length) ? 0 : +s.value + 1;
|
||||
s.forEach(function(el){ el.checked && el.parentNode.scrollIntoView && el.parentNode.scrollIntoView({behavior: 'smooth', block: 'center', inline: 'center'}); });
|
||||
el.firstChild.style[marginProp] = (-100 * s.value) + '%';
|
||||
} else {
|
||||
el.form.nextSibling.firstChild.style[marginProp] = (-100 * el.value) + '%';
|
||||
}
|
||||
return false;
|
||||
},
|
||||
initPreBlocks: function() {
|
||||
if (!hljs) return;
|
||||
var pres = document.getElementsByTagName('pre');
|
||||
for (var i = 0; i < pres.length; i++) {
|
||||
if (pres[i].hasAttribute('data-language')) {
|
||||
hljs.highlightBlock(pres[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
initEmbedBlocks: function() {
|
||||
var iframes = document.getElementsByTagName('iframe');
|
||||
for (var i = 0; i < iframes.length; i++) {
|
||||
(function(iframe) {
|
||||
window.addEventListener('message', function(event) {
|
||||
if (event.source !== iframe.contentWindow ||
|
||||
event.origin != window.origin) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var data = JSON.parse(event.data);
|
||||
} catch(e) {
|
||||
var data = {};
|
||||
}
|
||||
if (data.eventType == 'resize_frame') {
|
||||
if (data.eventData.height) {
|
||||
iframe.style.height = data.eventData.height + 'px';
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
})(iframes[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.onclick = IV.frameClickHandler;
|
||||
document.onkeydown = IV.frameKeyDown;
|
||||
window.onmessage = IV.postMessageHandler;
|
||||
|
|
|
@ -1547,12 +1547,12 @@ bool Application::closeActiveWindow() {
|
|||
if (_mediaView && _mediaView->isActive()) {
|
||||
_mediaView->close();
|
||||
return true;
|
||||
} else if (!calls().closeCurrentActiveCall()) {
|
||||
if (const auto window = activeWindow()) {
|
||||
if (window->widget()->isActive()) {
|
||||
window->close();
|
||||
return true;
|
||||
}
|
||||
} else if (_iv->closeActive() || calls().closeCurrentActiveCall()) {
|
||||
return true;
|
||||
} else if (const auto window = activeWindow()) {
|
||||
if (window->widget()->isActive()) {
|
||||
window->close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -1562,7 +1562,8 @@ bool Application::minimizeActiveWindow() {
|
|||
if (_mediaView && _mediaView->isActive()) {
|
||||
_mediaView->minimize();
|
||||
return true;
|
||||
} else if (calls().minimizeCurrentActiveCall()) {
|
||||
} else if (_iv->minimizeActive()
|
||||
|| calls().minimizeCurrentActiveCall()) {
|
||||
return true;
|
||||
} else {
|
||||
if (const auto window = activeWindow()) {
|
||||
|
|
|
@ -7,14 +7,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "iv/iv_controller.h"
|
||||
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "iv/iv_data.h"
|
||||
#include "ui/widgets/rp_window.h"
|
||||
#include "webview/webview_data_stream_memory.h"
|
||||
#include "webview/webview_embed.h"
|
||||
#include "webview/webview_interface.h"
|
||||
#include "styles/palette.h"
|
||||
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonValue>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtGui/QPainter>
|
||||
|
||||
namespace Iv {
|
||||
|
||||
|
@ -47,22 +53,56 @@ void Controller::show(const QString &dataPath, Prepared page) {
|
|||
_webview = nullptr;
|
||||
});
|
||||
if (!raw->widget()) {
|
||||
_webview = nullptr;
|
||||
_window = nullptr;
|
||||
_events.fire(Event::Close);
|
||||
return;
|
||||
}
|
||||
window->events(
|
||||
) | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||
if (e->type() == QEvent::Close) {
|
||||
close();
|
||||
} else if (e->type() == QEvent::KeyPress) {
|
||||
const auto event = static_cast<QKeyEvent*>(e.get());
|
||||
if (event->key() == Qt::Key_Escape) {
|
||||
escape();
|
||||
}
|
||||
}
|
||||
}, window->lifetime());
|
||||
raw->widget()->show();
|
||||
|
||||
container->geometryValue(
|
||||
) | rpl::start_with_next([=](QRect geometry) {
|
||||
raw->widget()->setGeometry(geometry);
|
||||
}, _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) {
|
||||
return true;
|
||||
});
|
||||
raw->setNavigationDoneHandler([=](bool success) {
|
||||
});
|
||||
raw->setMessageHandler([=](const QJsonDocument &message) {
|
||||
crl::on_main(_window.get(), [=] {
|
||||
const auto object = message.object();
|
||||
const auto event = object.value("event").toString();
|
||||
if (event == u"keydown"_q) {
|
||||
const auto key = object.value("key").toString();
|
||||
const auto modifier = object.value("modifier").toString();
|
||||
const auto ctrl = Platform::IsMac() ? u"cmd"_q : u"ctrl"_q;
|
||||
if (key == u"escape"_q) {
|
||||
escape();
|
||||
} else if (key == u"w"_q && modifier == ctrl) {
|
||||
close();
|
||||
} else if (key == u"m"_q && modifier == ctrl) {
|
||||
minimize();
|
||||
} else if (key == u"q"_q && modifier == ctrl) {
|
||||
quit();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
raw->setDataRequestHandler([=](Webview::DataRequest request) {
|
||||
if (!request.id.starts_with("iv/")) {
|
||||
_dataRequests.fire(std::move(request));
|
||||
|
@ -104,8 +144,27 @@ void Controller::show(const QString &dataPath, Prepared page) {
|
|||
window->show();
|
||||
}
|
||||
|
||||
rpl::producer<Webview::DataRequest> Controller::dataRequests() const {
|
||||
return _dataRequests.events();
|
||||
bool Controller::active() const {
|
||||
return _window && _window->isActiveWindow();
|
||||
}
|
||||
|
||||
void Controller::minimize() {
|
||||
if (_window) {
|
||||
_window->setWindowState(_window->windowState()
|
||||
| Qt::WindowMinimized);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::escape() {
|
||||
close();
|
||||
}
|
||||
|
||||
void Controller::close() {
|
||||
_events.fire(Event::Close);
|
||||
}
|
||||
|
||||
void Controller::quit() {
|
||||
_events.fire(Event::Quit);
|
||||
}
|
||||
|
||||
rpl::lifetime &Controller::lifetime() {
|
||||
|
|
|
@ -25,16 +25,34 @@ public:
|
|||
Controller();
|
||||
~Controller();
|
||||
|
||||
void show(const QString &dataPath, Prepared page);
|
||||
enum class Event {
|
||||
Close,
|
||||
Quit,
|
||||
};
|
||||
|
||||
[[nodiscard]] rpl::producer<Webview::DataRequest> dataRequests() const;
|
||||
void show(const QString &dataPath, Prepared page);
|
||||
[[nodiscard]] bool active() const;
|
||||
void minimize();
|
||||
|
||||
[[nodiscard]] rpl::producer<Webview::DataRequest> dataRequests() const {
|
||||
return _dataRequests.events();
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<Event> events() const {
|
||||
return _events.events();
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime();
|
||||
|
||||
private:
|
||||
void escape();
|
||||
void close();
|
||||
void quit();
|
||||
|
||||
std::unique_ptr<Ui::RpWindow> _window;
|
||||
std::unique_ptr<Webview::Window> _webview;
|
||||
rpl::event_stream<Webview::DataRequest> _dataRequests;
|
||||
rpl::event_stream<Event> _events;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "iv/iv_instance.h"
|
||||
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "data/data_cloud_file.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_file_origin.h"
|
||||
|
@ -63,6 +64,17 @@ public:
|
|||
not_null<Data*> data) const;
|
||||
[[nodiscard]] bool showingFrom(not_null<Main::Session*> session) const;
|
||||
[[nodiscard]] bool activeFor(not_null<Main::Session*> session) const;
|
||||
[[nodiscard]] bool active() const;
|
||||
|
||||
void minimize();
|
||||
|
||||
[[nodiscard]] rpl::producer<Controller::Event> events() const {
|
||||
return _events.events();
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
||||
private:
|
||||
struct MapPreview {
|
||||
|
@ -131,6 +143,8 @@ private:
|
|||
std::vector<QByteArray> _resources;
|
||||
int _resource = -1;
|
||||
|
||||
rpl::event_stream<Controller::Event> _events;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
@ -358,6 +372,10 @@ void Shown::writeEmbed(QString id, QString hash) {
|
|||
|
||||
void Shown::showWindowed(Prepared result) {
|
||||
_controller = std::make_unique<Controller>();
|
||||
|
||||
_controller->events(
|
||||
) | rpl::start_to_stream(_events, _controller->lifetime());
|
||||
|
||||
_controller->dataRequests(
|
||||
) | rpl::start_with_next([=](Webview::DataRequest request) {
|
||||
const auto requested = QString::fromStdString(request.id);
|
||||
|
@ -372,6 +390,7 @@ void Shown::showWindowed(Prepared result) {
|
|||
sendEmbed(id.mid(5).toUtf8(), std::move(request));
|
||||
}
|
||||
}, _controller->lifetime());
|
||||
|
||||
const auto domain = &_session->domain();
|
||||
_controller->show(domain->local().webviewDataPath(), std::move(result));
|
||||
}
|
||||
|
@ -628,6 +647,16 @@ bool Shown::activeFor(not_null<Main::Session*> session) const {
|
|||
return showingFrom(session) && _controller;
|
||||
}
|
||||
|
||||
bool Shown::active() const {
|
||||
return _controller && _controller->active();
|
||||
}
|
||||
|
||||
void Shown::minimize() {
|
||||
if (_controller) {
|
||||
_controller->minimize();
|
||||
}
|
||||
}
|
||||
|
||||
Instance::Instance() = default;
|
||||
|
||||
Instance::~Instance() = default;
|
||||
|
@ -641,6 +670,14 @@ void Instance::show(
|
|||
return;
|
||||
}
|
||||
_shown = std::make_unique<Shown>(show, data, local);
|
||||
_shown->events() | rpl::start_with_next([=](Controller::Event event) {
|
||||
if (event == Controller::Event::Close) {
|
||||
_shown = nullptr;
|
||||
} else if (event == Controller::Event::Quit) {
|
||||
Shortcuts::Launch(Shortcuts::Command::Quit);
|
||||
}
|
||||
}, _shown->lifetime());
|
||||
|
||||
if (!_tracking.contains(session)) {
|
||||
_tracking.emplace(session);
|
||||
session->lifetime().add([=] {
|
||||
|
@ -656,6 +693,22 @@ bool Instance::hasActiveWindow(not_null<Main::Session*> session) const {
|
|||
return _shown && _shown->activeFor(session);
|
||||
}
|
||||
|
||||
bool Instance::closeActive() {
|
||||
if (!_shown || !_shown->active()) {
|
||||
return false;
|
||||
}
|
||||
_shown = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Instance::minimizeActive() {
|
||||
if (!_shown || !_shown->active()) {
|
||||
return false;
|
||||
}
|
||||
_shown->minimize();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Instance::closeAll() {
|
||||
_shown = nullptr;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@ public:
|
|||
[[nodiscard]] bool hasActiveWindow(
|
||||
not_null<Main::Session*> session) const;
|
||||
|
||||
bool closeActive();
|
||||
bool minimizeActive();
|
||||
|
||||
void closeAll();
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime();
|
||||
|
|
|
@ -20,6 +20,12 @@ PRIVATE
|
|||
iv/iv_prepare.h
|
||||
)
|
||||
|
||||
nice_target_sources(td_iv ${res_loc}
|
||||
PRIVATE
|
||||
iv_html/page.css
|
||||
iv_html/page.js
|
||||
)
|
||||
|
||||
target_include_directories(td_iv
|
||||
PUBLIC
|
||||
${src_loc}
|
||||
|
|
Loading…
Add table
Reference in a new issue