mirror of
https://git.citron-emu.org/citron/emulator
synced 2026-02-01 07:13:33 +00:00
Merge pull request 'feat(multiplayer): More QoL Improvements' (#93) from feat/more-qol-changes into main
Reviewed-on: https://git.citron-emu.org/Citron/Emulator/pulls/93
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#include <QPainter>
|
||||
#include <QTime>
|
||||
#include <QUrl>
|
||||
#include <QPushButton>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include <QToolButton>
|
||||
#include <QGridLayout>
|
||||
@@ -164,9 +165,15 @@ public:
|
||||
}
|
||||
|
||||
QVariant data(int role) const override {
|
||||
// If compact mode is on, we tell the model to return no text
|
||||
if (role == Qt::DisplayRole && QStandardItem::data(Qt::UserRole + 7).toBool()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (role != Qt::DisplayRole) {
|
||||
return QStandardItem::data(role);
|
||||
}
|
||||
|
||||
QString name;
|
||||
const QString nickname = data(NicknameRole).toString();
|
||||
const QString username = data(UsernameRole).toString();
|
||||
@@ -190,24 +197,53 @@ public:
|
||||
ChatRoom::ChatRoom(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ChatRoom>()) {
|
||||
ui->setupUi(this);
|
||||
|
||||
// Setup the Emoji Button
|
||||
QToolButton* emoji_button = new QToolButton(this);
|
||||
emoji_button->setText(QStringLiteral("😀"));
|
||||
emoji_button->setPopupMode(QToolButton::InstantPopup);
|
||||
emoji_button->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
||||
emoji_button->setFixedSize(36, 30);
|
||||
emoji_button->setAutoRaise(true);
|
||||
emoji_button->setFixedSize(30, 30);
|
||||
// Hide the arrow indicator and remove padding to ensure the emoji is dead-center
|
||||
emoji_button->setStyleSheet(QStringLiteral("QToolButton::menu-indicator { image: none; } QToolButton { padding: 0px; }"));
|
||||
emoji_button->setPopupMode(QToolButton::InstantPopup);
|
||||
emoji_button->setStyleSheet(QStringLiteral(
|
||||
"QToolButton { padding: 0px; margin: 0px; }"
|
||||
"QToolButton::menu-indicator { image: none; width: 0px; }"
|
||||
));
|
||||
|
||||
ui->horizontalLayout_3->insertWidget(1, emoji_button);
|
||||
// Setup the Send Button
|
||||
send_message = new QPushButton(QStringLiteral("➤"), this);
|
||||
send_message->setObjectName(QStringLiteral("send_message"));
|
||||
send_message->setFixedSize(40, 30);
|
||||
send_message->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
// Rebuild Layout
|
||||
ui->horizontalLayout_3->removeWidget(ui->chat_message);
|
||||
ui->horizontalLayout_3->addWidget(ui->chat_message); // Index 0
|
||||
ui->horizontalLayout_3->addWidget(emoji_button); // Index 1
|
||||
ui->horizontalLayout_3->addWidget(send_message); // Index 2
|
||||
ui->horizontalLayout_3->setStretch(0, 1);
|
||||
ui->horizontalLayout_3->setStretch(1, 0);
|
||||
ui->horizontalLayout_3->setStretch(2, 0);
|
||||
|
||||
connect(ui->chat_message, &QLineEdit::returnPressed, this, &ChatRoom::OnSendChat);
|
||||
connect(send_message, &QPushButton::clicked, this, &ChatRoom::OnSendChat);
|
||||
|
||||
QMenu* emoji_menu = new QMenu(this);
|
||||
|
||||
QStringList emojis = {
|
||||
QStringLiteral("😀"), QStringLiteral("😂"), QStringLiteral("🤣"), QStringLiteral("😊"), QStringLiteral("😎"),
|
||||
QStringLiteral("🤔"), QStringLiteral("🤨"), QStringLiteral("😭"), QStringLiteral("😮"), QStringLiteral("💀"),
|
||||
QStringLiteral("👍"), QStringLiteral("👎"), QStringLiteral("🔥"), QStringLiteral("✨"), QStringLiteral("❤️"),
|
||||
QStringLiteral("🎉"), QStringLiteral("💯"), QStringLiteral("🚀"), QStringLiteral("🎮"), QStringLiteral("🕹️"),
|
||||
QStringLiteral("👾"), QStringLiteral("🍄"), QStringLiteral("⭐️"), QStringLiteral("⚔️"), QStringLiteral("🛡️")
|
||||
QStringLiteral("🤔"), QStringLiteral("🤨"), QStringLiteral("🙄"), QStringLiteral("🥺"), QStringLiteral("😭"),
|
||||
QStringLiteral("😮"), QStringLiteral("🥳"), QStringLiteral("😴"), QStringLiteral("🤡"), QStringLiteral("💀"),
|
||||
QStringLiteral("👀"), QStringLiteral("💤"), QStringLiteral("👑"), QStringLiteral("👻"), QStringLiteral("🥀"),
|
||||
QStringLiteral("👍"), QStringLiteral("👎"), QStringLiteral("👏"), QStringLiteral("🙌"), QStringLiteral("🙏"),
|
||||
QStringLiteral("🤝"), QStringLiteral("💪"), QStringLiteral("👋"), QStringLiteral("👊"), QStringLiteral("👌"),
|
||||
QStringLiteral("🎮"), QStringLiteral("🕹️"), QStringLiteral("👾"), QStringLiteral("💻"), QStringLiteral("📱"),
|
||||
QStringLiteral("🖱️"), QStringLiteral("⌨️"), QStringLiteral("🎧"), QStringLiteral("📺"), QStringLiteral("🔋"),
|
||||
QStringLiteral("🔥"), QStringLiteral("✨"), QStringLiteral("❤️"), QStringLiteral("🎉"), QStringLiteral("💯"),
|
||||
QStringLiteral("🚀"), QStringLiteral("🍄"), QStringLiteral("⭐️"), QStringLiteral("⚔️"), QStringLiteral("🛡️"),
|
||||
QStringLiteral("💎"), QStringLiteral("💡"), QStringLiteral("💣"), QStringLiteral("📢"), QStringLiteral("🔔"),
|
||||
QStringLiteral("✅"), QStringLiteral("❌"), QStringLiteral("⚠️"), QStringLiteral("🚫"), QStringLiteral("🌈"),
|
||||
QStringLiteral("🌊"), QStringLiteral("⚡"), QStringLiteral("🍃"), QStringLiteral("🐱"), QStringLiteral("🐉"),
|
||||
QStringLiteral("🍋"), QStringLiteral("🏆"), QStringLiteral("🧂"), QStringLiteral("🍿"), QStringLiteral("🫠")
|
||||
};
|
||||
|
||||
// Create a container widget for the grid
|
||||
@@ -216,22 +252,22 @@ ChatRoom::ChatRoom(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::C
|
||||
grid_layout->setSpacing(2);
|
||||
grid_layout->setContentsMargins(5, 5, 5, 5);
|
||||
|
||||
const int max_columns = 5;
|
||||
const int max_columns = 7;
|
||||
|
||||
for (int i = 0; i < emojis.size(); ++i) {
|
||||
const QString emoji = emojis[i];
|
||||
QToolButton* btn = new QToolButton(grid_container);
|
||||
btn->setText(emoji);
|
||||
btn->setFixedSize(34, 30);
|
||||
btn->setFixedSize(32, 30);
|
||||
btn->setAutoRaise(true);
|
||||
btn->setStyleSheet(QStringLiteral("font-size: 16px;"));
|
||||
|
||||
connect(btn, &QToolButton::clicked, [this, emoji, emoji_menu]() {
|
||||
ui->chat_message->insert(emoji);
|
||||
ui->chat_message->setFocus();
|
||||
emoji_menu->close(); // Close the menu after picking
|
||||
emoji_menu->close();
|
||||
});
|
||||
|
||||
// Add to grid: row = i / columns, col = i % columns
|
||||
grid_layout->addWidget(btn, i / max_columns, i % max_columns);
|
||||
}
|
||||
|
||||
@@ -265,9 +301,14 @@ ChatRoom::ChatRoom(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::C
|
||||
connect(ui->chat_history, &QTextEdit::customContextMenuRequested, this,
|
||||
&ChatRoom::OnChatContextMenu);
|
||||
connect(ui->chat_message, &QLineEdit::returnPressed, this, &ChatRoom::OnSendChat);
|
||||
connect(send_message, &QPushButton::clicked, this, &ChatRoom::OnSendChat);
|
||||
connect(ui->chat_message, &QLineEdit::textChanged, this, &ChatRoom::OnChatTextChanged);
|
||||
connect(ui->send_message, &QPushButton::clicked, this, &ChatRoom::OnSendChat);
|
||||
connect(ui->player_view, &QTreeView::doubleClicked, this, &ChatRoom::OnPlayerDoubleClicked);
|
||||
ui->horizontalLayout_3->setStretch(0, 1);
|
||||
ui->horizontalLayout_3->setStretch(1, 0);
|
||||
ui->horizontalLayout_3->setStretch(2, 0);
|
||||
send_message->setFixedSize(40, 30);
|
||||
send_message->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
UpdateTheme();
|
||||
}
|
||||
@@ -345,12 +386,12 @@ void ChatRoom::OnRoomUpdate(const Network::RoomInformation& info) {
|
||||
}
|
||||
|
||||
void ChatRoom::Disable() {
|
||||
ui->send_message->setDisabled(true);
|
||||
if (send_message) send_message->setDisabled(true);
|
||||
ui->chat_message->setDisabled(true);
|
||||
}
|
||||
|
||||
void ChatRoom::Enable() {
|
||||
ui->send_message->setEnabled(true);
|
||||
if (send_message) send_message->setEnabled(true);
|
||||
ui->chat_message->setEnabled(true);
|
||||
}
|
||||
|
||||
@@ -386,6 +427,9 @@ void ChatRoom::OnChatReceive(const Network::ChatEntry& chat) {
|
||||
}
|
||||
|
||||
AppendChatMessage(m.GetPlayerChatMessage(static_cast<u16>(player), show_timestamps, override_color));
|
||||
|
||||
// Trigger the 15-second border highlight for the person who just spoke
|
||||
HighlightPlayer(chat.nickname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,44 +512,139 @@ void ChatRoom::OnSendChat() {
|
||||
|
||||
AppendChatMessage(m.GetPlayerChatMessage(static_cast<u16>(player), show_timestamps, override_color));
|
||||
ui->chat_message->clear();
|
||||
|
||||
HighlightPlayer(nick);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatRoom::UpdateIconDisplay() {
|
||||
for (int row = 0; row < player_list->invisibleRootItem()->rowCount(); ++row) {
|
||||
QStandardItem* item = player_list->invisibleRootItem()->child(row);
|
||||
const std::string avatar_url = item->data(PlayerListItem::AvatarUrlRole).toString().toStdString();
|
||||
QColor ChatRoom::GetPlayerColor(const std::string& nickname, int index) const {
|
||||
if (color_overrides.count(nickname)) {
|
||||
return QColor(QString::fromStdString(color_overrides.at(nickname)));
|
||||
}
|
||||
const bool is_dark = QIcon::themeName().contains(QStringLiteral("dark")) ||
|
||||
QIcon::themeName().contains(QStringLiteral("midnight"));
|
||||
|
||||
QPixmap pixmap;
|
||||
static constexpr std::array<const char*, 16> default_colors = {
|
||||
"#0000FF", "#FF0000", "#8A2BE2", "#FF69B4", "#1E90FF", "#008000", "#00FF7F", "#B22222",
|
||||
"#DAA520", "#FF4500", "#2E8B57", "#5F9EA0", "#D2691E", "#9ACD32", "#FF7F50", "#FFFF00"};
|
||||
static constexpr std::array<const char*, 16> dark_colors = {
|
||||
"#559AD1", "#4EC9A8", "#D69D85", "#C6C923", "#B975B5", "#D81F1F", "#7EAE39", "#4F8733",
|
||||
"#F7CD8A", "#6FCACF", "#CE4897", "#8A2BE2", "#D2691E", "#9ACD32", "#FF7F50", "#152ccd"};
|
||||
|
||||
return QColor(is_dark ? dark_colors[index % 16] : default_colors[index % 16]);
|
||||
}
|
||||
|
||||
void ChatRoom::UpdateIconDisplay() {
|
||||
// 1. Determine canvas size based on mode
|
||||
int canvas_w, canvas_h;
|
||||
if (is_compact_mode) {
|
||||
canvas_w = std::max(80, ui->player_view->viewport()->width() - 2);
|
||||
canvas_h = 80; // Enough for avatar + name below
|
||||
} else {
|
||||
canvas_w = 54; // Just enough for 44px avatar + 4px border padding
|
||||
canvas_h = 54;
|
||||
}
|
||||
|
||||
const QSize canvas_size(canvas_w, canvas_h);
|
||||
ui->player_view->setIconSize(canvas_size);
|
||||
|
||||
for (int row = 0; row < player_list->rowCount(); ++row) {
|
||||
QStandardItem* item = player_list->item(row);
|
||||
if (!item) continue;
|
||||
|
||||
const QString nickname = item->data(PlayerListItem::NicknameRole).toString();
|
||||
const std::string nickname_std = nickname.toStdString();
|
||||
const std::string avatar_url = item->data(PlayerListItem::AvatarUrlRole).toString().toStdString();
|
||||
const QString game = item->data(PlayerListItem::GameNameRole).toString();
|
||||
const QString version = item->data(PlayerListItem::GameVersionRole).toString();
|
||||
|
||||
item->setData(is_compact_mode, Qt::UserRole + 7);
|
||||
|
||||
QPixmap avatar_pixmap;
|
||||
if (icon_cache.count(avatar_url)) {
|
||||
pixmap = icon_cache.at(avatar_url);
|
||||
avatar_pixmap = icon_cache.at(avatar_url);
|
||||
} else {
|
||||
pixmap = QIcon::fromTheme(QStringLiteral("no_avatar")).pixmap(48);
|
||||
avatar_pixmap = QIcon::fromTheme(QStringLiteral("no_avatar")).pixmap(48);
|
||||
}
|
||||
|
||||
QPixmap canvas = pixmap.copy();
|
||||
QPixmap canvas(canvas_size);
|
||||
canvas.fill(Qt::transparent);
|
||||
QPainter painter(&canvas);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setRenderHint(QPainter::TextAntialiasing);
|
||||
|
||||
const int avatar_size = 44;
|
||||
|
||||
// Center for Compact, Left-Align for Regular
|
||||
int avatar_x = is_compact_mode ? (canvas.width() - avatar_size) / 2 : 5;
|
||||
int avatar_y = is_compact_mode ? 4 : 5;
|
||||
|
||||
// --- Draw Fading Border ---
|
||||
float opacity = 0.0f;
|
||||
if (highlight_states.count(nickname_std)) {
|
||||
opacity = highlight_states[nickname_std].opacity;
|
||||
}
|
||||
|
||||
if (opacity > 0.0f) {
|
||||
QColor border_color = GetPlayerColor(nickname_std, row);
|
||||
border_color.setAlphaF(opacity);
|
||||
painter.setPen(QPen(border_color, 4));
|
||||
painter.drawEllipse(avatar_x, avatar_y, avatar_size, avatar_size);
|
||||
} else {
|
||||
painter.setPen(QPen(QColor(255, 255, 255, 30), 1));
|
||||
painter.drawEllipse(avatar_x, avatar_y, avatar_size, avatar_size);
|
||||
}
|
||||
|
||||
// --- Draw Avatar ---
|
||||
QPainterPath path;
|
||||
path.addEllipse(avatar_x + 2, avatar_y + 2, 40, 40);
|
||||
painter.setClipPath(path);
|
||||
painter.drawPixmap(avatar_x + 2, avatar_y + 2, 40, 40, avatar_pixmap);
|
||||
painter.setClipping(false);
|
||||
|
||||
// --- Draw Status Dot ---
|
||||
QString dot_type = item->data(PlayerListItem::StatusDotRole).toString();
|
||||
QColor dot_color;
|
||||
if (dot_type == QStringLiteral("🟢")) dot_color = Qt::green;
|
||||
else if (dot_type == QStringLiteral("🟡")) dot_color = Qt::yellow;
|
||||
else dot_color = Qt::gray;
|
||||
|
||||
// Draw a small "outline" circle
|
||||
painter.setBrush(QColor(30, 30, 30));
|
||||
QColor dot_color = (dot_type == QStringLiteral("🟢")) ? Qt::green :
|
||||
(dot_type == QStringLiteral("🟡")) ? Qt::yellow : Qt::gray;
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.drawEllipse(32, 32, 14, 14);
|
||||
|
||||
// Draw the actual status dot
|
||||
painter.setBrush(QColor(30, 30, 30));
|
||||
painter.drawEllipse(avatar_x + 30, avatar_y + 30, 12, 12);
|
||||
painter.setBrush(dot_color);
|
||||
painter.drawEllipse(34, 34, 10, 10);
|
||||
painter.drawEllipse(avatar_x + 32, avatar_y + 32, 8, 8);
|
||||
|
||||
if (is_compact_mode) {
|
||||
QFont font = painter.font();
|
||||
int point_size = 9;
|
||||
font.setBold(true);
|
||||
font.setPointSize(point_size);
|
||||
painter.setFont(font);
|
||||
|
||||
int text_width_limit = canvas.width() - 4;
|
||||
while (painter.fontMetrics().horizontalAdvance(nickname) > text_width_limit && point_size > 6) {
|
||||
point_size--;
|
||||
font.setPointSize(point_size);
|
||||
painter.setFont(font);
|
||||
}
|
||||
|
||||
QString elided_name = painter.fontMetrics().elidedText(nickname, Qt::ElideRight, text_width_limit);
|
||||
QRect text_rect(0, avatar_y + avatar_size + 2, canvas.width(), 20);
|
||||
|
||||
painter.setPen(QColor(0, 0, 0, 160));
|
||||
painter.drawText(text_rect.adjusted(1, 1, 1, 1), Qt::AlignCenter, elided_name);
|
||||
painter.setPen(UISettings::IsDarkTheme() ? Qt::white : Qt::black);
|
||||
painter.drawText(text_rect, Qt::AlignCenter, elided_name);
|
||||
}
|
||||
|
||||
painter.end();
|
||||
|
||||
// Set the final icon
|
||||
item->setData(canvas, Qt::DecorationRole);
|
||||
|
||||
// Tooltip logic
|
||||
QString display_game = version.isEmpty() ? game : QStringLiteral("%1 (%2)").arg(game, version);
|
||||
item->setToolTip(tr("<b>%1</b><br>%2").arg(nickname, display_game));
|
||||
|
||||
if (is_compact_mode) {
|
||||
item->setText(QString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,12 +721,33 @@ void ChatRoom::OnChatTextChanged() {
|
||||
}
|
||||
|
||||
void ChatRoom::PopupContextMenu(const QPoint& menu_location) {
|
||||
QModelIndex item = ui->player_view->indexAt(menu_location);
|
||||
if (!item.isValid()) return;
|
||||
|
||||
std::string nickname = player_list->item(item.row())->data(PlayerListItem::NicknameRole).toString().toStdString();
|
||||
QMenu context_menu;
|
||||
|
||||
// 1. Vertical Scrollbar Toggle
|
||||
QAction* scroll_action = context_menu.addAction(tr("Hide Member Scrollbar"));
|
||||
scroll_action->setCheckable(true);
|
||||
scroll_action->setChecked(member_scrollbar_hidden);
|
||||
connect(scroll_action, &QAction::triggered, [this](bool checked) {
|
||||
member_scrollbar_hidden = checked;
|
||||
ui->player_view->setVerticalScrollBarPolicy(checked ? Qt::ScrollBarAlwaysOff : Qt::ScrollBarAsNeeded);
|
||||
|
||||
if (is_compact_mode) {
|
||||
ui->player_view->setFixedWidth(checked ? 90 : 110);
|
||||
UpdateIconDisplay();
|
||||
}
|
||||
});
|
||||
context_menu.addSeparator();
|
||||
|
||||
QModelIndex item = ui->player_view->indexAt(menu_location);
|
||||
if (!item.isValid()) {
|
||||
// If clicking empty space, just show the scrollbar toggle and exit
|
||||
context_menu.exec(ui->player_view->viewport()->mapToGlobal(menu_location));
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Player-specific options (Only shows if you click a name)
|
||||
std::string nickname = player_list->item(item.row())->data(PlayerListItem::NicknameRole).toString().toStdString();
|
||||
|
||||
QAction* color_action = context_menu.addAction(tr("Set Name Color"));
|
||||
connect(color_action, &QAction::triggered, [this, nickname] {
|
||||
QColor color = QColorDialog::getColor(Qt::white, this, tr("Select Color for %1").arg(QString::fromStdString(nickname)));
|
||||
@@ -643,15 +803,21 @@ void ChatRoom::UpdateTheme() {
|
||||
const QString accent_color = Theme::GetAccentColor();
|
||||
if (UISettings::IsDarkTheme()) {
|
||||
style_sheet = QStringLiteral(R"(
|
||||
QListView, QTextEdit, QLineEdit { background-color: #252525; color: #E0E0E0; border: 1px solid #4A4A4A; border-radius: 4px; }
|
||||
QListView, QTextEdit { background-color: #252525; color: #E0E0E0; border: 1px solid #4A4A4A; border-radius: 4px; }
|
||||
QListView::item:selected { background-color: %1; }
|
||||
QPushButton { background-color: #3E3E3E; color: #E0E0E0; border: 1px solid #5A5A5A; padding: 5px; border-radius: 4px; }
|
||||
QLineEdit { background-color: #252525; color: #E0E0E0; border: 1px solid #4A4A4A; padding-left: 5px; border-radius: 4px; }
|
||||
QPushButton { background-color: #3E3E3E; color: #E0E0E0; border: 1px solid #5A5A5A; padding: 2px; border-radius: 4px; }
|
||||
QPushButton#send_message { padding: 0px; margin: 0px; min-width: 40px; max-width: 40px; }
|
||||
QToolButton { padding: 0px; margin: 0px; font-size: 14px; border: none; }
|
||||
)").arg(accent_color);
|
||||
} else {
|
||||
style_sheet = QStringLiteral(R"(
|
||||
QListView, QTextEdit, QLineEdit { background-color: #FFFFFF; color: #000000; border: 1px solid #CFCFCF; border-radius: 4px; }
|
||||
QListView, QTextEdit { background-color: #FFFFFF; color: #000000; border: 1px solid #CFCFCF; border-radius: 4px; }
|
||||
QListView::item:selected { background-color: %1; }
|
||||
QPushButton { background-color: #F0F0F0; color: #000000; border: 1px solid #BDBDBD; padding: 5px; border-radius: 4px; }
|
||||
QLineEdit { background-color: #FFFFFF; color: #000000; border: 1px solid #CFCFCF; padding-left: 5px; border-radius: 4px; }
|
||||
QPushButton { background-color: #F0F0F0; color: #000000; border: 1px solid #BDBDBD; padding: 2px; border-radius: 4px; }
|
||||
QPushButton#send_message { padding: 0px; margin: 0px; min-width: 40px; max-width: 40px; }
|
||||
QToolButton { padding: 0px; margin: 0px; font-size: 14px; border: none; }
|
||||
)").arg(accent_color);
|
||||
}
|
||||
this->setStyleSheet(style_sheet);
|
||||
@@ -660,9 +826,47 @@ void ChatRoom::UpdateTheme() {
|
||||
void ChatRoom::OnChatContextMenu(const QPoint& menu_location) {
|
||||
QMenu* context_menu = ui->chat_history->createStandardContextMenu(menu_location);
|
||||
context_menu->addSeparator();
|
||||
|
||||
QAction* clear_action = context_menu->addAction(tr("Clear Chat History"));
|
||||
connect(clear_action, &QAction::triggered, this, &ChatRoom::Clear);
|
||||
|
||||
QAction* compact_action = context_menu->addAction(tr("Compact Member List"));
|
||||
compact_action->setCheckable(true);
|
||||
compact_action->setChecked(is_compact_mode);
|
||||
connect(compact_action, &QAction::triggered, [this](bool checked) {
|
||||
this->is_compact_mode = checked;
|
||||
if (checked) {
|
||||
int view_w = member_scrollbar_hidden ? 90 : 110;
|
||||
ui->player_view->setFixedWidth(view_w);
|
||||
ui->player_view->setIndentation(0);
|
||||
ui->player_view->setHeaderHidden(true);
|
||||
ui->player_view->setRootIsDecorated(false);
|
||||
ui->player_view->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
ui->player_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
ui->player_view->setStyleSheet(QStringLiteral("QTreeView::item { padding: 0px; }"));
|
||||
} else {
|
||||
ui->player_view->setMinimumWidth(160);
|
||||
ui->player_view->setMaximumWidth(1000);
|
||||
ui->player_view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
ui->player_view->setIndentation(20);
|
||||
ui->player_view->setHeaderHidden(false);
|
||||
ui->player_view->setRootIsDecorated(true);
|
||||
ui->player_view->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||
ui->player_view->header()->setStretchLastSection(false);
|
||||
ui->player_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
ui->player_view->setStyleSheet(QString());
|
||||
}
|
||||
|
||||
UpdateIconDisplay();
|
||||
|
||||
// Refresh player list to restore text in regular mode
|
||||
if (room_network) {
|
||||
if (auto room = room_network->GetRoomMember().lock()) {
|
||||
SetPlayerList(room->GetMemberInformation());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
QAction* mute_action = context_menu->addAction(tr("Hide Future Messages"));
|
||||
mute_action->setCheckable(true);
|
||||
mute_action->setChecked(chat_muted);
|
||||
@@ -692,3 +896,80 @@ void ChatRoom::OnPlayerDoubleClicked(const QModelIndex& index) {
|
||||
ui->chat_message->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatRoom::HighlightPlayer(const std::string& nickname) {
|
||||
auto& state = highlight_states[nickname];
|
||||
|
||||
// 1. Clean up existing animations/timers
|
||||
// QPointer automatically becomes null if the animation was already deleted
|
||||
if (state.animation) {
|
||||
state.animation->stop();
|
||||
state.animation->deleteLater();
|
||||
}
|
||||
|
||||
if (state.linger_timer) {
|
||||
state.linger_timer->stop();
|
||||
state.linger_timer->deleteLater();
|
||||
state.linger_timer = nullptr;
|
||||
}
|
||||
|
||||
// 2. Create Fade-In Animation
|
||||
auto* fadeIn = new QVariantAnimation(this);
|
||||
state.animation = fadeIn;
|
||||
fadeIn->setDuration(400);
|
||||
fadeIn->setStartValue(state.opacity);
|
||||
fadeIn->setEndValue(1.0f);
|
||||
fadeIn->setEasingCurve(QEasingCurve::OutQuad);
|
||||
|
||||
connect(fadeIn, &QVariantAnimation::valueChanged, [this, nickname](const QVariant& value) {
|
||||
if (highlight_states.count(nickname)) {
|
||||
highlight_states[nickname].opacity = value.toFloat();
|
||||
UpdateIconDisplay();
|
||||
}
|
||||
});
|
||||
|
||||
connect(fadeIn, &QVariantAnimation::finished, [this, nickname]() {
|
||||
if (!highlight_states.count(nickname)) return;
|
||||
|
||||
auto& s1 = highlight_states[nickname];
|
||||
|
||||
// Cleanup the finished animation
|
||||
if (s1.animation) s1.animation->deleteLater();
|
||||
|
||||
s1.linger_timer = new QTimer(this);
|
||||
s1.linger_timer->setSingleShot(true);
|
||||
|
||||
connect(s1.linger_timer, &QTimer::timeout, [this, nickname]() {
|
||||
if (!highlight_states.count(nickname)) return;
|
||||
auto& s2 = highlight_states[nickname];
|
||||
|
||||
auto* fadeOut = new QVariantAnimation(this);
|
||||
s2.animation = fadeOut;
|
||||
fadeOut->setDuration(400);
|
||||
fadeOut->setStartValue(1.0f);
|
||||
fadeOut->setEndValue(0.0f);
|
||||
fadeOut->setEasingCurve(QEasingCurve::OutQuad);
|
||||
|
||||
connect(fadeOut, &QVariantAnimation::valueChanged, [this, nickname](const QVariant& value) {
|
||||
if (highlight_states.count(nickname)) {
|
||||
highlight_states[nickname].opacity = value.toFloat();
|
||||
UpdateIconDisplay();
|
||||
}
|
||||
});
|
||||
|
||||
connect(fadeOut, &QVariantAnimation::finished, [this, nickname]() {
|
||||
if (highlight_states.count(nickname)) {
|
||||
auto& final_state = highlight_states[nickname];
|
||||
if (final_state.animation) final_state.animation->deleteLater();
|
||||
highlight_states.erase(nickname);
|
||||
}
|
||||
UpdateIconDisplay();
|
||||
});
|
||||
|
||||
fadeOut->start();
|
||||
});
|
||||
s1.linger_timer->start(10000);
|
||||
});
|
||||
|
||||
fadeIn->start();
|
||||
}
|
||||
|
||||
@@ -4,28 +4,33 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono> // time tracking
|
||||
#include <vector> // storing timestamps
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <QDialog>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <vector>
|
||||
|
||||
#include <QAbstractAnimation>
|
||||
#include <QEasingCurve>
|
||||
#include <QPointer>
|
||||
#include <QStandardItemModel>
|
||||
#include <QVariant>
|
||||
#include <QTimer>
|
||||
#include <QVariantAnimation>
|
||||
#include <QWidget>
|
||||
|
||||
#include "network/network.h"
|
||||
|
||||
namespace Ui {
|
||||
class ChatRoom;
|
||||
class ChatRoom;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class AnnounceMultiplayerSession;
|
||||
class AnnounceMultiplayerSession;
|
||||
}
|
||||
|
||||
class QPushButton;
|
||||
class ConnectionError;
|
||||
class ComboBoxProxyModel;
|
||||
|
||||
class ChatMessage;
|
||||
|
||||
class ChatRoom : public QWidget {
|
||||
@@ -33,14 +38,14 @@ class ChatRoom : public QWidget {
|
||||
|
||||
public:
|
||||
explicit ChatRoom(QWidget* parent);
|
||||
~ChatRoom();
|
||||
|
||||
void Initialize(Network::RoomNetwork* room_network);
|
||||
void Shutdown();
|
||||
void RetranslateUi();
|
||||
void SetPlayerList(const Network::RoomMember::MemberList& member_list);
|
||||
void Clear();
|
||||
void AppendStatusMessage(const QString& msg);
|
||||
~ChatRoom();
|
||||
|
||||
void SetModPerms(bool is_mod);
|
||||
void UpdateIconDisplay();
|
||||
|
||||
@@ -63,20 +68,35 @@ signals:
|
||||
void UserPinged();
|
||||
|
||||
private:
|
||||
static constexpr u32 max_chat_lines = 1000;
|
||||
void AppendChatMessage(const QString&);
|
||||
bool ValidateMessage(const std::string&);
|
||||
void SendModerationRequest(Network::RoomMessageTypes type, const std::string& nickname);
|
||||
QColor GetPlayerColor(const std::string& nickname, int index) const;
|
||||
void HighlightPlayer(const std::string& nickname);
|
||||
|
||||
QPushButton* send_message = nullptr;
|
||||
static constexpr u32 max_chat_lines = 1000;
|
||||
bool has_mod_perms = false;
|
||||
QStandardItemModel* player_list;
|
||||
std::unique_ptr<Ui::ChatRoom> ui;
|
||||
std::unordered_set<std::string> block_list;
|
||||
std::unordered_map<std::string, QPixmap> icon_cache;
|
||||
std::unordered_map<std::string, std::string> color_overrides;
|
||||
|
||||
// Highlight tracking with smooth fade-in/out
|
||||
struct HighlightState {
|
||||
float opacity = 0.0f;
|
||||
QPointer<QVariantAnimation> animation;
|
||||
QTimer* linger_timer = nullptr;
|
||||
};
|
||||
std::unordered_map<std::string, HighlightState> highlight_states;
|
||||
|
||||
bool is_compact_mode = false;
|
||||
bool member_scrollbar_hidden = false;
|
||||
bool chat_muted = false;
|
||||
bool show_timestamps = true;
|
||||
Network::RoomNetwork* room_network = nullptr;
|
||||
|
||||
std::vector<std::chrono::steady_clock::time_point> sent_message_timestamps;
|
||||
static constexpr size_t MAX_MESSAGES_PER_INTERVAL = 3;
|
||||
static constexpr std::chrono::seconds THROTTLE_INTERVAL{5};
|
||||
|
||||
@@ -37,14 +37,7 @@
|
||||
<item>
|
||||
<widget class="QLineEdit" name="chat_message">
|
||||
<property name="placeholderText">
|
||||
<string>Send Chat Message</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="send_message">
|
||||
<property name="text">
|
||||
<string>Send Message</string>
|
||||
<string>Type Here...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include "citron/multiplayer/chat_room.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <QApplication>
|
||||
#include <QGraphicsDropShadowEffect>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QScreen>
|
||||
@@ -23,24 +24,25 @@ MultiplayerRoomOverlay::MultiplayerRoomOverlay(QWidget* parent)
|
||||
|
||||
main_window = qobject_cast<GMainWindow*>(parent->window());
|
||||
|
||||
// Switched to Qt::Tool to allow keyboard focus for typing in chat
|
||||
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
// Set smaller sizes for Steam Deck
|
||||
setMinimumSize(240, 180);
|
||||
resize(260, 220);
|
||||
|
||||
main_layout = new QGridLayout(this);
|
||||
main_layout->setContentsMargins(padding, padding, 0, 0);
|
||||
main_layout->setSpacing(6);
|
||||
main_layout->setContentsMargins(padding, padding, padding, padding);
|
||||
main_layout->setSpacing(8);
|
||||
|
||||
players_online_label = new QLabel(this);
|
||||
|
||||
QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect(this);
|
||||
shadow->setBlurRadius(6);
|
||||
shadow->setColor(Qt::black);
|
||||
shadow->setOffset(0, 0);
|
||||
players_online_label->setGraphicsEffect(shadow);
|
||||
|
||||
chat_room_widget = new ChatRoom(this);
|
||||
size_grip = new QSizeGrip(this);
|
||||
|
||||
players_online_label->setFont(QFont(QString::fromUtf8("Segoe UI"), 10, QFont::Bold));
|
||||
players_online_label->setText(QString::fromUtf8("Players Online: 0"));
|
||||
players_online_label->setFont(QFont(QString::fromUtf8("Segoe UI"), 12, QFont::Bold));
|
||||
players_online_label->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
|
||||
size_grip->setFixedSize(16, 16);
|
||||
@@ -66,21 +68,19 @@ MultiplayerRoomOverlay::MultiplayerRoomOverlay(QWidget* parent)
|
||||
|
||||
if (main_window) {
|
||||
connect(main_window, &GMainWindow::themeChanged, this, &MultiplayerRoomOverlay::UpdateTheme);
|
||||
connect(main_window, &GMainWindow::EmulationStarting, this, &MultiplayerRoomOverlay::OnEmulationStarting);
|
||||
connect(main_window, &GMainWindow::EmulationStopping, this, &MultiplayerRoomOverlay::OnEmulationStopping);
|
||||
}
|
||||
UpdateTheme();
|
||||
|
||||
const bool is_gamescope = UISettings::IsGamescope();
|
||||
if (is_gamescope) {
|
||||
setMinimumSize(320, 260);
|
||||
resize(600, 520);
|
||||
|
||||
players_online_label->setFont(QFont(QString::fromUtf8("Segoe UI"), 11, QFont::Bold));
|
||||
|
||||
this->padding = 12;
|
||||
main_layout->setContentsMargins(padding, padding, padding, padding);
|
||||
setMinimumSize(450, 350);
|
||||
resize(700, 550);
|
||||
this->padding = 15;
|
||||
} else {
|
||||
setMinimumSize(280, 220);
|
||||
resize(320, 280);
|
||||
setMinimumSize(360, 260);
|
||||
resize(420, 300);
|
||||
}
|
||||
|
||||
UpdatePosition();
|
||||
@@ -91,17 +91,30 @@ MultiplayerRoomOverlay::~MultiplayerRoomOverlay() {
|
||||
}
|
||||
|
||||
void MultiplayerRoomOverlay::OnEmulationStarting() {
|
||||
// The connection is now managed by the overlay's visibility.
|
||||
// Force a UI refresh immediately when a game starts
|
||||
UpdateRoomData();
|
||||
}
|
||||
|
||||
void MultiplayerRoomOverlay::OnEmulationStopping() {
|
||||
// The connection should persist even when emulation stops.
|
||||
update_timer.stop();
|
||||
|
||||
if (room_member && room_member->IsConnected()) {
|
||||
// Only send if the state is stable
|
||||
room_member->SendGameInfo({});
|
||||
}
|
||||
|
||||
// Clear the UI text but don't force a full room data poll yet
|
||||
players_online_label->setText(tr("Emulation Stopped."));
|
||||
|
||||
// Resume polling after 1 second once the LDN service has safely detached
|
||||
QTimer::singleShot(1000, this, [this] {
|
||||
if (is_visible) update_timer.start(500);
|
||||
});
|
||||
}
|
||||
|
||||
void MultiplayerRoomOverlay::SetVisible(bool visible) {
|
||||
if (is_visible == visible) return;
|
||||
is_visible = visible;
|
||||
|
||||
if (visible) {
|
||||
show();
|
||||
ConnectToRoom();
|
||||
@@ -117,21 +130,36 @@ void MultiplayerRoomOverlay::paintEvent(QPaintEvent* event) {
|
||||
Q_UNUSED(event)
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
QRect background_rect = rect();
|
||||
|
||||
// Move the top of the box down so the text floats above it
|
||||
int label_area_height = players_online_label->height() + main_layout->spacing() + padding;
|
||||
background_rect.setTop(label_area_height);
|
||||
|
||||
QPainterPath background_path;
|
||||
background_path.addRoundedRect(rect(), corner_radius, corner_radius);
|
||||
background_path.addRoundedRect(background_rect, corner_radius, corner_radius);
|
||||
|
||||
painter.fillPath(background_path, background_color);
|
||||
painter.setPen(QPen(border_color, border_width));
|
||||
painter.drawPath(background_path);
|
||||
}
|
||||
|
||||
void MultiplayerRoomOverlay::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); if (!has_been_moved) UpdatePosition(); }
|
||||
bool MultiplayerRoomOverlay::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::MouseButtonPress) { if (chat_room_widget->hasFocus()) { chat_room_widget->clearFocus(); } } return QObject::eventFilter(watched, event); }
|
||||
void MultiplayerRoomOverlay::resizeEvent(QResizeEvent* event) {
|
||||
QWidget::resizeEvent(event);
|
||||
if (!has_been_moved) UpdatePosition();
|
||||
}
|
||||
|
||||
bool MultiplayerRoomOverlay::eventFilter(QObject* watched, QEvent* event) {
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
if (chat_room_widget->hasFocus()) chat_room_widget->clearFocus();
|
||||
}
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void MultiplayerRoomOverlay::mousePressEvent(QMouseEvent* event) {
|
||||
if (event->button() == Qt::LeftButton && !size_grip->geometry().contains(event->pos())) {
|
||||
const bool is_gamescope = !qgetenv("GAMESCOPE_WIDTH").isEmpty() || qgetenv("XDG_CURRENT_DESKTOP") == "gamescope";
|
||||
if (is_gamescope) {
|
||||
if (UISettings::IsGamescope()) {
|
||||
is_dragging = true;
|
||||
drag_start_pos = event->globalPosition().toPoint() - this->pos();
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
@@ -171,36 +199,27 @@ void MultiplayerRoomOverlay::ConnectToRoom() {
|
||||
if (!main_window) return;
|
||||
multiplayer_state = main_window->GetMultiplayerState();
|
||||
if (!multiplayer_state) return;
|
||||
|
||||
if (multiplayer_state->IsClientRoomVisible()) {
|
||||
chat_room_widget->setEnabled(false);
|
||||
chat_room_widget->Clear();
|
||||
chat_room_widget->AppendStatusMessage(tr("In order to use chat functionality in the Overlay, please close the Multiplayer Room Window."));
|
||||
chat_room_widget->AppendStatusMessage(tr("Please close the Multiplayer Room Window to use the Overlay."));
|
||||
return;
|
||||
}
|
||||
|
||||
chat_room_widget->setEnabled(true);
|
||||
auto& room_network = multiplayer_state->GetRoomNetwork();
|
||||
room_member = room_network.GetRoomMember().lock();
|
||||
|
||||
if (room_member) {
|
||||
if (!is_chat_initialized) {
|
||||
chat_room_widget->Initialize(&room_network);
|
||||
is_chat_initialized = true;
|
||||
}
|
||||
} else {
|
||||
chat_room_widget->Clear();
|
||||
chat_room_widget->AppendStatusMessage(tr("Not connected to a room."));
|
||||
ClearUI();
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerRoomOverlay::DisconnectFromRoom() {
|
||||
// Tell the chat widget to disconnect its signals *before*
|
||||
// we reset our own state.
|
||||
if (is_chat_initialized && chat_room_widget) {
|
||||
chat_room_widget->Shutdown();
|
||||
}
|
||||
|
||||
if (is_chat_initialized && chat_room_widget) chat_room_widget->Shutdown();
|
||||
ClearUI();
|
||||
room_member.reset();
|
||||
multiplayer_state = nullptr;
|
||||
@@ -208,70 +227,90 @@ void MultiplayerRoomOverlay::DisconnectFromRoom() {
|
||||
}
|
||||
|
||||
void MultiplayerRoomOverlay::ClearUI() {
|
||||
players_online_label->setText(QString::fromUtf8("Players Online: 0"));
|
||||
players_online_label->setText(tr("Not connected to a room."));
|
||||
chat_room_widget->Clear();
|
||||
chat_room_widget->AppendStatusMessage(tr("Not connected to a room."));
|
||||
chat_room_widget->SetPlayerList({});
|
||||
}
|
||||
|
||||
void MultiplayerRoomOverlay::UpdateRoomData() {
|
||||
if (!multiplayer_state) {
|
||||
ConnectToRoom();
|
||||
return;
|
||||
}
|
||||
|
||||
if (multiplayer_state->IsClientRoomVisible()) {
|
||||
if (chat_room_widget->isEnabled()) {
|
||||
chat_room_widget->setEnabled(false);
|
||||
chat_room_widget->Clear();
|
||||
chat_room_widget->AppendStatusMessage(tr("In order to use chat functionality in the Overlay, please close the Multiplayer Room Window."));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chat_room_widget->isEnabled()) {
|
||||
ConnectToRoom();
|
||||
}
|
||||
if (!multiplayer_state) { ConnectToRoom(); return; }
|
||||
if (multiplayer_state->IsClientRoomVisible()) { chat_room_widget->setEnabled(false); return; }
|
||||
if (!chat_room_widget->isEnabled()) ConnectToRoom();
|
||||
|
||||
if (room_member && room_member->GetState() >= Network::RoomMember::State::Joined) {
|
||||
const auto& members = room_member->GetMemberInformation();
|
||||
QString label_text = QString::fromStdString("Players Online: <span style='color: #4CAF50;'>%1</span>").arg(members.size());
|
||||
players_online_label->setText(label_text);
|
||||
|
||||
if (chat_room_widget->isEnabled()) {
|
||||
chat_room_widget->SetPlayerList(members);
|
||||
AnnounceMultiplayerRoom::GameInfo local_game_info;
|
||||
std::string my_nick = room_member->GetNickname();
|
||||
for (const auto& m : members) {
|
||||
if (m.nickname == my_nick) {
|
||||
local_game_info = m.game_info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ClearUI();
|
||||
room_member.reset();
|
||||
|
||||
// Ensure we don't think we are emulating if the status is "Not playing a game"
|
||||
bool is_emulating = !local_game_info.name.empty() &&
|
||||
local_game_info.name != tr("Not playing a game").toStdString();
|
||||
|
||||
int point_size = UISettings::IsGamescope() ? 11 : 10;
|
||||
if (this->width() < 340) point_size = 10;
|
||||
|
||||
QFont font = players_online_label->font();
|
||||
font.setPointSize(point_size);
|
||||
players_online_label->setFont(font);
|
||||
|
||||
QString label_text;
|
||||
if (!is_emulating) {
|
||||
players_online_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
label_text = tr("<b>Players In Room: <span style='color: #00FF00;'>%1</span></b>").arg(members.size());
|
||||
} else {
|
||||
players_online_label->setAlignment(Qt::AlignCenter);
|
||||
int g = 0, d = 0, o = 0;
|
||||
for (const auto& m : members) {
|
||||
bool m_playing = !m.game_info.name.empty() &&
|
||||
m.game_info.name != tr("Not playing a game").toStdString();
|
||||
|
||||
if (m_playing && m.game_info.name == local_game_info.name) {
|
||||
if (m.game_info.version == local_game_info.version) g++; else d++;
|
||||
} else {
|
||||
o++;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList parts;
|
||||
if (g > 0) parts << tr("<b>In-Game: <span style='color: #00FF00;'>%1</span></b>").arg(g);
|
||||
if (d > 0) parts << tr("<b>Different Update: <span style='color: #FFD700;'>%1</span></b>").arg(d);
|
||||
if (o > 0) parts << tr("<b>Other: <span style='color: #E0E0E0;'>%1</span></b>").arg(o);
|
||||
|
||||
QString sep = QStringLiteral(" • ");
|
||||
if (this->width() < 400) sep = QStringLiteral(" • ");
|
||||
|
||||
label_text = parts.join(sep);
|
||||
}
|
||||
players_online_label->setText(label_text);
|
||||
if (chat_room_widget->isEnabled()) chat_room_widget->SetPlayerList(members);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerRoomOverlay::UpdatePosition() {
|
||||
if (!main_window) return;
|
||||
if (!has_been_moved) {
|
||||
QPoint main_window_pos = main_window->mapToGlobal(QPoint(0, 0));
|
||||
move(main_window_pos.x() + main_window->width() - this->width() - 10, main_window_pos.y() + 10);
|
||||
QPoint win_pos = main_window->mapToGlobal(QPoint(0, 0));
|
||||
move(win_pos.x() + main_window->width() - width() - 15, win_pos.y() + 15);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerRoomOverlay::UpdateTheme() {
|
||||
if (UISettings::IsDarkTheme()) {
|
||||
// Dark Theme Colors
|
||||
background_color = QColor(20, 20, 20, 180); // 180 alpha for transparency
|
||||
border_color = QColor(60, 60, 60, 120);
|
||||
players_online_label->setStyleSheet(QStringLiteral("color: #E0E0E0;"));
|
||||
background_color = QColor(25, 25, 25, 225);
|
||||
border_color = QColor(255, 255, 255, 40);
|
||||
players_online_label->setStyleSheet(QStringLiteral("color: #FFFFFF;"));
|
||||
} else {
|
||||
// Light Theme Colors
|
||||
background_color = QColor(245, 245, 245, 200); // 200 alpha for transparency
|
||||
border_color = QColor(200, 200, 200, 120);
|
||||
players_online_label->setStyleSheet(QStringLiteral("color: #141414;"));
|
||||
background_color = QColor(245, 245, 245, 235);
|
||||
border_color = QColor(0, 0, 0, 50);
|
||||
players_online_label->setStyleSheet(QStringLiteral("color: #111111;"));
|
||||
}
|
||||
|
||||
// The chat widget is a separate custom widget, so we need to tell it to update its theme too.
|
||||
if (chat_room_widget) {
|
||||
chat_room_widget->UpdateTheme();
|
||||
}
|
||||
|
||||
update(); // Force a repaint of the overlay itself
|
||||
if (chat_room_widget) chat_room_widget->UpdateTheme();
|
||||
update();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user