From bd27df2f3318ae9db9b7c91e9f9ec846a6eef171 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sat, 6 Aug 2022 19:25:04 +0200 Subject: [PATCH] Move unit_list_widget context menu handling to the widget It was previously operating on single items. In order to allow for multiple selection, we need to handle this at the list level instead. See #805. --- client/citydlg.cpp | 385 ++++++++++++++++++++++----------------------- client/citydlg.h | 41 ++--- 2 files changed, 205 insertions(+), 221 deletions(-) diff --git a/client/citydlg.cpp b/client/citydlg.cpp index a46b14662c..7a55ce495f 100644 --- a/client/citydlg.cpp +++ b/client/citydlg.cpp @@ -53,6 +53,7 @@ #include "text.h" #include "tooltips.h" #include "top_bar.h" +#include "unitlist.h" extern QString split_text(const QString &text, bool cut); extern QString cut_helptext(const QString &text); @@ -67,9 +68,50 @@ unit_list_widget::unit_list_widget(QWidget *parent) : QListWidget(parent) setWrapping(true); setMovement(QListView::Static); - setContextMenuPolicy(Qt::CustomContextMenu); - connect(this, &QWidget::customContextMenuRequested, this, - &unit_list_widget::context_menu); + connect(this, &QListWidget::itemDoubleClicked, this, + &unit_list_widget::activate); + + // Create the context menu + setContextMenuPolicy(Qt::ActionsContextMenu); + + m_activate = new QAction(_("Activate unit"), this); + connect(m_activate, &QAction::triggered, this, + &unit_list_widget::activate); + addAction(m_activate); + + m_sentry = new QAction(_("Sentry unit"), this); + connect(m_sentry, &QAction::triggered, this, &unit_list_widget::sentry); + addAction(m_sentry); + + m_fortify = new QAction(_("Fortify unit"), this); + connect(m_fortify, &QAction::triggered, this, &unit_list_widget::fortify); + addAction(m_fortify); + + m_disband = new QAction(_("Disband unit"), this); + connect(m_disband, &QAction::triggered, this, &unit_list_widget::disband); + addAction(m_disband); + + m_change_homecity = new QAction(_("Set homecity"), this); + connect(m_change_homecity, &QAction::triggered, this, + &unit_list_widget::change_homecity); + addAction(m_change_homecity); + + m_load = new QAction(_("Load"), this); + connect(m_load, &QAction::triggered, this, &unit_list_widget::load); + addAction(m_load); + + m_unload = new QAction(_("Unload"), this); + connect(m_unload, &QAction::triggered, this, &unit_list_widget::unload); + addAction(m_unload); + + m_unload_all = new QAction(_("Unload Transporter"), this); + connect(m_unload_all, &QAction::triggered, this, + &unit_list_widget::unload_all); + addAction(m_unload_all); + + m_upgrade = new QAction(_("Upgrade Unit"), this); + connect(m_upgrade, &QAction::triggered, this, &unit_list_widget::upgrade); + addAction(m_upgrade); } /** @@ -116,19 +158,148 @@ void unit_list_widget::set_units(unit_list *units) } /** - * Pops up the context menu when an item is clicked. + * Finds the list of currently selected units + */ +std::vector unit_list_widget::selected_playable_units() const +{ + if (!can_client_issue_orders()) { + return {}; + } + + auto units = std::vector(); + for (const auto item : selectedItems()) { + if (const auto *unit_item = dynamic_cast(item)) { + const auto unit = unit_item->unit(); + if (unit_owner(unit) == client_player()) { + units.push_back(unit); + } + } + } + return units; +} + +/** + * Popups MessageBox for disbanding unit and disbands it + */ +void unit_list_widget::disband() +{ + popup_disband_dialog(selected_playable_units()); +} + +/** + * Loads unit into some transport + */ +void unit_list_widget::load() +{ + for (const auto unit : selected_playable_units()) { + request_transport(unit, unit_tile(unit)); + } +} + +/** + * Unloads unit + */ +void unit_list_widget::unload() +{ + for (const auto unit : selected_playable_units()) { + request_unit_unload(unit); + } +} + +/** + * Unloads all units from transporter + */ +void unit_list_widget::unload_all() +{ + for (const auto unit : selected_playable_units()) { + request_unit_unload_all(unit); + } +} + +/** + * Upgrades unit + */ +void unit_list_widget::upgrade() +{ + popup_upgrade_dialog(selected_playable_units()); +} + +/** + * Changes home city for given unit + */ +void unit_list_widget::change_homecity() +{ + for (const auto unit : selected_playable_units()) { + request_unit_change_homecity(unit); + } +} + +/** + * Activates unit and closes city dialog + */ +void unit_list_widget::activate() +{ + const auto selection = selected_playable_units(); + + unit_focus_set(nullptr); // Clear + for (const auto unit : selection) { + unit_focus_add(unit); + } + + if (!selection.empty()) { + queen()->city_overlay->dont_focus = true; + popdown_city_dialog(); + } +} + +/** + * Fortifies unit in city dialog */ -void unit_list_widget::context_menu(const QPoint &loc) +void unit_list_widget::fortify() { - auto *item = itemAt(loc); - // Maybe there was no unit under the mouse - if (auto *unit_item = dynamic_cast(item)) { - // Maybe we can't give orders to this unit - if (auto *menu = unit_item->menu()) { - // OK, show the menu - menu->popup(mapToGlobal(loc)); + for (const auto unit : selected_playable_units()) { + request_unit_fortify(unit); + } +} + +/** + * Sentries unit in city dialog + */ +void unit_list_widget::sentry() +{ + for (const auto unit : selected_playable_units()) { + request_unit_sentry(unit); + } +} + +/** + * Updates the context menu to match the current selection + */ +void unit_list_widget::selectionChanged(const QItemSelection &selected, + const QItemSelection &deselected) +{ + Q_UNUSED(selected); + Q_UNUSED(deselected); + + // If we can't do anything, don't pretend we can + const auto units = selected_playable_units(); + if (units.empty()) { + for (auto action : actions()) { + action->setVisible(false); } + return; } + + // Build the menu according to what the selected units can do + m_activate->setVisible(true); + m_sentry->setVisible(can_units_do_activity(units, ACTIVITY_SENTRY)); + m_fortify->setVisible(can_units_do_activity(units, ACTIVITY_FORTIFYING)); + m_disband->setVisible( + units_can_do_action(units, ACTION_DISBAND_UNIT, true)); + m_load->setVisible(units_can_load(units)); + m_unload->setVisible(units_can_unload(units)); + m_unload_all->setVisible(units_are_occupied(units)); + m_upgrade->setVisible(units_can_upgrade(units)); } /** @@ -680,186 +851,11 @@ void impr_item::mouseDoubleClickEvent(QMouseEvent *event) /** Class representing one unit, manages the context menu */ -unit_list_item::unit_list_item(unit *punit) : m_unit(punit) +unit_list_item::unit_list_item(::unit *punit) : m_unit(punit) { - create_menu(); setToolTip(unit_description(m_unit)); } -/** - Initializes context menu - */ -void unit_list_item::create_menu() -{ - if (!can_issue_orders()) { - return; - } - - m_menu = new QMenu; - - auto *activate_and_close_action = new QAction(_("Activate unit"), this); - connect(activate_and_close_action, &QAction::triggered, this, - &unit_list_item::activate_and_close_dialog); - m_menu->addAction(activate_and_close_action); - - if (can_unit_do_activity(m_unit, ACTIVITY_SENTRY)) { - auto *sentry_action = new QAction(_("Sentry unit"), this); - connect(sentry_action, &QAction::triggered, this, - &unit_list_item::sentry); - m_menu->addAction(sentry_action); - } - - if (can_unit_do_activity(m_unit, ACTIVITY_FORTIFYING)) { - auto *fortify_action = new QAction(_("Fortify unit"), this); - connect(fortify_action, &QAction::triggered, this, - &unit_list_item::fortify); - m_menu->addAction(fortify_action); - } - - if (unit_can_do_action(m_unit, ACTION_DISBAND_UNIT)) { - auto *disband_action = new QAction(_("Disband unit"), this); - connect(disband_action, &QAction::triggered, this, - &unit_list_item::disband); - m_menu->addAction(disband_action); - } - - if (can_unit_change_homecity(m_unit)) { - auto *change_homecity_action = - new QAction(action_id_name_translation(ACTION_HOME_CITY), this); - connect(change_homecity_action, &QAction::triggered, this, - &unit_list_item::change_homecity); - m_menu->addAction(change_homecity_action); - } - - if (units_can_load({m_unit})) { - auto *load_action = new QAction(_("Load"), this); - connect(load_action, &QAction::triggered, this, &unit_list_item::load); - m_menu->addAction(load_action); - } - - if (units_can_unload({m_unit})) { - auto *unload_action = new QAction(_("Unload"), this); - connect(unload_action, &QAction::triggered, this, - &unit_list_item::unload); - m_menu->addAction(unload_action); - } - - if (units_are_occupied({m_unit})) { - auto *unload_all_action = - new QAction(_("Unload All From Transporter"), this); - connect(unload_all_action, &QAction::triggered, this, - &unit_list_item::unload_all); - m_menu->addAction(unload_all_action); - } - - if (units_can_upgrade({m_unit})) { - auto *upgrade_action = new QAction(_("Upgrade Unit"), this); - connect(upgrade_action, &QAction::triggered, this, - &unit_list_item::upgrade); - m_menu->addAction(upgrade_action); - } -} - -/** - Can we give orders to the unit? - */ -bool unit_list_item::can_issue_orders() const -{ - return can_client_issue_orders() && unit_owner(m_unit) == client_player(); -} - -/** - Popups MessageBox for disbanding unit and disbands it - */ -void unit_list_item::disband() -{ - if (can_issue_orders()) { - popup_disband_dialog({m_unit}); - } -} - -/** - Loads unit into some transport - */ -void unit_list_item::load() -{ - if (can_issue_orders()) { - request_transport(m_unit, unit_tile(m_unit)); - } -} - -/** - Unloads unit - */ -void unit_list_item::unload() -{ - if (can_issue_orders()) { - request_unit_unload(m_unit); - } -} - -/** - Unloads all units from transporter - */ -void unit_list_item::unload_all() -{ - if (can_issue_orders()) { - request_unit_unload_all(m_unit); - } -} - -/** - Upgrades unit - */ -void unit_list_item::upgrade() -{ - if (can_issue_orders()) { - popup_upgrade_dialog({m_unit}); - } -} - -/** - Changes home city for given unit - */ -void unit_list_item::change_homecity() -{ - if (can_issue_orders()) { - request_unit_change_homecity(m_unit); - } -} - -/** - Activates unit and closes city dialog - */ -void unit_list_item::activate_and_close_dialog() -{ - if (can_issue_orders()) { - unit_focus_set(m_unit); - queen()->city_overlay->dont_focus = true; - popdown_city_dialog(); - } -} - -/** - Fortifies unit in city dialog - */ -void unit_list_item::fortify() -{ - if (can_issue_orders()) { - request_unit_fortify(m_unit); - } -} - -/** - Sentries unit in city dialog - */ -void unit_list_item::sentry() -{ - if (can_issue_orders()) { - request_unit_sentry(m_unit); - } -} - cityIconInfoLabel::cityIconInfoLabel(QWidget *parent) : QWidget(parent) { auto f = fcFont::instance()->getFont(fonts::default_font); @@ -1404,20 +1400,7 @@ city_dialog::city_dialog(QWidget *parent) : QWidget(parent) ui.tabs_right->setTabText(0, _("General")); ui.tabs_right->setTabText(1, _("Citizens")); - connect(ui.present_units_list, &QListWidget::itemDoubleClicked, - [](QListWidgetItem *item) { - if (auto *uitem = dynamic_cast(item)) { - uitem->activate_and_close_dialog(); - } - }); ui.present_units_list->set_oneliner(true); - - connect(ui.supported_units, &QListWidget::itemDoubleClicked, - [](QListWidgetItem *item) { - if (auto *uitem = dynamic_cast(item)) { - uitem->activate_and_close_dialog(); - } - }); ui.supported_units->set_show_upkeep(true); installEventFilter(this); diff --git a/client/citydlg.h b/client/citydlg.h index a331374ee1..a9a7829209 100644 --- a/client/citydlg.h +++ b/client/citydlg.h @@ -73,14 +73,29 @@ class unit_list_widget : public QListWidget { void set_show_upkeep(bool show) { m_show_upkeep = show; } void set_units(unit_list *units); + std::vector selected_playable_units() const; -private slots: - void context_menu(const QPoint &loc); +private: + void disband(); + void change_homecity(); + void activate(); + void sentry(); + void fortify(); + void upgrade(); + void load(); + void unload(); + void unload_all(); + + void selectionChanged(const QItemSelection &selected, + const QItemSelection &deselected) override; QPixmap create_unit_image(const unit *punit); private: bool m_oneliner = false; bool m_show_upkeep = false; + + QAction *m_activate, *m_sentry, *m_fortify, *m_disband, *m_change_homecity, + *m_load, *m_unload, *m_unload_all, *m_upgrade; }; #define NUM_INFO_FIELDS 15 @@ -124,27 +139,13 @@ class unit_list_item : public QObject, public QListWidgetItem { Q_OBJECT public: - unit_list_item(unit *punit); + unit_list_item(::unit *punit); - bool can_issue_orders() const; - QMenu *menu() { return m_menu; } - - // Will hopefully become slots in unit class one day - void disband(); - void change_homecity(); - void activate_and_close_dialog(); - void sentry(); - void fortify(); - void upgrade(); - void load(); - void unload(); - void unload_all(); + /// Retrieves the unit represented by this item. + auto unit() const { return m_unit; } private: - void create_menu(); - - QMenu *m_menu = nullptr; - struct unit *m_unit; + ::unit *m_unit; }; /****************************************************************************