mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
Greatly improve borders rendering, added a few tests.
Fixed `UIStyle::getProperty`, now respects specificity. Fixed `StyleSheetPropertiesParser::addProperty` when inserted an already existing property.
This commit is contained in:
171
bin/unit_tests/assets/html/border_tests.html
Normal file
171
bin/unit_tests/assets/html/border_tests.html
Normal file
@@ -0,0 +1,171 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
background-color: #f5f5f5;
|
||||
font: 11px Verdana, Sans-Serif;
|
||||
color: #333;
|
||||
padding: 8px;
|
||||
}
|
||||
.row { clear: both; margin-bottom: 4px; }
|
||||
.label {
|
||||
float: left;
|
||||
width: 130px;
|
||||
font-size: 9px;
|
||||
line-height: 1.2;
|
||||
color: #555;
|
||||
padding-top: 2px;
|
||||
padding-right: 4px;
|
||||
text-align: right;
|
||||
}
|
||||
.box {
|
||||
float: left;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin: 2px;
|
||||
background-color: #e8f0f8;
|
||||
font-size: 8px;
|
||||
text-align: center;
|
||||
padding: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.clearfix { clear: both; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- ============ 1 BORDER ============ -->
|
||||
<div class="row">
|
||||
<div class="label">1 border</div>
|
||||
<div class="box" style="border-top: 4px solid #e74c3c;">top</div>
|
||||
<div class="box" style="border-right: 4px solid #27ae60;">right</div>
|
||||
<div class="box" style="border-bottom: 4px solid #2980b9;">bottom</div>
|
||||
<div class="box" style="border-left: 4px solid #8e44ad;">left</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label">1 border 8px</div>
|
||||
<div class="box" style="border-top: 8px solid #c0392b;">top</div>
|
||||
<div class="box" style="border-right: 8px solid #1e8449;">right</div>
|
||||
<div class="box" style="border-bottom: 8px solid #1f618d;">bottom</div>
|
||||
<div class="box" style="border-left: 8px solid #6c3483;">left</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ 2 ADJACENT BORDERS ============ -->
|
||||
<div class="row">
|
||||
<div class="label">2 adj borders</div>
|
||||
<div class="box" style="border-top: 4px solid #e74c3c; border-right: 4px solid #27ae60;">top+right</div>
|
||||
<div class="box" style="border-right: 4px solid #27ae60; border-bottom: 4px solid #2980b9;">right+bot</div>
|
||||
<div class="box" style="border-bottom: 4px solid #2980b9; border-left: 4px solid #8e44ad;">bot+left</div>
|
||||
<div class="box" style="border-left: 4px solid #8e44ad; border-top: 4px solid #e74c3c;">left+top</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label">2 adj 2px/8px</div>
|
||||
<div class="box" style="border-top: 2px solid #e74c3c; border-right: 8px solid #27ae60;">t2/r8</div>
|
||||
<div class="box" style="border-right: 8px solid #27ae60; border-bottom: 2px solid #2980b9;">r8/b2</div>
|
||||
<div class="box" style="border-bottom: 2px solid #2980b9; border-left: 8px solid #8e44ad;">b2/l8</div>
|
||||
<div class="box" style="border-left: 8px solid #8e44ad; border-top: 2px solid #e74c3c;">l8/t2</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ 2 OPPOSITE BORDERS ============ -->
|
||||
<div class="row">
|
||||
<div class="label">2 opp borders</div>
|
||||
<div class="box" style="border-top: 4px solid #e74c3c; border-bottom: 4px solid #2980b9;">top+bot</div>
|
||||
<div class="box" style="border-left: 4px solid #8e44ad; border-right: 4px solid #27ae60;">left+right</div>
|
||||
<div class="box" style="border-top: 8px solid #c0392b; border-bottom: 2px solid #1f618d;">t8/b2</div>
|
||||
<div class="box" style="border-left: 2px solid #6c3483; border-right: 8px solid #1e8449;">l2/r8</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ 3 BORDERS ============ -->
|
||||
<div class="row">
|
||||
<div class="label">3 borders</div>
|
||||
<div class="box" style="border-top: 4px solid #e74c3c; border-right: 4px solid #27ae60; border-bottom: 4px solid #2980b9;">no left</div>
|
||||
<div class="box" style="border-right: 4px solid #27ae60; border-bottom: 4px solid #2980b9; border-left: 4px solid #8e44ad;">no top</div>
|
||||
<div class="box" style="border-bottom: 4px solid #2980b9; border-left: 4px solid #8e44ad; border-top: 4px solid #e74c3c;">no right</div>
|
||||
<div class="box" style="border-left: 4px solid #8e44ad; border-top: 4px solid #e74c3c; border-right: 4px solid #27ae60;">no bottom</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label">3 brdrs mixed</div>
|
||||
<div class="box" style="border-top: 8px solid #c0392b; border-right: 2px solid #1e8449; border-bottom: 4px solid #1f618d;">t8/r2/b4</div>
|
||||
<div class="box" style="border-right: 2px solid #27ae60; border-bottom: 8px solid #2980b9; border-left: 4px solid #6c3483;">r2/b8/l4</div>
|
||||
<div class="box" style="border-bottom: 4px solid #2980b9; border-left: 8px solid #8e44ad; border-top: 2px solid #e74c3c;">b4/l8/t2</div>
|
||||
<div class="box" style="border-left: 2px solid #8e44ad; border-top: 4px solid #e74c3c; border-right: 8px solid #27ae60;">l2/t4/r8</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ 4 BORDERS ============ -->
|
||||
<div class="row">
|
||||
<div class="label">4 borders</div>
|
||||
<div class="box" style="border: 4px solid #555;">all 4px</div>
|
||||
<div class="box" style="border: 1px solid #555;">all 1px</div>
|
||||
<div class="box" style="border: 8px solid #555;">all 8px</div>
|
||||
<div class="box" style="border-top: 2px solid #e74c3c; border-right: 4px solid #27ae60; border-bottom: 6px solid #2980b9; border-left: 8px solid #8e44ad;">mixed</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label">4 brdrs colors</div>
|
||||
<div class="box" style="border: 6px solid #e67e22;">orange</div>
|
||||
<div class="box" style="border: 3px solid #1abc9c;">teal</div>
|
||||
<div class="box" style="border: 10px solid #9b59b6;">purple</div>
|
||||
<div class="box" style="border-top: 10px solid #f1c40f; border-right: 6px solid #e74c3c; border-bottom: 3px solid #2ecc71; border-left: 1px solid #3498db;">multi</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ 4 BORDERS + RADIUS ============ -->
|
||||
<div class="row">
|
||||
<div class="label">radius all same</div>
|
||||
<div class="box" style="border: 4px solid #e74c3c; border-radius: 4px;">r4</div>
|
||||
<div class="box" style="border: 4px solid #27ae60; border-radius: 8px;">r8</div>
|
||||
<div class="box" style="border: 4px solid #2980b9; border-radius: 12px;">r12</div>
|
||||
<div class="box" style="border: 4px solid #8e44ad; border-radius: 20px;">r20</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label">radius diff</div>
|
||||
<div class="box" style="border: 4px solid #e74c3c; border-top-left-radius: 16px; border-bottom-right-radius: 16px;">tl+br</div>
|
||||
<div class="box" style="border: 4px solid #27ae60; border-top-right-radius: 16px; border-bottom-left-radius: 16px;">tr+bl</div>
|
||||
<div class="box" style="border: 4px solid #2980b9; border-top-left-radius: 4px; border-top-right-radius: 8px; border-bottom-right-radius: 12px; border-bottom-left-radius: 2px;">mixed</div>
|
||||
<div class="box" style="border: 4px solid #8e44ad; border-top-left-radius: 32px;">tl32</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ PARTIAL BORDERS + RADIUS ============ -->
|
||||
<div class="row">
|
||||
<div class="label">part+radius</div>
|
||||
<div class="box" style="border-top: 4px solid #e74c3c; border-right: 4px solid #27ae60; border-radius: 8px;">t+r r8</div>
|
||||
<div class="box" style="border-bottom: 4px solid #2980b9; border-left: 4px solid #8e44ad; border-radius: 12px;">b+l r12</div>
|
||||
<div class="box" style="border-top: 4px solid #e74c3c; border-bottom: 4px solid #2980b9; border-radius: 8px;">t+b r8</div>
|
||||
<div class="box" style="border: 4px solid #27ae60; border-left: 0px; border-radius: 8px;">noL r8</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="label">part+radius2</div>
|
||||
<div class="box" style="border: 4px solid #e74c3c; border-bottom: 0px; border-radius: 8px;">noB r8</div>
|
||||
<div class="box" style="border: 4px solid #2980b9; border-top: 0px; border-radius: 8px;">noT r8</div>
|
||||
<div class="box" style="border: 4px solid #8e44ad; border-right: 0px; border-radius: 8px;">noR r8</div>
|
||||
<div class="box" style="border-top: 2px solid #e74c3c; border-left: 6px solid #8e44ad; border-radius: 6px;">t2+l6</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ THICK BORDERS + RADIUS ============ -->
|
||||
<div class="row">
|
||||
<div class="label">thick+radius</div>
|
||||
<div class="box" style="border: 8px solid #e74c3c; border-radius: 12px;">8px/r12</div>
|
||||
<div class="box" style="border: 12px solid #27ae60; border-radius: 16px;">12/r16</div>
|
||||
<div class="box" style="border: 3px solid #2980b9; border-radius: 32px;">3px/r32</div>
|
||||
<div class="box" style="border-top: 12px solid #e74c3c; border-radius: 8px;">t12</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ DIFFERENT SIZES ============ -->
|
||||
<div class="row">
|
||||
<div class="label">large boxes</div>
|
||||
<div class="box" style="width: 96px; height: 96px; border: 6px solid #e74c3c; border-radius: 16px;">96x96</div>
|
||||
<div class="box" style="width: 96px; height: 48px; border: 4px solid #27ae60;">96x48</div>
|
||||
<div class="box" style="width: 48px; height: 96px; border: 4px solid #2980b9; border-radius: 8px;">48x96</div>
|
||||
<div class="box" style="width: 96px; height: 96px; border: 2px solid #e74c3c; border-top: 10px solid #8e44ad; border-left: 1px solid #2ecc71;">mixed96</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
bin/unit_tests/assets/html/eepp-ui-border-rendering.webp
Normal file
BIN
bin/unit_tests/assets/html/eepp-ui-border-rendering.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
@@ -149,14 +149,120 @@ void Borders::createBorders( VertexBuffer* vbo, const Borders& borders, const Ve
|
||||
borderRight = eemin( (int)( size.getWidth() * 0.5f ), (int)borders.right.width );
|
||||
}
|
||||
|
||||
// draw top border
|
||||
if ( borderTop ) {
|
||||
double leftW = eemin( halfWidth, eemax( 0.f, borders.radius.topLeft.x ) );
|
||||
double rightW = eemin( halfHeight, eemax( 0.f, borders.radius.topRight.x ) );
|
||||
double leftH = eemin( halfWidth, eemax( 0.f, borders.radius.topLeft.y ) );
|
||||
double rightH = eemin( halfHeight, eemax( 0.f, borders.radius.topRight.y ) );
|
||||
bool hasTop = borderTop > 0;
|
||||
bool hasRight = borderRight > 0;
|
||||
bool hasBottom = borderBottom > 0;
|
||||
bool hasLeft = borderLeft > 0;
|
||||
|
||||
if ( leftW ) {
|
||||
if ( !hasTop && !hasRight && !hasBottom && !hasLeft )
|
||||
return;
|
||||
|
||||
// Pre-compute arc radii for each corner
|
||||
double tlArcW = eemin( halfWidth, eemax( 0.f, borders.radius.topLeft.x ) );
|
||||
double tlArcH = eemin( halfWidth, eemax( 0.f, borders.radius.topLeft.y ) );
|
||||
double trArcW = eemin( halfHeight, eemax( 0.f, borders.radius.topRight.x ) );
|
||||
double trArcH = eemin( halfHeight, eemax( 0.f, borders.radius.topRight.y ) );
|
||||
double brArcW = eemin( halfHeight, eemax( 0.f, borders.radius.bottomRight.x ) );
|
||||
double brArcH = eemin( halfHeight, eemax( 0.f, borders.radius.bottomRight.y ) );
|
||||
double blArcW = eemin( halfWidth, eemax( 0.f, borders.radius.bottomLeft.x ) );
|
||||
double blArcH = eemin( halfWidth, eemax( 0.f, borders.radius.bottomLeft.y ) );
|
||||
|
||||
// Corner positions
|
||||
Vector2f tlInner( pos.x + borderLeft, pos.y + borderTop );
|
||||
Vector2f tlOuter( pos.x, pos.y );
|
||||
Vector2f trInner( pos.x + size.getWidth() - borderRight, pos.y + borderTop );
|
||||
Vector2f trOuter( pos.x + size.getWidth(), pos.y );
|
||||
Vector2f brInner( pos.x + size.getWidth() - borderRight,
|
||||
pos.y + size.getHeight() - borderBottom );
|
||||
Vector2f brOuter( pos.x + size.getWidth(), pos.y + size.getHeight() );
|
||||
Vector2f blInner( pos.x + borderLeft, pos.y + size.getHeight() - borderBottom );
|
||||
Vector2f blOuter( pos.x, pos.y + size.getHeight() );
|
||||
|
||||
// Helper: compute arc outer vertex at a given angle
|
||||
auto arcOuterPos = []( const Vector2f& center, double rW, double rH,
|
||||
double angleDeg ) -> Vector2f {
|
||||
return Vector2f( center.x + rW * Math::cosAng( angleDeg ),
|
||||
center.y + rH * Math::sinAng( angleDeg ) );
|
||||
};
|
||||
|
||||
// Helper: compute arc inner vertex at a given angle
|
||||
auto arcInnerPos = []( const Vector2f& center, double rW, double rH, double angleDeg,
|
||||
double lineW, const Vector2f& basePos ) -> Vector2f {
|
||||
if ( rW > lineW )
|
||||
return Vector2f( center.x + ( rW - lineW ) * Math::cosAng( angleDeg ),
|
||||
center.y + ( rH - lineW ) * Math::sinAng( angleDeg ) );
|
||||
return basePos;
|
||||
};
|
||||
|
||||
// Pre-compute first inner vertex of each border (used as bridge targets)
|
||||
// Top border first inner (top-left corner)
|
||||
Vector2f topFirstInner;
|
||||
if ( tlArcW > 0 && hasLeft ) {
|
||||
Vector2f tlCenter( pos.x + tlArcW, pos.y + tlArcH );
|
||||
topFirstInner =
|
||||
arcInnerPos( tlCenter, tlArcW, tlArcH, 225, borderTop,
|
||||
Vector2f( pos.x + borderLeft, pos.y + borderTop ) );
|
||||
} else {
|
||||
topFirstInner = tlInner;
|
||||
}
|
||||
|
||||
// Right border first inner (top-right corner)
|
||||
Vector2f rightFirstInner;
|
||||
if ( trArcW > 0 && hasTop ) {
|
||||
Vector2f trCenter( pos.x + size.getWidth() - trArcW, pos.y + trArcH );
|
||||
rightFirstInner =
|
||||
arcInnerPos( trCenter, trArcW, trArcH, 315, borderRight,
|
||||
Vector2f( pos.x + size.getWidth() - borderRight, pos.y + borderTop ) );
|
||||
} else {
|
||||
rightFirstInner = trInner;
|
||||
}
|
||||
|
||||
// Bottom border first inner (bottom-right corner)
|
||||
Vector2f bottomFirstInner;
|
||||
if ( brArcW > 0 && hasRight ) {
|
||||
Vector2f brCenter( pos.x + size.getWidth() - brArcW,
|
||||
pos.y + size.getHeight() - brArcH );
|
||||
bottomFirstInner =
|
||||
arcInnerPos( brCenter, brArcW, brArcH, 45, borderBottom,
|
||||
Vector2f( pos.x + size.getWidth() - borderRight,
|
||||
pos.y + size.getHeight() - borderBottom ) );
|
||||
} else {
|
||||
bottomFirstInner = brInner;
|
||||
}
|
||||
|
||||
// Left border first inner (bottom-left corner)
|
||||
Vector2f leftFirstInner;
|
||||
if ( blArcW > 0 && hasBottom ) {
|
||||
Vector2f blCenter( pos.x + blArcW, pos.y + size.getHeight() - blArcH );
|
||||
leftFirstInner =
|
||||
arcInnerPos( blCenter, blArcW, blArcH, 135, borderLeft,
|
||||
Vector2f( pos.x + borderLeft,
|
||||
pos.y + size.getHeight() - borderBottom ) );
|
||||
} else {
|
||||
leftFirstInner = blInner;
|
||||
}
|
||||
|
||||
// Helper: insert degenerate triangle bridge between two disconnected border sections
|
||||
auto addBridge = [&]( const Vector2f& fromOuter, const Vector2f& toInner,
|
||||
const Color& bridgeColor ) {
|
||||
vbo->addVertex( fromOuter );
|
||||
vbo->addColor( bridgeColor );
|
||||
vbo->addVertex( toInner );
|
||||
vbo->addColor( bridgeColor );
|
||||
vbo->addVertex( toInner );
|
||||
vbo->addColor( bridgeColor );
|
||||
};
|
||||
|
||||
Vector2f lastOuter; // last emitted outer vertex, used as bridge source
|
||||
|
||||
// --- draw top border ---
|
||||
if ( hasTop ) {
|
||||
double leftW = tlArcW;
|
||||
double rightW = trArcW;
|
||||
double leftH = tlArcH;
|
||||
double rightH = trArcH;
|
||||
|
||||
if ( leftW && hasLeft ) {
|
||||
double endAngle = 270;
|
||||
double startAngle = 225;
|
||||
|
||||
@@ -170,7 +276,7 @@ void Borders::createBorders( VertexBuffer* vbo, const Borders& borders, const Ve
|
||||
vbo->addColor( borders.top.color );
|
||||
}
|
||||
|
||||
if ( rightW ) {
|
||||
if ( rightW && hasRight ) {
|
||||
double startAngle = 270;
|
||||
double endAngle = 315;
|
||||
Vector2f basePos( pos.x + size.getWidth() - borderRight, pos.y + borderTop );
|
||||
@@ -191,22 +297,33 @@ void Borders::createBorders( VertexBuffer* vbo, const Borders& borders, const Ve
|
||||
|
||||
borderAddArc( vbo, tPos, rightW, rightH, startAngle, endAngle, borders.top.color,
|
||||
borderTop, basePos );
|
||||
|
||||
lastOuter = arcOuterPos( tPos, rightW, rightH, endAngle );
|
||||
} else {
|
||||
vbo->addVertex( Vector2f( pos.x + size.getWidth() - borderRight, pos.y + borderTop ) );
|
||||
vbo->addColor( borders.top.color );
|
||||
vbo->addVertex( Vector2f( pos.x + size.getWidth(), pos.y ) );
|
||||
vbo->addColor( borders.top.color );
|
||||
|
||||
lastOuter = trOuter;
|
||||
}
|
||||
|
||||
if ( !hasRight ) {
|
||||
if ( hasBottom )
|
||||
addBridge( lastOuter, bottomFirstInner, borders.top.color );
|
||||
else if ( hasLeft )
|
||||
addBridge( lastOuter, leftFirstInner, borders.top.color );
|
||||
}
|
||||
}
|
||||
|
||||
// draw right border
|
||||
if ( borderRight ) {
|
||||
double topW = eemin( halfWidth, eemax( 0.f, borders.radius.topRight.x ) );
|
||||
double bottomW = eemin( halfHeight, eemax( 0.f, borders.radius.bottomRight.x ) );
|
||||
double topH = eemin( halfWidth, eemax( 0.f, borders.radius.topRight.y ) );
|
||||
double bottomH = eemin( halfHeight, eemax( 0.f, borders.radius.bottomRight.y ) );
|
||||
// --- draw right border ---
|
||||
if ( hasRight ) {
|
||||
double topW = trArcW;
|
||||
double bottomW = brArcW;
|
||||
double topH = trArcH;
|
||||
double bottomH = brArcH;
|
||||
|
||||
if ( topW ) {
|
||||
if ( topW && hasTop ) {
|
||||
double startAngle = 315;
|
||||
double endAngle = 360;
|
||||
Vector2f basePos( pos.x + size.getWidth() - borderRight, pos.y + borderTop );
|
||||
@@ -220,7 +337,7 @@ void Borders::createBorders( VertexBuffer* vbo, const Borders& borders, const Ve
|
||||
vbo->addColor( borders.right.color );
|
||||
}
|
||||
|
||||
if ( bottomH ) {
|
||||
if ( bottomH && hasBottom ) {
|
||||
double startAngle = 0;
|
||||
double endAngle = 45;
|
||||
Vector2f basePos( pos.x + size.getWidth() - borderRight,
|
||||
@@ -243,23 +360,29 @@ void Borders::createBorders( VertexBuffer* vbo, const Borders& borders, const Ve
|
||||
borderAddArc( vbo, tPos, bottomW, bottomH, startAngle, endAngle, borders.right.color,
|
||||
borderRight, basePos );
|
||||
|
||||
lastOuter = arcOuterPos( tPos, bottomW, bottomH, endAngle );
|
||||
} else {
|
||||
vbo->addVertex( Vector2f( pos.x + size.getWidth() - borderRight,
|
||||
pos.y + size.getHeight() - borderBottom ) );
|
||||
vbo->addColor( borders.right.color );
|
||||
vbo->addVertex( Vector2f( pos.x + size.getWidth(), pos.y + size.getHeight() ) );
|
||||
vbo->addColor( borders.right.color );
|
||||
|
||||
lastOuter = brOuter;
|
||||
}
|
||||
|
||||
if ( !hasBottom && hasLeft )
|
||||
addBridge( lastOuter, leftFirstInner, borders.right.color );
|
||||
}
|
||||
|
||||
// draw bottom border
|
||||
if ( borderBottom ) {
|
||||
double leftW = eemin( halfWidth, eemax( 0.f, borders.radius.bottomLeft.x ) );
|
||||
double rightW = eemin( halfHeight, eemax( 0.f, borders.radius.bottomRight.x ) );
|
||||
double leftH = eemin( halfWidth, eemax( 0.f, borders.radius.bottomLeft.y ) );
|
||||
double rightH = eemin( halfHeight, eemax( 0.f, borders.radius.bottomRight.y ) );
|
||||
// --- draw bottom border ---
|
||||
if ( hasBottom ) {
|
||||
double leftW = blArcW;
|
||||
double rightW = brArcW;
|
||||
double leftH = blArcH;
|
||||
double rightH = brArcH;
|
||||
|
||||
if ( rightW ) {
|
||||
if ( rightW && hasRight ) {
|
||||
double startAngle = 45;
|
||||
double endAngle = 90;
|
||||
Vector2f basePos( pos.x + size.getWidth() - borderRight,
|
||||
@@ -277,7 +400,7 @@ void Borders::createBorders( VertexBuffer* vbo, const Borders& borders, const Ve
|
||||
vbo->addColor( borders.bottom.color );
|
||||
}
|
||||
|
||||
if ( leftW ) {
|
||||
if ( leftW && hasLeft ) {
|
||||
double startAngle = 90;
|
||||
double endAngle = 135;
|
||||
Vector2f basePos( pos.x + borderLeft, pos.y + size.getHeight() - borderBottom );
|
||||
@@ -299,23 +422,30 @@ void Borders::createBorders( VertexBuffer* vbo, const Borders& borders, const Ve
|
||||
|
||||
borderAddArc( vbo, tPos, leftW, leftH, startAngle, endAngle, borders.bottom.color,
|
||||
borderBottom, basePos );
|
||||
|
||||
lastOuter = arcOuterPos( tPos, leftW, leftH, endAngle );
|
||||
} else {
|
||||
vbo->addVertex(
|
||||
Vector2f( pos.x + borderLeft, pos.y + size.getHeight() - borderBottom ) );
|
||||
vbo->addColor( borders.bottom.color );
|
||||
vbo->addVertex( Vector2f( pos.x, pos.y + size.getHeight() ) );
|
||||
vbo->addColor( borders.bottom.color );
|
||||
|
||||
lastOuter = blOuter;
|
||||
}
|
||||
|
||||
// After bottom, only left remains (already checked or skipped).
|
||||
// Bottom and left are adjacent, no bridge needed.
|
||||
}
|
||||
|
||||
// draw left border
|
||||
if ( borderLeft ) {
|
||||
double topW = eemin( halfWidth, eemax( 0.f, borders.radius.topLeft.x ) );
|
||||
double bottomW = eemin( halfHeight, eemax( 0.f, borders.radius.bottomLeft.x ) );
|
||||
double topH = eemin( halfWidth, eemax( 0.f, borders.radius.topLeft.y ) );
|
||||
double bottomH = eemin( halfHeight, eemax( 0.f, borders.radius.bottomLeft.y ) );
|
||||
// --- draw left border ---
|
||||
if ( hasLeft ) {
|
||||
double topW = tlArcW;
|
||||
double bottomW = blArcW;
|
||||
double topH = tlArcH;
|
||||
double bottomH = blArcH;
|
||||
|
||||
if ( bottomW ) {
|
||||
if ( bottomW && hasBottom ) {
|
||||
double startAngle = 135;
|
||||
double endAngle = 180;
|
||||
Vector2f basePos( pos.x + borderLeft, pos.y + size.getHeight() - borderBottom );
|
||||
@@ -331,7 +461,7 @@ void Borders::createBorders( VertexBuffer* vbo, const Borders& borders, const Ve
|
||||
vbo->addColor( borders.left.color );
|
||||
}
|
||||
|
||||
if ( topW ) {
|
||||
if ( topW && hasTop ) {
|
||||
double startAngle = 180;
|
||||
double endAngle = 225;
|
||||
Vector2f basePos( pos.x + borderLeft, pos.y + borderTop );
|
||||
|
||||
@@ -147,13 +147,13 @@ void StyleSheetPropertiesParser::addProperty( std::string name, std::string valu
|
||||
StyleSheetSpecification::instance()->getShorthand( name )->parse( value );
|
||||
|
||||
for ( auto& property : properties )
|
||||
mProperties.emplace( std::make_pair( property.getId(), std::move( property ) ) );
|
||||
mProperties[property.getId()] = std::move( property );
|
||||
} else {
|
||||
if ( String::startsWith( name, "--" ) ) {
|
||||
mVariables[String::hash( name )] = StyleSheetVariable( name, value );
|
||||
} else {
|
||||
StyleSheetProperty property( name, value );
|
||||
mProperties.emplace( std::make_pair( property.getId(), std::move( property ) ) );
|
||||
mProperties[property.getId()] = std::move( property );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,12 +210,11 @@ UnorderedSet<UIWidget*>& UIStyle::getStructurallyVolatileChildren() {
|
||||
}
|
||||
|
||||
const CSS::StyleSheetProperty* UIStyle::getProperty( const CSS::PropertyId& id ) {
|
||||
const CSS::StyleSheetProperty* prop = nullptr;
|
||||
if ( mGlobalDefinition && ( prop = mGlobalDefinition->getProperty( (Uint32)id ) ) )
|
||||
return prop;
|
||||
if ( mElementStyle )
|
||||
prop = mElementStyle->getPropertyById( id );
|
||||
return prop;
|
||||
const auto* gProp = mGlobalDefinition ? mGlobalDefinition->getProperty( (Uint32)id ) : nullptr;
|
||||
const auto* elProp = mElementStyle ? mElementStyle->getPropertyById( id ) : nullptr;
|
||||
if ( elProp && gProp )
|
||||
return elProp->getSpecificity() > gProp->getSpecificity() ? elProp : gProp;
|
||||
return elProp ? elProp : gProp;
|
||||
}
|
||||
|
||||
bool UIStyle::hasProperty( const CSS::PropertyId& propertyId ) const {
|
||||
|
||||
@@ -1846,7 +1846,7 @@ bool UIWidget::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
StyleSheetSelectorRule::SpecificityImportant ) );
|
||||
}
|
||||
setLayoutWidthPolicy( SizePolicy::Fixed );
|
||||
setSize( eefloor( lengthFromValueAsDp( attribute ) ), getSize().getHeight() );
|
||||
setSize( eefloor( lengthFromValueAsDp( attribute ) ), mDpSize.getHeight() );
|
||||
notifyLayoutAttrChange();
|
||||
}
|
||||
break;
|
||||
@@ -1860,7 +1860,7 @@ bool UIWidget::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
StyleSheetSelectorRule::SpecificityImportant ) );
|
||||
}
|
||||
setLayoutHeightPolicy( SizePolicy::Fixed );
|
||||
setSize( getSize().getWidth(), eefloor( lengthFromValueAsDp( attribute ) ) );
|
||||
setSize( mDpSize.getWidth(), eefloor( lengthFromValueAsDp( attribute ) ) );
|
||||
notifyLayoutAttrChange();
|
||||
}
|
||||
break;
|
||||
@@ -2276,12 +2276,12 @@ void UIWidget::loadFromXmlNode( const pugi::xml_node& node ) {
|
||||
StyleSheetPropertiesParser propertiesParser;
|
||||
propertiesParser.parse( std::string_view{ ait->value() } );
|
||||
if ( !propertiesParser.getProperties().empty() ) {
|
||||
for ( auto& [_, property] : propertiesParser.getProperties() ) {
|
||||
auto propertyImportant( property );
|
||||
propertyImportant.setImportant( true );
|
||||
if ( mStyle )
|
||||
mStyle->setStyleSheetProperty( propertyImportant );
|
||||
applyProperty( propertyImportant );
|
||||
for ( auto& [_, prop] : propertiesParser.getProperties() ) {
|
||||
auto property( prop );
|
||||
property.setSpecificity( StyleSheetSelectorRule::SpecificityInline );
|
||||
if ( NULL != mStyle )
|
||||
mStyle->setStyleSheetProperty( property );
|
||||
applyProperty( property );
|
||||
}
|
||||
}
|
||||
continue;
|
||||
|
||||
@@ -802,3 +802,37 @@ UTEST( UILayout, listStyleInheritanceFromUl ) {
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
UTEST( UIBorder, renderingVariations ) {
|
||||
auto win = Engine::instance()->createWindow(
|
||||
WindowSettings( 1200, 900, "Border Rendering Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32, {}, 1, false, true ),
|
||||
ContextSettings( false, 0, 0, GLv_default, true, false ) );
|
||||
FileSystem::changeWorkingDirectory( Sys::getProcessPath() );
|
||||
|
||||
FontTrueType* font = FontTrueType::New( "NotoSans-Regular" );
|
||||
font->loadFromFile( "../assets/fonts/NotoSans-Regular.ttf" );
|
||||
ASSERT_TRUE( font != nullptr && font->loaded() );
|
||||
FontFamily::loadFromRegular( font );
|
||||
|
||||
UI::UISceneNode* sceneNode = UI::UISceneNode::New();
|
||||
SceneManager::instance()->add( sceneNode );
|
||||
UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager();
|
||||
themeManager->setDefaultFont( font );
|
||||
sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" );
|
||||
std::string html;
|
||||
FileSystem::fileGet( "assets/html/border_tests.html", html );
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
|
||||
win->setClearColor( Color::White );
|
||||
|
||||
win->getInput()->update();
|
||||
SceneManager::instance()->update();
|
||||
|
||||
win->clear();
|
||||
SceneManager::instance()->draw();
|
||||
win->display();
|
||||
|
||||
compareImages( utest_state, utest_result, win, "eepp-ui-border-rendering", "html" );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user