kate-deft/src/radial/radialconfig.cpp

134 lines
4.7 KiB
C++

/*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "radialconfig.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QJsonValue>
namespace katecustom
{
namespace
{
// Recursively parse a slice node. A node with a non-empty "slices" array is a
// branch; otherwise it is a leaf bound to "action".
RadialNode parseNode(const QJsonObject &obj)
{
RadialNode node;
node.label = obj.value(QStringLiteral("label")).toString();
node.actionId = obj.value(QStringLiteral("action")).toString();
const QJsonArray slices = obj.value(QStringLiteral("slices")).toArray();
for (const QJsonValue &v : slices) {
if (v.isObject()) {
node.children.push_back(parseNode(v.toObject()));
}
}
// A branch must not also act as a leaf; the children take precedence.
if (!node.children.isEmpty()) {
node.actionId.clear();
}
return node;
}
} // namespace
QList<RadialDef> RadialConfig::fromJsonObject(const QJsonObject &obj)
{
QList<RadialDef> defs;
const QJsonArray radials = obj.value(QStringLiteral("radials")).toArray();
for (const QJsonValue &rv : radials) {
if (!rv.isObject()) {
continue;
}
const QJsonObject robj = rv.toObject();
RadialDef def;
def.name = robj.value(QStringLiteral("name")).toString();
def.trigger = robj.value(QStringLiteral("trigger")).toString();
def.mouseButton = robj.value(QStringLiteral("button")).toString();
const QJsonArray slices = robj.value(QStringLiteral("slices")).toArray();
for (const QJsonValue &sv : slices) {
if (sv.isObject()) {
def.root.children.push_back(parseNode(sv.toObject()));
}
}
// A radial with no slices is useless; skip it.
if (!def.root.children.isEmpty()) {
defs.push_back(def);
}
}
return defs;
}
QList<RadialDef> RadialConfig::fromJson(const QByteArray &json, QString *error)
{
QJsonParseError perr{};
const QJsonDocument doc = QJsonDocument::fromJson(json, &perr);
if (perr.error != QJsonParseError::NoError || !doc.isObject()) {
if (error) {
*error = perr.error != QJsonParseError::NoError
? perr.errorString()
: QStringLiteral("top-level JSON value is not an object");
}
return {};
}
return fromJsonObject(doc.object());
}
QList<RadialDef> RadialConfig::builtinDefault()
{
// One editing radial, triggered by Alt+R, built from common Kate actions.
// Leaf ids are Kate action objectNames; a trailing branch groups more.
auto leaf = [](const QString &label, const QString &action) {
RadialNode n;
n.label = label;
n.actionId = action;
return n;
};
RadialDef edit;
edit.name = QStringLiteral("edit");
edit.trigger = QStringLiteral("Alt+R");
edit.mouseButton = QStringLiteral("RightButton"); // natural radial gesture
// The radial holds actions that complete IN PLACE — no panel that pulls
// attention elsewhere. Panel-opening actions (Find, Replace) deliberately
// live in the command palette / keybinds instead, so the radial preserves
// editing flow. The "Command Palette…" slice is the escape hatch to those.
RadialNode more;
more.label = QStringLiteral("More");
more.children = {
leaf(QStringLiteral("Comment"), QStringLiteral("comment")),
leaf(QStringLiteral("Uncomment"), QStringLiteral("uncomment")),
leaf(QStringLiteral("Uppercase"), QStringLiteral("uppercase")),
leaf(QStringLiteral("Lowercase"), QStringLiteral("lowercase")),
leaf(QStringLiteral("Join Lines"), QStringLiteral("joinLines")),
// Opens the full command palette (handled specially by the plugin).
leaf(QStringLiteral("Command Palette…"), QStringLiteral("ollie_command_palette")),
};
edit.root.children = {
leaf(QStringLiteral("Cut"), QStringLiteral("edit_cut")),
leaf(QStringLiteral("Copy"), QStringLiteral("edit_copy")),
leaf(QStringLiteral("Paste"), QStringLiteral("edit_paste")),
leaf(QStringLiteral("Undo"), QStringLiteral("edit_undo")),
leaf(QStringLiteral("Redo"), QStringLiteral("edit_redo")),
// Multi-cursor on every selected line: a quintessential in-place action
// (select lines → gesture → keep typing). Referenced by visible text so
// it resolves regardless of the Kate action's objectName.
leaf(QStringLiteral("Cursors to Line Ends"),
QStringLiteral("Add Cursors to Line Ends")),
more,
};
return {edit};
}
} // namespace katecustom