diff --git a/include/Nazara/Widgets.hpp b/include/Nazara/Widgets.hpp index 008d7f794..413c12a52 100644 --- a/include/Nazara/Widgets.hpp +++ b/include/Nazara/Widgets.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/include/Nazara/Widgets/CheckboxWidget.hpp b/include/Nazara/Widgets/CheckboxWidget.hpp new file mode 100644 index 000000000..5336ba6b2 --- /dev/null +++ b/include/Nazara/Widgets/CheckboxWidget.hpp @@ -0,0 +1,59 @@ +// Copyright (C) 2021 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Widgets module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_WIDGETS_CHECKBOXWIDGET_HPP +#define NAZARA_WIDGETS_CHECKBOXWIDGET_HPP + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class MaterialPass; + + class NAZARA_WIDGETS_API CheckboxWidget : public BaseWidget + { + public: + CheckboxWidget(BaseWidget* parent); + CheckboxWidget(const CheckboxWidget&) = delete; + CheckboxWidget(CheckboxWidget&&) = default; + ~CheckboxWidget() = default; + + inline void EnableTristate(bool enabled); + + inline bool IsTristateEnabled() const; + + inline void SetState(bool checkboxState); + void SetState(CheckboxState checkboxState); + + inline void SwitchToNextState(); + + CheckboxWidget& operator=(const CheckboxWidget&) = delete; + CheckboxWidget& operator=(CheckboxWidget&&) = default; + + NazaraSignal(OnCheckboxStateUpdate, const CheckboxWidget* /*button*/, CheckboxState /*newState*/); + + private: + void Layout() override; + + void OnMouseEnter() override; + void OnMouseButtonPress(int x, int y, Mouse::Button button) override; + void OnMouseButtonRelease(int x, int y, Mouse::Button button) override; + void OnMouseExit() override; + + std::unique_ptr m_style; + CheckboxState m_state; + bool m_isTristateEnabled; + }; +} + +#include + +#endif // NAZARA_WIDGETS_CHECKBOXWIDGET_HPP diff --git a/include/Nazara/Widgets/CheckboxWidget.inl b/include/Nazara/Widgets/CheckboxWidget.inl new file mode 100644 index 000000000..fc640c4e7 --- /dev/null +++ b/include/Nazara/Widgets/CheckboxWidget.inl @@ -0,0 +1,46 @@ +// Copyright (C) 2021 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Widgets module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + inline void CheckboxWidget::EnableTristate(bool enabled) + { + m_isTristateEnabled = enabled; + } + + inline bool CheckboxWidget::IsTristateEnabled() const + { + return m_isTristateEnabled; + } + + void CheckboxWidget::SetState(bool checkboxState) + { + return SetState((checkboxState) ? CheckboxState::Checked : CheckboxState::Unchecked); + } + + inline void CheckboxWidget::SwitchToNextState() + { + switch (m_state) + { + case CheckboxState::Checked: + SetState(CheckboxState::Unchecked); + break; + + case CheckboxState::Unchecked: + { + SetState((m_isTristateEnabled) ? CheckboxState::Tristate : CheckboxState::Checked); + break; + } + + case CheckboxState::Tristate: + SetState(CheckboxState::Checked); + break; + } + } +} + +#include diff --git a/include/Nazara/Widgets/DefaultWidgetTheme.hpp b/include/Nazara/Widgets/DefaultWidgetTheme.hpp index 8590c2b37..efcca6c69 100644 --- a/include/Nazara/Widgets/DefaultWidgetTheme.hpp +++ b/include/Nazara/Widgets/DefaultWidgetTheme.hpp @@ -23,6 +23,7 @@ namespace Nz ~DefaultWidgetTheme() = default; std::unique_ptr CreateStyle(ButtonWidget* buttonWidget) const override; + std::unique_ptr CreateStyle(CheckboxWidget* buttonWidget) const override; std::unique_ptr CreateStyle(LabelWidget* buttonWidget) const override; DefaultWidgetTheme& operator=(const DefaultWidgetTheme&) = delete; @@ -33,6 +34,10 @@ namespace Nz std::shared_ptr m_buttonHoveredMaterial; std::shared_ptr m_buttonPressedHoveredMaterial; std::shared_ptr m_buttonPressedMaterial; + std::shared_ptr m_checkboxBackgroundMaterial; + std::shared_ptr m_checkboxBackgroundHoveredMaterial; + std::shared_ptr m_checkboxCheckMaterial; + std::shared_ptr m_checkboxTristateMaterial; }; } diff --git a/include/Nazara/Widgets/SimpleWidgetStyles.hpp b/include/Nazara/Widgets/SimpleWidgetStyles.hpp index cac9a26f7..84c263882 100644 --- a/include/Nazara/Widgets/SimpleWidgetStyles.hpp +++ b/include/Nazara/Widgets/SimpleWidgetStyles.hpp @@ -61,6 +61,50 @@ namespace Nz bool m_isHovered; bool m_isPressed; }; + + class NAZARA_WIDGETS_API SimpleCheckboxWidgetStyle : public CheckboxWidgetStyle + { + public: + struct StyleConfig; + + SimpleCheckboxWidgetStyle(CheckboxWidget* checkboxWidget, StyleConfig config); + SimpleCheckboxWidgetStyle(const SimpleCheckboxWidgetStyle&) = delete; + SimpleCheckboxWidgetStyle(SimpleCheckboxWidgetStyle&&) = default; + ~SimpleCheckboxWidgetStyle() = default; + + void Layout(const Vector2f& size) override; + + void OnHoverBegin() override; + void OnHoverEnd() override; + void OnNewState(CheckboxState newState) override; + + SimpleCheckboxWidgetStyle& operator=(const SimpleCheckboxWidgetStyle&) = delete; + SimpleCheckboxWidgetStyle& operator=(SimpleCheckboxWidgetStyle&&) = default; + + struct StyleConfig + { + std::shared_ptr backgroundMaterial; + std::shared_ptr backgroundHoveredMaterial; + std::shared_ptr checkMaterial; + std::shared_ptr tristateMaterial; + float backgroundCornerSize; + float backgroundCornerTexCoords; + }; + + protected: + virtual void UpdateMaterial(bool hovered); + + private: + std::shared_ptr m_checkMaterial; + std::shared_ptr m_hoveredMaterial; + std::shared_ptr m_material; + std::shared_ptr m_tristateMaterial; + std::shared_ptr m_checkSprite; + std::shared_ptr m_backgroundSprite; + entt::entity m_backgroundEntity; + entt::entity m_checkEntity; + bool m_isHovered; + }; class NAZARA_WIDGETS_API SimpleLabelWidgetStyle : public LabelWidgetStyle { diff --git a/include/Nazara/Widgets/WidgetTheme.hpp b/include/Nazara/Widgets/WidgetTheme.hpp index 8cfe8e4a2..131f3ac50 100644 --- a/include/Nazara/Widgets/WidgetTheme.hpp +++ b/include/Nazara/Widgets/WidgetTheme.hpp @@ -11,12 +11,15 @@ #include #include #include +#include namespace Nz { class AbstractTextDrawer; class ButtonWidget; class ButtonWidgetStyle; + class CheckboxWidget; + class CheckboxWidgetStyle; class LabelWidget; class LabelWidgetStyle; @@ -29,6 +32,7 @@ namespace Nz virtual ~WidgetTheme(); virtual std::unique_ptr CreateStyle(ButtonWidget* buttonWidget) const = 0; + virtual std::unique_ptr CreateStyle(CheckboxWidget* buttonWidget) const = 0; virtual std::unique_ptr CreateStyle(LabelWidget* buttonWidget) const = 0; WidgetTheme& operator=(const WidgetTheme&) = delete; @@ -81,6 +85,26 @@ namespace Nz ButtonWidgetStyle& operator=(ButtonWidgetStyle&&) = default; }; + class NAZARA_WIDGETS_API CheckboxWidgetStyle : public BaseWidgetStyle + { + public: + using BaseWidgetStyle::BaseWidgetStyle; + CheckboxWidgetStyle(const CheckboxWidgetStyle&) = delete; + CheckboxWidgetStyle(CheckboxWidgetStyle&&) = default; + ~CheckboxWidgetStyle() = default; + + virtual void Layout(const Vector2f& size) = 0; + + virtual void OnHoverBegin(); + virtual void OnHoverEnd(); + virtual void OnNewState(CheckboxState newState); + virtual void OnPress(); + virtual void OnRelease(); + + CheckboxWidgetStyle& operator=(const CheckboxWidgetStyle&) = delete; + CheckboxWidgetStyle& operator=(CheckboxWidgetStyle&&) = default; + }; + class NAZARA_WIDGETS_API LabelWidgetStyle : public BaseWidgetStyle { public: diff --git a/src/Nazara/Widgets/CheckboxWidget.cpp b/src/Nazara/Widgets/CheckboxWidget.cpp new file mode 100644 index 000000000..6694b14d7 --- /dev/null +++ b/src/Nazara/Widgets/CheckboxWidget.cpp @@ -0,0 +1,71 @@ +// Copyright (C) 2021 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Widgets module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + CheckboxWidget::CheckboxWidget(BaseWidget* parent) : + BaseWidget(parent), + m_state(CheckboxState::Unchecked), + m_isTristateEnabled(false) + { + m_style = GetTheme()->CreateStyle(this); + + Layout(); + } + + void CheckboxWidget::SetState(CheckboxState checkboxState) + { + OnCheckboxStateUpdate(this, checkboxState); + m_state = checkboxState; + + m_style->OnNewState(m_state); + } + + void CheckboxWidget::Layout() + { + BaseWidget::Layout(); + m_style->Layout(GetSize()); + } + + void CheckboxWidget::OnMouseButtonPress(int /*x*/, int /*y*/, Mouse::Button button) + { + if (button == Mouse::Left) + m_style->OnPress(); + } + + void CheckboxWidget::OnMouseButtonRelease(int x, int y, Mouse::Button button) + { + if (button == Mouse::Left) + { + m_style->OnRelease(); + + // If user clicks inside button and holds it outside, a release mouse button event will be triggered outside of the widget + // we don't want this to trigger the button, so double-check + if (IsInside(x, y)) + SwitchToNextState(); + } + } + + void CheckboxWidget::OnMouseEnter() + { + m_style->OnHoverBegin(); + } + + void CheckboxWidget::OnMouseExit() + { + m_style->OnHoverEnd(); + } +} diff --git a/src/Nazara/Widgets/DefaultWidgetTheme.cpp b/src/Nazara/Widgets/DefaultWidgetTheme.cpp index 06ec1e6f7..f9c6b901c 100644 --- a/src/Nazara/Widgets/DefaultWidgetTheme.cpp +++ b/src/Nazara/Widgets/DefaultWidgetTheme.cpp @@ -29,6 +29,22 @@ namespace Nz const UInt8 ButtonPressedHoveredImage[] = { #include }; + + const UInt8 CheckboxBackgroundImage[] = { + #include + }; + + const UInt8 CheckboxBackgroundHoveredImage[] = { + #include + }; + + const UInt8 CheckboxCheckImage[] = { + #include + }; + + const UInt8 CheckboxTristateImage[] = { + #include + }; } DefaultWidgetTheme::DefaultWidgetTheme() @@ -60,6 +76,12 @@ namespace Nz m_buttonHoveredMaterial = CreateMaterialFromTexture(Texture::LoadFromMemory(ButtonHoveredImage, sizeof(ButtonHoveredImage), texParams)); m_buttonPressedMaterial = CreateMaterialFromTexture(Texture::LoadFromMemory(ButtonPressedImage, sizeof(ButtonPressedImage), texParams)); m_buttonPressedHoveredMaterial = CreateMaterialFromTexture(Texture::LoadFromMemory(ButtonPressedHoveredImage, sizeof(ButtonPressedHoveredImage), texParams)); + + // Checkbox material + m_checkboxBackgroundMaterial = CreateMaterialFromTexture(Texture::LoadFromMemory(CheckboxBackgroundImage, sizeof(CheckboxBackgroundImage), texParams)); + m_checkboxBackgroundHoveredMaterial = CreateMaterialFromTexture(Texture::LoadFromMemory(CheckboxBackgroundHoveredImage, sizeof(CheckboxBackgroundHoveredImage), texParams)); + m_checkboxCheckMaterial = CreateMaterialFromTexture(Texture::LoadFromMemory(CheckboxCheckImage, sizeof(CheckboxCheckImage), texParams)); + m_checkboxTristateMaterial = CreateMaterialFromTexture(Texture::LoadFromMemory(CheckboxTristateImage, sizeof(CheckboxTristateImage), texParams)); } std::unique_ptr DefaultWidgetTheme::CreateStyle(ButtonWidget* buttonWidget) const @@ -75,8 +97,21 @@ namespace Nz return std::make_unique(buttonWidget, styleConfig); } - std::unique_ptr DefaultWidgetTheme::CreateStyle(LabelWidget* buttonWidget) const + std::unique_ptr DefaultWidgetTheme::CreateStyle(CheckboxWidget* checkboxWidget) const { - return std::make_unique(buttonWidget, Widgets::Instance()->GetTransparentMaterial()); + SimpleCheckboxWidgetStyle::StyleConfig styleConfig; + styleConfig.backgroundCornerSize = 10.f; + styleConfig.backgroundCornerTexCoords = 10.f / 64.f; + styleConfig.backgroundHoveredMaterial = m_checkboxBackgroundHoveredMaterial; + styleConfig.backgroundMaterial = m_checkboxBackgroundMaterial; + styleConfig.checkMaterial = m_checkboxCheckMaterial; + styleConfig.tristateMaterial = m_checkboxTristateMaterial; + + return std::make_unique(checkboxWidget, styleConfig); + } + + std::unique_ptr DefaultWidgetTheme::CreateStyle(LabelWidget* labelWidget) const + { + return std::make_unique(labelWidget, Widgets::Instance()->GetTransparentMaterial()); } } diff --git a/src/Nazara/Widgets/SimpleWidgetStyles.cpp b/src/Nazara/Widgets/SimpleWidgetStyles.cpp index cae47414b..2ce54e4d4 100644 --- a/src/Nazara/Widgets/SimpleWidgetStyles.cpp +++ b/src/Nazara/Widgets/SimpleWidgetStyles.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,92 @@ namespace Nz } + SimpleCheckboxWidgetStyle::SimpleCheckboxWidgetStyle(CheckboxWidget* buttonWidget, StyleConfig config) : + CheckboxWidgetStyle(buttonWidget), + m_checkMaterial(std::move(config.checkMaterial)), + m_hoveredMaterial(std::move(config.backgroundHoveredMaterial)), + m_material(std::move(config.backgroundMaterial)), + m_tristateMaterial(std::move(config.tristateMaterial)), + m_isHovered(false) + { + assert(m_material); + assert(m_checkMaterial); + assert(m_tristateMaterial); + + auto& registry = GetRegistry(); + UInt32 renderMask = GetRenderMask(); + + SlicedSprite::Corner corner; + corner.size.Set(config.backgroundCornerSize); + corner.textureCoords.Set(config.backgroundCornerTexCoords); + + m_backgroundSprite = std::make_shared(m_material); + m_backgroundSprite->SetCorners(corner, corner); + + m_checkSprite = std::make_shared(m_checkMaterial); + + m_backgroundEntity = CreateGraphicsEntity(); + registry.get(m_backgroundEntity).AttachRenderable(m_backgroundSprite, renderMask); + + m_checkEntity = CreateGraphicsEntity(); + { + auto& gfxComponent = registry.get(m_checkEntity); + gfxComponent.AttachRenderable(m_checkSprite, renderMask); + gfxComponent.Hide(); + } + } + + void SimpleCheckboxWidgetStyle::Layout(const Vector2f& size) + { + m_backgroundSprite->SetSize(size); + + Vector2f checkSize = size * 0.66f; + m_checkSprite->SetSize(checkSize); + + GetRegistry().get(m_checkEntity).SetPosition(size.x / 2.f - checkSize.x / 2.f, size.y / 2.f - checkSize.y / 2.f); + } + + void SimpleCheckboxWidgetStyle::OnHoverBegin() + { + m_isHovered = true; + UpdateMaterial(m_isHovered); + } + + void SimpleCheckboxWidgetStyle::OnHoverEnd() + { + m_isHovered = false; + UpdateMaterial(m_isHovered); + } + + void SimpleCheckboxWidgetStyle::OnNewState(CheckboxState newState) + { + switch (newState) + { + case CheckboxState::Unchecked: + { + GetRegistry().get(m_checkEntity).Hide(); + break; + } + + case CheckboxState::Tristate: + case CheckboxState::Checked: + { + m_checkSprite->SetMaterial((newState == CheckboxState::Checked) ? m_checkMaterial : m_tristateMaterial); + GetRegistry().get(m_checkEntity).Show(); + break; + } + } + } + + void SimpleCheckboxWidgetStyle::UpdateMaterial(bool hovered) + { + if (hovered && m_hoveredMaterial) + m_backgroundSprite->SetMaterial(m_hoveredMaterial); + else + m_backgroundSprite->SetMaterial(m_material); + } + + SimpleLabelWidgetStyle::SimpleLabelWidgetStyle(LabelWidget* labelWidget, std::shared_ptr material, std::shared_ptr hoveredMaterial) : LabelWidgetStyle(labelWidget), m_hoveredMaterial(std::move(hoveredMaterial)), diff --git a/src/Nazara/Widgets/WidgetTheme.cpp b/src/Nazara/Widgets/WidgetTheme.cpp index 823a58137..abef67580 100644 --- a/src/Nazara/Widgets/WidgetTheme.cpp +++ b/src/Nazara/Widgets/WidgetTheme.cpp @@ -48,6 +48,27 @@ namespace Nz } + void CheckboxWidgetStyle::OnHoverBegin() + { + } + + void CheckboxWidgetStyle::OnHoverEnd() + { + } + + void CheckboxWidgetStyle::OnNewState(CheckboxState newState) + { + } + + void CheckboxWidgetStyle::OnPress() + { + } + + void CheckboxWidgetStyle::OnRelease() + { + } + + void LabelWidgetStyle::OnHoverBegin() { }