1 #include "PopulationPanel.h"
2 
3 #include <GG/Button.h>
4 
5 #include "../util/i18n.h"
6 #include "../util/Logger.h"
7 #include "../universe/UniverseObject.h"
8 #include "../universe/PopCenter.h"
9 #include "../universe/Enums.h"
10 #include "../client/human/HumanClientApp.h"
11 #include "ClientUI.h"
12 #include "CUIControls.h"
13 #include "MeterBrowseWnd.h"
14 #include "MultiIconValueIndicator.h"
15 #include "MultiMeterStatusBar.h"
16 
17 namespace {
18     const int       EDGE_PAD(3);
19 
20     /** How big we want meter icons with respect to the current UI font size.
21       * Meters should scale along font size, but not below the size for the
22       * default 12 points font. */
MeterIconSize()23     GG::Pt MeterIconSize() {
24         const int icon_size = std::max(ClientUI::Pts(), 12) * 4/3;
25         return GG::Pt(GG::X(icon_size), GG::Y(icon_size));
26     }
27 }
28 
PopulationPanel(GG::X w,int object_id)29 PopulationPanel::PopulationPanel(GG::X w, int object_id) :
30     AccordionPanel(w, GG::Y(ClientUI::Pts()*2)),
31     m_popcenter_id(object_id)
32 {}
33 
CompleteConstruction()34 void PopulationPanel::CompleteConstruction() {
35     AccordionPanel::CompleteConstruction();
36     SetName("PopulationPanel");
37 
38     m_expand_button->LeftPressedSignal.connect(
39         boost::bind(&PopulationPanel::ExpandCollapseButtonPressed, this));
40 
41     auto pop = Objects().get<PopCenter>(m_popcenter_id);
42     if (!pop) {
43         ErrorLogger() << "Attempted to construct a PopulationPanel with an object id that is not a popcenter: " << m_popcenter_id;
44         return;
45     }
46 
47     // small meter indicators - for use when panel is collapsed
48     m_meter_stats.push_back({
49         METER_POPULATION,
50         GG::Wnd::Create<StatisticIcon>(ClientUI::SpeciesIcon(pop->SpeciesName()),
51                                        pop->GetMeter(METER_POPULATION)->Initial(), 3, false,
52                                        MeterIconSize().x, MeterIconSize().y)});
53     m_meter_stats.push_back({
54         METER_HAPPINESS,
55         GG::Wnd::Create<StatisticIcon>(ClientUI::MeterIcon(METER_HAPPINESS),
56                                        pop->GetMeter(METER_HAPPINESS)->Initial(), 3, false,
57                                        MeterIconSize().x, MeterIconSize().y)});
58     m_meter_stats.push_back({
59         METER_CONSTRUCTION,
60         GG::Wnd::Create<StatisticIcon>(ClientUI::MeterIcon(METER_CONSTRUCTION),
61                                        pop->GetMeter(METER_CONSTRUCTION)->Initial(), 3, false,
62                                        MeterIconSize().x, MeterIconSize().y)});
63 
64     // meter and production indicators
65     std::vector<std::pair<MeterType, MeterType>> meters;
66 
67     for (auto& meter_stat : m_meter_stats) {
68         MeterType meter_type = meter_stat.first;
69 
70         meter_stat.second->RightClickedSignal.connect([this, meter_type](const GG::Pt& pt) {
71             std::string meter_string = boost::lexical_cast<std::string>(meter_type);
72 
73             auto popup = GG::Wnd::Create<CUIPopupMenu>(pt.x, pt.y);
74             auto pc = Objects().get<PopCenter>(m_popcenter_id);
75             if (meter_type == METER_POPULATION && pc) {
76                 std::string species_name = pc->SpeciesName();
77                 if (!species_name.empty()) {
78                     auto zoom_species_action = [species_name]() { ClientUI::GetClientUI()->ZoomToSpecies(species_name); };
79                     std::string species_label = boost::io::str(FlexibleFormat(UserString("ENC_LOOKUP")) %
80                                                                               UserString(species_name));
81                     popup->AddMenuItem(GG::MenuItem(species_label, false, false, zoom_species_action));
82                 }
83             }
84 
85             auto pedia_meter_type_action = [meter_string]() { ClientUI::GetClientUI()->ZoomToMeterTypeArticle(meter_string); };
86             std::string popup_label = boost::io::str(FlexibleFormat(UserString("ENC_LOOKUP")) %
87                                                                     UserString(meter_string));
88             popup->AddMenuItem(GG::MenuItem(popup_label, false, false, pedia_meter_type_action));
89 
90             popup->Run();
91         });
92         AttachChild(meter_stat.second);
93         meters.push_back({meter_stat.first, AssociatedMeterType(meter_stat.first)});
94     }
95 
96     // attach and show meter bars and large resource indicators
97     m_multi_icon_value_indicator =  GG::Wnd::Create<MultiIconValueIndicator>(Width() - 2*EDGE_PAD,   m_popcenter_id, meters);
98     m_multi_meter_status_bar =      GG::Wnd::Create<MultiMeterStatusBar>(Width() - 2*EDGE_PAD,       m_popcenter_id, meters);
99 
100     // determine if this panel has been created yet.
101     std::map<int, bool>::iterator it = s_expanded_map.find(m_popcenter_id);
102     if (it == s_expanded_map.end())
103         s_expanded_map[m_popcenter_id] = false; // if not, default to collapsed state
104 
105     Refresh();
106 }
107 
~PopulationPanel()108 PopulationPanel::~PopulationPanel()
109 {}
110 
ExpandCollapse(bool expanded)111 void PopulationPanel::ExpandCollapse(bool expanded) {
112     if (expanded == s_expanded_map[m_popcenter_id]) return; // nothing to do
113     s_expanded_map[m_popcenter_id] = expanded;
114 
115     DoLayout();
116 }
117 
Update()118 void PopulationPanel::Update() {
119     // remove any old browse wnds
120     for (auto& meter_stat : m_meter_stats) {
121         meter_stat.second->ClearBrowseInfoWnd();
122         m_multi_icon_value_indicator->ClearToolTip(meter_stat.first);
123     }
124 
125     auto pop = Objects().get<PopCenter>(m_popcenter_id);
126     if (!pop) {
127         ErrorLogger() << "PopulationPanel::Update couldn't get PopCenter or couldn't get UniverseObject";
128         return;
129     }
130 
131     // meter bar displays population stats
132     if (m_multi_meter_status_bar)
133         m_multi_meter_status_bar->Update();
134     if (m_multi_icon_value_indicator)
135         m_multi_icon_value_indicator->Update();
136 
137     // tooltips
138     for (auto& meter_stat : m_meter_stats) {
139         meter_stat.second->SetValue(pop->GetMeter(meter_stat.first)->Initial());
140 
141         auto browse_wnd = GG::Wnd::Create<MeterBrowseWnd>(m_popcenter_id, meter_stat.first, AssociatedMeterType(meter_stat.first));
142         meter_stat.second->SetBrowseInfoWnd(browse_wnd);
143         m_multi_icon_value_indicator->SetToolTip(meter_stat.first, browse_wnd);
144     }
145 }
146 
Refresh()147 void PopulationPanel::Refresh() {
148     for (auto& meter_stat : m_meter_stats)
149         meter_stat.second->RequirePreRender();
150 
151     RequirePreRender();
152 }
153 
PreRender()154 void PopulationPanel::PreRender() {
155     AccordionPanel::PreRender();
156     Update();
157     DoLayout();
158 }
159 
ExpandCollapseButtonPressed()160 void PopulationPanel::ExpandCollapseButtonPressed()
161 { ExpandCollapse(!s_expanded_map[m_popcenter_id]); }
162 
DoLayout()163 void PopulationPanel::DoLayout() {
164     AccordionPanel::DoLayout();
165 
166     for (const auto& meter_stat : m_meter_stats)
167         DetachChild(meter_stat.second);
168 
169     // detach / hide meter bars and large resource indicators
170     DetachChild(m_multi_meter_status_bar);
171     DetachChild(m_multi_icon_value_indicator);
172 
173     // update size of panel and position and visibility of widgets
174     if (!s_expanded_map[m_popcenter_id]) {
175         // position and reattach icons to be shown
176         int n = 0;
177         GG::X stride = MeterIconSize().x * 7/2;
178         for (const auto& meter_stat : m_meter_stats) {
179             GG::X x = n * stride;
180 
181             auto& icon = meter_stat.second;
182             GG::Pt icon_ul(x, GG::Y0);
183             GG::Pt icon_lr = icon_ul + MeterIconSize();
184             icon->SizeMove(icon_ul, icon_lr);
185 
186             if (x + icon->MinUsableSize().x >= ClientWidth())
187                 break;
188 
189             AttachChild(icon);
190             icon->Show();
191 
192             n++;
193         }
194 
195         Resize(GG::Pt(Width(), std::max(MeterIconSize().y, m_expand_button->Height())));
196     } else if (m_multi_icon_value_indicator && m_multi_meter_status_bar) {
197         // attach and show meter bars and large resource indicators
198         GG::Y top = Top();
199 
200         AttachChild(m_multi_icon_value_indicator);
201         m_multi_icon_value_indicator->MoveTo(GG::Pt(GG::X(EDGE_PAD), GG::Y(EDGE_PAD)));
202         m_multi_icon_value_indicator->Resize(GG::Pt(Width() - 2*EDGE_PAD, m_multi_icon_value_indicator->Height()));
203 
204         AttachChild(m_multi_meter_status_bar);
205         m_multi_meter_status_bar->MoveTo(GG::Pt(GG::X(EDGE_PAD), m_multi_icon_value_indicator->Bottom() + EDGE_PAD - top));
206         m_multi_meter_status_bar->Resize(GG::Pt(Width() - 2*EDGE_PAD, m_multi_meter_status_bar->Height()));
207 
208         MoveChildUp(m_expand_button);
209 
210         Resize(GG::Pt(Width(), m_multi_meter_status_bar->Bottom() + EDGE_PAD - top));
211     }
212 
213     SetCollapsed(!s_expanded_map[m_popcenter_id]);
214 }
215 
216 std::map<int, bool> PopulationPanel::s_expanded_map;
217