Address comments.

This commit is contained in:
Lukas Brübach 2026-08-17 11:19:49 +02:00
parent 653e608dbe
commit 72843d1956
5 changed files with 47 additions and 60 deletions

View file

@ -33,7 +33,7 @@ DlgInviteToGame::DlgInviteToGame(TabSupervisor *_tabSupervisor,
// signals while the modal loop runs. // signals while the modal loop runs.
UserListManager *manager = tabSupervisor->getUserListManager(); UserListManager *manager = tabSupervisor->getUserListManager();
userList = new UserListWidget(tabSupervisor, tabSupervisor->getClient(), UserListWidget::RoomList, this, userList = new UserListWidget(tabSupervisor, tabSupervisor->getClient(), UserListWidget::RoomList, this,
/*withUserInfoPopup=*/false); /*hasUserInfoPopup=*/false);
userList->setUserFilter([this, manager](const QString &name, bool online) { userList->setUserFilter([this, manager](const QString &name, bool online) {
return !excludeUserNames.contains(name) && online && !manager->isUserIgnored(name); return !excludeUserNames.contains(name) && online && !manager->isUserIgnored(name);
}); });
@ -50,11 +50,6 @@ DlgInviteToGame::DlgInviteToGame(TabSupervisor *_tabSupervisor,
currentUserName = userName; currentUserName = userName;
inviteButton->setEnabled(!userName.isEmpty()); inviteButton->setEnabled(!userName.isEmpty());
}); });
connect(userList, &UserListWidget::userListChanged, this, &DlgInviteToGame::updateEmptyLabel);
emptyLabel = new QLabel(this);
emptyLabel->setAlignment(Qt::AlignCenter);
emptyLabel->setVisible(false);
inviteButton = new QPushButton(this); inviteButton = new QPushButton(this);
inviteButton->setEnabled(false); inviteButton->setEnabled(false);
@ -72,10 +67,8 @@ DlgInviteToGame::DlgInviteToGame(TabSupervisor *_tabSupervisor,
auto *layout = new QVBoxLayout(this); auto *layout = new QVBoxLayout(this);
layout->addWidget(searchEdit); layout->addWidget(searchEdit);
layout->addWidget(userList, 1); layout->addWidget(userList, 1);
layout->addWidget(emptyLabel);
layout->addLayout(buttonRow); layout->addLayout(buttonRow);
updateEmptyLabel();
retranslateUi(); retranslateUi();
// Default to a comfortably tall dialog so the list has room to breathe, // Default to a comfortably tall dialog so the list has room to breathe,
@ -88,12 +81,6 @@ DlgInviteToGame::DlgInviteToGame(TabSupervisor *_tabSupervisor,
void DlgInviteToGame::searchTextChanged(const QString &text) void DlgInviteToGame::searchTextChanged(const QString &text)
{ {
userList->setFilterText(text); userList->setFilterText(text);
updateEmptyLabel();
}
void DlgInviteToGame::updateEmptyLabel()
{
emptyLabel->setVisible(userList->visibleUserRowCount() == 0);
} }
void DlgInviteToGame::inviteCurrentUser(const QString &userName) void DlgInviteToGame::inviteCurrentUser(const QString &userName)
@ -122,5 +109,4 @@ void DlgInviteToGame::retranslateUi()
searchEdit->setPlaceholderText(tr("Search users...")); searchEdit->setPlaceholderText(tr("Search users..."));
inviteButton->setText(tr("Invite")); inviteButton->setText(tr("Invite"));
cancelButton->setText(tr("Cancel")); cancelButton->setText(tr("Cancel"));
emptyLabel->setText(tr("No matching users."));
} }

View file

@ -10,7 +10,6 @@
#include <QDialog> #include <QDialog>
#include <QStringList> #include <QStringList>
class QLabel;
class QLineEdit; class QLineEdit;
class QPushButton; class QPushButton;
class TabSupervisor; class TabSupervisor;
@ -38,11 +37,9 @@ private:
QString currentUserName; QString currentUserName;
QLineEdit *searchEdit; QLineEdit *searchEdit;
UserListWidget *userList; UserListWidget *userList;
QLabel *emptyLabel;
QPushButton *inviteButton; QPushButton *inviteButton;
QPushButton *cancelButton; QPushButton *cancelButton;
void updateEmptyLabel();
void retranslateUi(); void retranslateUi();
}; };

View file

@ -355,7 +355,7 @@ bool UserListItemDelegate::editorEvent(QEvent *event,
QMouseEvent *const mouseEvent = static_cast<QMouseEvent *>(event); QMouseEvent *const mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::RightButton) { if (mouseEvent->button() == Qt::RightButton) {
// Dialog mode has no context menu: consume the press, show nothing. // Dialog mode has no context menu: consume the press, show nothing.
if (owner->getWithUserInfoPopup()) { if (owner->getHasUserInfoPopup()) {
owner->showContextMenu(mouseEvent->globalPosition().toPoint(), index); owner->showContextMenu(mouseEvent->globalPosition().toPoint(), index);
} }
return true; return true;
@ -582,8 +582,8 @@ UserListWidget::UserListWidget(TabSupervisor *_tabSupervisor,
AbstractClient *_client, AbstractClient *_client,
UserListType _type, UserListType _type,
QWidget *parent, QWidget *parent,
bool _withUserInfoPopup) bool _hasUserInfoPopup)
: QGroupBox(parent), withUserInfoPopup(_withUserInfoPopup), tabSupervisor(_tabSupervisor), client(_client), : QGroupBox(parent), hasUserInfoPopup(_hasUserInfoPopup), tabSupervisor(_tabSupervisor), client(_client),
type(_type), onlineCount(0) type(_type), onlineCount(0)
{ {
avatarProvider = new UserAvatarProvider(client, this); avatarProvider = new UserAvatarProvider(client, this);
@ -610,16 +610,8 @@ UserListWidget::UserListWidget(TabSupervisor *_tabSupervisor,
userTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); userTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
userTree->header()->setStretchLastSection(true); userTree->header()->setStretchLastSection(true);
if (withUserInfoPopup) { // Always create timers so callers never segfault on a null deref;
// ── Hover popup ─────────────────────────────────────────────────────── // showPopupForUser / hidePopup already guard against a null userInfoPopup.
userInfoPopup = new UserInfoPopup(tabSupervisor, tabSupervisor->getClient(), &avatarProvider->cache(),
&cardArtProvider->cache(), &cardArtParamsMap,
window()); // parented to main window so it floats above siblings
userInfoPopup->hide();
userInfoPopup->setWindowOpacity(0.0);
userInfoPopup->installEventFilter(this);
showPopupTimer = new QTimer(this); showPopupTimer = new QTimer(this);
showPopupTimer->setSingleShot(true); showPopupTimer->setSingleShot(true);
showPopupTimer->setInterval(280); showPopupTimer->setInterval(280);
@ -645,11 +637,22 @@ UserListWidget::UserListWidget(TabSupervisor *_tabSupervisor,
// The hover ends when the cursor leaves the user row. Empty list // The hover ends when the cursor leaves the user row. Empty list
// space, a section divider and anything outside the tree all close // space, a section divider and anything outside the tree all close
// the popup, while the popup itself keeps it alive. // the popup, while the popup itself keeps it alive.
if (!popupPinned && !userInfoPopup->underMouse() && (hoveredUser.isEmpty() || !userTree->underMouse())) { if (!popupPinned && userInfoPopup && !userInfoPopup->underMouse() &&
(hoveredUser.isEmpty() || !userTree->underMouse())) {
hidePopup(); hidePopup();
} }
}); });
if (hasUserInfoPopup) {
// ── Hover popup ───────────────────────────────────────────────────────
userInfoPopup = new UserInfoPopup(tabSupervisor, tabSupervisor->getClient(), &avatarProvider->cache(),
&cardArtProvider->cache(), &cardArtParamsMap,
window()); // parented to main window so it floats above siblings
userInfoPopup->hide();
userInfoPopup->setWindowOpacity(0.0);
userInfoPopup->installEventFilter(this);
connectPopupSignals(); connectPopupSignals();
} }
@ -658,7 +661,7 @@ UserListWidget::UserListWidget(TabSupervisor *_tabSupervisor,
userTree->viewport()->installEventFilter(this); userTree->viewport()->installEventFilter(this);
userTree->installEventFilter(this); // keyboard handling for section dividers userTree->installEventFilter(this); // keyboard handling for section dividers
if (withUserInfoPopup) { if (hasUserInfoPopup) {
// Clicking anywhere outside the list clears its selection and closes the // Clicking anywhere outside the list clears its selection and closes the
// popup. The filter watches all widgets because the press can land on any // popup. The filter watches all widgets because the press can land on any
// part of the window, on another list or on the popup itself. // part of the window, on another list or on the popup itself.
@ -984,7 +987,7 @@ bool UserListWidget::eventFilter(QObject *obj, QEvent *event)
// Divider rows have no menu. Mouse-triggered context events are NOT handled // Divider rows have no menu. Mouse-triggered context events are NOT handled
// here — the delegate's right-press path already pops the menu, and // here — the delegate's right-press path already pops the menu, and
// handling both would open two menus on one right-click. // handling both would open two menus on one right-click.
if (withUserInfoPopup && (obj == userTree || obj == userTree->viewport()) && event->type() == QEvent::ContextMenu) { if (hasUserInfoPopup && (obj == userTree || obj == userTree->viewport()) && event->type() == QEvent::ContextMenu) {
auto *contextEvent = static_cast<QContextMenuEvent *>(event); auto *contextEvent = static_cast<QContextMenuEvent *>(event);
if (contextEvent->reason() == QContextMenuEvent::Keyboard) { if (contextEvent->reason() == QContextMenuEvent::Keyboard) {
QTreeWidgetItem *current = userTree->currentItem(); QTreeWidgetItem *current = userTree->currentItem();
@ -1018,7 +1021,7 @@ bool UserListWidget::eventFilter(QObject *obj, QEvent *event)
} }
} }
if (withUserInfoPopup && obj == userTree->viewport()) { if (hasUserInfoPopup && obj == userTree->viewport()) {
if (event->type() == QEvent::MouseMove) { if (event->type() == QEvent::MouseMove) {
if (!SettingsCache::instance().appearance().getStyleUserList()) { if (!SettingsCache::instance().appearance().getStyleUserList()) {
return QGroupBox::eventFilter(obj, event); return QGroupBox::eventFilter(obj, event);
@ -1589,7 +1592,7 @@ void UserListWidget::userClicked(QTreeWidgetItem *item, int /*column*/)
return; // divider rows open no chat return; // divider rows open no chat
} }
const QString userName = item->data(2, Qt::UserRole).toString(); const QString userName = item->data(2, Qt::UserRole).toString();
if (withUserInfoPopup) { if (hasUserInfoPopup) {
emit openMessageDialog(userName, true); emit openMessageDialog(userName, true);
} else { } else {
emit userActivated(userName); emit userActivated(userName);

View file

@ -173,7 +173,7 @@ private:
QString hoveredUser; QString hoveredUser;
bool popupPinned = false; bool popupPinned = false;
bool bulkLoading = false; bool bulkLoading = false;
bool withUserInfoPopup = true; bool hasUserInfoPopup = true;
std::function<bool(const QString &userName, bool online)> userFilter; std::function<bool(const QString &userName, bool online)> userFilter;
/** /**
@ -256,7 +256,7 @@ public:
AbstractClient *_client, AbstractClient *_client,
UserListType _type, UserListType _type,
QWidget *parent = nullptr, QWidget *parent = nullptr,
bool withUserInfoPopup = true); bool hasUserInfoPopup = true);
~UserListWidget() override; ~UserListWidget() override;
void bind(UserListManager *mgr); void bind(UserListManager *mgr);
void applyDisplayMode(); void applyDisplayMode();
@ -278,9 +278,9 @@ public:
userFilter = std::move(filter); userFilter = std::move(filter);
} }
[[nodiscard]] int visibleUserRowCount() const; [[nodiscard]] int visibleUserRowCount() const;
[[nodiscard]] bool getWithUserInfoPopup() const [[nodiscard]] bool getHasUserInfoPopup() const
{ {
return withUserInfoPopup; return hasUserInfoPopup;
} }
[[nodiscard]] const QList<Section> &getSectionIds() const [[nodiscard]] const QList<Section> &getSectionIds() const
{ {

View file

@ -1324,6 +1324,7 @@ void TabGame::createPlayerListDock(bool bReplay)
auto *playerListBox = new QWidget(this); auto *playerListBox = new QWidget(this);
auto *vbox = new QVBoxLayout(playerListBox); auto *vbox = new QVBoxLayout(playerListBox);
vbox->setContentsMargins(0, 0, 0, 0); vbox->setContentsMargins(0, 0, 0, 0);
vbox->setSpacing(0);
vbox->addWidget(playerListWidget); vbox->addWidget(playerListWidget);