diff options
| author | Salanto <62221668+Salanto@users.noreply.github.com> | 2024-07-12 11:48:01 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-12 11:48:01 +0200 |
| commit | fb64ca386c51cc3942e1f38cfd76132b1b50e9db (patch) | |
| tree | f805cd22b191a028312e7f546b571fb35488fa50 /src/widgets | |
| parent | c745d0a1b78732550ae790fee12bd23b71a4f25e (diff) | |
Add playerlist widget element (#996)
* Commit
* Boyfailure code commit
* Cooking code spaghetti
* Accidental overwrite recursive function call hell
* Implemented player list
* Add partial moderator widget
Sleepy time! Hee-Hoo!
* Moderator Dialog - Step 1 - WIP
* Appease the clang gods
* Clang appeasement policy
* *sacrifices goat to clang*
* Added player report, reworked implementation, ...
* Added player-specific report
* Reworked implementation
* No longer uses JSON.
* Removed preset loader.
---------
Co-authored-by: TrickyLeifa <date.epoch@gmail.com>
Co-authored-by: Leifa <26681464+TrickyLeifa@users.noreply.github.com>
Diffstat (limited to 'src/widgets')
| -rw-r--r-- | src/widgets/moderator_dialog.cpp | 102 | ||||
| -rw-r--r-- | src/widgets/moderator_dialog.h | 38 | ||||
| -rw-r--r-- | src/widgets/playerlistwidget.cpp | 169 | ||||
| -rw-r--r-- | src/widgets/playerlistwidget.h | 36 |
4 files changed, 345 insertions, 0 deletions
diff --git a/src/widgets/moderator_dialog.cpp b/src/widgets/moderator_dialog.cpp new file mode 100644 index 00000000..11b99ccf --- /dev/null +++ b/src/widgets/moderator_dialog.cpp @@ -0,0 +1,102 @@ +#include "moderator_dialog.h" + +#include "aoapplication.h" +#include "gui_utils.h" +#include "options.h" + +#include <QDebug> +#include <QFile> +#include <QMessageBox> +#include <QUiLoader> +#include <QVBoxLayout> + +const QString ModeratorDialog::UI_FILE_PATH = "moderator_action_dialog.ui"; + +ModeratorDialog::ModeratorDialog(int clientId, bool ban, AOApplication *ao_app, QWidget *parent) + : QWidget{parent} + , ao_app(ao_app) + , m_client_id(clientId) + , m_ban(ban) +{ + QFile file(Options::getInstance().getUIAsset(UI_FILE_PATH)); + if (!file.open(QFile::ReadOnly)) + { + qFatal("Unable to open file %s", qPrintable(file.fileName())); + return; + } + + QUiLoader loader; + ui_widget = loader.load(&file, this); + auto layout = new QVBoxLayout(this); + layout->addWidget(ui_widget); + + FROM_UI(QComboBox, action); + FROM_UI(QSpinBox, duration); + FROM_UI(QLabel, duration_label); + FROM_UI(QCheckBox, permanent); + FROM_UI(QTextEdit, details); + FROM_UI(QDialogButtonBox, button_box); + + if (m_ban) + { + ui_action->addItem(tr("Ban")); + } + else + { + ui_action->addItem(tr("Kick")); + } + + ui_duration->setVisible(m_ban); + ui_duration_label->setVisible(m_ban); + ui_permanent->setVisible(m_ban); + + connect(ui_button_box, &QDialogButtonBox::accepted, this, &ModeratorDialog::onAcceptedClicked); + connect(ui_button_box, &QDialogButtonBox::rejected, this, &ModeratorDialog::close); +} + +ModeratorDialog::~ModeratorDialog() +{} + +void ModeratorDialog::onAcceptedClicked() +{ + QString reason = ui_details->toPlainText(); + if (reason.isEmpty()) + { + if (QMessageBox::question(this, tr("Confirmation"), tr("Are you sure you want to confirm without a reason?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + { + return; + } + } + + bool permanent = ui_permanent->isChecked(); + if (permanent) + { + if (QMessageBox::question(this, tr("Confirmation"), tr("Are you sure you want to ban permanently?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + { + return; + } + } + + QStringList arglist; + arglist.append(QString::number(m_client_id)); + if (m_ban) + { + if (permanent) + { + arglist.append("-1"); + } + else + { + arglist.append(QString::number(ui_duration->value())); + } + } + else + { + arglist.append("0"); + } + arglist.append(reason); + + ao_app->send_server_packet(AOPacket("MA", arglist)); + + close(); +} diff --git a/src/widgets/moderator_dialog.h b/src/widgets/moderator_dialog.h new file mode 100644 index 00000000..648f979d --- /dev/null +++ b/src/widgets/moderator_dialog.h @@ -0,0 +1,38 @@ +#pragma once + +#include <QCheckBox> +#include <QComboBox> +#include <QDialogButtonBox> +#include <QLabel> +#include <QSpinBox> +#include <QTextEdit> +#include <QWidget> + +class AOApplication; + +class ModeratorDialog : public QWidget +{ + Q_OBJECT + +public: + static const QString UI_FILE_PATH; + + explicit ModeratorDialog(int clientId, bool ban, AOApplication *ao_app, QWidget *parent = nullptr); + virtual ~ModeratorDialog(); + +private: + AOApplication *ao_app; + int m_client_id; + bool m_ban; + + QWidget *ui_widget; + QComboBox *ui_action; + QSpinBox *ui_duration; + QLabel *ui_duration_label; + QCheckBox *ui_permanent; + QTextEdit *ui_details; + QDialogButtonBox *ui_button_box; + +private Q_SLOTS: + void onAcceptedClicked(); +}; diff --git a/src/widgets/playerlistwidget.cpp b/src/widgets/playerlistwidget.cpp new file mode 100644 index 00000000..849c62a9 --- /dev/null +++ b/src/widgets/playerlistwidget.cpp @@ -0,0 +1,169 @@ +#include "playerlistwidget.h" + +#include "aoapplication.h" +#include "moderation_functions.h" +#include "widgets/moderator_dialog.h" + +#include <QListWidgetItem> +#include <QMenu> + +PlayerListWidget::PlayerListWidget(AOApplication *ao_app, QWidget *parent) + : QListWidget(parent) + , ao_app(ao_app) +{ + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, &PlayerListWidget::customContextMenuRequested, this, &PlayerListWidget::onCustomContextMenuRequested); +} + +PlayerListWidget::~PlayerListWidget() +{} + +void PlayerListWidget::registerPlayer(const PlayerRegister &update) +{ + switch (update.type) + { + default: + Q_UNREACHABLE(); + break; + + case PlayerRegister::ADD_PLAYER: + addPlayer(update.id); + break; + + case PlayerRegister::REMOVE_PLAYER: + removePlayer(update.id); + break; + } +} + +void PlayerListWidget::updatePlayer(const PlayerUpdate &update) +{ + PlayerData &player = m_player_map[update.id]; + + bool update_icon = false; + switch (update.type) + { + default: + Q_UNREACHABLE(); + break; + + case PlayerUpdate::NAME: + player.name = update.data; + break; + + case PlayerUpdate::CHARACTER: + player.character = update.data; + update_icon = true; + break; + + case PlayerUpdate::CHARACTER_NAME: + player.character_name = update.data; + break; + + case PlayerUpdate::AREA_ID: + player.area_id = update.data.toInt(); + break; + } + updatePlayer(player.id, update_icon); + + filterPlayerList(); +} + +void PlayerListWidget::setAuthenticated(bool f_state) +{ + m_is_authenticated = f_state; +} + +void PlayerListWidget::onCustomContextMenuRequested(const QPoint &pos) +{ + QListWidgetItem *item = itemAt(pos); + if (item == nullptr) + { + return; + } + int id = item->data(Qt::UserRole).toInt(); + QString name = item->text(); + + QMenu *menu = new QMenu(this); + menu->setAttribute(Qt::WA_DeleteOnClose); + + QAction *report_player_action = menu->addAction("Report Player"); + connect(report_player_action, &QAction::triggered, this, [this, id, name] { + auto maybe_reason = call_moderator_support(name); + if (maybe_reason.has_value()) + { + ao_app->send_server_packet(AOPacket("ZZ", {maybe_reason.value(), QString::number(id)})); + } + }); + + if (!m_is_authenticated) + { + QAction *kick_player_action = menu->addAction("Kick"); + connect(kick_player_action, &QAction::triggered, this, [this, id, name] { + ModeratorDialog *dialog = new ModeratorDialog(id, false, ao_app); + dialog->setWindowTitle(tr("Kick %1").arg(name)); + connect(this, &PlayerListWidget::destroyed, dialog, &ModeratorDialog::deleteLater); + dialog->show(); + }); + + QAction *ban_player_action = menu->addAction("Ban"); + connect(ban_player_action, &QAction::triggered, this, [this, id, name] { + ModeratorDialog *dialog = new ModeratorDialog(id, true, ao_app); + dialog->setWindowTitle(tr("Ban %1").arg(name)); + connect(this, &PlayerListWidget::destroyed, dialog, &ModeratorDialog::deleteLater); + dialog->show(); + }); + } + + menu->popup(mapToGlobal(pos)); +} + +void PlayerListWidget::addPlayer(int playerId) +{ + m_player_map.insert(playerId, PlayerData{.id = playerId}); + QListWidgetItem *item = new QListWidgetItem(this); + item->setData(Qt::UserRole, playerId); + m_item_map.insert(playerId, item); + updatePlayer(playerId, false); +} + +void PlayerListWidget::removePlayer(int playerId) +{ + delete takeItem(row(m_item_map.take(playerId))); + m_player_map.remove(playerId); +} + +void PlayerListWidget::filterPlayerList() +{ + int area_id = m_player_map.value(ao_app->client_id).area_id; + for (int i = 0; i < count(); ++i) + { + m_item_map[i]->setHidden(m_player_map[i].area_id != area_id); + } +} + +void PlayerListWidget::updatePlayer(int playerId, bool updateIcon) +{ + PlayerData &data = m_player_map[playerId]; + QListWidgetItem *item = m_item_map[playerId]; + item->setText(data.name.isEmpty() ? QObject::tr("Unnamed Player") : data.name); + if (data.character.isEmpty()) + { + item->setToolTip(QString()); + return; + } + + QString tooltip = data.character; + if (!data.character_name.isEmpty()) + { + tooltip = QObject::tr("%1 aka %2").arg(data.character, data.character_name); + } + + item->setToolTip(tooltip); + + if (updateIcon) + { + item->setIcon(QIcon(ao_app->get_image_suffix(ao_app->get_character_path(data.character, "char_icon"), true))); + } +} diff --git a/src/widgets/playerlistwidget.h b/src/widgets/playerlistwidget.h new file mode 100644 index 00000000..e771d7e1 --- /dev/null +++ b/src/widgets/playerlistwidget.h @@ -0,0 +1,36 @@ +#pragma once + +#include "datatypes.h" + +#include <QList> +#include <QListWidget> +#include <QMap> + +class AOApplication; + +class PlayerListWidget : public QListWidget +{ +public: + explicit PlayerListWidget(AOApplication *ao_app, QWidget *parent = nullptr); + virtual ~PlayerListWidget(); + + void registerPlayer(const PlayerRegister &update); + void updatePlayer(const PlayerUpdate &update); + + void setAuthenticated(bool f_state); + +private: + AOApplication *ao_app; + QMap<int, PlayerData> m_player_map; + QMap<int, QListWidgetItem *> m_item_map; + bool m_is_authenticated = false; + + void addPlayer(int playerId); + void removePlayer(int playerId); + void updatePlayer(int playerId, bool updateIcon); + + void filterPlayerList(); + +private Q_SLOTS: + void onCustomContextMenuRequested(const QPoint &pos); +}; |
