zenilib  0.5.3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
Widget.cpp
Go to the documentation of this file.
1 /* This file is part of the Zenipex Library (zenilib).
2  * Copyright (C) 2011 Mitchell Keith Bloch (bazald).
3  *
4  * zenilib is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * zenilib is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with zenilib. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <zeni_rest.h>
19 
20 #include <list>
21 
22 #include <Zeni/Define.h>
23 
24 #if defined(_DEBUG) && defined(_WINDOWS)
25 #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
26 #define new DEBUG_NEW
27 #endif
28 
29 namespace Zeni {
30 
32  if(delete_m_renderer)
33  delete m_renderer;
34  }
35 
36  void Widget::set_editable(const bool &editable_) {
37  m_editable = editable_;
38  }
39 
40  void Widget::render_impl() const {
41  if(m_renderer)
42  m_renderer->render_to(*this);
43  }
44 
45  void Widget_Rectangle::set_upper_left(const Point2f &upper_left_) {
46  m_upper_left = upper_left_;
47  }
48 
49  void Widget_Rectangle::set_lower_right(const Point2f &lower_right_) {
50  m_lower_right = lower_right_;
51  }
52 
54  const Widget_Rectangle * const wrr = dynamic_cast<const Widget_Rectangle *>(&widget);
55 
56  if(!wrr)
58 
59  const Font &font = get_Fonts()[font_name];
60 
61  const Point2f center = wrr->get_center();
62  const float x = center.x;
63  const float y = center.y - 0.5f * font.get_text_height();
64  font.render_text(text, Point2f(x, y), color, ZENI_CENTER);
65  }
66 
68  return new Widget_Renderer_Text(*this);
69  }
70 
72  const Widget_Rectangle * const wrr = dynamic_cast<const Widget_Rectangle *>(&widget);
73 
74  if(!wrr)
76 
81 
82  get_Video().render(quad);
83  }
84 
86  return new Widget_Renderer_Color(*this);
87  }
88 
90  const Widget_Rectangle * const wrr = dynamic_cast<const Widget_Rectangle *>(&widget);
91 
92  if(!wrr)
94 
99  Material mat(texture);
100  quad.lend_Material(&mat);
101 
102  get_Video().render(quad);
103  }
104 
106  return new Widget_Renderer_Texture(*this);
107  }
108 
110  const Widget_Button * const wbr = dynamic_cast<const Widget_Button *>(&widget);
111  Widget_Renderer_Text * const wrtr = dynamic_cast<Widget_Renderer_Text *>(const_cast<Widget *>(&widget));
112 
113  if(!wbr || !wrtr)
115 
116  switch(wbr->get_State()) {
119  color = bg_normal;
120  wrtr->color = text_normal;
121  break;
122 
124  color = bg_clicked;
125  wrtr->color = text_clicked;
126  break;
127 
131  wrtr->color = text_hovered_strayed;
132  break;
133 
134  default:
135  break;
136  }
137 
139  wrtr->render_to(widget);
140  }
141 
143  return new Widget_Renderer_Tricolor(*this);
144  }
145 
147  const Check_Box * const cbr = dynamic_cast<const Check_Box *>(&widget);
148 
149  if(!cbr)
151 
152  Video &vr = get_Video();
153 
158 
159  Line_Segment<Vertex2f_Color> line_seg(ul, ll);
160  vr.render(line_seg);
161 
162  line_seg.a = lr;
163  vr.render(line_seg);
164 
165  line_seg.b = ur;
166  vr.render(line_seg);
167 
168  line_seg.a = ul;
169  vr.render(line_seg);
170 
171  if(cbr->is_checked() || cbr->is_toggling()) {
172  Color cc = check_color;
173  if(cbr->is_toggling()) {
174  if(cbr->is_checked())
175  cc.a /= 3.0f;
176  else
177  cc.a *= 2.0f / 3.0f;
178  }
179 
180  ul.set_Color(cc);
181  ll.set_Color(cc);
182  lr.set_Color(cc);
183  ur.set_Color(cc);
184 
185  line_seg.a = ul;
186  line_seg.b = lr;
187  vr.render(line_seg);
188 
189  line_seg.a = ll;
190  line_seg.b = ur;
191  vr.render(line_seg);
192  }
193  }
194 
196  return new Widget_Renderer_Check_Box(*this);
197  }
198 
200  const Slider * const sr = dynamic_cast<const Slider *>(&widget);
201 
202  if(!sr)
204 
205  Video &vr = get_Video();
206 
207  const Point3f p0(sr->get_end_point_a());
208  const Point3f p1(sr->get_end_point_b());
209  const Vector3f v = p1 - p0;
210  const Vector3f n(-v.j, v.i, 0.0f); // or (v.j, -v.i, 0.0f)
211 
212  const Point3f &midpt = p0 + sr->get_slider_position() * v;
213  const Vector3f &n2 = sr->get_slider_radius() * n.normalized();
214 
216  Vertex2f_Color(Point2f(midpt + n2), slider_color));
217 
218  vr.render(line_seg);
219 
220  line_seg.a.position = Point3f(p0);
221  line_seg.a.set_Color(line_color);
222  line_seg.b.position = Point3f(p1);
223  line_seg.b.set_Color(line_color);
224 
225  vr.render(line_seg);
226  }
227 
229  return new Widget_Renderer_Slider(*this);
230  }
231 
232  void Widget_Button::on_mouse_button(const Point2i &pos, const bool &down, const int &button) {
233 #ifndef ANDROID
234  if(!is_editable() || button != SDL_BUTTON_LEFT)
235  return;
236 
237  const bool inside = is_inside(pos);
238 
239  if(down)
240  if(inside) {
241  m_state = CLICKED;
242  on_click();
243 
244  set_busy(true);
245  }
246  else {
247  m_state = UNACTIONABLE;
248 
249  set_busy(false);
250  }
251  else {
252  if(inside) {
253  if(m_state == CLICKED) {
254  m_state = HOVERED;
255  on_accept();
256  on_mouse_motion(pos);
257  }
258  else
259  m_state = HOVERED;
260  }
261  else {
262  if(m_state == CLICKED) {
263  m_state = NORMAL;
264  on_reject();
265  }
266  else
267  m_state = NORMAL;
268  }
269 
270  set_busy(false);
271  }
272 #endif
273  }
274 
276 #ifndef ANDROID
277  if(!is_editable())
278  return;
279 
280  if(m_state == UNACTIONABLE && !get_Game().get_mouse_button_state(SDL_BUTTON_LEFT))
281  m_state = NORMAL;
282 
283  const bool inside = is_inside(pos);
284 
285  if(m_state != UNACTIONABLE) {
286  if(inside) {
287  if(m_state == STRAYED) {
288  m_state = CLICKED;
289  on_unstray();
290  }
291  else if(m_state == NORMAL) {
292  m_state = HOVERED;
293  on_hover();
294  }
295  }
296  else {
297  if(m_state == CLICKED) {
298  m_state = STRAYED;
299  on_stray();
300  }
301  else if(m_state == HOVERED) {
302  m_state = NORMAL;
303  on_unhover();
304  }
305  }
306  }
307 #endif
308  }
309 
310 #if SDL_VERSION_ATLEAST(2,0,0)
311  void Widget_Button::on_mouse_wheel(const Zeni::Point2i &, const int &) {
312  }
313 #endif
314 
316  m_checked = !m_checked;
317  m_toggling = false;
318  }
319 
321  m_toggling = true;
322  }
323 
325  m_toggling = true;
326  }
327 
329  m_toggling = false;
330  }
331 
333  m_toggling = false;
334  }
335 
337  if(is_checked())
339  else {
341 
342  m_radio_button_set->accept(*this);
343  }
344  }
345 
346  void Radio_Button_Set::on_mouse_button(const Point2i &pos, const bool &down, const int &button) {
347  if(!is_editable())
348  return;
349 
350  for(std::set<Radio_Button *>::iterator it = m_radio_buttons.begin(); it != m_radio_buttons.end(); ++it)
351  (*it)->on_mouse_button(pos, down, button);
352  }
353 
355  if(!is_editable())
356  return;
357 
358  for(std::set<Radio_Button *>::iterator it = m_radio_buttons.begin(); it != m_radio_buttons.end(); ++it)
359  (*it)->on_mouse_motion(pos);
360  }
361 
363  for(std::set<Radio_Button *>::const_iterator it = m_radio_buttons.begin(); it != m_radio_buttons.end(); ++it)
364  (*it)->render_impl();
365  }
366 
367  Slider::Slider(const Point2f &end_point_a_, const Point2f &end_point_b_,
368  const float &slider_radius_,
369  const float &slider_position_)
370  : m_line_segment(Point3f(end_point_a_), Point3f(end_point_b_)),
371  m_slider_radius(slider_radius_),
372  m_mouse_wheel_inverted(false),
373  m_mouse_wheel_continuous_rate(0.01f),
374  m_slider_position(slider_position_),
375  m_down(false)
376  {
377  give_Renderer(new Widget_Renderer_Slider(get_Colors()["default_button_bg_normal"], get_Colors()["default_button_bg_normal"]));
378  }
379 
380  void Slider::on_mouse_button(const Zeni::Point2i &pos, const bool &down, const int &button) {
381 #ifndef ANDROID
382  if(!is_editable() || button != SDL_BUTTON_LEFT)
383  return;
384 
385  if(down) {
386  const Point3f mouse_pos(float(pos.x), float(pos.y), 0.0f);
387 
388  const std::pair<float, float> test = m_line_segment.nearest_point(mouse_pos);
389  if(test.first < m_slider_radius) {
390  m_down = true;
391  m_backup_position = m_slider_position;
392  m_slider_position = test.second;
393  on_slide();
394 
395  set_busy(true);
396  }
397  else
398  m_down = false;
399  }
400  else {
401  if(m_down)
402  on_accept();
403 
404  set_busy(false);
405  }
406 #endif
407  }
408 
410  if(m_down) {
411  const Point3f mouse_pos(float(pos.x), float(pos.y), 0.0f);
412 
413  const std::pair<float, float> test = m_line_segment.nearest_point(mouse_pos);
414  if(test.first < m_slider_radius) {
415  m_slider_position = test.second;
416  on_slide();
417  }
418  else {
419  m_slider_position = m_backup_position;
420  on_slide();
421  }
422  }
423  }
424 
425 #if SDL_VERSION_ATLEAST(2,0,0)
426  void Slider::on_mouse_wheel(const Zeni::Point2i &pos, const int &up) {
427  if(!m_down) {
428  const Point3f mouse_pos(float(pos.x), float(pos.y), 0.0f);
429  const int up_ = is_mouse_wheel_inverted() ? -up : up;
430 
431  const std::pair<float, float> test = get_line_segment().nearest_point(mouse_pos);
432  if(test.first < get_slider_radius()) {
433  m_slider_position = std::max(0.0f, std::min(1.0f, m_slider_position + m_mouse_wheel_continuous_rate * up_));
434 
435  on_slide();
436  }
437  }
438  }
439 #endif
440 
442  }
443 
445  m_down = false;
446  }
447 
448  Slider_Int::Slider_Int(const Range &range,
449  const Point2f &end_point_a_, const Point2f &end_point_b_,
450  const float &slider_radius_,
451  const float &slider_position_)
452  : Slider(end_point_a_, end_point_b_, slider_radius_, slider_position_),
453  m_range(range)
454  {
455  assert(range.first <= range.second);
456  set_value(get_value());
457  }
458 
459 #if SDL_VERSION_ATLEAST(2,0,0)
460  void Slider_Int::on_mouse_wheel(const Zeni::Point2i &pos, const int &up) {
461  Slider::on_mouse_wheel(pos, up);
462 
463  const int up_ = is_mouse_wheel_inverted() ? -up : up;
464 #else
465  void Slider_Int::on_mouse_button(const Zeni::Point2i &pos, const bool &down, const int &button) {
466  Slider::on_mouse_button(pos, down, button);
467 
468  if(down || (button != SDL_BUTTON_WHEELDOWN && button != SDL_BUTTON_WHEELUP))
469  return;
470 
471  const int up_ = (button == (is_mouse_wheel_inverted() ? SDL_BUTTON_WHEELDOWN : SDL_BUTTON_WHEELUP)) ? 1 : -1;
472 #endif
473 
474 #ifndef ANDROID
475  if(!is_editable() || is_busy())
476  return;
477 
478  const Point3f mouse_pos(float(pos.x), float(pos.y), 0.0f);
479 
480  const std::pair<float, float> test = get_line_segment().nearest_point(mouse_pos);
481  if(test.first < get_slider_radius()) {
482  set_value(std::max(get_range().first, std::min(get_range().second, get_value() + up_)));
483 
484  on_slide();
485  }
486 #endif
487  }
488 
491  set_value(get_value());
492  }
493 
494  Selector::Normal_Button::Normal_Button(Selector &selector,
495  const Point2f &upper_left_,
496  const Point2f &lower_right_)
497  : Text_Button(upper_left_, lower_right_, selector.m_font, ""),
498  m_selector(&selector)
499  {
500  lend_Renderer(selector.m_button_renderer);
501  }
502 
503  void Selector::Normal_Button::on_accept() {
504  m_selector->decide_visible(m_selector->m_option);
505  m_selector->m_selected = true;
506 
507  m_selector->set_busy(true);
508  m_selector->set_layer(-0.5f);
509  }
510 
511  Selector::Selector_Button::Selector_Button(Selector &selector,
512  const String &option,
513  const Point2f &upper_left_,
514  const Point2f &lower_right_)
515  : Text_Button(upper_left_, lower_right_, selector.m_font, option),
516  m_selector(&selector)
517  {
518  lend_Renderer(selector.m_button_renderer);
519  }
520 
521  void Selector::Selector_Button::on_accept() {
522  m_selector->on_accept(text);
523  m_selector->m_selected = false;
524  }
525 
526  Selector::Selector_Slider::Selector_Slider(Selector &selector,
527  const float &slider_radius_,
528  const std::pair<float, float> &bg_coordinates_)
529  : Widget_Rectangle(Point2f(bg_coordinates_.first, 0.0f),
530  Point2f(bg_coordinates_.second, 0.0f)),
531  Slider_Int(Range(0u, 1u),
532  Point2f(), Point2f(),
533  slider_radius_),
534  m_selector(&selector)
535  {
536  lend_Renderer(selector.m_slider_renderer);
537  }
538 
539  void Selector::Selector_Slider::set_end_points(const Point2f &end_point_a_, const Point2f &end_point_b_) {
540  set_upper_left(Point2f(get_upper_left().x, end_point_a_.y - get_slider_radius()));
541  set_lower_right(Point2f(get_lower_right().x, end_point_b_.y + get_slider_radius()));
542 
543  Slider_Int::set_end_points(end_point_a_, end_point_b_);
544  }
545 
546  void Selector::Selector_Slider::on_slide() {
548  m_selector->decide_visible(size_t(get_value()));
549  }
550 
551  void Selector::Selector_Slider::render_impl() const {
552  m_selector->m_slider_bg_renderer->render_to(*this);
554  }
555 
556  Selector::Selector(const Point2f &upper_left_, const Point2f &lower_right_,
557  const Point2f &expanded_upper_left_, const Point2f &expanded_lower_right_,
558  const String &font_)
559 #ifdef _WINDOWS
560 #pragma warning( push )
561 #pragma warning( disable : 4355 )
562 #endif
563  : m_button_renderer(new Widget_Renderer_Tricolor),
564  delete_m_button_renderer(true),
565  m_slider_renderer(new Widget_Renderer_Slider(get_Colors()["default_button_text_normal"], get_Colors()["default_button_text_normal"])),
566  delete_m_slider_renderer(true),
567  m_slider_bg_renderer(new Widget_Renderer_Color(get_Colors()["default_button_bg_normal"])),
568  delete_m_slider_bg_renderer(true),
569  m_font(font_),
570  m_expanded(expanded_upper_left_, expanded_lower_right_),
571  m_option(0u),
572  m_selected(false),
573  m_normal_button(*this, upper_left_, lower_right_),
574  m_selector_slider(*this,
575  0.5f * (expanded_lower_right_.x - lower_right_.x),
576  std::make_pair(lower_right_.x, expanded_lower_right_.x))
577 #ifdef _WINDOWS
578 #pragma warning( pop )
579 #endif
580  {
581  m_selector_slider.invert_mouse_wheel(true);
582 
583  m_normal_button.lend_Renderer(m_button_renderer);
584  m_selector_slider.lend_Renderer(m_slider_renderer);
585  }
586 
588  clear();
589 
590  if(delete_m_button_renderer)
591  delete m_button_renderer;
592  if(delete_m_slider_renderer)
593  delete m_slider_renderer;
594  if(delete_m_slider_bg_renderer)
595  delete m_slider_bg_renderer;
596  }
597 
599  return m_options;
600  }
601 
603  if(m_options.empty())
604  m_option = 0u;
605  m_options.push_back(option);
606 
607  add_selector_button(option);
608  }
609 
611  Options::iterator it = std::find(m_options.begin(), m_options.end(), option);
612  if(it != m_options.end())
613  m_options.erase(it);
614 
615  build_selector_buttons();
616  }
617 
619  if(!m_options.empty())
620  return m_options[m_option];
621  return "";
622  }
623 
625  Options::iterator it = std::find(m_options.begin(), m_options.end(), option);
626  if(it != m_options.end())
627  m_option = size_t(it - m_options.begin());
628 
629  m_normal_button.text = m_options[m_option];
630  }
631 
632  void Selector::on_mouse_button(const Point2i &pos, const bool &down, const int &button) {
633 #ifndef ANDROID
634  if(!is_editable())
635  return;
636 
637  if(!m_selected)
638  m_normal_button.on_mouse_button(pos, down, button);
639  else {
640  const std::pair<Point2f, Point2f> v = visible_region();
641 
642  if(pos.x < v.first.x || pos.x > v.second.x || pos.y < v.first.y || pos.y > v.second.y) {
643  if(button == SDL_BUTTON_LEFT) {
644  m_selected = false;
645 
646  set_busy(false);
647  set_layer(0.0f);
648  }
649  }
650  else {
651  const Point2i offset_pos(pos.x, int(pos.y + vertical_offset()));
652  for(size_t i = view_start; i != view_end; ++i)
653  m_selector_buttons[i]->on_mouse_button(offset_pos, down, button);
654 
655  if(view_hidden) {
656 #if !SDL_VERSION_ATLEAST(2,0,0)
657  if(button == SDL_BUTTON_WHEELDOWN || button == SDL_BUTTON_WHEELUP) {
658  const Point2f &a = m_selector_slider.get_end_point_a();
659  m_selector_slider.on_mouse_button(Point2i(int(a.x), int(a.y)), down, button);
660  }
661  else
662 #endif
663  m_selector_slider.on_mouse_button(pos, down, button);
664  }
665  }
666  }
667 #endif
668  }
669 
670 #if SDL_VERSION_ATLEAST(2,0,0)
671  void Selector::on_mouse_wheel(const Zeni::Point2i &pos, const int &up) {
672 #ifndef ANDROID
673  if(!is_editable())
674  return;
675 
676  if(!m_selected)
677  m_normal_button.on_mouse_wheel(pos, up);
678  else {
679  const std::pair<Point2f, Point2f> v = visible_region();
680 
681  if(pos.x >= v.first.x && pos.x <= v.second.x && pos.y >= v.first.y && pos.y <= v.second.y) {
682  const Point2i offset_pos(pos.x, int(pos.y + vertical_offset()));
683  for(size_t i = view_start; i != view_end; ++i)
684  m_selector_buttons[i]->on_mouse_wheel(offset_pos, up);
685 
686  if(view_hidden) {
687  const Point2f &a = m_selector_slider.get_end_point_a();
688  m_selector_slider.on_mouse_wheel(Point2i(int(a.x), int(a.y)), up);
689  }
690  }
691  }
692 #endif
693  }
694 #endif
695 
697  if(!is_editable())
698  return;
699 
700  if(!m_selected)
701  m_normal_button.on_mouse_motion(pos);
702  else {
703  const Point2i offset_pos(pos.x, int(pos.y + vertical_offset()));
704  for(size_t i = view_start; i != view_end; ++i)
705  m_selector_buttons[i]->on_mouse_motion(offset_pos);
706 
707  if(view_hidden)
708  m_selector_slider.on_mouse_motion(pos);
709  }
710  }
711 
713  select_option(option);
714 
715  set_busy(false);
716  set_layer(0.0f);
717  }
718 
719  void Selector::render_impl() const {
720  if(!m_selected)
721  m_normal_button.render_impl();
722  else {
723  Video &vr = get_Video();
724  vr.push_world_stack();
725  vr.translate_scene(Vector3f(0.0f, -vertical_offset(), 0.0f));
726 
727  for(size_t i = view_start; i != view_end; ++i)
728  m_selector_buttons[i]->render_impl();
729 
730  vr.pop_world_stack();
731 
732  if(view_hidden)
733  m_selector_slider.render_impl();
734  }
735  }
736 
737  float Selector::button_height() const {
738  const Point2f &ul = m_normal_button.get_upper_left();
739  const Point2f &lr = m_normal_button.get_lower_right();
740  return lr.y - ul.y;
741  }
742 
743  float Selector::vertical_offset() const {
744  return float(view_offset) * button_height();
745  }
746 
747  void Selector::decide_visible(const size_t &centered) {
748  const Point2f &nul = m_normal_button.get_upper_left();
749  const Point2f &nlr = m_normal_button.get_lower_right();
750  const Point2f &eul = m_expanded.get_upper_left();
751  const Point2f &elr = m_expanded.get_lower_right();
752 
753  const size_t slots_above = size_t((nul.y - eul.y) / button_height());
754  const size_t slots_below = size_t((elr.y - nlr.y) / button_height());
755  size_t needed_above = centered;
756  size_t needed_below = m_options.size() - centered - 1u;
757 
758  view_start = centered - std::min(slots_above, needed_above);
759  view_end = centered + std::min(slots_below, needed_below) + 1u;
760  view_offset = centered;
761 
762  // Shift up as needed and possible
763  while(needed_above < slots_above && needed_below > slots_below) {
764  ++needed_above;
765  --needed_below;
766  ++view_end;
767  ++view_offset;
768  }
769 
770  // Shift down as needed and possible
771  while(needed_below < slots_below && needed_above > slots_above) {
772  --needed_above;
773  ++needed_below;
774  --view_start;
775  --view_offset;
776  }
777 
779 
780  view_hidden = 0u;
781  if(needed_above > slots_above)
782  view_hidden += needed_above - slots_above;
783  if(needed_below > slots_below)
784  view_hidden += needed_below - slots_below;
785 
786  if(view_hidden) {
787  const std::pair<Point2f, Point2f> v = visible_region();
788  const float r = m_selector_slider.get_slider_radius();
789  const float hx = 0.5f * (m_normal_button.get_lower_right().x + m_expanded.get_lower_right().x);
790 
791  m_selector_slider.set_end_points(Point2f(hx, v.first.y + r), Point2f(hx, v.second.y - r));
792  m_selector_slider.set_range(std::make_pair(int(view_offset - needed_above + slots_above),
793  int(view_offset + needed_below - slots_below)));
794  m_selector_slider.set_value(int(view_offset));
795  }
796  }
797 
798  std::pair<Point2f, Point2f> Selector::visible_region() const {
799  const Point2f &ul = m_normal_button.get_upper_left();
800  const Point2f &lr = m_normal_button.get_lower_right();
801  const float bh = button_height();
802  const float ex = view_hidden ? 2.0f * m_selector_slider.get_slider_radius() : 0.0f;
803  return std::make_pair(Point2f(ul.x, ul.y - bh * float(view_offset - view_start)),
804  Point2f(lr.x + ex, ul.y + bh * float(view_end - view_offset)));
805  }
806 
807  void Selector::add_selector_button(const String &option) {
808  const Point2f &ul = m_normal_button.get_upper_left();
809  const Point2f &lr = m_normal_button.get_lower_right();
810  const float vertical_offset = float(m_selector_buttons.size()) * (lr.y - ul.y);
811  m_selector_buttons.push_back(new Selector_Button(*this, option,
812  Point2f(ul.x, ul.y + vertical_offset),
813  Point2f(lr.x, lr.y + vertical_offset)));
814  }
815 
816  void Selector::build_selector_buttons() {
817  clear();
818 
819  for(Options::const_iterator it = m_options.begin(); it != m_options.end(); ++it)
820  add_selector_button(*it);
821  }
822 
823  void Selector::clear() {
824  for(std::vector<Selector_Button *>::const_iterator it = m_selector_buttons.begin(); it != m_selector_buttons.end(); ++it)
825  delete *it;
826  m_selector_buttons.clear();
827  }
828 
829  Text_Box::Text_Box(const Point2f &upper_left_, const Point2f &lower_right_,
830  const String &font_name_, const String &text_, const Color &text_color_,
831  const bool &editable_, const JUSTIFY &justify_, const int &tab_spaces_)
832  : Widget_Button(upper_left_, lower_right_),
833  m_bg_renderer(new Widget_Renderer_Color(get_Colors()["default_button_bg_normal"])),
834  m_text(font_name_, clean_string(text_), text_color_),
835  m_edit_pos(-1),
836  m_last_seek(0),
837  m_justify(justify_),
838  m_tab_spaces(tab_spaces_),
839  m_cursor_index(-1, -1)
840  {
841  Fonts::remove_post_reinit(&g_reinit);
842 
843  Fonts &fr = get_Fonts();
844 
845  set_editable(editable_);
846  format();
847 
848  get_text_boxes().insert(this);
849 
850  fr.lend_post_reinit(&g_reinit);
851  }
852 
854  get_text_boxes().erase(this);
855 
856  if(delete_m_bg_renderer)
857  delete m_bg_renderer;
858  }
859 
860 #ifndef ANDROID
861  void Text_Box::on_key(const SDL_Keysym &keysym, const bool &down) {
862  if(down && m_edit_pos != -1) {
863  Game &gr = get_Game();
864  const bool mod_alt = gr.get_key_state(SDLK_LALT) || gr.get_key_state(SDLK_RALT);
865  const bool mod_ctrl = gr.get_key_state(SDLK_LCTRL) || gr.get_key_state(SDLK_RCTRL);
866 #if SDL_VERSION_ATLEAST(1,3,0)
867  const bool mod_gui = gr.get_key_state(SDLK_LGUI) || gr.get_key_state(SDLK_RGUI);
868 #else
869  const bool mod_gui = gr.get_key_state(SDLK_LMETA) || gr.get_key_state(SDLK_RMETA) ||
870  gr.get_key_state(SDLK_LSUPER) || gr.get_key_state(SDLK_RSUPER);
871 #endif
872  const bool mod_shift = gr.get_key_state(SDLK_LSHIFT) || gr.get_key_state(SDLK_RSHIFT);
873  const bool mod_none = !mod_alt && !mod_ctrl && !mod_gui && !mod_shift;
874  const bool mod_ctrl_only = !mod_alt && mod_ctrl && !mod_gui && !mod_shift;
875  const bool mod_shift_only = !mod_alt && !mod_ctrl && !mod_gui && mod_shift;
876 
877  switch(keysym.sym) {
878  case SDLK_BACKSPACE:
879  if(mod_none)
880  {
881  const String &t = get_text();
882 
883  if(m_edit_pos > 0) {
884  String t0 = t.substr(0u, m_edit_pos - 1u);
885  String t1 = t.substr(size_t(m_edit_pos), t.size() - m_edit_pos);
886 
887  m_text.text = t0 + t1;
888  format();
889  seek(m_edit_pos - 1);
890 
891  on_change();
892  }
893  }
894  break;
895  case SDLK_DELETE:
896  if(mod_none)
897  {
898  const String &t = get_text();
899 
900  if(m_edit_pos < int(t.size())) {
901  String t0 = t.substr(0u, size_t(m_edit_pos));
902  String t1 = t.substr(m_edit_pos + 1u, t.size() - m_edit_pos - 1);
903 
904  m_text.text = t0 + t1;
905  format();
906  seek(m_edit_pos);
907 
908  on_change();
909  }
910  }
911  break;
912  case SDLK_HOME:
913  if(mod_none || mod_ctrl_only)
914  {
915  if(mod_ctrl)
916  seek_cursor(0);
917  else if(m_cursor_index.x) {
918  int cursor_pos = get_cursor_pos() - m_cursor_index.x;
919  if(m_lines[size_t(m_cursor_index.y)].endled)
920  ++cursor_pos;
921 
922  seek_cursor(cursor_pos);
923  }
924  }
925  break;
926  case SDLK_END:
927  if(mod_none || mod_ctrl_only)
928  {
929  int cursor_pos = mod_ctrl ?
931  get_cursor_pos() - m_cursor_index.x + int(m_lines[size_t(m_cursor_index.y)].unformatted.size());
932 
933  seek_cursor(cursor_pos);
934  }
935  break;
936  case SDLK_LEFT:
937  if(mod_none)
938  seek(m_edit_pos - 1);
939  break;
940  case SDLK_RIGHT:
941  if(mod_none)
942  seek(m_edit_pos + 1);
943  break;
944  case SDLK_UP:
945  if(mod_none)
946  if(m_cursor_index.y) {
947  size_t count = 0u;
948  for(size_t i = 0u, iend = m_cursor_index.y - 1u; i != iend; ++i)
949  count += m_lines[i].unformatted.size();
950  count += std::min(size_t(m_cursor_index.x), m_lines[m_cursor_index.y - 1u].unformatted.size());
951 
952  seek(int(count));
953  }
954  break;
955  case SDLK_DOWN:
956  if(mod_none)
957  if(m_cursor_index.y + 1 < int(m_lines.size())) {
958  size_t count = 0u;
959  for(size_t i = 0u, iend = m_cursor_index.y + 1u; i != iend; ++i)
960  count += m_lines[i].unformatted.size();
961  count += std::min(size_t(m_cursor_index.x), m_lines[m_cursor_index.y + 1u].unformatted.size());
962 
963  seek(int(count));
964  }
965  break;
966  default:
967  if(mod_none || mod_shift_only)
968  {
969  const char c = Gamestate_Base::to_char(keysym);
970  const String &t = get_text();
971  String t0 = t.substr(0u, size_t(m_edit_pos));
972  String t1 = t.substr(size_t(m_edit_pos), t.size() - m_edit_pos);
973  String next = clean_string(t0 + c + t1);
974 
975  if(next.size() != t.size()) {
976  m_text.text = next;
977  format();
978  seek(m_edit_pos + 1);
979 
980  on_change();
981  }
982  }
983  break;
984  }
985  }
986  }
987 #endif
988 
989  void Text_Box::on_mouse_button(const Point2i &pos, const bool &down, const int &button) {
990 #ifndef ANDROID
991  if(button != SDL_BUTTON_LEFT)
992  return;
993 
994  m_cursor_pos.x = int(pos.x - get_upper_left().x);
995  m_cursor_pos.y = int(pos.y - get_upper_left().y);
996 
997  const bool was_focused = m_edit_pos != -1;
998  invalidate_edit_pos();
999 
1000  Widget_Button::on_mouse_button(pos, down, button);
1001 
1002  if(m_edit_pos == -1) {
1003  if(was_focused)
1004  on_unfocus();
1005  }
1006  else {
1007  if(!was_focused)
1008  on_focus();
1009  }
1010 #endif
1011  }
1012 
1014  size_t j = 0, jend = m_lines.size();
1015  for(; j != jend && m_cursor_pos.y > m_lines[j].glyph_top; ++j);
1016  --j;
1017 
1019 
1020  const Font &f = get_Font();
1021 
1022  float x_pos;
1023  if(m_justify == ZENI_LEFT)
1024  x_pos = 0.0f;
1025  else if(m_justify == ZENI_RIGHT)
1026  x_pos = get_lower_right().x - get_upper_left().x;
1027  else
1028  x_pos = (get_lower_right().x - get_upper_left().x) / 2.0f;
1029 
1030  if(m_justify == ZENI_RIGHT)
1031  x_pos -= f.get_text_width(m_lines[j].formatted);
1032  else if(m_justify == ZENI_CENTER)
1033  x_pos -= f.get_text_width(m_lines[j].formatted) / 2.0f;
1034 
1036 
1037  int i = 0, iend = int(m_lines[j].unformatted_glyph_sides.size());
1038  for(; i != iend && m_cursor_pos.x > x_pos + m_lines[j].unformatted_glyph_sides[size_t(i)]; ++i);
1039  if(i) // Can be negative if using ZENI_CENTER or ZENI_RIGHT justification
1040  --i;
1041 
1043  if(i + 1 != iend && m_cursor_pos.x > x_pos + (0.2f * m_lines[j].unformatted_glyph_sides[size_t(i)] +
1044  0.8f * m_lines[j].unformatted_glyph_sides[i + 1u]))
1045  ++i;
1046 
1047  m_cursor_index.x = Sint32(i);
1048  m_cursor_index.y = Sint32(j);
1049  m_edit_pos = i;
1050  for(size_t k = 0; k < j; ++k)
1051  m_edit_pos += int(m_lines[k].unformatted.size());
1052 
1053 #ifdef _DEBUG
1054  {
1055  const size_t size = get_text().size();
1056  size_t count = 0u;
1057  for(size_t m = 0u, mend = m_lines.size(); m != mend; ++m)
1058  count += m_lines[m].unformatted.size();
1059 
1060  assert(size == count);
1061  }
1062 
1063  seek(m_edit_pos);
1064 #endif
1065  }
1066 
1068  }
1069 
1071  }
1072 
1074  }
1075 
1076  void Text_Box::render_impl() const {
1077  m_bg_renderer->render_to(*this);
1078 
1079  Video &vr = get_Video();
1080 
1081  const Font &f = get_Font();
1082  const Color &c = m_text.color;
1083 
1084  float x_pos;
1085  if(m_justify == ZENI_LEFT)
1086  x_pos = get_upper_left().x;
1087  else if(m_justify == ZENI_RIGHT)
1088  x_pos = get_lower_right().x;
1089  else
1090  x_pos = (get_upper_left().x + get_lower_right().x) / 2.0f;
1091 
1092  const float &y_offset = get_upper_left().y;
1093  for(size_t i = 0u, iend = m_lines.size(); i != iend; ++i)
1094  f.render_text(m_lines[i].formatted, Point2f(x_pos, y_offset + m_lines[i].glyph_top), c, m_justify);
1095 
1096  if(m_cursor_index.x != -1 && m_cursor_index.y != -1
1097  && !((get_Timer().get_time().get_ticks_since(m_last_seek) / SDL_DEFAULT_REPEAT_DELAY) & 1) // HACK: render every other second
1098  )
1099  {
1100  Point2f p0(x_pos + m_lines[size_t(m_cursor_index.y)].unformatted_glyph_sides[size_t(m_cursor_index.x)],
1101  get_upper_left().y + m_lines[size_t(m_cursor_index.y)].glyph_top);
1102  if(m_justify == ZENI_RIGHT)
1103  p0.x -= f.get_text_width(m_lines[size_t(m_cursor_index.y)].formatted);
1104  else if(m_justify == ZENI_CENTER)
1105  p0.x -= f.get_text_width(m_lines[size_t(m_cursor_index.y)].formatted) / 2.0f;
1106 
1107  const Point2f p1(p0.x, p0.y + f.get_text_height());
1108  const float epsilon = f.get_text_height() * ZENI_TEXT_CURSOR_WIDTH / 2.0f;
1109 
1110  const Vertex2f_Color v0(Point2f(p0.x - epsilon, p0.y), c);
1111  const Vertex2f_Color v1(Point2f(p1.x - epsilon, p1.y), c);
1112  const Vertex2f_Color v2(Point2f(p1.x + epsilon, p1.y), c);
1113  const Vertex2f_Color v3(Point2f(p0.x + epsilon, p0.y), c);
1114 
1115  const Quadrilateral<Vertex2f_Color> visible_cursor(v0, v1, v2, v3);
1116  vr.render(visible_cursor);
1117  }
1118  }
1119 
1120  void Text_Box::format() {
1121  m_lines.clear();
1122  m_lines.push_back(Line());
1123 
1124  const String t = get_text();
1125  const Font &f = get_Font();
1126  const float mll = max_line_width();
1127 
1128  if(t.empty())
1129  return;
1130 
1131  std::list<Word> words;
1132  Word next_word;
1133 
1134  for(unsigned int i = 0, iend = static_cast<unsigned int>(t.size()); i != iend; ++i) {
1135  const Word::Type type = isspace(t[i]) ? Word::SPACE : Word::WORD;
1136 
1137  if(next_word.type) {
1138  if(next_word.type != type || t[i] == '\n') {
1139  words.push_back(next_word);
1140  next_word = Word(type);
1141  }
1142  }
1143  else
1144  next_word.type = type;
1145 
1146  next_word.unformatted += t[i];
1147 
1148  const float next_width = get_text_width(f, next_word.unformatted);
1149  next_word.unformatted_glyph_sides.push_back(next_width);
1150  if(type != Word::SPACE && next_width > mll)
1151  next_word.splittable = true;
1152  }
1153  if(next_word.type != Word::NONSENSE)
1154  words.push_back(next_word);
1155 
1156  for(std::list<Word>::iterator it = words.begin(); it != words.end(); ++it)
1157  append_word(*it);
1158 
1159  float glyph_top = 0.0f;
1160  for(std::vector<Line>::iterator it = m_lines.begin(); it != m_lines.end(); ++it) {
1161  it->formatted = untablinebreak(it->unformatted);
1162  if(it->fpsplit)
1163  it->formatted += "-";
1164  it->glyph_top = glyph_top;
1165  if(!it->unformatted.empty() && it->unformatted[0] == '\n')
1166  it->endled = true;
1167 
1168  glyph_top += f.get_text_height();
1169  }
1170  }
1171 
1172  void Text_Box::append_word(const Word &word) {
1173  const Font &f = get_Font();
1174  const float mll = max_line_width();
1175 
1176  if(!word.unformatted.empty() && word.unformatted[0] == '\n')
1177  m_lines.push_back(Line());
1178 
1179  Line &l = *m_lines.rbegin();
1180  float next_sum = get_text_width(f, l.unformatted + word.unformatted);
1181 
1182  if(word.type != Word::SPACE && next_sum > mll && !word.fpsplit) {
1183  if(word.splittable) {
1184  size_t i = 0u, iend = word.unformatted.size();
1185  for(; i != iend && get_text_width(f, l.unformatted + word.unformatted.substr(0u, i) + "-") < mll; ++i);
1186  if(!l.unformatted.empty())
1187  --i;
1188  if(i != 0u && i != size_t(-1)) {
1189  {
1190  Word first_word(word.type);
1191  first_word.unformatted = word.unformatted.substr(0, i);
1192  for(size_t j = 1u, jend = first_word.unformatted.size(); j <= jend; ++j)
1193  first_word.unformatted_glyph_sides.push_back(get_text_width(f, first_word.unformatted.substr(0u, j)));
1194  first_word.fpsplit = true;
1195  append_word(first_word);
1196  }
1197 
1198  {
1199  Word second_word(word.type);
1200  second_word.unformatted = word.unformatted.substr(i, word.unformatted.size() - i);
1201  for(size_t j = 1u, jend = second_word.unformatted.size(); j <= jend; ++j)
1202  second_word.unformatted_glyph_sides.push_back(get_text_width(f, second_word.unformatted.substr(0u, j)));
1203  second_word.splittable = get_text_width(f, second_word.unformatted) > mll;
1204  append_word(second_word);
1205  }
1206  }
1207  else {
1208  Word only_word(word);
1209  only_word.fpsplit = l.unformatted.empty();
1210  m_lines.push_back(Line());
1211  append_word(only_word);
1212  return;
1213  }
1214  }
1215  else {
1216  m_lines.push_back(Line());
1217  append_word(word);
1218  }
1219  }
1220  else {
1221  for(unsigned int i = 0u, iend = static_cast<unsigned int>(word.unformatted.size()); i != iend; ++i) {
1222  l.unformatted += word.unformatted[i];
1223  l.unformatted_glyph_sides.push_back(get_text_width(f, l.unformatted));
1224  }
1225 
1226  l.fpsplit = word.fpsplit;
1227  }
1228  }
1229 
1230  void Text_Box::set_editable(const bool &editable_) {
1231  Widget::set_editable(editable_);
1232  format();
1233  invalidate_edit_pos();
1234  }
1235 
1236  void Text_Box::set_upper_left(const Point2f &upper_left_) {
1237  Widget_Rectangle::set_upper_left(upper_left_);
1238  format();
1239  seek(get_edit_pos());
1240  }
1241 
1242  void Text_Box::set_lower_right(const Point2f &lower_right_) {
1243  Widget_Rectangle::set_lower_right(lower_right_);
1244  format();
1245  seek(get_edit_pos());
1246  }
1247 
1248  const int & Text_Box::get_edit_pos() const {
1249  return m_edit_pos;
1250  }
1251 
1253  size_t count = size_t(m_cursor_index.x);
1254  for(size_t j = 0u, jend = size_t(m_cursor_index.y); j != jend; ++j)
1255  count += m_lines[j].unformatted_glyph_sides.size();
1256  return int(count);
1257  }
1258 
1260  return int(get_text().size());
1261  }
1262 
1264  if(!m_lines.empty()) {
1265  size_t count = 0u;
1266  for(std::vector<Line>::const_iterator it = m_lines.begin(); it != m_lines.end(); ++it)
1267  count += it->unformatted_glyph_sides.size();
1268  return int(count);
1269  }
1270  else
1271  return -1;
1272  }
1273 
1274  void Text_Box::seek(const int &edit_pos) {
1275  if(!is_editable())
1276  return;
1277 
1278  const String t = get_text();
1279 
1280  if(edit_pos < 0 ||
1281  edit_pos > int(t.size()))
1282  return;
1283 
1284  m_edit_pos = edit_pos;
1285  size_t j = 0u, jend = m_lines.size();
1286  int count = 0, i = -1, iend;
1287  for(; j != jend; ++j) {
1288  iend = int(m_lines[j].unformatted.size());
1289 
1290  if(count + iend < edit_pos) {
1291  count += iend;
1292  continue;
1293  }
1294 
1295  i = edit_pos - count;
1296  break;
1297  }
1298 
1299  m_cursor_index.x = Sint32(i);
1300  m_cursor_index.y = Sint32(j);
1301 
1302  m_last_seek = get_Timer().get_time();
1303  }
1304 
1305  void Text_Box::seek_cursor(const int &cursor_pos) {
1306  if(!is_editable() || cursor_pos < 0)
1307  return;
1308 
1309  size_t j = 0u, jend = m_lines.size();
1310  int edit_count = 0, count = 0, i = -1, iend;
1311  for(; j != jend; ++j) {
1312  iend = int(m_lines[j].unformatted_glyph_sides.size());
1313 
1314  if(count + iend <= cursor_pos) {
1315  count += iend;
1316  edit_count += int(m_lines[j].unformatted.size());
1317  continue;
1318  }
1319 
1320  i = cursor_pos - count;
1321  edit_count += i;
1322  break;
1323  }
1324 
1325  if(i != -1) {
1326  m_edit_pos = edit_count;
1327  m_cursor_index.x = Sint32(i);
1328  m_cursor_index.y = Sint32(j);
1329  }
1330 
1331  m_last_seek = get_Timer().get_time();
1332  }
1333 
1334  void Text_Box::set_focus(const bool &value) {
1335  if(!is_editable())
1336  return;
1337 
1338  if(value) {
1339  const bool was_focused = m_edit_pos != -1;
1340  seek(get_max_seek());
1341  if(!was_focused)
1342  on_focus();
1343  }
1344  else {
1345  const bool was_focused = m_edit_pos != -1;
1346  invalidate_edit_pos();
1347  if(was_focused)
1348  on_unfocus();
1349  }
1350  }
1351 
1352  String Text_Box::clean_string(const String &unclean_string) const {
1353  String cleaned;
1354  for(String::const_iterator it = unclean_string.begin(); it != unclean_string.end(); ++it)
1355  if(*it >= 0x20 ||
1356  *it == '\n' || *it == '\t')
1357  cleaned += *it;
1358  return cleaned;
1359  }
1360 
1361  String Text_Box::untablinebreak(const String &tabbed_text) const {
1362  String untabbed_text;
1363 
1364  for(String::const_iterator it = tabbed_text.begin(); it != tabbed_text.end(); ++it) {
1365  if(*it != '\t') {
1366  if(*it > 0x1F)
1367  untabbed_text += *it;
1368  }
1369  else
1370  for(int i = m_tab_spaces; i; --i)
1371  untabbed_text += ' ';
1372  }
1373 
1374  return untabbed_text;
1375  }
1376 
1377  float Text_Box::get_text_width(const Font &font, const String &text) {
1378  const String untabbed_text = untablinebreak(text);
1379 
1380  if(font.get_text_width(" "))
1381  return font.get_text_width(untabbed_text);
1382 
1383  String fake_text;
1384  for(String::const_iterator it = untabbed_text.begin(); it != untabbed_text.end(); ++it)
1385  if(*it != ' ')
1386  fake_text += *it;
1387  else
1388  fake_text += '.';
1389 
1390  return font.get_text_width(fake_text);
1391  }
1392 
1393  float Text_Box::max_line_width() const {
1394  return get_lower_right().x - get_upper_left().x;
1395  }
1396 
1398  std::set<Text_Box *> &text_boxes = get_text_boxes();
1399 
1400  for(std::set<Text_Box *>::iterator it = text_boxes.begin(); it != text_boxes.end(); ++it) {
1401  (*it)->format();
1402  (*it)->seek((*it)->get_edit_pos());
1403  }
1404  }
1405 
1406  std::set<Text_Box *> & Text_Box::get_text_boxes() {
1407  static std::set<Text_Box *> text_boxes;
1408  return text_boxes;
1409  }
1410 
1411 #ifndef ANDROID
1412  void Widget_Input_Repeater::on_key(const SDL_Keysym &keysym, const bool &down) {
1413  if(!is_editable())
1414  return;
1415 
1416  m_keysym = keysym;
1417  m_down = down;
1418 
1419  m_last_repeated = get_Timer().get_time();
1420  m_active = true;
1421  m_delay_finished = false;
1422 
1423  m_widget->on_key(m_keysym, m_down);
1424 
1425  set_busy(m_widget->is_busy());
1426  }
1427 #endif
1428 
1429  void Widget_Input_Repeater::on_mouse_button(const Point2i &pos, const bool &down, const int &button) {
1430  if(!is_editable())
1431  return;
1432 
1433  m_active = false;
1434 
1435  m_widget->on_mouse_button(pos, down, button);
1436 
1437  set_busy(m_widget->is_busy());
1438  }
1439 
1441  if(!is_editable())
1442  return;
1443 
1444  m_widget->on_mouse_motion(pos);
1445 
1446  set_busy(m_widget->is_busy());
1447  }
1448 
1450 #ifndef ANDROID
1451  if(!m_active)
1452  return;
1453 
1454  const Time current_time = get_Timer().get_time();
1455  const int ticks = int(current_time.get_ticks_since(m_last_repeated));
1456 
1457  if((m_delay_finished && ticks > m_repeat_interval) ||
1458  (!m_delay_finished && ticks > m_repeat_delay))
1459  {
1460  m_delay_finished = true;
1461  m_last_repeated = current_time;
1462  m_widget->on_key(m_keysym, m_down);
1463  }
1464 #endif
1465  }
1466 
1468  m_widget->render();
1469  }
1470 
1471  static bool widget_layer_less(const Widget * const &lhs, const Widget * const &rhs) {
1472  return lhs->get_layer() < rhs->get_layer();
1473  }
1474 
1475 #ifndef ANDROID
1476  void Widgets::on_key(const SDL_Keysym &keysym, const bool &down) {
1477  if(!is_editable())
1478  return;
1479 
1480  if(m_busy_one) {
1481  m_busy_one->on_key(keysym, down);
1482 
1483  if(!m_busy_one->is_busy()) {
1484  m_busy_one = 0;
1485  set_busy(false);
1486  }
1487  }
1488  else {
1489  std::sort(m_widgets.begin(), m_widgets.end(), &widget_layer_less);
1490 
1491  for(std::vector<Widget *>::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it) {
1492  (*it)->on_key(keysym, down);
1493 
1494  if(!m_busy_one && (*it)->is_busy()) {
1495  m_busy_one = *it;
1496  set_busy(true);
1497  }
1498  }
1499  }
1500  }
1501 #endif
1502 
1503  void Widgets::on_mouse_button(const Point2i &pos, const bool &down, const int &button) {
1504  if(!is_editable())
1505  return;
1506 
1507  if(m_busy_one) {
1508  m_busy_one->on_mouse_button(pos, down, button);
1509 
1510  if(!m_busy_one->is_busy()) {
1511  m_busy_one = 0;
1512  set_busy(false);
1513  }
1514  }
1515  else {
1516  std::sort(m_widgets.begin(), m_widgets.end(), &widget_layer_less);
1517 
1518  for(std::vector<Widget *>::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it) {
1519  (*it)->on_mouse_button(pos, down, button);
1520 
1521  if(!m_busy_one && (*it)->is_busy()) {
1522  m_busy_one = *it;
1523  set_busy(true);
1524  }
1525  }
1526  }
1527  }
1528 
1530  if(!is_editable())
1531  return;
1532 
1533  if(m_busy_one) {
1534  m_busy_one->on_mouse_motion(pos);
1535 
1536  if(!m_busy_one->is_busy()) {
1537  m_busy_one = 0;
1538  set_busy(false);
1539  }
1540  }
1541  else {
1542  std::sort(m_widgets.begin(), m_widgets.end(), &widget_layer_less);
1543 
1544  for(std::vector<Widget *>::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it) {
1545  (*it)->on_mouse_motion(pos);
1546 
1547  if(!m_busy_one && (*it)->is_busy()) {
1548  m_busy_one = *it;
1549  set_busy(true);
1550  }
1551  }
1552  }
1553  }
1554 
1555 #if SDL_VERSION_ATLEAST(2,0,0)
1556  void Widgets::on_mouse_wheel(const Zeni::Point2i &pos, const int &up) {
1557  if(!is_editable())
1558  return;
1559 
1560  if(m_busy_one) {
1561  m_busy_one->on_mouse_wheel(pos, up);
1562 
1563  if(!m_busy_one->is_busy()) {
1564  m_busy_one = 0;
1565  set_busy(false);
1566  }
1567  }
1568  else {
1569  std::sort(m_widgets.begin(), m_widgets.end(), &widget_layer_less);
1570 
1571  for(std::vector<Widget *>::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it) {
1572  (*it)->on_mouse_wheel(pos, up);
1573 
1574  if(!m_busy_one && (*it)->is_busy()) {
1575  m_busy_one = *it;
1576  set_busy(true);
1577  }
1578  }
1579  }
1580  }
1581 #endif
1582 
1584  std::sort(m_widgets.begin(), m_widgets.end(), &widget_layer_less);
1585 
1586  for(std::vector<Widget *>::const_reverse_iterator it = m_widgets.rbegin(), iend = m_widgets.rend(); it != iend; ++it)
1587  (*it)->perform_logic();
1588  }
1589 
1590  void Widgets::render_impl() const {
1591  std::sort(m_widgets.begin(), m_widgets.end(), &widget_layer_less);
1592 
1593  for(std::vector<Widget *>::const_reverse_iterator it = m_widgets.rbegin(), iend = m_widgets.rend(); it != iend; ++it)
1594  (*it)->render();
1595  }
1596 
1597  Text_Box::Reinit Text_Box::g_reinit;
1598 
1599 }
virtual void on_slide()
Definition: Widget.cpp:489
virtual float get_text_width(const String &text) const =0
Get the width of text rendering using this font. Approximately text_height * text.length() / 2.0f.
static bool widget_layer_less(const Widget *const &lhs, const Widget *const &rhs)
Definition: Widget.cpp:1471
virtual void on_mouse_motion(const Point2i &pos)
Definition: Widget.cpp:696
virtual void render_to(const Widget &widget)
rect must be of type Check_Box
Definition: Widget.cpp:146
virtual ~Widget()
Definition: Widget.cpp:31
GLfloat GLfloat v1
Definition: glew.h:1838
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t0
Definition: glew.h:11582
int32_t Sint32
A signed 32-bit integer type.
Definition: SDL_stdinc.h:141
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum GLenum type
Definition: gl2ext.h:845
Widget_Renderer_Color(const Color &color_)
Definition: Widget.hxx:247
Point2f get_end_point_b() const
Definition: Widget.hxx:391
GLuint color
Definition: glew.h:7185
An Abstraction of a Line.
Definition: Line_Segment.h:36
virtual void on_mouse_motion(const Point2i &pos)
Definition: Widget.cpp:409
virtual void on_accept()
Called when the cursor is released inside the button.
Definition: Widget.cpp:315
The Video Rendering Singleton.
Definition: Video.h:71
virtual void on_mouse_button(const Point2i &pos, const bool &down, const int &button)
Definition: Widget.cpp:380
const bool & is_editable() const
Definition: Widget.hxx:52
An Abstraction of a Vertex in 2-space, colored.
Definition: Vertex2f.h:80
Colors & get_Colors()
Get access to the singleton.
Definition: Colors.cpp:51
GLclampf f
Definition: glew.h:3390
A Check Box.
Definition: Widget.h:501
Point2f get_center() const
Definition: Widget.hxx:172
virtual void on_accept()
Called when the cursor is released inside the button.
Definition: Widget.h:484
const Font & get_Font() const
Definition: Widget.hxx:540
virtual void on_focus()
Definition: Widget.cpp:1067
bool get_key_state(const int &key) const
Get the state of a key.
Definition: Game.cpp:287
int32_t k
Definition: e_log.c:102
A Font database read in from a file.
Definition: Fonts.h:51
void lend_Renderer(const Widget_Render_Function *const &renderer)
Set the current Widget_Render_Function, giving the Widget no ownership.
Definition: Widget.hxx:133
GLclampd n
Definition: glew.h:7287
void render() const
Definition: Widget.hxx:111
bool empty() const
Definition: String.cpp:325
virtual void on_mouse_motion(const Point2i &pos)=0
EGLSurface EGLint x
Definition: eglext.h:293
const float & get_layer() const
Definition: Widget.hxx:56
virtual void on_key(const SDL_Keysym &keysym, const bool &down)
Definition: Widget.cpp:861
GLdouble GLdouble t
Definition: glew.h:1384
An Abstraction of a Material.
Definition: Material.h:56
Point2f get_lower_left() const
Definition: Widget.hxx:152
int32_t j
Definition: e_log.c:102
GLfloat GLfloat GLfloat GLfloat v3
Definition: glew.h:1846
static void reformat_all()
Reformat all Text_Box instances.
Definition: Widget.cpp:1397
virtual void on_slide()
Definition: Widget.cpp:441
Point2f get_upper_right() const
Definition: Widget.hxx:160
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:8736
void give_Renderer(Widget_Render_Function *const &renderer)
Set the current Widget_Render_Function, giving the Widget ownership.
Definition: Widget.hxx:126
#define SDL_DEFAULT_REPEAT_DELAY
Definition: SDL.h:6
virtual void render_to(const Widget &widget)
rect must be of type Widget_Rectangle
Definition: Widget.cpp:89
virtual void render_impl() const
Definition: Widget.cpp:1076
The SDL keysym structure, used in key events.
Definition: SDL_keyboard.h:47
An Abstraction of a Quadrilateral.
Definition: Quadrilateral.h:37
virtual void on_mouse_motion(const Point2i &pos)
Definition: Widget.cpp:354
Tick_Type get_ticks_since(const Time &time) const
Get the number of clock ticks passed between &#39;time&#39; and this Time.
Definition: Timer.hxx:40
#define assert(x)
Definition: SDL_malloc.c:1234
virtual void on_unhover()
Called when the cursor leaves the button without clicking.
Definition: Widget.h:474
virtual Widget_Renderer_Check_Box * get_duplicate() const
Definition: Widget.cpp:195
virtual void on_mouse_button(const Point2i &pos, const bool &down, const int &button)
Definition: Widget.cpp:465
const Range & get_range() const
Definition: Widget.hxx:440
virtual void set_upper_left(const Point2f &upper_left_)
Definition: Widget.cpp:45
static void remove_post_reinit(Event::Handler *const &handler)
JUSTIFY
Definition: Font.h:68
const float & get_slider_position() const
Definition: Widget.hxx:399
void lend_post_reinit(Event::Handler *const &handler)
Definition: Singleton.hxx:76
virtual void on_key(const SDL_Keysym &keysym, const bool &down)
Definition: Widget.cpp:1476
Game & get_Game()
Get access to the singleton.
Definition: Game.cpp:58
The Gamestate Stack.
Definition: Game.h:71
virtual void render_impl() const
Definition: Widget.cpp:1590
String substr(size_t pos=0, size_t n=npos) const
Definition: String.cpp:510
virtual Widget_Renderer_Text * get_duplicate() const
Definition: Widget.cpp:67
static char to_char(const SDL_Keysym &ks)
Returns a character key corresponding to the current combination of keys pressed or the null characte...
Definition: Gamestate.cpp:184
virtual void on_click()
Called when the cursor downclicks the button.
Definition: Widget.cpp:320
A 3D Point represented with floats.
Definition: Coordinate.h:133
static long get_time(void)
Definition: test_bbox.c:16
GLenum GLint * range
Definition: glew.h:3391
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
Definition: glew.h:11582
virtual void on_stray()
Called when the cursor is dragged off the button after being clicked.
Definition: Widget.cpp:332
void add_option(const String &option)
Definition: Widget.cpp:602
virtual void on_mouse_button(const Point2i &pos, const bool &down, const int &button)
on_mouse_button deactivates the repeater (if active) and then passes input to the Widget ...
Definition: Widget.cpp:1429
GLuint GLenum option
Definition: glew.h:3180
Timer & get_Timer()
Get access to the singleton.
Definition: Timer.cpp:73
A Featureful 3-Space Vector Class.
Definition: Vector3f.h:58
Widget_Renderer_Texture(const String &texture_)
Definition: Widget.hxx:252
ALuint u
Definition: alMain.h:58
GLint first
Definition: gl2ext.h:1011
virtual void set_upper_left(const Point2f &upper_left_)
Definition: Widget.cpp:1236
const GLdouble * v
Definition: glew.h:1377
virtual void render_to(const Widget &widget)
rect must be of type Widget_Button and Widget_Renderer_Text
Definition: Widget.cpp:109
virtual void set_lower_right(const Point2f &lower_right_)
Definition: Widget.cpp:1242
int
Definition: SDL_systhread.c:37
iterator end()
Definition: String.cpp:301
#define SDL_BUTTON_LEFT
Definition: SDL_mouse.h:204
virtual void on_hover()
Called when the cursor passes over the button.
Definition: Widget.h:472
virtual void set_editable(const bool &editable_)
Definition: Widget.cpp:1230
virtual void on_accept()
Called when the cursor is released inside the button.
Definition: Widget.cpp:336
virtual void pop_world_stack()=0
Pop a model view matrix off the stack.
float a
Definition: Color.h:69
virtual void render(const Renderable &renderable)=0
Render a Renderable.
void set_end_points(const Point2f &end_point_a_, const Point2f &end_point_b_)
Definition: Widget.hxx:403
virtual void on_key(const SDL_Keysym &, const bool &)
Definition: Widget.h:271
const Collision::Line_Segment & get_line_segment() const
Definition: Widget.hxx:436
GLint GLsizei count
Definition: gl2ext.h:1011
void set_value(const int &value)
Definition: Widget.hxx:453
virtual void on_accept()
Called when the cursor is released inside the button.
Definition: Widget.cpp:1013
const GLfloat * c
Definition: glew.h:14913
GLfloat v0
Definition: glew.h:1834
virtual void render_to(const Widget &widget)
rect must be of type Widget_Rectangle
Definition: Widget.cpp:53
Time get_time()
Get the current Time.
Definition: Timer.hxx:69
The Widget base class.
Definition: Widget.h:243
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl2ext.h:845
virtual void on_reject()
Called when the cursor is released outside the button.
Definition: Widget.cpp:328
Font Abstraction.
Definition: Font.h:70
iterator begin()
Definition: String.cpp:298
const Options & get_options() const
Definition: Widget.cpp:598
void remove_option(const String &option)
Definition: Widget.cpp:610
A Button Widget.
Definition: Widget.h:454
void select_option(const String &option)
Definition: Widget.cpp:624
virtual void render_to(const Widget &widget)
rect must be of type Slider
Definition: Widget.cpp:199
String get_selected() const
Definition: Widget.cpp:618
virtual void on_change()
Definition: Widget.cpp:1073
virtual Widget_Renderer_Slider * get_duplicate() const
Definition: Widget.cpp:228
virtual void perform_logic()
Definition: Widget.cpp:1583
Widget_Renderer_Text(const String &font_name_, const String &text_, const Color &color_)
Definition: Widget.hxx:240
const Point2f & get_lower_right() const
Definition: Widget.hxx:156
std::pair< int, int > Range
Definition: Widget.h:629
virtual void on_click()
Called when the cursor downclicks the button.
Definition: Widget.h:477
int get_max_cursor_seek() const
Definition: Widget.cpp:1263
A Text Button Widget.
Definition: Widget.h:492
virtual void render_impl() const
Definition: Widget.cpp:719
virtual void render_to(const Widget &widget)
rect must be of type Widget_Rectangle
Definition: Widget.cpp:71
virtual void push_world_stack()=0
Push a model view matrix onto the stack.
virtual void set_lower_right(const Point2f &lower_right_)
Definition: Widget.cpp:49
int get_cursor_pos() const
Definition: Widget.cpp:1252
virtual void render_impl() const
Definition: Widget.cpp:40
int get_value() const
Definition: Widget.hxx:449
virtual void render_impl() const
Definition: Widget.cpp:1467
const bool & is_toggling() const
Definition: Widget.hxx:352
EGLSurface EGLint EGLint y
Definition: eglext.h:293
GLenum GLenum GLuint texture
Definition: gl2ext.h:850
int32_t hx
Definition: e_log.c:102
GLdouble l
Definition: glew.h:8383
virtual void render_impl() const
Definition: Widget.cpp:362
virtual Widget_Renderer_Color * get_duplicate() const
Definition: Widget.cpp:85
EGLSurface EGLint void ** value
Definition: eglext.h:301
const State & get_State() const
Get the State of the button.
Definition: Widget.hxx:327
virtual void translate_scene(const Vector3f &direction)=0
Translate the scene.
virtual void perform_logic()
Call this function in your perform_logic function to get this Widget to actually do its job...
Definition: Widget.cpp:1449
An Abstraction of a Vertex in 2-space, textured.
Definition: Vertex2f.h:115
void set_layer(const float &layer_=0.0f)
Definition: Widget.hxx:64
virtual void on_mouse_button(const Point2i &pos, const bool &down, const int &button)
Definition: Widget.cpp:1503
void accept(Radio_Button &radio_button)
Definition: Widget.hxx:368
A Slider.
Definition: Widget.h:576
virtual void render_to(const Widget &widget)=0
std::pair< float, float > nearest_point(const Line_Segment &rhs) const
Returns &lt;distance, interpolation value [0.0f, 1.0f]&gt;
Definition: Collision.cpp:387
virtual void on_accept(const String &option)
Definition: Widget.cpp:712
Point2f get_end_point_a() const
Definition: Widget.hxx:387
virtual void on_key(const SDL_Keysym &keysym, const bool &down)
on_key input is repeated
Definition: Widget.cpp:1412
const int & get_edit_pos() const
Definition: Widget.cpp:1248
virtual void on_mouse_motion(const Point2i &pos)
Definition: Widget.cpp:1529
size_t size() const
Definition: String.cpp:310
SDL_Keycode sym
Definition: SDL_keyboard.h:50
virtual void on_accept()
Definition: Widget.cpp:444
std::vector< String > Options
Definition: Widget.h:715
const String & get_text() const
Definition: Widget.hxx:544
GLdouble GLdouble GLdouble r
Definition: glew.h:1392
const bool & is_busy() const
Definition: Widget.hxx:48
void seek_cursor(const int &cursor_pos)
Definition: Widget.cpp:1305
virtual void on_mouse_button(const Point2i &pos, const bool &down, const int &button)
Definition: Widget.cpp:632
virtual void on_mouse_button(const Point2i &pos, const bool &down, const int &button)
Definition: Widget.cpp:232
virtual void on_mouse_button(const Point2i &pos, const bool &down, const int &button)=0
virtual void on_mouse_motion(const Point2i &pos)
Definition: Widget.cpp:275
bool is_inside(const Point2i &pos) const
Definition: Widget.hxx:177
virtual void on_stray()
Called when the cursor is dragged off the button after being clicked.
Definition: Widget.h:479
Fonts & get_Fonts()
Get access to the singleton.
Definition: Fonts.cpp:60
const bool & is_checked() const
Definition: Widget.hxx:350
virtual void set_editable(const bool &editable_)
Definition: Widget.cpp:36
Video & get_Video()
Get access to the singleton.
Definition: Video.cpp:149
virtual void on_mouse_motion(const Point2i &pos)
on_mouse_motion input is simply passed through
Definition: Widget.cpp:1440
void seek(const int &edit_pos)
Definition: Widget.cpp:1274
#define min(x, y)
Definition: os.h:75
float get_text_height() const
Get the height of the font. The width is usually half the height, by default.
Definition: Font.hxx:29
int i
Definition: pngrutil.c:1377
void set_busy(const bool &busy_)
Definition: Widget.hxx:60
virtual void on_unstray()
Called when the cursor is dragged back onto the button without releasing the clicker.
Definition: Widget.cpp:324
virtual void on_unstray()
Called when the cursor is dragged back onto the button without releasing the clicker.
Definition: Widget.h:481
virtual void on_mouse_button(const Point2i &pos, const bool &down, const int &button)
Definition: Widget.cpp:989
const float & get_slider_radius() const
Definition: Widget.hxx:395
void lend_Material(const Material *const &material)
Set the Material, giving the Renderable no ownership.
Definition: Renderable.cpp:41
#define max(x, y)
Definition: os.h:79
const bool & is_mouse_wheel_inverted() const
Definition: Widget.hxx:420
int get_max_seek() const
Definition: Widget.cpp:1259
virtual Widget_Renderer_Tricolor * get_duplicate() const
Definition: Widget.cpp:142
virtual void on_reject()
Called when the cursor is released outside the button.
Definition: Widget.h:486
virtual void on_unfocus()
Definition: Widget.cpp:1070
void set_focus(const bool &value)
Definition: Widget.cpp:1334
A 2D Point represented with floats.
Definition: Coordinate.h:98
#define m(i, j)
const Point2f & get_upper_left() const
Definition: Widget.hxx:148
Vector3f normalized() const
Get the normalized vector.
Definition: Vector3f.cpp:58
virtual void on_mouse_button(const Point2i &pos, const bool &down, const int &button)
Definition: Widget.cpp:346
Rectangle positioning.
Definition: Widget.h:430
#define false
Definition: ftrandom.c:50
Color.
Definition: Color.h:41
GLfloat GLfloat GLfloat v2
Definition: glew.h:1842
virtual Widget_Renderer_Texture * get_duplicate() const
Definition: Widget.cpp:105
virtual void render_text(const String &text, const Point2f &position, const Color &color, const JUSTIFY &justify=ZENI_DEFAULT_JUSTIFY) const =0
Render text at screen position (x, y), with justification JUSTIFY.
unsigned int size_t
A Snapshot of the Timer.
Definition: Timer.h:63
GLsizei size
Definition: gl2ext.h:1467
const GLdouble * m
Definition: glew.h:8385
A 2D Point represented with integers.
Definition: Coordinate.h:85
#define ZENI_TEXT_CURSOR_WIDTH
Definition: Define.h:69