#include "pch.h" #include "legacy_ui_node_event.h" #include "node.h" #include "layout.h" namespace pp::panopainter { namespace { void transfer_mouse_focus(Node& node, Event* e) { if (e->m_cat != kEventCategory::MouseEvent) return; if (node.child_mouse_focus == node.current_mouse_capture) return; if (!node.is_child(node.current_mouse_capture.get())) return; MouseEvent* me = static_cast(e); if (node.child_mouse_focus) { MouseEvent e2 = *me; e2.m_type = kEventType::MouseUnfocus; node.child_mouse_focus->handle_event(&e2); node.child_mouse_focus->m_mouse_focus = false; } MouseEvent e2 = *me; e2.m_type = kEventType::MouseFocus; node.current_mouse_capture->handle_event(&e2); node.child_mouse_focus = node.current_mouse_capture; node.child_mouse_focus->m_mouse_focus = true; } bool should_skip_children(Node& node, Event* e) { bool skip_children = false; if (e->m_cat == kEventCategory::MouseEvent) { MouseEvent* me = static_cast(e); skip_children |= !node.m_mouse_inside && !point_in_rect(me->m_pos, node.m_clip); } skip_children |= (e->m_cat == kEventCategory::MouseEvent || e->m_cat == kEventCategory::GestureEvent) && (node.m_mouse_captured) && (node.root()->current_mouse_capture.get() == &node) && node.m_capture_children; // <-- THIS IS WRONG "!m_capture_children" is correct, but it breaks everything if changed return skip_children; } void handle_mouse_focus_transition(Node& node, Event* e, const std::shared_ptr& child) { if (e->m_cat != kEventCategory::MouseEvent || node.child_mouse_focus.get() == child.get()) return; MouseEvent* me = static_cast(e); if (node.child_mouse_focus && !node.child_mouse_focus->m_destroyed) { MouseEvent e2 = *me; e2.m_type = kEventType::MouseUnfocus; node.child_mouse_focus->handle_event(&e2); node.child_mouse_focus->m_mouse_focus = false; } MouseEvent e2 = *me; e2.m_type = kEventType::MouseFocus; child->handle_event(&e2); if (!child->m_destroyed) { node.child_mouse_focus = child; node.child_mouse_focus->m_mouse_focus = true; } } void update_mouse_inside(Node& node, MouseEvent* me) { bool old_inside = node.m_mouse_inside; node.m_mouse_inside = point_in_rect(me->m_pos, node.m_clip); if (old_inside == false && node.m_mouse_inside == true) { MouseEvent e2 = *me; e2.m_type = kEventType::MouseEnter; node.handle_event(&e2); } if (old_inside == true && node.m_mouse_inside == false) { MouseEvent e2 = *me; e2.m_type = kEventType::MouseLeave; node.handle_event(&e2); } } } // namespace kEventResult handle_legacy_ui_node_event(Node& node, Event* e) { kEventResult ret = kEventResult::Available; if (e->m_cat == kEventCategory::KeyEvent && node.current_key_capture) return node.current_key_capture->on_event(e); if (node.current_mouse_capture && node.current_mouse_capture.get() != &node) { transfer_mouse_focus(node, e); return node.current_mouse_capture->on_event(e); } // skip mouse events if outside if (!node.m_display || glm::any(glm::lessThanEqual(zw(node.m_clip), { 0, 0 }))) return kEventResult::Available; if (!should_skip_children(node, e)) { // make a copy because any handler can change the children vector auto children_copy = node.m_children; for (auto it = children_copy.rbegin(); it != children_copy.rend(); ++it) { if ((*it)->on_event(e) == kEventResult::Consumed) { if (node.m_flood_events) { ret = kEventResult::Consumed; } else { handle_mouse_focus_transition(node, e, *it); ret = kEventResult::Consumed; break; } } } if (ret == kEventResult::Consumed) { if (e->m_cat == kEventCategory::MouseEvent) update_mouse_inside(node, static_cast(e)); return kEventResult::Consumed; } } switch (e->m_cat) { case kEventCategory::MouseEvent: { if (node.m_mouse_ignore) break; MouseEvent* me = static_cast(e); bool inside = point_in_rect(me->m_pos, node.m_clip); bool inside_old = node.m_mouse_inside; node.m_mouse_inside = inside; switch (e->m_type) { case kEventType::MouseScroll: case kEventType::MouseDownL: case kEventType::MouseDownR: case kEventType::MouseUpL: case kEventType::MouseUpR: if ((inside || node.m_mouse_captured) && ((node.handle_event(e) == kEventResult::Consumed) || node.m_force_mouse_capture)) return kEventResult::Consumed; break; case kEventType::MouseMove: if (inside_old == false && inside == true) { MouseEvent e2 = *me; e2.m_type = kEventType::MouseEnter; node.handle_event(&e2); } if (inside || node.m_mouse_captured) { ret = node.handle_event(e); if (node.m_force_mouse_capture) ret = kEventResult::Consumed; } if (inside_old == true && inside == false) { MouseEvent e2 = *me; e2.m_type = kEventType::MouseLeave; node.handle_event(&e2); } break; default: if (node.handle_event(e) == kEventResult::Consumed) return kEventResult::Consumed; break; } break; } case kEventCategory::GestureEvent: { if (node.m_mouse_ignore) break; GestureEvent* ge = static_cast(e); bool inside = point_in_rect(ge->m_pos, node.m_clip); node.m_mouse_inside = inside; if ((inside || node.m_mouse_captured) && node.handle_event(e) == kEventResult::Consumed) return kEventResult::Consumed; break; } default: if (node.handle_event(e) == kEventResult::Consumed) return kEventResult::Consumed; break; } return ret; } void legacy_ui_node_mouse_capture(Node& node) { if (!node.m_parent || !node.m_manager) return; auto root = node.m_manager->get_ref("main"); if (!root) return; auto& c = root->current_mouse_capture; auto& s = root->m_capture_stack; // already owner of capture if (c.get() == &node || std::find_if(s.begin(), s.end(), [&node](const auto& a) { return a.get() == &node; }) != s.end()) return; if (c) { if (c->is_child_recursive(&node)) { // save on stack s.push_back(c); } else { // cancel previous owner MouseEvent e; e.m_type = kEventType::MouseCancel; c->handle_event(&e); // TODO: only delete nodes on a different tree, // so preserve direct parents of this // also clear the whole stack //s.clear(); s.push_back(c); } } // make current c = node.shared_from_this(); node.m_mouse_captured = true; } void legacy_ui_node_mouse_release(Node& node) { if (!node.m_parent || !node.m_manager) return; auto root = node.m_manager->get_ref("main"); if (!root) return; auto& c = root->current_mouse_capture; auto& s = root->m_capture_stack; s.erase(std::remove_if(s.begin(), s.end(), [&node](const auto& a) { return a.get() == &node; }), s.end()); if (c.get() == &node) { if (s.empty()) { c = nullptr; } else { c = s.back(); s.pop_back(); } } node.m_mouse_captured = false; } void legacy_ui_node_key_capture(Node& node) { if (!node.m_parent || !node.m_manager) return; auto root = node.m_manager->get_ref("main"); if (!root) return; root->current_key_capture = node.shared_from_this(); node.m_key_captured = true; } void legacy_ui_node_key_release(Node& node) { if (!node.m_parent || !node.m_manager) return; auto root = node.m_manager->get_ref("main"); if (!root) return; if (root->current_key_capture.get() == &node) root->current_key_capture = nullptr; node.m_key_captured = false; } } // namespace pp::panopainter