mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-26 08:08:28 +03:00
Fix remaining DPI inconsistencies and expand regression coverage
Correct checkbox and radio autosizing, submenu padding, scrollbar skin sizing, and nested menu-bar popup coordinates. Preserve resolved stroke widths through CSS serialization and transitions, including HTML's special px scaling. Add high-density regressions for these fixes and the preceding DPI cleanup commits, covering padding and margin setters, widget defaults, dialog dimensions, table padding, editor spacing, and diff gutters.
This commit is contained in:
@@ -27,9 +27,9 @@ arguments. Do not add sanitizer, linker, framework, generator, architecture, or
|
||||
outside the scripts unless `.ecode/project_build.json` itself requires them. Do not fall back to a
|
||||
hand-written Premake or direct Make command.
|
||||
|
||||
## Release Performance Builds (Linux)
|
||||
## Release Builds (Linux)
|
||||
|
||||
For performance investigations, use the `eepp-linux-ninja` configuration from
|
||||
For release builds, use the `eepp-linux-ninja` configuration from
|
||||
`.ecode/project_build.json`. At the time of writing, its commands are:
|
||||
|
||||
`premake5 --disable-static-build --with-debug-symbols --with-backend=SDL3 ninja`
|
||||
|
||||
@@ -7,7 +7,7 @@ The test binary manages its own current working directory, so you can execute it
|
||||
|
||||
The required default workflow is:
|
||||
|
||||
1. Build the release unit-test target.
|
||||
1. Build the release unit-test target (use ninja build).
|
||||
2. Run `bin/unit_tests/eepp-unit_tests` directly, without Xvfb.
|
||||
3. If sandbox restrictions prevent access to the host display, retry that same direct command with
|
||||
elevated permissions.
|
||||
|
||||
@@ -66,6 +66,8 @@ class EE_API UIMenuBar : public UIWidget {
|
||||
bool isGlobalMenuBarEnabled() const;
|
||||
|
||||
protected:
|
||||
void positionMenu( UISelectButton* button, UIPopUpMenu* menu );
|
||||
|
||||
UIMenuBar();
|
||||
|
||||
typedef std::vector<std::pair<UISelectButton*, UIPopUpMenu*>> MenuBarList;
|
||||
|
||||
@@ -1139,6 +1139,9 @@ class EE_API UINode : public Node {
|
||||
virtual Float convertLength( const CSS::StyleSheetLength& length,
|
||||
const Float& containerLength ) const;
|
||||
|
||||
/** Serializes a resolved pixel length for reapplication, including HTML px scaling. */
|
||||
std::string pixelsLengthToString( Float pixels ) const;
|
||||
|
||||
/**
|
||||
* @brief Converts a CSS length to density-independent pixels (dp).
|
||||
*
|
||||
|
||||
@@ -73,7 +73,7 @@ void StyleSheetPropertyAnimation::tweenProperty(
|
||||
static_cast<Int32>( startColor.a + ( endColor.a - startColor.a ) * progress ),
|
||||
255 ) );
|
||||
widget->applyProperty(
|
||||
StyleSheetProperty( property, resColor.toHexString(), propertyIndex ) );
|
||||
StyleSheetProperty( property, resColor.toHexString(), propertyIndex ) );
|
||||
break;
|
||||
}
|
||||
case PropertyType::NumberLength: {
|
||||
@@ -83,8 +83,8 @@ void StyleSheetPropertyAnimation::tweenProperty(
|
||||
Float end = widget->convertLength( endValue, containerLength );
|
||||
Float value = easingFn( timingFunction, timingFunctionParameters, normalizedProgress,
|
||||
start, end - start, 1.f );
|
||||
widget->applyProperty(
|
||||
StyleSheetProperty( property, String::fromFloat( value, "px" ), propertyIndex ) );
|
||||
widget->applyProperty( StyleSheetProperty(
|
||||
property, widget->pixelsLengthToString( value ), propertyIndex ) );
|
||||
|
||||
if ( isDone ) {
|
||||
widget->applyProperty( StyleSheetProperty( property, endValue, propertyIndex ) );
|
||||
|
||||
@@ -84,9 +84,9 @@ void UICheckBox::onThemeLoaded() {
|
||||
void UICheckBox::onAutoSize() {
|
||||
if ( mFlags & UI_AUTO_SIZE ) {
|
||||
if ( getSize().getWidth() == 0 ) {
|
||||
setInternalPixelsWidth( (int)mTextCache.getTextWidth() +
|
||||
mActiveButton->getPixelsSize().getWidth() + mTextSeparation +
|
||||
mPaddingPx.Left + mPaddingPx.Right );
|
||||
setInternalPixelsWidth(
|
||||
(int)mTextCache.getTextWidth() + mActiveButton->getPixelsSize().getWidth() +
|
||||
PixelDensity::dpToPx( mTextSeparation ) + mPaddingPx.Left + mPaddingPx.Right );
|
||||
}
|
||||
|
||||
if ( getSize().getHeight() == 0 ) {
|
||||
|
||||
@@ -3274,7 +3274,7 @@ std::string UICodeEditor::getPropertyString( const PropertyDefinition* propertyD
|
||||
case PropertyId::FontWeight:
|
||||
return Text::fontWeightToString( mFontStyleConfig.Weight );
|
||||
case PropertyId::TextStrokeWidth:
|
||||
return String::fromFloat( getOutlineThickness(), "px" );
|
||||
return pixelsLengthToString( getOutlineThickness() );
|
||||
case PropertyId::TextStrokeColor:
|
||||
return getOutlineColor().toHexString();
|
||||
case PropertyId::TextSelection:
|
||||
|
||||
@@ -281,7 +281,7 @@ std::string UIConsole::getPropertyString( const PropertyDefinition* propertyDef,
|
||||
case PropertyId::FontWeight:
|
||||
return Text::fontWeightToString( getFontStyleConfig().Weight );
|
||||
case PropertyId::TextStrokeWidth:
|
||||
return String::fromFloat( getFontOutlineThickness(), "px" );
|
||||
return pixelsLengthToString( getFontOutlineThickness() );
|
||||
case PropertyId::TextStrokeColor:
|
||||
return getFontOutlineColor().toHexString();
|
||||
default:
|
||||
|
||||
@@ -46,19 +46,25 @@ void UIMenuBar::setCurrentMenu( UIPopUpMenu* currentMenu ) {
|
||||
mWaitingUp = nullptr;
|
||||
}
|
||||
|
||||
void UIMenuBar::positionMenu( UISelectButton* button, UIPopUpMenu* menu ) {
|
||||
Vector2f pos( 0, button->getSize().getHeight() );
|
||||
button->nodeToWorld( pos );
|
||||
menu->setParent( getWindowContainer() );
|
||||
menu->getParent()->worldToNode( pos );
|
||||
menu->setPosition( pos );
|
||||
}
|
||||
|
||||
void UIMenuBar::showMenu( const Uint32& index ) {
|
||||
eeASSERT( index < mButtons.size() );
|
||||
auto but = mButtons[index];
|
||||
auto tbut = but.first;
|
||||
auto tpop = but.second;
|
||||
|
||||
Vector2f pos( tbut->getPosition().x, tbut->getPosition().y + tbut->getSize().getHeight() );
|
||||
tpop->setPosition( pos );
|
||||
positionMenu( tbut, tpop );
|
||||
|
||||
if ( !tpop->isVisible() ) {
|
||||
mCurrentMenu = tpop;
|
||||
tbut->select();
|
||||
tpop->setParent( getWindowContainer() );
|
||||
tpop->show();
|
||||
mWaitingUp = tpop;
|
||||
} else if ( mCurrentMenu != tpop || mWaitingUp == nullptr ) {
|
||||
@@ -321,15 +327,12 @@ Uint32 UIMenuBar::onMessage( const NodeMessage* msg ) {
|
||||
if ( tpop == nullptr )
|
||||
return 1;
|
||||
|
||||
Vector2f pos( tbut->getPosition().x,
|
||||
tbut->getPosition().y + tbut->getSize().getHeight() );
|
||||
tpop->setPosition( pos );
|
||||
positionMenu( tbut, tpop );
|
||||
|
||||
if ( msg->getMsg() == NodeMessage::MouseOver ) {
|
||||
if ( nullptr != mCurrentMenu && mCurrentMenu != tpop ) {
|
||||
mCurrentMenu = tpop;
|
||||
tbut->select();
|
||||
tpop->setParent( getWindowContainer() );
|
||||
tpop->show();
|
||||
}
|
||||
} else {
|
||||
@@ -338,7 +341,6 @@ Uint32 UIMenuBar::onMessage( const NodeMessage* msg ) {
|
||||
if ( !tpop->isVisible() ) {
|
||||
mCurrentMenu = tpop;
|
||||
tbut->select();
|
||||
tpop->setParent( getWindowContainer() );
|
||||
tpop->show();
|
||||
mWaitingUp = tpop;
|
||||
} else if ( mCurrentMenu != tpop || mWaitingUp == nullptr ) {
|
||||
|
||||
@@ -104,7 +104,7 @@ void UIMenuSubMenu::showSubMenu() {
|
||||
mSubMenu->setParent( menu->getParent() );
|
||||
Vector2f pos = getPixelsPosition();
|
||||
nodeToWorldTranslation( pos );
|
||||
pos.x += mSize.getWidth() + menu->getPadding().Right;
|
||||
pos.x += mSize.getWidth() + menu->getPixelsPadding().Right;
|
||||
UIMenu::findBestMenuPos( pos, mSubMenu, menu, this );
|
||||
mSubMenu->getParent()->worldToNode( pos );
|
||||
mSubMenu->setPosition( pos );
|
||||
|
||||
@@ -64,7 +64,7 @@ UIMessageBox::UIMessageBox( const Type& type, const String& message, const Uint3
|
||||
mTextEdit = UITextEdit::New();
|
||||
mTextEdit->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed )
|
||||
->setLayoutMargin( Rectf( 0, 4, 0, 4 ) )
|
||||
->setSize( Vector2f{ 400, 100 } )
|
||||
->setSize( Vector2f{ 600, 200 } )
|
||||
->setParent( vlay );
|
||||
mTextEdit->getDocument().setCommand( "complete-edit",
|
||||
[this] { sendCommonEvent( Event::OnConfirm ); } );
|
||||
|
||||
@@ -1848,6 +1848,11 @@ Float UINode::convertLength( const CSS::StyleSheetLength& length,
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string UINode::pixelsLengthToString( Float pixels ) const {
|
||||
return mFlags & UI_HTML_ELEMENT ? String::fromFloat( PixelDensity::pxToDp( pixels ), "dp" )
|
||||
: String::fromFloat( pixels, "px" );
|
||||
}
|
||||
|
||||
Float UINode::convertLengthAsDp( const StyleSheetLength& length,
|
||||
const Float& containerLength ) const {
|
||||
return PixelDensity::pxToDp( convertLength( length, containerLength ) );
|
||||
|
||||
@@ -87,8 +87,8 @@ void UIRadioButton::onAutoSize() {
|
||||
}
|
||||
|
||||
if ( getSize().getHeight() == 0 ) {
|
||||
setInternalHeight( mActiveButton->getSize().getHeight() + mPaddingPx.Top +
|
||||
mPaddingPx.Bottom );
|
||||
setInternalHeight( mActiveButton->getSize().getHeight() + mPadding.Top +
|
||||
mPadding.Bottom );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -681,7 +681,7 @@ std::string UIRichText::getPropertyString( const PropertyDefinition* propertyDef
|
||||
return String::fromFloat( getFontShadowOffset().x ) + " " +
|
||||
String::fromFloat( getFontShadowOffset().y );
|
||||
case PropertyId::TextStrokeWidth:
|
||||
return String::fromFloat( getOutlineThickness(), "px" );
|
||||
return pixelsLengthToString( getOutlineThickness() );
|
||||
case PropertyId::TextStrokeColor:
|
||||
return getOutlineColor().toHexString();
|
||||
case PropertyId::SelectionColor:
|
||||
|
||||
@@ -128,9 +128,9 @@ void UIScrollBar::onAutoSize() {
|
||||
UISkin* tSkin = mSlider->getBackSlider()->getSkin();
|
||||
|
||||
if ( NULL != tSkin ) {
|
||||
size = tSkin->getSize();
|
||||
size = PixelDensity::pxToDp( tSkin->getPixelsSize() );
|
||||
|
||||
setMinSize( PixelDensity::pxToDp( size ) );
|
||||
setMinSize( size );
|
||||
|
||||
if ( mFlags & UI_AUTO_SIZE ) {
|
||||
if ( mSlider->isVertical() ) {
|
||||
@@ -145,9 +145,9 @@ void UIScrollBar::onAutoSize() {
|
||||
tSkin = mSlider->getSliderButton()->getSkin();
|
||||
|
||||
if ( NULL != tSkin ) {
|
||||
size = tSkin->getSize();
|
||||
size = PixelDensity::pxToDp( tSkin->getPixelsSize() );
|
||||
|
||||
setMinSize( PixelDensity::pxToDp( size ) );
|
||||
setMinSize( size );
|
||||
|
||||
if ( mFlags & UI_AUTO_SIZE ) {
|
||||
if ( mSlider->isVertical() ) {
|
||||
@@ -160,7 +160,7 @@ void UIScrollBar::onAutoSize() {
|
||||
}
|
||||
|
||||
if ( mWidthPolicy == SizePolicy::WrapContent || mHeightPolicy == SizePolicy::WrapContent ) {
|
||||
size = PixelDensity::dpToPx( mSlider->getSize() ) + mPaddingPx;
|
||||
size = mSlider->getPixelsSize() + mPaddingPx;
|
||||
|
||||
if ( mScrollBarStyle == TwoButtons ) {
|
||||
if ( mSlider->isVertical() ) {
|
||||
|
||||
@@ -262,7 +262,7 @@ bool UITextEdit::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
setHintFontStyle( attribute.asFontStyle() );
|
||||
break;
|
||||
case PropertyId::HintStrokeWidth:
|
||||
setHintOutlineThickness( PixelDensity::dpToPx( attribute.asDpDimension() ) );
|
||||
setHintOutlineThickness( lengthFromValue( attribute ) );
|
||||
break;
|
||||
case PropertyId::HintStrokeColor:
|
||||
setHintOutlineColor( attribute.asColor() );
|
||||
@@ -301,7 +301,7 @@ std::string UITextEdit::getPropertyString( const PropertyDefinition* propertyDef
|
||||
case PropertyId::HintFontStyle:
|
||||
return Text::styleFlagToString( getHintFontStyle() );
|
||||
case PropertyId::HintStrokeWidth:
|
||||
return String::fromFloat( getHintOutlineThickness(), "px" );
|
||||
return pixelsLengthToString( getHintOutlineThickness() );
|
||||
case PropertyId::HintStrokeColor:
|
||||
return getHintOutlineColor().toHexString();
|
||||
case PropertyId::HintDisplay:
|
||||
|
||||
@@ -602,7 +602,7 @@ std::string UITextInput::getPropertyString( const PropertyDefinition* propertyDe
|
||||
case PropertyId::HintFontStyle:
|
||||
return Text::styleFlagToString( getHintFontStyle() );
|
||||
case PropertyId::HintStrokeWidth:
|
||||
return String::fromFloat( getHintOutlineThickness(), "px" );
|
||||
return pixelsLengthToString( getHintOutlineThickness() );
|
||||
case PropertyId::HintStrokeColor:
|
||||
return getHintOutlineColor().toHexString();
|
||||
case PropertyId::HintDisplay:
|
||||
@@ -682,7 +682,7 @@ bool UITextInput::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
setHintFontStyle( attribute.asFontStyle() );
|
||||
break;
|
||||
case PropertyId::HintStrokeWidth:
|
||||
setHintOutlineThickness( PixelDensity::dpToPx( attribute.asDpDimension() ) );
|
||||
setHintOutlineThickness( lengthFromValue( attribute ) );
|
||||
break;
|
||||
case PropertyId::HintStrokeColor:
|
||||
setHintOutlineColor( attribute.asColor() );
|
||||
|
||||
@@ -226,7 +226,7 @@ std::string UITextSpan::getPropertyString( const PropertyDefinition* propertyDef
|
||||
return String::fromFloat( getFontShadowOffset().x ) + " " +
|
||||
String::fromFloat( getFontShadowOffset().y );
|
||||
case PropertyId::TextStrokeWidth:
|
||||
return String::fromFloat( getOutlineThickness(), "px" );
|
||||
return pixelsLengthToString( getOutlineThickness() );
|
||||
case PropertyId::TextStrokeColor:
|
||||
return getOutlineColor().toHexString();
|
||||
case PropertyId::TextDecoration:
|
||||
|
||||
@@ -917,7 +917,7 @@ std::string UITextView::getPropertyString( const PropertyDefinition* propertyDef
|
||||
case PropertyId::FontWeight:
|
||||
return Text::fontWeightToString( mFontStyleConfig.Weight );
|
||||
case PropertyId::TextStrokeWidth:
|
||||
return String::fromFloat( getOutlineThickness(), "px" );
|
||||
return pixelsLengthToString( getOutlineThickness() );
|
||||
case PropertyId::TextStrokeColor:
|
||||
return getOutlineColor().toHexString();
|
||||
case PropertyId::Wordwrap:
|
||||
|
||||
@@ -513,7 +513,7 @@ std::string UITooltip::getPropertyString( const PropertyDefinition* propertyDef,
|
||||
case PropertyId::FontWeight:
|
||||
return Text::fontWeightToString( mStyleConfig.Weight );
|
||||
case PropertyId::TextStrokeWidth:
|
||||
return String::fromFloat( getOutlineThickness(), "px" );
|
||||
return pixelsLengthToString( getOutlineThickness() );
|
||||
case PropertyId::TextStrokeColor:
|
||||
return getOutlineColor().toHexString();
|
||||
case PropertyId::Wordwrap:
|
||||
@@ -652,7 +652,7 @@ bool UITooltip::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
break;
|
||||
case PropertyId::TextStrokeWidth:
|
||||
if ( !mUsingCustomStyling )
|
||||
setOutlineThickness( PixelDensity::dpToPx( attribute.asDpDimension() ) );
|
||||
setOutlineThickness( lengthFromValue( attribute ) );
|
||||
break;
|
||||
case PropertyId::TextStrokeColor:
|
||||
if ( !mUsingCustomStyling )
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
#include "utest.hpp"
|
||||
|
||||
#include <eepp/graphics/font.hpp>
|
||||
#include <eepp/graphics/fonttruetype.hpp>
|
||||
#include <eepp/graphics/ninepatch.hpp>
|
||||
#include <eepp/graphics/pixeldensity.hpp>
|
||||
#include <eepp/graphics/texturefactory.hpp>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/sys.hpp>
|
||||
#include <eepp/ui/tools/uidiffview.hpp>
|
||||
#include <eepp/ui/tools/uifontpickerdialog.hpp>
|
||||
#include <eepp/ui/tools/uiimageviewer.hpp>
|
||||
#include <eepp/ui/tools/uimergeview.hpp>
|
||||
#include <eepp/ui/uiapplication.hpp>
|
||||
#include <eepp/ui/uicombobox.hpp>
|
||||
#include <eepp/ui/uidropdownlist.hpp>
|
||||
#include <eepp/ui/uilistbox.hpp>
|
||||
#include <eepp/ui/uiloader.hpp>
|
||||
#include <eepp/ui/uimessagebox.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uitextedit.hpp>
|
||||
#include <eepp/ui/uiviewpager.hpp>
|
||||
#include <eepp/ui/uiwidgettable.hpp>
|
||||
|
||||
using namespace EE;
|
||||
using namespace EE::Graphics;
|
||||
using namespace EE::System;
|
||||
using namespace EE::UI;
|
||||
using namespace EE::UI::Tools;
|
||||
|
||||
namespace {
|
||||
|
||||
struct RestoreDensity {
|
||||
Float previous{ PixelDensity::getPixelDensity() };
|
||||
|
||||
~RestoreDensity() { PixelDensity::setPixelDensity( previous ); }
|
||||
};
|
||||
|
||||
struct DensityApplication {
|
||||
RestoreDensity density;
|
||||
UIApplication app{
|
||||
WindowSettings( 1200, 1000, "Pixel Density Regression", WindowStyle::Default,
|
||||
WindowBackend::Default, 32, {}, 1, false, true ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 2.f ) };
|
||||
};
|
||||
|
||||
class PixelSetterWidget : public UIWidget {
|
||||
public:
|
||||
PixelSetterWidget() : UIWidget( "pixel-setter-test" ) {}
|
||||
|
||||
int paddingChanges{ 0 };
|
||||
int marginChanges{ 0 };
|
||||
|
||||
protected:
|
||||
void onPaddingChange() {
|
||||
++paddingChanges;
|
||||
UIWidget::onPaddingChange();
|
||||
}
|
||||
|
||||
void onMarginChange() {
|
||||
++marginChanges;
|
||||
UIWidget::onMarginChange();
|
||||
}
|
||||
};
|
||||
|
||||
class DensityListBox : public UIListBox {
|
||||
public:
|
||||
void recalculateRowHeight() {
|
||||
mRowHeight = 0;
|
||||
setRowHeight();
|
||||
}
|
||||
};
|
||||
|
||||
class DensityWidgetTable : public UIWidgetTable {
|
||||
public:
|
||||
void refreshSkinPadding() {
|
||||
autoPadding();
|
||||
containerResize();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
UTEST( PixelDensityRegression, PixelPaddingUpdatesAndRepeatedValues ) {
|
||||
DensityApplication fixture;
|
||||
auto* widget = eeNew( PixelSetterWidget, () );
|
||||
widget->setParent( fixture.app.getUI() );
|
||||
const Rectf logical( 4, 6, 8, 10 );
|
||||
widget->setPadding( logical );
|
||||
int changes = widget->paddingChanges;
|
||||
// A pixel value equal to the previous DP value must still update the widget.
|
||||
widget->setPaddingPixels( logical );
|
||||
EXPECT_TRUE( widget->getPixelsPadding() == logical );
|
||||
EXPECT_TRUE( widget->getPadding() == Rectf( 2, 3, 4, 5 ) );
|
||||
EXPECT_EQ( widget->paddingChanges, changes + 1 );
|
||||
widget->setPaddingPixels( logical );
|
||||
EXPECT_EQ( widget->paddingChanges, changes + 1 );
|
||||
|
||||
using Setter = UIWidget* ( UIWidget::* )( const Float& );
|
||||
Setter setters[] = { &UIWidget::setPaddingPixelsLeft, &UIWidget::setPaddingPixelsTop,
|
||||
&UIWidget::setPaddingPixelsRight, &UIWidget::setPaddingPixelsBottom };
|
||||
for ( auto setter : setters ) {
|
||||
widget->setPadding( Rectf( 4, 4, 4, 4 ) );
|
||||
changes = widget->paddingChanges;
|
||||
( widget->*setter )( 4 );
|
||||
EXPECT_EQ( widget->paddingChanges, changes + 1 );
|
||||
( widget->*setter )( 4 );
|
||||
EXPECT_EQ( widget->paddingChanges, changes + 1 );
|
||||
( widget->*setter )( 5 );
|
||||
const Rectf pixels = widget->getPixelsPadding();
|
||||
const Rectf dp = widget->getPadding();
|
||||
EXPECT_TRUE( dp == PixelDensity::pxToDp( pixels ).ceil() );
|
||||
EXPECT_EQ( pixels.Left + pixels.Top + pixels.Right + pixels.Bottom, 29.f );
|
||||
}
|
||||
}
|
||||
|
||||
UTEST( PixelDensityRegression, PixelMarginsUpdateAndRepeatedValues ) {
|
||||
DensityApplication fixture;
|
||||
auto* widget = eeNew( PixelSetterWidget, () );
|
||||
widget->setParent( fixture.app.getUI() );
|
||||
const Rectf logical( 4, 6, 8, 10 );
|
||||
widget->setLayoutMargin( logical );
|
||||
int changes = widget->marginChanges;
|
||||
widget->setLayoutPixelsMargin( logical );
|
||||
EXPECT_TRUE( widget->getLayoutPixelsMargin() == logical );
|
||||
EXPECT_TRUE( widget->getLayoutMargin() == Rectf( 2, 3, 4, 5 ) );
|
||||
EXPECT_EQ( widget->marginChanges, changes + 1 );
|
||||
widget->setLayoutPixelsMargin( logical );
|
||||
EXPECT_EQ( widget->marginChanges, changes + 1 );
|
||||
|
||||
using Setter = UIWidget* ( UIWidget::* )( const Float& );
|
||||
Setter setters[] = { &UIWidget::setLayoutPixelsMarginLeft, &UIWidget::setLayoutPixelsMarginTop,
|
||||
&UIWidget::setLayoutPixelsMarginRight,
|
||||
&UIWidget::setLayoutPixelsMarginBottom };
|
||||
for ( auto setter : setters ) {
|
||||
widget->setLayoutMargin( Rectf( 4, 4, 4, 4 ) );
|
||||
changes = widget->marginChanges;
|
||||
( widget->*setter )( 4 );
|
||||
EXPECT_EQ( widget->marginChanges, changes + 1 );
|
||||
( widget->*setter )( 4 );
|
||||
EXPECT_EQ( widget->marginChanges, changes + 1 );
|
||||
( widget->*setter )( 5 );
|
||||
const Rectf pixels = widget->getLayoutPixelsMargin();
|
||||
EXPECT_TRUE( widget->getLayoutMargin() == PixelDensity::pxToDp( pixels ).ceil() );
|
||||
EXPECT_EQ( pixels.Left + pixels.Top + pixels.Right + pixels.Bottom, 29.f );
|
||||
}
|
||||
}
|
||||
|
||||
UTEST( PixelDensityRegression, LoaderPagerAndMergeToolbarUseLogicalDefaults ) {
|
||||
DensityApplication fixture;
|
||||
auto* loader = UILoader::New();
|
||||
loader->setParent( fixture.app.getUI() );
|
||||
EXPECT_EQ( loader->getOutlineThickness(), 8.f );
|
||||
auto* viewer = UIImageViewer::New();
|
||||
viewer->setParent( fixture.app.getUI() );
|
||||
EXPECT_EQ( viewer->getLoader()->getOutlineThickness(), 6.f );
|
||||
EXPECT_EQ( viewer->getLoader()->getPixelsSize().x, 128.f );
|
||||
auto* pager = UIViewPager::New();
|
||||
pager->setParent( fixture.app.getUI() );
|
||||
EXPECT_EQ( pager->getDragResistance(), 8.f );
|
||||
auto* merge = UIMergeView::New();
|
||||
merge->setParent( fixture.app.getUI() );
|
||||
auto* button = merge->addToolbarAction( "dpi-test", "Test", {}, "", [] {} );
|
||||
EXPECT_EQ( button->getLayoutMargin().Right, 4.f );
|
||||
EXPECT_EQ( button->getLayoutPixelsMargin().Right, 8.f );
|
||||
}
|
||||
|
||||
UTEST( PixelDensityRegression, ListBoxAutomaticRowHeightAddsLogicalPadding ) {
|
||||
DensityApplication fixture;
|
||||
auto* list = eeNew( DensityListBox, () );
|
||||
list->setParent( fixture.app.getUI() );
|
||||
list->getReferenceItem()->setFontSize( 16 );
|
||||
list->recalculateRowHeight();
|
||||
const auto& style = list->getReferenceItem()->getFontStyleConfig();
|
||||
const Uint32 fontHeight =
|
||||
style.getFont() ? style.getFont()->getFontHeight( style.getFontCharacterSize() ) : 12;
|
||||
EXPECT_EQ( list->getRowHeight(), static_cast<Uint32>( fontHeight / 2.f + 4 ) );
|
||||
}
|
||||
|
||||
UTEST( PixelDensityRegression, MessageBoxControlsUseLogicalDimensions ) {
|
||||
DensityApplication fixture;
|
||||
auto* editBox = UIMessageBox::New( UIMessageBox::TEXT_EDIT, "Edit" );
|
||||
EXPECT_EQ( editBox->getTextEdit()->getSize().x, 400.f );
|
||||
EXPECT_EQ( editBox->getTextEdit()->getSize().y, 100.f );
|
||||
EXPECT_EQ( editBox->getTextEdit()->getPixelsSize().x, 800.f );
|
||||
auto* dropdownBox = UIMessageBox::New( UIMessageBox::DROPDOWNLIST, "Choose" );
|
||||
EXPECT_EQ( dropdownBox->getDropDownList()->getSize().x, 200.f );
|
||||
EXPECT_EQ( dropdownBox->getDropDownList()->getPixelsSize().x, 400.f );
|
||||
auto* comboBox = UIMessageBox::New( UIMessageBox::COMBOBOX, "Choose" );
|
||||
EXPECT_EQ( comboBox->getComboBox()->getSize().x, 200.f );
|
||||
EXPECT_EQ( comboBox->getComboBox()->getPixelsSize().x, 400.f );
|
||||
}
|
||||
|
||||
UTEST( PixelDensityRegression, FontPickerUsesLogicalSceneInset ) {
|
||||
DensityApplication fixture;
|
||||
fixture.app.getUI()->setSize( 600, 450 );
|
||||
const Sizef scene = fixture.app.getUI()->getSize();
|
||||
auto* dialog = UIFontPickerDialog::New();
|
||||
|
||||
EXPECT_EQ( dialog->getMinWindowSize().x, scene.x - 32.f );
|
||||
EXPECT_EQ( dialog->getMinWindowSize().y, scene.y - 32.f );
|
||||
}
|
||||
|
||||
UTEST( PixelDensityRegression, EditorLineNumberPaddingScalesExactlyOnce ) {
|
||||
DensityApplication fixture;
|
||||
auto* editor = UICodeEditor::New();
|
||||
editor->setParent( fixture.app.getUI() );
|
||||
editor->setLineNumberPadding( 3, 5 );
|
||||
EXPECT_EQ( editor->getLineNumberPaddingLeft(), 6.f );
|
||||
EXPECT_EQ( editor->getLineNumberPaddingRight(), 10.f );
|
||||
editor->setLineNumberPadding( 3, 5 );
|
||||
EXPECT_EQ( editor->getLineNumberPaddingLeft(), 6.f );
|
||||
EXPECT_EQ( editor->getLineNumberPaddingRight(), 10.f );
|
||||
EXPECT_EQ( editor->getLineNumberWidth(),
|
||||
eeceil( editor->getLineNumberDigits() * editor->getGlyphWidth() + 16.f ) );
|
||||
}
|
||||
|
||||
UTEST( PixelDensityRegression, DiffGuttersUsePhysicalGlyphWidthAndGrowWithLineNumbers ) {
|
||||
DensityApplication fixture;
|
||||
auto* diff = UIDiffView::New();
|
||||
diff->setParent( fixture.app.getUI() );
|
||||
for ( bool large : { false, true, false } ) {
|
||||
diff->loadFromPatch( large
|
||||
? "--- a/test\n+++ b/test\n@@ -100000,1 +100000,1 @@\n-old\n+new\n"
|
||||
: "--- a/test\n+++ b/test\n@@ -1,1 +1,1 @@\n-old\n+new\n" );
|
||||
const int digits = large ? 6 : 5;
|
||||
EXPECT_EQ( diff->getEditor()->getPluginsGutterSpace(),
|
||||
diff->getEditor()->getGlyphWidth() * ( digits * 2 + 1 ) );
|
||||
diff->setViewMode( UIDiffView::ViewMode::SideBySide );
|
||||
for ( auto* editor : { diff->getLeftEditor(), diff->getRightEditor() } ) {
|
||||
EXPECT_EQ( editor->getPluginsGutterSpace(), editor->getGlyphWidth() * digits );
|
||||
editor->setFontSize( 18 );
|
||||
EXPECT_EQ( editor->getPluginsGutterSpace(), editor->getGlyphWidth() * digits );
|
||||
}
|
||||
diff->setViewMode( UIDiffView::ViewMode::Unified );
|
||||
}
|
||||
}
|
||||
|
||||
UTEST( PixelDensityRegression, TableContainerPaddingAndOriginStayLogical ) {
|
||||
DensityApplication fixture;
|
||||
auto* table = eeNew( DensityWidgetTable, () );
|
||||
table->setParent( fixture.app.getUI() );
|
||||
table->unsetFlags( UI_AUTO_PADDING );
|
||||
table->setSize( 200, 100 );
|
||||
table->setPadding( Rectf( 3, 4, 5, 6 ) );
|
||||
EXPECT_TRUE( table->getContainerPadding() == Rectf( 3, 4, 5, 6 ) );
|
||||
EXPECT_EQ( table->getContainer()->getPosition().x, 3.f );
|
||||
EXPECT_EQ( table->getContainer()->getPosition().y, 4.f );
|
||||
EXPECT_EQ( table->getContainer()->getPixelsPosition().x, 6.f );
|
||||
EXPECT_EQ( table->getContainer()->getPixelsPosition().y, 8.f );
|
||||
unsigned char pixels[20 * 30 * 4] = {};
|
||||
auto texture = TextureFactory::instance()->loadFromPixels( pixels, 20, 30, 4 );
|
||||
auto skin = UISkin::New( "table-dpi-test" );
|
||||
skin->setStateDrawable( UIState::StateFlagNormal, NinePatch::New( texture, 2, 4, 6, 8, 2.f ) );
|
||||
table->setSkin( skin.get() );
|
||||
table->setFlags( UI_AUTO_PADDING );
|
||||
table->refreshSkinPadding();
|
||||
const Rectf expected = PixelDensity::pxToDp( skin->getBorderSize() ) + table->getPadding();
|
||||
EXPECT_TRUE( table->getContainerPadding() == expected );
|
||||
EXPECT_EQ( table->getContainer()->getPosition().x, expected.Left );
|
||||
EXPECT_EQ( table->getContainer()->getPosition().y, expected.Top );
|
||||
}
|
||||
@@ -13,15 +13,20 @@
|
||||
#include <eepp/ui/css/stylesheetspecification.hpp>
|
||||
#include <eepp/ui/tools/htmlformatter.hpp>
|
||||
#include <eepp/ui/uiapplication.hpp>
|
||||
#include <eepp/ui/uicodeeditor.hpp>
|
||||
#include <eepp/ui/uiconsole.hpp>
|
||||
#include <eepp/ui/uidropdownlist.hpp>
|
||||
#include <eepp/ui/uinodedrawable.hpp>
|
||||
#include <eepp/ui/uirichtext.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uistyle.hpp>
|
||||
#include <eepp/ui/uitabwidget.hpp>
|
||||
#include <eepp/ui/uitextedit.hpp>
|
||||
#include <eepp/ui/uitextinput.hpp>
|
||||
#include <eepp/ui/uitextspan.hpp>
|
||||
#include <eepp/ui/uitextview.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
#include <eepp/ui/uitooltip.hpp>
|
||||
#include <eepp/ui/uiwidget.hpp>
|
||||
#include <eepp/window/input.hpp>
|
||||
|
||||
@@ -2101,3 +2106,47 @@ UTEST( CSSFunctions, NotAFunctionReturnsDefault ) {
|
||||
EXPECT_EQ( StyleSheetLength::Unit::Px, len.getUnit() );
|
||||
EXPECT_EQ( 0, len.getValue() );
|
||||
}
|
||||
|
||||
UTEST( CSSUnits, StrokeRoundTripAndTransitionAtHighDensity ) {
|
||||
UIApplication app( WindowSettings( 800, 600, "eepp - CSS Units Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32, {}, 1, false, true ),
|
||||
UIApplication::Settings( System::Sys::getProcessPath() + ".." +
|
||||
System::FileSystem::getOSSlash(),
|
||||
1 ) );
|
||||
const Float previousDensity = PixelDensity::getPixelDensity();
|
||||
PixelDensity::setPixelDensity( 2.f );
|
||||
auto* definition =
|
||||
StyleSheetSpecification::instance()->getProperty( PropertyId::TextStrokeWidth );
|
||||
UIWidget* widgets[] = { UITextView::New(), UITextSpan::New(), UIRichText::New(),
|
||||
UITooltip::New(), UIConsole::New(), UICodeEditor::New(),
|
||||
UITextInput::New(), UITextEdit::New() };
|
||||
for ( auto* widget : widgets ) {
|
||||
widget->setParent( app.getUI() );
|
||||
definition = StyleSheetSpecification::instance()->getProperty(
|
||||
widget->isType( UI_TYPE_TEXTINPUT ) || widget->isType( UI_TYPE_TEXTEDIT )
|
||||
? PropertyId::HintStrokeWidth
|
||||
: PropertyId::TextStrokeWidth );
|
||||
for ( bool html : { false, true } ) {
|
||||
if ( html )
|
||||
widget->setFlags( UI_HTML_ELEMENT );
|
||||
else
|
||||
widget->unsetFlags( UI_HTML_ELEMENT );
|
||||
for ( const char* source : { "2px", "2dp" } ) {
|
||||
widget->applyProperty( StyleSheetProperty( definition, source ) );
|
||||
const Float expected = html || std::string_view( source ) == "2dp" ? 4.f : 2.f;
|
||||
std::string serialized = widget->getPropertyString( definition );
|
||||
EXPECT_EQ( widget->lengthFromValue( serialized, PropertyRelativeTarget::None, 0 ),
|
||||
expected );
|
||||
widget->applyProperty( StyleSheetProperty( definition, serialized ) );
|
||||
EXPECT_TRUE( widget->getPropertyString( definition ) == serialized );
|
||||
StyleSheetPropertyAnimation::tweenProperty( widget, 0.5f, definition, serialized,
|
||||
"6dp", Ease::Interpolation::Linear, {},
|
||||
0, false );
|
||||
EXPECT_EQ( widget->lengthFromValue( widget->getPropertyString( definition ),
|
||||
PropertyRelativeTarget::None, 0 ),
|
||||
( expected + 12.f ) / 2.f );
|
||||
}
|
||||
}
|
||||
}
|
||||
PixelDensity::setPixelDensity( previousDensity );
|
||||
}
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
#include "utest.h"
|
||||
|
||||
#include <eepp/graphics/ninepatch.hpp>
|
||||
#include <eepp/graphics/pixeldensity.hpp>
|
||||
#include <eepp/graphics/rectangledrawable.hpp>
|
||||
#include <eepp/graphics/texturefactory.hpp>
|
||||
#include <eepp/graphics/textureregion.hpp>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/sys.hpp>
|
||||
#include <eepp/ui/uiapplication.hpp>
|
||||
#include <eepp/ui/uicheckbox.hpp>
|
||||
#include <eepp/ui/uimenu.hpp>
|
||||
#include <eepp/ui/uimenubar.hpp>
|
||||
#include <eepp/ui/uipopupmenu.hpp>
|
||||
#include <eepp/ui/uiradiobutton.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uiscrollbar.hpp>
|
||||
|
||||
using namespace EE;
|
||||
using namespace EE::System;
|
||||
using namespace EE::Graphics;
|
||||
using namespace EE::UI;
|
||||
using namespace EE::Window;
|
||||
|
||||
@@ -85,3 +95,125 @@ UTEST( UIMenu, SemanticActivationLifecycleAndRoles ) {
|
||||
subMenu->showSubMenu();
|
||||
EXPECT_EQ( subMenuShowStep, 2 );
|
||||
}
|
||||
|
||||
UTEST( UIMenu, NestedMenuBarAndSubmenuAtHighDensity ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - Menu DPI Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32, {}, 1, false, true ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
const Float previousDensity = PixelDensity::getPixelDensity();
|
||||
PixelDensity::setPixelDensity( 2.f );
|
||||
auto* container = UIWidget::New();
|
||||
container->setParent( app.getUI() );
|
||||
container->setPosition( 30, 40 );
|
||||
auto* bar = UIMenuBar::New();
|
||||
bar->setParent( container );
|
||||
bar->setPosition( 10, 15 );
|
||||
bar->setSize( 300, 24 );
|
||||
auto* popup = UIPopUpMenu::New();
|
||||
popup->add( "Item" );
|
||||
bar->addMenuButton( "File", popup );
|
||||
auto* button = bar->getButton( 0 );
|
||||
Vector2f expected( 0, button->getSize().getHeight() );
|
||||
button->nodeToWorld( expected );
|
||||
bar->showMenu( 0 );
|
||||
Vector2f actual( 0, 0 );
|
||||
popup->nodeToWorld( actual );
|
||||
EXPECT_EQ( actual.x, expected.x );
|
||||
EXPECT_EQ( actual.y, expected.y );
|
||||
popup->setPadding( Rectf( 0, 0, 3, 0 ) );
|
||||
auto* child = UIPopUpMenu::New();
|
||||
child->add( "Child" );
|
||||
auto* item = popup->addSubMenu( "Submenu", {}, child );
|
||||
// The placement policy aligns the submenu to the parent menu's right edge.
|
||||
Vector2f subExpected( popup->getSize().getWidth(), 0 );
|
||||
popup->nodeToWorld( subExpected );
|
||||
item->showSubMenu();
|
||||
Vector2f subActual( 0, 0 );
|
||||
child->nodeToWorld( subActual );
|
||||
EXPECT_EQ( subActual.x, subExpected.x );
|
||||
PixelDensity::setPixelDensity( previousDensity );
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class DensityTestCheckBox : public UICheckBox {
|
||||
public:
|
||||
using UICheckBox::onAutoSize;
|
||||
};
|
||||
|
||||
class DensityTestRadioButton : public UIRadioButton {
|
||||
public:
|
||||
using UIRadioButton::onAutoSize;
|
||||
};
|
||||
|
||||
class DensityTestScrollBar : public UIScrollBar {
|
||||
public:
|
||||
DensityTestScrollBar( UIOrientation orientation ) : UIScrollBar( "scrollbar", orientation ) {}
|
||||
|
||||
using UIScrollBar::onAutoSize;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
UTEST( UIUnits, AutoSizeAtHighDensity ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - Sizing DPI Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32, {}, 1, false, true ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ) );
|
||||
const Float previousDensity = PixelDensity::getPixelDensity();
|
||||
PixelDensity::setPixelDensity( 2.f );
|
||||
auto* checkbox = eeNew( DensityTestCheckBox, () );
|
||||
checkbox->setParent( app.getUI() );
|
||||
checkbox->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
|
||||
checkbox->setFlags( UI_AUTO_SIZE );
|
||||
checkbox->setTextSeparation( 8 );
|
||||
checkbox->setPadding( Rectf( 3, 4, 5, 6 ) );
|
||||
checkbox->getCheckedButton()->setSize( 10, 12 );
|
||||
checkbox->setSize( 0, 0 );
|
||||
checkbox->onAutoSize();
|
||||
EXPECT_EQ( checkbox->getPixelsSize().x, (int)checkbox->getTextWidth() + 20 + 16 +
|
||||
checkbox->getPixelsPadding().Left +
|
||||
checkbox->getPixelsPadding().Right );
|
||||
auto* radio = eeNew( DensityTestRadioButton, () );
|
||||
radio->setParent( app.getUI() );
|
||||
radio->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed );
|
||||
radio->setFlags( UI_AUTO_SIZE );
|
||||
radio->setPadding( Rectf( 3, 4, 5, 6 ) );
|
||||
radio->getActiveButton()->setSize( 10, 12 );
|
||||
radio->setMinSize( Sizef::Zero );
|
||||
radio->setSize( 0, 0 );
|
||||
radio->onAutoSize();
|
||||
EXPECT_EQ( radio->getSize().y, 12 + radio->getPadding().Top + radio->getPadding().Bottom );
|
||||
|
||||
auto region = TextureRegion::New();
|
||||
region->setDestSize( Sizef( 20, 30 ) );
|
||||
region->setPixelDensity( 2.f );
|
||||
unsigned char texturePixels[20 * 30 * 4] = {};
|
||||
auto texture = TextureFactory::instance()->loadFromPixels( texturePixels, 20, 30, 4 );
|
||||
auto ninePatch = NinePatch::New( texture, 2, 2, 2, 2, 2.f );
|
||||
DrawablePtr drawables[] = { DrawablePtr( RectangleDrawable::New( {}, Sizef( 20, 30 ) ) ),
|
||||
region, ninePatch };
|
||||
for ( auto& drawable : drawables ) {
|
||||
auto skin = UISkin::New( "dpi-test" );
|
||||
skin->setStateDrawable( UIState::StateFlagNormal, drawable );
|
||||
for ( auto orientation : { UIOrientation::Vertical, UIOrientation::Horizontal } ) {
|
||||
auto* scrollbar = eeNew( DensityTestScrollBar, ( orientation ) );
|
||||
scrollbar->setParent( app.getUI() );
|
||||
scrollbar->setSize( 100, 100 );
|
||||
scrollbar->getSlider()->getBackSlider()->setSkin( skin.get() );
|
||||
scrollbar->onAutoSize();
|
||||
const Sizef pixels = skin->getPixelsSize();
|
||||
EXPECT_EQ( scrollbar->getCurMinSize().x, pixels.x / 2.f );
|
||||
EXPECT_EQ( scrollbar->getCurMinSize().y, pixels.y / 2.f );
|
||||
if ( orientation == UIOrientation::Vertical ) {
|
||||
EXPECT_EQ( scrollbar->getPixelsSize().x, pixels.x );
|
||||
EXPECT_EQ( scrollbar->getSlider()->getPixelsSize().x, pixels.x );
|
||||
} else {
|
||||
EXPECT_EQ( scrollbar->getPixelsSize().y, pixels.y );
|
||||
EXPECT_EQ( scrollbar->getSlider()->getPixelsSize().y, pixels.y );
|
||||
}
|
||||
}
|
||||
}
|
||||
PixelDensity::setPixelDensity( previousDensity );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user