feat!: register shortcuts in the separate category in the System Settings

This makes the shortcuts appear in the separate category in the System
Settings. Note, that we do not delete Action.ts just yet. We need to
extract the layouts logic into C++ to do that, since some shortcuts
actions depend on the current layout.
This commit is contained in:
Mikhail Zolotukhin 2022-02-09 18:37:48 +03:00
parent b3d4296e92
commit 1a49257180
14 changed files with 149 additions and 35 deletions

View File

@ -68,6 +68,7 @@ find_package(
I18n
KCMUtils
Declarative
GlobalAccel
Config
)

View File

@ -12,6 +12,8 @@ target_sources(
bismuth_core
PRIVATE
qml-plugin.cpp
ts-proxy.cpp
controller.cpp
qmldir
)
@ -24,6 +26,7 @@ target_link_libraries(
KF5::ConfigCore
KF5::ConfigGui
KF5::GlobalAccel
KF5::I18n
)
target_compile_definitions(

50
src/core/controller.cpp Normal file
View File

@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: 2022 Mikhail Zolotukhin <mail@genda.life>
//
// SPDX-License-Identifier: MIT
#include "controller.hpp"
#include <KGlobalAccel>
#include <KLocalizedString>
#include <QAction>
#include <QDebug>
#include <QObject>
#include <memory>
namespace Bismuth
{
void Controller::registerAction(const Action &data)
{
auto action = new QAction(this);
action->setProperty("componentName", QStringLiteral("bismuth"));
action->setProperty("componentDisplayName", i18n("Window Tiling"));
action->setObjectName(data.id);
action->setText(data.description);
// Register the keybinding as the default. This is needed for KCM to
// recognize it as such, so that it can properly show whether it is changed
// from the default.
KGlobalAccel::self()->setDefaultShortcut(action, data.defaultKeybinding);
// How this function works:
// Set the shortcut from the global shortcuts configuration, or set it to
// the provided value if it is not found in the config
KGlobalAccel::self()->setShortcut(action, data.defaultKeybinding);
QObject::connect(action, &QAction::triggered, data.callback);
m_registeredShortcuts.push_back(action);
};
Action::Action(const QString &id, const QString &description, const QString &defaultKeybinding, std::function<void()> callback)
{
this->id = id;
this->description = description;
this->defaultKeybinding = {QKeySequence(defaultKeybinding)};
this->callback = callback;
};
}

36
src/core/controller.hpp Normal file
View File

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: 2022 Mikhail Zolotukhin <mail@genda.life>
// SPDX-License-Identifier: MIT
#pragma once
#include <QAction>
#include <QKeySequence>
#include <QList>
#include <functional>
#include <memory>
#include <vector>
namespace Bismuth
{
struct Action {
Action(const QString &id, const QString &description, const QString &defaultKeybinding, std::function<void()> callback);
QString id;
QString description;
QList<QKeySequence> defaultKeybinding;
std::function<void()> callback;
};
class Controller : public QObject
{
Q_OBJECT
public:
void registerAction(const Action &);
private:
std::vector<QAction *> m_registeredShortcuts{};
};
}

View File

@ -10,6 +10,7 @@
#include <memory>
#include "config.hpp"
#include "controller.hpp"
#include "plasma-api/plasma-api.hpp"
#include "ts-proxy.hpp"
@ -24,9 +25,10 @@ void CorePlugin::registerTypes(const char *uri)
Core::Core(QQuickItem *parent)
: QQuickItem(parent)
, m_engine() // We cannot get engine from the pointer in the constructor
, m_plasmaApi()
, m_controller()
, m_tsProxy()
, m_config(std::make_unique<Bismuth::Config>())
, m_plasmaApi()
{
}
@ -37,7 +39,8 @@ void Core::init()
qDebug() << "[Bismuth] Core QmlEngine ptr: " << m_engine;
}
m_plasmaApi = std::make_unique<PlasmaApi::PlasmaApi>(m_engine);
m_tsProxy = std::make_unique<TSProxy>(m_engine, *m_config);
m_controller = std::make_unique<Bismuth::Controller>();
m_tsProxy = std::make_unique<TSProxy>(m_engine, *m_controller, *m_config);
}
TSProxy *Core::tsProxy() const

View File

@ -12,6 +12,7 @@
#include <memory>
#include "config.hpp"
#include "controller.hpp"
#include "plasma-api/plasma-api.hpp"
#include "ts-proxy.hpp"
@ -45,6 +46,7 @@ public:
private:
QQmlEngine *m_engine; ///< Pointer to the engine, that is currently using the Core element
std::unique_ptr<Bismuth::Controller> m_controller; ///< Legacy TS Backend proxy
std::unique_ptr<TSProxy> m_tsProxy; ///< Legacy TS Backend proxy
std::unique_ptr<Bismuth::Config> m_config;
std::unique_ptr<PlasmaApi::PlasmaApi> m_plasmaApi;

View File

@ -2,11 +2,18 @@
// SPDX-License-Identifier: MIT
#include "ts-proxy.hpp"
#include "controller.hpp"
TSProxy::TSProxy(QQmlEngine *engine, Bismuth::Config &config)
#include <KGlobalAccel>
#include <KLocalizedString>
#include <QAction>
#include <QKeySequence>
TSProxy::TSProxy(QQmlEngine *engine, Bismuth::Controller &controller, Bismuth::Config &config)
: QObject()
, m_engine(engine)
, m_config(config)
, m_controller(controller)
{
}
@ -98,3 +105,19 @@ QJSValue TSProxy::jsConfig()
return configJSObject;
}
void TSProxy::registerShortcut(const QJSValue &tsAction)
{
auto id = tsAction.property("key").toString();
auto desk = tsAction.property("description").toString();
auto keybinding = tsAction.property("defaultKeybinding").toString();
qDebug() << "[Bismuth] SK: { id:" << id << ", description:" << desk << ", keybind:" << keybinding << "};";
// NOTE: Lambda MUST capture by copy, otherwise it is an undefined behavior
m_controller.registerAction({id, desk, keybinding, [=]() {
auto callback = tsAction.property("execute");
qDebug() << "Shortcut triggered! Id:" << id;
callback.callWithInstance(tsAction);
}});
}

View File

@ -8,6 +8,7 @@
#include <QQmlEngine>
#include "config.hpp"
#include "controller.hpp"
/**
* Proxy object for the legacy TS backend.
@ -16,14 +17,21 @@ class TSProxy : public QObject
{
Q_OBJECT
public:
explicit TSProxy(QQmlEngine *, Bismuth::Config &);
explicit TSProxy(QQmlEngine *, Bismuth::Controller &, Bismuth::Config &);
/**
* Returns the config usable in the legacy TypeScript logic
*/
Q_INVOKABLE QJSValue jsConfig();
/**
* Register the actions from the legacy backend
* @param tsaction
*/
Q_INVOKABLE void registerShortcut(const QJSValue &);
private:
QQmlEngine *m_engine;
Bismuth::Config &m_config;
Bismuth::Controller &m_controller;
};

View File

@ -53,10 +53,7 @@ abstract class ActionImpl implements Action {
public description: string,
public defaultKeybinding: string,
protected log: Log
) {
this.key = `bismuth_${this.key}`;
this.description = `Bismuth: ${this.description}`;
}
) {}
/**
* Action execution pattern. Executes the action override optionally

View File

@ -14,6 +14,7 @@ import { Config } from "../config";
import { Log } from "../util/log";
import * as Action from "./action";
import { TSProxy } from "../extern/proxy";
/**
* Entry point of the script (apart from QML). Handles the user input (shortcuts)
@ -161,7 +162,8 @@ export class ControllerImpl implements Controller {
private qmlObjects: Bismuth.Qml.Main,
kwinApi: KWin.Api,
private config: Config,
private log: Log
private log: Log,
private proxy: TSProxy
) {
this.engine = new EngineImpl(this, config, log);
this.driver = new DriverImpl(qmlObjects, kwinApi, this, config, log);
@ -441,7 +443,7 @@ export class ControllerImpl implements Controller {
];
for (const action of allPossibleActions) {
this.driver.bindShortcut(action);
this.proxy.registerShortcut(action);
}
}
}

View File

@ -8,7 +8,6 @@ import { DriverSurfaceImpl } from "./surface";
import { DriverWindowImpl } from "./window";
import { Controller } from "../controller";
import { Action } from "../controller/action";
import { EngineWindow, EngineWindowImpl } from "../engine/window";
@ -50,12 +49,6 @@ export interface Driver {
*/
bindEvents(): void;
/**
* Bind the shortcut for the action
* @param action
*/
bindShortcut(action: Action): void;
/**
* Manage the windows, that were active before script loading
*/
@ -321,17 +314,6 @@ export class DriverImpl implements Driver {
this.qml.popupDialog.show(text, icon, hint);
}
public bindShortcut(action: Action): void {
this.kwinApi.KWin.registerShortcut(
action.key,
action.description,
action.defaultKeybinding,
(): void => {
this.enter(() => action.execute());
}
);
}
public drop(): void {
this.log.log(`Dropping all registered callbacks... Goodbye.`);
for (const pair of this.registeredConnections) {

View File

@ -328,7 +328,7 @@ export class EngineWindowImpl implements EngineWindow {
public commit(): void {
const state = this.state;
this.log.log(["Window#commit", { state: WindowState[state] }]);
// this.log.log(["Window#commit", { state: WindowState[state] }]);
switch (state) {
case WindowState.NativeMaximized:
this.window.commit(

10
src/kwinscript/extern/proxy.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2022 Mikhail Zolotukhin <mail@genda.life>
// SPDX-License-Identifier: MIT
import { Config } from "../config";
import { Action } from "../controller/action";
export interface TSProxy {
jsConfig(): Config;
registerShortcut(data: Action): void;
}

View File

@ -2,14 +2,10 @@
//
// SPDX-License-Identifier: MIT
import { Config } from "./config";
import { Controller, ControllerImpl } from "./controller";
import { TSProxy } from "./extern/proxy";
import { LogImpl } from "./util/log";
interface TSProxy {
jsConfig(): Config;
}
/**
* Script entry-point from QML side.
* @param qmlObjects objects from QML gui. Required for the interaction with QML, as we cannot access globals.
@ -30,7 +26,8 @@ export function init(
qmlObjects,
kwinScriptingApi,
config,
logger
logger,
proxy
);
controller.start();