Freeciv21
Develop your civilization from humble roots to a global empire
unitselect.cpp
Go to the documentation of this file.
1 /*
2  /\ ___ /\ Copyright (c) 1996-2023 FREECIV 21 and Freeciv
3  ( o o ) contributors. This file is part of Freeciv21.
4  \ >#< / Freeciv21 is free software: you can redistribute it
5  / \ and/or modify it under the terms of the GNU
6  / \ ^ General Public License as published by the Free
7 | | // Software Foundation, either version 3 of the License,
8  \ / // or (at your option) any later version.
10  GNU General Public License along with Freeciv21.
11  If not, see https://www.gnu.org/licenses/.
12  */
13 
14 #include "unitselect.h"
15 
16 #include <QMouseEvent>
17 #include <QPainter>
18 #include <QTimer>
19 #include <QtMath>
20 // common
21 #include "movement.h"
22 // client
23 #include "canvas.h"
24 #include "client_main.h"
25 #include "control.h"
26 #include "fonts.h"
27 #include "page_game.h"
28 #include "tileset/tilespec.h"
29 #include "utils/unit_utils.h"
30 #include "views/view_map.h"
31 #include "views/view_map_common.h"
32 
36 units_select::units_select(struct tile *ptile, QWidget *parent)
37  : QMenu(parent)
38 {
39  utile = ptile;
40  pix = nullptr;
41  show_line = 0;
42  highligh_num = -1;
43  ufont.setItalic(true);
45  update_units();
46  h_pix = nullptr;
47  create_pixmap();
48  setMouseTracking(true);
49 
50  popup(mapFromGlobal(QCursor::pos()));
51 }
52 
57 {
58  delete h_pix;
59  delete pix;
60 }
61 
66 {
67  int a;
68  int x, y, i;
69  QFontMetrics fm(info_font);
70  QImage cropped_img;
71  QImage img;
72  QList<QPixmap *> pix_list;
73  QPainter p;
74  QPen pen;
75  QPixmap pixc;
76  QPixmap *pixp;
77  QPixmap *tmp_pix;
78  QRect crop;
79  QPixmap *unit_pixmap;
80  const unit *punit;
81  float isosize;
82 
83  delete pix;
84  isosize = 0.7;
86  isosize = 0.5;
87  }
88 
89  update_units();
90  if (!unit_list.empty()) {
94  } else {
95  item_size.setWidth(tileset_unit_width(tileset) * isosize);
96  item_size.setHeight(tileset_unit_width(tileset) * isosize);
97  }
98  more = false;
99  delete h_pix;
100  h_pix = new QPixmap(item_size.width(), item_size.height());
101  h_pix->fill(palette().color(QPalette::HighlightedText));
102 
103  // Determine the layout. 5 columns up to 25 units, then 6.
104  column_count = qMin(unit_count, unit_count <= 25 ? 5 : 6);
105  // Up to 6 rows visible at the same time.
106  row_count = qMin((unit_count + column_count - 1) / column_count, 6);
107  // And whether we go over.
109 
110  pix = new QPixmap(column_count * item_size.width(),
111  row_count * item_size.height());
112  pix->fill(Qt::transparent);
113  for (auto *punit : qAsConst(unit_list)) {
114  unit_pixmap = new QPixmap(tileset_unit_width(tileset),
116  unit_pixmap->fill(Qt::transparent);
117  put_unit(punit, unit_pixmap, QPoint());
118  img = unit_pixmap->toImage();
119  crop = zealous_crop_rect(img);
120  cropped_img = img.copy(crop);
122  img = cropped_img.scaled(
124  Qt::KeepAspectRatio, Qt::SmoothTransformation);
125  } else {
126  img = cropped_img.scaled(tileset_unit_width(tileset) * isosize,
127  tileset_unit_width(tileset) * isosize,
128  Qt::KeepAspectRatio,
129  Qt::SmoothTransformation);
130  }
131  pixc = QPixmap::fromImage(img);
132  pixp = new QPixmap(pixc);
133  pix_list.push_back(pixp);
134  delete unit_pixmap;
135  }
136  a = qMin(item_size.width() / 4, 12);
137  x = 0, y = -item_size.height(), i = -1;
138  p.begin(pix);
139  ufont.setPixelSize(a);
140  p.setFont(ufont);
141  pen.setColor(palette().color(QPalette::Text));
142  p.setPen(pen);
143 
144  while (!pix_list.isEmpty()) {
145  tmp_pix = pix_list.takeFirst();
146  i++;
147  if (i % column_count == 0) {
148  x = 0;
149  y = y + item_size.height();
150  }
151  punit = unit_list.at(i);
152  Q_ASSERT(punit != nullptr);
153 
154  if (i == highligh_num) {
155  p.drawPixmap(x, y, *h_pix);
156  p.drawPixmap(x, y, *tmp_pix);
157  } else {
158  p.drawPixmap(x, y, *tmp_pix);
159  }
160 
162  || unit_owner(punit) == client.conn.playing) {
163  auto str = QString(move_points_text(punit->moves_left, false));
164  if (utype_fuel(unit_type_get(punit))) {
165  // TRANS: T for turns
166  str += " " + QString(_("(%1T)")).arg(punit->fuel - 1);
167  }
168  // TRANS: MP = Movement points
169  str = QString(_("MP:")) + str;
170  p.drawText(x, y + item_size.height() - 4, str);
171  }
172 
173  x = x + item_size.width();
174  delete tmp_pix;
175  }
176  p.end();
177  setFixedWidth(pix->width() + 20);
178  setFixedHeight(pix->height() + 3 * fm.height() + 2 * 6);
179  qDeleteAll(pix_list.begin(), pix_list.end());
180  }
181 }
182 
187 {
188  int a, b;
189  int old_h;
190  QFontMetrics fm(info_font);
191 
192  old_h = highligh_num;
193  highligh_num = -1;
194  if (event->x() > width() - 11 || event->y() > height() - fm.height() - 5
195  || event->y() < fm.height() + 3 || event->x() < 11) {
197  } else if (row_count > 0) {
198  a = (event->x() - 10) / item_size.width();
199  b = (event->y() - fm.height() - 3) / item_size.height();
200  highligh_num = b * column_count + a;
201  }
202  if (old_h != highligh_num) {
203  create_pixmap();
204  update();
205  }
206 }
207 
214 {
215  if (event->button() == Qt::LeftButton && highligh_num != -1) {
216  update_units();
217  if (highligh_num >= unit_list.size()) {
218  return;
219  }
221  }
222  QMenu::mousePressEvent(event);
223 }
224 
228 void units_select::paint(QPainter *painter, QPaintEvent *event)
229 {
230  Q_UNUSED(event)
231  QFontMetrics fm(info_font);
232  int h, i;
233  int *f_size;
234  QPen pen;
235  QString str, str2, unit_name;
236  int point_size = info_font.pointSize();
237  int pixel_size = info_font.pixelSize();
238 
239  if (point_size < 0) {
240  f_size = &pixel_size;
241  } else {
242  f_size = &point_size;
243  }
244  if (highligh_num != -1 && highligh_num < unit_list.size()) {
245  auto punit = unit_list.at(highligh_num);
246  // TRANS: HP - hit points
247  unit_name = unit_name_translation(punit);
248  str2 = QString(_("%1 HP:%2/%3"))
249  .arg(unit_activity_text(punit), QString::number(punit->hp),
250  QString::number(unit_type_get(punit)->hp));
251  }
252  str = QString(PL_("%1 unit", "%1 units", unit_list_size(utile->units)))
253  .arg(unit_list_size(utile->units));
254  for (i = *f_size; i > 4; i--) {
255  if (point_size < 0) {
256  info_font.setPixelSize(i);
257  } else {
258  info_font.setPointSize(i);
259  }
260  QFontMetrics qfm(info_font);
261  if (10 + qfm.horizontalAdvance(str2) < width()) {
262  break;
263  }
264  }
265  h = fm.height();
266  if (pix != nullptr) {
267  painter->drawPixmap(10, h + 3, *pix);
268  pen.setColor(palette().color(QPalette::Text));
269  painter->setPen(pen);
270  painter->setFont(info_font);
271  painter->drawText(10, h, str);
272  if (highligh_num != -1 && highligh_num < unit_list.size()) {
273  painter->drawText(10, height() - 5 - h, unit_name);
274  painter->drawText(10, height() - 5, str2);
275  }
276  // draw scroll
277  if (more) {
278  int maxl = ((unit_count - 1) / column_count) + 1;
279  float page_height = 3.0f / maxl;
280  float page_start = (static_cast<float>(show_line)) / maxl;
281  pen.setColor(palette().color(QPalette::HighlightedText));
282  painter->setBrush(palette().color(QPalette::HighlightedText).darker());
283  painter->setPen(palette().color(QPalette::HighlightedText).darker());
284  painter->drawRect(pix->width() + 10, h, 8, h + pix->height());
285  painter->setPen(pen);
286  painter->drawRoundedRect(pix->width() + 10,
287  h + page_start * pix->height(), 8,
288  h + page_height * pix->height(), 2, 2);
289  }
290  }
291  if (point_size < 0) {
292  info_font.setPixelSize(*f_size);
293  } else {
294  info_font.setPointSize(*f_size);
295  }
296 }
297 
301 void units_select::paintEvent(QPaintEvent *event)
302 {
303  QMenu::paintEvent(event); // Draw background
304 
305  QPainter painter;
306  painter.begin(this);
307  paint(&painter, event);
308  painter.end();
309 }
310 
315 {
316  int i = 1;
317  struct unit_list *punit_list;
318 
319  if (utile == nullptr) {
320  return;
321  }
322  unit_count = 0;
323  if (utile == nullptr) {
324  struct unit *punit = head_of_units_in_focus();
325  if (punit) {
326  utile = unit_tile(punit);
327  }
328  }
329  unit_list.clear();
330  if (utile != nullptr) {
331  punit_list = utile->units;
332  if (punit_list != nullptr) {
333  for (auto *punit : sorted(utile->units)) {
334  unit_count++;
335  if (i > show_line * column_count) {
336  unit_list.push_back(punit);
337  }
338  i++;
339  }
340  }
341  }
342  if (unit_list.empty()) {
343  close();
344  }
345 }
346 
350 void units_select::closeEvent(QCloseEvent *event)
351 {
352  queen()->mapview_wdg->setFocus();
353  QMenu::closeEvent(event);
354 }
355 
359 void units_select::wheelEvent(QWheelEvent *event)
360 {
361  if (!more && utile == nullptr) {
362  return;
363  }
364 
365  // The number of hidden lines. This is the number of rows needed to show
366  // all units, minus what is shown without scrolling.
367  auto nr = (unit_list_size(utile->units) + column_count - 1) / column_count
368  - row_count;
369 
370  // We scroll one full row per scroll event. The angle delta determines the
371  // direction in which we scroll. We don't scroll when it's 0.
372  const auto delta = event->angleDelta().y();
373  if (delta < 0) {
374  show_line++;
375  show_line = qMin(show_line, nr);
376  } else if (delta > 0) {
377  show_line--;
378  show_line = qMax(0, show_line);
379  }
380  update_units();
381  create_pixmap();
382  update();
383  event->accept();
384 }
385 
389 void toggle_unit_sel_widget(struct tile *ptile)
390 {
391  units_select *unit_sel = queen()->unit_selector;
392  if (unit_sel != nullptr) {
393  unit_sel->close();
394  delete unit_sel;
395  unit_sel = new units_select(ptile, queen()->mapview_wdg);
396  unit_sel->show();
397  } else {
398  unit_sel = new units_select(ptile, queen()->mapview_wdg);
399  unit_sel->show();
400  }
401 }
402 
407 {
408  units_select *unit_sel = queen()->unit_selector;
409  if (unit_sel != nullptr) {
410  unit_sel->update_units();
411  unit_sel->create_pixmap();
412  unit_sel->update();
413  }
414 }
415 
420 {
421  units_select *unit_sel = queen()->unit_selector;
422  if (unit_sel != nullptr) {
423  unit_sel->close();
424  delete unit_sel;
425  unit_sel = nullptr;
426  }
427 }
QRect zealous_crop_rect(QImage &p)
Return rectangle containing pure image (crops transparency)
Definition: canvas.cpp:81
QFont getFont(const QString &name, double zoom=1.0) const
Returns desired font.
Definition: fonts.cpp:57
static fcFont * instance()
Returns instance of fc_font.
Definition: fonts.cpp:34
map_view * mapview_wdg
Definition: page_game.h:81
units_select * unit_selector
Definition: page_game.h:75
QPixmap * h_pix
Definition: unitselect.h:32
QFont ufont
Definition: unitselect.h:35
QFont info_font
Definition: unitselect.h:36
std::vector< unit * > unit_list
size of each pixmap of unit
Definition: unitselect.h:34
void mousePressEvent(QMouseEvent *event) override
Mouse pressed event for units_select.
Definition: unitselect.cpp:213
~units_select() override
Destructor for unit select.
Definition: unitselect.cpp:56
int highligh_num
Definition: unitselect.h:57
int column_count
Definition: unitselect.h:37
void paint(QPainter *painter, QPaintEvent *event)
Redirected paint event.
Definition: unitselect.cpp:228
QPixmap * pix
Definition: unitselect.h:31
void closeEvent(QCloseEvent *event) override
Close event for units_select, restores focus to map.
Definition: unitselect.cpp:350
void update_units()
Updates unit list on tile.
Definition: unitselect.cpp:314
tile * utile
Definition: unitselect.h:44
void wheelEvent(QWheelEvent *event) override
Mouse wheel event for units_select.
Definition: unitselect.cpp:359
int unit_count
Definition: unitselect.h:58
QSize item_size
pixmap for highlighting
Definition: unitselect.h:33
units_select(struct tile *ptile, QWidget *parent=0)
Contructor for units_select.
Definition: unitselect.cpp:36
void create_pixmap()
Create pixmap of whole widget except borders (pix)
Definition: unitselect.cpp:65
void paintEvent(QPaintEvent *event) override
Paint event, redirects to paint(...)
Definition: unitselect.cpp:301
void mouseMoveEvent(QMouseEvent *event) override
Event for mouse moving around units_select.
Definition: unitselect.cpp:186
std::vector< unit * > sorted(const unit_list *units)
Returns a version of units sorted in the way the user would like to see them.
Definition: unit_utils.cpp:87
bool client_is_global_observer()
Returns whether client is global observer.
struct civclient client
void unit_focus_set(struct unit *punit)
Sets the focus unit directly.
Definition: control.cpp:479
struct unit * head_of_units_in_focus()
Return head of focus units list.
Definition: control.cpp:387
enum event_type event
Definition: events.cpp:68
#define PL_(String1, String2, n)
Definition: fcintl.h:54
#define _(String)
Definition: fcintl.h:50
const char * move_points_text(int mp, bool reduce)
Simple version of move_points_text_full() – render positive movement points as text without any prefi...
Definition: movement.cpp:856
const char *const notify_label
Definition: fonts.h:19
pageGame * queen()
Return game instandce.
Definition: page_game.cpp:557
struct connection conn
Definition: client_main.h:89
struct player * playing
Definition: connection.h:142
Definition: mapimg.cpp:266
Definition: tile.h:42
struct unit_list * units
Definition: tile.h:50
Definition: unit.h:134
int moves_left
Definition: unit.h:147
int fuel
Definition: unit.h:150
int tileset_hex_width(const struct tileset *t)
Return the hex_width of the current tileset.
Definition: tilespec.cpp:345
int tileset_unit_width(const struct tileset *t)
Return the unit tile width of the current tileset.
Definition: tilespec.cpp:426
int tileset_unit_height(const struct tileset *t)
Return the unit tile height of the current tileset.
Definition: tilespec.cpp:434
bool tileset_is_isometric(const struct tileset *t)
Return whether the current tileset is isometric.
Definition: tilespec.cpp:336
int tileset_hex_height(const struct tileset *t)
Return the hex_height of the current tileset.
Definition: tilespec.cpp:351
const QString unit_activity_text(const struct unit *punit)
Return text describing the unit's current activity as a static string.
Definition: unit.cpp:1085
#define unit_tile(_pu)
Definition: unit.h:371
#define unit_owner(_pu)
Definition: unit.h:370
void toggle_unit_sel_widget(struct tile *ptile)
Shows/closes unit selection widget.
Definition: unitselect.cpp:389
void update_unit_sel()
Update unit selection widget if open.
Definition: unitselect.cpp:406
void popdown_unit_sel()
Closes unit selection widget.
Definition: unitselect.cpp:419
const struct unit_type * unit_type_get(const struct unit *punit)
Return the unit type for this unit.
Definition: unittype.cpp:114
const char * unit_name_translation(const struct unit *punit)
Return the (translated) name of the unit.
Definition: unittype.cpp:1265
#define utype_fuel(ptype)
Definition: unittype.h:772
void put_unit(const struct unit *punit, QPixmap *pcanvas, const QPoint &canvas_loc)
Draw the given unit onto the canvas store at the given location.