GG: Slider.h Source File

GG

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

- File List
- File Members

Slider.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_Slider_h_
31 #define _GG_Slider_h_
32
33 #include <GG/Button.h>
34 #include <GG/DrawUtil.h>
35 #include <GG/StyleFactory.h>
36 #include <GG/WndEvent.h>
37
38
39 namespace GG {
40
41 class Button;
42 class WndEvent;
43
56 template <class T>
57 class Slider : public Control
58 {
59 public:
61 typedef boost::signal<void (T, T, T)> SlidSignalType;
62 typedef boost::signal<void (T, T, T)> SlidAndStoppedSignalType;
63
64
66 Slider(X x, Y y, X w, Y h, T min, T max,
67 Orientation orientation, SliderLineStyle style, Clr color,
68 unsigned int tab_width, unsigned int line_width = 5, Flags<WndFlag> flags = INTERACTIVE);
69
70
72 virtual Pt MinUsableSize() const;
73
74 T Posn() const;
75 std::pair<T, T> SliderRange() const;
76
80 T PageSize() const;
81
82 Orientation GetOrientation() const;
83 unsigned int TabWidth() const;
84 unsigned int LineWidth() const;
85 SliderLineStyle LineStyle() const;
86
87 mutable SlidSignalType SlidSignal;
88 mutable SlidAndStoppedSignalType SlidAndStoppedSignal;
89
90
92 virtual void Render();
93 virtual void SizeMove(const Pt& ul, const Pt& lr);
94 virtual void Disable(bool b = true);
95 virtual void SetColor(Clr c);
96
97 void SizeSlider(T min, T max);
98 void SetMax(T max);
99 void SetMin(T min);
100 void SlideTo(T p);
101
106 void SetPageSize(T size);
107
108 void SetLineStyle(SliderLineStyle style);
109
110
111 static const T INVALID_PAGE_SIZE;
112
113 protected:
115 Slider();
116
117
119 Button* Tab() const;
120 T PtToPosn(const Pt& pt) const;
121
122
124 virtual void LClick(const Pt& pt, Flags<ModKey> mod_keys);
125 virtual void KeyPress(Key key, boost::uint32_t key_code_point, Flags<ModKey> mod_keys);
126
127 virtual bool EventFilter(Wnd* w, const WndEvent& event);
128
129 void MoveTabToPosn();
130
131
132 private:
133 void SlideToImpl(T p, bool signal);
134 void UpdatePosn();
135
136 struct SlidEcho
137 {
138 SlidEcho(const std::string& name);
139 void operator()(T pos, T min, T max);
140 std::string m_name;
141 };
142
143 T m_posn;
144 T m_range_min;
145 T m_range_max;
146 T m_page_sz;
147 Orientation m_orientation;
148 unsigned int m_line_width;
149 unsigned int m_tab_width;
150 SliderLineStyle m_line_style;
151 int m_tab_drag_offset;
152 Button* m_tab;
153 bool m_dragging_tab;
154 };
155
156 // template implementations
157 template <class T>
158 const T Slider<T>::INVALID_PAGE_SIZE = std::numeric_limits<T>::max();
159
160 template <class T>
161 Slider<T>::Slider() :
162 Control(),
163 m_posn(0),
164 m_range_min(T(0)),
165 m_range_max(T(99)),
166 m_page_sz(INVALID_PAGE_SIZE),
167 m_orientation(VERTICAL),
168 m_line_width(5),
169 m_tab_width(5),
170 m_line_style(RAISED),
171 m_tab_drag_offset(-1),
172 m_tab(0),
173 m_dragging_tab(false)
174 {}
175
176 template <class T>
177 Slider<T>::Slider(X x, Y y, X w, Y h, T min, T max, Orientation orientation, SliderLineStyle style,
178 Clr color, int unsigned tab_width, int unsigned line_width/* = 5*/,
179 Flags<WndFlag> flags/* = INTERACTIVE*/) :
180 Control(x, y, w, h, flags),
181 m_posn(min),
182 m_range_min(min),
183 m_range_max(max),
184 m_page_sz(INVALID_PAGE_SIZE),
185 m_orientation(orientation),
186 m_line_width(line_width),
187 m_tab_width(tab_width),
188 m_line_style(style),
189 m_tab_drag_offset(-1),
190 m_tab(m_orientation == VERTICAL ?
191 GetStyleFactory()->NewVSliderTabButton(X0, Y0, Width(), Y(m_tab_width), "", boost::shared_ptr<Font>(), color) :
192 GetStyleFactory()->NewHSliderTabButton(X0, Y0, X(m_tab_width), Height(), "", boost::shared_ptr<Font>(), color)),
193 m_dragging_tab(false)
194 {
195 Control::SetColor(color);
196 AttachChild(m_tab);
197 m_tab->InstallEventFilter(this);
198 SizeMove(UpperLeft(), LowerRight());
199
200 if (INSTRUMENT_ALL_SIGNALS) {
201 Connect(SlidSignal, SlidEcho("Slider<T>::SlidSignal"));
202 Connect(SlidAndStoppedSignal, SlidEcho("Slider<T>::SlidAndStoppedSignal"));
203 }
204 }
205
206 template <class T>
207 Pt Slider<T>::MinUsableSize() const
208 {
209 Pt tab_min = m_tab->MinUsableSize();
210 return Pt(m_orientation == VERTICAL ? tab_min.x : Size().x,
211 m_orientation == VERTICAL ? Size().y : tab_min.y);
212 }
213
214 template <class T>
215 T Slider<T>::Posn() const
216 { return m_posn; }
217
218 template <class T>
219 std::pair<T, T> Slider<T>::SliderRange() const
220 { return std::pair<T, T>(m_range_min, m_range_max); }
221
222 template <class T>
223 T Slider<T>::PageSize() const
224 { return m_page_sz != INVALID_PAGE_SIZE ? m_page_sz : (m_range_max - m_range_min) / 10; }
225
226 template <class T>
227 Orientation Slider<T>::GetOrientation() const
228 { return m_orientation; }
229
230 template <class T>
231 unsigned int Slider<T>::TabWidth() const
232 { return m_tab_width; }
233
234 template <class T>
235 unsigned int Slider<T>::LineWidth() const
236 { return m_line_width; }
237
238 template <class T>
239 SliderLineStyle Slider<T>::LineStyle() const
240 { return m_line_style; }
241
242 template <class T>
243 void Slider<T>::Render()
244 {
245 const Pt UL = UpperLeft();
246 const Pt LR = LowerRight();
247 Clr color_to_use = Disabled() ? DisabledColor(Color()) : Color();
248 int tab_width = m_orientation == VERTICAL ? Value(m_tab->Height()) : Value(m_tab->Width());
249 Pt ul, lr;
250 if (m_orientation == VERTICAL) {
251 ul.x = ((LR.x + UL.x) - static_cast<int>(m_line_width)) / 2;
252 lr.x = ul.x + static_cast<int>(m_line_width);
253 ul.y = UL.y + tab_width / 2;
254 lr.y = LR.y - tab_width / 2;
255 } else {
256 ul.x = UL.x + tab_width / 2;
257 lr.x = LR.x - tab_width / 2;
258 ul.y = ((LR.y + UL.y) - static_cast<int>(m_line_width)) / 2;
259 lr.y = ul.y + static_cast<int>(m_line_width);
260 }
261 switch (m_line_style) {
262 case FLAT:
263 FlatRectangle(ul, lr, color_to_use, CLR_BLACK, 1);
264 break;
265 case RAISED:
266 BeveledRectangle(ul, lr, color_to_use, color_to_use, true, m_line_width / 2);
267 break;
268 case GROOVED:
269 BeveledRectangle(ul, lr, color_to_use, color_to_use, false, m_line_width / 2);
270 break;
271 }
272 }
273
274 template <class T>
275 void Slider<T>::SizeMove(const Pt& ul, const Pt& lr)
276 {
277 Wnd::SizeMove(ul, lr);
278 if (m_orientation == VERTICAL)
279 m_tab->SizeMove(Pt(), Pt(lr.x - ul.x, Y(m_tab_width)));
280 else
281 m_tab->SizeMove(Pt(), Pt(X(m_tab_width), lr.y - ul.y));
282 MoveTabToPosn();
283 }
284
285 template <class T>
286 void Slider<T>::Disable(bool b/* = true*/)
287 {
288 Control::Disable(b);
289 m_tab->Disable(b);
290 }
291
292 template <class T>
293 void Slider<T>::SetColor(Clr c)
294 {
295 Control::SetColor(c);
296 m_tab->SetColor(c);
297 }
298
299 template <class T>
300 void Slider<T>::SizeSlider(T min, T max)
301 {
302 assert(m_range_min != m_range_max);
303 m_range_min = min;
304 m_range_max = max;
305 if (m_posn < m_range_min)
306 SlideToImpl(m_range_min, false);
307 else if (m_range_max < m_posn)
308 SlideToImpl(m_range_max, false);
309 else
310 MoveTabToPosn();
311 }
312
313 template <class T>
314 void Slider<T>::SetMax(T max)
315 { SizeSlider(m_range_min, max); }
316
317 template <class T>
318 void Slider<T>::SetMin(T min)
319 { SizeSlider(min, m_range_max); }
320
321 template <class T>
322 void Slider<T>::SlideTo(T p)
323 { SlideToImpl(p, false); }
324
325 template <class T>
326 void Slider<T>::SetPageSize(T size)
327 { m_page_sz = size; }
328
329 template <class T>
330 void Slider<T>::SetLineStyle(SliderLineStyle style)
331 { m_line_style = style; }
332
333 template <class T>
334 Button* Slider<T>::Tab() const
335 { return m_tab; }
336
337 template <class T>
338 T Slider<T>::PtToPosn(const Pt& pt) const
339 {
340 Pt ul = UpperLeft(), lr = LowerRight();
341 int line_min = 0;
342 int line_max = 0;
343 int pixel_nearest_to_pt_on_line = 0;
344 if (m_orientation == VERTICAL) {
345 line_min = Value(m_tab->Height() / 2);
346 line_max = Value(Height() - (m_tab->Height() - m_tab->Height() / 2));
347 pixel_nearest_to_pt_on_line = std::max(line_min, std::min(Value(lr.y - pt.y), line_max));
348 } else {
349 line_min = Value(m_tab->Width() / 2);
350 line_max = Value(Width() - (m_tab->Width() - m_tab->Width() / 2));
351 pixel_nearest_to_pt_on_line = std::max(line_min, std::min(Value(pt.x - ul.x), line_max));
352 }
353 double fractional_distance = static_cast<double>(pixel_nearest_to_pt_on_line) / (line_max - line_min);
354 return m_range_min + static_cast<T>((m_range_max - m_range_min) * fractional_distance);
355 }
356
357 template <class T>
358 void Slider<T>::LClick(const Pt& pt, Flags<ModKey> mod_keys)
359 { SlideToImpl(m_posn < PtToPosn(pt) ? m_posn + PageSize() : m_posn - PageSize(), true); }
360
361 template <class T>
362 void Slider<T>::KeyPress(Key key, boost::uint32_t key_code_point, Flags<ModKey> mod_keys)
363 {
364 if (!Disabled()) {
365 switch (key) {
366 case GGK_HOME:
367 SlideToImpl(m_range_min, true);
368 break;
369 case GGK_END:
370 SlideToImpl(m_range_max, true);
371 break;
372 case GGK_UP:
373 if (m_orientation != HORIZONTAL)
374 SlideToImpl(m_posn + (0 < (m_range_max - m_range_min) ? 1 : -1), true);
375 break;
376 case GGK_RIGHT:
377 if (m_orientation != VERTICAL)
378 SlideToImpl(m_posn + (0 < (m_range_max - m_range_min) ? 1 : -1), true);
379 break;
380 case GGK_DOWN:
381 if (m_orientation != HORIZONTAL)
382 SlideToImpl(m_posn - (0 < (m_range_max - m_range_min) ? 1 : -1), true);
383 break;
384 case GGK_LEFT:
385 if (m_orientation != VERTICAL)
386 SlideToImpl(m_posn - (0 < (m_range_max - m_range_min) ? 1 : -1), true);
387 break;
388 case GGK_PLUS:
389 case GGK_KP_PLUS:
390 SlideToImpl(m_posn + 1, true);
391 break;
392 case GGK_MINUS:
393 case GGK_KP_MINUS:
394 SlideToImpl(m_posn - 1, true);
395 break;
396 default:
397 Control::KeyPress(key, key_code_point, mod_keys);
398 break;
399 }
400 } else {
401 Control::KeyPress(key, key_code_point, mod_keys);
402 }
403 }
404
405 template <class T>
406 bool Slider<T>::EventFilter(Wnd* w, const WndEvent& event)
407 {
408 if (w == m_tab) {
409 switch (event.Type()) {
410 case WndEvent::LDrag: {
411 if (!Disabled()) {
412 Pt new_ul = m_tab->RelativeUpperLeft() + event.DragMove();
413 if (m_orientation == VERTICAL) {
414 new_ul.x = m_tab->RelativeUpperLeft().x;
415 new_ul.y = std::max(Y0, std::min(new_ul.y, ClientHeight() - m_tab->Height()));
416 } else {
417 new_ul.x = std::max(X0, std::min(new_ul.x, ClientWidth() - m_tab->Width()));
418 new_ul.y = m_tab->RelativeUpperLeft().y;
419 }
420 m_tab->MoveTo(new_ul);
421 UpdatePosn();
422 }
423 return true;
424 }
425 case WndEvent::LButtonDown:
426 m_dragging_tab = true;
427 break;
428 case WndEvent::LButtonUp:
429 case WndEvent::LClick: {
430 if (!Disabled())
431 SlidAndStoppedSignal(m_posn, m_range_min, m_range_max);
432 m_dragging_tab = false;
433 break;
434 }
435 case WndEvent::MouseLeave:
436 return m_dragging_tab;
437 default:
438 break;
439 }
440 }
441 return false;
442 }
443
444 template <class T>
445 void Slider<T>::MoveTabToPosn()
446 {
447 assert(m_range_min <= m_posn && m_posn <= m_range_max ||
448 m_range_max <= m_posn && m_posn <= m_range_min);
449 double fractional_distance = static_cast<double>(m_posn - m_range_min) / (m_range_max - m_range_min);
450 int tab_width = m_orientation == VERTICAL ? Value(m_tab->Height()) : Value(m_tab->Width());
451 int line_length = (m_orientation == VERTICAL ? Value(Height()) : Value(Width())) - tab_width;
452 int pixel_distance = static_cast<int>(line_length * fractional_distance);
453 if (m_orientation == VERTICAL)
454 m_tab->MoveTo(Pt(m_tab->RelativeUpperLeft().x, Height() - tab_width - pixel_distance));
455 else
456 m_tab->MoveTo(Pt(X(pixel_distance), m_tab->RelativeUpperLeft().y));
457 }
458
459 template <class T>
460 void Slider<T>::UpdatePosn()
461 {
462 T old_posn = m_posn;
463 int line_length = m_orientation == VERTICAL ? Value(Height() - m_tab->Height()) : Value(Width() - m_tab->Width());
464 int tab_posn = (m_orientation == VERTICAL ? Value(Height() - m_tab->RelativeLowerRight().y) : Value(m_tab->RelativeUpperLeft().x));
465 double fractional_distance = static_cast<double>(tab_posn) / line_length;
466 m_posn = m_range_min + static_cast<T>((m_range_max - m_range_min) * fractional_distance);
467 if (m_posn != old_posn)
468 SlidSignal(m_posn, m_range_min, m_range_max);
469 }
470
471 template <class T>
472 void Slider<T>::SlideToImpl(T p, bool signal)
473 {
474 T old_posn = m_posn;
475 if (0 < (m_range_max - m_range_min) ? p < m_range_min : p > m_range_min)
476 m_posn = m_range_min;
477 else if (0 < (m_range_max - m_range_min) ? m_range_max < p : m_range_max > p)
478 m_posn = m_range_max;
479 else
480 m_posn = p;
481 MoveTabToPosn();
482 if (signal && m_posn != old_posn) {
483 SlidSignal(m_posn, m_range_min, m_range_max);
484 SlidAndStoppedSignal(m_posn, m_range_min, m_range_max);
485 }
486 }
487
488 template <class T>
489 Slider<T>::SlidEcho::SlidEcho(const std::string& name) :
490 m_name(name)
491 {}
492
493 template <class T>
494 void Slider<T>::SlidEcho::operator()(T pos, T min, T max)
495 {
496 std::cerr << "GG SIGNAL : " << m_name
497 << "(pos=" << pos << " min=" << min << " max=" << max << ")\n";
498 }
499
500 } // namespace GG
501
502 #endif