Add desktop keyboard shortcuts

Signed-off-by: Vitaliy Vlasov <siphiuel@gmail.com>
This commit is contained in:
Vitaliy Vlasov 2018-12-06 22:00:14 +02:00
parent 2e7d89e690
commit c8e5fd6a9c
No known key found for this signature in database
GPG Key ID: A7D57C347F2B2964
14 changed files with 223 additions and 1 deletions

View File

@ -74,6 +74,7 @@
"react-native-desktop-linking" "react-native-desktop-linking"
"react-native-desktop-menu" "react-native-desktop-menu"
"react-native-desktop-config" "react-native-desktop-config"
"react-native-desktop-shortcuts"
"react-native-desktop-notification" "react-native-desktop-notification"
"text-encoding" "text-encoding"
"js-sha3" "js-sha3"

View File

@ -18,6 +18,7 @@
"modules/react-native-desktop-linking/desktop", "modules/react-native-desktop-linking/desktop",
"modules/react-native-desktop-menu/desktop", "modules/react-native-desktop-menu/desktop",
"modules/react-native-desktop-config/desktop", "modules/react-native-desktop-config/desktop",
"modules/react-native-desktop-shortcuts/desktop",
"modules/react-native-desktop-notification/desktop", "modules/react-native-desktop-notification/desktop",
"node_modules/google-breakpad" "node_modules/google-breakpad"
], ],

View File

@ -0,0 +1,9 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_TYPE_NAMES ${REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_TYPE_NAMES}
\"DesktopShortcuts\" PARENT_SCOPE)
set(REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_SRC ${REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_SRC}
${CMAKE_CURRENT_SOURCE_DIR}/desktopshortcuts.cpp PARENT_SCOPE)
include(${CMAKE_ROOT}/Modules/ExternalProject.cmake)

View File

@ -0,0 +1,83 @@
#include "desktopshortcuts.h"
#include "bridge.h"
#include "eventdispatcher.h"
#include <QDebug>
#include <QCoreApplication>
#include <QKeySequence>
#include <QEvent>
#include <QKeyEvent>
Q_LOGGING_CATEGORY(DESKTOPSHORTCUTS, "DesktopShortcuts")
namespace {
struct RegisterQMLMetaType {
RegisterQMLMetaType() { qRegisterMetaType<DesktopShortcuts *>(); }
} registerMetaType;
} // namespace
DesktopShortcuts::DesktopShortcuts(QObject *parent)
: QObject(parent) {
QCoreApplication::instance()->installEventFilter(this);
connect(this, &DesktopShortcuts::shortcutInvoked, this, &DesktopShortcuts::onShortcutInvoked);
}
DesktopShortcuts::~DesktopShortcuts() {
}
void DesktopShortcuts::setBridge(Bridge *bridge) {
this->bridge = bridge;
}
QString DesktopShortcuts::moduleName() { return "DesktopShortcutsManager"; }
QList<ModuleMethod *> DesktopShortcuts::methodsToExport() {
return QList<ModuleMethod *>{};
}
QVariantMap DesktopShortcuts::constantsToExport() { return QVariantMap(); }
void DesktopShortcuts::registerShortcuts(const QStringList& shortcuts) {
//qCDebug(DESKTOPSHORTCUTS) << "registerShortcuts" << shortcuts << " " << shortcuts.size();
this->registeredShortcuts = shortcuts;
}
bool DesktopShortcuts::eventFilter(QObject* obj, QEvent* event) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent* ke = static_cast<QKeyEvent*>(event);
QString modifier;
if (ke->modifiers() & Qt::ShiftModifier) {
modifier += "Shift+";
}
if (ke->modifiers() & Qt::ControlModifier) {
modifier += "Ctrl+";
}
if (ke->modifiers() & Qt::AltModifier) {
modifier += "Alt+";
}
if (ke->modifiers() & Qt::MetaModifier) {
modifier += "Meta+";
}
QString key = QKeySequence(ke->key()).toString();
//qCDebug(DESKTOPSHORTCUTS) << "### arrow " << key;
if (registeredShortcuts.contains(modifier+key)) {
emit shortcutInvoked(modifier+key);
return true;
}
else {
return false;
}
}
else {
return QObject::eventFilter(obj, event);
}
}
void DesktopShortcuts::onShortcutInvoked(const QString& shortcut) {
//qCDebug(DESKTOPSHORTCUTS) << "onShortcutInvoked " << shortcut << " " << registeredShortcuts.size();
bridge->eventDispatcher()->sendDeviceEvent("shortcutInvoked", QVariantList{shortcut});
}

View File

@ -0,0 +1,41 @@
#ifndef DESKTOPSHORTCUTS_H
#define DESKTOPSHORTCUTS_H
#include "moduleinterface.h"
#include <QLoggingCategory>
#include <QMap>
Q_DECLARE_LOGGING_CATEGORY(SHORTCUTS)
class DesktopShortcutsPrivate;
class DesktopShortcuts : public QObject, public ModuleInterface {
Q_OBJECT
Q_INTERFACES(ModuleInterface)
public:
Q_INVOKABLE DesktopShortcuts(QObject* parent = 0);
virtual ~DesktopShortcuts();
void setBridge(Bridge* bridge) override;
QString moduleName() override;
QList<ModuleMethod*> methodsToExport() override;
QVariantMap constantsToExport() override;
Q_INVOKABLE void registerShortcuts(const QStringList& shortcuts);
signals:
void shortcutInvoked(const QString& shortcut);
public slots:
void onShortcutInvoked(const QString& shortcut);
private:
Bridge* bridge;
QStringList registeredShortcuts;
bool eventFilter(QObject* obj, QEvent* event) override;
};
#endif // DESKTOPSHORTCUTS_H

View File

@ -0,0 +1,44 @@
'use strict';
const NativeModules = require('react-native').NativeModules;
const NativeEventEmitter = require('react-native').NativeEventEmitter;
type Shortcuts = Array<{
shortcut?: string,
onPress?: ?Function,
}>;
class DesktopShortcuts {
constructor() {
this.shortcuts = new Map();
this.eventEmitter = new NativeEventEmitter(NativeModules.DesktopShortcutsManager);
this.eventEmitter.addListener('shortcutInvoked', this.handleShortcut.bind(this));
}
handleShortcut(shortcut) {
var fn;// = this.shortcuts.get(shortcut);
for (var [key, value] of this.shortcuts) {
if (shortcut == key) {
fn = value;
break;
}
}
if (fn) {
fn();
};
}
register(shortcuts: Shortcuts): void {
//console.log('### register(shortcuts)' + JSON.stringify(shortcuts));
this.shortcuts = new Map();
var shortcutKeys = shortcuts.map(s => s.shortcut);
for (let i = 0; i < shortcuts.length; ++i) {
this.shortcuts.set(shortcuts[i].shortcut, shortcuts[i].onPress);
}
NativeModules.DesktopShortcutsManager.registerShortcuts(shortcutKeys);
}
}
module.exports = new DesktopShortcuts();

View File

@ -0,0 +1,13 @@
{
"private": true,
"nativePackage": true,
"name": "react-native-desktop-shortcuts",
"version": "1.0.0",
"description": "App-global keyboard shortcuts for Desktop",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": ""
}

View File

@ -15,7 +15,7 @@
(def desktop-linking (.-DesktopLinking (.-NativeModules react-native))) (def desktop-linking (.-DesktopLinking (.-NativeModules react-native)))
(def desktop-menu (js/require "react-native-desktop-menu")) (def desktop-menu (js/require "react-native-desktop-menu"))
(def desktop-config (js/require "react-native-desktop-config")) (def desktop-config (js/require "react-native-desktop-config"))
(def desktop-shortcuts (js/require "react-native-desktop-shortcuts"))
(def react-native-firebase #js {}) (def react-native-firebase #js {})
(def camera #js {:default #js {:constants {:Aspect "Portrait"}}}) (def camera #js {:default #js {:constants {:Aspect "Portrait"}}})
(def status-keycard #js {:default #js {}}) (def status-keycard #js {:default #js {}})

View File

@ -29,3 +29,4 @@
(def desktop-linking #js {:addEventListener (fn [])}) (def desktop-linking #js {:addEventListener (fn [])})
(def desktop-menu #js {:addEventListener (fn [])}) (def desktop-menu #js {:addEventListener (fn [])})
(def desktop-config #js {:addEventListener (fn [])}) (def desktop-config #js {:addEventListener (fn [])})
(def desktop-shortcuts #js {:addEventListener (fn [])})

View File

@ -34,6 +34,7 @@ external_modules_dir=( \
'modules/react-native-desktop-linking/desktop' \ 'modules/react-native-desktop-linking/desktop' \
'modules/react-native-desktop-menu/desktop' \ 'modules/react-native-desktop-menu/desktop' \
'modules/react-native-desktop-config/desktop' \ 'modules/react-native-desktop-config/desktop' \
'modules/react-native-desktop-shortcuts/desktop' \
'modules/react-native-desktop-notification/desktop' \ 'modules/react-native-desktop-notification/desktop' \
) )

View File

@ -7,6 +7,7 @@
status-im.ui.screens.subs status-im.ui.screens.subs
status-im.data-store.core status-im.data-store.core
[reagent.impl.component :as reagent.component] [reagent.impl.component :as reagent.component]
[status-im.ui.components.desktop.shortcuts :as shortcuts]
[status-im.ui.screens.desktop.views :as views] [status-im.ui.screens.desktop.views :as views]
[status-im.core :as core] [status-im.core :as core]
[status-im.desktop.deep-links :as deep-links])) [status-im.desktop.deep-links :as deep-links]))
@ -15,6 +16,7 @@
(reagent/create-class (reagent/create-class
{:component-did-mount (fn [this] {:component-did-mount (fn [this]
(re-frame/dispatch [:set-initial-props (reagent/props this)]) (re-frame/dispatch [:set-initial-props (reagent/props this)])
(shortcuts/register-default-shortcuts)
(deep-links/add-event-listener)) (deep-links/add-event-listener))
:reagent-render (fn [props] :reagent-render (fn [props]
views/main)})) views/main)}))

View File

@ -0,0 +1,23 @@
(ns status-im.ui.components.desktop.shortcuts
(:require [status-im.react-native.js-dependencies :refer [desktop-shortcuts]]
[status-im.ui.screens.desktop.main.tabs.home.views :as chat-list]
[re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.utils.utils :as utils]))
(defn register-shortcut [shortcut on-press]
(.set desktop-shortcuts (clj->js {:shortcut shortcut
:onPress on-press})))
(defn register-default-shortcuts []
(.register desktop-shortcuts
(clj->js (vector
{:shortcut "Ctrl+N"
:onPress #(re-frame/dispatch [:navigate-to :desktop/new-one-to-one])}
{:shortcut "Ctrl+G"
:onPress #(re-frame/dispatch [:navigate-to :desktop/new-group-chat])}
{:shortcut "Ctrl+P"
:onPress #(re-frame/dispatch [:navigate-to :desktop/new-public-chat])}
{:shortcut "Ctrl+F"
:onPress #(utils/show-popup "" "Ctrl+F")}))))

View File

@ -45,6 +45,7 @@
(when show-error-tooltip? (when show-error-tooltip?
[error-tooltip chat-error]) [error-tooltip chat-error])
[react/text-input {:placeholder "name.stateofus.eth" [react/text-input {:placeholder "name.stateofus.eth"
:auto-focus true
:flex 1 :flex 1
:selection-color colors/blue :selection-color colors/blue
:font :default :font :default
@ -108,6 +109,7 @@
[error-tooltip topic-error]) [error-tooltip topic-error])
[react/text-input {:flex 1 [react/text-input {:flex 1
:auto-focus true
:font :default :font :default
:selection-color colors/blue :selection-color colors/blue
:placeholder "" :placeholder ""

View File

@ -33,6 +33,7 @@
(def react-native-firebase #js {:default #js {:notifications #js {:Notification Notification}}}) (def react-native-firebase #js {:default #js {:notifications #js {:Notification Notification}}})
(def desktop-linking #js {:addEventListener (fn [])}) (def desktop-linking #js {:addEventListener (fn [])})
(def desktop-shortcuts #js {:addEventListener (fn [])})
(def snoopy #js {:default #js {}}) (def snoopy #js {:default #js {}})
(def snoopy-filter #js {:default #js {}}) (def snoopy-filter #js {:default #js {}})