reflexion 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/reflex/application.cpp +19 -1
  3. data/.doc/ext/reflex/chase_constraint.cpp +84 -0
  4. data/.doc/ext/reflex/clipboard.cpp +65 -0
  5. data/.doc/ext/reflex/constraint.cpp +135 -0
  6. data/.doc/ext/reflex/key_event.cpp +6 -6
  7. data/.doc/ext/reflex/link_constraint.cpp +178 -0
  8. data/.doc/ext/reflex/menu.cpp +302 -0
  9. data/.doc/ext/reflex/native.cpp +22 -4
  10. data/.doc/ext/reflex/pin.cpp +156 -0
  11. data/.doc/ext/reflex/pointer.cpp +2 -0
  12. data/.doc/ext/reflex/pointer_event.cpp +1 -1
  13. data/.doc/ext/reflex/reflex.cpp +42 -7
  14. data/.doc/ext/reflex/selector.cpp +4 -4
  15. data/.doc/ext/reflex/snap_constraint.cpp +125 -0
  16. data/.doc/ext/reflex/style_length.cpp +3 -2
  17. data/.doc/ext/reflex/view.cpp +96 -23
  18. data/.doc/ext/reflex/wheel_constraint.cpp +142 -0
  19. data/.doc/ext/reflex/window.cpp +59 -17
  20. data/.github/workflows/release-gem.yml +10 -1
  21. data/.github/workflows/test.yml +9 -0
  22. data/ChangeLog.md +30 -0
  23. data/README.md +1 -0
  24. data/VERSION +1 -1
  25. data/ext/reflex/application.cpp +21 -1
  26. data/ext/reflex/chase_constraint.cpp +89 -0
  27. data/ext/reflex/clipboard.cpp +69 -0
  28. data/ext/reflex/constraint.cpp +146 -0
  29. data/ext/reflex/key_event.cpp +6 -6
  30. data/ext/reflex/link_constraint.cpp +194 -0
  31. data/ext/reflex/menu.cpp +330 -0
  32. data/ext/reflex/native.cpp +22 -4
  33. data/ext/reflex/pin.cpp +165 -0
  34. data/ext/reflex/pointer.cpp +2 -0
  35. data/ext/reflex/pointer_event.cpp +1 -1
  36. data/ext/reflex/reflex.cpp +42 -7
  37. data/ext/reflex/selector.cpp +4 -4
  38. data/ext/reflex/selector.h +1 -1
  39. data/ext/reflex/snap_constraint.cpp +135 -0
  40. data/ext/reflex/style_length.cpp +3 -2
  41. data/ext/reflex/view.cpp +117 -37
  42. data/ext/reflex/wheel_constraint.cpp +154 -0
  43. data/ext/reflex/window.cpp +65 -18
  44. data/include/reflex/application.h +9 -0
  45. data/include/reflex/clipboard.h +53 -0
  46. data/include/reflex/constraint.h +275 -0
  47. data/include/reflex/defs.h +204 -195
  48. data/include/reflex/event.h +2 -0
  49. data/include/reflex/menu.h +120 -0
  50. data/include/reflex/pin.h +68 -0
  51. data/include/reflex/pointer.h +4 -0
  52. data/include/reflex/ruby/clipboard.h +23 -0
  53. data/include/reflex/ruby/constraint.h +94 -0
  54. data/include/reflex/ruby/menu.h +103 -0
  55. data/include/reflex/ruby/pin.h +40 -0
  56. data/include/reflex/ruby/view.h +36 -0
  57. data/include/reflex/ruby/window.h +36 -18
  58. data/include/reflex/view.h +39 -12
  59. data/include/reflex/window.h +17 -4
  60. data/include/reflex.h +5 -0
  61. data/lib/reflex/chase_constraint.rb +15 -0
  62. data/lib/reflex/clipboard.rb +19 -0
  63. data/lib/reflex/constraint.rb +29 -0
  64. data/lib/reflex/extension.rb +1 -1
  65. data/lib/reflex/helper.rb +28 -0
  66. data/lib/reflex/key_event.rb +7 -12
  67. data/lib/reflex/link_constraint.rb +31 -0
  68. data/lib/reflex/menu.rb +76 -0
  69. data/lib/reflex/pin.rb +39 -0
  70. data/lib/reflex/pointer.rb +18 -0
  71. data/lib/reflex/pointer_event.rb +1 -1
  72. data/lib/reflex/snap_constraint.rb +28 -0
  73. data/lib/reflex/view.rb +56 -4
  74. data/lib/reflex/wheel_constraint.rb +28 -0
  75. data/lib/reflex/wheel_event.rb +9 -0
  76. data/lib/reflex.rb +15 -6
  77. data/pod.rake +4 -2
  78. data/reflex.gemspec +3 -3
  79. data/samples/constraint.rb +152 -0
  80. data/samples/menu.rb +29 -0
  81. data/src/application.cpp +21 -2
  82. data/src/application.h +5 -0
  83. data/src/clipboard.cpp +81 -0
  84. data/src/constraint.cpp +1662 -0
  85. data/src/constraint.h +64 -0
  86. data/src/event.cpp +7 -1
  87. data/src/event.h +1 -2
  88. data/src/ios/application.mm +5 -0
  89. data/src/ios/clipboard.mm +44 -0
  90. data/src/ios/event.h +6 -2
  91. data/src/ios/event.mm +31 -12
  92. data/src/ios/menu.mm +36 -0
  93. data/src/ios/view_controller.mm +45 -12
  94. data/src/ios/window.mm +6 -0
  95. data/src/menu.cpp +325 -0
  96. data/src/menu.h +65 -0
  97. data/src/osx/app_delegate.mm +74 -125
  98. data/src/osx/application.mm +16 -0
  99. data/src/osx/clipboard.mm +45 -0
  100. data/src/osx/event.mm +40 -10
  101. data/src/osx/menu.h +25 -0
  102. data/src/osx/menu.mm +372 -0
  103. data/src/osx/native_window.mm +27 -0
  104. data/src/osx/opengl_view.mm +51 -0
  105. data/src/osx/window.mm +11 -0
  106. data/src/pin.cpp +137 -0
  107. data/src/pointer.cpp +27 -16
  108. data/src/pointer.h +6 -0
  109. data/src/sdl/application.cpp +5 -0
  110. data/src/sdl/clipboard.cpp +39 -0
  111. data/src/sdl/event.cpp +20 -3
  112. data/src/sdl/event.h +6 -3
  113. data/src/sdl/menu.cpp +35 -0
  114. data/src/sdl/window.cpp +26 -8
  115. data/src/view.cpp +313 -52
  116. data/src/view.h +18 -2
  117. data/src/win32/application.cpp +5 -0
  118. data/src/win32/clipboard.cpp +210 -0
  119. data/src/win32/event.cpp +11 -1
  120. data/src/win32/event.h +2 -0
  121. data/src/win32/menu.cpp +352 -0
  122. data/src/win32/menu.h +27 -0
  123. data/src/win32/window.cpp +62 -0
  124. data/src/win32/window.h +3 -0
  125. data/src/window.cpp +235 -22
  126. data/src/window.h +13 -2
  127. data/src/world.cpp +77 -14
  128. data/src/world.h +9 -2
  129. data/test/helper.rb +7 -0
  130. data/test/test_application.rb +11 -0
  131. data/test/test_chase_constraint.rb +124 -0
  132. data/test/test_clipboard.rb +54 -0
  133. data/test/test_constraint.rb +186 -0
  134. data/test/test_key_event.rb +6 -0
  135. data/test/test_link_constraint.rb +239 -0
  136. data/test/test_menu.rb +207 -0
  137. data/test/test_pin.rb +65 -0
  138. data/test/test_pointer.rb +23 -9
  139. data/test/test_pointer_event.rb +1 -1
  140. data/test/test_snap_constraint.rb +142 -0
  141. data/test/test_view.rb +113 -23
  142. data/test/test_wheel_constraint.rb +127 -0
  143. data/test/test_wheel_event.rb +15 -9
  144. data/test/test_window.rb +10 -0
  145. metadata +82 -8
data/src/win32/window.h CHANGED
@@ -4,6 +4,7 @@
4
4
  #define __REFLEX_SRC_WIN32_WINDOW_H__
5
5
 
6
6
 
7
+ #include <xot/windows.h>
7
8
  #include "../window.h"
8
9
 
9
10
 
@@ -11,6 +12,8 @@ namespace Reflex
11
12
  {
12
13
 
13
14
 
15
+ HWND Window_get_hwnd (const Window* window);
16
+
14
17
  void Window_update (Window* win);
15
18
 
16
19
 
data/src/window.cpp CHANGED
@@ -14,14 +14,14 @@ namespace Reflex
14
14
  {
15
15
 
16
16
 
17
- using ViewList = std::vector<View::Ref>;
18
-
19
17
  using PointerMap = std::map<Pointer::ID, Pointer>;
20
18
 
21
19
  using ExtractedPointerIDSet = std::set<Pointer::ID>;
22
20
 
23
21
  using CaptureTargetIDList = Window::Data::CaptureTargetIDList;
24
22
 
23
+ using WeakViewList = Window::Data::WeakViewList;
24
+
25
25
 
26
26
  void
27
27
  Window_register (Window* win)
@@ -176,6 +176,9 @@ namespace Reflex
176
176
 
177
177
  Event e;
178
178
  window->on_activate(&e);
179
+ if (e.is_blocked()) return;
180
+
181
+ View_activate_tree(window->root(), &e);
179
182
  }
180
183
 
181
184
  void
@@ -183,10 +186,13 @@ namespace Reflex
183
186
  {
184
187
  if (!window) return;
185
188
 
189
+ Xot::remove_flag(&window->self->flags, Window::Data::ACTIVE);
190
+
186
191
  Event e;
187
192
  window->on_deactivate(&e);
193
+ if (e.is_blocked()) return;
188
194
 
189
- Xot::remove_flag(&window->self->flags, Window::Data::ACTIVE);
195
+ View_deactivate_tree(window->root(), &e);
190
196
  }
191
197
 
192
198
  void
@@ -288,6 +294,14 @@ namespace Reflex
288
294
  cleanup_captures(window);
289
295
  }
290
296
 
297
+ static bool
298
+ need_next_pointer_id (const Pointer& pointer, const Pointer& prev_pointer)
299
+ {
300
+ return
301
+ pointer.action() == Pointer::DOWN ||
302
+ prev_pointer.action() == Pointer::UP;
303
+ }
304
+
291
305
  static Pointer::ID
292
306
  get_next_pointer_id (Window* window)
293
307
  {
@@ -304,14 +318,13 @@ namespace Reflex
304
318
 
305
319
  Window::Data* self = window->self.get();
306
320
 
307
- auto action = pointer->action();
308
321
  auto& prev_pointer = self->prev_mouse_pointer;
309
322
 
310
323
  auto id = prev_pointer ? prev_pointer.id() : get_next_pointer_id(window);
311
324
  auto* down = prev_pointer.down();
312
325
  if (
313
326
  Pointer_mask_flag(prev_pointer, MOUSE_BUTTONS) == 0 &&
314
- (action == Pointer::DOWN || prev_pointer.action() == Pointer::UP))
327
+ need_next_pointer_id(*pointer, prev_pointer))
315
328
  {
316
329
  id = get_next_pointer_id(window);
317
330
  down = NULL;
@@ -322,6 +335,7 @@ namespace Reflex
322
335
  Pointer_set_prev(pointer, &prev_pointer);
323
336
  Pointer_set_down(pointer, down);
324
337
 
338
+ auto action = pointer->action();
325
339
  if (action == Pointer::DOWN)
326
340
  Pointer_add_flag(pointer, pointer->types() & MOUSE_BUTTONS);
327
341
 
@@ -351,6 +365,21 @@ namespace Reflex
351
365
  });
352
366
  }
353
367
 
368
+ static bool
369
+ is_last_pointer_event (const Pointer& pointer)
370
+ {
371
+ auto action = pointer.action();
372
+ if (action == Pointer::UP || action == Pointer::CANCEL)
373
+ {
374
+ // a mouse cursor and a floatable pen (Apple Pencil) survive their
375
+ // up/cancel; a finger or non-floatable pen ends here.
376
+ return
377
+ !(pointer.types() & Pointer::MOUSE) &&
378
+ !Pointer_is_floatable(pointer);
379
+ }
380
+ return action == Pointer::LEAVE;
381
+ }
382
+
354
383
  static void
355
384
  setup_pointer (Window* window, Pointer* pointer)
356
385
  {
@@ -362,16 +391,21 @@ namespace Reflex
362
391
 
363
392
  if (prev_pointer)
364
393
  {
365
- Pointer_set_id(pointer, prev_pointer->id());
394
+ if (need_next_pointer_id(*pointer, *prev_pointer))
395
+ Pointer_set_id(pointer, get_next_pointer_id(window));
396
+ else
397
+ {
398
+ Pointer_set_id(pointer, prev_pointer->id());
399
+ Pointer_set_down(pointer, prev_pointer->down());
400
+ }
401
+ Pointer_set_floatable(pointer, Pointer_is_floatable(*prev_pointer));
366
402
  Pointer_set_prev(pointer, prev_pointer);
367
- Pointer_set_down(pointer, prev_pointer->down());
368
403
  prev_pointers.erase(it);
369
404
  }
370
405
  else
371
406
  Pointer_set_id(pointer, get_next_pointer_id(window));
372
407
 
373
- auto action = pointer->action();
374
- if (action != Pointer::UP && action != Pointer::CANCEL)
408
+ if (!is_last_pointer_event(*pointer))
375
409
  {
376
410
  prev_pointers.emplace_back(*pointer);
377
411
  Pointer_set_prev(&prev_pointers.back(), NULL);
@@ -460,7 +494,7 @@ namespace Reflex
460
494
  extract_targeted_pointers(&event, extracteds, targets, pointers);
461
495
  if (event.empty()) continue;
462
496
 
463
- PointerEvent_update_for_capturing_view(&event, view);
497
+ PointerEvent_to_view_coord(&event, view);
464
498
  View_call_pointer_event(const_cast<View*>(view.get()), &event);
465
499
 
466
500
  if (event.is_blocked()) *blocked = true;
@@ -502,7 +536,7 @@ namespace Reflex
502
536
  if (!view->window()) continue;
503
537
 
504
538
  PointerEvent e = event.dup();
505
- PointerEvent_update_for_capturing_view(&e, view);
539
+ PointerEvent_to_view_coord(&e, view);
506
540
  View_call_pointer_event(const_cast<View*>(view.get()), &e);
507
541
 
508
542
  if (e.is_blocked()) *blocked = true;
@@ -561,6 +595,137 @@ namespace Reflex
561
595
  if (blocked) event->block();
562
596
  }
563
597
 
598
+ static void
599
+ get_current_hovered_views (
600
+ ViewList* result, Window* window, const Pointer& pointer)
601
+ {
602
+ if (pointer.is_drag())
603
+ {
604
+ // while dragging, only the capturing view(s) take part in enter/leave,
605
+ // tested against their own frame; views merely passed over are ignored.
606
+ for (auto& [view, targets] : window->self->captures)
607
+ {
608
+ if (!view->window()) continue;
609
+ if (std::find(targets.begin(), targets.end(), pointer.id()) == targets.end())
610
+ continue;
611
+
612
+ Point pos = view->from_window(pointer.position());
613
+ if (Bounds(view->frame().size()).is_include(pos))
614
+ result->emplace_back(view);
615
+ }
616
+ }
617
+ else
618
+ View_get_hovered_views(result, window->root(), pointer.position());
619
+ }
620
+
621
+ static WeakViewList*
622
+ get_prev_hovered_views (Window* window, const Pointer& pointer)
623
+ {
624
+ // a mouse pointer gets a fresh id on every click, so follow the prev()
625
+ // chain to keep tracking the same physical pointer across id changes.
626
+ auto& map = window->self->hovered_views;
627
+
628
+ auto it = map.find(pointer.id());
629
+ if (
630
+ it == map.end() &&
631
+ pointer.prev() && pointer.prev()->id() != pointer.id())
632
+ {
633
+ auto prev_it = map.find(pointer.prev()->id());
634
+ if (prev_it != map.end())
635
+ {
636
+ it = map.emplace(pointer.id(), std::move(prev_it->second)).first;
637
+ map.erase(prev_it);
638
+ }
639
+ }
640
+
641
+ return it != map.end() ? &it->second : NULL;
642
+ }
643
+
644
+ static void
645
+ call_pointer_boundary_event (
646
+ View* view, const Pointer& pointer, Pointer::Action action)
647
+ {
648
+ if (!view->window()) return;
649
+
650
+ Pointer p = pointer;
651
+ Pointer_set_action(&p, action);
652
+
653
+ PointerEvent event(&p, 1);
654
+ PointerEvent_to_view_coord(&event, view);
655
+ View_call_pointer_event(view, &event);
656
+ }
657
+
658
+ static void
659
+ call_pointer_boundary_events (
660
+ Window* window, const Pointer& pointer, ViewList* news)
661
+ {
662
+ ViewList olds;
663
+ if (auto* weak_views = get_prev_hovered_views(window, pointer))
664
+ {
665
+ for (auto& view : *weak_views)
666
+ if (view) olds.emplace_back(view.get());
667
+
668
+ for (auto it = olds.rbegin(); it != olds.rend(); ++it)
669
+ {
670
+ if (std::find(news->begin(), news->end(), it->get()) != news->end())
671
+ continue;
672
+ call_pointer_boundary_event(it->get(), pointer, Pointer::LEAVE);
673
+ }
674
+ }
675
+
676
+ for (auto& view : *news)
677
+ {
678
+ if (std::find(olds.begin(), olds.end(), view.get()) != olds.end())
679
+ continue;
680
+ call_pointer_boundary_event(view.get(), pointer, Pointer::ENTER);
681
+ }
682
+
683
+ auto& map = window->self->hovered_views;
684
+ if (news->empty())
685
+ map.erase(pointer.id());
686
+ else
687
+ map[pointer.id()] = WeakViewList(news->begin(), news->end());
688
+ }
689
+
690
+ static void
691
+ call_pointer_enter_events (Window* window, const auto& pointers)
692
+ {
693
+ for (const auto& pointer : pointers)
694
+ {
695
+ auto a = pointer.action();
696
+ if (a == Pointer::DOWN || a == Pointer::MOVE || a == Pointer::ENTER)
697
+ {
698
+ ViewList views;
699
+ get_current_hovered_views(&views, window, pointer);
700
+ call_pointer_boundary_events(window, pointer, &views);
701
+ }
702
+ }
703
+ }
704
+
705
+ static void
706
+ call_pointer_leave_events (Window* window, const auto& pointers)
707
+ {
708
+ for (const auto& pointer : pointers)
709
+ {
710
+ auto a = pointer.action();
711
+ if (a == Pointer::UP || a == Pointer::CANCEL || a == Pointer::LEAVE)
712
+ {
713
+ // a pointer that survives this event keeps hovering whatever is under
714
+ // it; a pointer that ends here hovers nothing and leaves every view.
715
+ ViewList views;
716
+ if (!is_last_pointer_event(pointer))
717
+ get_current_hovered_views(&views, window, pointer);
718
+ call_pointer_boundary_events(window, pointer, &views);
719
+ }
720
+ }
721
+ }
722
+
723
+ static bool
724
+ is_boundary_action (Pointer::Action action)
725
+ {
726
+ return action == Pointer::ENTER || action == Pointer::LEAVE;
727
+ }
728
+
564
729
  void
565
730
  Window_call_pointer_event (Window* window, PointerEvent* event)
566
731
  {
@@ -571,6 +736,10 @@ namespace Reflex
571
736
 
572
737
  setup_pointer_event(window, event);
573
738
 
739
+ bool boundary = !event->empty() && is_boundary_action((*event)[0].action());
740
+ std::vector<Pointer> pointers;
741
+ PointerEvent_each_pointer(event, [&](const auto& p) {pointers.emplace_back(p);});
742
+
574
743
  if (!event->empty())
575
744
  window->on_pointer(event);
576
745
 
@@ -582,18 +751,25 @@ namespace Reflex
582
751
  case Pointer::UP: window->on_pointer_up(event); break;
583
752
  case Pointer::MOVE: window->on_pointer_move(event); break;
584
753
  case Pointer::CANCEL: window->on_pointer_cancel(event); break;
754
+ case Pointer::ENTER: window->on_pointer_enter(event); break;
755
+ case Pointer::LEAVE: window->on_pointer_leave(event); break;
585
756
  default: break;
586
757
  }
587
758
  }
588
759
 
589
- if (!event->empty() && !event->is_blocked())
590
- call_captured_pointer_events(window, event);
591
-
592
- if (!event->empty() && !event->is_blocked())
760
+ call_pointer_enter_events(window, pointers);
761
+ if (!boundary)
593
762
  {
594
- PointerEvent_update_for_child_view(event, window->root());
595
- View_call_pointer_event(window->root(), event);
763
+ if (!event->empty() && !event->is_blocked())
764
+ call_captured_pointer_events(window, event);
765
+
766
+ if (!event->empty() && !event->is_blocked())
767
+ {
768
+ PointerEvent_update_for_child_view(event, window->root());
769
+ View_call_pointer_event(window->root(), event);
770
+ }
596
771
  }
772
+ call_pointer_leave_events(window, pointers);
597
773
 
598
774
  cleanup_captures(window);
599
775
  }
@@ -786,6 +962,8 @@ namespace Reflex
786
962
  on_close(&e);
787
963
  if (!force && e.is_blocked()) return;
788
964
 
965
+ self->hovered_views.clear();
966
+
789
967
  View_set_window(self->root.get(), NULL);
790
968
 
791
969
  Window_close(this);
@@ -809,6 +987,12 @@ namespace Reflex
809
987
  return point + frame().position();
810
988
  }
811
989
 
990
+ bool
991
+ Window::active () const
992
+ {
993
+ return Xot::has_flag(self->flags, Data::ACTIVE);
994
+ }
995
+
812
996
  void
813
997
  Window::set_title (const char* title)
814
998
  {
@@ -839,6 +1023,25 @@ namespace Reflex
839
1023
  return Window_get_frame(*this);
840
1024
  }
841
1025
 
1026
+ void
1027
+ Window::set_menu (Menu* menu)
1028
+ {
1029
+ self->menu = menu;
1030
+ Window_set_menu(this, menu);
1031
+ }
1032
+
1033
+ Menu*
1034
+ Window::menu ()
1035
+ {
1036
+ return self->menu.get();
1037
+ }
1038
+
1039
+ const Menu*
1040
+ Window::menu () const
1041
+ {
1042
+ return const_cast<Window*>(this)->menu();
1043
+ }
1044
+
842
1045
  void
843
1046
  Window::set_flag (uint flags)
844
1047
  {
@@ -924,27 +1127,27 @@ namespace Reflex
924
1127
  }
925
1128
 
926
1129
  void
927
- Window::on_activate (Event* e)
1130
+ Window::on_show (Event* e)
928
1131
  {
929
1132
  }
930
1133
 
931
1134
  void
932
- Window::on_deactivate (Event* e)
1135
+ Window::on_hide (Event* e)
933
1136
  {
934
1137
  }
935
1138
 
936
1139
  void
937
- Window::on_show (Event* e)
1140
+ Window::on_close (Event* e)
938
1141
  {
939
1142
  }
940
1143
 
941
1144
  void
942
- Window::on_hide (Event* e)
1145
+ Window::on_activate (Event* e)
943
1146
  {
944
1147
  }
945
1148
 
946
1149
  void
947
- Window::on_close (Event* e)
1150
+ Window::on_deactivate (Event* e)
948
1151
  {
949
1152
  }
950
1153
 
@@ -1008,6 +1211,16 @@ namespace Reflex
1008
1211
  {
1009
1212
  }
1010
1213
 
1214
+ void
1215
+ Window::on_pointer_enter (PointerEvent* e)
1216
+ {
1217
+ }
1218
+
1219
+ void
1220
+ Window::on_pointer_leave (PointerEvent* e)
1221
+ {
1222
+ }
1223
+
1011
1224
  void
1012
1225
  Window::on_wheel (WheelEvent* e)
1013
1226
  {
data/src/window.h CHANGED
@@ -12,6 +12,7 @@
12
12
  #include <rays/painter.h>
13
13
  #include "reflex/window.h"
14
14
  #include "reflex/view.h"
15
+ #include "reflex/menu.h"
15
16
  #include "application.h"
16
17
  #include "pointer.h"
17
18
 
@@ -33,6 +34,10 @@ namespace Reflex
33
34
 
34
35
  typedef std::map<View::Ref, CaptureTargetIDList> CaptureMap;
35
36
 
37
+ typedef std::vector<Xot::WeakRef<View>> WeakViewList;
38
+
39
+ typedef std::map<Pointer::ID, WeakViewList> HoveredViewMap;
40
+
36
41
  enum Flag
37
42
  {
38
43
 
@@ -48,12 +53,12 @@ namespace Reflex
48
53
 
49
54
  View::Ref root, focus;
50
55
 
56
+ Menu::Ref menu;
57
+
51
58
  Point prev_position, prev_size;
52
59
 
53
60
  double prev_time_update, prev_time_draw, prev_fps = 0;
54
61
 
55
- CaptureMap captures;
56
-
57
62
  int next_pointer_id = 1;
58
63
 
59
64
  Pointer prev_mouse_pointer;
@@ -62,6 +67,10 @@ namespace Reflex
62
67
 
63
68
  uint flags;
64
69
 
70
+ CaptureMap captures;
71
+
72
+ HoveredViewMap hovered_views;
73
+
65
74
  Data ();
66
75
 
67
76
  virtual ~Data ();
@@ -111,6 +120,8 @@ namespace Reflex
111
120
 
112
121
  Bounds Window_get_frame (const Window& window);
113
122
 
123
+ void Window_set_menu (Window* window, Menu* menu);
124
+
114
125
  Screen Window_get_screen (const Window& window);
115
126
 
116
127
  void Window_set_flags (Window* window, uint flags);
data/src/world.cpp CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
 
4
4
  #include <assert.h>
5
+ #include <algorithm>
5
6
  #include <memory>
6
7
  #include <vector>
7
8
  #include <map>
@@ -9,8 +10,9 @@
9
10
  #include <box2d/box2d.h>
10
11
  #include "reflex/event.h"
11
12
  #include "reflex/exception.h"
12
- #include "shape.h"
13
13
  #include "view.h"
14
+ #include "shape.h"
15
+ #include "constraint.h"
14
16
  #include "body.h"
15
17
  #include "fixture.h"
16
18
 
@@ -274,12 +276,24 @@ namespace Reflex
274
276
 
275
277
  bool stepping = false;
276
278
 
277
- std::unique_ptr<DebugDraw> debug_draw;
278
-
279
279
  // tracked to deliver contact-end events for shapes that Box2D has already
280
280
  // forgotten on destruction
281
281
  ShapePairMap touching_pairs;
282
282
 
283
+ std::vector<Xot::WeakRef<Constraint>> constraints;
284
+
285
+ // fixture-less static body for constraints that anchor to the world
286
+ // (e.g. chasing a target point) without affecting any collision
287
+ std::unique_ptr<Body> pground;
288
+
289
+ std::unique_ptr<DebugDraw> pdebug_draw;
290
+
291
+ Body* ground (World* world)
292
+ {
293
+ if (!pground) pground.reset(new Body(world));
294
+ return pground.get();
295
+ }
296
+
283
297
  void begin_contact (b2ShapeId b2shape1, b2ShapeId b2shape2, bool is_sensor_event)
284
298
  {
285
299
  Shape* s1 = get_shape(b2shape1);
@@ -338,6 +352,25 @@ namespace Reflex
338
352
  }
339
353
  }
340
354
 
355
+ void update_constraints ()
356
+ {
357
+ for (auto& constraint : constraints)
358
+ {
359
+ if (constraint)
360
+ Constraint_on_world_update(constraint);
361
+ }
362
+ }
363
+
364
+ void clear_constraints ()
365
+ {
366
+ for (auto& constraint : constraints)
367
+ {
368
+ if (constraint)
369
+ Constraint_on_world_destroyed(constraint);
370
+ }
371
+ constraints.clear();
372
+ }
373
+
341
374
  };// World::Data
342
375
 
343
376
 
@@ -361,6 +394,8 @@ namespace Reflex
361
394
  World::~World ()
362
395
  {
363
396
  self->touching_pairs.clear();
397
+ self->clear_constraints();
398
+ self->pground.reset();
364
399
 
365
400
  b2DestroyWorld(self->b2world);
366
401
  self->b2world = b2_nullWorldId;
@@ -377,6 +412,8 @@ namespace Reflex
377
412
 
378
413
  for (int i = 0; i < count; ++i)
379
414
  {
415
+ self->update_constraints();
416
+
380
417
  self->stepping = true;
381
418
  b2World_Step(self->b2world, dt, 4);
382
419
  self->stepping = false;
@@ -426,19 +463,19 @@ namespace Reflex
426
463
  if (state == debug()) return;
427
464
 
428
465
  if (state)
429
- self->debug_draw.reset(new DebugDraw(self->ppm));
466
+ self->pdebug_draw.reset(new DebugDraw(self->ppm));
430
467
  else
431
- self->debug_draw.reset();
468
+ self->pdebug_draw.reset();
432
469
  }
433
470
 
434
471
  bool
435
472
  World::debug () const
436
473
  {
437
- return !!self->debug_draw;
474
+ return !!self->pdebug_draw;
438
475
  }
439
476
 
440
477
  void
441
- World::on_update (float dt)
478
+ World::on_update ()
442
479
  {
443
480
  update(DELTA_TIME);
444
481
  }
@@ -446,11 +483,11 @@ namespace Reflex
446
483
  void
447
484
  World::on_draw (Painter* painter)
448
485
  {
449
- if (!self->debug_draw) return;
486
+ if (!self->pdebug_draw) return;
450
487
 
451
- self->debug_draw->begin(painter);
452
- b2World_Draw(self->b2world, self->debug_draw->b2ptr());
453
- self->debug_draw->end();
488
+ self->pdebug_draw->begin(painter);
489
+ b2World_Draw(self->b2world, self->pdebug_draw->b2ptr());
490
+ self->pdebug_draw->end();
454
491
  }
455
492
 
456
493
 
@@ -460,10 +497,24 @@ namespace Reflex
460
497
  return world ? world->self->b2world : b2_nullWorldId;
461
498
  }
462
499
 
463
- bool
464
- World_is_stepping (const World* world)
500
+ void
501
+ World_add_constraint (World* world, Constraint* constraint)
465
502
  {
466
- return world ? world->self->stepping : false;
503
+ if (!world || !constraint)
504
+ argument_error(__FILE__, __LINE__);
505
+
506
+ world->self->constraints.emplace_back(constraint);
507
+ }
508
+
509
+ void
510
+ World_remove_constraint (World* world, Constraint* constraint)
511
+ {
512
+ if (!world || !constraint)
513
+ argument_error(__FILE__, __LINE__);
514
+
515
+ std::erase_if(
516
+ world->self->constraints,
517
+ [=](auto& c) {return !c || c == constraint;});
467
518
  }
468
519
 
469
520
  void
@@ -490,6 +541,18 @@ namespace Reflex
490
541
  call_contact_events(pair.first, pair.second, ContactEvent::END);
491
542
  }
492
543
 
544
+ bool
545
+ World_is_stepping (const World* world)
546
+ {
547
+ return world ? world->self->stepping : false;
548
+ }
549
+
550
+ const Body*
551
+ World_get_ground (World* world)
552
+ {
553
+ return world ? world->self->ground(world) : NULL;
554
+ }
555
+
493
556
  World*
494
557
  World_get_temporary ()
495
558
  {
data/src/world.h CHANGED
@@ -19,6 +19,7 @@ namespace Reflex
19
19
 
20
20
  class View;
21
21
  class Body;
22
+ class Constraint;
22
23
 
23
24
 
24
25
  class World : public Xot::NonCopyable
@@ -51,7 +52,7 @@ namespace Reflex
51
52
 
52
53
  bool debug () const;
53
54
 
54
- virtual void on_update (float dt);
55
+ virtual void on_update ();
55
56
 
56
57
  virtual void on_draw (Painter* painter);
57
58
 
@@ -104,10 +105,16 @@ namespace Reflex
104
105
 
105
106
  b2WorldId World_get_id (const World* world);
106
107
 
107
- bool World_is_stepping (const World* world);
108
+ void World_add_constraint (World* world, Constraint* constraint);
109
+
110
+ void World_remove_constraint (World* world, Constraint* constraint);
108
111
 
109
112
  void World_end_contacts_for (World* world, b2ShapeId b2shape);
110
113
 
114
+ bool World_is_stepping (const World* world);
115
+
116
+ const Body* World_get_ground (World* world);
117
+
111
118
  World* World_get_temporary ();
112
119
 
113
120
 
data/test/helper.rb CHANGED
@@ -8,3 +8,10 @@ require 'reflex'
8
8
  require 'test/unit'
9
9
 
10
10
  include Xot::Test
11
+
12
+
13
+ def assert_equal_point(expected, actual, delta = 0.01, *args)
14
+ assert_in_delta expected.x, actual.x, delta, *args
15
+ assert_in_delta expected.y, actual.y, delta, *args
16
+ assert_in_delta expected.z, actual.z, delta, *args
17
+ end