diff --git a/ChangeLog.md b/ChangeLog.md index b2f71d16d..bfb292c04 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -189,6 +189,7 @@ Nazara Engine: - ⚠ TextSprite will now use multiple render layers by itself (the current one and the one right before, ex: [-1, 0] if base layer is 0) if you use text outlines. - ⚠ SimpleTextDrawer no longer supports faux bold rendering - Added PhysWorld2D::[RaycastQuery, RegionQuery] overloads taking a callback +- Added x and y mouse position to MouseWheelEvent Nazara Development Kit: - Added ImageWidget (#139) @@ -267,6 +268,13 @@ Nazara Development Kit: - Added PhysicsSystem2D::[RaycastQuery, RegionQuery] overloads taking a callback - Added TextAreaWidget support for outline - Fixed possible crash when disabling BaseWidget background +- Added BaseWidget::OnMouseWheelMoved +- Added Entity::OnEntity[Disabled|Enabled] signals +- Added BaseWidget::SetParent +- BaseWidget::Show will no longer show entities disabled by the widget +- BaseWidget now has a rendering rect property (allowing to tell a widget what part of it will be rendered) +- Added ScrollAreaWidget +- Console has been remade with widgets (allowing to scroll back history, select text, etc.) # 0.4: diff --git a/SDK/include/NDK/Application.hpp b/SDK/include/NDK/Application.hpp index 401f80228..69e97bdce 100644 --- a/SDK/include/NDK/Application.hpp +++ b/SDK/include/NDK/Application.hpp @@ -15,6 +15,7 @@ #include #ifndef NDK_SERVER +#include #include #include #include @@ -81,7 +82,7 @@ namespace Ndk #ifndef NDK_SERVER struct ConsoleOverlay { - std::unique_ptr console; + Console* console; Nz::LuaInstance lua; NazaraSlot(Nz::EventHandler, OnEvent, eventSlot); @@ -114,10 +115,11 @@ namespace Ndk Nz::RenderTarget* renderTarget; std::unique_ptr window; std::unique_ptr console; + std::unique_ptr canvas; std::unique_ptr fpsCounter; std::unique_ptr overlayWorld; }; - + void SetupConsole(WindowInfo& info); void SetupFPSCounter(WindowInfo& info); void SetupOverlay(WindowInfo& info); diff --git a/SDK/include/NDK/Application.inl b/SDK/include/NDK/Application.inl index e01fb2ce4..42139db0c 100644 --- a/SDK/include/NDK/Application.inl +++ b/SDK/include/NDK/Application.inl @@ -112,7 +112,6 @@ namespace Ndk } m_overlayFlags |= OverlayFlags_Console; - } else { diff --git a/SDK/include/NDK/BaseWidget.hpp b/SDK/include/NDK/BaseWidget.hpp index 26c7f5539..010abd437 100644 --- a/SDK/include/NDK/BaseWidget.hpp +++ b/SDK/include/NDK/BaseWidget.hpp @@ -26,8 +26,6 @@ namespace Ndk friend Canvas; public: - struct Padding; - BaseWidget(BaseWidget* parent); BaseWidget(const BaseWidget&) = delete; BaseWidget(BaseWidget&&) = delete; @@ -41,6 +39,7 @@ namespace Ndk inline void CenterVertical(); void ClearFocus(); + inline void ClearRenderingRect(); void Destroy(); @@ -68,6 +67,8 @@ namespace Ndk inline Nz::Vector2f GetPreferredSize() const; inline float GetPreferredWidth() const; + inline const Nz::Rectf& GetRenderingRect() const; + inline Nz::Vector2f GetSize() const; inline float GetWidth() const; inline std::size_t GetWidgetChildCount() const; @@ -81,6 +82,7 @@ namespace Ndk void SetBackgroundColor(const Nz::Color& color); void SetCursor(Nz::SystemCursor systemCursor); void SetFocus(); + void SetParent(BaseWidget* widget); inline void SetFixedHeight(float fixedHeight); inline void SetFixedSize(const Nz::Vector2f& fixedSize); @@ -94,6 +96,8 @@ namespace Ndk inline void SetMinimumSize(const Nz::Vector2f& minimumSize); inline void SetMinimumWidth(float minimumWidth); + virtual void SetRenderingRect(const Nz::Rectf& renderingRect); + void Show(bool show = true); BaseWidget& operator=(const BaseWidget&) = delete; @@ -115,6 +119,7 @@ namespace Ndk virtual void OnMouseMoved(int x, int y, int deltaX, int deltaY); virtual void OnMouseButtonPress(int x, int y, Nz::Mouse::Button button); virtual void OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button); + virtual void OnMouseWheelMoved(int x, int y, float delta); virtual void OnMouseExit(); virtual void OnParentResized(const Nz::Vector2f& newSize); virtual void OnTextEntered(char32_t character, bool repeated); @@ -136,6 +141,10 @@ namespace Ndk struct WidgetEntity { EntityOwner handle; + bool isEnabled = true; + + NazaraSlot(Ndk::Entity, OnEntityDisabled, onDisabledSlot); + NazaraSlot(Ndk::Entity, OnEntityEnabled, onEnabledSlot); }; static constexpr std::size_t InvalidCanvasIndex = std::numeric_limits::max(); @@ -147,6 +156,7 @@ namespace Ndk EntityOwner m_backgroundEntity; WorldHandle m_world; Nz::Color m_backgroundColor; + Nz::Rectf m_renderingRect; Nz::SpriteRef m_backgroundSprite; Nz::SystemCursor m_cursor; Nz::Vector2f m_maximumSize; diff --git a/SDK/include/NDK/BaseWidget.inl b/SDK/include/NDK/BaseWidget.inl index e24b89285..605220774 100644 --- a/SDK/include/NDK/BaseWidget.inl +++ b/SDK/include/NDK/BaseWidget.inl @@ -5,6 +5,7 @@ #include #include #include +#include namespace Ndk { @@ -12,6 +13,7 @@ namespace Ndk m_canvasIndex(InvalidCanvasIndex), m_canvas(nullptr), m_backgroundColor(Nz::Color(230, 230, 230, 255)), + m_renderingRect(-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()), m_cursor(Nz::SystemCursor_Default), m_size(50.f, 50.f), m_maximumSize(std::numeric_limits::infinity()), @@ -66,6 +68,11 @@ namespace Ndk SetPosition(GetPosition(Nz::CoordSys_Local).x, (parentSize.y - mySize.y) / 2.f); } + inline void BaseWidget::ClearRenderingRect() + { + SetRenderingRect(Nz::Rectf(-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity())); + } + template inline void BaseWidget::ForEachWidgetChild(F iterator) { @@ -145,6 +152,11 @@ namespace Ndk return m_preferredSize.x; } + inline const Nz::Rectf& BaseWidget::GetRenderingRect() const + { + return m_renderingRect; + } + inline Nz::Vector2f BaseWidget::GetSize() const { return Nz::Vector2f(GetWidth(), GetHeight()); @@ -237,7 +249,7 @@ namespace Ndk { m_preferredSize = preferredSize; - Resize(m_preferredSize); + //Resize(m_preferredSize); } inline bool BaseWidget::IsRegisteredToCanvas() const diff --git a/SDK/include/NDK/Canvas.hpp b/SDK/include/NDK/Canvas.hpp index d563c6cd3..1b0fbda4f 100644 --- a/SDK/include/NDK/Canvas.hpp +++ b/SDK/include/NDK/Canvas.hpp @@ -48,8 +48,9 @@ namespace Ndk private: void OnEventMouseButtonPressed(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseButtonEvent& event); void OnEventMouseButtonRelease(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseButtonEvent& event); - void OnEventMouseMoved(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseMoveEvent& event); void OnEventMouseLeft(const Nz::EventHandler* eventHandler); + void OnEventMouseMoved(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseMoveEvent& event); + void OnEventMouseWheelMoved(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::MouseWheelEvent& event); void OnEventKeyPressed(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::KeyEvent& event); void OnEventKeyReleased(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::KeyEvent& event); void OnEventTextEntered(const Nz::EventHandler* eventHandler, const Nz::WindowEvent::TextEvent& event); @@ -65,8 +66,9 @@ namespace Ndk NazaraSlot(Nz::EventHandler, OnKeyReleased, m_keyReleasedSlot); NazaraSlot(Nz::EventHandler, OnMouseButtonPressed, m_mouseButtonPressedSlot); NazaraSlot(Nz::EventHandler, OnMouseButtonReleased, m_mouseButtonReleasedSlot); - NazaraSlot(Nz::EventHandler, OnMouseMoved, m_mouseMovedSlot); NazaraSlot(Nz::EventHandler, OnMouseLeft, m_mouseLeftSlot); + NazaraSlot(Nz::EventHandler, OnMouseMoved, m_mouseMovedSlot); + NazaraSlot(Nz::EventHandler, OnMouseWheelMoved, m_mouseWheelMovedSlot); NazaraSlot(Nz::EventHandler, OnTextEntered, m_textEnteredSlot); std::size_t m_keyboardOwner; diff --git a/SDK/include/NDK/Canvas.inl b/SDK/include/NDK/Canvas.inl index 7a602cffb..75b642328 100644 --- a/SDK/include/NDK/Canvas.inl +++ b/SDK/include/NDK/Canvas.inl @@ -24,8 +24,9 @@ namespace Ndk 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_mouseMovedSlot.Connect(eventHandler.OnMouseMoved, this, &Canvas::OnEventMouseMoved); m_mouseLeftSlot.Connect(eventHandler.OnMouseLeft, this, &Canvas::OnEventMouseLeft); + m_mouseMovedSlot.Connect(eventHandler.OnMouseMoved, this, &Canvas::OnEventMouseMoved); + m_mouseWheelMovedSlot.Connect(eventHandler.OnMouseWheelMoved, this, &Canvas::OnEventMouseWheelMoved); m_textEnteredSlot.Connect(eventHandler.OnTextEntered, this, &Canvas::OnEventTextEntered); } diff --git a/SDK/include/NDK/Console.hpp b/SDK/include/NDK/Console.hpp index 728cbcbb0..cb79fcbee 100644 --- a/SDK/include/NDK/Console.hpp +++ b/SDK/include/NDK/Console.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace Nz @@ -25,13 +26,16 @@ namespace Nz namespace Ndk { class Console; + class Entity; + class ScrollAreaWidget; + class TextAreaWidget; using ConsoleHandle = Nz::ObjectHandle; - class NDK_API Console : public Nz::Node, public Nz::HandledObject + class NDK_API Console : public BaseWidget, public Nz::HandledObject { public: - Console(World& world, const Nz::Vector2f& size, Nz::LuaState& state); + Console(BaseWidget* parent, Nz::LuaState& state); Console(const Console& console) = delete; Console(Console&& console) = default; ~Console() = default; @@ -39,34 +43,23 @@ namespace Ndk void AddLine(const Nz::String& text, const Nz::Color& color = Nz::Color::White); void Clear(); + void ClearFocus(); inline unsigned int GetCharacterSize() const; - inline const EntityHandle& GetHistory() const; - inline const EntityHandle& GetHistoryBackground() const; - inline const EntityHandle& GetInput() const; - inline const EntityHandle& GetInputBackground() const; - inline const Nz::Vector2f& GetSize() const; + inline const TextAreaWidget* GetHistory() const; + inline const TextAreaWidget* GetInput() const; inline const Nz::FontRef& GetTextFont() const; - inline bool IsVisible() const; - - void SendCharacter(char32_t character); - void SendEvent(const Nz::WindowEvent& event); - void SetCharacterSize(unsigned int size); - void SetSize(const Nz::Vector2f& size); + void SetFocus(); void SetTextFont(Nz::FontRef font); - void Show(bool show = true); - Console& operator=(const Console& console) = delete; Console& operator=(Console&& console) = default; private: - void AddLineInternal(const Nz::String& text, const Nz::Color& color = Nz::Color::White); - void ExecuteInput(); - void Layout(); - void RefreshHistory(); + void ExecuteInput(const TextAreaWidget* textArea, bool* ignoreDefaultAction); + void Layout() override; struct Line { @@ -77,20 +70,11 @@ namespace Ndk std::size_t m_historyPosition; std::vector m_commandHistory; std::vector m_historyLines; - EntityOwner m_historyBackground; - EntityOwner m_history; - EntityOwner m_input; - EntityOwner m_inputBackground; + ScrollAreaWidget* m_historyArea; + TextAreaWidget* m_history; + TextAreaWidget* m_input; Nz::FontRef m_defaultFont; Nz::LuaState& m_state; - Nz::SpriteRef m_historyBackgroundSprite; - Nz::SpriteRef m_inputBackgroundSprite; - Nz::SimpleTextDrawer m_historyDrawer; - Nz::SimpleTextDrawer m_inputDrawer; - Nz::TextSpriteRef m_historyTextSprite; - Nz::TextSpriteRef m_inputTextSprite; - Nz::Vector2f m_size; - bool m_opened; unsigned int m_characterSize; unsigned int m_maxHistoryLines; }; diff --git a/SDK/include/NDK/Console.inl b/SDK/include/NDK/Console.inl index 746616ffd..0e5733f75 100644 --- a/SDK/include/NDK/Console.inl +++ b/SDK/include/NDK/Console.inl @@ -19,51 +19,21 @@ namespace Ndk * \return History of the console */ - inline const EntityHandle& Console::GetHistory() const + inline const TextAreaWidget* Console::GetHistory() const { return m_history; } - /*! - * \brief Gets the entity representing the background of the console's history - * \return Background history of the console - */ - - inline const EntityHandle& Console::GetHistoryBackground() const - { - return m_historyBackground; - } - /*! * \brief Gets the entity representing the input of the console * \return Input of the console */ - inline const EntityHandle& Console::GetInput() const + inline const TextAreaWidget* Console::GetInput() const { return m_input; } - /*! - * \brief Gets the entity representing the background of the console's input - * \return Background input of the console - */ - - inline const EntityHandle& Console::GetInputBackground() const - { - return m_inputBackground; - } - - /*! - * \brief Gets the size of the console - * \return Size (Width, Height) of the console - */ - - inline const Nz::Vector2f& Console::GetSize() const - { - return m_size; - } - /*! * \brief Gets the font used by the console * \return A reference to the font currenty used @@ -73,14 +43,4 @@ namespace Ndk { return m_defaultFont; } - - /*! - * \brief Checks whether the console is visible - * \return true If it is the case - */ - - inline bool Console::IsVisible() const - { - return m_opened; - } } diff --git a/SDK/include/NDK/Entity.hpp b/SDK/include/NDK/Entity.hpp index 43a4a43c7..e09f4eac4 100644 --- a/SDK/include/NDK/Entity.hpp +++ b/SDK/include/NDK/Entity.hpp @@ -74,6 +74,8 @@ namespace Ndk Entity& operator=(Entity&&) = delete; NazaraSignal(OnEntityDestruction, Entity* /*entity*/); + NazaraSignal(OnEntityDisabled, Entity* /*entity*/); + NazaraSignal(OnEntityEnabled, Entity* /*entity*/); private: Entity(World* world, EntityId id); diff --git a/SDK/include/NDK/Widgets.hpp b/SDK/include/NDK/Widgets.hpp index a4b74b2d6..303b22519 100644 --- a/SDK/include/NDK/Widgets.hpp +++ b/SDK/include/NDK/Widgets.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #endif // NDK_WIDGETS_GLOBAL_HPP diff --git a/SDK/include/NDK/Widgets/ScrollAreaWidget.hpp b/SDK/include/NDK/Widgets/ScrollAreaWidget.hpp new file mode 100644 index 000000000..5b7106b1f --- /dev/null +++ b/SDK/include/NDK/Widgets/ScrollAreaWidget.hpp @@ -0,0 +1,67 @@ +// Copyright (C) 2019 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#pragma once + +#ifndef NDK_WIDGETS_SCROLLAREAWIDGET_HPP +#define NDK_WIDGETS_SCROLLAREAWIDGET_HPP + +#include +#include +#include + +namespace Ndk +{ + class NDK_API ScrollAreaWidget : public BaseWidget + { + public: + ScrollAreaWidget(BaseWidget* parent, BaseWidget* content); + ScrollAreaWidget(const ScrollAreaWidget&) = delete; + ScrollAreaWidget(ScrollAreaWidget&&) = default; + ~ScrollAreaWidget() = default; + + inline float GetScrollHeight() const; + inline float GetScrollRatio() const; + + inline void ScrollToHeight(float height); + void ScrollToRatio(float ratio); + + ScrollAreaWidget& operator=(const ScrollAreaWidget&) = delete; + ScrollAreaWidget& operator=(ScrollAreaWidget&&) = default; + + private: + enum class ScrollBarStatus + { + Grabbed, + Hovered, + None + }; + + Nz::Rectf GetScrollbarRect() const; + + void Layout() override; + + void OnMouseButtonPress(int x, int y, Nz::Mouse::Button button) override; + void OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button) override; + void OnMouseExit() override; + void OnMouseMoved(int x, int y, int deltaX, int deltaY) override; + void OnMouseWheelMoved(int x, int y, float delta) override; + + void UpdateScrollbarStatus(ScrollBarStatus status); + + BaseWidget* m_content; + EntityHandle m_scrollbarBackgroundEntity; + EntityHandle m_scrollbarEntity; + Nz::SpriteRef m_scrollbarBackgroundSprite; + Nz::SpriteRef m_scrollbarSprite; + Nz::Vector2i m_grabbedDelta; + ScrollBarStatus m_scrollbarStatus; + bool m_isScrollBarVisible; + float m_scrollRatio; + }; +} + +#include + +#endif // NDK_WIDGETS_SCROLLAREAWIDGET_HPP diff --git a/SDK/include/NDK/Widgets/ScrollAreaWidget.inl b/SDK/include/NDK/Widgets/ScrollAreaWidget.inl new file mode 100644 index 000000000..c98230180 --- /dev/null +++ b/SDK/include/NDK/Widgets/ScrollAreaWidget.inl @@ -0,0 +1,24 @@ +// Copyright (C) 2019 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#include + +namespace Ndk +{ + inline float ScrollAreaWidget::GetScrollHeight() const + { + return m_scrollRatio * m_content->GetHeight(); + } + + inline float ScrollAreaWidget::GetScrollRatio() const + { + return m_scrollRatio; + } + + inline void ScrollAreaWidget::ScrollToHeight(float height) + { + float contentHeight = m_content->GetHeight(); + ScrollToRatio(height / contentHeight); + } +} diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.hpp b/SDK/include/NDK/Widgets/TextAreaWidget.hpp index d215603eb..bb121c5f0 100644 --- a/SDK/include/NDK/Widgets/TextAreaWidget.hpp +++ b/SDK/include/NDK/Widgets/TextAreaWidget.hpp @@ -46,7 +46,8 @@ namespace Ndk inline Nz::Vector2ui GetCursorPosition(std::size_t glyphIndex) const; inline const Nz::String& GetDisplayText() const; inline EchoMode GetEchoMode() const; - inline std::size_t GetGlyphIndex(const Nz::Vector2ui& cursorPosition); + inline std::size_t GetGlyphIndex() const; + inline std::size_t GetGlyphIndex(const Nz::Vector2ui& cursorPosition) const; inline const Nz::String& GetText() const; inline const Nz::Color& GetTextColor() const; inline const Nz::Color& GetTextOulineColor() const; diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.inl b/SDK/include/NDK/Widgets/TextAreaWidget.inl index 0c931cebd..a2e9f22c9 100644 --- a/SDK/include/NDK/Widgets/TextAreaWidget.inl +++ b/SDK/include/NDK/Widgets/TextAreaWidget.inl @@ -13,6 +13,7 @@ namespace Ndk m_drawer.Clear(); m_text.Clear(); m_textSprite->Update(m_drawer); + SetPreferredSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); RefreshCursor(); OnTextChanged(this, m_text); @@ -76,7 +77,17 @@ namespace Ndk return m_drawer.GetText(); } - inline std::size_t TextAreaWidget::GetGlyphIndex(const Nz::Vector2ui& cursorPosition) + inline EchoMode TextAreaWidget::GetEchoMode() const + { + return m_echoMode; + } + + inline std::size_t TextAreaWidget::GetGlyphIndex() const + { + return GetGlyphIndex(m_cursorPositionBegin); + } + + inline std::size_t TextAreaWidget::GetGlyphIndex(const Nz::Vector2ui& cursorPosition) const { std::size_t glyphIndex = m_drawer.GetLine(cursorPosition.y).glyphIndex + cursorPosition.x; if (m_drawer.GetLineCount() > cursorPosition.y + 1) @@ -87,11 +98,6 @@ namespace Ndk return glyphIndex; } - inline EchoMode TextAreaWidget::GetEchoMode() const - { - return m_echoMode; - } - inline const Nz::String& TextAreaWidget::GetText() const { return m_text; diff --git a/SDK/src/NDK/Application.cpp b/SDK/src/NDK/Application.cpp index a4fca055a..c26b1e7d2 100644 --- a/SDK/src/NDK/Application.cpp +++ b/SDK/src/NDK/Application.cpp @@ -147,16 +147,15 @@ namespace Ndk Nz::Vector2ui windowDimensions; if (info.window->IsValid()) - { windowDimensions = info.window->GetSize(); - windowDimensions.y /= 4; - } else windowDimensions.MakeZero(); - overlay->console = std::make_unique(*info.overlayWorld, Nz::Vector2f(windowDimensions), overlay->lua); + overlay->console = info.canvas->Add(overlay->lua); Console& consoleRef = *overlay->console; + consoleRef.Resize({float(windowDimensions.x), windowDimensions.y / 4.f}); + consoleRef.Show(false); // Redirect logs toward the console overlay->logSlot.Connect(Nz::Log::OnLogWrite, [&consoleRef] (const Nz::String& str) @@ -201,22 +200,34 @@ namespace Ndk // Setup a few event callback to handle the console Nz::EventHandler& eventHandler = info.window->GetEventHandler(); - overlay->eventSlot.Connect(eventHandler.OnEvent, [&consoleRef] (const Nz::EventHandler*, const Nz::WindowEvent& event) + /*overlay->eventSlot.Connect(eventHandler.OnEvent, [&consoleRef] (const Nz::EventHandler*, const Nz::WindowEvent& event) { if (consoleRef.IsVisible()) consoleRef.SendEvent(event); - }); + });*/ overlay->keyPressedSlot.Connect(eventHandler.OnKeyPressed, [&consoleRef] (const Nz::EventHandler*, const Nz::WindowEvent::KeyEvent& event) { if (event.code == Nz::Keyboard::F9) - consoleRef.Show(!consoleRef.IsVisible()); + { + // Toggle console visibility and focus + if (consoleRef.IsVisible()) + { + consoleRef.ClearFocus(); + consoleRef.Show(false); + } + else + { + consoleRef.Show(true); + consoleRef.SetFocus(); + } + } }); overlay->resizedSlot.Connect(info.renderTarget->OnRenderTargetSizeChange, [&consoleRef] (const Nz::RenderTarget* renderTarget) { Nz::Vector2ui size = renderTarget->GetSize(); - consoleRef.SetSize({float(size.x), size.y / 4.f}); + consoleRef.Resize({float(size.x), size.y / 4.f}); }); info.console = std::move(overlay); @@ -238,6 +249,9 @@ namespace Ndk { info.overlayWorld = std::make_unique(false); //< No default system + if (info.window->IsValid()) + info.canvas = std::make_unique(info.overlayWorld->CreateHandle(), info.window->GetEventHandler(), info.window->GetCursorController().CreateHandle()); + RenderSystem& renderSystem = info.overlayWorld->AddSystem(); renderSystem.ChangeRenderTechnique(); renderSystem.SetDefaultBackground(nullptr); diff --git a/SDK/src/NDK/BaseWidget.cpp b/SDK/src/NDK/BaseWidget.cpp index 499a7aa2b..647f451c6 100644 --- a/SDK/src/NDK/BaseWidget.cpp +++ b/SDK/src/NDK/BaseWidget.cpp @@ -145,6 +145,27 @@ namespace Ndk m_canvas->SetKeyboardOwner(m_canvasIndex); } + void BaseWidget::SetParent(BaseWidget* widget) + { + Canvas* oldCanvas = m_canvas; + Canvas* newCanvas = widget->GetCanvas(); + + // Changing a widget canvas is a problem because of the canvas entities + NazaraAssert(oldCanvas == newCanvas, "Transferring a widget between canvas is not yet supported"); + + Node::SetParent(widget); + m_widgetParent = widget; + + Layout(); + } + + void BaseWidget::SetRenderingRect(const Nz::Rectf& renderingRect) + { + m_renderingRect = renderingRect; + + UpdatePositionAndSize(); + } + void BaseWidget::Show(bool show) { if (m_visible != show) @@ -157,14 +178,20 @@ namespace Ndk UnregisterFromCanvas(); for (WidgetEntity& entity : m_entities) - entity.handle->Enable(show); + { + if (entity.isEnabled) + { + entity.handle->Enable(show); //< This will override isEnabled + entity.isEnabled = true; + } + } for (const auto& widgetPtr : m_children) widgetPtr->Show(show); } } - const Ndk::EntityHandle& BaseWidget::CreateEntity() + const EntityHandle& BaseWidget::CreateEntity() { const EntityHandle& newEntity = m_world->CreateEntity(); newEntity->Enable(m_visible); @@ -172,6 +199,21 @@ namespace Ndk m_entities.emplace_back(); WidgetEntity& widgetEntity = m_entities.back(); widgetEntity.handle = newEntity; + widgetEntity.onDisabledSlot.Connect(newEntity->OnEntityDisabled, [this](Entity* entity) + { + auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; }); + NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget"); + + it->isEnabled = false; + }); + + widgetEntity.onEnabledSlot.Connect(newEntity->OnEntityEnabled, [this](Entity* entity) + { + auto it = std::find_if(m_entities.begin(), m_entities.end(), [&](const WidgetEntity& widgetEntity) { return widgetEntity.handle == entity; }); + NazaraAssert(it != m_entities.end(), "Entity does not belong to this widget"); + + it->isEnabled = true; + }); return newEntity; } @@ -237,6 +279,10 @@ namespace Ndk { } + void BaseWidget::OnMouseWheelMoved(int /*x*/, int /*y*/, float /*delta*/) + { + } + void BaseWidget::OnMouseExit() { } @@ -290,7 +336,13 @@ namespace Ndk Nz::Vector2f widgetPos = Nz::Vector2f(GetPosition()); Nz::Vector2f widgetSize = GetSize(); - Nz::Recti fullBounds(Nz::Rectf(widgetPos.x, widgetPos.y, widgetSize.x, widgetSize.y)); + Nz::Rectf widgetRect(widgetPos.x, widgetPos.y, widgetSize.x, widgetSize.y); + Nz::Rectf widgetRenderingRect(widgetPos.x + m_renderingRect.x, widgetPos.y + m_renderingRect.y, m_renderingRect.width, m_renderingRect.height); + + Nz::Rectf widgetBounds; + widgetRect.Intersect(widgetRenderingRect, &widgetBounds); + + Nz::Recti fullBounds(widgetBounds); for (WidgetEntity& widgetEntity : m_entities) { const Ndk::EntityHandle& entity = widgetEntity.handle; diff --git a/SDK/src/NDK/Canvas.cpp b/SDK/src/NDK/Canvas.cpp index 3959f1765..aced59e4e 100644 --- a/SDK/src/NDK/Canvas.cpp +++ b/SDK/src/NDK/Canvas.cpp @@ -61,7 +61,7 @@ namespace Ndk } } - void Canvas::OnEventMouseButtonRelease(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseButtonEvent & event) + void Canvas::OnEventMouseButtonRelease(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseButtonEvent& event) { if (m_hoveredWidget != InvalidCanvasIndex) { @@ -128,6 +128,19 @@ namespace Ndk } } + void Canvas::OnEventMouseWheelMoved(const Nz::EventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseWheelEvent& event) + { + if (m_hoveredWidget != InvalidCanvasIndex) + { + WidgetEntry& hoveredWidget = m_widgetEntries[m_hoveredWidget]; + + int x = static_cast(std::round(event.x - hoveredWidget.box.x)); + int y = static_cast(std::round(event.y - hoveredWidget.box.y)); + + hoveredWidget.widget->OnMouseWheelMoved(x, y, event.delta); + } + } + void Canvas::OnEventMouseLeft(const Nz::EventHandler* /*eventHandler*/) { if (m_hoveredWidget != InvalidCanvasIndex) diff --git a/SDK/src/NDK/Console.cpp b/SDK/src/NDK/Console.cpp index e5b4f1306..84d9b08e4 100644 --- a/SDK/src/NDK/Console.cpp +++ b/SDK/src/NDK/Console.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include ///TODO: For now is unable to display different color in the history, it needs a RichTextDrawer to do so @@ -34,71 +35,63 @@ namespace Ndk * \param instance Lua instance that will interact with the world */ - Console::Console(World& world, const Nz::Vector2f& size, Nz::LuaState& state) : + Console::Console(BaseWidget* parent, Nz::LuaState& state) : + BaseWidget(parent), m_historyPosition(0), m_defaultFont(Nz::Font::GetDefault()), m_state(state), - m_size(size), - m_opened(false), m_characterSize(24) { - Nz::MaterialRef backgroundMaterial = Nz::Material::New(); - backgroundMaterial->EnableBlending(true); - backgroundMaterial->EnableDepthBuffer(false); - backgroundMaterial->SetDstBlend(Nz::BlendFunc_InvSrcAlpha); - backgroundMaterial->SetSrcBlend(Nz::BlendFunc_SrcAlpha); - - // History bakckground - m_historyBackgroundSprite = Nz::Sprite::New(); - m_historyBackgroundSprite->SetColor(Nz::Color(80, 80, 160, 128)); - m_historyBackgroundSprite->SetMaterial(backgroundMaterial); - - m_historyBackground = world.CreateEntity(); - m_historyBackground->Enable(m_opened); - m_historyBackground->AddComponent().Attach(m_historyBackgroundSprite, -1); - m_historyBackground->AddComponent().SetParent(this); - // History - m_historyDrawer.SetCharacterSize(m_characterSize); - m_historyDrawer.SetColor(Nz::Color(200, 200, 200)); - m_historyDrawer.SetFont(m_defaultFont); + m_history = Add(); + m_history->EnableBackground(true); + m_history->SetReadOnly(true); + m_history->SetBackgroundColor(Nz::Color(80, 80, 160, 128)); - m_historyTextSprite = Nz::TextSprite::New(); - - m_history = world.CreateEntity(); - m_history->Enable(m_opened); - m_history->AddComponent().Attach(m_historyTextSprite); - - Ndk::NodeComponent& historyNode = m_history->AddComponent(); - historyNode.SetParent(this); - - // Input background - m_inputBackgroundSprite = Nz::Sprite::New(); - m_inputBackgroundSprite->SetColor(Nz::Color(255, 255, 255, 200)); - m_inputBackgroundSprite->SetMaterial(backgroundMaterial); - - m_inputBackground = world.CreateEntity(); - m_inputBackground->Enable(m_opened); - m_inputBackground->AddComponent().Attach(m_inputBackgroundSprite, -1); - m_inputBackground->AddComponent().SetParent(this); + m_historyArea = Add(m_history); // Input - m_inputDrawer.SetColor(Nz::Color::Black); - m_inputDrawer.SetCharacterSize(m_characterSize); - m_inputDrawer.SetFont(m_defaultFont); - m_inputDrawer.SetText(s_inputPrefix); + m_input = Add(); + m_input->EnableBackground(true); + m_input->SetText(s_inputPrefix); + m_input->SetTextColor(Nz::Color::Black); - m_inputTextSprite = Nz::TextSprite::New(); - m_inputTextSprite->Update(m_inputDrawer); + m_input->OnTextAreaKeyReturn.Connect(this, &Console::ExecuteInput); - m_input = world.CreateEntity(); - m_input->Enable(m_opened); - m_input->AddComponent().Attach(m_inputTextSprite); + // Protect input prefix from erasure/selection + m_input->SetCursorPosition(s_inputPrefixSize); - Ndk::NodeComponent& inputNode = m_input->AddComponent(); - inputNode.SetParent(this); + m_input->OnTextAreaCursorMove.Connect([](const TextAreaWidget* textArea, std::size_t* newCursorPos) + { + *newCursorPos = std::max(*newCursorPos, s_inputPrefixSize); + }); - Layout(); + m_input->OnTextAreaKeyBackspace.Connect([](const TextAreaWidget* textArea, bool* ignoreDefaultAction) + { + if (textArea->GetGlyphIndex() <= s_inputPrefixSize) + *ignoreDefaultAction = true; + }); + + // Handle history + m_input->OnTextAreaKeyUp.Connect([&] (const TextAreaWidget* textArea, bool* ignoreDefaultAction) + { + *ignoreDefaultAction = true; + + if (m_historyPosition > 0) + m_historyPosition--; + + m_input->SetText(s_inputPrefix + m_commandHistory[m_historyPosition]); + }); + + m_input->OnTextAreaKeyDown.Connect([&] (const TextAreaWidget* textArea, bool* ignoreDefaultAction) + { + *ignoreDefaultAction = true; + + if (++m_historyPosition >= m_commandHistory.size()) + m_historyPosition = 0; + + m_input->SetText(s_inputPrefix + m_commandHistory[m_historyPosition]); + }); } /*! @@ -107,110 +100,37 @@ namespace Ndk * \param text New line of text * \param color Color for the text */ - void Console::AddLine(const Nz::String& text, const Nz::Color& color) { - AddLineInternal(text, color); - RefreshHistory(); + m_historyLines.emplace_back(Line{ color, text }); + m_history->AppendText(text + '\n'); + m_history->Resize(m_history->GetPreferredSize()); + m_historyArea->Resize(m_historyArea->GetSize()); + m_historyArea->ScrollToRatio(1.f); } /*! * \brief Clears the console + * + * Clears the console history and input */ - void Console::Clear() { m_historyLines.clear(); - RefreshHistory(); + m_history->Clear(); + m_history->Resize(m_history->GetPreferredSize()); + m_historyArea->Resize(m_historyArea->GetSize()); + m_input->SetText(s_inputPrefix); } /*! - * \brief Sends a character to the console + * \brief Clears the console focus * - * \param character Character that will be added to the console + * Clear console input widget focus (if owned) */ - - void Console::SendCharacter(char32_t character) + void Console::ClearFocus() { - switch (character) - { - case '\b': - { - Nz::String input = m_inputDrawer.GetText(); - if (input.GetLength() <= s_inputPrefixSize) // Prevent removal of the input prefix - return; // Ignore if no user character is there - - input.Resize(-1, Nz::String::HandleUtf8); - m_inputDrawer.SetText(input); - break; - } - - case '\r': - case '\n': - ExecuteInput(); - break; - - default: - { - if (Nz::Unicode::GetCategory(character) == Nz::Unicode::Category_Other_Control) - return; - - m_inputDrawer.AppendText(Nz::String::Unicode(character)); - break; - } - } - - m_inputTextSprite->Update(m_inputDrawer); - } - - /*! - * \brief Sends an event to the console - * - * \param event Event to be takin into consideration by the console - */ - void Console::SendEvent(const Nz::WindowEvent& event) - { - switch (event.type) - { - case Nz::WindowEventType_TextEntered: - SendCharacter(event.text.character); - break; - - case Nz::WindowEventType_KeyPressed: - { - switch (event.key.code) - { - case Nz::Keyboard::Down: - case Nz::Keyboard::Up: - { - if (event.key.code == Nz::Keyboard::Up) - m_historyPosition = std::min(m_commandHistory.size(), m_historyPosition + 1); - else - { - if (m_historyPosition > 1) - m_historyPosition--; - else if (m_historyPosition == 0) - m_historyPosition = 1; - } - - if (!m_commandHistory.empty()) - { - Nz::String text = m_commandHistory[m_commandHistory.size() - m_historyPosition]; - m_inputDrawer.SetText(s_inputPrefix + text); - m_inputTextSprite->Update(m_inputDrawer); - } - break; - } - - default: - break; - } - break; - } - - default: - break; - } + m_input->ClearFocus(); } /*! @@ -218,30 +138,23 @@ namespace Ndk * * \param size Size of the font */ - void Console::SetCharacterSize(unsigned int size) { m_characterSize = size; - m_historyDrawer.SetCharacterSize(m_characterSize); - m_historyTextSprite->Update(m_historyDrawer); - m_inputDrawer.SetCharacterSize(m_characterSize); - m_inputTextSprite->Update(m_inputDrawer); + m_history->SetCharacterSize(size); + m_input->SetCharacterSize(size); Layout(); } /*! - * \brief Sets the console size + * \brief Give the console input focus * - * \param size (Width, Height) of the console */ - - void Console::SetSize(const Nz::Vector2f& size) + void Console::SetFocus() { - m_size = size; - m_historyBackgroundSprite->SetSize(m_size); - Layout(); + m_input->SetFocus(); } /*! @@ -251,70 +164,38 @@ namespace Ndk * * \remark Produces a NazaraAssert if font is invalid or null */ - void Console::SetTextFont(Nz::FontRef font) { NazaraAssert(font && font->IsValid(), "Invalid font"); m_defaultFont = std::move(font); - m_historyDrawer.SetFont(m_defaultFont); - m_inputDrawer.SetFont(m_defaultFont); + //m_historyDrawer.SetFont(m_defaultFont); + //m_inputDrawer.SetFont(m_defaultFont); Layout(); } - /*! - * \brief Shows the console - * - * \param show Should the console be showed - */ - - void Console::Show(bool show) - { - if (m_opened != show) - { - m_historyBackground->Enable(show); - m_history->Enable(show); - m_input->Enable(show); - m_inputBackground->Enable(show); - - m_opened = show; - } - } - - /*! - * \brief Adds a line to the history of the console - * - * \param text New line of text - * \param color Color for the text - */ - - void Console::AddLineInternal(const Nz::String& text, const Nz::Color& color) - { - m_historyLines.emplace_back(Line{color, text}); - } - /*! * \brief Performs this action when an input is added to the console */ - void Console::ExecuteInput() + void Console::ExecuteInput(const TextAreaWidget* textArea, bool* ignoreDefaultAction) { - Nz::String input = m_inputDrawer.GetText(); + NazaraAssert(textArea == m_input, "Unexpected signal from an other text area"); + + Nz::String input = m_input->GetText(); Nz::String inputCmd = input.SubString(s_inputPrefixSize); - m_inputDrawer.SetText(s_inputPrefix); + m_input->SetText(s_inputPrefix); if (m_commandHistory.empty() || m_commandHistory.back() != inputCmd) m_commandHistory.push_back(inputCmd); - m_historyPosition = 0; + m_historyPosition = m_commandHistory.size(); - AddLineInternal(input); //< With the input prefix + AddLine(input); //< With the input prefix if (!m_state.Execute(inputCmd)) - AddLineInternal(m_state.GetLastError(), Nz::Color::Red); - - RefreshHistory(); + AddLine(m_state.GetLastError(), Nz::Color::Red); } /*! @@ -323,49 +204,19 @@ namespace Ndk void Console::Layout() { + Nz::Vector2f origin = Nz::Vector2f(GetPosition()); + const Nz::Vector2f& size = GetSize(); + unsigned int lineHeight = m_defaultFont->GetSizeInfo(m_characterSize).lineHeight; - - Ndk::NodeComponent& inputNode = m_input->GetComponent(); - inputNode.SetPosition(0.f, m_size.y - lineHeight - 5.f); - - float historyHeight = m_size.y - lineHeight - 5.f - 2.f; - m_historyBackgroundSprite->SetSize(m_size.x, historyHeight); + float historyHeight = size.y - lineHeight; m_maxHistoryLines = static_cast(std::ceil(historyHeight / lineHeight)); + float diff = historyHeight - m_maxHistoryLines * lineHeight; - Ndk::NodeComponent& historyNode = m_history->GetComponent(); - historyNode.SetPosition(0.f, historyHeight - m_maxHistoryLines * lineHeight); + m_historyArea->SetPosition(origin.x, origin.y + diff); + m_historyArea->Resize({ size.x, historyHeight - diff - 4.f }); - Ndk::NodeComponent& inputBackgroundNode = m_inputBackground->GetComponent(); - inputBackgroundNode.SetPosition(0.f, historyHeight + 2.f); - - m_inputBackgroundSprite->SetSize(m_size.x, m_size.y - historyHeight); - } - - /*! - * \brief Refreshes the history of the console - */ - - void Console::RefreshHistory() - { - m_historyDrawer.Clear(); - auto it = m_historyLines.end(); - if (m_historyLines.size() > m_maxHistoryLines) - it -= m_maxHistoryLines; - else - it = m_historyLines.begin(); - - for (unsigned int i = 0; i < m_maxHistoryLines; ++i) - { - if (m_maxHistoryLines - i <= m_historyLines.size() && it != m_historyLines.end()) - { - m_historyDrawer.AppendText(it->text); - ++it; - } - - m_historyDrawer.AppendText(Nz::String('\n')); - } - - m_historyTextSprite->Update(m_historyDrawer); + m_input->Resize({size.x, size.y - historyHeight}); + m_input->SetPosition(origin.x, origin.y + historyHeight); } } diff --git a/SDK/src/NDK/Entity.cpp b/SDK/src/NDK/Entity.cpp index 7bc81982d..4e211f0db 100644 --- a/SDK/src/NDK/Entity.cpp +++ b/SDK/src/NDK/Entity.cpp @@ -111,11 +111,15 @@ namespace Ndk { for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i)) m_components[i]->OnEntityEnabled(); + + OnEntityEnabled(this); } else { for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i)) m_components[i]->OnEntityDisabled(); + + OnEntityDisabled(this); } Invalidate(); diff --git a/SDK/src/NDK/Lua/LuaBinding_SDK.cpp b/SDK/src/NDK/Lua/LuaBinding_SDK.cpp index 36c28d973..805f7742d 100644 --- a/SDK/src/NDK/Lua/LuaBinding_SDK.cpp +++ b/SDK/src/NDK/Lua/LuaBinding_SDK.cpp @@ -62,24 +62,14 @@ namespace Ndk console.BindMethod("AddLine", &Console::AddLine, Nz::Color::White); console.BindMethod("Clear", &Console::Clear); console.BindMethod("GetCharacterSize", &Console::GetCharacterSize); - console.BindMethod("GetHistory", &Console::GetHistory); - console.BindMethod("GetHistoryBackground", &Console::GetHistoryBackground); - console.BindMethod("GetInput", &Console::GetInput); - console.BindMethod("GetInputBackground", &Console::GetInputBackground); - console.BindMethod("GetSize", &Console::GetSize); + //console.BindMethod("GetHistory", &Console::GetHistory); + //console.BindMethod("GetInput", &Console::GetInput); console.BindMethod("GetTextFont", &Console::GetTextFont); console.BindMethod("IsValidHandle", &ConsoleHandle::IsValid); - console.BindMethod("IsVisible", &Console::IsVisible); - - console.BindMethod("SendCharacter", &Console::SendCharacter); - //consoleClass.SetMethod("SendEvent", &Console::SendEvent); console.BindMethod("SetCharacterSize", &Console::SetCharacterSize); - console.BindMethod("SetSize", &Console::SetSize); console.BindMethod("SetTextFont", &Console::SetTextFont); - - console.BindMethod("Show", &Console::Show, true); } #endif diff --git a/SDK/src/NDK/Widgets/ScrollAreaWidget.cpp b/SDK/src/NDK/Widgets/ScrollAreaWidget.cpp new file mode 100644 index 000000000..4f5ca3f6b --- /dev/null +++ b/SDK/src/NDK/Widgets/ScrollAreaWidget.cpp @@ -0,0 +1,184 @@ +// Copyright (C) 2019 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequisites.hpp + +#include +#include +#include +#include + +namespace Ndk +{ + namespace + { + constexpr float scrollbarPadding = 5.f; + } + + ScrollAreaWidget::ScrollAreaWidget(BaseWidget* parent, BaseWidget* content) : + BaseWidget(parent), + m_content(content), + m_scrollbarStatus(ScrollBarStatus::None), + m_scrollRatio(0.f) + { + m_content->SetParent(this); + m_content->SetPosition(Nz::Vector3f::Zero()); + + m_scrollbarBackgroundSprite = Nz::Sprite::New(); + m_scrollbarBackgroundSprite->SetColor(Nz::Color(62, 62, 62)); + m_scrollbarBackgroundSprite->SetMaterial("Basic2D"); + + m_scrollbarBackgroundEntity = CreateEntity(); + m_scrollbarBackgroundEntity->AddComponent().SetParent(this); + m_scrollbarBackgroundEntity->AddComponent().Attach(m_scrollbarBackgroundSprite, 1); + + m_scrollbarSprite = Nz::Sprite::New(); + m_scrollbarSprite->SetColor(Nz::Color(104, 104, 104)); + m_scrollbarSprite->SetMaterial("Basic2D"); + + m_scrollbarEntity = CreateEntity(); + m_scrollbarEntity->AddComponent().SetParent(this); + m_scrollbarEntity->AddComponent().Attach(m_scrollbarSprite); + + Resize(m_content->GetSize()); + } + + void ScrollAreaWidget::ScrollToRatio(float ratio) + { + m_scrollRatio = Nz::Clamp(ratio, 0.f, 1.f); + + float widgetHeight = GetHeight(); + float maxHeight = widgetHeight - m_scrollbarSprite->GetSize().y - 2.f * scrollbarPadding; + + auto& scrollbarNode = m_scrollbarEntity->GetComponent(); + scrollbarNode.SetPosition(Nz::Vector2f(scrollbarNode.GetPosition(Nz::CoordSys_Local).x, scrollbarPadding + m_scrollRatio * maxHeight)); + + float contentPosition = m_scrollRatio * (widgetHeight - m_content->GetHeight()); + + m_content->SetPosition(0.f, contentPosition); + m_content->SetRenderingRect(Nz::Rectf(-std::numeric_limits::infinity(), -contentPosition, std::numeric_limits::infinity(), widgetHeight)); + } + + Nz::Rectf ScrollAreaWidget::GetScrollbarRect() const + { + Nz::Vector2f scrollBarPosition = Nz::Vector2f(m_scrollbarEntity->GetComponent().GetPosition(Nz::CoordSys_Local)); + Nz::Vector2f scrollBarSize = m_scrollbarSprite->GetSize(); + return Nz::Rectf(scrollBarPosition.x, scrollBarPosition.y, scrollBarSize.x, scrollBarSize.y); + } + + void ScrollAreaWidget::Layout() + { + constexpr float scrollBarBackgroundWidth = 20.f; + constexpr float scrollBarWidth = scrollBarBackgroundWidth - 2.f * scrollbarPadding; + + float areaHeight = GetHeight(); + float contentHeight = m_content->GetHeight(); + + if (contentHeight > areaHeight) + { + m_isScrollBarVisible = true; + + Nz::Vector2f contentSize(GetWidth() - scrollBarBackgroundWidth, contentHeight); + m_content->Resize(contentSize); + + m_scrollbarEntity->Enable(); + m_scrollbarBackgroundEntity->Enable(); + + float scrollBarHeight = std::max(std::floor(areaHeight * (areaHeight / contentHeight)), 20.f); + + m_scrollbarBackgroundSprite->SetSize(scrollBarBackgroundWidth, areaHeight); + m_scrollbarSprite->SetSize(scrollBarWidth, scrollBarHeight); + + m_scrollbarBackgroundEntity->GetComponent().SetPosition(contentSize.x, 0.f); + m_scrollbarEntity->GetComponent().SetPosition(contentSize.x + (scrollBarBackgroundWidth - scrollBarWidth) / 2.f, 0.f); + + ScrollToRatio(m_scrollRatio); + } + else + { + m_isScrollBarVisible = false; + + m_content->Resize(GetSize()); + + m_scrollbarEntity->Disable(); + m_scrollbarBackgroundEntity->Disable(); + + ScrollToRatio(0.f); + } + + BaseWidget::Layout(); + } + + void ScrollAreaWidget::OnMouseButtonPress(int x, int y, Nz::Mouse::Button button) + { + if (button != Nz::Mouse::Left) + return; + + if (m_scrollbarStatus == ScrollBarStatus::Hovered) + { + UpdateScrollbarStatus(ScrollBarStatus::Grabbed); + + auto& scrollbarNode = m_scrollbarEntity->GetComponent(); + + m_grabbedDelta.Set(x, y - scrollbarNode.GetPosition(Nz::CoordSys_Local).y); + } + } + + void ScrollAreaWidget::OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button) + { + if (button != Nz::Mouse::Left) + return; + + if (m_scrollbarStatus == ScrollBarStatus::Grabbed) + { + Nz::Rectf scrollBarRect = GetScrollbarRect(); + UpdateScrollbarStatus((scrollBarRect.Contains(Nz::Vector2f(x, y))) ? ScrollBarStatus::Hovered : ScrollBarStatus::None); + } + } + + void ScrollAreaWidget::OnMouseExit() + { + //if (m_scrollbarStatus == ScrollBarStatus::Hovered) + UpdateScrollbarStatus(ScrollBarStatus::None); + } + + void ScrollAreaWidget::OnMouseMoved(int x, int y, int /*deltaX*/, int /*deltaY*/) + { + if (m_scrollbarStatus == ScrollBarStatus::Grabbed) + { + float height = GetHeight(); + float maxHeight = height - m_scrollbarSprite->GetSize().y; + float newHeight = Nz::Clamp(float(y - m_grabbedDelta.y), 0.f, maxHeight); + + ScrollToHeight(newHeight / maxHeight * m_content->GetHeight()); + } + else + { + Nz::Rectf scrollBarRect = GetScrollbarRect(); + UpdateScrollbarStatus((scrollBarRect.Contains(Nz::Vector2f(x, y))) ? ScrollBarStatus::Hovered : ScrollBarStatus::None); + } + } + + void ScrollAreaWidget::OnMouseWheelMoved(int /*x*/, int /*y*/, float delta) + { + constexpr float scrollStep = 100.f; + + ScrollToHeight(GetScrollHeight() - scrollStep * delta); + } + + void ScrollAreaWidget::UpdateScrollbarStatus(ScrollBarStatus status) + { + if (m_scrollbarStatus != status) + { + Nz::Color newColor; + switch (status) + { + case ScrollBarStatus::Grabbed: newColor = Nz::Color(235, 235, 235); break; + case ScrollBarStatus::Hovered: newColor = Nz::Color(152, 152, 152); break; + case ScrollBarStatus::None: newColor = Nz::Color(104, 104, 104); break; + } + + m_scrollbarSprite->SetColor(newColor); + m_scrollbarStatus = status; + } + } +} diff --git a/SDK/src/NDK/Widgets/TextAreaWidget.cpp b/SDK/src/NDK/Widgets/TextAreaWidget.cpp index 09355c09c..9b946eac1 100644 --- a/SDK/src/NDK/Widgets/TextAreaWidget.cpp +++ b/SDK/src/NDK/Widgets/TextAreaWidget.cpp @@ -31,8 +31,10 @@ namespace Ndk m_textEntity = CreateEntity(); m_textEntity->AddComponent().Attach(m_textSprite); - m_textEntity->AddComponent().SetParent(this); - m_textEntity->GetComponent().SetPosition(5.f, 3.f); + + auto& textNode = m_textEntity->AddComponent(); + textNode.SetParent(this); + textNode.SetPosition(5.f, 3.f); SetCursor(Nz::SystemCursor_Text); SetCharacterSize(GetCharacterSize()); //< Actualize minimum / preferred size @@ -73,6 +75,7 @@ namespace Ndk } m_textSprite->Update(m_drawer); + SetPreferredSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); OnTextChanged(this, m_text); } @@ -163,7 +166,6 @@ namespace Ndk Nz::Vector2f size = { float(spaceAdvance), float(lineHeight) + 5.f }; SetMinimumSize(size); - SetPreferredSize({ size.x * 6.f, size.y }); } void TextAreaWidget::Write(const Nz::String& text, std::size_t glyphPosition) @@ -606,6 +608,7 @@ namespace Ndk } m_textSprite->Update(m_drawer); + SetPreferredSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); SetCursorPosition(m_cursorPositionBegin); //< Refresh cursor position (prevent it from being outside of the text) } diff --git a/include/Nazara/Platform/Event.hpp b/include/Nazara/Platform/Event.hpp index 1fe219e8e..02a842889 100644 --- a/include/Nazara/Platform/Event.hpp +++ b/include/Nazara/Platform/Event.hpp @@ -55,6 +55,8 @@ namespace Nz struct MouseWheelEvent { float delta; + int x; + int y; }; // Used by: diff --git a/src/Nazara/Platform/Win32/WindowImpl.cpp b/src/Nazara/Platform/Win32/WindowImpl.cpp index 27b4fa889..6ee3365ac 100644 --- a/src/Nazara/Platform/Win32/WindowImpl.cpp +++ b/src/Nazara/Platform/Win32/WindowImpl.cpp @@ -712,7 +712,10 @@ namespace Nz { WindowEvent event; event.type = WindowEventType_MouseWheelMoved; - event.mouseWheel.delta = static_cast(GET_WHEEL_DELTA_WPARAM(wParam))/WHEEL_DELTA; + event.mouseWheel.delta = static_cast(GET_WHEEL_DELTA_WPARAM(wParam)) / WHEEL_DELTA; + event.mouseWheel.x = GET_X_LPARAM(lParam); + event.mouseWheel.y = GET_Y_LPARAM(lParam); + m_parent->PushEvent(event); } else @@ -722,7 +725,10 @@ namespace Nz { WindowEvent event; event.type = WindowEventType_MouseWheelMoved; - event.mouseWheel.delta = static_cast(m_scrolling/WHEEL_DELTA); + event.mouseWheel.delta = static_cast(m_scrolling / WHEEL_DELTA); + event.mouseWheel.x = GET_X_LPARAM(lParam); + event.mouseWheel.y = GET_Y_LPARAM(lParam); + m_parent->PushEvent(event); m_scrolling %= WHEEL_DELTA; diff --git a/src/Nazara/Platform/X11/WindowImpl.cpp b/src/Nazara/Platform/X11/WindowImpl.cpp index 0e8215e37..abf2db675 100644 --- a/src/Nazara/Platform/X11/WindowImpl.cpp +++ b/src/Nazara/Platform/X11/WindowImpl.cpp @@ -1233,6 +1233,8 @@ namespace Nz { event.type = Nz::WindowEventType_MouseWheelMoved; event.mouseWheel.delta = (buttonReleaseEvent->detail == XCB_BUTTON_INDEX_4) ? 1 : -1; + event.mouseWheel.x = buttonReleaseEvent->event_x; + event.mouseWheel.y = buttonReleaseEvent->event_y; break; } default: