Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent signal recursion when canceling group change #88

Merged
merged 1 commit into from
Mar 4, 2025

Conversation

wyu71
Copy link
Contributor

@wyu71 wyu71 commented Feb 5, 2025

  • Block signals when resetting combobox text to avoid triggering another selectedGroupChanged event.

@wengxt
Copy link
Member

wengxt commented Feb 5, 2025

Are you having issue because of this?

I think line 199 already prevent this.

@wyu71
Copy link
Contributor Author

wyu71 commented Feb 11, 2025

This is primarily a performance optimization. While the extra signal wouldn't cause bugs (since the group hasn't changed), it would trigger unnecessary signal processing. The fix ensures that when we reset the combobox text after cancellation, we don't waste CPU cycles on processing a redundant signal.
It's a good practice in Qt to block signals when programmatically updating UI elements if you don't want those updates to trigger change events.

@wengxt
Copy link
Member

wengxt commented Feb 11, 2025

In complex composed widget I think it's risky and make a dependency on how QWidget currently implement the widget.

For example, QComboBox has model, hidden internal line edit and how it really works purely relies on the implementation of QWidget.

What we really want, is to prevent the signal-slot only in our code

    connect(ui_->inputMethodGroupComboBox, &QComboBox::currentTextChanged, this,
            &IMPage::selectedGroupChanged);

From firing. SignalBlocker is doing the thing in a much larger scope. I would prefer, to have a bool flag and check it in selectedGroupChanged to prevent the actual handling.

@wyu71
Copy link
Contributor Author

wyu71 commented Feb 12, 2025

I would argue that the current approach using blockSignals() is actually more appropriate here:

  • Signal Connection Context:
    We have exactly one signal connection:

    connect(ui_->inputMethodGroupComboBox, &QComboBox::currentTextChanged, this,
               &IMPage::selectedGroupChanged);
    

    The signal blocking is specifically used when programmatically resetting the text

  • Signal Handling Clarity:
    Using blockSignals() clearly communicates that "this is a programmatic change that should not trigger any signal handlers"
    A boolean flag would require additional state management and could be less obvious in intent

  • Future Maintenance:
    If additional signal connections are added later, blockSignals() ensures consistent behavior
    A boolean flag would only protect our specific handler, potentially leading to inconsistencies if other handlers are added

  • Implementation Independence:
    While QComboBox does have complex internals, blockSignals() is a well-defined Qt API that's stable across versions
    The implementation actually protects us from internal widget changes since it works at the QObject level

The current solution is both clear in intent and correct in implementation. While a boolean flag might seem simpler, it could actually introduce more complexity and potential issues in a multi-signal environment.

@wengxt
Copy link
Member

wengxt commented Feb 12, 2025

@wyu71
The signal is not queued, but ignored. In such case, imagine if a widget is implemented in a composite way:

MyWidget::MyWidget() {
    connect(this, SIGNAL(aChanged), subWidget_, SLOT(B));
}

If you block signal on MyWidget, it's internal implementation my ends up in an inconsistent state.

While your code may work today, it actually adds a hidden assumption that QComboBox will not change it's implementation to rely on signal from itself. This is what I try to avoid so we are less likely to have unintentional breakage.

I would, rather do "disconnect()" "set()", "connect()", or put a bool value to implement my own block logic.

Ideally, if QMetaObject::Connection can provides a way to block signal on a per-connection basis, I would use that. However, block all signal on an object, unless I own the full implementation of that object class, I would try to avoid using blockSignals on it.

@wyu71 wyu71 force-pushed the master branch 10 times, most recently from 7f03c12 to a5e1f69 Compare February 20, 2025 11:27
Use explicit disconnect/connect of the signal handler when resetting
combobox text to prevent recursion in selectedGroupChanged.
@wyu71
Copy link
Contributor Author

wyu71 commented Feb 20, 2025

I understand your concern about using blockSignals() on Qt widgets. Let me propose a better solution using the disconnect/connect approach:

void IMPage::selectedGroupChanged() {
    if (config_->currentGroup() ==
        ui_->inputMethodGroupComboBox->currentText()) {
        return;
    }
    if (!config_->currentGroup().isEmpty() && config_->needSave()) {
        if (QMessageBox::No ==
            QMessageBox::question(this, _("Current group changed"),
                                  _("Do you want to change group? Changes to "
                                    "current group will be lost!"))) {
            // Temporarily disconnect the signal
            bool connection = disconnect(ui_->inputMethodGroupComboBox,
                                         &QComboBox::currentTextChanged, this,
                                         &IMPage::selectedGroupChanged);

            ui_->inputMethodGroupComboBox->setCurrentText(
                config_->currentGroup());

            // Restore the connection
            if (connection) {
                connect(ui_->inputMethodGroupComboBox,
                        &QComboBox::currentTextChanged, this,
                        &IMPage::selectedGroupChanged);
            }
            return;
        }
    }

    config_->setCurrentGroup(ui_->inputMethodGroupComboBox->currentText());
}

@wyu71
Copy link
Contributor Author

wyu71 commented Feb 26, 2025

@wengxt Please review it

@wengxt wengxt merged commit a5260fe into fcitx:master Mar 4, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants