diff --git a/include/Nazara/Widgets/Canvas.hpp b/include/Nazara/Widgets/Canvas.hpp index d4115e325..8952266ab 100644 --- a/include/Nazara/Widgets/Canvas.hpp +++ b/include/Nazara/Widgets/Canvas.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace Nz { @@ -36,8 +37,10 @@ namespace Nz protected: inline void ClearKeyboardOwner(std::size_t canvasIndex); + inline void ClearMouseOwner(std::size_t canvasIndex); inline bool IsKeyboardOwner(std::size_t canvasIndex) const; + inline bool IsMouseOwner(std::size_t canvasIndex) const; inline void NotifyWidgetBoxUpdate(std::size_t index); inline void NotifyWidgetCursorUpdate(std::size_t index); @@ -45,20 +48,25 @@ namespace Nz std::size_t RegisterWidget(BaseWidget* widget); inline void SetKeyboardOwner(std::size_t canvasIndex); + inline void SetMouseOwner(std::size_t canvasIndex); void UnregisterWidget(std::size_t index); private: void OnEventMouseButtonPressed(const EventHandler* eventHandler, const WindowEvent::MouseButtonEvent& event); void OnEventMouseButtonRelease(const EventHandler* eventHandler, const WindowEvent::MouseButtonEvent& event); + void OnEventMouseEntered(const EventHandler* eventHandler); void OnEventMouseLeft(const EventHandler* eventHandler); void OnEventMouseMoved(const EventHandler* eventHandler, const WindowEvent::MouseMoveEvent& event); + void OnEventMouseWheelMoved(const EventHandler* eventHandler, const WindowEvent::MouseWheelEvent& event); void OnEventKeyPressed(const EventHandler* eventHandler, const WindowEvent::KeyEvent& event); void OnEventKeyReleased(const EventHandler* eventHandler, const WindowEvent::KeyEvent& event); void OnEventTextEntered(const EventHandler* eventHandler, const WindowEvent::TextEvent& event); void OnEventTextEdited(const EventHandler* eventHandler, const WindowEvent::EditEvent& event); + void UpdateHoveredWidget(int x, int y); + struct WidgetEntry { BaseWidget* widget; @@ -70,6 +78,7 @@ namespace Nz NazaraSlot(EventHandler, OnKeyReleased, m_keyReleasedSlot); NazaraSlot(EventHandler, OnMouseButtonPressed, m_mouseButtonPressedSlot); NazaraSlot(EventHandler, OnMouseButtonReleased, m_mouseButtonReleasedSlot); + NazaraSlot(EventHandler, OnMouseEntered, m_mouseEnteredSlot); NazaraSlot(EventHandler, OnMouseLeft, m_mouseLeftSlot); NazaraSlot(EventHandler, OnMouseMoved, m_mouseMovedSlot); NazaraSlot(EventHandler, OnMouseWheelMoved, m_mouseWheelMovedSlot); @@ -78,8 +87,10 @@ namespace Nz CursorControllerHandle m_cursorController; UInt32 m_renderMask; + std::bitset m_mouseOwnerButtons; std::size_t m_keyboardOwner; std::size_t m_hoveredWidget; + std::size_t m_mouseOwner; std::vector m_widgetEntries; entt::registry& m_registry; }; diff --git a/include/Nazara/Widgets/Canvas.inl b/include/Nazara/Widgets/Canvas.inl index 3efc005cc..323513c38 100644 --- a/include/Nazara/Widgets/Canvas.inl +++ b/include/Nazara/Widgets/Canvas.inl @@ -12,6 +12,7 @@ namespace Nz m_renderMask(renderMask), m_keyboardOwner(InvalidCanvasIndex), m_hoveredWidget(InvalidCanvasIndex), + m_mouseOwner(InvalidCanvasIndex), m_registry(registry), m_cursorController(cursorController) { @@ -26,6 +27,7 @@ namespace Nz m_keyReleasedSlot.Connect(eventHandler.OnKeyReleased, this, &Canvas::OnEventKeyReleased); m_mouseButtonPressedSlot.Connect(eventHandler.OnMouseButtonPressed, this, &Canvas::OnEventMouseButtonPressed); m_mouseButtonReleasedSlot.Connect(eventHandler.OnMouseButtonReleased, this, &Canvas::OnEventMouseButtonRelease); + m_mouseEnteredSlot.Connect(eventHandler.OnMouseEntered, this, &Canvas::OnEventMouseEntered); m_mouseLeftSlot.Connect(eventHandler.OnMouseLeft, this, &Canvas::OnEventMouseLeft); m_mouseMovedSlot.Connect(eventHandler.OnMouseMoved, this, &Canvas::OnEventMouseMoved); m_mouseWheelMovedSlot.Connect(eventHandler.OnMouseWheelMoved, this, &Canvas::OnEventMouseWheelMoved); @@ -63,11 +65,22 @@ namespace Nz SetKeyboardOwner(InvalidCanvasIndex); } + inline void Canvas::ClearMouseOwner(std::size_t canvasIndex) + { + if (m_mouseOwner == canvasIndex) + SetMouseOwner(InvalidCanvasIndex); + } + inline bool Canvas::IsKeyboardOwner(std::size_t canvasIndex) const { return m_keyboardOwner == canvasIndex; } + inline bool Canvas::IsMouseOwner(std::size_t canvasIndex) const + { + return m_mouseOwner == canvasIndex; + } + inline void Canvas::NotifyWidgetBoxUpdate(std::size_t index) { WidgetEntry& entry = m_widgetEntries[index]; @@ -100,6 +113,14 @@ namespace Nz m_widgetEntries[m_keyboardOwner].widget->OnFocusReceived(); } } + + inline void Canvas::SetMouseOwner(std::size_t canvasIndex) + { + if (m_mouseOwner != canvasIndex) + { + m_mouseOwner = canvasIndex; + } + } } #include diff --git a/src/Nazara/Widgets/Canvas.cpp b/src/Nazara/Widgets/Canvas.cpp index e19fe7c98..cb9f332d5 100644 --- a/src/Nazara/Widgets/Canvas.cpp +++ b/src/Nazara/Widgets/Canvas.cpp @@ -28,6 +28,12 @@ namespace Nz if (m_hoveredWidget == index) m_hoveredWidget = InvalidCanvasIndex; + if (m_mouseOwner == index) + { + m_mouseOwner = InvalidCanvasIndex; + m_mouseOwnerButtons.reset(); + } + if (m_keyboardOwner == index) m_keyboardOwner = InvalidCanvasIndex; @@ -42,6 +48,9 @@ namespace Nz if (m_hoveredWidget == lastEntryIndex) m_hoveredWidget = index; + if (m_mouseOwner == lastEntryIndex) + m_mouseOwner = index; + if (m_keyboardOwner == lastEntryIndex) m_keyboardOwner = index; } @@ -51,6 +60,8 @@ namespace Nz void Canvas::OnEventMouseButtonPressed(const EventHandler* /*eventHandler*/, const WindowEvent::MouseButtonEvent& event) { + UpdateHoveredWidget(event.x, event.y); + if (m_hoveredWidget != InvalidCanvasIndex) { WidgetEntry& hoveredWidget = m_widgetEntries[m_hoveredWidget]; @@ -60,6 +71,9 @@ namespace Nz hoveredWidget.widget->OnMouseButtonPress(x, y, event.button); } + + SetMouseOwner(m_hoveredWidget); + m_mouseOwnerButtons[event.button] = true; } void Canvas::OnEventMouseButtonRelease(const EventHandler* /*eventHandler*/, const WindowEvent::MouseButtonEvent& event) @@ -73,59 +87,46 @@ namespace Nz hoveredWidget.widget->OnMouseButtonRelease(x, y, event.button); } + + m_mouseOwnerButtons[event.button] = false; + if (m_mouseOwnerButtons.none()) + SetMouseOwner(InvalidCanvasIndex); + + UpdateHoveredWidget(event.x, event.y); + } + + void Canvas::OnEventMouseEntered(const EventHandler* eventHandler) + { + // Keep previous mouse states but not new ones + for (std::size_t i = 0; i < Mouse::ButtonCount; ++i) + m_mouseOwnerButtons[i] = m_mouseOwnerButtons[i] && Mouse::IsButtonPressed(static_cast(i)); + + if (m_mouseOwnerButtons.none()) + SetMouseOwner(InvalidCanvasIndex); + } + + void Canvas::OnEventMouseLeft(const EventHandler* /*eventHandler*/) + { + if (m_hoveredWidget != InvalidCanvasIndex && m_mouseOwner == InvalidCanvasIndex) + { + m_widgetEntries[m_hoveredWidget].widget->OnMouseExit(); + m_hoveredWidget = InvalidCanvasIndex; + } } void Canvas::OnEventMouseMoved(const EventHandler* /*eventHandler*/, const WindowEvent::MouseMoveEvent& event) { - std::size_t bestEntry = InvalidCanvasIndex; - float bestEntryArea = std::numeric_limits::infinity(); + // Don't update hovered widget while the user doesn't release its mouse + UpdateHoveredWidget(event.x, event.y); - Vector3f mousePos(float(event.x), m_size.y - float(event.y), 0.f); - for (std::size_t i = 0; i < m_widgetEntries.size(); ++i) + if (m_hoveredWidget != InvalidCanvasIndex) { - const Boxf& box = m_widgetEntries[i].box; - - if (box.Contains(mousePos)) - { - float area = box.width * box.height; - if (area < bestEntryArea) - { - bestEntry = i; - bestEntryArea = area; - } - } - } - - if (bestEntry != InvalidCanvasIndex) - { - if (m_hoveredWidget != bestEntry) - { - if (m_hoveredWidget != InvalidCanvasIndex) - { - WidgetEntry& previouslyHovered = m_widgetEntries[m_hoveredWidget]; - previouslyHovered.widget->OnMouseExit(); - } - - m_hoveredWidget = bestEntry; - m_widgetEntries[m_hoveredWidget].widget->OnMouseEnter(); - - if (m_cursorController) - m_cursorController->UpdateCursor(Cursor::Get(m_widgetEntries[m_hoveredWidget].cursor)); - } - WidgetEntry& hoveredWidget = m_widgetEntries[m_hoveredWidget]; - int x = static_cast(std::round(mousePos.x - hoveredWidget.box.x)); - int y = static_cast(std::round(mousePos.y - hoveredWidget.box.y)); - hoveredWidget.widget->OnMouseMoved(x, y, event.deltaX, event.deltaY); - } - else if (m_hoveredWidget != InvalidCanvasIndex) - { - m_widgetEntries[m_hoveredWidget].widget->OnMouseExit(); - m_hoveredWidget = InvalidCanvasIndex; + int x = static_cast(std::round(event.x - hoveredWidget.box.x)); + int y = static_cast(std::round(m_size.y - event.y - hoveredWidget.box.y)); - if (m_cursorController) - m_cursorController->UpdateCursor(Cursor::Get(SystemCursor::Default)); + hoveredWidget.widget->OnMouseMoved(x, y, event.deltaX, -event.deltaY); } } @@ -142,15 +143,6 @@ namespace Nz } } - void Canvas::OnEventMouseLeft(const EventHandler* /*eventHandler*/) - { - if (m_hoveredWidget != InvalidCanvasIndex) - { - m_widgetEntries[m_hoveredWidget].widget->OnMouseExit(); - m_hoveredWidget = InvalidCanvasIndex; - } - } - void Canvas::OnEventKeyPressed(const EventHandler* eventHandler, const WindowEvent::KeyEvent& event) { if (m_keyboardOwner != InvalidCanvasIndex) @@ -228,4 +220,55 @@ namespace Nz if (m_keyboardOwner != InvalidCanvasIndex) m_widgetEntries[m_keyboardOwner].widget->OnTextEdited(event.text, event.length); } + + void Canvas::UpdateHoveredWidget(int x, int y) + { + if (m_mouseOwner != InvalidCanvasIndex) + return; + + std::size_t bestEntry = InvalidCanvasIndex; + float bestEntryArea = std::numeric_limits::infinity(); + + Vector3f mousePos(float(x), m_size.y - float(y), 0.f); + for (std::size_t i = 0; i < m_widgetEntries.size(); ++i) + { + const Boxf& box = m_widgetEntries[i].box; + + if (box.Contains(mousePos)) + { + float area = box.width * box.height; + if (area < bestEntryArea) + { + bestEntry = i; + bestEntryArea = area; + } + } + } + + if (bestEntry != InvalidCanvasIndex) + { + if (m_hoveredWidget != bestEntry) + { + if (m_hoveredWidget != InvalidCanvasIndex) + { + WidgetEntry& previouslyHovered = m_widgetEntries[m_hoveredWidget]; + previouslyHovered.widget->OnMouseExit(); + } + + m_hoveredWidget = bestEntry; + m_widgetEntries[m_hoveredWidget].widget->OnMouseEnter(); + + if (m_cursorController) + m_cursorController->UpdateCursor(Cursor::Get(m_widgetEntries[m_hoveredWidget].cursor)); + } + } + else if (m_hoveredWidget != InvalidCanvasIndex) + { + m_widgetEntries[m_hoveredWidget].widget->OnMouseExit(); + m_hoveredWidget = InvalidCanvasIndex; + + if (m_cursorController) + m_cursorController->UpdateCursor(Cursor::Get(SystemCursor::Default)); + } + } }