Freeciv21
Develop your civilization from humble roots to a global empire
top_bar.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 1996-2023 Freeciv21 and Freeciv contributors. This file is
3  part of Freeciv21. Freeciv21 is free software: you can redistribute it
4  and/or modify it under the terms of the GNU General Public License as
5  published by the Free Software Foundation, either version 3 of the
6  License, or (at your option) any later version. You should have received
7  a copy of the GNU General Public License along with Freeciv21. If not,
8  see https://www.gnu.org/licenses/.
9  */
10 
11 // Qt
12 #include <QAction>
13 #include <QApplication>
14 #include <QCommandLinkButton>
15 #include <QHBoxLayout>
16 #include <QMenu>
17 #include <QPaintEvent>
18 #include <QPainter>
19 #include <QScreen>
20 #include <QStyle>
21 #include <QStyleOptionToolButton>
22 #include <QTextStream>
23 #include <QTimer>
24 
25 // common
26 #include "chatline_common.h"
27 #include "government.h"
28 #include "nation.h"
29 #include "research.h"
30 
31 // client
32 #include "client_main.h"
33 #include "climisc.h"
34 #include "fc_client.h"
35 #include "fonts.h"
36 #include "page_game.h"
37 #include "qtg_cxxside.h"
38 #include "ratesdlg_g.h"
39 #include "tileset/sprite.h"
40 #include "top_bar.h"
41 #include "views/view_map.h"
42 #include "views/view_research.h"
43 #include "views/view_units.h"
44 
49 {
50  setToolButtonStyle(Qt::ToolButtonIconOnly);
51  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
52 }
53 
58 
63 {
65  // Nothing to show
66  return QSize();
67  }
68 
69  // Assume that all icons have the same size
70  auto content_size = get_tax_sprite(tileset, O_GOLD)->size();
71  content_size.setWidth(10 * content_size.width());
72 
73  // See QToolButton::sizeHint
74  ensurePolished();
75 
76  QStyleOptionToolButton opt;
77  initStyleOption(&opt);
78 
79  return style()->sizeFromContents(QStyle::CT_ToolButton, &opt, content_size,
80  this);
81 }
82 
87 {
89  // Nothing to show
90  return;
91  }
92 
93  // Draw a button without contents
94  QToolButton::paintEvent(event);
95 
96  // Draw the tax icons on top (centered; the style might expect something
97  // else but screw it)
98  auto tax = get_tax_sprite(tileset, O_GOLD);
99  auto sci = get_tax_sprite(tileset, O_SCIENCE);
100  auto lux = get_tax_sprite(tileset, O_LUXURY);
101 
102  // Assume that they have the same size
103  auto icon_size = tax->size();
104  auto center = size() / 2;
105 
106  auto x = center.width() - 5 * icon_size.width();
107  auto y = center.height() - icon_size.height() / 2;
108 
109  QPainter p(this);
110  for (int i = 0; i < 10; ++i) {
111  if (i < client.conn.playing->economic.tax / 10) {
112  p.drawPixmap(QPointF(x, y), *tax);
113  } else if (i < (client.conn.playing->economic.tax
115  / 10) {
116  p.drawPixmap(QPointF(x, y), *sci);
117  } else {
118  p.drawPixmap(QPointF(x, y), *lux);
119  }
120 
121  x += icon_size.width();
122  }
123 }
124 
129 {
130  setToolButtonStyle(Qt::ToolButtonIconOnly);
131  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
132 }
133 
138 
143 {
144  // Assume that all icons have the same size
145  auto content_size = client_research_sprite()->size();
147  // Global observers can only see climate change
148  content_size.setWidth(2 * content_size.width());
149  } else {
150  content_size.setWidth(4 * content_size.width());
151  }
152 
153  // See QToolButton::sizeHint
154  ensurePolished();
155 
156  QStyleOptionToolButton opt;
157  initStyleOption(&opt);
158 
159  return style()->sizeFromContents(QStyle::CT_ToolButton, &opt, content_size,
160  this);
161 }
162 
167 {
168  // Draw a button without contents
169  QToolButton::paintEvent(event);
170 
171  // Draw the icons on top (centered; the style might expect something else
172  // but screw it)
173  // Assume that they have the same size
174  auto icon_size = client_warming_sprite()->size();
175  auto center = size() / 2;
176 
177  auto x = center.width()
178  - (client_is_global_observer() ? 1 : 2) * icon_size.width();
179  auto y = center.height() - icon_size.height() / 2;
180 
181  QPainter p(this);
182  p.drawPixmap(QPointF(x, y), *client_warming_sprite());
183  x += icon_size.width();
184  p.drawPixmap(QPointF(x, y), *client_cooling_sprite());
185 
186  if (!client_is_global_observer()) {
187  x += icon_size.width();
188  p.drawPixmap(QPointF(x, y), *client_research_sprite());
189  x += icon_size.width();
190  p.drawPixmap(QPointF(x, y), *client_government_sprite());
191  }
192 }
193 
197 top_bar_widget::top_bar_widget(const QString &label, const QString &pg,
198  pfcn func)
199  : QToolButton(), blink(false), keep_blinking(false), page(pg),
200  right_click(nullptr), wheel_down(nullptr), wheel_up(nullptr),
201  left_click(func)
202 {
203  setText(label);
204  setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
205  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
206  setContextMenuPolicy(Qt::CustomContextMenu);
207  setIconSize(QSize(22, 22));
208 
209  timer = new QTimer;
210  timer->setSingleShot(false);
211  timer->setInterval(700);
212  connect(timer, &QTimer::timeout, this, &top_bar_widget::sblink);
213 }
214 
219 
223 void top_bar_widget::setCustomLabels(const QString &l) { setText(l); }
224 
229 {
230  // HACK Should improve this logic, paintEvent is NOT the right place.
231  if (!page.isEmpty()) {
232  int i = queen()->gimmeIndexOf(page);
233  setChecked(i == queen()->game_tab_widget->currentIndex());
234  }
235 
236  QToolButton::paintEvent(event);
237 
238  if (blink) {
239  QPainter p;
240  p.begin(this);
241  p.setPen(Qt::NoPen);
242  p.setCompositionMode(QPainter::CompositionMode_SoftLight);
243  p.setBrush(palette().color(QPalette::HighlightedText));
244  p.drawRect(0, 0, width(), height());
245  p.end();
246  }
247 }
248 
253 
258 
263 
268 
273 {
274  if (event->button() == Qt::LeftButton && left_click != nullptr) {
275  left_click();
276  } else if (event->button() == Qt::RightButton && right_click != nullptr) {
277  right_click();
278  } else if (event->button() == Qt::RightButton && right_click == nullptr) {
279  queen()->game_tab_widget->setCurrentIndex(0);
280  } else {
281  QToolButton::mousePressEvent(event);
282  }
283 }
284 
289 {
290  if (event->angleDelta().y() < 0 && wheel_down) {
291  wheel_down();
292  } else if (event->angleDelta().y() > 0 && wheel_up) {
293  wheel_up();
294  }
295 
296  event->accept();
297 }
298 
303 {
304  if (keep_blinking) {
305  if (!timer->isActive()) {
306  timer->start();
307  }
308  blink = !blink;
309  } else {
310  blink = false;
311  if (timer->isActive()) {
312  timer->stop();
313  }
314  }
315  update();
316 }
317 
324 {
325  QVariant qvar;
326  struct player *obs_player;
327  QAction *act;
328 
329  act = qobject_cast<QAction *>(sender());
330  qvar = act->data();
331 
332  if (!qvar.isValid()) {
333  return;
334  }
335 
336  if (act->property("scimenu").toBool()) {
337  dsend_packet_player_research(&client.conn, qvar.toInt());
338  return;
339  }
340 
341  if (qvar.toInt() == -1) {
342  send_chat("/observe");
343  return;
344  }
345 
346  obs_player = reinterpret_cast<struct player *>(qvar.value<void *>());
347  if (obs_player != nullptr) {
348  QString s;
349  QByteArray cn_bytes;
350 
351  s = QStringLiteral("/observe \"%1\"").arg(obs_player->name);
352  cn_bytes = s.toLocal8Bit();
353  send_chat(cn_bytes.data());
354  }
355 }
356 
361  : top_bar_widget("", QStringLiteral("ECO"), economy_report_dialog_popup)
362 {
363  setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
364  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
365 }
366 
371 
376 {
377  // Get a localized string representing the income, with the sign
378  QString income;
379  QTextStream s(&income);
380  s << Qt::forcesign << m_income;
381 
382  // TRANS: Top bar: "gold (income)". The income always includes a sign (e.g.
383  // +123, or -42).
384  setText(QString(_("%1 (%2)")).arg(m_gold).arg(income));
385 
386  // This is only a hint for the style, we don't do any painting ourselves.
387  warning new_warning = warning::no_warning;
388  if (m_income < 0 && m_gold + m_income >= 0) {
389  new_warning = warning::losing_money;
390  } else if (m_income < 0) {
391  new_warning = warning::low_on_funds;
392  }
393 
394  // Notify
395  if (new_warning != m_warning) {
396  m_warning = new_warning;
397 
398  // Allow using the warning state in CSS selectors.
399  style()->unpolish(this);
400  style()->polish(this);
401  }
402 }
403 
407 void gold_widget::paintEvent(QPaintEvent *event)
408 {
410  // Nothing to show
411  return;
412  }
413 
414  // Draw the button
415  QToolButton::paintEvent(event);
416 }
417 
422 {
423  layout = new QHBoxLayout;
424  layout->setContentsMargins(0, 0, 0, 0);
425  layout->setSpacing(0);
426  setLayout(layout);
427  setProperty("top_bar", true);
428  setAutoFillBackground(true);
429 }
430 
434 top_bar::~top_bar() = default;
435 
439 void top_bar::addWidget(QWidget *fsw)
440 {
441  objects.append(fsw);
442  layout->addWidget(fsw);
443 }
444 
449 {
451  queen()->game_tab_widget->setCurrentIndex(0);
452 }
453 
458 
463 {
464  queen()->game_tab_widget->setCurrentIndex(0);
466 }
467 
472 {
473  gov_menu *menu = new gov_menu(queen()->top_bar_wdg);
474 
475  menu->create();
476  menu->update();
477  menu->popup(QCursor::pos());
478 }
479 
486 {
487  if (client_is_observer()) {
488  QMenu *menu = new QMenu(king()->central_wdg);
489  QAction *eiskalt;
490  QString erwischt;
491 
492  players_iterate(pplayer)
493  {
494  if (pplayer == client.conn.playing) {
495  continue;
496  }
497  erwischt = QString(_("Observe %1")).arg(pplayer->name);
498  erwischt =
499  erwischt + " (" + nation_plural_translation(pplayer->nation) + ")";
500  eiskalt = new QAction(erwischt, queen()->mapview_wdg);
501  eiskalt->setData(QVariant::fromValue((void *) pplayer));
502  QObject::connect(eiskalt, &QAction::triggered, queen()->sw_diplo,
504  menu->addAction(eiskalt);
505  }
507 
509  {
510  eiskalt = new QAction(_("Observe globally"), queen()->mapview_wdg);
511  eiskalt->setData(-1);
512  menu->addAction(eiskalt);
513  QObject::connect(eiskalt, &QAction::triggered, queen()->sw_diplo,
515  }
516 
517  menu->setAttribute(Qt::WA_DeleteOnClose);
518  menu->popup(QCursor::pos());
519  }
520 }
521 
526 {
527  QMenu *menu;
528  QAction *act;
529  QVariant qvar;
530  QVector<qlist_item> curr_list;
532 
533  if (!client_is_observer()) {
535 
537  {
538  if (TECH_PREREQS_KNOWN == research->inventions[i].state
539  && research->researching != i) {
540  item.tech_str = QString::fromUtf8(
542  item.id = i;
543  curr_list.append(item);
544  }
545  }
547  if (curr_list.isEmpty()) {
548  return;
549  }
550  std::sort(curr_list.begin(), curr_list.end(), comp_less_than);
551  menu = new QMenu(king()->central_wdg);
552  for (int i = 0; i < curr_list.count(); i++) {
553  QIcon ic;
554 
555  qvar = curr_list.at(i).id;
556  auto sp = get_tech_sprite(tileset, curr_list.at(i).id);
557  if (sp) {
558  ic = QIcon(*sp);
559  }
560  act = new QAction(ic, curr_list.at(i).tech_str, queen()->mapview_wdg);
561  act->setData(qvar);
562  act->setProperty("scimenu", true);
563  menu->addAction(act);
564  QObject::connect(act, &QAction::triggered, queen()->sw_science,
566  }
567  menu->setAttribute(Qt::WA_DeleteOnClose);
568  menu->popup(QCursor::pos());
569  }
570 }
571 
576 {
577  science_report *sci_rep;
578  int i;
579  QWidget *w;
580 
582  return;
583  }
584  if (!queen()->isRepoDlgOpen(QStringLiteral("SCI"))) {
585  sci_rep = new science_report;
586  sci_rep->init(true);
587  } else {
588  i = queen()->gimmeIndexOf(QStringLiteral("SCI"));
589  w = queen()->game_tab_widget->widget(i);
590  if (w->isVisible()) {
592  return;
593  }
594  sci_rep = reinterpret_cast<science_report *>(w);
595  queen()->game_tab_widget->setCurrentWidget(sci_rep);
596  }
597 }
598 
603 {
604  units_view *uv;
605  int i;
606  QWidget *w;
607 
608  if (!queen()->isRepoDlgOpen(QStringLiteral("UNI"))) {
609  uv = new units_view;
610  uv->init();
611  uv->update_view();
612  } else {
613  i = queen()->gimmeIndexOf(QStringLiteral("UNI"));
614  w = queen()->game_tab_widget->widget(i);
615  if (w->isVisible()) {
617  return;
618  }
619  uv = reinterpret_cast<units_view *>(w);
620  uv->update_units();
621  queen()->game_tab_widget->setCurrentWidget(uv);
622  }
623 }
int send_chat(const char *message)
Send the message as a chat to the server.
void popdown_city_dialog()
Closes the city overlay.
Definition: citydlg.cpp:2264
Definition: shared.h:24
void update_contents()
Updates the displayed text after the gold or income changed.
Definition: top_bar.cpp:375
int income() const
Returns the incom as currently shown.
Definition: top_bar.h:126
gold_widget()
Constructor.
Definition: top_bar.cpp:360
int m_income
Definition: top_bar.h:154
warning
Types of warnings displayed by gold_widget.
Definition: top_bar.h:115
@ no_warning
Used when no warning is shown.
@ low_on_funds
The current player will soon go bankrupt.
@ losing_money
The current player has negative gold income.
~gold_widget() override
Destructor.
Definition: top_bar.cpp:370
int m_gold
Definition: top_bar.h:154
warning m_warning
Definition: top_bar.h:155
void paintEvent(QPaintEvent *event) override
Renders the national budget widget.
Definition: top_bar.cpp:407
Definition: menu.h:109
void create()
Creates the menu once the government list is known.
Definition: menu.cpp:159
void update()
Updates the menu to take gov availability into account.
Definition: menu.cpp:199
QSize sizeHint() const override
Size hint.
Definition: top_bar.cpp:142
void paintEvent(QPaintEvent *event) override
Renders the indicators widget.
Definition: top_bar.cpp:166
indicators_widget()
Constructor.
Definition: top_bar.cpp:128
~indicators_widget() override
Destructor.
Definition: top_bar.cpp:137
QSize sizeHint() const override
Size hint.
Definition: top_bar.cpp:62
void paintEvent(QPaintEvent *event) override
Renders the national budget widget.
Definition: top_bar.cpp:86
~national_budget_widget() override
Destructor.
Definition: top_bar.cpp:57
national_budget_widget()
Constructor.
Definition: top_bar.cpp:48
int gimmeIndexOf(const QString &str)
Returns index on game tab page of given report dialog.
Definition: page_game.cpp:706
fc_game_tab_widget * game_tab_widget
Definition: page_game.h:72
void init(bool raise)
Updates science_report and marks it as opened It has to be called soon after constructor.
void paintEvent(QPaintEvent *event) override
Paint event for top bar widget.
Definition: top_bar.cpp:228
top_bar_widget(const QString &label, const QString &pg, pfcn func)
Sidewidget constructor.
Definition: top_bar.cpp:197
pfcn left_click
Definition: top_bar.h:98
bool blink
Definition: top_bar.h:82
void setWheelUp(pfcn func)
Sets callback for mouse wheel up.
Definition: top_bar.cpp:267
pfcn wheel_down
Definition: top_bar.h:96
QTimer * timer
Definition: top_bar.h:99
void setRightClick(pfcn func)
Sets callback for mouse right click.
Definition: top_bar.cpp:257
void sblink()
Blinks current top_bar widget.
Definition: top_bar.cpp:302
void setWheelDown(pfcn func)
Sets callback for mouse wheel down.
Definition: top_bar.cpp:262
pfcn wheel_up
Definition: top_bar.h:97
~top_bar_widget() override
Sidewidget destructor.
Definition: top_bar.cpp:218
QString page
Definition: top_bar.h:84
void someSlot()
Miscelanous slot, helping observe players currently, and changing science extra functionality might b...
Definition: top_bar.cpp:323
bool keep_blinking
Definition: top_bar.h:83
void wheelEvent(QWheelEvent *event) override
Mouse wheel event.
Definition: top_bar.cpp:288
pfcn right_click
Definition: top_bar.h:95
void setLeftClick(pfcn func)
Sets callback for mouse left click.
Definition: top_bar.cpp:252
void setCustomLabels(const QString &)
Sets custom text visible on top of sidewidget.
Definition: top_bar.cpp:223
void mousePressEvent(QMouseEvent *event) override
Mouse press event for sidewidget.
Definition: top_bar.cpp:272
void addWidget(QWidget *fsw)
Adds new top_bar widget.
Definition: top_bar.cpp:439
QHBoxLayout * layout
Definition: top_bar.h:171
top_bar()
Sidebar constructor.
Definition: top_bar.cpp:421
QList< QWidget * > objects
Definition: top_bar.h:168
~top_bar() override
Sidebar destructor.
Table widget to display units view (F2)
Definition: view_units.h:54
void init()
Initializes place in tab for units view.
Definition: view_units.cpp:128
void update_view()
Refresh all widgets for units view.
Definition: view_units.cpp:133
void update_units()
Updates the units table.
Definition: view_units.cpp:146
bool client_is_global_observer()
Returns whether client is global observer.
struct player * client_player()
Either controlling or observing.
struct civclient client
bool client_is_observer()
Returns whether client is observer.
const QPixmap * client_government_sprite()
Return the sprite for the government indicator.
Definition: climisc.cpp:374
const QPixmap * client_research_sprite()
Return the sprite for the research indicator.
Definition: climisc.cpp:317
const QPixmap * client_warming_sprite()
Return the sprite for the global-warming indicator.
Definition: climisc.cpp:340
const QPixmap * client_cooling_sprite()
Return the sprite for the global-cooling indicator.
Definition: climisc.cpp:357
void request_center_focus_unit()
Center to focus unit.
Definition: control.cpp:2254
void key_end_turn()
Handle user 'end turn' input.
Definition: control.cpp:2762
enum event_type event
Definition: events.cpp:68
class fc_client * king()
Return fc_client instance.
Definition: gui_main.cpp:58
@ O_SCIENCE
Definition: fc_types.h:90
@ O_LUXURY
Definition: fc_types.h:89
@ O_GOLD
Definition: fc_types.h:88
#define _(String)
Definition: fcintl.h:50
get_token_fn_t func
Definition: inputfile.cpp:119
const char * nation_plural_translation(const struct nation_type *pnation)
Return the (translated) plural noun of the given nation.
Definition: nation.cpp:136
pageGame * queen()
Return game instandce.
Definition: page_game.cpp:557
#define players_iterate_end
Definition: player.h:520
#define players_iterate(_pplayer)
Definition: player.h:514
void economy_report_dialog_popup()
Display the economy report.
struct research * research_get(const struct player *pplayer)
Returns the research structure associated with the player.
Definition: research.cpp:110
size_t size
Definition: specvec.h:64
struct connection conn
Definition: client_main.h:89
struct player * playing
Definition: connection.h:142
Definition: climisc.h:66
Definition: player.h:231
struct player_economic economic
Definition: player.h:266
char name[MAX_LEN_NAME]
Definition: player.h:233
struct nation_style * style
Definition: player.h:261
enum tech_state state
Definition: research.h:66
Tech_type_id researching
Definition: research.h:45
struct research::research_invention inventions[A_LAST]
struct advance * advance_by_number(const Tech_type_id atype)
Return the advance for the given advance index.
Definition: tech.cpp:94
const char * advance_name_translation(const struct advance *padvance)
Return the (translated) name of the given advance/technology.
Definition: tech.cpp:274
#define advance_index_iterate_end
Definition: tech.h:226
#define A_FIRST
Definition: tech.h:37
#define advance_index_iterate(_start, _index)
Definition: tech.h:221
const QPixmap * get_tech_sprite(const struct tileset *t, Tech_type_id tech)
Return the sprite for the technology/advance.
Definition: tilespec.cpp:3385
const QPixmap * get_tax_sprite(const struct tileset *t, Output_type_id otype)
Return a tax sprite for the given output type (usually gold/lux/sci).
Definition: tilespec.cpp:3448
void top_bar_right_click_diplomacy()
Right click for diplomacy Opens diplomacy meeting for player For observer popups menu.
Definition: top_bar.cpp:485
void top_bar_right_click_science()
Right click for science, allowing to choose current tech.
Definition: top_bar.cpp:525
void top_bar_units_view()
Click for units view, allowing to close/open.
Definition: top_bar.cpp:602
void top_bar_indicators_menu()
Popups menu on indicators widget.
Definition: top_bar.cpp:471
void top_bar_left_click_science()
Left click for science, allowing to close/open.
Definition: top_bar.cpp:575
void top_bar_show_map()
Callback to show map.
Definition: top_bar.cpp:448
void top_bar_center_unit()
Callback to center on current unit.
Definition: top_bar.cpp:462
void top_bar_finish_turn()
Callback for finishing turn.
Definition: top_bar.cpp:457
void(* pfcn)()
Definition: top_bar.h:19
bool comp_less_than(const qlist_item &q1, const qlist_item &q2)
Compare unit_items (used for techs) by name.