From 1df57a3e341f205f5776ef6812db5b6fb17f1730 Mon Sep 17 00:00:00 2001 From: Ryo Suzuki Date: Thu, 31 Aug 2023 00:56:18 +0900 Subject: [PATCH] =?UTF-8?q?UI1=20=E4=BD=9C=E6=A5=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Siv3D/include/Siv3D.hpp | 2 + Siv3D/include/Siv3D/UI1/UIPanel.hpp | 16 +- Siv3D/include/Siv3D/UI1/UIWindow.hpp | 135 +++++++++++++++++ Siv3D/src/Siv3D/UI1/SivUIContainer.cpp | 2 +- Siv3D/src/Siv3D/UI1/SivUIPanel.cpp | 5 +- Siv3D/src/Siv3D/UI1/SivUIWindow.cpp | 202 +++++++++++++++++++++++++ WindowsDesktop/Siv3D.vcxproj | 2 + WindowsDesktop/Siv3D.vcxproj.filters | 6 + 8 files changed, 361 insertions(+), 9 deletions(-) create mode 100644 Siv3D/include/Siv3D/UI1/UIWindow.hpp create mode 100644 Siv3D/src/Siv3D/UI1/SivUIWindow.cpp diff --git a/Siv3D/include/Siv3D.hpp b/Siv3D/include/Siv3D.hpp index 5738d8a25..353459dc3 100644 --- a/Siv3D/include/Siv3D.hpp +++ b/Siv3D/include/Siv3D.hpp @@ -1718,6 +1718,8 @@ # include +# include + # include ////////////////////////////////////////////////// diff --git a/Siv3D/include/Siv3D/UI1/UIPanel.hpp b/Siv3D/include/Siv3D/UI1/UIPanel.hpp index f5ba7e398..10b8a3dbd 100644 --- a/Siv3D/include/Siv3D/UI1/UIPanel.hpp +++ b/Siv3D/include/Siv3D/UI1/UIPanel.hpp @@ -22,7 +22,7 @@ namespace s3d inline namespace UI1 { /// @brief UI 要素を配置するパネル - class UIPanel final : public UIContainer + class UIPanel : public UIContainer { public: @@ -84,17 +84,13 @@ namespace s3d /// @brief UI パネルを作成します。 /// @param name UI コンテナとしての一意な名前 - /// @param rect パネルの領域 + /// @param rect パネルの初期領域 /// @param style スタイル /// @return 作成されたパネル [[nodiscard]] static std::shared_ptr Create(UIContainerNameView name, const RectF& rect, const Style& style = Style::Default()); - private: - - Style m_style; - - RectF m_rect = RectF::Empty(); + protected: [[nodiscard]] RoundRect getShape() const noexcept; @@ -104,6 +100,12 @@ namespace s3d [[nodiscard]] void drawDebugBackground() const; + + private: + + Style m_style; + + RectF m_rect = RectF::Empty(); }; } } diff --git a/Siv3D/include/Siv3D/UI1/UIWindow.hpp b/Siv3D/include/Siv3D/UI1/UIWindow.hpp new file mode 100644 index 000000000..05c503b28 --- /dev/null +++ b/Siv3D/include/Siv3D/UI1/UIWindow.hpp @@ -0,0 +1,135 @@ +//----------------------------------------------- +// +// This file is part of the Siv3D Engine. +// +// Copyright (c) 2008-2023 Ryo Suzuki +// Copyright (c) 2016-2023 OpenSiv3D Project +// +// Licensed under the MIT License. +// +//----------------------------------------------- + +# pragma once +# include "../Common.hpp" +# include "../Optional.hpp" +# include "../ColorHSV.hpp" +# include "Padding.hpp" +# include "BoxShadow.hpp" +# include "UIContainer.hpp" + +namespace s3d +{ + inline namespace UI1 + { + /// @brief UI 要素を配置するウィンドウ + class UIWindow : public UIContainer + { + public: + + struct Style + { + /// @brief 枠の太さ(ピクセル) + double borderThickness = 1.0; + + /// @brief パネルの角の半径(ピクセル) + double borderRadius = 4.0; + + /// @brief 背景色 | Background color + Optional backgroundColor = ColorF{ 1.0 }; + + /// @brief 無効時のオーバーレイ色 | Disabled overlay color + Optional disabledOverlayColor = ColorF{ 0.98, 0.7 }; + + /// @brief アクティブ時の枠の色 | Active border color + Optional activeBorderColor = ColorF{ 0.68 }; + + /// @brief 非アクティブ時の枠の色 | Inactive border color + Optional inactiveBorderColor = ColorF{ 0.72 }; + + /// @brief 無効時の枠の色 | Disabled border color + Optional disabledBorderColor = ColorF{ 0.75 }; + + /// @brief 影の設定 | Box shadow + Optional boxShadow = BoxShadow{ Vec2{ 2, 2 }, 24, 1 }; + + Padding padding = { 10, 20 }; + + double titleBarHeight = 36.0; + + /// @brief アクティブ時のタイトルバーの色 + ColorF titleBarActiveColor = ColorF{ 0.82, 0.85, 0.93 }; + + /// @brief 非アクティブ時のタイトルバーの色 + ColorF titleBarInactiveColor = ColorF{ 0.91 }; + + [[nodiscard]] + static Style Default(); + }; + + SIV3D_NODISCARD_CXX20 + UIWindow() = default; + + SIV3D_NODISCARD_CXX20 + explicit UIWindow(UIContainerNameView name, StringView title, const RectF& rect, const Style& style = Style::Default()); + + [[nodiscard]] + StringView type() const noexcept override; + + [[nodiscard]] + SizeF getSize() const noexcept override; + + [[nodiscard]] + RectF getBounds() const noexcept override; + + bool onUpdate(bool cursorCapturable = true) override; + + void onDraw() const override; + + void onDrawOverlay() const override; + + void onDrawDebug() const override; + + void setPos(const Vec2& pos) noexcept; + + void setSize(const SizeF& size) noexcept; + + /// @brief UI ウィンドウを作成します。 + /// @param name UI コンテナとしての一意な名前 + /// @param rect ウィンドウの初期領域 + /// @param style スタイル + /// @return 作成されたウィンドウ + [[nodiscard]] + static std::shared_ptr Create(UIContainerNameView name, StringView title, const RectF& rect, const Style& style = Style::Default()); + + protected: + + void onPressed() override; + + void onReleased() override; + + private: + + Style m_style; + + RectF m_rect = RectF::Empty(); + + String m_title; + + bool m_active = false; + + bool m_dragging = false; + + [[nodiscard]] + RoundRect getShape() const noexcept; + + [[nodiscard]] + RectF getTitleBarRect() const noexcept; + + [[nodiscard]] + void drawBackground() const; + + [[nodiscard]] + void drawDebugBackground() const; + }; + } +} diff --git a/Siv3D/src/Siv3D/UI1/SivUIContainer.cpp b/Siv3D/src/Siv3D/UI1/SivUIContainer.cpp index f3e6f01eb..db647d3ea 100644 --- a/Siv3D/src/Siv3D/UI1/SivUIContainer.cpp +++ b/Siv3D/src/Siv3D/UI1/SivUIContainer.cpp @@ -134,7 +134,7 @@ namespace s3d String UIContainer::dumpDebugInfo() const { - String result = U"{} ({}) {} "_fmt(m_name, type(), getBounds()); + String result = U"{} ({}) {:.1f} "_fmt(m_name, type(), getBounds()); if (shouldUpdate()) { diff --git a/Siv3D/src/Siv3D/UI1/SivUIPanel.cpp b/Siv3D/src/Siv3D/UI1/SivUIPanel.cpp index c8b9887b4..12dcd026e 100644 --- a/Siv3D/src/Siv3D/UI1/SivUIPanel.cpp +++ b/Siv3D/src/Siv3D/UI1/SivUIPanel.cpp @@ -9,6 +9,7 @@ // //----------------------------------------------- +# include # include namespace s3d @@ -99,7 +100,9 @@ namespace s3d if (m_style.boxShadow) { - shape.drawShadow(m_style.boxShadow->offset, m_style.boxShadow->blur, m_style.boxShadow->spread, m_style.boxShadow->color); + // [Siv3D ToDo] より多くのケースで fill を false にする + const bool fill = ((m_style.boxShadow->blur * 0.5 + m_style.boxShadow->spread) < (Math::Abs(m_style.boxShadow->offset).maxComponent())); + shape.drawShadow(m_style.boxShadow->offset, m_style.boxShadow->blur, m_style.boxShadow->spread, m_style.boxShadow->color, fill); } if (m_style.backgroundColor) diff --git a/Siv3D/src/Siv3D/UI1/SivUIWindow.cpp b/Siv3D/src/Siv3D/UI1/SivUIWindow.cpp new file mode 100644 index 000000000..31386ca4f --- /dev/null +++ b/Siv3D/src/Siv3D/UI1/SivUIWindow.cpp @@ -0,0 +1,202 @@ +//----------------------------------------------- +// +// This file is part of the Siv3D Engine. +// +// Copyright (c) 2008-2023 Ryo Suzuki +// Copyright (c) 2016-2023 OpenSiv3D Project +// +// Licensed under the MIT License. +// +//----------------------------------------------- + +# include +# include +# include +# include +# include +# include +# include +# include "UICanvasDetail.hpp" + +namespace s3d +{ + namespace UI1 + { + UIWindow::Style UIWindow::Style::Default() + { + return UIWindow::Style{}; + } + + UIWindow::UIWindow(const UIContainerNameView name, const StringView title, const RectF& rect, const Style& style) + : UIContainer{ name } + , m_style{ style } + , m_rect{ rect } + , m_title{ title } {} + + StringView UIWindow::type() const noexcept + { + return U"UIWindow"; + } + + SizeF UIWindow::getSize() const noexcept + { + return m_rect.size; + } + + RectF UIWindow::getBounds() const noexcept + { + return m_rect; + } + + bool UIWindow::onUpdate(const bool cursorCapturable) + { + if (auto pCanvas = m_pCanvas.lock()) + { + m_active = (pCanvas->findTopmostContainer() == this); + } + else + { + m_active = true; + } + + if (m_dragging) + { + setPos(getBounds().pos + Cursor::DeltaF()); + } + + return onUpdateHelper(cursorCapturable, getShape().mouseOver(), m_style.padding, [this](SizeF size){ setSize(size); }); + } + + void UIWindow::onDraw() const + { + drawBackground(); + + onDrawHelper(m_style.padding); + } + + void UIWindow::onDrawOverlay() const + { + onDrawOverlayHelper(m_style.padding); + + // 無効状態の場合、全体にオーバーレイを描画する + if (not isEnabled()) + { + if (m_style.disabledOverlayColor) + { + getShape().draw(*m_style.disabledOverlayColor); + } + } + } + + void UIWindow::onDrawDebug() const + { + drawDebugBackground(); + + onDrawDebugHelper(m_style.padding); + } + + void UIWindow::setPos(const Vec2& pos) noexcept + { + m_rect.pos = pos; + } + + void UIWindow::setSize(const SizeF& size) noexcept + { + m_rect.size = size; + } + + std::shared_ptr UIWindow::Create(const UIContainerNameView name, const StringView title, const RectF& rect, const Style& style) + { + return std::make_shared(name, title, rect, style); + } + + void UIWindow::onPressed() + { + if (getTitleBarRect().mouseOver()) + { + m_dragging = true; + } + } + + void UIWindow::onReleased() + { + m_dragging = false; + } + + RoundRect UIWindow::getShape() const noexcept + { + return{ m_rect, m_style.borderRadius }; + } + + RectF UIWindow::getTitleBarRect() const noexcept + { + return{ m_rect.pos, m_rect.w, m_style.titleBarHeight }; + } + + void UIWindow::drawBackground() const + { + const RoundRect shape = getShape(); + + if (m_style.boxShadow) + { + // [Siv3D ToDo] より多くのケースで fill を false にする + const bool fill = ((m_style.boxShadow->blur * 0.5 + m_style.boxShadow->spread) < (Math::Abs(m_style.boxShadow->offset).maxComponent())); + shape.drawShadow(m_style.boxShadow->offset, m_style.boxShadow->blur, m_style.boxShadow->spread, m_style.boxShadow->color, fill); + } + + if (m_style.backgroundColor) + { + shape.draw(*m_style.backgroundColor); + } + + const RectF titleBarRect = getTitleBarRect(); + titleBarRect.rounded(m_style.borderRadius, m_style.borderRadius, 0, 0).draw(m_active ? m_style.titleBarActiveColor : m_style.titleBarInactiveColor); + SimpleGUI::GetFont()(m_title).drawAt(18, titleBarRect.center().movedBy(0, -1), ColorF{ 0.11 }); + + if (0.0 < m_style.borderThickness) + { + if (isEnabled()) + { + if (m_active && m_style.activeBorderColor) + { + shape.drawFrame(0.0, m_style.borderThickness, *m_style.activeBorderColor); + } + else if ((not m_active) && m_style.inactiveBorderColor) + { + shape.drawFrame(0.0, m_style.borderThickness, *m_style.inactiveBorderColor); + } + } + else if ((not isEnabled()) && m_style.disabledBorderColor) + { + shape.drawFrame(0.0, m_style.borderThickness, *m_style.disabledBorderColor); + } + } + } + + void UIWindow::drawDebugBackground() const + { + const RectF rect = getBounds(); + + rect.drawFrame(1, 0, Palette::Red); + + if (not isEnabled()) + { + Line{ rect.tl(), rect.br() }.draw(1, Palette::Red); + Line{ rect.bl(), rect.tr() }.draw(1, Palette::Red); + } + + if (isHovered()) + { + rect.stretched(-1).drawFrame(5, 0, Palette::Orange); + } + + if (hasMouseCapture()) + { + Circle{ rect.tl(), 10 }.drawFrame(3, 0, Palette::Red); + Circle{ rect.tr(), 10 }.drawFrame(3, 0, Palette::Red); + Circle{ rect.br(), 10 }.drawFrame(3, 0, Palette::Red); + Circle{ rect.bl(), 10 }.drawFrame(3, 0, Palette::Red); + } + } + } +} diff --git a/WindowsDesktop/Siv3D.vcxproj b/WindowsDesktop/Siv3D.vcxproj index b7cd75567..8e46a7d44 100644 --- a/WindowsDesktop/Siv3D.vcxproj +++ b/WindowsDesktop/Siv3D.vcxproj @@ -763,6 +763,7 @@ + @@ -2512,6 +2513,7 @@ + diff --git a/WindowsDesktop/Siv3D.vcxproj.filters b/WindowsDesktop/Siv3D.vcxproj.filters index 7826f6c96..8c1594c1d 100644 --- a/WindowsDesktop/Siv3D.vcxproj.filters +++ b/WindowsDesktop/Siv3D.vcxproj.filters @@ -7149,6 +7149,9 @@ include\Siv3D\UI1 + + include\Siv3D\UI1 + @@ -10379,6 +10382,9 @@ src\Siv3D\UI1 + + src\Siv3D\UI1 +