GG: Spin.h Source File

GG

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

Spin.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 /* GG is a GUI for SDL and OpenGL.
3 Copyright (C) 2003-2008 T. Zachary Laine
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public License
7 as published by the Free Software Foundation; either version 2.1
8 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA
19
20 If you do not wish to comply with the terms of the LGPL please
21 contact the author as other terms are available for a fee.
22
23 Zach Laine
24 whatwasthataddress@gmail.com */
25
30 #ifndef _GG_Spin_h_
31 #define _GG_Spin_h_
32
33 #include <GG/Button.h>
34 #include <GG/DrawUtil.h>
35 #include <GG/Edit.h>
36 #include <GG/GUI.h>
37 #include <GG/StyleFactory.h>
38 #include <GG/WndEvent.h>
39
40 #include <cmath>
41 #include <limits>
42
43
44 namespace GG {
45
46 // forward declaration of helper functions and classes
47 namespace spin_details {
48 template <class T> T mod(T, T);
49 template <class T> T div(T, T);
50 }
51
52
72 template <class T>
73 class Spin : public Control
74 {
75 public:
77 typedef typename boost::signal<void (T)> ValueChangedSignalType;
78
79
81
83 Spin(X x, Y y, X w, T value, T step, T min, T max, bool edits, const boost::shared_ptr<Font>& font, Clr color,
84 Clr text_color = CLR_BLACK, Clr interior = CLR_ZERO, Flags<WndFlag> flags = INTERACTIVE);
85
86 ~Spin(); // dtor
88
90 virtual Pt MinUsableSize() const;
91
92 T Value() const;
93 T StepSize() const;
94 T MinValue() const;
95 T MaxValue() const;
96 bool Editable() const;
97
98 X ButtonWidth() const;
99
100 Clr TextColor() const;
101 Clr InteriorColor() const;
102 Clr HiliteColor() const;
103 Clr SelectedTextColor() const;
104
105 mutable ValueChangedSignalType ValueChangedSignal;
106
107
109 virtual void Render();
110
111 virtual void SizeMove(const Pt& ul, const Pt& lr);
112
113 virtual void Disable(bool b = true);
114 virtual void SetColor(Clr c);
115
116 void Incr();
117 void Decr();
118
120 void SetValue(T value);
121
122 void SetStepSize(T step);
123 void SetMinValue(T value);
124 void SetMaxValue(T value);
125
127 void AllowEdits(bool b = true);
128
129 void SetButtonWidth(X width);
130
131 void SetTextColor(Clr c);
132 void SetInteriorColor(Clr c);
133 void SetHiliteColor(Clr c);
134 void SetSelectedTextColor(Clr c);
135
136
137 protected:
138 typedef T ValueType;
139
140 enum {BORDER_THICK = 2, PIXEL_MARGIN = 5};
141
143 Spin();
144
145
147 Button* UpButton() const;
148 Button* DownButton() const;
149 Edit* GetEdit() const;
150
151
153 virtual void KeyPress(Key key, boost::uint32_t key_code_point, Flags<ModKey> mod_keys);
154 virtual void MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys);
155
156 virtual bool EventFilter(Wnd* w, const WndEvent& event);
158
159 private:
160 void ConnectSignals();
161 void Init(const boost::shared_ptr<Font>& font, Clr color, Clr text_color, Clr interior, Flags<WndFlag> flags);
162 void ValueUpdated(const std::string& val_text);
163 void IncrImpl(bool signal);
164 void DecrImpl(bool signal);
165 void SetValueImpl(T value, bool signal);
166
167 T m_value;
168 T m_step_size;
169 T m_min_value;
170 T m_max_value;
171
172 bool m_editable;
173
174 Edit* m_edit;
175 Button* m_up_button;
176 Button* m_down_button;
177
178 X m_button_width;
179
180 static void ValueChangedEcho(const T& value);
181 };
182
183
184 // template implementations
185 template<class T>
186 Spin<T>::Spin() :
187 Control(),
188 m_value(),
189 m_step_size(),
190 m_min_value(),
191 m_max_value(),
192 m_editable(false),
193 m_edit(0),
194 m_up_button(0),
195 m_down_button(0),
196 m_button_width(15)
197 {}
198
199 template<class T>
200 Spin<T>::Spin(X x, Y y, X w, T value, T step, T min, T max, bool edits, const boost::shared_ptr<Font>& font, Clr color,
201 Clr text_color/* = CLR_BLACK*/, Clr interior/* = CLR_ZERO*/, Flags<WndFlag> flags/* = INTERACTIVE*/) :
202 Control(x, y, w, font->Height() + 2 * PIXEL_MARGIN, flags),
203 m_value(value),
204 m_step_size(step),
205 m_min_value(min),
206 m_max_value(max),
207 m_editable(edits),
208 m_edit(0),
209 m_up_button(0),
210 m_down_button(0),
211 m_button_width(15)
212 {
213 Init(font, color, text_color, interior, flags);
214
215 if (INSTRUMENT_ALL_SIGNALS)
216 Connect(ValueChangedSignal, &ValueChangedEcho);
217 }
218
219 template<class T>
220 Spin<T>::~Spin()
221 {}
222
223 template<class T>
224 Pt Spin<T>::MinUsableSize() const
225 {
226 Pt edit_min = m_edit->MinUsableSize();
227 Pt up_min = m_up_button->MinUsableSize();
228 Pt down_min = m_down_button->MinUsableSize();
229 return Pt(edit_min.x + std::max(up_min.x, down_min.x) + 2 * BORDER_THICK,
230 std::max(up_min.y + down_min.y, edit_min.y) + 2 * BORDER_THICK);
231 }
232
233 template<class T>
234 T Spin<T>::Value() const
235 { return m_value; }
236
237 template<class T>
238 T Spin<T>::StepSize() const
239 { return m_step_size; }
240
241 template<class T>
242 T Spin<T>::MinValue() const
243 { return m_min_value; }
244
245 template<class T>
246 T Spin<T>::MaxValue() const
247 { return m_max_value; }
248
249 template<class T>
250 bool Spin<T>::Editable() const
251 { return m_editable; }
252
253 template<class T>
254 X Spin<T>::ButtonWidth() const
255 { return m_button_width; }
256
257 template<class T>
258 Clr Spin<T>::TextColor() const
259 { return m_edit->TextColor(); }
260
261 template<class T>
262 Clr Spin<T>::InteriorColor() const
263 { return m_edit->InteriorColor(); }
264
265 template<class T>
266 Clr Spin<T>::HiliteColor() const
267 { return m_edit->HiliteColor(); }
268
269 template<class T>
270 Clr Spin<T>::SelectedTextColor() const
271 { return m_edit->SelectedTextColor(); }
272
273 template<class T>
274 void Spin<T>::Render()
275 {
276 Clr color_to_use = Disabled() ? DisabledColor(Color()) : Color();
277 Clr int_color_to_use = Disabled() ? DisabledColor(InteriorColor()) : InteriorColor();
278 Pt ul = UpperLeft(), lr = LowerRight();
279 BeveledRectangle(ul, lr, int_color_to_use, color_to_use, false, BORDER_THICK);
280 }
281
282 template<class T>
283 void Spin<T>::SizeMove(const Pt& ul, const Pt& lr)
284 {
285 Wnd::SizeMove(ul, lr);
286 const X BUTTON_X_POS = Width() - m_button_width - BORDER_THICK;
287 const Y BUTTONS_HEIGHT = Height() - 2 * BORDER_THICK; // height of *both* buttons
288 m_edit->SizeMove(Pt(), Pt(Width() - m_button_width, Height()));
289 m_up_button->SizeMove(Pt(BUTTON_X_POS, Y(BORDER_THICK)),
290 Pt(BUTTON_X_POS + m_button_width, BORDER_THICK + BUTTONS_HEIGHT / 2));
291 m_down_button->SizeMove(Pt(BUTTON_X_POS, BORDER_THICK + BUTTONS_HEIGHT / 2),
292 Pt(BUTTON_X_POS + m_button_width, BORDER_THICK + BUTTONS_HEIGHT));
293 }
294
295 template<class T>
296 void Spin<T>::Disable(bool b/* = true*/)
297 {
298 Control::Disable(b);
299 m_edit->Disable(b);
300 m_up_button->Disable(b);
301 m_down_button->Disable(b);
302 }
303
304 template<class T>
305 void Spin<T>::SetColor(Clr c)
306 {
307 Control::SetColor(c);
308 m_up_button->SetColor(c);
309 m_down_button->SetColor(c);
310 }
311
312 template<class T>
313 void Spin<T>::Incr()
314 { SetValueImpl(m_value + m_step_size, false); }
315
316 template<class T>
317 void Spin<T>::Decr()
318 { SetValueImpl(m_value - m_step_size, false); }
319
320 template<class T>
321 void Spin<T>::SetValue(T value)
322 { SetValueImpl(value, false); }
323
324 template<class T>
325 void Spin<T>::SetStepSize(T step)
326 {
327 m_step_size = step;
328 SetValue(m_value);
329 }
330
331 template<class T>
332 void Spin<T>::SetMinValue(T value)
333 {
334 m_min_value = value;
335 if (m_value < m_min_value)
336 SetValue(m_min_value);
337 }
338
339 template<class T>
340 void Spin<T>::SetMaxValue(T value)
341 {
342 m_max_value = value;
343 if (m_max_value < m_value)
344 SetValue(m_max_value);
345 }
346
347 template<class T>
348 void Spin<T>::SetTextColor(Clr c)
349 { m_edit->SetTextColor(c); }
350
351 template<class T>
352 void Spin<T>::SetButtonWidth(X width)
353 {
354 if (1 <= width) {
355 if (Width() - 2 * BORDER_THICK - 1 < width)
356 width = Width() - 2 * BORDER_THICK - 1;
357 m_button_width = width;
358 SizeMove(RelativeUpperLeft(), RelativeLowerRight());
359 }
360 }
361
362 template<class T>
363 void Spin<T>::SetInteriorColor(Clr c)
364 { m_edit->SetInteriorColor(c); }
365
366 template<class T>
367 void Spin<T>::SetHiliteColor(Clr c)
368 { m_edit->SetHiliteColor(c); }
369
370 template<class T>
371 void Spin<T>::SetSelectedTextColor(Clr c)
372 { m_edit->SetSelectedTextColor(c); }
373
374 template<class T>
375 Button* Spin<T>::UpButton() const
376 { return m_up_button; }
377
378 template<class T>
379 Button* Spin<T>::DownButton() const
380 { return m_down_button; }
381
382 template<class T>
383 Edit* Spin<T>::GetEdit() const
384 { return m_edit; }
385
386 template<class T>
387 void Spin<T>::KeyPress(Key key, boost::uint32_t key_code_point, Flags<ModKey> mod_keys)
388 {
389 switch (key) {
390 case GGK_HOME:
391 SetValueImpl(m_min_value, true);
392 break;
393 case GGK_END:
394 SetValueImpl(m_max_value, true);
395 break;
396 case GGK_PAGEUP:
397 case GGK_UP:
398 case GGK_PLUS:
399 case GGK_KP_PLUS:
400 IncrImpl(true);
401 break;
402 case GGK_PAGEDOWN:
403 case GGK_DOWN:
404 case GGK_MINUS:
405 case GGK_KP_MINUS:
406 DecrImpl(true);
407 break;
408 default:
409 break;
410 }
411 }
412
413 template<class T>
414 void Spin<T>::MouseWheel(const Pt& pt, int move, Flags<ModKey> mod_keys)
415 {
416 for (int i = 0; i < move; ++i) {
417 IncrImpl(true);
418 }
419 for (int i = 0; i < -move; ++i) {
420 DecrImpl(true);
421 }
422 }
423
424 template<class T>
425 bool Spin<T>::EventFilter(Wnd* w, const WndEvent& event)
426 {
427 if (w == m_edit) {
428 if (!m_editable && event.Type() == WndEvent::GainingFocus) {
429 GUI::GetGUI()->SetFocusWnd(this);
430 return true;
431 } else {
432 return !m_editable;
433 }
434 }
435 return false;
436 }
437
438 template<class T>
439 void Spin<T>::ConnectSignals()
440 {
441 Connect(m_edit->FocusUpdateSignal, &Spin::ValueUpdated, this);
442 Connect(m_up_button->ClickedSignal, boost::bind(&Spin::IncrImpl, this, true));
443 Connect(m_down_button->ClickedSignal, boost::bind(&Spin::DecrImpl, this, true));
444 }
445
446 template<class T>
447 void Spin<T>::Init(const boost::shared_ptr<Font>& font, Clr color, Clr text_color, Clr interior, Flags<WndFlag> flags)
448 {
449 boost::shared_ptr<StyleFactory> style = GetStyleFactory();
450 Control::SetColor(color);
451 m_edit = style->NewSpinEdit(X0, Y0, X1, boost::lexical_cast<std::string>(m_value), font, CLR_ZERO, text_color, interior);
452 boost::shared_ptr<Font> small_font = GUI::GetGUI()->GetFont(font, static_cast<int>(font->PointSize() * 0.75));
453 m_up_button = style->NewSpinIncrButton(X0, Y0, X1, Y1, "+", small_font, color);
454 m_down_button = style->NewSpinDecrButton(X0, Y0, X1, Y1, "-", small_font, color);
455 m_edit->InstallEventFilter(this);
456 m_up_button->InstallEventFilter(this);
457 m_down_button->InstallEventFilter(this);
458 AttachChild(m_edit);
459 AttachChild(m_up_button);
460 AttachChild(m_down_button);
461 ConnectSignals();
462 SizeMove(UpperLeft(), LowerRight());
463 }
464
465 template<class T>
466 void Spin<T>::ValueUpdated(const std::string& val_text)
467 {
468 T value;
469 try {
470 value = boost::lexical_cast<T>(val_text);
471 } catch (boost::bad_lexical_cast) {
472 SetValueImpl(m_min_value, true);
473 return;
474 }
475 SetValueImpl(value, true);
476 }
477
478 template<class T>
479 void Spin<T>::IncrImpl(bool signal)
480 { SetValueImpl(static_cast<T>(m_value + m_step_size), signal); }
481
482 template<class T>
483 void Spin<T>::DecrImpl(bool signal)
484 { SetValueImpl(static_cast<T>(m_value - m_step_size), signal); }
485
486 template<class T>
487 void Spin<T>::SetValueImpl(T value, bool signal)
488 {
489 T old_value = m_value;
490 if (value < m_min_value) {
491 m_value = m_min_value;
492 } else if (m_max_value < value) {
493 m_value = m_max_value;
494 } else {
495 // if the value supplied does not equal a valid value
496 if (std::abs(spin_details::mod(static_cast<T>(value - m_min_value), m_step_size)) >
497 std::numeric_limits<T>::epsilon()) {
498 // find nearest valid value to the one supplied
499 T closest_below =
500 static_cast<T>(
501 spin_details::div(static_cast<T>(value - m_min_value), m_step_size) *
502 static_cast<T>(m_step_size + m_min_value));
503 T closest_above =
504 static_cast<T>(closest_below + m_step_size);
505 m_value =
506 ((value - closest_below) < (closest_above - value) ?
507 closest_below : closest_above);
508 } else {
509 m_value = value;
510 }
511 }
512 *m_edit << m_value;
513 if (signal && m_value != old_value)
514 ValueChangedSignal(m_value);
515 }
516
517 template <class T>
518 void Spin<T>::ValueChangedEcho(const T& value)
519 { std::cerr << "GG SIGNAL : Spin<>::ValueChangedSignal(value=" << value << ")\n"; }
520
521
522 namespace spin_details {
523 // provides a typesafe mod function
524 template <class T> inline
525 T mod (T dividend, T divisor) {return static_cast<T>(dividend % divisor);}
526
527 // template specializations
528 template <> inline
529 float mod<float> (float dividend, float divisor) {return std::fmod(dividend, divisor);}
530 template <> inline
531 double mod<double> (double dividend, double divisor) {return std::fmod(dividend, divisor);}
532 template <> inline
533 long double mod<long double> (long double dividend, long double divisor) {return std::fmod(dividend, divisor);}
534
535 // provides a typesafe div function
536 template <class T> inline
537 T div (T dividend, T divisor) {return static_cast<T>(dividend / divisor);}
538
539 // template specializations
540 template <> inline
541 float div<float> (float dividend, float divisor) {return std::floor(dividend / divisor);}
542 template <> inline
543 double div<double> (double dividend, double divisor) {return std::floor(dividend / divisor);}
544 template <> inline
545 long double div<long double> (long double dividend, long double divisor) {return std::floor(dividend / divisor);}
546 } // namespace spin_details
547
548 } // namespace GG
549
550 #endif
551