neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
widget.ipp
Go to the documentation of this file.
1// widget.ipp
2/*
3 neogfx C++ App/Game Engine
4 Copyright (c) 2015, 2020 Leigh Johnston. All Rights Reserved.
5
6 This program is free software: you can redistribute it and / or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include <neogfx/neogfx.hpp>
21#include <unordered_map>
22#include <typeindex>
25#include <neogfx/app/i_app.hpp>
33
34namespace neogfx
35{
36 template <typename Interface>
38 iSingular{ false },
39 iParent{ nullptr },
40 iAddingChild{ false },
41 iLinkBefore{ nullptr },
42 iLinkAfter{ nullptr },
43 iParentLayout{ nullptr },
44 iResizing{ false },
45 iLayoutPending{ false },
46 iLayoutInProgress{ 0 },
47 iLayer{ LayerWidget }
48 {
49 base_type::Position.Changed([this](const point&) { moved(); });
50 base_type::Size.Changed([this](const size&) { resized(); });
51 base_type::set_alive();
52 }
53
54 template <typename Interface>
56 iSingular{ false },
57 iParent{ nullptr },
58 iAddingChild{ false },
59 iLinkBefore{ nullptr },
60 iLinkAfter{ nullptr },
61 iParentLayout{ nullptr },
62 iResizing{ false },
63 iLayoutPending{ false },
64 iLayoutInProgress{ 0 },
65 iLayer{ LayerWidget }
66 {
67 base_type::Position.Changed([this](const point&) { moved(); });
68 base_type::Size.Changed([this](const size&) { resized(); });
69 aParent.add(*this);
70 base_type::set_alive();
71 }
72
73 template <typename Interface>
75 iSingular{ false },
76 iParent{ nullptr },
77 iAddingChild{ false },
78 iLinkBefore{ nullptr },
79 iLinkAfter{ nullptr },
80 iParentLayout{ nullptr },
81 iResizing{ false },
82 iLayoutPending{ false },
83 iLayoutInProgress{ 0 },
84 iLayer{ LayerWidget }
85 {
86 base_type::Position.Changed([this](const point&) { moved(); });
87 base_type::Size.Changed([this](const size&) { resized(); });
88 aLayout.add(*this);
89 base_type::set_alive();
90 }
92 template <typename Interface>
94 {
95 unlink();
96 if (service<i_keyboard>().is_keyboard_grabbed_by(*this))
97 service<i_keyboard>().ungrab_keyboard(*this);
98 remove_all();
99 {
100 auto layout = iLayout;
101 iLayout.reset();
103 if (has_parent())
104 parent().remove(*this);
105 if (has_parent_layout())
106 parent_layout().remove(*this);
109 template <typename Interface>
112 static auto invalidate_layout = [](i_widget& self)
113 {
114 self.update_layout();
115 };
116 static auto invalidate_canvas = [](i_widget& self)
117 {
118 self.update(true);
119 };
120 static auto invalidate_window_canvas = [](i_widget& self)
121 {
122 self.root().as_widget().update(true);
123 };
124 static auto ignore = [](i_widget&) {};
125 static const std::unordered_map<std::type_index, std::function<void(i_widget&)>> sActions =
127 { std::type_index{ typeid(property_category::hard_geometry) }, invalidate_layout },
128 { std::type_index{ typeid(property_category::soft_geometry) }, invalidate_window_canvas },
129 { std::type_index{ typeid(property_category::font) }, invalidate_layout },
130 { std::type_index{ typeid(property_category::color) }, invalidate_canvas },
131 { std::type_index{ typeid(property_category::other_appearance) }, invalidate_canvas },
132 { std::type_index{ typeid(property_category::other) }, ignore }
133 };
134 auto iterAction = sActions.find(std::type_index{ aProperty.category() });
135 if (iterAction != sActions.end())
136 iterAction->second(*this);
138
139 template <typename Interface>
142 if (iDeviceMetrics == std::nullopt)
143 {
144 if (self_type::has_root() && self_type::root().has_native_window())
146 iDeviceMetrics = &self_type::surface();
147 DeviceMetricsUpdated.trigger(*this);
148 }
150 return iDeviceMetrics != std::nullopt;
151 }
153 template <typename Interface>
156 if (self_type::device_metrics_available())
157 return **iDeviceMetrics;
158 return service<i_surface_manager>().display().metrics();
161 template <typename Interface>
164 return iSingular;
167 template <typename Interface>
169 {
170 if (iSingular != aSingular)
172 iSingular = aSingular;
173 if (iSingular)
175 iParent = nullptr;
179
180 template <typename Interface>
182 {
183 if constexpr (std::is_base_of_v<i_window, Interface>)
184 return iRoot == this;
185 else
186 return false;
189 template <typename Interface>
192 if (iRoot == std::nullopt)
194 const i_widget* w = this;
195 while (!w->is_root() && w->has_parent())
196 w = &w->parent();
197 if (w->is_root())
198 iRoot = &w->root();
200 return iRoot != std::nullopt;
203 template <typename Interface>
206 if constexpr (std::is_base_of_v<i_window, Interface>)
207 return *this;
208 else
210 if (self_type::has_root())
211 return **iRoot;
212 throw no_root();
216 template <typename Interface>
219 return const_cast<i_window&>(to_const(*this).self_type::root());
222 template <typename Interface>
225 iRoot = &aRoot;
228 template <typename Interface>
231 return self_type::has_root() && self_type::root().has_surface();
234 template <typename Interface>
237 return self_type::is_root() && self_type::root().is_surface();
240 template <typename Interface>
243 return self_type::root().surface();
246 template <typename Interface>
248 {
249 return self_type::root().surface();
252 template <typename Interface>
255 return self_type::root().real_surface();
258 template <typename Interface>
261 return self_type::root().real_surface();
264 template <typename Interface>
267 return iParent != nullptr;
270 template <typename Interface>
273 if (!has_parent())
274 throw no_parent();
275 return *iParent;
276 }
277
278 template <typename Interface>
280 {
281 return const_cast<i_widget&>(to_const(*this).parent());
282 }
283
284 template <typename Interface>
286 {
287 if (iParent != &aParent)
288 {
289 if (!self_type::is_root() && aParent.has_root())
290 self_type::set_root(aParent.root());
291 if ((self_type::is_root() && !self_type::root().is_nested()) || aParent.adding_child())
292 {
293 iParent = &aParent;
294 parent_changed();
295 }
296 else
297 {
298 if (self_type::use_count())
299 aParent.add(ref_ptr<i_widget>{ this });
300 else
301 aParent.add(*this);
302 }
303 }
304 }
305
306 template <typename Interface>
308 {
309 auto& self = base_type::as_widget();
310
311 if (!self_type::is_root())
312 {
313 self.reset_origin();
314 if (self.has_parent_layout())
315 self.update_layout();
316 }
317 }
318
319 template <typename Interface>
321 {
322 return iAddingChild;
323 }
324
325 template <typename Interface>
327 {
328 return add(ref_ptr<i_widget>{ ref_ptr<i_widget>{}, &aChild });
329 }
330
331 template <typename Interface>
333 {
334 if (aChild->has_parent() && &aChild->parent() == this)
335 return *aChild;
336 neolib::scoped_flag sf{ iAddingChild };
337 i_widget* oldParent = aChild->has_parent() ? &aChild->parent() : nullptr;
338 ref_ptr<i_widget> child = aChild;
339 if (oldParent != nullptr)
340 oldParent->remove(*child, true);
341 iChildren.push_back(child);
342 child->set_parent(*this);
343 child->set_singular(false);
344 if (self_type::has_root())
345 self_type::root().widget_added(*child);
346 ChildAdded.trigger(*child);
347 return *child;
348 }
349
350 template <typename Interface>
351 void widget<Interface>::remove(i_widget& aChild, bool aSingular, i_ref_ptr<i_widget>& aChildRef)
352 {
353 auto existing = find(aChild, false);
354 if (existing == iChildren.end())
355 return;
356 destroyed_flag childDestroyed{ aChild };
357 ref_ptr<i_widget> keep = **existing;
358 iChildren.erase(existing);
359 if (childDestroyed)
360 return;
361 if (aSingular)
362 keep->set_singular(true);
363 if (has_layout())
364 layout().remove(aChild);
365 if (childDestroyed)
366 return;
367 if (self_type::has_root())
368 self_type::root().widget_removed(aChild);
369 if (childDestroyed)
370 return;
371 ChildRemoved.trigger(*keep);
372 aChildRef = keep;
373 }
374
375 template <typename Interface>
377 {
378 while (!iChildren.empty())
379 remove(*iChildren.back(), true);
380 }
381
382 template <typename Interface>
384 {
385 return !iChildren.empty();
386 }
387
388 template <typename Interface>
390 {
391 return iChildren;
392 }
393
394 template <typename Interface>
396 {
397 if (!has_children())
398 {
399 if (has_parent())
400 return parent().find(*this);
401 else
402 throw no_children();
403 }
404 else
405 return iChildren.back()->last();
406 }
407
408 template <typename Interface>
410 {
411 if (!has_children())
412 {
413 if (has_parent())
414 return parent().find(*this);
415 else
416 throw no_children();
417 }
418 else
419 return iChildren.back()->last();
420 }
421
422 template <typename Interface>
423 typename widget<Interface>::widget_list::const_iterator widget<Interface>::find(const i_widget& aChild, bool aThrowIfNotFound) const
424 {
425 for (auto i = iChildren.begin(); i != iChildren.end(); ++i)
426 if (&**i == &aChild)
427 return i;
428 if (aThrowIfNotFound)
429 throw not_child();
430 else
431 return iChildren.end();
432 }
433
434 template <typename Interface>
435 typename widget<Interface>::widget_list::iterator widget<Interface>::find(const i_widget& aChild, bool aThrowIfNotFound)
436 {
437 for (auto i = iChildren.begin(); i != iChildren.end(); ++i)
438 if (&**i == &aChild)
439 return i;
440 if (aThrowIfNotFound)
441 throw not_child();
442 else
443 return iChildren.end();
444 }
445
446 template <typename Interface>
448 {
449 auto existing = std::find_if(iChildren.begin(), iChildren.end(), [&](auto&& c) { return &*c == &aChild; });
450 if (existing != iChildren.end())
451 {
452 ref_ptr<i_widget> child = *existing;
453 iChildren.erase(existing);
454 iChildren.insert(iChildren.begin(), child);
455 }
456 }
457
458 template <typename Interface>
460 {
461 auto existing = std::find_if(iChildren.begin(), iChildren.end(), [&](auto&& c) { return &*c == &aChild; });
462 if (existing != iChildren.end())
463 {
464 ref_ptr<i_widget> child = *existing;
465 iChildren.erase(existing);
466 iChildren.insert(iChildren.end(), child);
467 }
468 }
469
470 template <typename Interface>
472 {
473 return iLayer;
474 }
475
476 template <typename Interface>
478 {
479 if (iLayer != aLayer)
480 {
481 iLayer = aLayer;
482 update(true);
483 }
484 }
485
486 template <typename Interface>
488 {
489 if (iLinkBefore != nullptr)
490 return *iLinkBefore;
491 if (has_parent())
492 {
493 auto me = parent().find(*this);
494 if (me != parent().children().begin())
495 return **(*(me - 1))->last();
496 else
497 return parent();
498 }
499 else if (has_children())
500 return **last();
501 else
502 return *this;
503 }
504
505 template <typename Interface>
507 {
508 return const_cast<i_widget&>(to_const(*this).before());
509 }
510
511 template <typename Interface>
513 {
514 if (iLinkAfter != nullptr)
515 return *iLinkAfter;
516 if (has_children())
517 return *iChildren.front();
518 if (has_parent())
519 {
520 auto me = parent().find(*this);
521 if (me + 1 != parent().children().end())
522 return *(*(me + 1));
523 else if (parent().has_parent())
524 {
525 auto myParent = parent().parent().find(parent());
526 while ((*myParent)->has_parent() && (*myParent)->parent().has_parent() &&
527 myParent + 1 == (*myParent)->parent().children().end())
528 myParent = (*myParent)->parent().parent().find((*myParent)->parent());
529 if ((*myParent)->has_parent() && myParent + 1 != (*myParent)->parent().children().end())
530 return **(myParent + 1);
531 else if ((*(myParent))->has_parent())
532 return (*(myParent))->parent();
533 else
534 return **myParent;
535 }
536 else
537 return parent();
538 }
539 else
540 return *this;
541 }
542
543 template <typename Interface>
545 {
546 return const_cast<i_widget&>(to_const(*this).after());
547 }
548
549 template <typename Interface>
551 {
552 iLinkBefore = aPreviousWidget;
553 }
554
555 template <typename Interface>
557 {
558 iLinkAfter = aNextWidget;
559 }
560
561 template <typename Interface>
563 {
564 if (iLinkBefore != nullptr)
565 iLinkBefore->link_after(iLinkAfter);
566 if (iLinkAfter != nullptr)
567 iLinkAfter->link_before(iLinkBefore);
568 iLinkBefore = nullptr;
569 iLinkAfter = nullptr;
570 }
571
572 template <typename Interface>
574 {
575 return iLayout != nullptr;
576 }
577
578 template <typename Interface>
579 void widget<Interface>::set_layout(i_layout& aLayout, bool aMoveExistingItems)
580 {
581 set_layout(ref_ptr<i_layout>{ ref_ptr<i_layout>{}, &aLayout }, aMoveExistingItems);
582 }
583
584 template <typename Interface>
585 void widget<Interface>::set_layout(const i_ref_ptr<i_layout>& aLayout, bool aMoveExistingItems)
586 {
587 if (iLayout == aLayout)
588 throw layout_already_set();
589 auto oldLayout = iLayout;
590 iLayout = aLayout;
591 if (iLayout != nullptr)
592 {
593 if (has_parent_layout())
594 iLayout->set_parent_layout(&parent_layout());
595 iLayout->set_parent_widget(this);
596 if (aMoveExistingItems)
597 {
598 if (oldLayout == nullptr)
599 {
600 for (auto& child : iChildren)
601 if (child->has_parent_layout() && &child->parent_layout() == nullptr)
602 iLayout->add(child);
603 }
604 else
605 oldLayout->move_all_to(*iLayout);
606 }
607 }
608 }
609
610 template <typename Interface>
612 {
613 if (!iLayout)
614 throw no_layout();
615 return *iLayout;
616 }
617
618 template <typename Interface>
620 {
621 if (!iLayout)
622 throw no_layout();
623 return *iLayout;
624 }
625
626 template <typename Interface>
628 {
629 return is_managing_layout();
630 }
631
632 template <typename Interface>
634 {
635 return false;
636 }
637
638 template <typename Interface>
640 {
641 return client_rect();
642 }
643
644 template <typename Interface>
646 {
647 return iParentLayout != nullptr;
648 }
649
650 template <typename Interface>
652 {
653 if (has_parent_layout())
654 return *iParentLayout;
655 throw no_parent_layout();
656 }
657
658 template <typename Interface>
660 {
661 return const_cast<i_layout&>(to_const(*this).parent_layout());
662 }
663
664 template <typename Interface>
666 {
667 if (has_layout() && layout().has_parent_layout() && &layout().parent_layout() == iParentLayout)
668 layout().set_parent_layout(aParentLayout);
669 iParentLayout = aParentLayout;
670 }
671
672 template <typename Interface>
674 {
675 return has_parent();
676 }
677
678 template <typename Interface>
680 {
681 return parent();
682 }
683
684 template <typename Interface>
686 {
687 return parent();
688 }
689
690 template <typename Interface>
692 {
693 if (aParentWidget != nullptr)
694 set_parent(*aParentWidget);
695 else if (has_parent())
696 {
698 parent().remove(*this, true, keep);
699 iParent = nullptr;
700 parent_changed();
701 }
702 }
703
704 template <typename Interface>
709
710 template <typename Interface>
712 {
713 auto& self = base_type::as_widget();
714
715 if (layout_items_in_progress())
716 return;
717
718 if (!aDefer)
719 {
720#ifdef NEOGFX_DEBUG
721 if (debug::layoutItem == this)
722 {
723 if (!iLayoutPending)
724 service<debug::logger>() << neolib::logger::severity::Debug << "widget:layout_items: layout now" << endl;
725 else
726 service<debug::logger>() << neolib::logger::severity::Debug << "widget:layout_items: layout a deferred layout now" << endl;
727 }
728#endif
729 iLayoutPending = false;
730 service<i_async_layout>().validate(*this);
731 if (has_layout())
732 {
733 layout_items_started();
734 if (self_type::is_root() && size_policy() != size_constraint::Manual)
735 {
736 size desiredSize = self.extents();
737 switch (size_policy().horizontal_constraint())
738 {
740 desiredSize.cx = self.has_fixed_size() ? self.fixed_size().cx : minimum_size(self.extents()).cx;
741 break;
743 desiredSize.cx = minimum_size(self.extents()).cx;
744 break;
746 desiredSize.cx = maximum_size(self.extents()).cx;
747 break;
748 default:
749 break;
750 }
751 switch (size_policy().vertical_constraint())
752 {
754 desiredSize.cy = self.has_fixed_size() ? self.fixed_size().cy : minimum_size(self.extents()).cy;
755 break;
757 desiredSize.cy = minimum_size(self.extents()).cy;
758 break;
760 desiredSize.cy = maximum_size(self.extents()).cy;
761 break;
762 default:
763 break;
764 }
765 resize(desiredSize);
766 }
767 auto const& cr = client_rect(false);
768 layout().layout_items(cr.top_left(), cr.extents());
769 layout_items_completed();
770 }
771 }
772 else if (can_defer_layout())
773 {
774 if (!iLayoutPending)
775 {
776#ifdef NEOGFX_DEBUG
777 if (debug::layoutItem == this)
778 service<debug::logger>() << neolib::logger::severity::Debug << "widget:layout_items: deferred layout" << endl;
779#endif
780 iLayoutPending = service<i_async_layout>().defer_layout(*this);
781 }
782 }
783 else if (self.has_layout_manager())
784 {
785 throw widget_cannot_defer_layout();
786 }
787 }
788
789 template <typename Interface>
791 {
792#ifdef NEOGFX_DEBUG
793 if (debug::layoutItem == this)
794 service<debug::logger>() << neolib::logger::severity::Debug << "widget:layout_items_started()" << endl;
795#endif // NEOGFX_DEBUG
796 ++iLayoutInProgress;
797 }
798
799 template <typename Interface>
801 {
802 return iLayoutInProgress != 0;
803 }
804
805 template <typename Interface>
807 {
808#ifdef NEOGFX_DEBUG
809 if (debug::layoutItem == this)
810 service<debug::logger>() << neolib::logger::severity::Debug << "widget:layout_items_completed()" << endl;
811#endif // NEOGFX_DEBUG
812 if (--iLayoutInProgress == 0)
813 {
814 LayoutCompleted.trigger();
815 update();
816 }
817 }
818
819 template <typename Interface>
821 {
822 return LogicalCoordinateSystem != std::nullopt;
823 }
824
825 template <typename Interface>
827 {
828 if (has_logical_coordinate_system())
829 return *LogicalCoordinateSystem;
831 }
832
833 template <typename Interface>
835 {
836 LogicalCoordinateSystem = aLogicalCoordinateSystem;
837 }
838
839 template <typename Interface>
840 void widget<Interface>::move(const point& aPosition)
841 {
842 auto& self = base_type::as_widget();
843
844#ifdef NEOGFX_DEBUG
845 if (debug::layoutItem == this)
846 service<debug::logger>() << neolib::logger::severity::Debug << "widget<Interface>::move(" << aPosition << ")" << endl;
847#endif // NEOGFX_DEBUG
848 self.set_position(aPosition);
849 }
850
851 template <typename Interface>
853 {
854 auto& self = base_type::as_widget();
855
856 if (!self_type::is_root() || self_type::root().is_nested())
857 {
858 update(true);
859 self.reset_origin();
860 update(true);
861 for (auto& child : iChildren)
862 child->parent_moved();
864 {
865 parent().layout_items_started();
866 parent().layout_items_completed();
868 }
869 }
870 if (self_type::is_root())
871 self_type::root().surface().move_surface(self.position());
872 PositionChanged.trigger();
873 }
874
875 template <typename Interface>
877 {
878 auto& self = base_type::as_widget();
879
880 self.reset_origin();
881 for (auto& child : iChildren)
882 child->parent_moved();
883 ParentPositionChanged.trigger();
884 }
885
886 template <typename Interface>
888 {
889 return iResizing;
890 }
891
892 template <typename Interface>
894 {
895 auto& self = base_type::as_widget();
896
897 neolib::scoped_flag sf{ iResizing };
898
899#ifdef NEOGFX_DEBUG
900 if (debug::layoutItem == this)
901 service<debug::logger>() << neolib::logger::severity::Debug << "widget<Interface>::resize(" << aSize << ")" << endl;
902#endif // NEOGFX_DEBUG
903
904 if (base_type::Size != units_converter{ *this }.to_device_units(aSize))
905 {
906 update(true);
907 self.set_extents(aSize);
908 }
909 }
910
911 template <typename Interface>
913 {
914 auto& self = base_type::as_widget();
915
916 if (self_type::is_root())
917 self_type::root().surface().resize_surface(self.extents());
918
919 update(true);
920
921 SizeChanged.trigger();
922
924
925 layout_items();
926
928 {
929 parent().layout_items_started();
930 parent().layout_items_completed();
932 }
933 }
934
935 template <typename Interface>
937 {
938 auto& self = base_type::as_widget();
939 return rect{self.origin(), self.extents()};
940 }
941
942 template <typename Interface>
943 rect widget<Interface>::client_rect(bool aExtendIntoPadding) const
944 {
945 auto& self = base_type::as_widget();
946 auto const& internalSpacing = self.internal_spacing(!aExtendIntoPadding);
947 auto const& topLeft = internalSpacing.top_left();
948 auto const& extents = self.extents();
949 auto const& adjustedExtents = (extents - internalSpacing.size()).max(size{});
950 rect const result{ topLeft, adjustedExtents };
951 return result;
952 }
953
954 template <typename Interface>
955 const i_widget& widget<Interface>::get_widget_at(const point& aPosition) const
956 {
957 scoped_units su{ *this, units::Pixels };
958
959 if (client_rect().contains(aPosition))
960 {
961 i_widget const* hitWidget = nullptr;
962 for (auto const& child : children())
963 if (child->visible() && to_client_coordinates(child->non_client_rect()).contains(aPosition))
964 {
965 if (hitWidget == nullptr || child->layer() > hitWidget->layer())
966 hitWidget = &*child;
967 }
968 if (hitWidget)
969 return hitWidget->get_widget_at(aPosition - hitWidget->position());
970 }
971 return *this;
972 }
973
974 template <typename Interface>
976 {
977 return const_cast<i_widget&>(to_const(*this).get_widget_at(aPosition));
978 }
979
980 template <typename Interface>
985
986 template <typename Interface>
988 {
989 return true;
990 }
991
992 template <typename Interface>
994 {
995 if (client_rect().contains(aPosition))
996 return widget_part{ *this, widget_part::Client };
997 else if (to_client_coordinates(non_client_rect()).contains(aPosition))
998 return widget_part{ *this, widget_part::NonClient };
999 else
1000 return widget_part{ *this, widget_part::Nowhere };
1001 }
1002
1003 template <typename Interface>
1005 {
1006 return part(aPosition);
1007 }
1008
1009 template <typename Interface>
1011 {
1012 auto& self = base_type::as_widget();
1013
1014#ifdef NEOGFX_DEBUG
1015 if (debug::layoutItem == this)
1016 service<debug::logger>() << neolib::logger::severity::Debug << typeid(*this).name() << "::size_policy()" << endl;
1017#endif // NEOGFX_DEBUG
1018 if (self.has_size_policy())
1019 return base_type::size_policy();
1020 else if (self.has_fixed_size())
1022 else
1024 }
1025
1026 template <typename Interface>
1028 {
1029 auto& self = base_type::as_widget();
1030
1031#ifdef NEOGFX_DEBUG
1032 if (debug::layoutItem == this)
1033 service<debug::logger>() << neolib::logger::severity::Debug << typeid(*this).name() << "::minimum_size(" << aAvailableSpace << ")" << endl;
1034#endif // NEOGFX_DEBUG
1035 size result;
1036 if (self.has_ideal_size() && querying_ideal_size())
1037 result = base_type::ideal_size(aAvailableSpace);
1038 else if (self.has_minimum_size() || (base_type::Anchor_MinimumSize.active() && !base_type::Anchor_MinimumSize.calculating()))
1039 result = base_type::minimum_size(aAvailableSpace);
1040 else if (has_layout())
1041 {
1042 auto const is = self.internal_spacing();
1043 result = layout().minimum_size(aAvailableSpace != std::nullopt ? *aAvailableSpace - is.size() : aAvailableSpace);
1044 if (result.cx != 0.0)
1045 result.cx += is.size().cx;
1046 if (result.cy != 0.0)
1047 result.cy += is.size().cy;
1048 }
1049 else
1050 result = self.internal_spacing().size();
1051#ifdef NEOGFX_DEBUG
1052 if (debug::layoutItem == this)
1053 service<debug::logger>() << neolib::logger::severity::Debug << typeid(*this).name() << "::minimum_size(" << aAvailableSpace << ") --> " << result << endl;
1054#endif // NEOGFX_DEBUG
1055 return result;
1056 }
1057
1058 template <typename Interface>
1060 {
1061 auto& self = base_type::as_widget();
1062
1063#ifdef NEOGFX_DEBUG
1064 if (debug::layoutItem == this)
1065 service<debug::logger>() << neolib::logger::severity::Debug << typeid(*this).name() << "::maximum_size(" << aAvailableSpace << ")" << endl;
1066#endif // NEOGFX_DEBUG
1067 size result;
1068 if (self.has_maximum_size() || (base_type::Anchor_MaximumSize.active() && !base_type::Anchor_MaximumSize.calculating()))
1069 result = base_type::maximum_size(aAvailableSpace);
1070 else if (size_policy() == size_constraint::Minimum)
1071 result = minimum_size(aAvailableSpace);
1072 else if (has_layout())
1073 {
1074 auto const is = self.internal_spacing();
1075 result = layout().maximum_size(aAvailableSpace != std::nullopt ? *aAvailableSpace - is.size() : aAvailableSpace);
1076 if (result.cx != 0.0)
1077 result.cx += is.size().cx;
1078 if (result.cy != 0.0)
1079 result.cy += is.size().cy;
1080 }
1081 else
1082 result = size::max_size();
1083 if (size_policy().horizontal_constraint() == size_constraint::Maximum)
1084 result.cx = size::max_size().cx;
1085 if (size_policy().vertical_constraint() == size_constraint::Maximum)
1086 result.cy = size::max_size().cy;
1087#ifdef NEOGFX_DEBUG
1088 if (debug::layoutItem == this)
1089 service<debug::logger>() << neolib::logger::severity::Debug << typeid(*this).name() << "::maximum_size(" << aAvailableSpace << ") --> " << result << endl;
1090#endif // NEOGFX_DEBUG
1091 return result;
1092 }
1093
1094 template <typename Interface>
1096 {
1097 auto& self = base_type::as_widget();
1098 auto const& adjustedPadding = (self.has_padding() ?
1099 *base_type::Padding : service<i_app>().current_style().padding(self_type::is_root() ? padding_role::Window : padding_role::Widget));
1100 return self.transformation() * units_converter{ *this }.from_device_units(adjustedPadding);
1101 }
1102
1103 template <typename Interface>
1104 void widget<Interface>::layout_as(const point& aPosition, const size& aSize)
1105 {
1106 auto& self = base_type::as_widget();
1107
1108#ifdef NEOGFX_DEBUG
1109 if (debug::layoutItem == this)
1110 service<debug::logger>() << neolib::logger::severity::Debug << typeid(*this).name() << "::layout_as(" << aPosition << ", " << aSize << ")" << endl;
1111#endif // NEOGFX_DEBUG
1112 move(aPosition);
1113 if (self.extents() != aSize)
1114 resize(aSize);
1115 else if (has_layout() && layout().invalidated())
1116 {
1118 layout_items();
1119 }
1120 }
1121
1122 template <typename Interface>
1124 {
1125 return iView;
1126 }
1127
1128 template <typename Interface>
1130 {
1131 return iView;
1132 }
1133
1134 template <typename Interface>
1136 {
1137 if (iRenderLayer != std::nullopt)
1138 return *iRenderLayer;
1139 return layer();
1140 }
1141
1142 template <typename Interface>
1143 void widget<Interface>::set_render_layer(const std::optional<layer_t>& aLayer)
1144 {
1145 if (iRenderLayer != aLayer)
1146 {
1147 iRenderLayer = aLayer;
1148 update(true);
1149 }
1150 }
1151
1152 template <typename Interface>
1154 {
1155 return self_type::has_root() && self_type::root().has_native_surface() && !effectively_hidden() && !layout_items_in_progress();
1156 }
1157
1158 template <typename Interface>
1159 bool widget<Interface>::update(bool aIncludeNonClient)
1160 {
1161 if (!can_update())
1162 return false;
1163 return update(aIncludeNonClient ? to_client_coordinates(non_client_rect()) : client_rect());
1164 }
1165
1166 template <typename Interface>
1167 bool widget<Interface>::update(const rect& aUpdateRect)
1168 {
1169#ifdef NEOGFX_DEBUG
1170 if (debug::renderItem == this)
1171 service<debug::logger>() << neolib::logger::severity::Debug << typeid(*this).name() << "::update(" << aUpdateRect << ")" << endl;
1172#endif // NEOGFX_DEBUG
1173 if (!can_update())
1174 return false;
1175 if (aUpdateRect.empty())
1176 return false;
1177 surface().invalidate_surface(to_window_coordinates(aUpdateRect));
1178 return true;
1179 }
1180
1181 template <typename Interface>
1183 {
1184 return surface().has_invalidated_area() && !surface().invalidated_area().intersection(non_client_rect()).empty();
1185 }
1186
1187 template <typename Interface>
1189 {
1190 if (!requires_update())
1191 throw no_update_rect();
1192 return to_client_coordinates(surface().invalidated_area().intersection(non_client_rect()));
1193 }
1194
1195 template <typename Interface>
1196 rect widget<Interface>::default_clip_rect(bool aIncludeNonClient) const
1197 {
1198 auto& cachedRect = (aIncludeNonClient ? iDefaultClipRect.first : iDefaultClipRect.second);
1199 if (cachedRect != std::nullopt)
1200 return *cachedRect;
1201 rect clipRect = to_client_coordinates(non_client_rect());
1202 if (!aIncludeNonClient)
1203 clipRect = clipRect.intersection(client_rect());
1204 if (!self_type::is_root())
1205 clipRect = clipRect.intersection(to_client_coordinates(parent().to_window_coordinates(parent().default_clip_rect((widget_type() & neogfx::widget_type::NonClient) == neogfx::widget_type::NonClient))));
1206 return *(cachedRect = clipRect);
1207 }
1208
1209 template <typename Interface>
1211 {
1212 return !iLayoutPending;
1213 }
1214
1215 template <typename Interface>
1217 {
1218 auto& self = base_type::as_widget();
1219
1220 if (effectively_hidden())
1221 return;
1222 if (!requires_update())
1223 return;
1224
1225 scoped_units su{ *this, units::Pixels };
1226
1227 iDefaultClipRect = std::make_pair(std::nullopt, std::nullopt);
1228
1229 const rect updateRect = update_rect();
1230 const rect nonClientClipRect = default_clip_rect(true).intersection(updateRect);
1231
1232#ifdef NEOGFX_DEBUG
1233 if (debug::renderItem == this)
1234 service<debug::logger>() << neolib::logger::severity::Debug << typeid(*this).name() << "::render(...), updateRect: " << updateRect << ", nonClientClipRect: " << nonClientClipRect << endl;
1235#endif // NEOGFX_DEBUG
1236
1237 aGc.set_extents(self.extents());
1238 aGc.set_origin(self.origin());
1239
1240 aGc.set_default_font(font());
1241
1242 scoped_snap_to_pixel snap{ aGc };
1243 scoped_opacity sc{ aGc, effectively_enabled() ? opacity() : opacity() * 0.75 };
1244
1245 {
1246 scoped_scissor scissor(aGc, nonClientClipRect);
1247 paint_non_client(aGc);
1248
1249 for (auto iterChild = iChildren.rbegin(); iterChild != iChildren.rend(); ++iterChild)
1250 {
1251 auto const& childWidget = **iterChild;
1252 if ((childWidget.widget_type() & neogfx::widget_type::Client) == neogfx::widget_type::Client)
1253 continue;
1254 rect intersection = nonClientClipRect.intersection(childWidget.non_client_rect() - self.origin());
1255 if (intersection.empty() && !childWidget.is_root())
1256 continue;
1257 childWidget.render(aGc);
1258 }
1259 }
1260
1261 {
1262 const rect clipRect = default_clip_rect().intersection(updateRect);
1263
1264 aGc.set_extents(client_rect().extents());
1265 aGc.set_origin(self.origin());
1266
1267#ifdef NEOGFX_DEBUG
1268 if (debug::renderItem == this)
1269 service<debug::logger>() << neolib::logger::severity::Debug << typeid(*this).name() << "::render(...): client_rect: " << client_rect() << ", origin: " << self.origin() << endl;
1270#endif // NEOGFX_DEBUG
1271
1272 scoped_scissor scissor(aGc, clipRect);
1273
1274 scoped_coordinate_system scs1(aGc, self.origin(), self.extents(), logical_coordinate_system());
1275
1276 Painting.trigger(aGc);
1277
1278 paint(aGc);
1279
1280 scoped_coordinate_system scs2(aGc, self.origin(), self.extents(), logical_coordinate_system());
1281
1282 PaintingChildren.trigger(aGc);
1283
1284 typedef std::map<int32_t, std::vector<i_widget const*>> widget_layers_t;
1285 shared_thread_local(std::vector<std::unique_ptr<widget_layers_t>>, neogfx::widget::render, widgetLayersStack);
1286
1287 shared_thread_local(std::size_t, neogfx::widget::render, stack);
1288 neolib::scoped_counter<std::size_t> stackCounter{ stack };
1289 if (widgetLayersStack.size() < stack)
1290 widgetLayersStack.push_back(std::make_unique<widget_layers_t>());
1291
1292 widget_layers_t& widgetLayers = *widgetLayersStack[stack - 1];
1293
1294 for (auto& layer : widgetLayers)
1295 layer.second.clear();
1296
1297 for (auto iterChild = iChildren.rbegin(); iterChild != iChildren.rend(); ++iterChild)
1298 {
1299 auto const& childWidget = **iterChild;
1300 if ((childWidget.widget_type() & neogfx::widget_type::NonClient) == neogfx::widget_type::NonClient)
1301 continue;
1302 rect intersection = clipRect.intersection(to_client_coordinates(childWidget.non_client_rect()));
1303 if (intersection.empty() && !childWidget.is_root())
1304 continue;
1305 widgetLayers[childWidget.render_layer()].push_back(&childWidget);
1306 }
1307
1308 for (auto const& layer : widgetLayers)
1309 {
1310 for (auto const& childWidgetPtr : layer.second)
1311 {
1312 auto const& childWidget = *childWidgetPtr;
1313 childWidget.render(aGc);
1314 }
1315 }
1316
1317 aGc.set_extents(client_rect().extents());
1318 aGc.set_origin(self.origin());
1319
1320 scoped_coordinate_system scs3(aGc, self.origin(), self.extents(), logical_coordinate_system());
1321
1322 Painted.trigger(aGc);
1323 }
1324
1325 aGc.set_extents(self.extents());
1326 aGc.set_origin(self.origin());
1327 {
1328 scoped_scissor scissor(aGc, nonClientClipRect);
1329 paint_non_client_after(aGc);
1330 }
1331 }
1332
1333 template <typename Interface>
1335 {
1336 auto& self = base_type::as_widget();
1337
1338 auto const& updateRect = update_rect();
1339
1340#ifdef NEOGFX_DEBUG
1341 if (debug::renderItem == this)
1342 {
1343 aGc.flush();
1344 service<debug::logger>() << neolib::logger::severity::Debug << typeid(*this).name() << "::paint_non_client(...), updateRect: " << updateRect << endl;
1345 }
1346#endif // NEOGFX_DEBUG
1347
1348 if (self.has_background_color() || !self.background_is_transparent())
1349 aGc.fill_rect(updateRect, self.background_color().with_combined_alpha(has_background_opacity() ? background_opacity() : 1.0));
1350 }
1351
1352 template <typename Interface>
1354 {
1355 auto& self = base_type::as_widget();
1356
1357#ifdef NEOGFX_DEBUG
1358 // todo: move to debug::layoutItem function/service
1359 if (debug::renderItem == this || debug::layoutItem == this || (debug::layoutItem != nullptr && has_layout() && debug::layoutItem->is_layout() &&
1360 (debug::layoutItem == &layout() || static_cast<i_layout const*>(debug::layoutItem)->is_descendent_of(layout()))))
1361 {
1362 neogfx::font debugFont1 = service<i_app>().current_style().font().with_size(16);
1363 neogfx::font debugFont2 = service<i_app>().current_style().font().with_size(8);
1364 if (debug::renderGeometryText)
1365 {
1366 if (debug::layoutItem == this)
1367 {
1368 aGc.draw_text(self.position(), typeid(*this).name(), debugFont1, text_format{ color::Yellow.with_alpha(0.75), text_effect{ text_effect_type::Outline, color::Black.with_alpha(0.75), 2.0 } });
1369 std::ostringstream oss;
1370 oss << "sizepol: " << size_policy();
1371 oss << " minsize: " << minimum_size() << " maxsize: " << maximum_size();
1372 oss << " fixsize: " << (self.has_fixed_size() ? self.fixed_size() : optional_size{}) << " weight: " << self.weight() << " extents: " << self.extents();
1373 aGc.draw_text(self.position() + size{ 0.0, debugFont1.height() }, oss.str(), debugFont2, text_format{ color::Orange.with_alpha(0.75), text_effect{ text_effect_type::Outline, color::Black.with_alpha(0.75), 2.0 } });
1374 }
1375 }
1376 rect const nonClientRect = to_client_coordinates(non_client_rect());
1377 aGc.draw_rect(nonClientRect, pen{ color::White, 3.0 });
1378 aGc.line_stipple_on(1.0, 0x5555);
1379 aGc.draw_rect(nonClientRect, pen{ color::Green, 3.0 });
1380 aGc.line_stipple_off();
1381 if (nonClientRect != client_rect(false))
1382 {
1383 aGc.draw_rect(client_rect(false), pen{ color::White, 1.0 });
1384 aGc.line_stipple_on(1.0, 0x5555);
1385 aGc.draw_rect(client_rect(false), pen{ color::Red, 1.0 });
1386 aGc.line_stipple_off();
1387 }
1388 if (debug::layoutItem != nullptr && (debug::layoutItem != this || has_layout()))
1389 {
1390 i_layout const& debugLayout = (debug::layoutItem == this ? layout() : *static_cast<i_layout const*>(debug::layoutItem));
1391 if (debug::renderGeometryText)
1392 {
1393 if (debug::layoutItem != this)
1394 {
1395 aGc.draw_text(debugLayout.position(), typeid(debugLayout).name(), debugFont1, text_format{ color::Yellow.with_alpha(0.75), text_effect{ text_effect_type::Outline, color::Black.with_alpha(0.75), 2.0 } });
1396 std::ostringstream oss;
1397 oss << "sizepol: " << debugLayout.size_policy();
1398 oss << " minsize: " << debugLayout.minimum_size() << " maxsize: " << debugLayout.maximum_size();
1399 oss << " fixsize: " << (debugLayout.has_fixed_size() ? debugLayout.fixed_size() : optional_size{}) << " weight: " << debugLayout.weight() << " extents: " << debugLayout.extents();
1400 aGc.draw_text(debugLayout.position() + size{ 0.0, debugFont1.height() }, oss.str(), debugFont2, text_format{ color::Orange.with_alpha(0.75), text_effect{ text_effect_type::Outline, color::Black.with_alpha(0.75), 2.0 } });
1401 }
1402 }
1403 for (layout_item_index itemIndex = 0; itemIndex < debugLayout.count(); ++itemIndex)
1404 {
1405 auto const& item = debugLayout.item_at(itemIndex);
1406 if (debug::renderGeometryText)
1407 {
1408 std::string text = typeid(item).name();
1409 auto* l = &item;
1410 while (l->has_parent_layout())
1411 {
1412 l = &l->parent_layout();
1413 text = typeid(*l).name() + " > "_s + text;
1414 }
1415 aGc.draw_text(item.position(), text, debugFont2, text_format{ color::White.with_alpha(0.5), text_effect{ text_effect_type::Outline, color::Black.with_alpha(0.5), 2.0 } });
1416 }
1417 rect const itemRect{ item.position(), item.extents() };
1418 aGc.draw_rect(itemRect, color::White.with_alpha(0.5));
1419 aGc.line_stipple_on(1.0, 0x5555);
1420 aGc.draw_rect(itemRect, color::Black.with_alpha(0.5));
1421 aGc.line_stipple_off();
1422 }
1423 rect const layoutRect{ debugLayout.position(), debugLayout.extents() };
1424 aGc.draw_rect(layoutRect, color::White);
1425 aGc.line_stipple_on(1.0, 0x5555);
1426 aGc.draw_rect(layoutRect, debug::layoutItem == &layout() ? color::Blue : color::Purple);
1427 aGc.line_stipple_off();
1428 }
1429 }
1430#endif // NEOGFX_DEBUG
1431 }
1432
1433 template <typename Interface>
1435 {
1436 // do nothing
1437 }
1438
1439 template <typename Interface>
1441 {
1442 return Opacity;
1443 }
1444
1445 template <typename Interface>
1447 {
1448 if (Opacity != aOpacity)
1449 {
1450 Opacity = aOpacity;
1451 update(true);
1452 }
1453 }
1454
1455 template <typename Interface>
1457 {
1458 return BackgroundOpacity != std::nullopt;
1459 }
1460
1461 template <typename Interface>
1463 {
1464 if (has_background_opacity())
1465 return *BackgroundOpacity.value();
1466 return 0.0;
1467 }
1468
1469 template <typename Interface>
1471 {
1472 if (BackgroundOpacity != aOpacity)
1473 {
1474 BackgroundOpacity = aOpacity;
1475 update(true);
1476 }
1477 }
1478
1479 template <typename Interface>
1481 {
1482 return Palette != std::nullopt;
1483 }
1484
1485 template <typename Interface>
1487 {
1488 if (has_palette())
1489 return *Palette.value();
1490 return service<i_app>().current_style().palette();
1491 }
1492
1493 template <typename Interface>
1495 {
1496 if (Palette != aPalette)
1497 {
1498 Palette = aPalette;
1499 update(true);
1500 }
1501 }
1502
1503 template <typename Interface>
1505 {
1506 return has_palette() && palette().has_color(aColorRole);
1507 }
1508
1509 template <typename Interface>
1511 {
1512 return palette().color(aColorRole);
1513 }
1514
1515 template <typename Interface>
1517 {
1518 if (Palette == std::nullopt)
1520 if (palette_color(aColorRole) != aColor)
1521 {
1522 auto existing = neogfx::palette{ palette() }; // todo: support indirectly changing and notifying a property so we don't have to make a copy?
1523 existing.set_color(aColorRole, aColor);
1524 Palette = existing;
1525 update(true);
1526 }
1527 }
1528
1529 template <typename Interface>
1531 {
1532 const i_widget* w = this;
1533 while (w->background_is_transparent() && w->has_parent())
1534 w = &w->parent();
1536 return w->background_color();
1537 else
1538 return service<i_app>().current_style().palette().color(color_role::Theme);
1539 }
1540
1541 template <typename Interface>
1543 {
1544 return FontRole != std::nullopt;
1545 }
1546
1547 template <typename Interface>
1549 {
1550 if (has_font_role())
1551 return *FontRole.value();
1553 }
1554
1555 template <typename Interface>
1557 {
1558 if (FontRole != aFontRole)
1559 {
1560 FontRole = aFontRole;
1561 update(true);
1562 }
1563 }
1564
1565 template <typename Interface>
1567 {
1568 return Font != std::nullopt;
1569 }
1570
1571 template <typename Interface>
1572 const font& widget<Interface>::font() const
1573 {
1574 if (has_font())
1575 return *Font;
1576 else
1577 return service<i_app>().current_style().font(font_role());
1578 }
1579
1580 template <typename Interface>
1581 void widget<Interface>::set_font(optional_font const& aFont)
1582 {
1583 auto& self = base_type::as_widget();
1584
1585 if (Font != aFont)
1586 {
1587 Font = aFont;
1588 self.update_layout();
1589 update(true);
1590 }
1591 }
1592
1593 template <typename Interface>
1595 {
1596 return Visible && (base_type::MaximumSize == std::nullopt || (base_type::MaximumSize->cx != 0.0 && base_type::MaximumSize->cy != 0.0));
1597 }
1598
1599 template <typename Interface>
1601 {
1602 return visible() && (self_type::is_root() || !has_parent() || parent().effectively_visible());
1603 }
1604
1605 template <typename Interface>
1607 {
1608 return !visible();
1609 }
1610
1611 template <typename Interface>
1613 {
1614 return !effectively_visible();
1615 }
1616
1617 template <typename Interface>
1618 bool widget<Interface>::show(bool aVisible)
1619 {
1620 if (Visible != aVisible)
1621 {
1622 bool isEntered = entered();
1623 Visible = aVisible;
1624 if (!visible() && isEntered)
1625 {
1626 if (!self_type::is_root())
1627 self_type::root().as_widget().mouse_entered(self_type::root().mouse_position());
1628 else
1629 mouse_left();
1630 }
1631 VisibilityChanged.trigger();
1632 if (effectively_hidden())
1633 release_focus();
1634 base_type::as_widget().update_layout(true, true);
1635 return true;
1636 }
1637 return false;
1638 }
1639
1640 template <typename Interface>
1642 {
1643 return Enabled;
1644 }
1645
1646 template <typename Interface>
1648 {
1649 return enabled() && (self_type::is_root() || !has_parent() || parent().effectively_enabled());
1650 }
1651
1652 template <typename Interface>
1654 {
1655 return !enabled();
1656 }
1657
1658 template <typename Interface>
1660 {
1661 return !effectively_enabled();
1662 }
1663
1664 template <typename Interface>
1665 bool widget<Interface>::enable(bool aEnable)
1666 {
1667 if (Enabled != aEnable)
1668 {
1669 bool isEntered = entered();
1670 Enabled = aEnable;
1671 if (!enabled() && isEntered)
1672 {
1673 if (!self_type::is_root())
1674 self_type::root().as_widget().mouse_entered(self_type::root().mouse_position());
1675 else
1676 mouse_left();
1677 }
1678 update(true);
1679 return true;
1680 }
1681 return false;
1682 }
1683
1684 template <typename Interface>
1685 bool widget<Interface>::entered(bool aChildEntered) const
1686 {
1687 return self_type::has_root() && self_type::root().has_entered_widget() && (&self_type::root().entered_widget() == this || (aChildEntered && self_type::root().entered_widget().is_descendent_of(*this)));
1688 }
1689
1690 template <typename Interface>
1692 {
1693 return true;
1694 }
1695
1696 template <typename Interface>
1698 {
1699 return surface().as_surface_window().has_capturing_widget() && &surface().as_surface_window().capturing_widget() == this;
1700 }
1701
1702 template <typename Interface>
1704 {
1705 return iCapturePosition;
1706 }
1707
1708 template <typename Interface>
1709 void widget<Interface>::set_capture(capture_reason aReason, const optional_point& aPosition)
1710 {
1711 auto& self = base_type::as_widget();
1712
1713 if (can_capture())
1714 {
1715 switch (aReason)
1716 {
1717 case capture_reason::MouseEvent:
1718 if (!self.mouse_event_is_non_client())
1719 surface().as_surface_window().set_capture(*this);
1720 else
1721 surface().as_surface_window().non_client_set_capture(*this);
1722 break;
1723 default:
1724 surface().as_surface_window().set_capture(*this);
1725 break;
1726 }
1727 iCapturePosition = aPosition;
1728 }
1729 else
1730 throw widget_cannot_capture();
1731 }
1732
1733 template <typename Interface>
1734 void widget<Interface>::release_capture(capture_reason aReason)
1735 {
1736 auto& self = base_type::as_widget();
1737
1738 switch (aReason)
1739 {
1740 case capture_reason::MouseEvent:
1741 if (!self.mouse_event_is_non_client())
1742 surface().as_surface_window().release_capture(*this);
1743 else
1744 surface().as_surface_window().non_client_release_capture(*this);
1745 break;
1746 default:
1747 surface().as_surface_window().release_capture(*this);
1748 break;
1749 }
1750 iCapturePosition = std::nullopt;
1751 }
1752
1753 template <typename Interface>
1755 {
1756 if (can_capture())
1757 surface().as_surface_window().non_client_set_capture(*this);
1758 else
1759 throw widget_cannot_capture();
1760 }
1761
1762 template <typename Interface>
1764 {
1765 surface().as_surface_window().non_client_release_capture(*this);
1766 }
1767
1768 template <typename Interface>
1770 {
1771 }
1772
1773 template <typename Interface>
1777
1778 template <typename Interface>
1780 {
1781 return FocusPolicy != std::nullopt;
1782 }
1783
1784 template <typename Interface>
1786 {
1787 if (has_focus_policy())
1788 return *FocusPolicy;
1790 }
1791
1792 template <typename Interface>
1794 {
1795 FocusPolicy = aFocusPolicy;
1796 }
1797
1798 template <typename Interface>
1799 bool widget<Interface>::can_set_focus(focus_reason aFocusReason) const
1800 {
1801 if (effectively_hidden() || effectively_disabled())
1802 return false;
1803 switch (aFocusReason)
1804 {
1805 case focus_reason::ClickNonClient:
1807 case focus_reason::ClickClient:
1809 case focus_reason::Tab:
1811 case focus_reason::Wheel:
1813 case focus_reason::Pointer:
1815 case focus_reason::WindowActivation:
1816 case focus_reason::Other:
1817 default:
1819 }
1820 }
1821
1822 template <typename Interface>
1824 {
1825 return self_type::has_root() && self_type::root().is_active() && self_type::root().has_focused_widget() && &self_type::root().focused_widget() == this;
1826 }
1827
1828 template <typename Interface>
1830 {
1831 return self_type::has_root() && self_type::root().is_active() && self_type::root().has_focused_widget() && self_type::root().focused_widget().is_descendent_of(*this);
1832 }
1833
1834 template <typename Interface>
1835 bool widget<Interface>::set_focus(focus_reason aFocusReason)
1836 {
1837 if (!has_focus())
1838 {
1839 self_type::root().set_focused_widget(*this, aFocusReason);
1840 return true;
1841 }
1842 return false;
1843 }
1844
1845 template <typename Interface>
1847 {
1848 if (has_focus() || child_has_focus())
1849 {
1850 self_type::root().release_focused_widget(self_type::root().focused_widget());
1851 return true;
1852 }
1853 return false;
1854 }
1855
1856 template <typename Interface>
1857 void widget<Interface>::focus_gained(focus_reason aReason)
1858 {
1859 update(true);
1860 Focus.trigger(focus_event::FocusGained, aReason);
1861 }
1862
1863 template <typename Interface>
1864 void widget<Interface>::focus_lost(focus_reason aReason)
1865 {
1866 update(true);
1867 Focus.trigger(focus_event::FocusLost, aReason);
1868 }
1869
1870 template <typename Interface>
1872 {
1873 return ConsiderAncestorsForMouseEvents;
1874 }
1875
1876 template <typename Interface>
1878 {
1879 ConsiderAncestorsForMouseEvents = aConsiderAncestors;
1880 }
1881
1882 template <typename Interface>
1883 bool widget<Interface>::ignore_mouse_events(bool aConsiderAncestors) const
1884 {
1885 return IgnoreMouseEvents || (aConsiderAncestors && consider_ancestors_for_mouse_events() &&
1886 has_parent() && parent().ignore_mouse_events());
1887 }
1888
1889 template <typename Interface>
1891 {
1892 IgnoreMouseEvents = aIgnoreMouseEvents;
1893 }
1894
1895 template <typename Interface>
1896 bool widget<Interface>::ignore_non_client_mouse_events(bool aConsiderAncestors) const
1897 {
1898 return IgnoreNonClientMouseEvents || (aConsiderAncestors && consider_ancestors_for_mouse_events() &&
1899 has_parent() && parent().ignore_non_client_mouse_events());
1900 }
1901
1902 template <typename Interface>
1903 void widget<Interface>::set_ignore_non_client_mouse_events(bool aIgnoreNonClientMouseEvents)
1904 {
1905 IgnoreNonClientMouseEvents = aIgnoreNonClientMouseEvents;
1906 }
1907
1908 template <typename Interface>
1910 {
1911 if (self_type::has_root() && self_type::root().has_native_surface())
1912 return surface().as_surface_window().current_mouse_event_location();
1913 else
1915 }
1916
1917 template <typename Interface>
1918 bool widget<Interface>::mouse_wheel_scrolled(mouse_wheel aWheel, const point& aPosition, delta aDelta, key_modifiers_e aKeyModifiers)
1919 {
1920 auto& self = base_type::as_widget();
1921
1922 if (has_parent() && same_surface(parent()))
1923 return parent().mouse_wheel_scrolled(aWheel, aPosition + self.position(), aDelta, aKeyModifiers);
1924
1925 return false;
1926 }
1927
1928 template <typename Interface>
1929 void widget<Interface>::mouse_button_pressed(mouse_button aButton, const point& aPosition, key_modifiers_e aKeyModifiers)
1930 {
1931 auto& self = base_type::as_widget();
1932
1933 if (aButton == mouse_button::Middle && has_parent())
1934 parent().mouse_button_pressed(aButton, aPosition + self.position(), aKeyModifiers);
1935 else if (aButton == mouse_button::Left && capture_ok(hit_test(aPosition)) && can_capture())
1936 set_capture(capture_reason::MouseEvent, aPosition);
1937 }
1938
1939 template <typename Interface>
1941 {
1942 auto& self = base_type::as_widget();
1943
1944 if (aButton == mouse_button::Middle && has_parent())
1945 parent().mouse_button_double_clicked(aButton, aPosition + self.position(), aKeyModifiers);
1946 else if (aButton == mouse_button::Left && capture_ok(hit_test(aPosition)) && can_capture())
1947 set_capture(capture_reason::MouseEvent, aPosition);
1948 }
1949
1950 template <typename Interface>
1952 {
1953 auto& self = base_type::as_widget();
1954
1955 if (aButton == mouse_button::Middle && has_parent())
1956 parent().mouse_button_released(aButton, aPosition + self.position());
1957 else if (capturing())
1958 release_capture(capture_reason::MouseEvent);
1959 }
1960
1961 template <typename Interface>
1963 {
1964 // do nothing
1965 }
1966
1967 template <typename Interface>
1969 {
1970 // do nothing
1971 }
1972
1973 template <typename Interface>
1975 {
1976 // do nothing
1977 }
1978
1979 template <typename Interface>
1981 {
1982 auto& self = base_type::as_widget();
1983
1984 auto const rootMousePosition = self_type::root().mouse_position();
1985 if (self_type::is_root())
1986 return rootMousePosition;
1987 else
1988 return rootMousePosition + self_type::root().origin() - self.origin();
1989 }
1990
1991 template <typename Interface>
1993 {
1994 std::optional<neogfx::mouse_cursor> mouseCursor;
1995 auto const mousePosition = mouse_position();
1996 auto const partUnderMouse = part(mousePosition);
1997 if (part_active(partUnderMouse))
1998 {
1999 switch (partUnderMouse.part)
2000 {
2001 case widget_part::BorderLeft:
2002 mouseCursor = mouse_system_cursor::SizeWE;
2003 break;
2004 case widget_part::BorderTopLeft:
2005 mouseCursor = mouse_system_cursor::SizeNWSE;
2006 break;
2007 case widget_part::BorderTop:
2008 mouseCursor = mouse_system_cursor::SizeNS;
2009 break;
2010 case widget_part::BorderTopRight:
2011 mouseCursor = mouse_system_cursor::SizeNESW;
2012 break;
2013 case widget_part::BorderRight:
2014 mouseCursor = mouse_system_cursor::SizeWE;
2015 break;
2016 case widget_part::BorderBottomRight:
2017 mouseCursor = mouse_system_cursor::SizeNWSE;
2018 break;
2019 case widget_part::BorderBottom:
2020 mouseCursor = mouse_system_cursor::SizeNS;
2021 break;
2022 case widget_part::BorderBottomLeft:
2023 mouseCursor = mouse_system_cursor::SizeNESW;
2024 break;
2025 case widget_part::GrowBox:
2026 mouseCursor = mouse_system_cursor::SizeNWSE;
2027 break;
2028 case widget_part::VerticalScrollbar:
2029 mouseCursor = mouse_system_cursor::Arrow;
2030 break;
2031 case widget_part::HorizontalScrollbar:
2032 mouseCursor = mouse_system_cursor::Arrow;
2033 break;
2034 }
2035 }
2036 if (!mouseCursor && has_parent())
2037 mouseCursor = parent().mouse_cursor();
2038 if (!mouseCursor)
2039 mouseCursor = mouse_system_cursor::Arrow;
2040 QueryMouseCursor.trigger(*mouseCursor);
2041 return mouseCursor.value();
2042 }
2043
2044 template <typename Interface>
2049
2050 template <typename Interface>
2055
2056 template <typename Interface>
2058 {
2059 return false;
2060 }
2061
2062 template <typename Interface>
2064 {
2065 return false;
2066 }
2067
2068 template <typename Interface>
2069 const i_widget& widget<Interface>::widget_for_mouse_event(const point& aPosition, bool aForHitTest) const
2070 {
2071 auto& self = base_type::as_widget();
2072
2073 scoped_units su{ *this, units::Pixels };
2074
2075 auto const clientPosition = aPosition - self.origin();
2076 const i_widget* result = nullptr;
2077 if (self_type::is_root() && (self_type::root().style() & window_style::Resize) == window_style::Resize)
2078 {
2079 auto const outerRect = to_client_coordinates(non_client_rect());
2080 auto const innerRect = outerRect.deflated(self_type::root().window_border());
2081 if (outerRect.contains(clientPosition) && !innerRect.contains(clientPosition))
2082 result = this;
2083 }
2084 if (!result && non_client_rect().contains(aPosition))
2085 {
2086 auto const location = mouse_event_location();
2087 const i_widget* w = &get_widget_at(clientPosition);
2088 for (; w != this ; w = &w->parent())
2089 {
2090 auto const widgetClientPosition = aPosition - w->origin();
2091 if (w->effectively_hidden() || (w->effectively_disabled() && !aForHitTest))
2092 continue;
2093 switch (w->part(widgetClientPosition).part)
2094 {
2095 case widget_part::Nowhere:
2096 continue;
2097 case widget_part::TitleBar:
2098 case widget_part::BorderBottomRight:
2099 if (w->root().is_nested())
2100 return w->root();
2101 break;
2102 default:
2103 break;
2104 }
2106 break;
2108 break;
2111 w->non_client_rect().deflated(w->internal_spacing()).contains(aPosition) &&
2112 !w->client_rect(false).contains(widgetClientPosition))
2113 break;
2114 }
2115 result = w;
2116 }
2117 else if (!result)
2118 result = this;
2119 return *result;
2120 }
2121
2122 template <typename Interface>
2123 i_widget& widget<Interface>::widget_for_mouse_event(const point& aPosition, bool aForHitTest)
2124 {
2125 return const_cast<i_widget&>(to_const(*this).widget_for_mouse_event(aPosition, aForHitTest));
2126 }
2127}
2128
self_type intersection(const self_type &other) const
const point_type & position() const
self_type deflated(Args &&... aArgs) const
bool contains(const point_type &point) const
const size_type & extents() const
return_type with_alpha(view_component aAlpha) const
Definition color.hpp:154
static constexpr basic_size max_size()
dimension_type cy
dimension_type cx
virtual void line_stipple_off() const =0
virtual point origin() const =0
virtual void flush() const =0
virtual void draw_text(const point &aPoint, std::string const &aText, const text_format &aTextFormat) const =0
virtual void line_stipple_on(scalar aFactor, uint16_t aPattern, scalar aPosition=0.0) const =0
void fill_rect(const rect &aRect, const brush &aFill) const
virtual void set_origin(const point &aOrigin) const =0
virtual void set_extents(const size &aExtents) const =0
virtual void draw_rect(const rect &aRect, const pen &aPen, const brush &aFill=brush{}) const =0
virtual void set_default_font(const font &aDefaultFont) const =0
virtual const i_layout & parent_layout() const =0
virtual layout_item_index count() const =0
virtual i_layout_item & add(i_layout_item &aItem)=0
virtual const i_layout_item & item_at(layout_item_index aIndex) const =0
virtual const std::type_info & category() const =0
virtual bool ignore_non_client_mouse_events(bool aConsiderAncestors=true) const =0
virtual const i_widget & get_widget_at(const point &aPosition) const =0
virtual bool effectively_hidden() const =0
virtual rect non_client_rect() const =0
virtual bool has_parent() const =0
virtual bool has_root() const =0
virtual const i_window & root() const =0
virtual widget_list::const_iterator find(const i_widget &aChild, bool aThrowIfNotFound=true) const =0
color background_color() const
Definition i_widget.hpp:382
virtual widget_part part(const point &aPosition) const =0
virtual rect client_rect(bool aExtendIntoPadding=true) const =0
virtual void remove(i_widget &aChild, bool aSingular, i_ref_ptr< i_widget > &aChildRef)=0
virtual const i_widget & parent() const =0
virtual bool effectively_disabled() const =0
virtual bool ignore_mouse_events(bool aConsiderAncestors=true) const =0
virtual i_widget & add(i_widget &aChild)=0
bool background_is_transparent() const
Definition i_widget.hpp:367
bool has_background_color() const
Definition i_widget.hpp:378
virtual void link_after(i_widget *aNextWidget)=0
virtual bool adding_child() const =0
virtual bool is_root() const =0
virtual layer_t layer() const =0
virtual bool has_surface() const =0
virtual bool is_nested() const =0
size maximum_size(optional_size const &aAvailableSpace={}) const override
size minimum_size(optional_size const &aAvailableSpace={}) const override
void set_parent_layout(i_layout *aParentLayout) final
bool remove(i_layout_item &aItem) override
neogfx::color color(color_role aRole) const override
bool has_color(color_role aRole) const override
void set_color(color_role aRole, const optional_color &aColor) override
static const sRGB_color Yellow
Definition color.hpp:1034
static const sRGB_color Orange
Definition color.hpp:878
static const sRGB_color White
Definition color.hpp:1032
static const sRGB_color Red
Definition color.hpp:932
static const sRGB_color Blue
Definition color.hpp:405
static const sRGB_color Purple
Definition color.hpp:927
static const sRGB_color Black
Definition color.hpp:403
static const sRGB_color Green
Definition color.hpp:634
vector2 to_device_units(const vector2 &aValue) const
vector2 from_device_units(const vector2 &aValue) const
neogfx::size_policy size_policy() const override
Definition widget.ipp:1010
const i_widget & after() const override
Definition widget.ipp:512
bool has_children() const override
Definition widget.ipp:383
bool has_surface() const override
Definition widget.ipp:229
void paint_non_client_after(i_graphics_context &aGc) const override
Definition widget.ipp:1353
bool has_logical_coordinate_system() const override
Definition widget.ipp:820
void move(const point &aPosition) override
Definition widget.ipp:840
const i_palette & palette() const override
Definition widget.ipp:1486
void resized() override
Definition widget.ipp:912
bool device_metrics_available() const override
Definition widget.ipp:140
void parent_moved() override
Definition widget.ipp:876
void moved() override
Definition widget.ipp:852
void link_before(i_widget *aPreviousWidget) override
Definition widget.ipp:550
void set_root(i_window &aRoot) final
Definition widget.ipp:223
void bring_child_to_front(const i_widget &aChild) override
Definition widget.ipp:447
void set_parent_widget(i_widget *aParentWidget) final
Definition widget.ipp:691
void set_singular(bool aSingular) final
Definition widget.ipp:168
widget_list::const_iterator find(const i_widget &aChild, bool aThrowIfNotFound=true) const override
Definition widget.ipp:423
bool can_defer_layout() const override
Definition widget.ipp:627
const widget_list & children() const override
Definition widget.ipp:389
const i_widget & parent() const final
Definition widget.ipp:271
void parent_changed() final
Definition widget.ipp:307
const i_widget & get_widget_at(const point &aPosition) const override
Definition widget.ipp:955
void layout_items_completed() override
Definition widget.ipp:806
void link_after(i_widget *aNextWidget) override
Definition widget.ipp:556
void remove_all() override
Definition widget.ipp:376
void property_changed(i_property &aProperty) override
Definition widget.ipp:110
const i_widget & parent_widget() const final
Definition widget.ipp:679
void layout_items_started() override
Definition widget.ipp:790
void set_parent_layout(i_layout *aParentLayout) final
Definition widget.ipp:665
void set_render_layer(const std::optional< layer_t > &aLayer) override
Definition widget.ipp:1143
bool adding_child() const override
Definition widget.ipp:320
void unlink() override
Definition widget.ipp:562
bool can_update() const override
Definition widget.ipp:1153
void set_logical_coordinate_system(const optional_logical_coordinate_system &aLogicalCoordinateSystem) override
Definition widget.ipp:834
bool is_singular() const final
Definition widget.ipp:162
bool has_parent_widget() const final
Definition widget.ipp:673
bool is_root() const final
Definition widget.ipp:181
bool has_parent() const final
Definition widget.ipp:265
const i_layout & layout() const override
Definition widget.ipp:611
bool update(bool aIncludeNonClient=false) override
Definition widget.ipp:1159
rect client_rect(bool aExtendIntoPadding=true) const override
Definition widget.ipp:943
bool part_active(widget_part aPart) const override
Definition widget.ipp:987
const i_device_metrics & device_metrics() const override
Definition widget.ipp:154
const i_window & root() const final
Definition widget.ipp:204
neogfx::view const & view() const override
Definition widget.ipp:1123
void remove(i_widget &aChild, bool aSingular, i_ref_ptr< i_widget > &aChildRef) override
Definition widget.ipp:351
void non_client_release_capture() override
Definition widget.ipp:1763
bool is_surface() const override
Definition widget.ipp:235
void layout_items(bool aDefer=false) override
Definition widget.ipp:711
bool layout_items_in_progress() const override
Definition widget.ipp:800
neogfx::padding padding() const override
Definition widget.ipp:1095
widget_list::const_iterator last() const override
Definition widget.ipp:395
bool has_root() const final
Definition widget.ipp:190
layer_t render_layer() const override
Definition widget.ipp:1135
rect default_clip_rect(bool aIncludeNonClient=false) const override
Definition widget.ipp:1196
bool requires_update() const override
Definition widget.ipp:1182
size maximum_size(optional_size const &aAvailableSpace={}) const override
Definition widget.ipp:1059
void resize(const size &aSize) override
Definition widget.ipp:893
const i_surface & real_surface() const override
Definition widget.ipp:253
bool has_parent_layout() const final
Definition widget.ipp:645
bool is_managing_layout() const override
Definition widget.ipp:633
void set_layer(layer_t aLayer) override
Definition widget.ipp:477
void set_parent(i_widget &aParent) override
Definition widget.ipp:285
const i_widget & before() const override
Definition widget.ipp:487
const i_layout & parent_layout() const final
Definition widget.ipp:651
bool has_layout() const override
Definition widget.ipp:573
void set_layout(i_layout &aLayout, bool aMoveExistingItems=true) override
Definition widget.ipp:579
neogfx::logical_coordinate_system logical_coordinate_system() const override
Definition widget.ipp:826
optional< neogfx::layout_reason > & layout_reason() override
Definition widget.ipp:705
void non_client_set_capture() override
Definition widget.ipp:1754
layer_t layer() const override
Definition widget.ipp:471
bool ready_to_render() const override
Definition widget.ipp:1210
void layout_as(const point &aPosition, const size &aSize) override
Definition widget.ipp:1104
rect update_rect() const override
Definition widget.ipp:1188
void render(i_graphics_context &aGc) const override
Definition widget.ipp:1216
void paint_non_client(i_graphics_context &aGc) const override
Definition widget.ipp:1334
neogfx::widget_type widget_type() const override
Definition widget.ipp:981
i_widget & add(i_widget &aChild) override
Definition widget.ipp:326
bool resizing() const override
Definition widget.ipp:887
rect non_client_rect() const override
Definition widget.ipp:936
size minimum_size(optional_size const &aAvailableSpace={}) const override
Definition widget.ipp:1027
widget_part part(const point &aPosition) const override
Definition widget.ipp:993
widget_part hit_test(const point &aPosition) const override
Definition widget.ipp:1004
const i_surface & surface() const override
Definition widget.ipp:241
void send_child_to_back(const i_widget &aChild) override
Definition widget.ipp:459
rect element_rect(skin_element aElement) const override
Definition widget.ipp:639
#define shared_thread_local(VariableType, VariableScope, VariableName,...)
logical_coordinate_system
bool contains(item_selection const &aSelection, item_presentation_model_index aIndex)
bool querying_ideal_size()
mouse_wheel
Definition i_mouse.hpp:42
layer_t constexpr LayerWidget
bool capture_ok(widget_part aWidgetPart)
uint32_t layout_item_index
Definition i_layout.hpp:35
bool has_font(glyph_char const &g)
layout_reason
Definition i_widget.hpp:43
mouse_button
Definition i_mouse.hpp:31
basic_scoped_units< units > scoped_units
Definition units.hpp:927
const current_style_palette_proxy_t current_style_palette_proxy()
Definition palette.hpp:30
enum neogfx::widget_part::widget_part_e part