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