GG
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 
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>
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);
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>
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>
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>
224 { return m_page_sz != INVALID_PAGE_SIZE ? m_page_sz : (m_range_max - m_range_min) / 10; }
225 
226 template <class T>
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>
240 { return m_line_style; }
241 
242 template <class T>
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>
294 {
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>
323 { SlideToImpl(p, false); }
324 
325 template <class T>
327 { m_page_sz = size; }
328 
329 template <class T>
331 { m_line_style = style; }
332 
333 template <class T>
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>
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>
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