1 //CUIWnd.cpp
2
3 #include "CUIWnd.h"
4
5 #include "../client/human/HumanClientApp.h"
6 #include "ClientUI.h"
7 #include "CUIControls.h"
8 #include "Sound.h"
9 #include "../util/i18n.h"
10 #include "../util/Logger.h"
11 #include "../util/OptionsDB.h"
12 #include "../util/Directories.h"
13 #include "../util/Logger.h"
14
15 #include <GG/GUI.h>
16
17 #include <limits>
18 #include <boost/algorithm/string.hpp>
19
20
21 namespace {
PlayMinimizeSound()22 void PlayMinimizeSound()
23 { Sound::GetSound().PlaySound(GetOptionsDB().Get<std::string>("ui.window.maximize.sound.path"), true); }
PlayMaximizeSound()24 void PlayMaximizeSound()
25 { Sound::GetSound().PlaySound(GetOptionsDB().Get<std::string>("ui.window.minimize.sound.path"), true); }
PlayCloseSound()26 void PlayCloseSound()
27 { Sound::GetSound().PlaySound(GetOptionsDB().Get<std::string>("ui.window.close.sound.path"), true); }
28
AddOptions(OptionsDB & db)29 void AddOptions(OptionsDB& db) {
30 db.AddFlag('w', "window-reset", UserStringNop("OPTIONS_DB_WINDOW_RESET"), false);
31 }
32 bool temp_bool = RegisterOptions(&AddOptions);
33
34 const double BUTTON_DIMMING_SCALE_FACTOR = 0.75;
35
36 const GG::X::value_type INVALID_POS = std::numeric_limits<GG::X::value_type>::min();
37 const GG::X INVALID_X = GG::X(INVALID_POS);
38 const GG::Y INVALID_Y = GG::Y(INVALID_POS);
39 }
40
41 ////////////////////////////////////////////////
42 // CUI_MinRestoreButton
43 ////////////////////////////////////////////////
CUI_MinRestoreButton()44 CUI_MinRestoreButton::CUI_MinRestoreButton() :
45 GG::Button("", nullptr, ClientUI::WndInnerBorderColor()),
46 m_mode(Mode::MINIMIZE)
47 {
48 LeftClickedSignal.connect(boost::bind(&CUI_MinRestoreButton::Toggle, this));
49 }
50
Render()51 void CUI_MinRestoreButton::Render() {
52 GG::Pt ul = UpperLeft();
53 GG::Pt lr = LowerRight();
54 GG::Clr color_to_use = ClientUI::WndInnerBorderColor();
55 if (State() != BN_ROLLOVER)
56 AdjustBrightness(color_to_use, BUTTON_DIMMING_SCALE_FACTOR);
57 if (m_mode == Mode::MINIMIZE) {
58 // draw a dash to signify the minimize command
59 GG::Y middle_y = (lr.y + ul.y) / 2;
60 glColor(color_to_use);
61 GG::Line(ul.x, middle_y, lr.x, middle_y);
62 } else {
63 // draw a square to signify the restore command
64 GG::FlatRectangle(ul, lr, GG::CLR_ZERO, ClientUI::WndInnerBorderColor(), 1);
65 }
66 }
67
Toggle()68 void CUI_MinRestoreButton::Toggle() {
69 if (m_mode == Mode::MINIMIZE) {
70 PlayMinimizeSound();
71 m_mode = Mode::RESTORE;
72 } else {
73 PlayMaximizeSound();
74 m_mode = Mode::MINIMIZE;
75 }
76 }
77
78
79 ////////////////////////////////////////////////
80 // CUI_PinButton
81 ////////////////////////////////////////////////
CUI_PinButton()82 CUI_PinButton::CUI_PinButton() :
83 GG::Button("", nullptr, ClientUI::WndInnerBorderColor())
84 {
85 LeftClickedSignal.connect(-1,
86 &PlayCloseSound);
87 SetUnpressedGraphic(GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin.png" )));
88 SetPressedGraphic (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin.png" )));
89 SetRolloverGraphic (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin_mouseover.png")));
90 }
91
Toggle(bool pinned)92 void CUI_PinButton::Toggle(bool pinned) {
93 if (!pinned) {
94 SetUnpressedGraphic(GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin.png")));
95 SetPressedGraphic (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin.png")));
96 SetRolloverGraphic (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pin_mouseover.png")));
97 } else {
98 SetUnpressedGraphic(GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pinned.png")));
99 SetPressedGraphic (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pinned.png")));
100 SetRolloverGraphic (GG::SubTexture(ClientUI::GetTexture( ClientUI::ArtDir() / "icons" / "buttons" / "pinned_mouseover.png")));
101 }
102 }
103
104 ////////////////////////////////////////////////
105 // CUIWnd
106 ////////////////////////////////////////////////
107 GG::WndFlag MINIMIZABLE(1 << 10);
108 GG::WndFlag CLOSABLE(1 << 11);
109 GG::WndFlag PINABLE(1 << 12);
110
111 namespace {
RegisterWndFlags()112 bool RegisterWndFlags() {
113 GG::FlagSpec<GG::WndFlag>::instance().insert(MINIMIZABLE, "MINIMIZABLE");
114 GG::FlagSpec<GG::WndFlag>::instance().insert(CLOSABLE, "CLOSABLE");
115 GG::FlagSpec<GG::WndFlag>::instance().insert(PINABLE, "PINABLE");
116 return true;
117 }
118 bool dummy = RegisterWndFlags();
119
WindowNameFromOption(const std::string & option_name)120 std::string WindowNameFromOption(const std::string& option_name) {
121 std::string::size_type prefix_len { std::string("ui.").length() };
122
123 // Determine end of window name from start of window mode
124 std::string::size_type mode_substr_pos { option_name.find(".fullscreen", prefix_len + 1) };
125 if (mode_substr_pos == std::string::npos) {
126 mode_substr_pos = option_name.find(".windowed", prefix_len + 1);
127 if (mode_substr_pos == std::string::npos) {
128 ErrorLogger() << "Could not determine window name from option " << option_name;
129 return {};
130 }
131 }
132
133 std::string::size_type name_len = mode_substr_pos - prefix_len;
134 return option_name.substr(prefix_len, name_len);
135 }
136 }
137
138 const GG::Y CUIWnd::BUTTON_TOP_OFFSET(3);
139 const GG::X CUIWnd::MINIMIZED_WND_WIDTH(50);
140 const GG::X CUIWnd::BORDER_LEFT(5);
141 const GG::X CUIWnd::BORDER_RIGHT(5);
142 const GG::Y CUIWnd::BORDER_BOTTOM(5);
143 const int CUIWnd::OUTER_EDGE_ANGLE_OFFSET = 8;
144 const int CUIWnd::INNER_BORDER_ANGLE_OFFSET = 15;
145 const int CUIWnd::TITLE_OFFSET = 2;
146 const int CUIWnd::RESIZE_HASHMARK1_OFFSET = 9;
147 const int CUIWnd::RESIZE_HASHMARK2_OFFSET = 4;
148
CUIWnd(const std::string & wnd_name,GG::X x,GG::Y y,GG::X w,GG::Y h,GG::Flags<GG::WndFlag> flags,const std::string & config_name,bool visible)149 CUIWnd::CUIWnd(const std::string& wnd_name,
150 GG::X x, GG::Y y,
151 GG::X w, GG::Y h,
152 GG::Flags<GG::WndFlag> flags,
153 const std::string& config_name,
154 bool visible) :
155 GG::Wnd(x, y, w, h, flags & ~GG::RESIZABLE),
156 m_resizable(flags & GG::RESIZABLE),
157 m_closable(flags & CLOSABLE),
158 m_minimizable(flags & MINIMIZABLE),
159 m_pinable(flags & PINABLE),
160 m_drag_offset(-GG::X1, -GG::Y1),
161 m_config_name(AddWindowOptions(config_name, x, y, w, h, visible, false, false))
162 {
163 SetName(wnd_name);
164 if (!m_config_name.empty()) {
165 // Default position was already supplied
166 GetOptionsDB().Set<bool>("ui." + m_config_name + ".initialized", true);
167 }
168 }
169
CUIWnd(const std::string & wnd_name,GG::Flags<GG::WndFlag> flags,const std::string & config_name,bool visible)170 CUIWnd::CUIWnd(const std::string& wnd_name, GG::Flags<GG::WndFlag> flags, const std::string& config_name, bool visible) :
171 GG::Wnd(INVALID_X, INVALID_Y, GG::X1, GG::Y1, flags & ~GG::RESIZABLE),
172 m_resizable(flags & GG::RESIZABLE),
173 m_closable(flags & CLOSABLE),
174 m_minimizable(flags & MINIMIZABLE),
175 m_pinable(flags & PINABLE),
176 m_drag_offset(-GG::X1, -GG::Y1),
177 m_config_name(AddWindowOptions(config_name, INVALID_POS, INVALID_POS, 1, 1, visible, false, false))
178 { SetName(wnd_name); }
179
CompleteConstruction()180 void CUIWnd::CompleteConstruction() {
181 GG::Wnd::CompleteConstruction();
182 Init();
183 ValidatePosition();
184 SetDefaultedOptions();
185 }
186
Init()187 void CUIWnd::Init() {
188 InitButtons();
189 SetChildClippingMode(ClipToClientAndWindowSeparately);
190
191 if (!m_config_name.empty()) {
192 LoadOptions();
193 HumanClientApp::GetApp()->FullscreenSwitchSignal.connect(
194 boost::bind(&CUIWnd::LoadOptions, this));
195 }
196
197 // User-dragable windows recalculate their position only when told to (e.g.
198 // auto-reposition is set or user clicks a 'reset windows' button).
199 // Non-user-dragable windows are given the chance to position themselves on
200 // every resize event.
201 if (Dragable() || m_resizable)
202 HumanClientApp::GetApp()->RepositionWindowsSignal.connect(
203 boost::bind(&CUIWnd::ResetDefaultPosition, this));
204 else
205 HumanClientApp::GetApp()->WindowResizedSignal.connect(
206 boost::bind(&CUIWnd::ResetDefaultPosition, this));
207 }
208
InitSizeMove(const GG::Pt & ul,const GG::Pt & lr)209 void CUIWnd::InitSizeMove(const GG::Pt& ul, const GG::Pt& lr) {
210 OptionsDB& db = GetOptionsDB();
211
212 if (!m_config_name.empty()) {
213 std::string option_prefix = "ui." + m_config_name;
214 if (db.OptionExists(option_prefix + ".initialized")) {
215 std::string window_mode = db.Get<bool>("video.fullscreen.enabled") ?
216 ".fullscreen" : ".windowed";
217 // If the window has already had its default position specified
218 // (either in the ctor or a previous call to this function), apply
219 // this position to the window.
220 if (db.Get<bool>(option_prefix + ".initialized") ||
221 db.Get<int>(option_prefix + window_mode + ".left") == INVALID_X)
222 {
223 SetDefaultedOptions();
224 SizeMove(ul, lr);
225 SaveDefaultedOptions();
226 }
227 db.Set<bool>(option_prefix + ".initialized", true);
228 } else {
229 ErrorLogger() << "CUIWnd::InitSizeMove() : attempted to check if window using name \"" << m_config_name
230 << "\" was initialized but the options do not appear to be registered in the OptionsDB.";
231 }
232 } else {
233 SizeMove(ul, lr);
234 }
235 }
236
~CUIWnd()237 CUIWnd::~CUIWnd() {
238 try {
239 if (!m_config_name.empty() && GetOptionsDB().OptionExists("ui." + m_config_name + ".initialized"))
240 GetOptionsDB().Remove("ui." + m_config_name + ".initialized");
241 } catch (std::exception& e) { // catch std::runtime_error, boost::bad_any_cast
242 ErrorLogger() << "CUIWnd::~CUIWnd() : caught exception while removing \"ui." << m_config_name
243 << ".initialized\": " << e.what();
244 }
245 m_vertex_buffer.clear();
246 }
247
ValidatePosition()248 void CUIWnd::ValidatePosition()
249 { SizeMove(RelativeUpperLeft(), RelativeLowerRight()); }
250
SizeMove(const GG::Pt & ul,const GG::Pt & lr)251 void CUIWnd::SizeMove(const GG::Pt& ul, const GG::Pt& lr) {
252 GG::Pt old_sz = Size();
253 if (m_config_save) { // can write position/size to OptionsDB
254
255 GG::Pt available_size;
256 if (const auto&& parent = Parent()) {
257 // Keep this CUIWnd entirely inside its parent.
258 available_size = parent->ClientSize();
259 } else if (const HumanClientApp* app = HumanClientApp::GetApp()) {
260 // Keep this CUIWnd entirely inside the application window.
261 available_size = GG::Pt(app->AppWidth(), app->AppHeight());
262 } else {
263 available_size = GG::Pt(GG::X(HumanClientApp::MaximumPossibleWidth()),
264 GG::Y(HumanClientApp::MaximumPossibleHeight()));
265 ErrorLogger() << "CUIWnd::SizeMove() could not get app instance!";
266 }
267
268 // Limit window size to be no larger than the containing window.
269 GG::Pt new_size(std::max(std::min(lr.x - ul.x, available_size.x), MinimizedSize().x),
270 std::max(std::min(lr.y - ul.y, available_size.y),
271 TopBorder() + INNER_BORDER_ANGLE_OFFSET + BORDER_BOTTOM + 50));
272
273 // Clamp position of this window to keep its entire area visible in the
274 // containing window.
275 GG::Pt new_ul(std::min(available_size.x - new_size.x, std::max(GG::X0, ul.x)),
276 std::min(available_size.y - new_size.y, std::max(GG::Y0, ul.y)));
277
278 Wnd::SizeMove(new_ul, new_ul + new_size);
279
280 } else { // don't write position/size to OptionsDB
281 Wnd::SizeMove(ul, lr);
282 }
283
284 if (Size() != old_sz)
285 PositionButtons();
286
287 SaveOptions();
288 m_vertex_buffer.clear(); // force buffer re-init on next Render call
289 }
290
Render()291 void CUIWnd::Render() {
292 if (m_vertex_buffer.empty())
293 InitBuffers();
294
295 glDisable(GL_TEXTURE_2D);
296 glLineWidth(1.0f);
297
298 glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
299
300 glEnableClientState(GL_VERTEX_ARRAY);
301 glDisableClientState(GL_COLOR_ARRAY);
302 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
303
304 m_vertex_buffer.activate();
305
306 // within m_vertex_buffer:
307 // [0] is the start and range for minimized background triangle fan and minimized border line loop
308 // [1] is ... the background fan / outer border line loop
309 // [2] is ... the inner border line loop
310 // [3] is ... the resize tab line list
311
312 if (m_minimized) {
313 glColor(ClientUI::WndColor());
314 glDrawArrays(GL_TRIANGLE_FAN, m_buffer_indices[0].first, m_buffer_indices[0].second);
315 glColor(ClientUI::WndOuterBorderColor());
316 glDrawArrays(GL_LINE_LOOP, m_buffer_indices[0].first, m_buffer_indices[0].second);
317
318 } else {
319 bool flashing = m_flashing && static_cast<int>(GG::GUI::GetGUI()->Ticks()) % (m_flash_duration * 2) > m_flash_duration;
320 auto focus_wnd = GG::GUI::GetGUI()->FocusWnd();
321 bool highlight = (focus_wnd.get() == this || this->IsAncestorOf(focus_wnd));
322
323 flashing ? glColor(GG::LightenClr(ClientUI::WndColor())) : glColor(ClientUI::WndColor());
324 glDrawArrays(GL_TRIANGLE_FAN, m_buffer_indices[1].first, m_buffer_indices[1].second);
325 flashing || highlight ? glColor(GG::LightenClr(ClientUI::WndOuterBorderColor())) : glColor(ClientUI::WndOuterBorderColor());
326 glDrawArrays(GL_LINE_LOOP, m_buffer_indices[1].first, m_buffer_indices[1].second);
327 flashing ? glColor(GG::LightenClr(ClientUI::WndInnerBorderColor())) : glColor(ClientUI::WndInnerBorderColor());
328 glDrawArrays(GL_LINE_LOOP, m_buffer_indices[2].first, m_buffer_indices[2].second);
329
330 if (m_resizable) {
331 GG::Clr tab_lines_colour = m_mouse_in_resize_tab ? ClientUI::WndInnerBorderColor() : ClientUI::WndOuterBorderColor();
332 glColor(tab_lines_colour);
333 glDrawArrays(GL_LINES, m_buffer_indices[3].first, m_buffer_indices[3].second);
334 }
335 }
336
337 glEnable(GL_TEXTURE_2D);
338
339 glPopClientAttrib();
340
341 GG::Pt ul = UpperLeft();
342 GG::Pt lr = LowerRight();
343 GG::BeginScissorClipping(ul, lr);
344 glColor(ClientUI::TextColor());
345 std::shared_ptr<GG::Font> font = ClientUI::GetTitleFont();
346 font->RenderText(GG::Pt(ul.x + BORDER_LEFT, ul.y + TITLE_OFFSET), Name());
347 GG::EndScissorClipping();
348 }
349
LButtonDown(const GG::Pt & pt,GG::Flags<GG::ModKey> mod_keys)350 void CUIWnd::LButtonDown(const GG::Pt& pt, GG::Flags<GG::ModKey> mod_keys) {
351 if (!InResizeTab(pt))
352 return;
353 m_drag_offset = pt - LowerRight();
354 }
355
InResizeTab(const GG::Pt & pt) const356 bool CUIWnd::InResizeTab(const GG::Pt& pt) const {
357 if (!m_resizable || m_minimized)
358 return false;
359
360 GG::Pt cl_lr = LowerRight() - GG::Pt(BORDER_RIGHT, BORDER_BOTTOM);
361 GG::Pt dist_from_lr = cl_lr - pt;
362 if (Value(dist_from_lr.x) + Value(dist_from_lr.y) <= INNER_BORDER_ANGLE_OFFSET)
363 return true;
364
365 return false;
366 }
367
LDrag(const GG::Pt & pt,const GG::Pt & move,GG::Flags<GG::ModKey> mod_keys)368 void CUIWnd::LDrag(const GG::Pt& pt, const GG::Pt& move, GG::Flags<GG::ModKey> mod_keys) {
369 if (m_pinned)
370 return;
371
372 if (m_drag_offset != GG::Pt(-GG::X1, -GG::Y1)) { // resize-dragging
373 // drag offset: position of cursor relative to lower-right of window when left button was pressed
374 // pt: position of cursor relative to upper-left of screen
375 GG::Pt requested_lr = pt - m_drag_offset;
376
377 GG::Pt max_lr;
378 if (const auto&& parent = Parent()) {
379 max_lr = parent->ClientLowerRight();
380 } else {
381 max_lr.x = GG::GUI::GetGUI()->AppWidth();
382 max_lr.y = GG::GUI::GetGUI()->AppHeight();
383 }
384
385 GG::X new_x = std::min(max_lr.x, requested_lr.x);
386 GG::Y new_y = std::min(max_lr.y, requested_lr.y);
387 GG::Pt new_lr(new_x, new_y);
388
389 Resize(new_lr - UpperLeft());
390
391 } else { // normal-dragging
392 GG::Wnd::LDrag(pt, move, mod_keys);
393 }
394 }
395
LButtonUp(const GG::Pt & pt,GG::Flags<GG::ModKey> mod_keys)396 void CUIWnd::LButtonUp(const GG::Pt& pt, GG::Flags<GG::ModKey> mod_keys) {
397 m_drag_offset = GG::Pt(-GG::X1, -GG::Y1);
398 SaveOptions();
399 }
400
MouseEnter(const GG::Pt & pt,GG::Flags<GG::ModKey> mod_keys)401 void CUIWnd::MouseEnter(const GG::Pt& pt, GG::Flags<GG::ModKey> mod_keys) {
402 m_mouse_in_resize_tab = InResizeTab(pt);
403 Wnd::MouseEnter(pt, mod_keys);
404 }
405
MouseHere(const GG::Pt & pt,GG::Flags<GG::ModKey> mod_keys)406 void CUIWnd::MouseHere(const GG::Pt& pt, GG::Flags<GG::ModKey> mod_keys) {
407 m_mouse_in_resize_tab = InResizeTab(pt);
408 Wnd::MouseHere(pt, mod_keys);
409 }
410
MouseLeave()411 void CUIWnd::MouseLeave() {
412 m_mouse_in_resize_tab = false;
413 Wnd::MouseLeave();
414 }
415
ClientUpperLeft() const416 GG::Pt CUIWnd::ClientUpperLeft() const
417 { return m_minimized ? UpperLeft() : UpperLeft() + GG::Pt(BORDER_LEFT, TopBorder()); }
418
ClientLowerRight() const419 GG::Pt CUIWnd::ClientLowerRight() const
420 { return m_minimized ? LowerRight() : LowerRight() - GG::Pt(BORDER_RIGHT, BORDER_BOTTOM); }
421
InWindow(const GG::Pt & pt) const422 bool CUIWnd::InWindow(const GG::Pt& pt) const {
423 GG::Pt lr = LowerRight();
424 if (m_resizable) {
425 return UpperLeft() <= pt && pt < lr;
426 } else {
427 GG::Pt dist_from_lr = lr - pt;
428 bool inside_lower_right_corner = OUTER_EDGE_ANGLE_OFFSET < Value(dist_from_lr.x) + Value(dist_from_lr.y);
429 return (UpperLeft() <= pt && pt < lr && inside_lower_right_corner);
430 }
431 }
432
PositionButtons()433 void CUIWnd::PositionButtons() {
434 // The buttons are to be positioned based on the presence of other buttons
435 GG::Pt button_ul = GG::Pt(Width() - ClientUI::Pts() - BORDER_RIGHT, BUTTON_TOP_OFFSET) + UpperLeft() - ClientUpperLeft();
436
437 if (m_close_button) {
438 m_close_button->MoveTo(GG::Pt(button_ul.x, button_ul.y));
439 button_ul -= GG::Pt(m_close_button->Width(), GG::Y0) + GG::Pt(GG::X(ClientUI::TitlePts()/3), GG::Y0);
440 }
441
442 if (m_minimize_button) {
443 m_minimize_button->MoveTo(GG::Pt(button_ul.x, button_ul.y));
444 button_ul -= GG::Pt(m_minimize_button->Width(), GG::Y0) + GG::Pt(GG::X(ClientUI::TitlePts()/3), GG::Y0);
445 }
446
447 if (m_pin_button)
448 m_pin_button->MoveTo(GG::Pt(button_ul.x, button_ul.y));
449 }
450
InitButtons()451 void CUIWnd::InitButtons() {
452 boost::filesystem::path button_texture_dir = ClientUI::ArtDir() / "icons" / "buttons";
453
454 // create the close button
455 if (m_closable) {
456 m_close_button = Wnd::Create<CUIButton>(
457 GG::SubTexture(ClientUI::GetTexture(button_texture_dir / "close.png")),
458 GG::SubTexture(ClientUI::GetTexture(button_texture_dir / "close_clicked.png")),
459 GG::SubTexture(ClientUI::GetTexture(button_texture_dir / "close_mouseover.png")));
460 m_close_button->SetColor(ClientUI::WndInnerBorderColor());
461 m_close_button->LeftClickedSignal.connect(-1,
462 &PlayCloseSound);
463 m_close_button->Resize(GG::Pt(GG::X(ClientUI::TitlePts()), GG::Y(ClientUI::TitlePts())));
464 m_close_button->LeftClickedSignal.connect(boost::bind(&CUIWnd::CloseClicked, this));
465 AttachChild(m_close_button);
466 m_close_button->NonClientChild(true);
467 }
468
469 // create the minimize button
470 if (m_minimizable) {
471 m_minimize_button = Wnd::Create<CUI_MinRestoreButton>();
472 m_minimize_button->Resize(GG::Pt(GG::X(ClientUI::TitlePts()), GG::Y(ClientUI::TitlePts())));
473 m_minimize_button->LeftClickedSignal.connect(boost::bind(&CUIWnd::MinimizeClicked, this));
474 AttachChild(m_minimize_button);
475 m_minimize_button->NonClientChild(true);
476 }
477
478 // create the pin button
479 if (m_pinable) {
480 m_pin_button = Wnd::Create<CUI_PinButton>();
481 m_pin_button->Resize(GG::Pt(GG::X(ClientUI::TitlePts()), GG::Y(ClientUI::TitlePts())));
482 m_pin_button->LeftClickedSignal.connect(boost::bind(&CUIWnd::PinClicked, this));
483 AttachChild(m_pin_button);
484 m_pin_button->NonClientChild(true);
485 }
486
487 // All buttons were created at the same spot, position them to the correct spot
488 PositionButtons();
489 }
490
MinimizedSize() const491 GG::Pt CUIWnd::MinimizedSize() const
492 { return GG::Pt(MINIMIZED_WND_WIDTH, TopBorder()); }
493
LeftBorder() const494 GG::X CUIWnd::LeftBorder() const
495 { return BORDER_LEFT; }
496
TopBorder() const497 GG::Y CUIWnd::TopBorder() const
498 { return GG::Y(ClientUI::TitlePts() + TITLE_OFFSET*4); }
499
RightBorder() const500 GG::X CUIWnd::RightBorder() const
501 { return BORDER_RIGHT; }
502
BottomBorder() const503 GG::Y CUIWnd::BottomBorder() const
504 { return BORDER_BOTTOM; }
505
InnerBorderAngleOffset() const506 int CUIWnd::InnerBorderAngleOffset() const
507 { return INNER_BORDER_ANGLE_OFFSET; }
508
CloseClicked()509 void CUIWnd::CloseClicked() {
510 m_done = true;
511 if (auto&& parent = Parent())
512 parent->DetachChild(this);
513 else
514 GG::GUI::GetGUI()->Remove(shared_from_this());
515
516 //m_minimized_buffer.clear();
517 //m_outer_border_buffer.clear();
518 //m_inner_border_buffer.clear();
519 //m_resize_corner_lines_buffer.clear();
520 }
521
PinClicked()522 void CUIWnd::PinClicked() {
523 m_pinned = !m_pinned;
524 m_resizable = !m_pinned;
525 m_pin_button->Toggle(m_pinned); // Change the icon on the pin button
526 m_vertex_buffer.clear(); // force buffer re-init on next Render call
527 SaveOptions();
528 }
529
MinimizeClicked()530 void CUIWnd::MinimizeClicked() {
531 if (!m_minimized) {
532 m_minimized = true;
533 m_original_size = Size();
534 SetMinSize(MinimizedSize());
535 Resize(MinimizedSize());
536
537 // hide all children, re-showing only position/size controls
538 Hide();
539 Show();
540 if (m_close_button)
541 m_close_button->Show();
542 if (m_minimize_button)
543 m_minimize_button->Show();
544 if (m_pin_button)
545 m_pin_button->Show();
546
547 } else {
548 m_minimized = false;
549 SetMinSize(GG::Pt(MinimizedSize().x, TopBorder() + INNER_BORDER_ANGLE_OFFSET + BORDER_BOTTOM + 10));
550 Resize(GG::Pt(m_original_size));
551 Show();
552 }
553 SaveOptions();
554 }
555
InitBuffers()556 void CUIWnd::InitBuffers() {
557 m_vertex_buffer.clear();
558 m_vertex_buffer.reserve(17);
559 m_buffer_indices.resize(4);
560 std::size_t previous_buffer_size = m_vertex_buffer.size();
561
562 GG::Pt ul = UpperLeft(), lr = LowerRight();
563 GG::Pt cl_ul = ClientUpperLeft(), cl_lr = ClientLowerRight();
564
565 // within m_vertex_buffer:
566 // [0] is the start and range for minimized background triangle fan and minimized border line loop
567 // [1] is ... the background fan / outer border line loop
568 // [2] is ... the inner border line loop
569 // [3] is ... the resize tab line list
570
571 // minimized background fan and border line loop
572 m_vertex_buffer.store(Value(ul.x), Value(ul.y));
573 m_vertex_buffer.store(Value(lr.x), Value(ul.y));
574 m_vertex_buffer.store(Value(lr.x), Value(lr.y));
575 m_vertex_buffer.store(Value(ul.x), Value(lr.y));
576 m_buffer_indices[0].first = previous_buffer_size;
577 m_buffer_indices[0].second = m_vertex_buffer.size() - previous_buffer_size;
578 previous_buffer_size = m_vertex_buffer.size();
579
580 // outer border, with optional corner cutout
581 m_vertex_buffer.store(Value(ul.x), Value(ul.y));
582 m_vertex_buffer.store(Value(lr.x), Value(ul.y));
583 if (!m_resizable) {
584 m_vertex_buffer.store(Value(lr.x), Value(lr.y) - OUTER_EDGE_ANGLE_OFFSET);
585 m_vertex_buffer.store(Value(lr.x) - OUTER_EDGE_ANGLE_OFFSET, Value(lr.y));
586 } else {
587 m_vertex_buffer.store(Value(lr.x), Value(lr.y));
588 }
589 m_vertex_buffer.store(Value(ul.x), Value(lr.y));
590 m_buffer_indices[1].first = previous_buffer_size;
591 m_buffer_indices[1].second = m_vertex_buffer.size() - previous_buffer_size;
592 previous_buffer_size = m_vertex_buffer.size();
593
594 // inner border, with optional corner cutout
595 m_vertex_buffer.store(Value(cl_ul.x), Value(cl_ul.y));
596 m_vertex_buffer.store(Value(cl_lr.x), Value(cl_ul.y));
597 if (m_resizable) {
598 m_vertex_buffer.store(Value(cl_lr.x), Value(cl_lr.y) - INNER_BORDER_ANGLE_OFFSET);
599 m_vertex_buffer.store(Value(cl_lr.x) - INNER_BORDER_ANGLE_OFFSET, Value(cl_lr.y));
600 } else {
601 m_vertex_buffer.store(Value(cl_lr.x), Value(cl_lr.y));
602 }
603 m_vertex_buffer.store(Value(cl_ul.x), Value(cl_lr.y));
604 m_buffer_indices[2].first = previous_buffer_size;
605 m_buffer_indices[2].second = m_vertex_buffer.size() - previous_buffer_size;
606 previous_buffer_size = m_vertex_buffer.size();
607
608 // resize hash marks
609 m_vertex_buffer.store(Value(cl_lr.x), Value(cl_lr.y) - RESIZE_HASHMARK1_OFFSET);
610 m_vertex_buffer.store(Value(cl_lr.x) - RESIZE_HASHMARK1_OFFSET, Value(cl_lr.y));
611 m_vertex_buffer.store(Value(cl_lr.x), Value(cl_lr.y) - RESIZE_HASHMARK2_OFFSET);
612 m_vertex_buffer.store(Value(cl_lr.x) - RESIZE_HASHMARK2_OFFSET, Value(cl_lr.y));
613 m_buffer_indices[3].first = previous_buffer_size;
614 m_buffer_indices[3].second = m_vertex_buffer.size() - previous_buffer_size;
615 //previous_buffer_size = m_vertex_buffer.size();
616
617 m_vertex_buffer.createServerBuffer();
618
619 //TraceLogger() << "CUIWnd vertex buffer final size: " << previous_buffer_size << std::endl;
620 }
621
Hide()622 void CUIWnd::Hide() {
623 GG::Wnd::Hide();
624 SaveOptions();
625 }
626
Show()627 void CUIWnd::Show() {
628 GG::Wnd::Show();
629 SaveOptions();
630 }
631
ResetDefaultPosition()632 void CUIWnd::ResetDefaultPosition() {
633 GG::Rect default_position = CalculatePosition();
634 if (default_position.ul.x != INVALID_X) // do nothing if not overridden
635 InitSizeMove(default_position.ul, default_position.lr);
636 }
637
CalculatePosition() const638 GG::Rect CUIWnd::CalculatePosition() const
639 { return GG::Rect(INVALID_X, INVALID_Y, INVALID_X, INVALID_Y); }
640
SetDefaultedOptions()641 void CUIWnd::SetDefaultedOptions() {
642 OptionsDB& db = GetOptionsDB();
643 std::set<std::string> window_options;
644 db.FindOptions(window_options, "ui." + m_config_name);
645 for (auto& option : window_options) {
646 if (db.IsDefaultValue(option))
647 m_defaulted_options.emplace(option);
648 }
649 }
650
SaveDefaultedOptions()651 void CUIWnd::SaveDefaultedOptions() {
652 OptionsDB& db = GetOptionsDB();
653 std::string config_prefix = "ui." + m_config_name;
654 std::string window_mode = db.Get<bool>("video.fullscreen.enabled") ?
655 ".fullscreen" : ".windowed";
656 GG::Pt size;
657 if (m_minimized)
658 size = m_original_size;
659 else
660 size = Size();
661
662 std::string config_name = config_prefix + window_mode + ".left";
663 int int_value = Value(RelativeUpperLeft().x);
664 if (m_defaulted_options.count(config_name))
665 db.SetDefault<int>(config_name, int_value);
666
667 config_name = config_prefix + window_mode + ".top";
668 int_value = Value(RelativeUpperLeft().y);
669 if (m_defaulted_options.count(config_name))
670 db.SetDefault<int>(config_name, int_value);
671
672 config_name = config_prefix + window_mode + ".width";
673 int_value = Value(size.x);
674 if (m_defaulted_options.count(config_name))
675 db.SetDefault<int>(config_name, int_value);
676
677 config_name = config_prefix + window_mode + ".height";
678 int_value = Value(size.y);
679 if (m_defaulted_options.count(config_name))
680 db.SetDefault<int>(config_name, int_value);
681
682 if (!Modal()) {
683 config_name = config_prefix + ".visible";
684 bool bool_value = Visible();
685 if (m_defaulted_options.count(config_name))
686 db.SetDefault<bool>(config_name, bool_value);
687
688 config_name = config_prefix + ".pinned";
689 bool_value = m_pinned;
690 if (m_defaulted_options.count(config_name))
691 db.SetDefault<bool>(config_name, bool_value);
692
693 config_name = config_prefix + ".minimized";
694 bool_value = m_minimized;
695 if (m_defaulted_options.count(config_name))
696 db.SetDefault<bool>(config_name, bool_value);
697 }
698 }
699
SaveOptions() const700 void CUIWnd::SaveOptions() const {
701 OptionsDB& db = GetOptionsDB();
702
703 // The default empty string means 'do not save/load properties'
704 // Also do not save while the window is being dragged.
705 auto gui = GG::GUI::GetGUI();
706 std::string option_prefix = "ui." + m_config_name;
707 if (m_config_name.empty() || !m_config_save || !gui || gui->DragWnd(this, 0)) {
708 return;
709 } else if (!db.OptionExists(option_prefix + ".initialized")) {
710 ErrorLogger() << "CUIWnd::SaveOptions() : attempted to save window options using name \"" << m_config_name << "\" but the options do not appear to be registered in the OptionsDB.";
711 return;
712 } else if (!db.Get<bool>(option_prefix + ".initialized")) {
713 // Don't save until the window has been given its proper default values
714 return;
715 }
716
717 GG::Pt size;
718 if (m_minimized)
719 size = m_original_size;
720 else
721 size = Size();
722
723 std::string window_mode = db.Get<bool>("video.fullscreen.enabled") ?
724 ".fullscreen" : ".windowed";
725
726 db.Set<int>(option_prefix + window_mode + ".left", Value(RelativeUpperLeft().x));
727 db.Set<int>(option_prefix + window_mode + ".top", Value(RelativeUpperLeft().y));
728 db.Set<int>(option_prefix + window_mode + ".width", Value(size.x));
729 db.Set<int>(option_prefix + window_mode + ".height", Value(size.y));
730
731 if (!Modal()) {
732 db.Set<bool>(option_prefix + ".visible", Visible());
733 db.Set<bool>(option_prefix + ".pinned", m_pinned);
734 db.Set<bool>(option_prefix + ".minimized", m_minimized);
735 }
736
737 db.Commit();
738 }
739
LoadOptions()740 void CUIWnd::LoadOptions() {
741 OptionsDB& db = GetOptionsDB();
742
743 // The default empty string means 'do not save/load properties'
744 std::string option_prefix = "ui." + m_config_name;
745 if (m_config_name.empty()) {
746 return;
747 } else if (!db.OptionExists(option_prefix + ".initialized")) {
748 ErrorLogger() << "CUIWnd::LoadOptions() : attempted to load window options using name \"" << m_config_name << "\" but the options do not appear to be registered in the OptionsDB.";
749 return;
750 }
751
752 // These functions are only called in certain circumstances, could pass in
753 // things like the fullscreen/windowed mode instead of using global program
754 // state like this?
755 std::string window_mode = db.Get<bool>("video.fullscreen.enabled") ?
756 ".fullscreen" : ".windowed";
757 GG::Pt ul = GG::Pt(GG::X(db.Get<int>(option_prefix + window_mode + ".left")),
758 GG::Y(db.Get<int>(option_prefix + window_mode + ".top")));
759 GG::Pt size = GG::Pt(GG::X(db.Get<int>(option_prefix + window_mode + ".width")),
760 GG::Y(db.Get<int>(option_prefix + window_mode + ".height")));
761
762 m_config_save = false;
763
764 if (m_minimized) {
765 MinimizeClicked();
766 }
767
768 if (ul.x == INVALID_X || ul.y == INVALID_Y) {
769 // If no options have been saved yet, allow the window to calculate its
770 // own position. Note that when this is first called from the CUIWnd
771 // constructor it will call CUIWnd::CalculatePosition() (a no-op) but
772 // afterwards will call derived overrides. This will still do nothing
773 // for windows that don't override CalculatePosition() but they should
774 // be positioned by their owners in any case.
775 ResetDefaultPosition();
776 } else {
777 SizeMove(ul, ul + size);
778 }
779
780 if (!Modal()) {
781 if (db.Get<bool>(option_prefix + ".visible")) {
782 Show();
783 } else {
784 Hide();
785 }
786
787 if (db.Get<bool>(option_prefix + ".pinned") != m_pinned) {
788 PinClicked();
789 }
790
791 if (db.Get<bool>(option_prefix + ".minimized") != m_minimized) {
792 MinimizeClicked();
793 }
794 }
795
796 m_config_save = true;
797 }
798
AddWindowOptions(const std::string & config_name,int left,int top,int width,int height,bool visible,bool pinned,bool minimized)799 const std::string CUIWnd::AddWindowOptions(const std::string& config_name,
800 int left, int top,
801 int width, int height,
802 bool visible, bool pinned, bool minimized)
803 {
804 OptionsDB& db = GetOptionsDB();
805 std::string new_name = "";
806
807 if (db.OptionExists("ui." + config_name + ".fullscreen.left")) {
808 // If the option has already been added, a window was previously created with this name...
809 if (config_name.empty()) {
810 // Should never happen, but just in case.
811 db.Remove("ui..fullscreen.left");
812 db.Remove("ui..fullscreen.top");
813 db.Remove("ui..windowed.left");
814 db.Remove("ui..windowed.top");
815 db.Remove("ui..fullscreen.width");
816 db.Remove("ui..fullscreen.height");
817 db.Remove("ui..windowed.width");
818 db.Remove("ui..windowed.height");
819 db.Remove("ui..visible");
820 db.Remove("ui..pinned");
821 db.Remove("ui..minimized");
822 ErrorLogger() << "CUIWnd::AddWindowOptions() : Found window options with a blank name, removing those options.";
823 } else if (db.OptionExists("ui." + config_name + ".initialized")) {
824 // If the window's still there, shouldn't use the same name (but the window can still be created so don't throw)
825 ErrorLogger() << "CUIWnd::AddWindowOptions() : Attempted to create a window with config_name = " << config_name << " but one already exists with that name.";
826 } else {
827 // Old window has been destroyed, use the properties it had.
828 db.Add<bool>("ui." + config_name + ".initialized", UserStringNop("OPTIONS_DB_UI_WINDOWS_EXISTS"), false, Validator<bool>(), false);
829 new_name = config_name;
830 }
831 } else if (!config_name.empty()) {
832 const int max_width_plus_one = HumanClientApp::MaximumPossibleWidth() + 1;
833 const int max_height_plus_one = HumanClientApp::MaximumPossibleHeight() + 1;
834
835 db.Add<bool>("ui." + config_name + ".initialized", UserStringNop("OPTIONS_DB_UI_WINDOWS_EXISTS"), false, Validator<bool>(), false);
836
837 db.Add<int> ("ui." + config_name + ".fullscreen.left", UserStringNop("OPTIONS_DB_UI_WINDOWS_LEFT"), left, OrValidator<int>(RangedValidator<int>(0, max_width_plus_one), DiscreteValidator<int>(INVALID_POS)));
838 db.Add<int> ("ui." + config_name + ".fullscreen.top", UserStringNop("OPTIONS_DB_UI_WINDOWS_TOP"), top, OrValidator<int>(RangedValidator<int>(0, max_height_plus_one), DiscreteValidator<int>(INVALID_POS)));
839 db.Add<int> ("ui." + config_name + ".windowed.left", UserStringNop("OPTIONS_DB_UI_WINDOWS_LEFT_WINDOWED"), left, OrValidator<int>(RangedValidator<int>(0, max_width_plus_one), DiscreteValidator<int>(INVALID_POS)));
840 db.Add<int> ("ui." + config_name + ".windowed.top", UserStringNop("OPTIONS_DB_UI_WINDOWS_TOP_WINDOWED"), top, OrValidator<int>(RangedValidator<int>(0, max_height_plus_one), DiscreteValidator<int>(INVALID_POS)));
841
842 db.Add<int> ("ui." + config_name + ".fullscreen.width", UserStringNop("OPTIONS_DB_UI_WINDOWS_WIDTH"), width, RangedValidator<int>(0, max_width_plus_one));
843 db.Add<int> ("ui." + config_name + ".fullscreen.height", UserStringNop("OPTIONS_DB_UI_WINDOWS_HEIGHT"), height, RangedValidator<int>(0, max_height_plus_one));
844 db.Add<int> ("ui." + config_name + ".windowed.width", UserStringNop("OPTIONS_DB_UI_WINDOWS_WIDTH_WINDOWED"), width, RangedValidator<int>(0, max_width_plus_one));
845 db.Add<int> ("ui." + config_name + ".windowed.height", UserStringNop("OPTIONS_DB_UI_WINDOWS_HEIGHT_WINDOWED"), height, RangedValidator<int>(0, max_height_plus_one));
846
847 db.Add<bool>("ui." + config_name + ".visible", UserStringNop("OPTIONS_DB_UI_WINDOWS_VISIBLE"), visible, Validator<bool>());
848 db.Add<bool>("ui." + config_name + ".pinned", UserStringNop("OPTIONS_DB_UI_WINDOWS_PINNED"), pinned, Validator<bool>());
849 db.Add<bool>("ui." + config_name + ".minimized", UserStringNop("OPTIONS_DB_UI_WINDOWS_MINIMIZED"), minimized, Validator<bool>());
850
851 new_name = config_name;
852 }
853
854 return new_name;
855 }
856
AddWindowOptions(const std::string & config_name,GG::X left,GG::Y top,GG::X width,GG::Y height,bool visible,bool pinned,bool minimized)857 const std::string CUIWnd::AddWindowOptions(const std::string& config_name,
858 GG::X left, GG::Y top,
859 GG::X width, GG::Y height,
860 bool visible, bool pinned, bool minimized)
861 {
862 return AddWindowOptions(config_name,
863 Value(left), Value(top),
864 Value(width), Value(height),
865 visible, pinned, minimized);
866 }
867
InvalidateWindowOptions(const std::string & config_name)868 void CUIWnd::InvalidateWindowOptions(const std::string& config_name) {
869 OptionsDB& db = GetOptionsDB();
870 std::string window_mode = db.Get<bool>("video.fullscreen.enabled") ? ".fullscreen" : ".windowed";
871 std::string edge_option_prefix = "ui." + config_name + window_mode;
872
873 if (db.OptionExists("ui." + config_name + ".initialized")) {
874 // Should be removed in window dtor.
875 ErrorLogger() << "CUIWnd::RemoveWindowOptions() : attempted to remove window options using name \"" << config_name << "\" but they appear to be in use by a window.";
876 return;
877 } else if (!db.OptionExists(edge_option_prefix + ".left")) {
878 ErrorLogger() << "CUIWnd::RemoveWindowOptions() : attempted to remove window options using name \"" << config_name << "\" but the options do not appear to be registered in the OptionsDB.";
879 return;
880 }
881
882 db.Set<int>(edge_option_prefix + ".left", INVALID_POS);
883 db.Set<int>(edge_option_prefix + ".top", INVALID_POS);
884 db.Set<bool>("ui." + config_name + ".visible", db.GetDefault<bool>("ui." + config_name + ".visible"));
885 db.Set<bool>("ui." + config_name + ".pinned", db.GetDefault<bool>("ui." + config_name + ".pinned"));
886 db.Set<bool>("ui." + config_name + ".minimized", db.GetDefault<bool>("ui." + config_name + ".minimized"));
887 }
888
InvalidateUnusedOptions()889 void CUIWnd::InvalidateUnusedOptions() {
890 OptionsDB& db = GetOptionsDB();
891 std::string prefix("ui.");
892 std::string suffix_exist(".left");
893
894 // Remove unrecognized options from the DB so that their values aren't
895 // applied when they are eventually registered.
896 db.RemoveUnrecognized(prefix);
897
898 // Removed registered options for windows that aren't currently
899 // instantiated so they fall back on defaults when they are re-constructed.
900 std::set<std::string> window_options;
901 db.FindOptions(window_options, prefix);
902 for (const std::string& option : window_options) {
903 if (!boost::algorithm::find_last(option, suffix_exist))
904 continue;
905 // If the ".left" option is registered, the rest are implied to be there.
906 if ((option.rfind(suffix_exist) == (option.length() - suffix_exist.length())) && db.OptionExists(option)) {
907 auto window_name = WindowNameFromOption(option);
908 // If the ".initialized" option isn't present under this name, remove the options.
909 if (!window_name.empty() && !window_options.count(prefix + window_name + ".initialized"))
910 InvalidateWindowOptions(window_name);
911 }
912 }
913
914 db.Commit();
915 }
916
SetParent(const std::shared_ptr<GG::Wnd> & wnd)917 void CUIWnd::SetParent(const std::shared_ptr<GG::Wnd>& wnd) {
918 GG::Wnd::SetParent(wnd);
919 m_vertex_buffer.clear(); // force buffer re-init on next Render call, so background is properly positioned for new parent-relative position
920 }
921
922 ///////////////////////////////////////
923 // class CUIEditWnd
924 ///////////////////////////////////////
925 const GG::X CUIEditWnd::BUTTON_WIDTH(75);
926 const int CUIEditWnd::CONTROL_MARGIN = 5;
927
CUIEditWnd(GG::X w,const std::string & prompt_text,const std::string & edit_text,GG::Flags<GG::WndFlag> flags)928 CUIEditWnd::CUIEditWnd(GG::X w, const std::string& prompt_text, const std::string& edit_text, GG::Flags<GG::WndFlag> flags/* = Wnd::MODAL*/) :
929 CUIWnd(prompt_text, GG::X0, GG::Y0, w, GG::Y1, flags)
930 {
931 m_edit = GG::Wnd::Create<CUIEdit>(edit_text);
932 }
933
CompleteConstruction()934 void CUIEditWnd::CompleteConstruction() {
935 CUIWnd::CompleteConstruction();
936
937 m_ok_bn = GG::Wnd::Create<CUIButton>(UserString("OK"));
938 m_cancel_bn = GG::Wnd::Create<CUIButton>(UserString("CANCEL"));
939
940 m_edit->MoveTo(GG::Pt(LeftBorder() + 3, TopBorder() + 3));
941 m_edit->Resize(GG::Pt(ClientWidth() - 2 * BUTTON_WIDTH - 2 * CONTROL_MARGIN - 6 - LeftBorder() - RightBorder(), m_edit->MinUsableSize().y));
942
943 m_ok_bn->MoveTo(GG::Pt(m_edit->Right() + CONTROL_MARGIN, TopBorder() + 3));
944 m_ok_bn->Resize(GG::Pt(BUTTON_WIDTH, m_ok_bn->MinUsableSize().y));
945 m_ok_bn->OffsetMove(GG::Pt(GG::X0, (m_edit->Height() - m_ok_bn->Height()) / 2));
946
947 m_cancel_bn->MoveTo(GG::Pt(m_ok_bn->Right() + CONTROL_MARGIN, TopBorder() + 3));
948 m_cancel_bn->Resize(GG::Pt(BUTTON_WIDTH, m_cancel_bn->MinUsableSize().y));
949 m_cancel_bn->OffsetMove(GG::Pt(GG::X0, (m_edit->Height() - m_ok_bn->Height()) / 2));
950
951 Resize(GG::Pt(Width(), std::max(m_edit->Bottom(), m_cancel_bn->Bottom()) + BottomBorder() + 3));
952 MoveTo(GG::Pt((GG::GUI::GetGUI()->AppWidth() - Width()) / 2, (GG::GUI::GetGUI()->AppHeight() - Height()) / 2));
953
954 AttachChild(m_edit);
955 AttachChild(m_ok_bn);
956 AttachChild(m_cancel_bn);
957
958 m_ok_bn->LeftClickedSignal.connect(boost::bind(&CUIEditWnd::OkClicked, this));
959 m_cancel_bn->LeftClickedSignal.connect(boost::bind(&CUIWnd::CloseClicked, static_cast<CUIWnd*>(this)));
960
961 m_edit->SelectAll();
962 }
963
ModalInit()964 void CUIEditWnd::ModalInit()
965 { GG::GUI::GetGUI()->SetFocusWnd(m_edit); }
966
KeyPress(GG::Key key,std::uint32_t key_code_point,GG::Flags<GG::ModKey> mod_keys)967 void CUIEditWnd::KeyPress(GG::Key key, std::uint32_t key_code_point, GG::Flags<GG::ModKey> mod_keys) {
968 switch (key) {
969 case GG::GGK_RETURN: if (!m_ok_bn->Disabled()) OkClicked(); break;
970 case GG::GGK_ESCAPE: CloseClicked(); break;
971 default: break;
972 }
973 }
974
Result() const975 const std::string& CUIEditWnd::Result() const
976 { return m_result; }
977
OkClicked()978 void CUIEditWnd::OkClicked() {
979 m_result = m_edit->Text();
980 CloseClicked();
981 }
982