[education/rkward] rkward: Finish frontend implementaiton of rk.menu()
Thomas Friedrichsmeier
null at kde.org
Sun Sep 8 20:42:17 BST 2024
Git commit f9a21d5c958204761a334f1570067760920c769e by Thomas Friedrichsmeier.
Committed on 08/08/2024 at 19:54.
Pushed by tfry into branch 'master'.
Finish frontend implementaiton of rk.menu()
M +30 -0 rkward/autotests/core_test.cpp
M +26 -1 rkward/misc/rkrapimenu.cpp
M +5 -0 rkward/misc/rkrapimenu.h
M +5 -0 rkward/rbackend/rkrinterface.cpp
M +2 -1 rkward/rbackend/rpackages/rkward/R/rk.menu.R
M +4 -2 rkward/rbackend/rpackages/rkward/man/rk.menu.Rd
https://invent.kde.org/education/rkward/-/commit/f9a21d5c958204761a334f1570067760920c769e
diff --git a/rkward/autotests/core_test.cpp b/rkward/autotests/core_test.cpp
index d836daade..645a40394 100644
--- a/rkward/autotests/core_test.cpp
+++ b/rkward/autotests/core_test.cpp
@@ -33,6 +33,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include "../misc/rkcommonfunctions.h"
#include "../misc/rkcommandlineargs.h"
#include "../misc/rkxmlguipreviewarea.h"
+#include "../misc/rkrapimenu.h"
#include "../settings/rksettings.h"
#include "../settings/rksettingsmodulekateplugins.h"
#include "../windows/katepluginintegration.h"
@@ -530,6 +531,35 @@ private Q_SLOTS:
RKWardMainWindow::getMain()->slotCloseAllEditors();
}
+ void rkMenuTest() {
+ const QStringList actionpath {"analysis", "myaction"};
+ RInterface::issueCommand(new RCommand("a <- rk.menu()", RCommand::App));
+ for (int i = 0; i < actionpath.size(); ++i) {
+ RInterface::issueCommand(new RCommand("a <- a$item(" + RObject::rQuote(actionpath[i]) + ")", RCommand::App));
+ }
+ RInterface::issueCommand(new RCommand("a$define('My Label', function() assign('x', 'actionval', envir=globalenv()))", RCommand::User));
+ waitForAllFinished();
+ auto m = RKWardMainWindow::getMain()->rApiMenu();
+ auto a = m->actionByPath(actionpath);
+ QVERIFY(a);
+ QVERIFY(a && a->text()=="My Label");
+ QVERIFY(a && a->isEnabled());
+ if (a) a->trigger();
+ RInterface::issueCommand(new RCommand("a$enable(FALSE)", RCommand::User));
+ runCommandAsync(new RCommand("stopifnot(x == 'actionval')", RCommand::App), nullptr, [](RCommand *c) {
+ QVERIFY(c->succeeded());
+ });
+ waitForAllFinished();
+ auto b = m->actionByPath(actionpath);
+ QVERIFY(b);
+ QVERIFY(b == a); // existing action should have been reused
+ QVERIFY(b && (!b->isEnabled()));
+ RInterface::issueCommand(new RCommand("a$remove()", RCommand::App));
+ RInterface::issueCommand(new RCommand("rm(x)", RCommand::App));
+ waitForAllFinished();
+ QVERIFY(!m->actionByPath(actionpath));
+ }
+
void restartRBackend() {
RInterface::issueCommand(new RCommand("setwd(tempdir())", RCommand::User)); // retart used to fail, if in non-existant directory
RInterface::issueCommand(new RCommand("x <- 1", RCommand::User));
diff --git a/rkward/misc/rkrapimenu.cpp b/rkward/misc/rkrapimenu.cpp
index 8e8b9ee52..e49b2b3d8 100644
--- a/rkward/misc/rkrapimenu.cpp
+++ b/rkward/misc/rkrapimenu.cpp
@@ -10,6 +10,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include <QDomElement>
#include <QDomDocument>
#include <QAction>
+#include <QTimer>
#include <KActionCollection>
#include <KXMLGUIFactory>
@@ -68,7 +69,8 @@ void RKRApiMenu::makeXML(QDomDocument &doc, QDomElement e, const QVariantList &l
}
}
-void RKRApiMenu::updateFromR(const QVariantList &rep) {
+void RKRApiMenu::commit() {
+ if (rep.isEmpty()) return;
RK_TRACE(MISC);
auto f = RKWardMainWindow::getMain()->factory();
@@ -95,4 +97,27 @@ void RKRApiMenu::updateFromR(const QVariantList &rep) {
}
f->addClient(this);
+ rep = QVariantList();
+}
+
+void RKRApiMenu::updateFromR(const QVariantList &_rep) {
+ RK_TRACE(MISC);
+
+ // Actual update is done debounced
+ if (rep.isEmpty()) QTimer::singleShot(100, factory(), [this](){ commit(); });
+ rep = _rep;
+}
+
+QAction *RKRApiMenu::actionByPath(const QStringList &path) {
+ commit(); // force commit before lookup
+ return action(path.join(','));
+}
+
+void RKRApiMenu::enableAction(const QStringList &path, bool enable, bool show) {
+ RK_TRACE(MISC);
+ auto a = actionByPath(path);
+ if (a) {
+ a->setEnabled(enable);
+ a->setVisible(show);
+ }
}
diff --git a/rkward/misc/rkrapimenu.h b/rkward/misc/rkrapimenu.h
index 35bed97ab..862e74749 100644
--- a/rkward/misc/rkrapimenu.h
+++ b/rkward/misc/rkrapimenu.h
@@ -16,8 +16,13 @@ public:
RKRApiMenu();
~RKRApiMenu() override;
void updateFromR(const QVariantList &rep);
+ void enableAction(const QStringList &path, bool enable, bool show);
+ /** mostly for testing: Lookup action by path */
+ QAction* actionByPath(const QStringList &path);
private:
void makeXML(QDomDocument &doc, QDomElement e, const QVariantList &l, const QString &path, QStringList *actionlist);
+ QVariantList rep;
+ void commit();
};
#endif
diff --git a/rkward/rbackend/rkrinterface.cpp b/rkward/rbackend/rkrinterface.cpp
index df445914c..5838e473f 100644
--- a/rkward/rbackend/rkrinterface.cpp
+++ b/rkward/rbackend/rkrinterface.cpp
@@ -801,6 +801,11 @@ GenericRRequestResult RInterface::processRCallRequest (const QString &call, cons
RKMessageCatalog::switchLanguage(arglist.value(0));
} else if (call == "menuupdate") {
RKWardMainWindow::getMain()->rApiMenu()->updateFromR(args.toList());
+ } else if (call == "menuenable") {
+ auto path = args.toList().value(0).toStringList();
+ auto enable = args.toList().value(1).toList().value(0).toBool(); // NOTE: bool value is a vector of ints, in R, therefore the toList(), first
+ auto show = args.toList().value(2).toList().value(0).toBool();
+ RKWardMainWindow::getMain()->rApiMenu()->enableAction(path, enable, show);
} else {
return GenericRRequestResult::makeError(i18n("Error: unrecognized request '%1'", call));
}
diff --git a/rkward/rbackend/rpackages/rkward/R/rk.menu.R b/rkward/rbackend/rpackages/rkward/R/rk.menu.R
index 118e3590b..53e2b7552 100644
--- a/rkward/rbackend/rpackages/rkward/R/rk.menu.R
+++ b/rkward/rbackend/rpackages/rkward/R/rk.menu.R
@@ -18,7 +18,8 @@
#' associated R function.
#'
#' Adding/removing menu items is a fairly computation heavy exercise, internally, and is handled asynchronously, in the frontend. Should you need
-#' to remove and re-add certainly elements, frequently, hiding them will be more efficient (see \code{$enable()}).
+#' to remove and re-add certainly elements, frequently, hiding them will be more efficient (see \code{$enable()}). Note: A disabled menu item
+#' can still be called programmatically, using \code{$call()}
#'
#' This interface is still somewhat experimental, and currently kept to a minimal set of functions, deliberately. Please don't hesistate to give
#' us feedback on what you would like to see added. Only items defined using this mechanism can be manipulated / removed.
diff --git a/rkward/rbackend/rpackages/rkward/man/rk.menu.Rd b/rkward/rbackend/rpackages/rkward/man/rk.menu.Rd
index 74d2f56c5..986c177a1 100644
--- a/rkward/rbackend/rpackages/rkward/man/rk.menu.Rd
+++ b/rkward/rbackend/rpackages/rkward/man/rk.menu.Rd
@@ -12,7 +12,8 @@
\item{func}{Function to call for leaf item}
}
\value{
-\code{rk.menu()} and \code{$item()} return a handle. \code{call()} passes on the return value of the associated function. The other methods return \code{NULL}
+\code{rk.menu()} and \code{$item()} return a handle. \code{$define() returns the handle it was given (to allow command chaining)}.
+ \code{call()} passes on the return value of the associated function. The other methods return \code{NULL}
}
\description{
Allows to add (and subsequently remove) menu items associated to specific plain R functions in the RKWard main window.
@@ -28,7 +29,8 @@ Allows to add (and subsequently remove) menu items associated to specific plain
associated R function.
Adding/removing menu items is a fairly computation heavy exercise, internally, and is handled asynchronously, in the frontend. Should you need
- to remove and re-add certainly elements, frequently, hiding them will be more efficient (see \code{$enable()}).
+ to remove and re-add certainly elements, frequently, hiding them will be more efficient (see \code{$enable()}). Note: A disabled menu item
+ can still be called programmatically, using \code{$call()}
This interface is still somewhat experimental, and currently kept to a minimal set of functions, deliberately. Please don't hesistate to give
us feedback on what you would like to see added. Only items defined using this mechanism can be manipulated / removed.
More information about the rkward-tracker
mailing list