Add pixel-density default and improve chart rendering

- Honor EEPP_PIXEL_DENSITY when an app has not specified a scale,
  without resizing its window.
- Scale chart measurements in dp, expose chart styling, and resolve
  default colors through the UI theme.
- Fix gaps at miter joins and round chart text to device pixels.
- Use inline storage for common chart axis, series, and label counts.
- Add density and rendering regression tests.
This commit is contained in:
Martín Lucas Golini
2026-09-25 10:51:55 -03:00
parent 7a66563114
commit b3e6ea559d
19 changed files with 424 additions and 136 deletions
+6
View File
@@ -423,6 +423,12 @@
"command": "${project_root}/bin/eproc-debug",
"name": "eproc-debug",
"working_dir": "${project_root}/bin"
},
{
"args": "",
"command": "${project_root}/bin/eepp-ui-charts-debug",
"name": "eepp-ui-charts-debug",
"working_dir": "${project_root}/bin"
}
],
"var": {
+3
View File
@@ -34,6 +34,9 @@ class EE_API PixelDensity {
static const Float& getPixelDensity();
/** Positive EEPP_PIXEL_DENSITY value, or zero when unset or invalid. */
static Float getEnvironmentPixelDensity();
static void setPixelDensity( const Float& pixelDensity );
static void setPixelDensity( const PixelDensitySize& pixelDensity );
+69 -20
View File
@@ -15,6 +15,8 @@ class Font;
namespace EE { namespace UI { namespace Charts {
using namespace EE::Graphics;
struct PointTooltipContext {
size_t index{ 0 };
double x{ 0.0 };
@@ -43,6 +45,35 @@ using PointTooltipProvider =
enum class LineJoin : Uint8 { Miter, Bevel };
enum class LineCap : Uint8 { Butt, Square };
/** Chart measurements are in device-independent pixels. Colors left unset follow the UI theme. */
struct ChartStyle {
Font* font{ nullptr }; //!< Null uses the current UI theme font.
Float fontSize{ 12.f };
Float tickLength{ 4.f };
Float tickLabelGap{ 6.f };
Float axisLabelGap{ 25.f };
Float axisLabelInset{ 2.f };
Float leftMargin{ 50.f };
Float rightMargin{ 14.f };
Float topMargin{ 12.f };
Float topMarginWithLabel{ 26.f };
Float bottomMargin{ 34.f };
Float bottomMarginWithLabel{ 52.f };
Float minimumAxisMargin{ 42.f };
Float axisLabelPadding{ 12.f };
Float rightAxisLabelPadding{ 16.f };
Float xTickSpacing{ 80.f };
Float yTickSpacing{ 50.f };
Float hoverDistance{ 10.f };
Float axisWidth{ 1.f };
std::optional<Color> axisColor;
std::optional<Color> tickColor;
std::optional<Color> tickLabelColor;
std::optional<Color> axisLabelColor;
std::optional<Color> hoverColor;
std::optional<Color> seriesColor;
};
class EE_API LineSeries {
public:
const String& name() const { return mName; }
@@ -63,9 +94,12 @@ class EE_API LineSeries {
Color color() const { return mColor; }
void setWidth( float width );
bool hasCustomColor() const { return mColorExplicit; }
float width() const { return mWidth; }
/** Stroke width in device-independent pixels. */
void setWidth( Float width );
Float width() const { return mWidth; }
void setJoin( LineJoin join );
@@ -75,9 +109,9 @@ class EE_API LineSeries {
LineCap cap() const { return mCap; }
void setMiterLimit( float limit );
void setMiterLimit( Float limit );
float miterLimit() const { return mMiterLimit; }
Float miterLimit() const { return mMiterLimit; }
Uint64 geometryRevision() const { return mGeometryRevision; }
@@ -110,8 +144,9 @@ class EE_API LineSeries {
ChartAxis* mXAxis;
ChartAxis* mYAxis;
Color mColor{ 48, 130, 220 };
float mWidth{ 2.f };
float mMiterLimit{ 4.f };
bool mColorExplicit{ false };
Float mWidth{ 2.f };
Float mMiterLimit{ 4.f };
LineJoin mJoin{ LineJoin::Miter };
LineCap mCap{ LineCap::Butt };
bool mVisible{ true };
@@ -131,9 +166,9 @@ class EE_API ChartModel {
ChartAxis* yAxis() const { return mAxes[1].get(); }
const std::vector<std::unique_ptr<ChartAxis>>& axes() const { return mAxes; }
const SmallVector<std::unique_ptr<ChartAxis>, 4>& axes() const { return mAxes; }
const std::vector<std::unique_ptr<LineSeries>>& series() const { return mSeries; }
const SmallVector<std::unique_ptr<LineSeries>, 2>& series() const { return mSeries; }
Uint64 revision() const { return mRevision.get(); }
@@ -141,8 +176,8 @@ class EE_API ChartModel {
friend class UIChart;
void changed() { mRevision.set( mRevision.get() + 1 ); }
std::vector<std::unique_ptr<ChartAxis>> mAxes;
std::vector<std::unique_ptr<LineSeries>> mSeries;
SmallVector<std::unique_ptr<ChartAxis>, 4> mAxes;
SmallVector<std::unique_ptr<LineSeries>, 2> mSeries;
ObservableValue<Uint64> mRevision{ 0 };
};
@@ -171,6 +206,10 @@ class EE_API UIChart : public UIWidget {
/** Follow the newest X while preserving a fixed data-space span; zero disables follow mode. */
void setFollowX( double span );
void setChartStyle( ChartStyle style );
const ChartStyle& chartStyle() const { return mChartStyle; }
void draw() override;
protected:
@@ -212,18 +251,18 @@ class EE_API UIChart : public UIWidget {
struct RightAxis {
ChartAxis* axis{ nullptr };
SmallVector<double, 16> ticks;
std::vector<String> labels;
float width{ 0.f };
SmallVector<String, 8> labels;
Float width{ 0.f };
};
Rectf plot;
SmallVector<double, 16> xTicks;
SmallVector<double, 16> yTicks;
std::vector<String> xLabels;
std::vector<String> yLabels;
std::vector<float> xLabelWidths;
std::vector<float> yLabelWidths;
float xAxisLabelWidth{ 0.f };
SmallVector<String, 8> xLabels;
SmallVector<String, 8> yLabels;
SmallVector<Float, 8> xLabelWidths;
SmallVector<Float, 8> yLabelWidths;
Float xAxisLabelWidth{ 0.f };
std::vector<RightAxis> rightAxes;
Uint64 revision{ 0 };
};
@@ -244,14 +283,16 @@ class EE_API UIChart : public UIWidget {
void hideHoverTooltip();
void updateThemeColors();
AxisViewport& viewport( const ChartAxis* axis );
const AxisViewport& viewport( const ChartAxis* axis ) const;
std::shared_ptr<ChartModel> mModel;
ObservableValue<Uint64>::Connection mModelConnection;
std::vector<ViewAxis> mAxes;
std::vector<SeriesCache> mCaches;
SmallVector<ViewAxis, 4> mAxes;
SmallVector<SeriesCache, 2> mCaches;
Layout mLayout;
Uint64 mViewRevision{ 1 };
Uint64 mLayoutRevision{ 1 };
@@ -267,7 +308,15 @@ class EE_API UIChart : public UIWidget {
Uint64 mAutoRevision{ std::numeric_limits<Uint64>::max() };
String mLastXAxisLabel;
String mLastYAxisLabel;
Graphics::Font* mLastFont{ nullptr };
Font* mLastFont{ nullptr };
ChartStyle mChartStyle;
Color mAxisColor{ 125, 130, 138 };
Color mTickColor{ 125, 130, 138 };
Color mTickLabelColor{ 190, 194, 201 };
Color mAxisLabelColor{ 210, 214, 220 };
Color mHoverColor{ 160, 165, 173, 140 };
Color mSeriesColor{ 48, 130, 220 };
Float mLastDensity{ 0.f };
};
}}} // namespace EE::UI::Charts
+1 -1
View File
@@ -43,7 +43,7 @@ class EE_API UIApplication {
//! By default it will use the current process path as the base path. This will set the
//! default working directory.
std::optional<std::string> basePath;
//! Not setting anything will automatically try to detect the main screen pixel density
//! If unset, uses EEPP_PIXEL_DENSITY when valid, otherwise the main display density.
std::optional<Float> pixelDensity;
//! Must be set to true in order to initialize the basic UI resources (font and UI theme).
//! Otherwise it will initialize with an empty UI scene node
+3
View File
@@ -63,6 +63,9 @@ class EE_API UIStyle : public UIState {
CSS::StyleSheetVariable getVariable( const std::string& variable );
/** Resolve a color variable without copying its stored text. */
Color getColorVariable( const std::string& variable, Color fallback );
bool getForceReapplyProperties() const;
void setForceReapplyProperties( bool forceReapplyProperties );
+3
View File
@@ -1063,6 +1063,9 @@ class EE_API UIWidget : public UINode {
*/
UIStyle* getUIStyle() const;
/** Resolve a CSS color variable, or return fallback when it is absent. */
Color themeColor( const std::string& variable, Color fallback ) const;
/**
* @brief Reloads the widget's style.
*
+3 -3
View File
@@ -77,7 +77,7 @@ class WindowSettings {
inline WindowSettings( Uint32 width, Uint32 height, const std::string& title = std::string(),
Uint32 style = WindowStyle::Default,
WindowBackend backend = WindowBackend::Default, Uint32 bpp = 32,
const std::string& icon = std::string(), const Float& pixelDensity = 1,
const std::string& icon = std::string(), const Float& pixelDensity = 0,
const bool& useScreenKeyboard = EE_SCREEN_KEYBOARD_ENABLED,
bool disableHiDPI = false ) :
Style( style ),
@@ -97,7 +97,7 @@ class WindowSettings {
Height( 600 ),
BitsPerPixel( 32 ),
Backend( WindowBackend::Default ),
PixelDensity( 1 ),
PixelDensity( 0 ),
UseScreenKeyboard( EE_SCREEN_KEYBOARD_ENABLED ),
DisableHiDPI( false ) {}
@@ -108,7 +108,7 @@ class WindowSettings {
std::string Icon;
std::string Title;
WindowBackend Backend{ WindowBackend::Default };
Float PixelDensity{ 1 };
Float PixelDensity{ 0 }; //!< Zero leaves UI density to EEPP_PIXEL_DENSITY or display detection.
bool UseScreenKeyboard{ EE_SCREEN_KEYBOARD_ENABLED };
bool DisableHiDPI{ false };
};
+15
View File
@@ -1,3 +1,6 @@
#include <cerrno>
#include <cmath>
#include <cstdlib>
#include <eepp/core/string.hpp>
#include <eepp/graphics/pixeldensity.hpp>
@@ -65,6 +68,18 @@ const Float& PixelDensity::getPixelDensity() {
return sPixelDensity;
}
Float PixelDensity::getEnvironmentPixelDensity() {
const char* value = std::getenv( "EEPP_PIXEL_DENSITY" );
if ( !value || !*value )
return 0.f;
char* end = nullptr;
errno = 0;
const Float density = std::strtof( value, &end );
return errno == 0 && end != value && *end == '\0' && std::isfinite( density ) && density > 0.f
? density
: 0.f;
}
void PixelDensity::setPixelDensity( const Float& pixelDensity ) {
sPixelDensity = pixelDensity;
}
+5
View File
@@ -80,8 +80,13 @@ UIApplication::UIApplication( const WindowSettings& windowSettings, const Settin
mDidRun = true;
const Float environmentDensity = PixelDensity::getEnvironmentPixelDensity();
if ( appSettings.pixelDensity && *appSettings.pixelDensity > 0 ) {
PixelDensity::setPixelDensity( *appSettings.pixelDensity );
} else if ( windowSettings.PixelDensity > 0 ) {
PixelDensity::setPixelDensity( windowSettings.PixelDensity );
} else if ( environmentDensity > 0 ) {
PixelDensity::setPixelDensity( environmentDensity );
} else if ( offscreen ) {
PixelDensity::setPixelDensity( 1.f );
} else {
+154 -85
View File
@@ -1,11 +1,13 @@
#include <algorithm>
#include <cmath>
#include <eepp/graphics/pixeldensity.hpp>
#include <eepp/graphics/primitives.hpp>
#include <eepp/graphics/renderer/renderer.hpp>
#include <eepp/graphics/text.hpp>
#include <eepp/ui/charts/uichart.hpp>
#include <eepp/ui/uieventdispatcher.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uistyle.hpp>
#include <eepp/ui/uithememanager.hpp>
#include <eepp/ui/uitooltip.hpp>
#include <limits>
@@ -51,8 +53,9 @@ std::optional<DataRange> axisExtent( const ChartModel& model, const ChartAxis* a
return extent;
}
float chartTextWidth( Font* font, const String& label ) {
return font ? Text::getTextWidth( font, 12, label, 0, 4, 0.f, label.getTextHints() ) : 0.f;
Float chartTextWidth( Font* font, const String& label, Uint32 fontSize ) {
return font ? Text::getTextWidth( font, fontSize, label, 0, 4, 0.f, label.getTextHints() )
: 0.f;
}
} // namespace
@@ -88,14 +91,15 @@ void LineSeries::setVisible( bool visible ) {
}
void LineSeries::setColor( Color color ) {
if ( mColor != color ) {
if ( mColor != color || !mColorExplicit ) {
mColor = color;
mColorExplicit = true;
++mGeometryRevision;
changed();
}
}
void LineSeries::setWidth( float width ) {
void LineSeries::setWidth( Float width ) {
width = std::max( 0.f, width );
if ( mWidth != width ) {
mWidth = width;
@@ -120,7 +124,7 @@ void LineSeries::setCap( LineCap cap ) {
}
}
void LineSeries::setMiterLimit( float limit ) {
void LineSeries::setMiterLimit( Float limit ) {
limit = std::isfinite( limit ) ? std::max( 1.f, limit ) : 4.f;
if ( mMiterLimit != limit ) {
mMiterLimit = limit;
@@ -302,6 +306,35 @@ void UIChart::setFollowX( double span ) {
invalidateDraw();
}
void UIChart::setChartStyle( ChartStyle style ) {
mChartStyle = std::move( style );
++mLayoutRevision;
++mViewRevision;
invalidateDraw();
}
void UIChart::updateThemeColors() {
mAxisColor = mChartStyle.axisColor ? *mChartStyle.axisColor
: themeColor( "--tab-line", { 125, 130, 138 } );
mTickColor = mChartStyle.tickColor.value_or( mAxisColor );
mTickLabelColor = mChartStyle.tickLabelColor ? *mChartStyle.tickLabelColor
: themeColor( "--font-hint", { 190, 194, 201 } );
mAxisLabelColor = mChartStyle.axisLabelColor ? *mChartStyle.axisLabelColor
: themeColor( "--font", { 210, 214, 220 } );
if ( mChartStyle.hoverColor ) {
mHoverColor = *mChartStyle.hoverColor;
} else {
mHoverColor = themeColor( "--primary", { 160, 165, 173 } );
mHoverColor.a = 140;
}
const Color series = mChartStyle.seriesColor ? *mChartStyle.seriesColor
: themeColor( "--primary", { 48, 130, 220 } );
if ( mSeriesColor != series ) {
mSeriesColor = series;
++mViewRevision;
}
}
void UIChart::updateFollow() {
if ( mFollowSpan <= 0 )
return;
@@ -382,61 +415,75 @@ void UIChart::onSizeChange() {
void UIChart::updateLayout() {
if ( mLayout.revision == mLayoutRevision )
return;
const float width = getPixelsSize().getWidth();
const float height = getPixelsSize().getHeight();
float leftMargin = 50.f;
const float bottomMargin = xAxis()->label().empty() ? 34.f : 52.f;
const float topMargin = yAxis()->label().empty() ? 12.f : 26.f;
Font* font =
getUISceneNode() ? getUISceneNode()->getUIThemeManager()->getDefaultFont() : nullptr;
mLayout.xAxisLabelWidth = chartTextWidth( font, xAxis()->label() );
float rightMargin = 14.f;
const auto dp = []( Float value ) { return PixelDensity::dpToPx( value ); };
const Uint32 fontSize =
std::max( 1u, static_cast<Uint32>( std::round( dp( mChartStyle.fontSize ) ) ) );
const Float width = getPixelsSize().getWidth();
const Float height = getPixelsSize().getHeight();
Float leftMargin = dp( mChartStyle.leftMargin );
const Float bottomMargin = dp( xAxis()->label().empty() ? mChartStyle.bottomMargin
: mChartStyle.bottomMarginWithLabel );
const Float topMargin =
dp( yAxis()->label().empty() ? mChartStyle.topMargin : mChartStyle.topMarginWithLabel );
Font* font = mChartStyle.font ? mChartStyle.font
: getUISceneNode() ? getUISceneNode()->getUIThemeManager()->getDefaultFont()
: nullptr;
mLayout.xAxisLabelWidth = chartTextWidth( font, xAxis()->label(), fontSize );
Float rightMargin = dp( mChartStyle.rightMargin );
mLayout.rightAxes.clear();
for ( const auto& view : mAxes ) {
if ( view.axis->position() != AxisPosition::Right )
continue;
Layout::RightAxis axisLayout;
axisLayout.axis = view.axis;
axisLayout.ticks = linearTicks( view.viewport.range(),
std::max( 1.f, height - topMargin - bottomMargin ), 50.f );
axisLayout.ticks =
linearTicks( view.viewport.range(), std::max( 1.f, height - topMargin - bottomMargin ),
dp( mChartStyle.yTickSpacing ) );
const double step =
axisLayout.ticks.size() > 1 ? axisLayout.ticks[1] - axisLayout.ticks[0] : 1.0;
float labelWidth = 0.f;
Float labelWidth = 0.f;
for ( double tick : axisLayout.ticks ) {
axisLayout.labels.emplace_back( axisLayout.axis->formatTick( tick, step ) );
labelWidth = std::max( labelWidth, chartTextWidth( font, axisLayout.labels.back() ) );
labelWidth =
std::max( labelWidth, chartTextWidth( font, axisLayout.labels.back(), fontSize ) );
}
axisLayout.width = std::max( 42.f, labelWidth + 16.f );
axisLayout.width = std::max( dp( mChartStyle.minimumAxisMargin ),
labelWidth + dp( mChartStyle.rightAxisLabelPadding ) );
rightMargin += axisLayout.width;
mLayout.rightAxes.emplace_back( std::move( axisLayout ) );
}
for ( int pass = 0; pass < 2; ++pass ) {
const float plotWidth = std::max( 1.f, width - leftMargin - rightMargin );
const float plotHeight = std::max( 1.f, height - topMargin - bottomMargin );
const Float plotWidth = std::max( 1.f, width - leftMargin - rightMargin );
const Float plotHeight = std::max( 1.f, height - topMargin - bottomMargin );
mLayout.plot =
Rectf( leftMargin, topMargin, leftMargin + plotWidth, topMargin + plotHeight );
mLayout.xTicks = linearTicks( viewport( xAxis() ).range(), plotWidth );
mLayout.yTicks = linearTicks( viewport( yAxis() ).range(), plotHeight, 50.f );
mLayout.xTicks =
linearTicks( viewport( xAxis() ).range(), plotWidth, dp( mChartStyle.xTickSpacing ) );
mLayout.yTicks =
linearTicks( viewport( yAxis() ).range(), plotHeight, dp( mChartStyle.yTickSpacing ) );
mLayout.xLabels.clear();
mLayout.yLabels.clear();
mLayout.xLabelWidths.clear();
mLayout.yLabelWidths.clear();
float maximumLabelWidth = 0.f;
Float maximumLabelWidth = 0.f;
const double xStep =
mLayout.xTicks.size() > 1 ? mLayout.xTicks[1] - mLayout.xTicks[0] : 1.0;
const double yStep =
mLayout.yTicks.size() > 1 ? mLayout.yTicks[1] - mLayout.yTicks[0] : 1.0;
for ( double tick : mLayout.xTicks ) {
mLayout.xLabels.emplace_back( xAxis()->formatTick( tick, xStep ) );
mLayout.xLabelWidths.emplace_back( chartTextWidth( font, mLayout.xLabels.back() ) );
mLayout.xLabelWidths.emplace_back(
chartTextWidth( font, mLayout.xLabels.back(), fontSize ) );
}
for ( double tick : mLayout.yTicks ) {
mLayout.yLabels.emplace_back( yAxis()->formatTick( tick, yStep ) );
const float labelWidth = chartTextWidth( font, mLayout.yLabels.back() );
const Float labelWidth = chartTextWidth( font, mLayout.yLabels.back(), fontSize );
mLayout.yLabelWidths.emplace_back( labelWidth );
maximumLabelWidth = std::max( maximumLabelWidth, labelWidth );
}
const float measuredMargin = std::max( 42.f, maximumLabelWidth + 12.f );
const Float measuredMargin =
std::max( dp( mChartStyle.minimumAxisMargin ),
maximumLabelWidth + dp( mChartStyle.axisLabelPadding ) );
if ( pass == 0 && std::abs( measuredMargin - leftMargin ) > 2.f )
leftMargin = measuredMargin;
else
@@ -478,16 +525,17 @@ void UIChart::updateGeometry( SeriesCache& cache, const XYDataRead& read ) {
chunk.xs, chunk.ys );
}
}
const float halfWidth = cache.series->width() * 0.5f;
const Float halfWidth = PixelDensity::dpToPx( cache.series->width() ) * 0.5f;
bool previousValid = false;
bool priorSegment = false;
Vector2f previous;
Vector2f priorDirection;
Vector2f priorNormal;
const Color seriesColor = cache.series->hasCustomColor() ? cache.series->color() : mSeriesColor;
auto addTriangle = [&]( Vector2f a, Vector2f b, Vector2f c ) {
for ( const auto& vertex : { a, b, c } ) {
cache.geometry->addVertex( vertex );
cache.geometry->addColor( cache.series->color() );
cache.geometry->addColor( seriesColor );
}
};
auto addSquareCap = [&]( Vector2f center, Vector2f direction, Vector2f normal, bool start ) {
@@ -506,14 +554,14 @@ void UIChart::updateGeometry( SeriesCache& cache, const XYDataRead& read ) {
continue;
}
const Vector2f current(
static_cast<float>( xView.toPixel( point.x, mLayout.plot.Left, mLayout.plot.getWidth(),
static_cast<Float>( xView.toPixel( point.x, mLayout.plot.Left, mLayout.plot.getWidth(),
false, *cache.series->xAxis() ) ),
static_cast<float>( yView.toPixel( point.y, mLayout.plot.Top, mLayout.plot.getHeight(),
static_cast<Float>( yView.toPixel( point.y, mLayout.plot.Top, mLayout.plot.getHeight(),
true, *cache.series->yAxis() ) ) );
if ( previousValid ) {
const float dx = current.x - previous.x;
const float dy = current.y - previous.y;
const float length = std::hypot( dx, dy );
const Float dx = current.x - previous.x;
const Float dy = current.y - previous.y;
const Float length = std::hypot( dx, dy );
if ( length > 0.001f ) {
const Vector2f direction( dx / length, dy / length );
const Vector2f normal( -dy / length * halfWidth, dx / length * halfWidth );
@@ -522,15 +570,17 @@ void UIChart::updateGeometry( SeriesCache& cache, const XYDataRead& read ) {
addTriangle( previous + normal, previous - normal, current + normal );
addTriangle( current + normal, previous - normal, current - normal );
if ( priorSegment ) {
const float cross =
const Float cross =
priorDirection.x * direction.y - priorDirection.y * direction.x;
if ( std::abs( cross ) > 0.0001f ) {
const float side = cross > 0 ? -1.f : 1.f;
const Float side = cross > 0 ? -1.f : 1.f;
const Vector2f outerA = priorNormal * side;
const Vector2f outerB = normal * side;
// The two segment quads leave this wedge empty at every turn.
addTriangle( previous + outerA, previous, previous + outerB );
const Vector2f sum = outerA + outerB;
const float sumLength = std::hypot( sum.x, sum.y );
const float divisor = sumLength > 0
const Float sumLength = std::hypot( sum.x, sum.y );
const Float divisor = sumLength > 0
? ( sum.x * outerB.x + sum.y * outerB.y ) /
( sumLength * halfWidth )
: 0.f;
@@ -539,8 +589,6 @@ void UIChart::updateGeometry( SeriesCache& cache, const XYDataRead& read ) {
const Vector2f miter =
sum * ( 1.f / sumLength ) * ( halfWidth / divisor );
addTriangle( previous + outerA, previous + miter, previous + outerB );
} else {
addTriangle( previous + outerA, previous, previous + outerB );
}
}
}
@@ -557,89 +605,109 @@ void UIChart::updateGeometry( SeriesCache& cache, const XYDataRead& read ) {
}
void UIChart::drawAxes() {
const auto dp = []( Float value ) { return PixelDensity::dpToPx( value ); };
const Float tickLength = dp( mChartStyle.tickLength );
const Float tickLabelGap = dp( mChartStyle.tickLabelGap );
Primitives primitive;
primitive.setColor( Color( 125, 130, 138 ) );
primitive.setLineWidth( dp( mChartStyle.axisWidth ) );
primitive.setColor( mAxisColor );
const auto& plot = mLayout.plot;
const Vector2f origin( mScreenPos.x, mScreenPos.y );
primitive.drawLine( Line2f( { origin.x + plot.Left, origin.y + plot.Bottom },
{ origin.x + plot.Right, origin.y + plot.Bottom } ) );
primitive.drawLine( Line2f( { origin.x + plot.Left, origin.y + plot.Top },
{ origin.x + plot.Left, origin.y + plot.Bottom } ) );
Font* font =
getUISceneNode() ? getUISceneNode()->getUIThemeManager()->getDefaultFont() : nullptr;
Font* font = mChartStyle.font ? mChartStyle.font
: getUISceneNode() ? getUISceneNode()->getUIThemeManager()->getDefaultFont()
: nullptr;
FontStyleConfig textStyle;
textStyle.Font = font;
textStyle.CharacterSize = 12;
textStyle.FontColor = Color( 190, 194, 201 );
textStyle.CharacterSize =
std::max( 1u, static_cast<Uint32>( std::round( dp( mChartStyle.fontSize ) ) ) );
textStyle.FontColor = mTickLabelColor;
auto drawText = [&]( const String& label, Vector2f position ) {
Text::draw( label, { std::round( position.x ), std::round( position.y ) }, textStyle, 4,
label.getTextHints() );
};
primitive.setColor( mTickColor );
for ( size_t i = 0; i < mLayout.xTicks.size(); ++i ) {
const float x = static_cast<float>( viewport( xAxis() ).toPixel(
const Float x = static_cast<Float>( viewport( xAxis() ).toPixel(
mLayout.xTicks[i], plot.Left, plot.getWidth(), false, *xAxis() ) );
primitive.drawLine( Line2f( { origin.x + x, origin.y + plot.Bottom },
{ origin.x + x, origin.y + plot.Bottom + 4 } ) );
{ origin.x + x, origin.y + plot.Bottom + tickLength } ) );
if ( font ) {
const float textWidth = mLayout.xLabelWidths[i];
const Float textWidth = mLayout.xLabelWidths[i];
const String& label = mLayout.xLabels[i];
Text::draw( label, { origin.x + x - textWidth * 0.5f, origin.y + plot.Bottom + 6 },
textStyle, 4, label.getTextHints() );
drawText( label,
{ origin.x + x - textWidth * 0.5f, origin.y + plot.Bottom + tickLabelGap } );
}
}
for ( size_t i = 0; i < mLayout.yTicks.size(); ++i ) {
const float y = static_cast<float>( viewport( yAxis() ).toPixel(
const Float y = static_cast<Float>( viewport( yAxis() ).toPixel(
mLayout.yTicks[i], plot.Top, plot.getHeight(), true, *yAxis() ) );
primitive.drawLine( Line2f( { origin.x + plot.Left - 4, origin.y + y },
primitive.drawLine( Line2f( { origin.x + plot.Left - tickLength, origin.y + y },
{ origin.x + plot.Left, origin.y + y } ) );
if ( font ) {
const float textWidth = mLayout.yLabelWidths[i];
const Float textWidth = mLayout.yLabelWidths[i];
const String& label = mLayout.yLabels[i];
Text::draw( label, { origin.x + plot.Left - 8 - textWidth, origin.y + y - 7 },
textStyle, 4, label.getTextHints() );
drawText( label, { origin.x + plot.Left - tickLength - tickLabelGap - textWidth,
origin.y + y - textStyle.CharacterSize * 0.6f } );
}
}
float rightOffset = 0.f;
Float rightOffset = 0.f;
for ( const auto& axisLayout : mLayout.rightAxes ) {
const float axisX = plot.Right + rightOffset;
const Float axisX = plot.Right + rightOffset;
primitive.setColor( mAxisColor );
primitive.drawLine( Line2f( { origin.x + axisX, origin.y + plot.Top },
{ origin.x + axisX, origin.y + plot.Bottom } ) );
primitive.setColor( mTickColor );
for ( size_t i = 0; i < axisLayout.ticks.size(); ++i ) {
const float y =
static_cast<float>( viewport( axisLayout.axis )
const Float y =
static_cast<Float>( viewport( axisLayout.axis )
.toPixel( axisLayout.ticks[i], plot.Top, plot.getHeight(),
true, *axisLayout.axis ) );
primitive.drawLine( Line2f( { origin.x + axisX, origin.y + y },
{ origin.x + axisX + 4, origin.y + y } ) );
{ origin.x + axisX + tickLength, origin.y + y } ) );
if ( font ) {
const String& label = axisLayout.labels[i];
Text::draw( label, { origin.x + axisX + 7, origin.y + y - 7 }, textStyle, 4,
label.getTextHints() );
drawText( label, { origin.x + axisX + tickLength + tickLabelGap,
origin.y + y - textStyle.CharacterSize * 0.6f } );
}
}
rightOffset += axisLayout.width;
}
if ( font && !xAxis()->label().empty() ) {
textStyle.FontColor = Color( 210, 214, 220 );
textStyle.FontColor = mAxisLabelColor;
const String& label = xAxis()->label();
Text::draw( label,
{ origin.x + plot.Left + ( plot.getWidth() - mLayout.xAxisLabelWidth ) * 0.5f,
origin.y + plot.Bottom + 25 },
textStyle, 4, label.getTextHints() );
drawText( label,
{ origin.x + plot.Left + ( plot.getWidth() - mLayout.xAxisLabelWidth ) * 0.5f,
origin.y + plot.Bottom + dp( mChartStyle.axisLabelGap ) } );
}
if ( font && !yAxis()->label().empty() ) {
textStyle.FontColor = Color( 210, 214, 220 );
textStyle.FontColor = mAxisLabelColor;
const String& label = yAxis()->label();
Text::draw( label, { origin.x + plot.Left, origin.y + 2 }, textStyle, 4,
label.getTextHints() );
drawText( label, { origin.x + plot.Left, origin.y + dp( mChartStyle.axisLabelInset ) } );
}
}
void UIChart::draw() {
UIWidget::draw();
if ( !mVisible || getPixelsSize().getWidth() < 20 || getPixelsSize().getHeight() < 20 )
if ( !mVisible || getPixelsSize().getWidth() < PixelDensity::dpToPx( 20.f ) ||
getPixelsSize().getHeight() < PixelDensity::dpToPx( 20.f ) )
return;
const Float density = PixelDensity::getPixelDensity();
if ( mLastDensity != density ) {
mLastDensity = density;
++mLayoutRevision;
++mViewRevision;
}
updateThemeColors();
synchronize();
updateFollow();
updateAutoRanges();
Font* font =
getUISceneNode() ? getUISceneNode()->getUIThemeManager()->getDefaultFont() : nullptr;
Font* font = mChartStyle.font ? mChartStyle.font
: getUISceneNode() ? getUISceneNode()->getUIThemeManager()->getDefaultFont()
: nullptr;
if ( mLastXAxisLabel != xAxis()->label() || mLastYAxisLabel != yAxis()->label() ||
mLastFont != font ) {
mLastXAxisLabel = xAxis()->label();
@@ -696,7 +764,8 @@ void UIChart::draw() {
}
if ( mHover ) {
Primitives primitive;
primitive.setColor( Color( 160, 165, 173, 140 ) );
primitive.setColor( mHoverColor );
primitive.setLineWidth( PixelDensity::dpToPx( mChartStyle.axisWidth ) );
primitive.drawLine( Line2f( { mLastMouse.x, mScreenPos.y + plot.Top },
{ mLastMouse.x, mScreenPos.y + plot.Bottom } ) );
}
@@ -713,7 +782,7 @@ void UIChart::updateHover( Vector2f screenPosition ) {
return;
}
mHover = true;
float bestDistance = 10.f;
Float bestDistance = PixelDensity::dpToPx( mChartStyle.hoverDistance );
LineSeries* bestSeries = nullptr;
ChartPoint bestPoint;
size_t bestIndex = 0;
@@ -734,23 +803,23 @@ void UIChart::updateHover( Vector2f screenPosition ) {
previous = nullptr;
continue;
}
const Vector2f pixel( static_cast<float>( xView.toPixel(
const Vector2f pixel( static_cast<Float>( xView.toPixel(
point.x, mLayout.plot.Left, mLayout.plot.getWidth(),
false, *series->xAxis() ) ),
static_cast<float>( yView.toPixel(
static_cast<Float>( yView.toPixel(
point.y, mLayout.plot.Top, mLayout.plot.getHeight(), true,
*series->yAxis() ) ) );
if ( previous ) {
const Vector2f segment = pixel - previousPixel;
const float lengthSquared = segment.x * segment.x + segment.y * segment.y;
const float t =
const Float lengthSquared = segment.x * segment.x + segment.y * segment.y;
const Float t =
lengthSquared > 0.0001f
? std::clamp( ( ( local.x - previousPixel.x ) * segment.x +
( local.y - previousPixel.y ) * segment.y ) /
lengthSquared,
0.f, 1.f )
: 0.f;
const float distance = std::hypot( previousPixel.x + t * segment.x - local.x,
const Float distance = std::hypot( previousPixel.x + t * segment.x - local.x,
previousPixel.y + t * segment.y - local.y );
if ( distance < bestDistance ) {
const size_t candidate =
@@ -780,15 +849,15 @@ void UIChart::updateHover( Vector2f screenPosition ) {
const ChartPoint point = pointAt( read, candidate );
if ( !std::isfinite( point.x ) || !std::isfinite( point.y ) )
continue;
const float px = static_cast<float>( viewport( series->xAxis() )
const Float px = static_cast<Float>( viewport( series->xAxis() )
.toPixel( point.x, mLayout.plot.Left,
mLayout.plot.getWidth(), false,
*series->xAxis() ) );
const float py = static_cast<float>( viewport( series->yAxis() )
const Float py = static_cast<Float>( viewport( series->yAxis() )
.toPixel( point.y, mLayout.plot.Top,
mLayout.plot.getHeight(), true,
*series->yAxis() ) );
const float distance = std::hypot( px - local.x, py - local.y );
const Float distance = std::hypot( px - local.x, py - local.y );
if ( distance < bestDistance ) {
bestDistance = distance;
bestSeries = series;
@@ -882,8 +951,8 @@ Uint32 UIChart::onMouseMove( const Vector2i& position, const Uint32& flags ) {
if ( mPanning && ( flags & EE_BUTTON_LMASK ) && mLayout.plot.getWidth() > 0 &&
mLayout.plot.getHeight() > 0 ) {
mFollowSpan = 0.0;
const float dx = mouse.x - mLastMouse.x;
const float dy = mouse.y - mLastMouse.y;
const Float dx = mouse.x - mLastMouse.x;
const Float dy = mouse.y - mLastMouse.y;
for ( auto& view : mAxes ) {
const bool horizontal = view.axis->position() == AxisPosition::Bottom ||
view.axis->position() == AxisPosition::Top;
+5
View File
@@ -190,6 +190,11 @@ StyleSheetVariable UIStyle::getVariable( const std::string& variable ) {
return resolved ? *resolved : StyleSheetVariable();
}
Color UIStyle::getColorVariable( const std::string& variable, Color fallback ) {
const StyleSheetVariable* resolved = getVariableRef( variable );
return resolved ? Color::fromString( resolved->getValue() ) : fallback;
}
const StyleSheetVariable* UIStyle::getVariableRef( const std::string& variable ) {
if ( NULL != mWidget && NULL != mWidget->getUISceneNode() )
resetGlobalDefinition();
+4
View File
@@ -1460,6 +1460,10 @@ UIStyle* UIWidget::getUIStyle() const {
return mStyle;
}
Color UIWidget::themeColor( const std::string& variable, Color fallback ) const {
return mStyle ? mStyle->getColorVariable( variable, fallback ) : fallback;
}
void UIWidget::reloadStyle( bool reloadChildren, bool disableAnimations, bool reportStateChange,
bool forceReApplyProperties, bool resetPropertyCache ) {
createStyle();
+4 -2
View File
@@ -134,8 +134,10 @@ bool WindowSDL::create( WindowSettings Settings, ContextSettings Context ) {
#if EE_PLATFORM != EE_PLATFORM_MACOS && EE_PLATFORM != EE_PLATFORM_IOS && \
EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN
mWindow.WindowConfig.Width *= mWindow.WindowConfig.PixelDensity;
mWindow.WindowConfig.Height *= mWindow.WindowConfig.PixelDensity;
if ( mWindow.WindowConfig.PixelDensity > 0 ) {
mWindow.WindowConfig.Width *= mWindow.WindowConfig.PixelDensity;
mWindow.WindowConfig.Height *= mWindow.WindowConfig.PixelDensity;
}
#endif
mSDLWindow = SDL_CreateWindow( mWindow.WindowConfig.Title.c_str(), mWindow.WindowConfig.Width,
+7 -2
View File
@@ -336,8 +336,13 @@ EE::Window::Window* Engine::createWindow( WindowSettings Settings, ContextSettin
mWindows.insert( { mWindow->getWindowID(), mWindow } );
if ( firstWindow && Settings.PixelDensity > 0 )
PixelDensity::setPixelDensity( Settings.PixelDensity );
if ( firstWindow ) {
const Float density = Settings.PixelDensity > 0
? Settings.PixelDensity
: PixelDensity::getEnvironmentPixelDensity();
if ( density > 0 )
PixelDensity::setPixelDensity( density );
}
return window;
}
+6 -2
View File
@@ -81,7 +81,7 @@ EE_MAIN_FUNC int main( int, char** ) {
[]( double value, double ) { return String::format( "%.1f C", value ); } );
std::vector<ChartPoint> load;
std::vector<double> temperatureX;
std::vector<float> temperatureY;
std::vector<Float> temperatureY;
load.reserve( 120 );
temperatureX.reserve( 120 );
temperatureY.reserve( 120 );
@@ -90,7 +90,7 @@ EE_MAIN_FUNC int main( int, char** ) {
load.push_back( { x, 50.0 + 18.0 * std::sin( x * 0.14 ) + 5.0 * std::sin( x * 0.9 ) } );
temperatureX.push_back( x );
temperatureY.push_back(
static_cast<float>( 37.0 + 0.08 * x + 2.0 * std::sin( x * 0.12 ) ) );
static_cast<Float>( 37.0 + 0.08 * x + 2.0 * std::sin( x * 0.12 ) ) );
}
auto* loadSeries = dual->addLineSeries( "Load" );
loadSeries->setColor( Color( 129, 211, 142 ) );
@@ -124,6 +124,10 @@ EE_MAIN_FUNC int main( int, char** ) {
dual->fit();
auto* dense = layout->find<UIChart>( "dense" );
ChartStyle denseStyle = dense->chartStyle();
denseStyle.hoverColor = Color( 182, 146, 255, 150 );
denseStyle.tickLabelColor = Color( 202, 193, 227 );
dense->setChartStyle( denseStyle );
dense->xAxis()->setLabel( "Sample" );
dense->yAxis()->setLabel( "Signal" );
std::vector<ChartPoint> samples;
+71 -8
View File
@@ -1,16 +1,22 @@
#include "utest.h"
#include <eepp/graphics/image.hpp>
#include <eepp/graphics/pixeldensity.hpp>
#include <eepp/system/filesystem.hpp>
#include <eepp/system/sys.hpp>
#include <eepp/ui/charts/uichart.hpp>
#include <eepp/ui/uiapplication.hpp>
#include <eepp/ui/uiroot.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uistyle.hpp>
#include <eepp/ui/uitooltip.hpp>
using namespace EE;
using namespace EE::Window;
using namespace EE::UI;
using namespace EE::UI::Charts;
using namespace EE::UI::CSS;
using namespace EE::Graphics;
using namespace EE::System;
namespace {
@@ -28,10 +34,9 @@ class ChartProbe : public UIChart {
} // namespace
UTEST( UIChart, buildsAndDrawsMultipleSeries ) {
UIApplication app( WindowSettings{ 480, 320, "Chart Test" },
UIApplication::Settings( System::Sys::getProcessPath() + ".." +
System::FileSystem::getOSSlash(),
1.f ) );
UIApplication app(
WindowSettings{ 480, 320, "Chart Test" },
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1.f ) );
auto* declarative = app.getUI()->loadLayoutFromString(
"<Chart id=\"declarative_chart\" layout_width=\"20dp\" layout_height=\"20dp\" />" );
ASSERT_TRUE( declarative != nullptr );
@@ -53,10 +58,12 @@ UTEST( UIChart, buildsAndDrawsMultipleSeries ) {
second->setCap( LineCap::Square );
second->setJoin( LineJoin::Bevel );
auto* right = chart->model()->addAxis( AxisPosition::Right );
EXPECT_TRUE( chart->model()->axes().is_small() );
second->setYAxis( right );
second->setPoints( std::vector<ChartPoint>{ { 0, 2 }, { 1, 1 }, { 2, 3 } } );
chart->fit();
EXPECT_EQ( chart->model()->series().size(), 2u );
EXPECT_TRUE( chart->model()->series().is_small() );
auto* secondView = UIChart::New();
secondView->setModel( chart->sharedModel() );
secondView->setPixelsSize( 200, 140 );
@@ -78,10 +85,9 @@ UTEST( UIChart, buildsAndDrawsMultipleSeries ) {
}
UTEST( UIChart, hoverShowsUpdatesAndHidesTooltip ) {
UIApplication app( WindowSettings{ 480, 320, "Chart Hover Test" },
UIApplication::Settings( System::Sys::getProcessPath() + ".." +
System::FileSystem::getOSSlash(),
1.f ) );
UIApplication app(
WindowSettings{ 480, 320, "Chart Hover Test" },
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1.f ) );
auto* chart = eeNew( ChartProbe, () );
chart->setPixelsSize( 400, 240 );
chart->setParent( app.getUI()->getRoot() );
@@ -115,3 +121,60 @@ UTEST( UIChart, hoverShowsUpdatesAndHidesTooltip ) {
EXPECT_TRUE( chart->getTooltipText().empty() );
EXPECT_TRUE( chart->getTooltip()->getText().empty() );
}
UTEST( UIChart, styleUsesDeviceIndependentMeasurements ) {
const Float previousDensity = PixelDensity::getPixelDensity();
UIApplication app(
WindowSettings{ 480, 320, "Chart Density Test" },
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 2.f ) );
auto* chart = UIChart::New();
chart->setPixelsSize( 400, 240 );
chart->setParent( app.getUI()->getRoot() );
ChartStyle style = chart->chartStyle();
style.fontSize = 15.f;
style.axisColor = Color( 12, 34, 56 );
chart->setChartStyle( style );
chart->getUIStyle()->setStyleSheetVariable( StyleSheetVariable( "--chart-test", "#123456" ) );
EXPECT_TRUE( chart->themeColor( "--chart-test", Color::Black ) == Color( 18, 52, 86 ) );
EXPECT_TRUE( chart->themeColor( "--chart-absent", Color::White ) == Color::White );
chart->addLineSeries( "Dense" )->setPoints( std::vector<ChartPoint>{ { 0, 0 }, { 1, 1 } } );
app.getUI()->flushDirtyStyleAndLayout();
app.getUI()->draw();
EXPECT_EQ( PixelDensity::getPixelDensity(), 2.f );
EXPECT_EQ( chart->chartStyle().fontSize, 15.f );
EXPECT_TRUE( chart->chartStyle().axisColor == Color( 12, 34, 56 ) );
EXPECT_EQ( app.getWindow()->getWindowInfo()->WindowConfig.Width, 480u );
PixelDensity::setPixelDensity( previousDensity );
}
UTEST( UIChart, miterJoinFillsInnerWedgeAtHighDensity ) {
const Float previousDensity = PixelDensity::getPixelDensity();
UIApplication app(
WindowSettings{ 220, 220, "Chart Join Test" },
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 3.f ) );
auto* chart = UIChart::New();
chart->setPixelsSize( 200, 200 );
chart->setParent( app.getUI()->getRoot() );
ChartStyle style;
style.leftMargin = style.rightMargin = style.topMargin = style.bottomMargin = 0.f;
style.minimumAxisMargin = style.axisLabelPadding = 0.f;
chart->setChartStyle( style );
chart->xAxis()->setFormatter( []( double, double ) { return String(); } );
chart->yAxis()->setFormatter( []( double, double ) { return String(); } );
chart->setAxisRange( chart->xAxis(), { 0, 100 } );
chart->setAxisRange( chart->yAxis(), { 0, 100 } );
auto* line = chart->addLineSeries( "Corner" );
line->setColor( Color( 0, 255, 0 ) );
line->setWidth( 12.f );
line->setPoints( std::vector<ChartPoint>{ { 20, 50 }, { 60, 50 }, { 60, 90 } } );
app.getUI()->flushDirtyStyleAndLayout();
app.getWindow()->setClearColor( Color( 20, 20, 20 ) );
app.getWindow()->clear();
app.getUI()->draw();
Image image = app.getWindow()->getFrontBufferImage();
ASSERT_TRUE( image.getWidth() > 125 && image.getHeight() > 105 );
const Color wedge = image.getPixel( 125, 105 );
EXPECT_TRUE_MSG( wedge.g > 180 && wedge.r < 100,
"The inside wedge between adjacent stroke quads must be filled" );
PixelDensity::setPixelDensity( previousDensity );
}
@@ -0,0 +1,46 @@
#include "utest.h"
#include <cstdlib>
#include <eepp/graphics/pixeldensity.hpp>
#include <eepp/system/sys.hpp>
#include <eepp/ui/uiapplication.hpp>
#include <eepp/window/window.hpp>
#include <string>
using namespace EE;
using namespace EE::Graphics;
using namespace EE::System;
using namespace EE::UI;
using namespace EE::Window;
UTEST( PixelDensity, environmentDefaultIsStrictAndDoesNotScaleWindowSettings ) {
const char* prior = std::getenv( "EEPP_PIXEL_DENSITY" );
const std::string previous = prior ? prior : "";
EXPECT_TRUE( Sys::setEnv( "EEPP_PIXEL_DENSITY", "1.75" ) );
EXPECT_EQ( PixelDensity::getEnvironmentPixelDensity(), 1.75f );
WindowSettings window( 320, 240, "Density Test" );
EXPECT_EQ( window.Width, 320u );
EXPECT_EQ( window.Height, 240u );
EXPECT_EQ( window.PixelDensity, 0.f );
EXPECT_TRUE( Sys::setEnv( "EEPP_PIXEL_DENSITY", "1.75oops" ) );
EXPECT_EQ( PixelDensity::getEnvironmentPixelDensity(), 0.f );
EXPECT_TRUE( Sys::setEnv( "EEPP_PIXEL_DENSITY", "-2" ) );
EXPECT_EQ( PixelDensity::getEnvironmentPixelDensity(), 0.f );
EXPECT_TRUE( Sys::setEnv( "EEPP_PIXEL_DENSITY", "nan" ) );
EXPECT_EQ( PixelDensity::getEnvironmentPixelDensity(), 0.f );
EXPECT_TRUE( Sys::setEnv( "EEPP_PIXEL_DENSITY", previous.c_str() ) );
}
UTEST( PixelDensity, environmentDefaultAppliesToUIWithoutResizingWindow ) {
const char* prior = std::getenv( "EEPP_PIXEL_DENSITY" );
const std::string previous = prior ? prior : "";
const Float priorDensity = PixelDensity::getPixelDensity();
EXPECT_TRUE( Sys::setEnv( "EEPP_PIXEL_DENSITY", "1.75" ) );
{
UIApplication app( WindowSettings{ 320, 240, "Environment Density Test" } );
EXPECT_EQ( PixelDensity::getPixelDensity(), 1.75f );
EXPECT_EQ( app.getWindow()->getWindowInfo()->WindowConfig.Width, 320u );
EXPECT_EQ( app.getWindow()->getWindowInfo()->WindowConfig.Height, 240u );
}
EXPECT_TRUE( Sys::setEnv( "EEPP_PIXEL_DENSITY", previous.c_str() ) );
PixelDensity::setPixelDensity( priorDensity );
}
+11 -11
View File
@@ -4574,6 +4574,7 @@ void App::init( InitParameters& params ) {
mPortableMode = params.portable || !params.profile.empty();
mProfilePath = params.profile;
mDisplayDPI = currentDisplay->getDPI();
const Float environmentDensity = PixelDensity::getEnvironmentPixelDensity();
mUseFrameBuffer = params.frameBuffer;
mBenchmarkMode = params.benchmarkMode;
mDisablePlugins = params.disablePlugins;
@@ -4618,19 +4619,18 @@ void App::init( InitParameters& params ) {
mDisplayDPI = currentDisplay->getDPI();
#if EE_PLATFORM == EE_PLATFORM_ANDROID
mConfig.windowState.pixelDensity =
params.pidelDensity > 0
? params.pidelDensity
: ( mConfig.windowState.pixelDensity > 0 ? mConfig.windowState.pixelDensity
: currentDisplay->getPixelDensity() > 2 ? currentDisplay->getPixelDensity() / 2
: currentDisplay->getPixelDensity() );
const Float displayDensity = currentDisplay->getPixelDensity() > 2
? currentDisplay->getPixelDensity() / 2
: currentDisplay->getPixelDensity();
#else
mConfig.windowState.pixelDensity =
params.pidelDensity > 0
? params.pidelDensity
: ( mConfig.windowState.pixelDensity > 0 ? mConfig.windowState.pixelDensity
: currentDisplay->getPixelDensity() );
const Float displayDensity = currentDisplay->getPixelDensity();
#endif
if ( params.pidelDensity > 0 ) {
mConfig.windowState.pixelDensity = params.pidelDensity;
} else if ( mConfig.windowState.pixelDensity <= 0 ) {
mConfig.windowState.pixelDensity =
environmentDensity > 0 ? environmentDensity : displayDensity;
}
displayManager->enableScreenSaver();
displayManager->enableMouseFocusClickThrough();
+8 -2
View File
@@ -814,8 +814,14 @@ int App::run( int argc, char* argv[] ) {
}
UIApplication::Settings appSettings;
appSettings.basePath = FileSystem::removeLastFolderFromPath( resPath );
appSettings.pixelDensity = config->window.pixelDensity > 0 ? config->window.pixelDensity
: currentDisplay->getPixelDensity();
const Float environmentDensity = PixelDensity::getEnvironmentPixelDensity();
if ( config->window.pixelDensity > 0 ) {
appSettings.pixelDensity = config->window.pixelDensity;
} else if ( environmentDensity > 0 ) {
appSettings.pixelDensity = environmentDensity;
} else {
appSettings.pixelDensity = currentDisplay->getPixelDensity();
}
appSettings.fontHinting = config->font.hinting;
appSettings.fontAntialiasing = config->font.antialiasing;
appSettings.baseFont = uiFont.get();