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