[education/rkward] rkward: Port setup dialog to RKRadioGroup, too
Thomas Friedrichsmeier
null at kde.org
Sat Aug 10 11:45:13 BST 2024
Git commit 75d27860845a7d790a816d5a5c8d0dc1602de5a8 by Thomas Friedrichsmeier.
Committed on 10/08/2024 at 10:45.
Pushed by tfry into branch 'master'.
Port setup dialog to RKRadioGroup, too
M +16 -31 rkward/dialogs/rksetupwizard.cpp
M +40 -24 rkward/misc/rkradiogroup.cpp
M +1 -3 rkward/misc/rkradiogroup.h
https://invent.kde.org/education/rkward/-/commit/75d27860845a7d790a816d5a5c8d0dc1602de5a8
diff --git a/rkward/dialogs/rksetupwizard.cpp b/rkward/dialogs/rksetupwizard.cpp
index 966850387..d61b367c0 100644
--- a/rkward/dialogs/rksetupwizard.cpp
+++ b/rkward/dialogs/rksetupwizard.cpp
@@ -18,9 +18,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include <QTimer>
#include <QFileInfo>
#include <QStandardPaths>
-#include <QRadioButton>
-#include <QGroupBox>
-#include <QButtonGroup>
#include <KLocalizedString>
#include <KUrlRequester>
@@ -32,6 +29,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include "../misc/rkcommonfunctions.h"
#include "../misc/rkcommandlineargs.h"
#include "../misc/rkstandardicons.h"
+#include "../misc/rkradiogroup.h"
#include "../dialogs/rkloadlibsdialog.h"
#include "../windows/katepluginintegration.h"
#include "../rbackend/rksessionvars.h"
@@ -175,26 +173,19 @@ private:
RInterface::BackendError backend_error;
};
-class RBackendSelectionWidget : public QGroupBox {
+class RBackendSelectionWidget : public RKRadioGroup {
public:
- RBackendSelectionWidget(QWidget *parent) : QGroupBox(parent) {
- group = new QButtonGroup(this);
- auto l = new QVBoxLayout(this);
- bl = new QVBoxLayout();
- l->addLayout(bl);
-
- auto button = new QRadioButton(i18n("Use R at:")); l->addWidget(button); group->addButton(button, -1);
- req = new KUrlRequester(); l->addWidget(req);
+ RBackendSelectionWidget(QWidget *parent) : RKRadioGroup(parent) {
+ req = new KUrlRequester();
req->setPlaceholderText(i18n("Select another R executable"));
- req->setEnabled(false);
req->setWindowTitle(i18n("Select R executable"));
- connect(button, &QAbstractButton::toggled, req, &QWidget::setEnabled);
}
void updateOptions() {
// clear previous buttons, if any
- for (int i = 0; i < r_installations.size(); ++i) {
- delete (group->button(i));
+ const auto buttons = group()->buttons();
+ for (auto button : buttons) {
+ delete button;
}
r_installations = RKSessionVars::findRInstallations();
@@ -205,29 +196,23 @@ public:
for(int i = 0; i < r_installations.size(); ++i) {
addButton(i18n("Use R at %1", r_installations[i]), i);
}
- auto button = group->button(0);
- if (RInterface::instance()->backendIsDead()) button->setText(i18n("Attempt to restart R at %1", RKSessionVars::RBinary()));
- else button->setText(i18n("Keep current version (%1)", RKSessionVars::RBinary()));
+ addButton(i18n("Use R at:"), -1, req);
+
+ auto button0 = group()->button(0);
+ if (RInterface::instance()->backendIsDead()) button0->setText(i18n("Attempt to restart R at %1", RKSessionVars::RBinary()));
+ else button0->setText(i18n("Keep current version (%1)", RKSessionVars::RBinary()));
if (RKSessionVars::isPathInAppImage(RKSessionVars::RBinary()) && (r_installations.size() > 1)) {
- group->button(1)->setChecked(true);
+ setButtonChecked(1, true);
} else {
- button->setChecked(true);
+ button0->setChecked(true);
}
}
- QRadioButton *addButton(const QString &text, int index) {
- auto button = new QRadioButton(text);
- bl->addWidget(button);
- group->addButton(button, index);
- return button;
- }
QString selectedOpt() {
- int index = group->checkedId();
+ int index = group()->checkedId();
if (index >= 0) return r_installations[index];
return req->text();
}
- QButtonGroup *group;
private:
- QVBoxLayout *bl;
QStringList r_installations;
KUrlRequester *req;
};
@@ -409,7 +394,7 @@ RKSetupWizard::RKSetupWizard(QWidget* parent, InvokationReason reason, const QLi
setValid(pageref, true);
next_callbacks.insert(pageref, [select, pageref, this]() -> bool {
- bool restart_needed = (select->group->checkedId() != 0) || RInterface::instance()->backendIsDead();
+ bool restart_needed = (select->group()->checkedId() != 0) || RInterface::instance()->backendIsDead();
if (restart_needed) {
RKSessionVars::r_binary = select->selectedOpt();
RKCommandLineArgs::instance->set(RKCommandLineArgs::Setup, QVariant(true));
diff --git a/rkward/misc/rkradiogroup.cpp b/rkward/misc/rkradiogroup.cpp
index 0d088b208..f2b606bbd 100644
--- a/rkward/misc/rkradiogroup.cpp
+++ b/rkward/misc/rkradiogroup.cpp
@@ -11,6 +11,43 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include "../debug.h"
+/** Internal radio button class, which allows to be installed as an event filter on "controlled" widgets.
+ * Using a separate class rather than filtering in RKRadioGroup has the advantage, that the filter will
+ * automatically be removed, in case the button is deleted independent of the controlled widget. */
+class RKRadioGroupButton : public QRadioButton {
+public:
+ explicit RKRadioGroupButton(const QString &label) : QRadioButton(label) {};
+ void control(QWidget *controlled) {
+ RKRadioGroupButton::controlled = controlled;
+ controlled->installEventFilter(this);
+ const auto children = controlled->findChildren<QWidget*>();
+ for (auto child : children) child->installEventFilter(this); // need to receive clicks on all child widgets!
+ controlled->setEnabled(isChecked());
+ // TODO (see EditFormatDialog: it may also make sense the other way around: if the associated widget is clicked, set this button as active)
+ connect(this, &QAbstractButton::toggled, controlled, &QWidget::setEnabled);
+ }
+private:
+ bool eventFilter(QObject* obj, QEvent *ev) override {
+ if (ev->isSinglePointEvent()) {
+ // When clicking in the widget controlled by a button, automatically check the button (enabling the controlled widget).
+ // In effect, the appearance is that the widget "belongs" to the button.
+ // NOTE: This does not wait for a full click to occur, only a mouse press. The reason is that some widgets will not react on the click,
+ // if they have been disabled during the press.
+ auto e = static_cast<QSinglePointEvent *>(ev);
+ if (e->isBeginEvent()) {
+ // Perhaps a bit paranoid: Only react on controlled and its children
+ while (obj) {
+ if (obj == controlled) break;
+ obj = obj->parent();
+ }
+ if (obj && isEnabled()) setChecked(true);
+ }
+ }
+ return false;
+ }
+ QWidget *controlled;
+};
+
RKRadioGroup::RKRadioGroup(QWidget *parent) : RKRadioGroup(QString(), parent) {
}
@@ -24,7 +61,7 @@ RKRadioGroup::~RKRadioGroup() {
QRadioButton* RKRadioGroup::addButton(const QString &label, int id) {
RK_TRACE(MISC);
- auto button = new QRadioButton(label);
+ auto button = new RKRadioGroupButton(label);
_group->addButton(button, id);
_layout->addWidget(button);
return button;
@@ -32,20 +69,15 @@ QRadioButton* RKRadioGroup::addButton(const QString &label, int id) {
QRadioButton* RKRadioGroup::addButton(const QString &label, int id, QWidget* controlled, QBoxLayout::Direction dir) {
RK_TRACE(MISC);
- QRadioButton *button;
auto old_layout = _layout;
if (dir != _layout->direction()) {
_layout = new QBoxLayout(dir);
old_layout->addLayout(_layout);
}
- button = addButton(label, id);
+ RKRadioGroupButton *button = static_cast<RKRadioGroupButton*>(addButton(label, id));
_layout->addWidget(controlled);
- controlled->installEventFilter(this);
- controlled->setProperty(property, QVariant::fromValue(button));
_layout = old_layout;
- controlled->setEnabled(false);
- // TODO (see EditFormatDialog: it may also make sense the other way around: if the associated widget is clicked, set this button as active)
- connect(button, &QAbstractButton::toggled, controlled, &QWidget::setEnabled);
+ button->control(controlled);
return button;
}
@@ -58,19 +90,3 @@ bool RKRadioGroup::setButtonChecked(int id, bool checked) {
}
return false;
}
-
-bool RKRadioGroup::eventFilter(QObject *obj, QEvent *ev) {
- if (ev->isSinglePointEvent()) {
- // When clicking in the widget controlled by a button, automatically check the button (enabling the controlled widget).
- // In effect, the appearance is that the widget "belongs" to the button.
- // NOTE: This does not wait for a full click to occur, only a mouse press. The reason is that some widgets will not react on the click,
- // if they have been disabled during the press.
- auto e = static_cast<QSinglePointEvent *>(ev);
- auto w = static_cast<QWidget *>(obj);
- if (!w->isEnabled() && e->isBeginEvent()) {
- auto button = w->property(property).value<QRadioButton*>();
- if (button && button->isEnabled()) button->setChecked(true);
- }
- }
- return false;
-}
diff --git a/rkward/misc/rkradiogroup.h b/rkward/misc/rkradiogroup.h
index bb36d7cb1..a05f6a3e7 100644
--- a/rkward/misc/rkradiogroup.h
+++ b/rkward/misc/rkradiogroup.h
@@ -22,6 +22,7 @@ public:
~RKRadioGroup() override;
QRadioButton* addButton(const QString &label, int id = -1);
+ /** NOTE: the group, but not the associated button assumes ownership over controlled! */
QRadioButton* addButton(const QString &label, int id, QWidget* controlled, QBoxLayout::Direction dir=QBoxLayout::TopToBottom);
QRadioButton* addButton(const QString &label, QWidget* controlled, QBoxLayout::Direction dir=QBoxLayout::TopToBottom) {
return addButton(label, -1, controlled, dir);
@@ -30,12 +31,9 @@ public:
* @return false, if there is no such button, true otherwise. */
bool setButtonChecked(int id, bool checked);
QButtonGroup* group() const { return _group; };
-protected:
- bool eventFilter(QObject *obj, QEvent *ev) override;
private:
QButtonGroup *_group;
QBoxLayout *_layout;
- static constexpr const char property[] = "_rkradiogroup_";
};
#endif
More information about the rkward-tracker
mailing list