From c707c65ab586646d04fdb1e79b2e4aeb39be4718 Mon Sep 17 00:00:00 2001 From: Tobias Rehbein Date: Wed, 18 Sep 2024 21:23:48 +0200 Subject: [PATCH] Implement autoscroll for research tree Allows the user to conveniently scroll the technology tree to the currently researched technology or the technology goal. A button next to the corresponding combo box is available to the user for this purpose. The icon used for the buttons is based on `crosshair-light` from Phosphor Icons with minor modifications to let it play nicely with our theming and to be recognizable on the small button. --- client/views/view_research.cpp | 94 +++++++++++++++++++++++-- client/views/view_research.h | 8 +++ client/views/view_research_reqtree.cpp | 22 ++++++ client/views/view_research_reqtree.h | 3 + data/themes/icons/crosshair.svg | 10 +++ data/themes/icons/crosshair.svg.license | 3 + 6 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 data/themes/icons/crosshair.svg create mode 100644 data/themes/icons/crosshair.svg.license diff --git a/client/views/view_research.cpp b/client/views/view_research.cpp index 313f023509..eae452a067 100644 --- a/client/views/view_research.cpp +++ b/client/views/view_research.cpp @@ -20,12 +20,16 @@ #include #include #include +#include #include +#include #include // common +#include "fc_types.h" #include "game.h" #include "government.h" +#include "icons.h" #include "research.h" // client @@ -107,6 +111,16 @@ void research_diagram::reset() resize(width, height); } +/** + Find the center of a node, identified by tech id, and return true + if the node was found; false otherwise. If a node is found, x and y + are filled with the center of the node. + */ +bool research_diagram::get_tech_position(Tech_type_id id, int *x, int *y) +{ + return get_position_on_reqtree(req, id, x, y); +} + /** Mouse handler for research_diagram */ @@ -274,25 +288,47 @@ science_report::science_report() : QWidget() researching_combo = new QComboBox(); auto sci_layout = new QGridLayout(); res_diag = new research_diagram(); - auto scroll = new QScrollArea(); + scroll = new QScrollArea(); refresh_but = new QPushButton(); refresh_but->setText(_("Refresh")); refresh_but->setToolTip(_("Press to refresh currently researched " "technology calculation again.")); refresh_but->setDisabled(true); + locate_researching_but = new QToolButton(); + locate_researching_but->setIcon( + fcIcons::instance()->getIcon(QStringLiteral("crosshair"))); + locate_researching_but->setIconSize( + QSize(locate_researching_but->fontInfo().pixelSize(), + locate_researching_but->fontInfo().pixelSize())); + locate_researching_but->setToolTip( + _("Press to locate currently researched " + "technology in tree.")); + locate_researching_but->setDisabled(true); + + locate_goal_but = new QToolButton(); + locate_goal_but->setIcon( + fcIcons::instance()->getIcon(QStringLiteral("crosshair"))); + locate_goal_but->setIconSize( + QSize(locate_researching_but->fontInfo().pixelSize(), + locate_researching_but->fontInfo().pixelSize())); + locate_goal_but->setToolTip(_("Press to locate technology goal in tree.")); + locate_goal_but->setDisabled(true); + progress->setTextVisible(true); progress_label->setSizePolicy(size_fixed_policy); - sci_layout->addWidget(progress_label, 0, 0, 1, 8); + sci_layout->addWidget(progress_label, 0, 0, 1, 9); sci_layout->addWidget(researching_combo, 1, 0, 1, 3); - sci_layout->addWidget(refresh_but, 1, 3, 1, 1); researching_combo->setSizePolicy(size_fixed_policy); + sci_layout->addWidget(locate_researching_but, 1, 3, 1, 1); + sci_layout->addWidget(refresh_but, 1, 4, 1, 1); refresh_but->setSizePolicy(size_fixed_policy); - sci_layout->addWidget(progress, 1, 5, 1, 4); + sci_layout->addWidget(progress, 1, 6, 1, 4); progress->setSizePolicy(size_fixed_policy); sci_layout->addWidget(goal_combo, 2, 0, 1, 3); goal_combo->setSizePolicy(size_fixed_policy); - sci_layout->addWidget(info_label, 2, 5, 1, 4); + sci_layout->addWidget(locate_goal_but, 2, 3, 1, 1); + sci_layout->addWidget(info_label, 2, 6, 1, 4); info_label->setSizePolicy(size_fixed_policy); size = res_diag->size(); @@ -310,6 +346,12 @@ science_report::science_report() : QWidget() QObject::connect(refresh_but, &QAbstractButton::pressed, this, &science_report::push_research); + QObject::connect(locate_goal_but, &QAbstractButton::pressed, this, + &science_report::locate_goal); + + QObject::connect(locate_researching_but, &QAbstractButton::pressed, this, + &science_report::locate_researching); + QObject::connect(goal_combo, QOverload::of(&QComboBox::currentIndexChanged), this, &science_report::goal_tech_changed); @@ -518,6 +560,10 @@ void science_report::update_report() refresh_but->setDisabled(false); } + // Update locate buttons + locate_researching_but->setDisabled(research->researching == A_UNSET); + locate_goal_but->setDisabled(research->tech_goal == A_UNSET); + update_reqtree(); } @@ -526,6 +572,17 @@ void science_report::update_report() */ void science_report::update_reqtree() { res_diag->update_reqtree(); } +/** + Scroll the science tree to display the technology identified by tech id. + */ +void science_report::scroll_reqtree_to_tech(Tech_type_id id) +{ + int x, y; + if (res_diag->get_tech_position(id, &x, &y)) { + scroll->ensureVisible(x, y, scroll->width() / 2, scroll->height() / 2); + } +} + /** Slot used when combo box with current tech changes */ @@ -570,6 +627,33 @@ void science_report::push_research() } } +/** + Locate technology goal in tree and scroll so that it is visible. +*/ +void science_report::locate_goal() +{ + auto research = research_get(client_player()); + if (!research) { + return; + } + + scroll_reqtree_to_tech(research->tech_goal); +} + +/** + Locate the currently researched technology in tree and scroll so that it + is visible. +*/ +void science_report::locate_researching() +{ + auto research = research_get(client_player()); + if (!research) { + return; + } + + scroll_reqtree_to_tech(research->researching); +} + /** Update the science report. */ diff --git a/client/views/view_research.h b/client/views/view_research.h index 57d551b8cb..93096c7780 100644 --- a/client/views/view_research.h +++ b/client/views/view_research.h @@ -24,6 +24,7 @@ class QPaintEvent; class QScrollArea; class progress_bar; class QPushButton; +class QToolButton; /**************************************************************************** Custom widget representing research diagram in science_report @@ -37,6 +38,7 @@ class research_diagram : public QWidget { void update_reqtree(); void reset(); QSize size(); + bool get_tech_position(Tech_type_id id, int *x, int *y); private slots: void show_tooltip(); @@ -71,9 +73,12 @@ struct qlist_item { class science_report : public QWidget { Q_OBJECT + QScrollArea *scroll; QComboBox *goal_combo; QComboBox *researching_combo; QPushButton *refresh_but; + QToolButton *locate_researching_but; + QToolButton *locate_goal_but; progress_bar *progress; QLabel *info_label; QLabel *progress_label; @@ -92,11 +97,14 @@ class science_report : public QWidget { private: void update_reqtree(); int index{0}; + void scroll_reqtree_to_tech(Tech_type_id id); private slots: void current_tech_changed(int index); void goal_tech_changed(int index); void push_research(); + void locate_researching(); + void locate_goal(); }; void popdown_science_report(); diff --git a/client/views/view_research_reqtree.cpp b/client/views/view_research_reqtree.cpp index 3c9bf70b28..75fd237859 100644 --- a/client/views/view_research_reqtree.cpp +++ b/client/views/view_research_reqtree.cpp @@ -1183,3 +1183,25 @@ Tech_type_id get_tech_on_reqtree(struct reqtree *tree, int x, int y) } return A_NONE; } + +/** + Find the center of a node, identified by tech id in a given reqtree + and return true if the node was found; false otherwise. If a node + is found, x and y are filled with the center of the node in + reqtrees coordinate system. + */ +bool get_position_on_reqtree(struct reqtree *tree, Tech_type_id tech, int *x, + int *y) +{ + for (int i = 0; i < tree->num_nodes; i++) { + struct tree_node *node = tree->nodes[i]; + + if (tech == node->tech) { + *x = node->node_x + node->node_width / 2; + *y = node->node_y + node->node_height / 2; + return true; + } + } + + return false; +} diff --git a/client/views/view_research_reqtree.h b/client/views/view_research_reqtree.h index 12939259e3..87c3d30f20 100644 --- a/client/views/view_research_reqtree.h +++ b/client/views/view_research_reqtree.h @@ -97,3 +97,6 @@ QList *draw_reqtree(struct reqtree *tree, int w, int h); Tech_type_id get_tech_on_reqtree(struct reqtree *tree, int x, int y); + +bool get_position_on_reqtree(struct reqtree *tree, Tech_type_id tech, int *x, + int *y); diff --git a/data/themes/icons/crosshair.svg b/data/themes/icons/crosshair.svg new file mode 100644 index 0000000000..9d927a2d66 --- /dev/null +++ b/data/themes/icons/crosshair.svg @@ -0,0 +1,10 @@ + + + + diff --git a/data/themes/icons/crosshair.svg.license b/data/themes/icons/crosshair.svg.license new file mode 100644 index 0000000000..b12e0e7e20 --- /dev/null +++ b/data/themes/icons/crosshair.svg.license @@ -0,0 +1,3 @@ +SPDX-License-Identifier: MIT +SPDX-FileCopyrightText: Copyright (c) 2023 Phosphor Icons +SPDX-FileCopyrightText: 2024 Tobias Rehbein