Fixed a few bugs in Color (this fixes the eepp Tests blurred windows background).

Added `Primitives::drawSoftShadow` to draw simple shadows with primitives. Improved the shadow rendering adding correct rounded borders. UIWindow::drawShadow now uses this function.
Fixed a bug not displaying a recently create window when default transitions are enabled.
Plus some minor fixes.
This commit is contained in:
Martín Lucas Golini
2025-10-07 00:13:45 -03:00
parent 4edb776d51
commit ba1d9bdba2
10 changed files with 164 additions and 70 deletions

View File

@@ -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": {

View File

@@ -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 );

View File

@@ -5,7 +5,11 @@
#include <eepp/core/containers.hpp>
#include <eepp/core/string.hpp>
#include <eepp/system/bitop.hpp>
#include <array>
#include <string>
#include <type_traits>
#if EE_PLATFORM == EE_PLATFORM_WIN
#undef RGB
#endif
@@ -44,8 +48,10 @@ template <typename T> class tRGB {
/** @brief Template class for a RGBA color */
template <typename T> class tColor {
public:
using ValueType = std::conditional_t<std::is_same_v<T, Uint8>, Uint32, std::array<T, 4>>;
union {
Uint32 Value;
ValueType Value;
struct {
T r;
@@ -92,10 +98,17 @@ template <typename T> class tColor {
tColor( const tColor<T>& Col ) : Value( Col.Value ) {}
/** From a 32 bits value with RGBA byte order */
template <typename U = T, std::enable_if_t<std::is_same_v<U, Uint8>, 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<T, Uint8> ) {
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 ) {

View File

@@ -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;

View File

@@ -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();

View File

@@ -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<Float>( 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

View File

@@ -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 );
}
}

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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();