diff --git a/.ecode/project_build.json b/.ecode/project_build.json index 91f3be7bd..774783197 100644 --- a/.ecode/project_build.json +++ b/.ecode/project_build.json @@ -312,6 +312,12 @@ "command": "${project_root}/bin/eepp-sound-debug", "name": "eepp-sound-debug", "working_dir": "${project_root}/bin" + }, + { + "args": "", + "command": "${project_root}/bin/eepp-test-debug", + "name": "eepp-test-debug", + "working_dir": "${project_root}/bin" } ], "var": { diff --git a/include/eepp/graphics/primitives.hpp b/include/eepp/graphics/primitives.hpp index b66288968..785239c74 100644 --- a/include/eepp/graphics/primitives.hpp +++ b/include/eepp/graphics/primitives.hpp @@ -135,6 +135,22 @@ class EE_API Primitives { */ void drawPolygon( const Polygon2f& p ); + /** + * @brief Draws a soft-edged shadow for a rectangular box using only primitives. + * + * This function improves upon basic rectangular shadows by rendering the corners + * with a radial gradient (using a triangle fan), which produces a much smoother look. + * + * @param boxRect The rectangle of the UI element casting the shadow (in screen coordinates). + * @param shadowOffset An offset to apply to the shadow's position relative to the box. + * @param shadowSize The distance the shadow extends and fades out from the box edges. + * @param shadowColor The base color of the shadow (alpha will be used for max opacity). + * @param cornerSegments The number of triangles to use for each corner fan. More segments + * result in a smoother corner. + */ + void drawSoftShadow( const Rectf& boxRect, const Vector2f& shadowOffset, Float shadowSize, + const Color& shadowColor, Uint32 cornerSegments = 8 ); + /** Set the current color for drawing primitives */ void setColor( const Color& Color ); diff --git a/include/eepp/system/color.hpp b/include/eepp/system/color.hpp index 80233331a..99247644b 100644 --- a/include/eepp/system/color.hpp +++ b/include/eepp/system/color.hpp @@ -5,7 +5,11 @@ #include #include #include + +#include #include +#include + #if EE_PLATFORM == EE_PLATFORM_WIN #undef RGB #endif @@ -44,8 +48,10 @@ template class tRGB { /** @brief Template class for a RGBA color */ template class tColor { public: + using ValueType = std::conditional_t, Uint32, std::array>; + union { - Uint32 Value; + ValueType Value; struct { T r; @@ -92,10 +98,17 @@ template class tColor { tColor( const tColor& Col ) : Value( Col.Value ) {} /** From a 32 bits value with RGBA byte order */ + template , int> = 0> tColor( const Uint32& Col ) : Value( BitOp::swapBE32( Col ) ) {} //! @return The color represented as an Uint32 ( as 0xRRGGBBAA for Little Endian ) - Uint32 getValue() const { return BitOp::swapBE32( Value ); } + ValueType getValue() const { + if constexpr ( std::is_same_v ) { + return BitOp::swapBE32( Value ); + } else { + return Value; + } + } /** @brief Assign the RGBA colors, from each component. */ void assign( T r, T g, T b, T a ) { diff --git a/include/eepp/ui/uifiledialog.hpp b/include/eepp/ui/uifiledialog.hpp index a0e2ea676..1725c184f 100644 --- a/include/eepp/ui/uifiledialog.hpp +++ b/include/eepp/ui/uifiledialog.hpp @@ -123,7 +123,7 @@ class EE_API UIFileDialog : public UIWindow { virtual bool show(); - virtual bool hide(); + virtual bool hide( bool immediate = false ); bool usingNativeFileDialog() const; diff --git a/include/eepp/ui/uiwindow.hpp b/include/eepp/ui/uiwindow.hpp index f62433ebe..717808ff6 100644 --- a/include/eepp/ui/uiwindow.hpp +++ b/include/eepp/ui/uiwindow.hpp @@ -105,7 +105,7 @@ class EE_API UIWindow : public UIWidget { virtual bool show(); - virtual bool hide(); + virtual bool hide( bool immediate = false /* ignores animations */ ); UIWindow* showWhenReady(); diff --git a/src/eepp/graphics/primitives.cpp b/src/eepp/graphics/primitives.cpp index 8003e0baf..f215adda6 100644 --- a/src/eepp/graphics/primitives.cpp +++ b/src/eepp/graphics/primitives.cpp @@ -465,4 +465,106 @@ const Float& Primitives::getLineWidth() const { return mLineWidth; } +void Primitives::drawSoftShadow( const Rectf& boxRect, const Vector2f& shadowOffset, + Float shadowSize, const Color& shadowColor, + Uint32 cornerSegments ) { + if ( shadowSize <= 0.f ) + return; + + if ( cornerSegments == 0 ) + cornerSegments = 1; + + setForceDraw( false ); + + // Define the start (opaque) and end (transparent) colors for the gradient + Color beginC = shadowColor; + Color endC( shadowColor.r, shadowColor.g, shadowColor.b, 0 ); + + // Calculate the position of the main, solid part of the shadow + Rectf shadowBox = boxRect; + shadowBox.move( shadowOffset ); + + // 1. Draw the central, solid part of the shadow + // drawRectangle(const Rectf& R, const Color& TopLeft, const Color& BottomLeft, const Color& + // BottomRight, const Color& TopRight) + drawRectangle( shadowBox, beginC, beginC, beginC, beginC ); + + // 2. Draw the four faded edge rectangles, using the correct (Left, Top, Right, Bottom) + // constructor Top edge + drawRectangle( + Rectf( shadowBox.Left, shadowBox.Top - shadowSize, shadowBox.Right, shadowBox.Top ), endC, + beginC, beginC, endC ); + + // Bottom edge + drawRectangle( + Rectf( shadowBox.Left, shadowBox.Bottom, shadowBox.Right, shadowBox.Bottom + shadowSize ), + beginC, endC, endC, beginC ); + + // Left edge + drawRectangle( + Rectf( shadowBox.Left - shadowSize, shadowBox.Top, shadowBox.Left, shadowBox.Bottom ), endC, + endC, beginC, beginC ); + + // Right edge + drawRectangle( + Rectf( shadowBox.Right, shadowBox.Top, shadowBox.Right + shadowSize, shadowBox.Bottom ), + beginC, beginC, endC, endC ); + + // 3. Draw the four corners using triangle fans + if ( cornerSegments > 0 ) { + Float step = + 90.0f / static_cast( cornerSegments ); // Angle step for each triangle in the fan + + // Top-Left Corner + Vector2f tlCenter( shadowBox.Left, shadowBox.Top ); + for ( Uint32 i = 0; i < cornerSegments; ++i ) { + Float ang1 = 180.0f + i * step; + Float ang2 = 180.0f + ( i + 1 ) * step; + Vector2f p1( tlCenter.x + shadowSize * Math::cosAng( ang1 ), + tlCenter.y + shadowSize * Math::sinAng( ang1 ) ); + Vector2f p2( tlCenter.x + shadowSize * Math::cosAng( ang2 ), + tlCenter.y + shadowSize * Math::sinAng( ang2 ) ); + drawTriangle( Triangle2f( tlCenter, p1, p2 ), beginC, endC, endC ); + } + + // Top-Right Corner + Vector2f trCenter( shadowBox.Right, shadowBox.Top ); + for ( Uint32 i = 0; i < cornerSegments; ++i ) { + Float ang1 = 270.0f + i * step; + Float ang2 = 270.0f + ( i + 1 ) * step; + Vector2f p1( trCenter.x + shadowSize * Math::cosAng( ang1 ), + trCenter.y + shadowSize * Math::sinAng( ang1 ) ); + Vector2f p2( trCenter.x + shadowSize * Math::cosAng( ang2 ), + trCenter.y + shadowSize * Math::sinAng( ang2 ) ); + drawTriangle( Triangle2f( trCenter, p1, p2 ), beginC, endC, endC ); + } + + // Bottom-Left Corner + Vector2f blCenter( shadowBox.Left, shadowBox.Bottom ); + for ( Uint32 i = 0; i < cornerSegments; ++i ) { + Float ang1 = 90.0f + i * step; + Float ang2 = 90.0f + ( i + 1 ) * step; + Vector2f p1( blCenter.x + shadowSize * Math::cosAng( ang1 ), + blCenter.y + shadowSize * Math::sinAng( ang1 ) ); + Vector2f p2( blCenter.x + shadowSize * Math::cosAng( ang2 ), + blCenter.y + shadowSize * Math::sinAng( ang2 ) ); + drawTriangle( Triangle2f( blCenter, p1, p2 ), beginC, endC, endC ); + } + + // Bottom-Right Corner + Vector2f brCenter( shadowBox.Right, shadowBox.Bottom ); + for ( Uint32 i = 0; i < cornerSegments; ++i ) { + Float ang1 = 0.0f + i * step; + Float ang2 = 0.0f + ( i + 1 ) * step; + Vector2f p1( brCenter.x + shadowSize * Math::cosAng( ang1 ), + brCenter.y + shadowSize * Math::sinAng( ang1 ) ); + Vector2f p2( brCenter.x + shadowSize * Math::cosAng( ang2 ), + brCenter.y + shadowSize * Math::sinAng( ang2 ) ); + drawTriangle( Triangle2f( brCenter, p1, p2 ), beginC, endC, endC ); + } + } + + drawBatch(); +} + }} // namespace EE::Graphics diff --git a/src/eepp/scene/scenenode.cpp b/src/eepp/scene/scenenode.cpp index b66e587ea..2120c27b8 100644 --- a/src/eepp/scene/scenenode.cpp +++ b/src/eepp/scene/scenenode.cpp @@ -270,7 +270,7 @@ void SceneNode::createFrameBuffer() { FrameBuffer::New( fboSize.getWidth(), fboSize.getHeight(), true, false, false, 4, mWindow ); // Frame buffer failed to create? - if ( !mFrameBuffer->created() ) { + if ( mFrameBuffer == nullptr || !mFrameBuffer->created() ) { eeSAFE_DELETE( mFrameBuffer ); } } diff --git a/src/eepp/ui/uifiledialog.cpp b/src/eepp/ui/uifiledialog.cpp index 57e666b39..ddbf07370 100644 --- a/src/eepp/ui/uifiledialog.cpp +++ b/src/eepp/ui/uifiledialog.cpp @@ -952,13 +952,13 @@ bool UIFileDialog::show() { return UIWindow::show(); } -bool UIFileDialog::hide() { +bool UIFileDialog::hide( bool immediate ) { if ( usingNativeFileDialog() ) { eeSAFE_DELETE( mHandler ); mHandler = eeNew( NativeFileDialogHandler, () ); return true; } - return UIWindow::hide(); + return UIWindow::hide( immediate ); } bool UIFileDialog::usingNativeFileDialog() const { diff --git a/src/eepp/ui/uiwindow.cpp b/src/eepp/ui/uiwindow.cpp index 753453b52..387e40d0a 100644 --- a/src/eepp/ui/uiwindow.cpp +++ b/src/eepp/ui/uiwindow.cpp @@ -375,67 +375,19 @@ void UIWindow::drawHighlightInvalidation() { } void UIWindow::drawShadow() { - if ( mStyleConfig.WinFlags & UI_WIN_SHADOW ) { - UIWidget::matrixSet(); + if ( !( mStyleConfig.WinFlags & UI_WIN_SHADOW ) ) + return; - Primitives P; - P.setForceDraw( false ); + UIWidget::matrixSet(); - Color BeginC( 0, 0, 0, 25 * ( getAlpha() / (Float)255 ) ); - Color EndC( 0, 0, 0, 0 ); - Float SSize = PixelDensity::dpToPx( 16.f ); + Primitives P; + Color shadowColor( 0, 0, 0, 25 * ( getAlpha() / 255.f ) ); + Float shadowSize = PixelDensity::dpToPx( 16.f ); + Vector2f shadowOffset( 0, shadowSize ); + Rectf windowRect( mScreenPos, mSize ); + P.drawSoftShadow( windowRect, shadowOffset, shadowSize, shadowColor, shadowSize / 2 ); - Vector2f ShadowPos = mScreenPos + Vector2f( 0, SSize ); - - P.drawRectangle( Rectf( Vector2f( ShadowPos.x, ShadowPos.y ), - Sizef( mSize.getWidth(), mSize.getHeight() ) ), - BeginC, BeginC, BeginC, BeginC ); - - P.drawRectangle( - Rectf( Vector2f( ShadowPos.x, ShadowPos.y - SSize ), Sizef( mSize.getWidth(), SSize ) ), - EndC, BeginC, BeginC, EndC ); - - P.drawRectangle( Rectf( Vector2f( ShadowPos.x - SSize, ShadowPos.y ), - Sizef( SSize, mSize.getHeight() ) ), - EndC, EndC, BeginC, BeginC ); - - P.drawRectangle( Rectf( Vector2f( ShadowPos.x + mSize.getWidth(), ShadowPos.y ), - Sizef( SSize, mSize.getHeight() ) ), - BeginC, BeginC, EndC, EndC ); - - P.drawRectangle( Rectf( Vector2f( ShadowPos.x, ShadowPos.y + mSize.getHeight() ), - Sizef( mSize.getWidth(), SSize ) ), - BeginC, EndC, EndC, BeginC ); - - P.drawTriangle( - Triangle2f( Vector2f( ShadowPos.x + mSize.getWidth(), ShadowPos.y ), - Vector2f( ShadowPos.x + mSize.getWidth(), ShadowPos.y - SSize ), - Vector2f( ShadowPos.x + mSize.getWidth() + SSize, ShadowPos.y ) ), - BeginC, EndC, EndC ); - - P.drawTriangle( Triangle2f( Vector2f( ShadowPos.x, ShadowPos.y ), - Vector2f( ShadowPos.x, ShadowPos.y - SSize ), - Vector2f( ShadowPos.x - SSize, ShadowPos.y ) ), - BeginC, EndC, EndC ); - - P.drawTriangle( - Triangle2f( - Vector2f( ShadowPos.x + mSize.getWidth(), ShadowPos.y + mSize.getHeight() ), - Vector2f( ShadowPos.x + mSize.getWidth(), ShadowPos.y + mSize.getHeight() + SSize ), - Vector2f( ShadowPos.x + mSize.getWidth() + SSize, - ShadowPos.y + mSize.getHeight() ) ), - BeginC, EndC, EndC ); - - P.drawTriangle( - Triangle2f( Vector2f( ShadowPos.x, ShadowPos.y + mSize.getHeight() ), - Vector2f( ShadowPos.x - SSize, ShadowPos.y + mSize.getHeight() ), - Vector2f( ShadowPos.x, ShadowPos.y + mSize.getHeight() + SSize ) ), - BeginC, EndC, EndC ); - - P.setForceDraw( true ); - - UIWidget::matrixUnset(); - } + UIWidget::matrixUnset(); } void UIWindow::onPaddingChange() { @@ -1119,10 +1071,10 @@ bool UIWindow::show() { return false; } -bool UIWindow::hide() { +bool UIWindow::hide( bool immediate ) { if ( isVisible() ) { UIThemeManager* themeManager = getUISceneNode()->getUIThemeManager(); - if ( themeManager->getDefaultEffectsEnabled() ) { + if ( !immediate && themeManager->getDefaultEffectsEnabled() ) { runAction( Actions::Sequence::New( Actions::FadeOut::New( themeManager->getWidgetsFadeOutTime() ), Actions::Spawn::New( Actions::Disable::New(), Actions::Visible::New( false ) ) ) ); @@ -1150,7 +1102,7 @@ UIWindow* UIWindow::showWhenReady() { show(); } else { mShowWhenReady = true; - hide(); + hide( true ); } return this; } diff --git a/src/tests/test_all/test.cpp b/src/tests/test_all/test.cpp index d4beb10ee..c52669ed2 100644 --- a/src/tests/test_all/test.cpp +++ b/src/tests/test_all/test.cpp @@ -45,7 +45,7 @@ class UIBlurredWindow : public UIWindow { mScreenPos.y + mSize.y ) ); RGB cc = getSceneNode()->getWindow()->getClearColor(); - mFboBlur->setClearColor( ColorAf( cc.r / 255.f, cc.g / 255.f, cc.b / 255.f, 0 ) ); + mFboBlur->setClearColor( ColorAf( cc.r / 255.f, cc.g / 255.f, cc.b / 255.f, 1.f ) ); mFboBlur->bind(); mFboBlur->clear(); textureRegion.draw( Vector2f( 0, 0 ), mFboBlur->getSizef() ); @@ -1172,7 +1172,8 @@ void EETest::createDecoratedWindow() { } void EETest::onCloseClick( const Event* ) { - mUIWindow = NULL; + if ( TestInstance ) + mUIWindow = NULL; } void EETest::onItemClick( const Event* event ) { @@ -1934,6 +1935,9 @@ void EETest::input() { if ( KM->isKeyUp( KEY_F8 ) ) uiSceneNode->setDrawDebugData( !uiSceneNode->getDrawDebugData() ); + if ( KM->isKeyUp( KEY_F11 ) ) + UIWidgetInspector::create( uiSceneNode, PixelDensity::dpToPx( 12 ) ); + if ( !mWindow->isVisible() ) { mWasMinimized = true; @@ -2553,6 +2557,7 @@ EE_MAIN_FUNC int main( int, char*[] ) { Test->process(); + TestInstance = nullptr; eeDelete( Test ); PhysicsManager::destroySingleton();