mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-09-22 04:58:34 +03:00
perf(ui): store Variant values inline
Replace Variant's heap-owned payloads with manually managed inline storage while preserving its return-by-value API. - inline std::string, String, DrawablePtr, Vector2f, and Rectf - add move construction and capacity-preserving assignment - add explicit borrowed std::string support and string views - avoid redundant conversions in table and tree hot paths - borrow stable process fields in eproc - eliminate per-frame eproc class allocation churn - focus the eproc search input on startup - add comprehensive Variant lifecycle and ownership tests
This commit is contained in:
@@ -762,6 +762,11 @@ class EE_API String {
|
||||
**/
|
||||
String( const String& str );
|
||||
|
||||
/** @brief Move constructor
|
||||
** @param str Instance to move
|
||||
**/
|
||||
String( String&& str ) noexcept;
|
||||
|
||||
/** @brief Copy constructor
|
||||
** @param str Instance to copy
|
||||
**/
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#ifndef EE_UI_MODEL_VARIANT_HPP
|
||||
#define EE_UI_MODEL_VARIANT_HPP
|
||||
|
||||
#include <cstring>
|
||||
#include <eepp/core/core.hpp>
|
||||
#include <eepp/graphics/drawable.hpp>
|
||||
#include <eepp/graphics/drawableresource.hpp>
|
||||
#include <eepp/math/rect.hpp>
|
||||
#include <eepp/ui/uiicon.hpp>
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
using namespace EE::Graphics;
|
||||
using namespace EE::Math;
|
||||
@@ -31,56 +35,407 @@ class EE_API Variant {
|
||||
Vector2f,
|
||||
Rectf,
|
||||
cstr,
|
||||
StringPtr
|
||||
StringPtr,
|
||||
StdStringPtr
|
||||
};
|
||||
Variant() : mType( Type::Invalid ) {}
|
||||
explicit Variant( const std::string& string ) : mType( Type::StdString ) {
|
||||
mValue.asStdString = eeNew( std::string, ( string ) );
|
||||
|
||||
Variant() = default;
|
||||
|
||||
explicit Variant( const std::string& string ) {
|
||||
new ( &mValue.asStdString ) std::string( string );
|
||||
mType = Type::StdString;
|
||||
}
|
||||
explicit Variant( const String& string ) : mType( Type::String ) {
|
||||
mValue.asString = eeNew( String, ( string ) );
|
||||
|
||||
explicit Variant( std::string&& string ) {
|
||||
new ( &mValue.asStdString ) std::string( std::move( string ) );
|
||||
mType = Type::StdString;
|
||||
}
|
||||
|
||||
explicit Variant( const std::string* string ) : mType( Type::StdStringPtr ) {
|
||||
mValue.asStdStringPtr = string;
|
||||
}
|
||||
|
||||
explicit Variant( const String& string ) {
|
||||
new ( &mValue.asString ) String( string );
|
||||
mType = Type::String;
|
||||
}
|
||||
|
||||
explicit Variant( String&& string ) {
|
||||
new ( &mValue.asString ) String( std::move( string ) );
|
||||
mType = Type::String;
|
||||
}
|
||||
|
||||
explicit Variant( const String* string ) : mType( Type::StringPtr ) {
|
||||
mValue.asStringPtr = string;
|
||||
}
|
||||
Variant( DrawablePtr drawable ) : mDrawable( std::move( drawable ) ), mType( Type::Drawable ) {}
|
||||
Variant( UIIcon* icon ) : mType( Type::Icon ) { mValue.asIcon = icon; }
|
||||
Variant( const Vector2f& v ) : mType( Type::Vector2f ) {
|
||||
mValue.asVector2f = eeNew( Vector2f, ( v ) );
|
||||
}
|
||||
Variant( void* data ) : mType( Type::DataPtr ) { mValue.asDataPtr = data; }
|
||||
Variant( const Rectf& r ) : mType( Type::Rectf ) { mValue.asRectf = eeNew( Rectf, ( r ) ); }
|
||||
Variant( bool val ) : mType( Type::Bool ) { mValue.asBool = val; }
|
||||
Variant( const Float& val ) : mType( Type::Float ) { mValue.asFloat = val; }
|
||||
Variant( const int& val ) : mType( Type::Int ) { mValue.asInt = val; }
|
||||
Variant( const unsigned int& val ) : mType( Type::Uint ) { mValue.asUint = val; }
|
||||
Variant( const Int64& val ) : mType( Type::Int64 ) { mValue.asInt64 = val; }
|
||||
Variant( const Uint64& val ) : mType( Type::Uint64 ) { mValue.asUint64 = val; }
|
||||
explicit Variant( const char* data ) : mType( Type::cstr ) { mValue.asCStr = data; }
|
||||
~Variant() { reset(); }
|
||||
|
||||
Variant( const Variant& other ) : mType( Type::Invalid ) {
|
||||
switch ( other.mType ) {
|
||||
Variant( DrawablePtr drawable ) {
|
||||
new ( &mValue.asDrawable ) DrawablePtr( std::move( drawable ) );
|
||||
mType = Type::Drawable;
|
||||
}
|
||||
|
||||
Variant( UIIcon* icon ) : mType( Type::Icon ) { mValue.asIcon = icon; }
|
||||
|
||||
Variant( const Vector2f& v ) {
|
||||
new ( &mValue.asVector2f ) Vector2f( v );
|
||||
mType = Type::Vector2f;
|
||||
}
|
||||
|
||||
Variant( void* data ) : mType( Type::DataPtr ) { mValue.asDataPtr = data; }
|
||||
|
||||
Variant( const Rectf& r ) {
|
||||
new ( &mValue.asRectf ) Rectf( r );
|
||||
mType = Type::Rectf;
|
||||
}
|
||||
|
||||
Variant( bool val ) : mType( Type::Bool ) { mValue.asBool = val; }
|
||||
|
||||
Variant( const Float& val ) : mType( Type::Float ) { mValue.asFloat = val; }
|
||||
|
||||
Variant( const int& val ) : mType( Type::Int ) { mValue.asInt = val; }
|
||||
|
||||
Variant( const unsigned int& val ) : mType( Type::Uint ) { mValue.asUint = val; }
|
||||
|
||||
Variant( const Int64& val ) : mType( Type::Int64 ) { mValue.asInt64 = val; }
|
||||
|
||||
Variant( const Uint64& val ) : mType( Type::Uint64 ) { mValue.asUint64 = val; }
|
||||
|
||||
explicit Variant( const char* data ) : mType( Type::cstr ) { mValue.asCStr = data; }
|
||||
|
||||
~Variant() { destroy(); }
|
||||
|
||||
Variant( const Variant& other ) { copyConstruct( other ); }
|
||||
|
||||
Variant( Variant&& other ) { moveConstruct( std::move( other ) ); }
|
||||
|
||||
Variant& operator=( const Variant& other ) {
|
||||
if ( this == &other )
|
||||
return *this;
|
||||
if ( mType == other.mType ) {
|
||||
copyAssignSameType( other );
|
||||
} else {
|
||||
destroy();
|
||||
copyConstruct( other );
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Variant& operator=( Variant&& other ) {
|
||||
if ( this == &other )
|
||||
return *this;
|
||||
if ( mType == other.mType ) {
|
||||
moveAssignSameType( std::move( other ) );
|
||||
} else {
|
||||
destroy();
|
||||
moveConstruct( std::move( other ) );
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
static Variant fromRef( const std::string& string ) { return Variant( &string ); }
|
||||
|
||||
const std::string& asStdString() const { return mValue.asStdString; }
|
||||
|
||||
const std::string& asStdStringPtr() const { return *mValue.asStdStringPtr; }
|
||||
|
||||
const String& asString() const { return mValue.asString; }
|
||||
|
||||
const String& asStringPtr() const { return *mValue.asStringPtr; }
|
||||
|
||||
const DrawablePtr& asDrawable() const { return mValue.asDrawable; }
|
||||
|
||||
const bool& asBool() const { return mValue.asBool; }
|
||||
|
||||
const Float& asFloat() const { return mValue.asFloat; }
|
||||
|
||||
const int& asInt() const { return mValue.asInt; }
|
||||
|
||||
const unsigned int& asUint() const { return mValue.asUint; }
|
||||
|
||||
const Int64& asInt64() const { return mValue.asInt64; }
|
||||
|
||||
const Uint64& asUint64() const { return mValue.asUint64; }
|
||||
|
||||
const Vector2f& asVector2f() const { return mValue.asVector2f; }
|
||||
|
||||
const Rectf& asRectf() const { return mValue.asRectf; }
|
||||
|
||||
const char* asCStr() const { return mValue.asCStr; }
|
||||
|
||||
UIIcon* asIcon() const { return mValue.asIcon; }
|
||||
|
||||
void* asDataPtr() const { return mValue.asDataPtr; }
|
||||
|
||||
Type getType() const { return mType; }
|
||||
|
||||
bool is( const Type& type ) const { return type == mType; }
|
||||
|
||||
bool isString() const {
|
||||
return mType == Type::StdString || mType == Type::StdStringPtr || mType == Type::cstr ||
|
||||
mType == Type::String || mType == Type::StringPtr;
|
||||
}
|
||||
|
||||
bool isStdStringLike() const {
|
||||
return mType == Type::StdString || mType == Type::StdStringPtr || mType == Type::cstr;
|
||||
}
|
||||
|
||||
std::string_view asStdStringView() const {
|
||||
switch ( mType ) {
|
||||
case Type::StdString:
|
||||
mValue.asStdString = eeNew( std::string, ( *other.mValue.asStdString ) );
|
||||
return asStdString();
|
||||
case Type::StdStringPtr:
|
||||
return asStdStringPtr();
|
||||
case Type::cstr:
|
||||
return asCStr();
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void reset() { destroy(); }
|
||||
|
||||
bool isValid() const { return mType != Type::Invalid; }
|
||||
|
||||
std::string toString() const {
|
||||
switch ( mType ) {
|
||||
case Type::Bool:
|
||||
return asBool() ? "true" : "false";
|
||||
case Type::Int:
|
||||
return String::toString( asInt() );
|
||||
case Type::Uint:
|
||||
return String::toString( asUint() );
|
||||
case Type::Int64:
|
||||
return String::toString( asInt64() );
|
||||
case Type::Uint64:
|
||||
return String::toString( asUint64() );
|
||||
case Type::Float:
|
||||
return String::toString( asFloat() );
|
||||
case Type::StdString:
|
||||
return asStdString();
|
||||
case Type::StdStringPtr:
|
||||
return asStdStringPtr();
|
||||
case Type::String:
|
||||
return asString();
|
||||
case Type::StringPtr:
|
||||
return asStringPtr();
|
||||
case Type::Drawable:
|
||||
return asDrawable()->isDrawableResource()
|
||||
? static_cast<DrawableResource*>( asDrawable().get() )->getName()
|
||||
: "Drawable";
|
||||
case Type::Icon:
|
||||
return asIcon()->getName();
|
||||
case Type::DataPtr:
|
||||
return String::format( "%p", asDataPtr() );
|
||||
case Type::Vector2f:
|
||||
return String::format( "%.2f-%.2f", asVector2f().x, asVector2f().y );
|
||||
case Type::Rectf:
|
||||
return String::format( "%.2f-%.2f-%.2f-%.2f", asRectf().Top, asRectf().Right,
|
||||
asRectf().Bottom, asRectf().Left );
|
||||
case Type::cstr:
|
||||
return asCStr();
|
||||
case Type::Invalid:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool operator<( const Variant& other ) const {
|
||||
if ( mType != other.mType ) {
|
||||
if ( isStdStringLike() && other.isStdStringLike() )
|
||||
return asStdStringView() < other.asStdStringView();
|
||||
return toString() < other.toString();
|
||||
}
|
||||
switch ( mType ) {
|
||||
case Type::Bool:
|
||||
return asBool() < other.asBool();
|
||||
case Type::Int:
|
||||
return asInt() < other.asInt();
|
||||
case Type::Uint:
|
||||
return asUint() < other.asUint();
|
||||
case Type::Int64:
|
||||
return asInt64() < other.asInt64();
|
||||
case Type::Uint64:
|
||||
return asUint64() < other.asUint64();
|
||||
case Type::Float:
|
||||
return asFloat() < other.asFloat();
|
||||
case Type::StdString:
|
||||
return asStdString() < other.asStdString();
|
||||
case Type::StdStringPtr:
|
||||
return asStdStringPtr() < other.asStdStringPtr();
|
||||
case Type::String:
|
||||
return asString() < other.asString();
|
||||
case Type::StringPtr:
|
||||
return asStringPtr() < other.asStringPtr();
|
||||
case Type::Drawable:
|
||||
return asDrawable() < other.asDrawable();
|
||||
case Type::Icon:
|
||||
return asIcon() < other.asIcon();
|
||||
case Type::DataPtr:
|
||||
return asDataPtr() < other.asDataPtr();
|
||||
case Type::Vector2f:
|
||||
return asVector2f() < other.asVector2f();
|
||||
case Type::Rectf:
|
||||
return asRectf().getSize() < other.asRectf().getSize();
|
||||
case Type::cstr:
|
||||
return std::strcmp( asCStr(), other.asCStr() ) < 0;
|
||||
case Type::Invalid:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==( const Variant& other ) const {
|
||||
if ( mType != other.mType ) {
|
||||
if ( isStdStringLike() && other.isStdStringLike() )
|
||||
return asStdStringView() == other.asStdStringView();
|
||||
return toString() == other.toString();
|
||||
}
|
||||
switch ( mType ) {
|
||||
case Type::Bool:
|
||||
return asBool() == other.asBool();
|
||||
case Type::Int:
|
||||
return asInt() == other.asInt();
|
||||
case Type::Uint:
|
||||
return asUint() == other.asUint();
|
||||
case Type::Int64:
|
||||
return asInt64() == other.asInt64();
|
||||
case Type::Uint64:
|
||||
return asUint64() == other.asUint64();
|
||||
case Type::Float:
|
||||
return asFloat() == other.asFloat();
|
||||
case Type::StdString:
|
||||
return asStdString() == other.asStdString();
|
||||
case Type::StdStringPtr:
|
||||
return asStdStringPtr() == other.asStdStringPtr();
|
||||
case Type::String:
|
||||
return asString() == other.asString();
|
||||
case Type::StringPtr:
|
||||
return asStringPtr() == other.asStringPtr();
|
||||
case Type::Drawable:
|
||||
return asDrawable() == other.asDrawable();
|
||||
case Type::Icon:
|
||||
return asIcon() == other.asIcon();
|
||||
case Type::DataPtr:
|
||||
return asDataPtr() == other.asDataPtr();
|
||||
case Type::Vector2f:
|
||||
return asVector2f() == other.asVector2f();
|
||||
case Type::Rectf:
|
||||
return asRectf().getSize() == other.asRectf().getSize();
|
||||
case Type::cstr:
|
||||
return std::strcmp( asCStr(), other.asCStr() ) == 0;
|
||||
case Type::Invalid:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
switch ( mType ) {
|
||||
case Type::Bool:
|
||||
return 1;
|
||||
case Type::Int:
|
||||
return sizeof( int );
|
||||
case Type::Uint:
|
||||
return sizeof( unsigned int );
|
||||
case Type::Int64:
|
||||
return sizeof( Int64 );
|
||||
case Type::Uint64:
|
||||
return sizeof( Uint64 );
|
||||
case Type::Float:
|
||||
return sizeof( Float );
|
||||
case Type::StdString:
|
||||
return asStdString().size();
|
||||
case Type::StdStringPtr:
|
||||
return asStdStringPtr().size();
|
||||
case Type::String:
|
||||
return asString().size();
|
||||
case Type::StringPtr:
|
||||
return asStringPtr().size();
|
||||
case Type::Drawable:
|
||||
return sizeof( mValue.asDrawable );
|
||||
case Type::Icon:
|
||||
return asIcon()->getName().size();
|
||||
case Type::DataPtr:
|
||||
return sizeof( mValue.asDataPtr );
|
||||
case Type::Vector2f:
|
||||
return sizeof( mValue.asVector2f );
|
||||
case Type::Rectf:
|
||||
return sizeof( mValue.asRectf );
|
||||
case Type::cstr:
|
||||
return std::strlen( asCStr() );
|
||||
case Type::Invalid:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
union Storage {
|
||||
void* asDataPtr;
|
||||
UIIcon* asIcon;
|
||||
std::string asStdString;
|
||||
const std::string* asStdStringPtr;
|
||||
String asString;
|
||||
const String* asStringPtr;
|
||||
DrawablePtr asDrawable;
|
||||
bool asBool;
|
||||
Float asFloat;
|
||||
int asInt;
|
||||
unsigned int asUint;
|
||||
Int64 asInt64;
|
||||
Uint64 asUint64;
|
||||
Vector2f asVector2f;
|
||||
Rectf asRectf;
|
||||
const char* asCStr;
|
||||
|
||||
Storage() {}
|
||||
~Storage() {}
|
||||
} mValue;
|
||||
|
||||
Type mType{ Type::Invalid };
|
||||
|
||||
void destroy() {
|
||||
switch ( mType ) {
|
||||
case Type::StdString:
|
||||
mValue.asStdString.~basic_string();
|
||||
break;
|
||||
case Type::String:
|
||||
mValue.asString = eeNew( String, ( *other.mValue.asString ) );
|
||||
mValue.asString.~String();
|
||||
break;
|
||||
case Type::Drawable:
|
||||
mValue.asDrawable.~DrawablePtr();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mType = Type::Invalid;
|
||||
}
|
||||
|
||||
void copyConstruct( const Variant& other ) {
|
||||
switch ( other.mType ) {
|
||||
case Type::StdString:
|
||||
new ( &mValue.asStdString ) std::string( other.mValue.asStdString );
|
||||
break;
|
||||
case Type::StdStringPtr:
|
||||
mValue.asStdStringPtr = other.mValue.asStdStringPtr;
|
||||
break;
|
||||
case Type::String:
|
||||
new ( &mValue.asString ) String( other.mValue.asString );
|
||||
break;
|
||||
case Type::StringPtr:
|
||||
mValue.asStringPtr = other.mValue.asStringPtr;
|
||||
break;
|
||||
case Type::Drawable:
|
||||
mDrawable = other.mDrawable;
|
||||
new ( &mValue.asDrawable ) DrawablePtr( other.mValue.asDrawable );
|
||||
break;
|
||||
case Type::Icon:
|
||||
mValue.asIcon = other.mValue.asIcon;
|
||||
break;
|
||||
case Type::Vector2f:
|
||||
mValue.asVector2f = eeNew( Vector2f, ( *other.mValue.asVector2f ) );
|
||||
new ( &mValue.asVector2f ) Vector2f( other.mValue.asVector2f );
|
||||
break;
|
||||
case Type::Rectf:
|
||||
mValue.asRectf = eeNew( Rectf, ( *other.mValue.asRectf ) );
|
||||
new ( &mValue.asRectf ) Rectf( other.mValue.asRectf );
|
||||
break;
|
||||
case Type::Bool:
|
||||
mValue.asBool = other.mValue.asBool;
|
||||
@@ -112,254 +467,101 @@ class EE_API Variant {
|
||||
mType = other.mType;
|
||||
}
|
||||
|
||||
Variant( Variant&& other ) noexcept :
|
||||
mValue( other.mValue ), mDrawable( std::move( other.mDrawable ) ), mType( other.mType ) {
|
||||
other.mType = Type::Invalid;
|
||||
other.mValue = {};
|
||||
}
|
||||
|
||||
Variant& operator=( const Variant& other ) {
|
||||
if ( this != &other ) {
|
||||
reset();
|
||||
new ( this ) Variant( other ); // reuse copy constructor
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Variant& operator=( Variant&& other ) noexcept {
|
||||
if ( this != &other ) {
|
||||
reset();
|
||||
mType = other.mType;
|
||||
mDrawable = std::move( other.mDrawable );
|
||||
mValue = other.mValue;
|
||||
other.mType = Type::Invalid;
|
||||
other.mValue = {};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::string& asStdString() const { return *mValue.asStdString; }
|
||||
const String& asString() const { return *mValue.asString; }
|
||||
const String& asStringPtr() const { return *mValue.asStringPtr; }
|
||||
const DrawablePtr& asDrawable() const { return mDrawable; }
|
||||
const bool& asBool() const { return mValue.asBool; }
|
||||
const Float& asFloat() const { return mValue.asFloat; }
|
||||
const int& asInt() const { return mValue.asInt; }
|
||||
const unsigned int& asUint() const { return mValue.asUint; }
|
||||
const Int64& asInt64() const { return mValue.asInt64; }
|
||||
const Uint64& asUint64() const { return mValue.asUint64; }
|
||||
const Vector2f& asVector2f() const { return *mValue.asVector2f; }
|
||||
const Rectf& asRectf() const { return *mValue.asRectf; }
|
||||
const char* asCStr() const { return mValue.asCStr; }
|
||||
UIIcon* asIcon() const { return mValue.asIcon; }
|
||||
void* asDataPtr() const { return mValue.asDataPtr; }
|
||||
bool is( const Type& type ) const { return type == mType; }
|
||||
bool isString() const {
|
||||
return mType == Type::StdString || mType == Type::cstr || mType == Type::String ||
|
||||
mType == Type::StringPtr;
|
||||
}
|
||||
void reset() {
|
||||
switch ( mType ) {
|
||||
void moveConstruct( Variant&& other ) {
|
||||
switch ( other.mType ) {
|
||||
case Type::StdString:
|
||||
eeSAFE_DELETE( mValue.asStdString );
|
||||
new ( &mValue.asStdString ) std::string( std::move( other.mValue.asStdString ) );
|
||||
break;
|
||||
case Type::StdStringPtr:
|
||||
mValue.asStdStringPtr = other.mValue.asStdStringPtr;
|
||||
break;
|
||||
case Type::String:
|
||||
eeSAFE_DELETE( mValue.asString );
|
||||
new ( &mValue.asString ) String( std::move( other.mValue.asString ) );
|
||||
break;
|
||||
case Type::StringPtr:
|
||||
mValue.asStringPtr = other.mValue.asStringPtr;
|
||||
break;
|
||||
case Type::Drawable:
|
||||
mDrawable.reset();
|
||||
new ( &mValue.asDrawable ) DrawablePtr( std::move( other.mValue.asDrawable ) );
|
||||
break;
|
||||
case Type::Icon:
|
||||
mValue.asIcon = other.mValue.asIcon;
|
||||
break;
|
||||
case Type::Vector2f:
|
||||
eeSAFE_DELETE( mValue.asVector2f );
|
||||
new ( &mValue.asVector2f ) Vector2f( std::move( other.mValue.asVector2f ) );
|
||||
break;
|
||||
case Type::Rectf:
|
||||
eeSAFE_DELETE( mValue.asRectf );
|
||||
new ( &mValue.asRectf ) Rectf( std::move( other.mValue.asRectf ) );
|
||||
break;
|
||||
case Type::Bool:
|
||||
mValue.asBool = other.mValue.asBool;
|
||||
break;
|
||||
case Type::Float:
|
||||
mValue.asFloat = other.mValue.asFloat;
|
||||
break;
|
||||
case Type::Int:
|
||||
mValue.asInt = other.mValue.asInt;
|
||||
break;
|
||||
case Type::Uint:
|
||||
mValue.asUint = other.mValue.asUint;
|
||||
break;
|
||||
case Type::Int64:
|
||||
mValue.asInt64 = other.mValue.asInt64;
|
||||
break;
|
||||
case Type::Uint64:
|
||||
mValue.asUint64 = other.mValue.asUint64;
|
||||
break;
|
||||
case Type::cstr:
|
||||
mValue.asCStr = other.mValue.asCStr;
|
||||
break;
|
||||
case Type::DataPtr:
|
||||
mValue.asDataPtr = other.mValue.asDataPtr;
|
||||
break;
|
||||
case Type::Invalid:
|
||||
break;
|
||||
}
|
||||
mType = other.mType;
|
||||
other.destroy();
|
||||
}
|
||||
|
||||
void copyAssignSameType( const Variant& other ) {
|
||||
switch ( mType ) {
|
||||
case Type::StdString:
|
||||
mValue.asStdString = other.mValue.asStdString;
|
||||
break;
|
||||
case Type::String:
|
||||
mValue.asString = other.mValue.asString;
|
||||
break;
|
||||
case Type::Drawable:
|
||||
mValue.asDrawable = other.mValue.asDrawable;
|
||||
break;
|
||||
default:
|
||||
destroy();
|
||||
copyConstruct( other );
|
||||
break;
|
||||
}
|
||||
mType = Type::Invalid;
|
||||
}
|
||||
bool isValid() const { return mType != Type::Invalid; }
|
||||
|
||||
std::string toString() const {
|
||||
void moveAssignSameType( Variant&& other ) {
|
||||
switch ( mType ) {
|
||||
case Type::Bool:
|
||||
return asBool() ? "true" : "false";
|
||||
case Type::Int:
|
||||
return String::toString( asInt() );
|
||||
case Type::Uint:
|
||||
return String::toString( asUint() );
|
||||
case Type::Int64:
|
||||
return String::toString( asInt64() );
|
||||
case Type::Uint64:
|
||||
return String::toString( asUint64() );
|
||||
case Type::Float:
|
||||
return String::toString( asFloat() );
|
||||
case Type::StdString:
|
||||
return asStdString();
|
||||
mValue.asStdString = std::move( other.mValue.asStdString );
|
||||
other.destroy();
|
||||
break;
|
||||
case Type::String:
|
||||
return asString();
|
||||
case Type::StringPtr:
|
||||
return asStringPtr();
|
||||
mValue.asString = std::move( other.mValue.asString );
|
||||
other.destroy();
|
||||
break;
|
||||
case Type::Drawable:
|
||||
return asDrawable()->isDrawableResource()
|
||||
? static_cast<DrawableResource*>( asDrawable().get() )->getName()
|
||||
: "Drawable";
|
||||
case Type::Icon:
|
||||
return asIcon()->getName();
|
||||
case Type::DataPtr:
|
||||
return String::format( "%p", asDataPtr() );
|
||||
case Type::Vector2f:
|
||||
return String::format( "%.2f-%.2f", asVector2f().x, asVector2f().y );
|
||||
case Type::Rectf:
|
||||
return String::format( "%.2f-%.2f-%.2f-%.2f", asRectf().Top, asRectf().Right,
|
||||
asRectf().Bottom, asRectf().Left );
|
||||
case Type::cstr:
|
||||
return asCStr();
|
||||
case Type::Invalid:
|
||||
mValue.asDrawable = std::move( other.mValue.asDrawable );
|
||||
other.destroy();
|
||||
break;
|
||||
default:
|
||||
destroy();
|
||||
moveConstruct( std::move( other ) );
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool operator<( const Variant& other ) const {
|
||||
if ( mType != other.mType )
|
||||
return toString() < other.toString();
|
||||
switch ( mType ) {
|
||||
case Type::Bool:
|
||||
return asBool() < other.asBool();
|
||||
case Type::Int:
|
||||
return asInt() < other.asInt();
|
||||
case Type::Uint:
|
||||
return asUint() < other.asUint();
|
||||
case Type::Int64:
|
||||
return asInt64() < other.asInt64();
|
||||
case Type::Uint64:
|
||||
return asUint64() < other.asUint64();
|
||||
case Type::Float:
|
||||
return asFloat() < other.asFloat();
|
||||
case Type::StdString:
|
||||
return asStdString() < other.asStdString();
|
||||
case Type::String:
|
||||
return asString() < other.asString();
|
||||
case Type::StringPtr:
|
||||
return asStringPtr() < other.asStringPtr();
|
||||
case Type::Drawable:
|
||||
return asDrawable() < other.asDrawable();
|
||||
case Type::Icon:
|
||||
return asIcon() < other.asIcon();
|
||||
case Type::DataPtr:
|
||||
return asDataPtr() < other.asDataPtr();
|
||||
case Type::Vector2f:
|
||||
return asVector2f() < other.asVector2f();
|
||||
case Type::Rectf:
|
||||
return asRectf().getSize() < other.asRectf().getSize();
|
||||
case Type::cstr:
|
||||
return strcmp( asCStr(), other.asCStr() ) < 0;
|
||||
case Type::Invalid:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==( const Variant& other ) const {
|
||||
if ( mType != other.mType )
|
||||
return toString() == other.toString();
|
||||
switch ( mType ) {
|
||||
case Type::Bool:
|
||||
return asBool() == other.asBool();
|
||||
case Type::Int:
|
||||
return asInt() == other.asInt();
|
||||
case Type::Uint:
|
||||
return asUint() == other.asUint();
|
||||
case Type::Int64:
|
||||
return asInt64() == other.asInt64();
|
||||
case Type::Uint64:
|
||||
return asUint64() == other.asUint64();
|
||||
case Type::Float:
|
||||
return asFloat() == other.asFloat();
|
||||
case Type::StdString:
|
||||
return asStdString() == other.asStdString();
|
||||
case Type::String:
|
||||
return asString() == other.asString();
|
||||
case Type::StringPtr:
|
||||
return asStringPtr() == other.asStringPtr();
|
||||
case Type::Drawable:
|
||||
return asDrawable() == other.asDrawable();
|
||||
case Type::Icon:
|
||||
return asIcon() == other.asIcon();
|
||||
case Type::DataPtr:
|
||||
return asDataPtr() == other.asDataPtr();
|
||||
case Type::Vector2f:
|
||||
return asVector2f() == other.asVector2f();
|
||||
case Type::Rectf:
|
||||
return asRectf().getSize() == other.asRectf().getSize();
|
||||
case Type::cstr:
|
||||
return strcmp( asCStr(), other.asCStr() ) == 0;
|
||||
case Type::Invalid:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
switch ( mType ) {
|
||||
case Type::Bool:
|
||||
return 1;
|
||||
case Type::Int:
|
||||
return sizeof( int );
|
||||
case Type::Uint:
|
||||
return sizeof( unsigned int );
|
||||
case Type::Int64:
|
||||
return sizeof( Int64 );
|
||||
case Type::Uint64:
|
||||
return sizeof( Uint64 );
|
||||
case Type::Float:
|
||||
return sizeof( Float );
|
||||
case Type::StdString:
|
||||
return asStdString().size();
|
||||
case Type::String:
|
||||
return asString().size();
|
||||
case Type::StringPtr:
|
||||
return asStringPtr().size();
|
||||
case Type::Drawable:
|
||||
return sizeof( mDrawable );
|
||||
case Type::Icon:
|
||||
return asIcon()->getName().size();
|
||||
case Type::DataPtr:
|
||||
return sizeof( mValue.asDataPtr );
|
||||
case Type::Vector2f:
|
||||
return sizeof( mValue.asVector2f );
|
||||
case Type::Rectf:
|
||||
return sizeof( mValue.asRectf );
|
||||
case Type::cstr:
|
||||
return strlen( asCStr() );
|
||||
case Type::Invalid:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
void* asDataPtr{ nullptr };
|
||||
UIIcon* asIcon;
|
||||
std::string* asStdString;
|
||||
String* asString;
|
||||
const String* asStringPtr;
|
||||
bool asBool;
|
||||
Float asFloat;
|
||||
int asInt;
|
||||
unsigned int asUint;
|
||||
Int64 asInt64;
|
||||
Uint64 asUint64;
|
||||
Vector2f* asVector2f;
|
||||
Rectf* asRectf;
|
||||
const char* asCStr;
|
||||
} mValue;
|
||||
DrawablePtr mDrawable;
|
||||
Type mType;
|
||||
};
|
||||
|
||||
}}} // namespace EE::UI::Models
|
||||
|
||||
@@ -2081,6 +2081,8 @@ String::String( const StringType& utf32String ) : mString( utf32String ) {}
|
||||
|
||||
String::String( const String& str ) : mString( str.mString ) {}
|
||||
|
||||
String::String( String&& str ) noexcept : mString( std::move( str.mString ) ) {}
|
||||
|
||||
String::String( const String::View& utf32String ) : mString( utf32String ) {}
|
||||
|
||||
String String::fromUtf16( const char* utf16String, const size_t& utf16StringSize,
|
||||
|
||||
@@ -15,6 +15,29 @@ namespace EE { namespace UI { namespace Abstract {
|
||||
|
||||
static constexpr String::HashType onModelUpdateTag = String::hash( "onModelUpdate" );
|
||||
|
||||
template <typename Callback> static void consumeVariantText( const Variant& value, Callback&& cb ) {
|
||||
switch ( value.getType() ) {
|
||||
case Variant::Type::String:
|
||||
cb( value.asString() );
|
||||
break;
|
||||
case Variant::Type::StringPtr:
|
||||
cb( value.asStringPtr() );
|
||||
break;
|
||||
case Variant::Type::StdString:
|
||||
cb( value.asStdString() );
|
||||
break;
|
||||
case Variant::Type::StdStringPtr:
|
||||
cb( value.asStdStringPtr() );
|
||||
break;
|
||||
case Variant::Type::cstr:
|
||||
cb( value.asCStr() );
|
||||
break;
|
||||
default:
|
||||
cb( value.toString() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UIAbstractTableView::UIAbstractTableView( const std::string& tag ) :
|
||||
UIAbstractView( tag ),
|
||||
mDragBorderDistance( PixelDensity::dpToPx( 4 ) ),
|
||||
@@ -1009,19 +1032,26 @@ void UIAbstractTableView::updateTableCellData( UITableCell* cell, const ModelInd
|
||||
Variant cls( getModel()->data( index, ModelRole::Class ) );
|
||||
cell->setLoadingState( true );
|
||||
if ( cls.isValid() ) {
|
||||
bool hasClass = false;
|
||||
|
||||
hasClass =
|
||||
( cls.is( Variant::Type::cstr ) &&
|
||||
cell->hasClass( std::string_view{ cls.asCStr() } ) ) ||
|
||||
( cls.is( Variant::Type::StdString ) && cell->hasClass( cls.asStdString() ) ) ||
|
||||
cell->hasClass( cls.toString() );
|
||||
const bool isStdStringLike = cls.isStdStringLike();
|
||||
std::string convertedClass;
|
||||
const std::string_view className =
|
||||
isStdStringLike ? cls.asStdStringView()
|
||||
: std::string_view{ convertedClass = cls.toString() };
|
||||
const bool hasClass = cell->hasClass( className );
|
||||
|
||||
needsReloadStyle =
|
||||
cell->getClasses().empty() || cell->getClasses().size() != 1 || !hasClass;
|
||||
|
||||
if ( !hasClass )
|
||||
cell->setClass( cls.toString() );
|
||||
if ( !hasClass ) {
|
||||
if ( cls.is( Variant::Type::StdString ) )
|
||||
cell->setClass( cls.asStdString() );
|
||||
else if ( cls.is( Variant::Type::StdStringPtr ) )
|
||||
cell->setClass( cls.asStdStringPtr() );
|
||||
else if ( isStdStringLike )
|
||||
cell->setClass( std::string{ className } );
|
||||
else
|
||||
cell->setClass( std::move( convertedClass ) );
|
||||
}
|
||||
} else {
|
||||
needsReloadStyle = !cell->getClasses().empty();
|
||||
cell->resetClass();
|
||||
@@ -1033,25 +1063,14 @@ void UIAbstractTableView::updateTableCellData( UITableCell* cell, const ModelInd
|
||||
|
||||
if ( getModel()->tooltipModelRoleEnabled() ) {
|
||||
Variant tooltip( getModel()->data( index, ModelRole::Tooltip ) );
|
||||
if ( tooltip.isValid() ) {
|
||||
if ( tooltip.is( Variant::Type::String ) )
|
||||
cell->setTooltipText( tooltip.asString() );
|
||||
else if ( tooltip.is( Variant::Type::StringPtr ) )
|
||||
cell->setTooltipText( tooltip.asStringPtr() );
|
||||
else
|
||||
cell->setTooltipText( tooltip.toString() );
|
||||
}
|
||||
if ( tooltip.isValid() )
|
||||
consumeVariantText( tooltip,
|
||||
[cell]( const auto& text ) { cell->setTooltipText( text ); } );
|
||||
}
|
||||
|
||||
Variant txt( getModel()->data( index, ModelRole::Display ) );
|
||||
if ( txt.isValid() ) {
|
||||
if ( txt.is( Variant::Type::String ) )
|
||||
cell->setText( txt.asString() );
|
||||
else if ( txt.is( Variant::Type::StringPtr ) )
|
||||
cell->setText( txt.asStringPtr() );
|
||||
else
|
||||
cell->setText( txt.toString() );
|
||||
}
|
||||
if ( txt.isValid() )
|
||||
consumeVariantText( txt, [cell]( const auto& text ) { cell->setText( text ); } );
|
||||
}
|
||||
|
||||
void UIAbstractTableView::moveSelection( int steps ) {
|
||||
|
||||
@@ -218,13 +218,13 @@ bool SortingProxyModel::lessThan( const ModelIndex& index1, const ModelIndex& in
|
||||
auto data1 = mSource->data( index1, mSortRole );
|
||||
auto data2 = mSource->data( index2, mSortRole );
|
||||
if ( data1.isString() && data2.isString() ) {
|
||||
if ( data1.is( Variant::Type::StdString ) && data2.is( Variant::Type::StdString ) )
|
||||
return String::toLower( data1.asStdString() ) < String::toLower( data2.asStdString() );
|
||||
if ( data1.isStdStringLike() && data2.isStdStringLike() )
|
||||
return String::toLower( std::string{ data1.asStdStringView() } ) <
|
||||
String::toLower( std::string{ data2.asStdStringView() } );
|
||||
if ( data1.is( Variant::Type::String ) && data2.is( Variant::Type::String ) )
|
||||
return String::toLower( data1.asString() ) < String::toLower( data2.asString() );
|
||||
if ( data1.is( Variant::Type::cstr ) && data2.is( Variant::Type::cstr ) )
|
||||
return String::toLower( std::string( data1.asCStr() ) ) <
|
||||
String::toLower( std::string( data2.asCStr() ) );
|
||||
if ( data1.is( Variant::Type::StringPtr ) && data2.is( Variant::Type::StringPtr ) )
|
||||
return String::toLower( data1.asStringPtr() ) < String::toLower( data2.asStringPtr() );
|
||||
}
|
||||
return data1 < data2;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <eepp/ui/uitableview.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <string_view>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
@@ -392,19 +393,22 @@ ModelIndex UITableView::findRowWithText( const std::string& text, const bool& ca
|
||||
: ( model->treeColumn() >= 0 ? model->treeColumn() : 0 ) );
|
||||
Variant var = model->data( index );
|
||||
if ( var.isValid() ) {
|
||||
std::string convertedValue;
|
||||
const std::string_view value =
|
||||
var.isStdStringLike() ? var.asStdStringView()
|
||||
: std::string_view{ convertedValue = var.toString() };
|
||||
bool matches = false;
|
||||
switch ( matchKind ) {
|
||||
case Abstract::UIAbstractView::FindRowWithTextMatchKind::Equals:
|
||||
matches = var.toString() == text;
|
||||
matches = value == text;
|
||||
break;
|
||||
case Abstract::UIAbstractView::FindRowWithTextMatchKind::StartsWith:
|
||||
matches = String::startsWith( caseSensitive ? var.toString()
|
||||
: String::toLower( var.toString() ),
|
||||
caseSensitive ? text : String::toLower( text ) );
|
||||
matches = caseSensitive ? String::startsWith( value, text )
|
||||
: String::istartsWith( value, text );
|
||||
break;
|
||||
case Abstract::UIAbstractView::FindRowWithTextMatchKind::Contains:
|
||||
matches = caseSensitive ? String::contains( var.toString(), text )
|
||||
: String::icontains( var.toString(), text );
|
||||
matches = caseSensitive ? String::contains( value, text )
|
||||
: String::icontains( value, text );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <eepp/ui/uitooltip.hpp>
|
||||
#include <eepp/ui/uitreeview.hpp>
|
||||
#include <stack>
|
||||
#include <string_view>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
@@ -811,19 +812,22 @@ ModelIndex UITreeView::findRowWithText( const std::string& text, const bool& cas
|
||||
traverseTree( [&]( const int&, const ModelIndex& index, const size_t&, const Float& ) {
|
||||
Variant var = model->data( index );
|
||||
if ( var.isValid() ) {
|
||||
std::string convertedValue;
|
||||
const std::string_view value =
|
||||
var.isStdStringLike() ? var.asStdStringView()
|
||||
: std::string_view{ convertedValue = var.toString() };
|
||||
bool matches = false;
|
||||
switch ( matchKind ) {
|
||||
case Abstract::UIAbstractView::FindRowWithTextMatchKind::Equals:
|
||||
matches = var.toString() == text;
|
||||
matches = value == text;
|
||||
break;
|
||||
case Abstract::UIAbstractView::FindRowWithTextMatchKind::StartsWith:
|
||||
matches = String::startsWith( caseSensitive ? var.toString()
|
||||
: String::toLower( var.toString() ),
|
||||
caseSensitive ? text : String::toLower( text ) );
|
||||
matches = caseSensitive ? String::startsWith( value, text )
|
||||
: String::istartsWith( value, text );
|
||||
break;
|
||||
case Abstract::UIAbstractView::FindRowWithTextMatchKind::Contains:
|
||||
matches = caseSensitive ? String::contains( var.toString(), text )
|
||||
: String::icontains( var.toString(), text );
|
||||
matches = caseSensitive ? String::contains( value, text )
|
||||
: String::icontains( value, text );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -847,18 +851,21 @@ ModelIndex UITreeView::openRowWithPath( const std::vector<std::string>& pathTree
|
||||
ModelIndex foundIndex = {};
|
||||
const auto& part = pathTree[i];
|
||||
|
||||
traverseTree(
|
||||
[&model, &foundIndex, &part, &parentIndex,
|
||||
i]( const int&, const ModelIndex& index, const size_t& indentLevel, const Float& ) {
|
||||
Variant var = model->data( index );
|
||||
if ( i == indentLevel && var.isValid() && var.toString() == part ) {
|
||||
if ( !parentIndex.isValid() || parentIndex == index.parent() ) {
|
||||
foundIndex = index;
|
||||
return IterationDecision::Stop;
|
||||
}
|
||||
traverseTree( [&model, &foundIndex, &part, &parentIndex,
|
||||
i]( const int&, const ModelIndex& index, const size_t& indentLevel,
|
||||
const Float& ) {
|
||||
Variant var = model->data( index );
|
||||
const bool matches =
|
||||
i == indentLevel && var.isValid() &&
|
||||
( var.isStdStringLike() ? var.asStdStringView() == part : var.toString() == part );
|
||||
if ( matches ) {
|
||||
if ( !parentIndex.isValid() || parentIndex == index.parent() ) {
|
||||
foundIndex = index;
|
||||
return IterationDecision::Stop;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
} );
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
} );
|
||||
|
||||
if ( foundIndex == ModelIndex() )
|
||||
break;
|
||||
|
||||
229
src/tests/unit_tests/variant_tests.cpp
Normal file
229
src/tests/unit_tests/variant_tests.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#include "utest.hpp"
|
||||
|
||||
#include <eepp/graphics/rectangledrawable.hpp>
|
||||
#include <eepp/ui/models/variant.hpp>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
using namespace EE;
|
||||
using namespace EE::Graphics;
|
||||
using namespace EE::Math;
|
||||
using namespace EE::UI;
|
||||
using namespace EE::UI::Models;
|
||||
|
||||
static_assert( static_cast<int>( Variant::Type::Invalid ) == 0 );
|
||||
static_assert( static_cast<int>( Variant::Type::StringPtr ) == 15 );
|
||||
static_assert( static_cast<int>( Variant::Type::StdStringPtr ) == 16 );
|
||||
static_assert( std::is_nothrow_move_constructible_v<std::string> );
|
||||
static_assert( std::is_nothrow_move_constructible_v<String> );
|
||||
static_assert( std::is_nothrow_move_constructible_v<DrawablePtr> );
|
||||
|
||||
UTEST( Variant, ownedStdStringLifecycle ) {
|
||||
const std::string shortString = "ecode";
|
||||
const std::string longString( 256, 'x' );
|
||||
|
||||
Variant shortValue( shortString );
|
||||
EXPECT_TRUE( shortValue.is( Variant::Type::StdString ) );
|
||||
EXPECT_TRUE( shortValue.isString() );
|
||||
EXPECT_TRUE( shortValue.isStdStringLike() );
|
||||
EXPECT_TRUE( shortValue.asStdString() == shortString );
|
||||
EXPECT_TRUE( shortValue.asStdStringView() == shortString );
|
||||
EXPECT_EQ( shortValue.size(), shortString.size() );
|
||||
|
||||
Variant copied( shortValue );
|
||||
EXPECT_TRUE( copied == shortValue );
|
||||
|
||||
Variant longValue( longString );
|
||||
const char* longData = longValue.asStdString().data();
|
||||
Variant moved( std::move( longValue ) );
|
||||
EXPECT_TRUE( !longValue.isValid() );
|
||||
EXPECT_TRUE( moved.asStdString() == longString );
|
||||
EXPECT_EQ( moved.asStdString().data(), longData );
|
||||
|
||||
Variant copyAssigned( std::string( 512, 'z' ) );
|
||||
copyAssigned = moved;
|
||||
EXPECT_TRUE( copyAssigned.asStdString() == longString );
|
||||
|
||||
Variant moveAssigned( std::string( 128, 'q' ) );
|
||||
const char* movedData = moved.asStdString().data();
|
||||
moveAssigned = std::move( moved );
|
||||
EXPECT_TRUE( !moved.isValid() );
|
||||
EXPECT_EQ( moveAssigned.asStdString().data(), movedData );
|
||||
EXPECT_TRUE( moveAssigned.asStdString() == longString );
|
||||
|
||||
copyAssigned = copyAssigned;
|
||||
EXPECT_TRUE( copyAssigned.asStdString() == longString );
|
||||
Variant* self = &moveAssigned;
|
||||
moveAssigned = std::move( *self );
|
||||
EXPECT_TRUE( moveAssigned.asStdString() == longString );
|
||||
|
||||
Variant empty( std::string{} );
|
||||
EXPECT_TRUE( empty.asStdString().empty() );
|
||||
empty.reset();
|
||||
EXPECT_TRUE( !empty.isValid() );
|
||||
}
|
||||
|
||||
UTEST( Variant, ownedStringLifecycle ) {
|
||||
String source( "héllo" );
|
||||
Variant value( source );
|
||||
EXPECT_TRUE( value.is( Variant::Type::String ) );
|
||||
EXPECT_TRUE( value.asString() == source );
|
||||
|
||||
Variant copied( value );
|
||||
EXPECT_TRUE( copied.asString() == source );
|
||||
|
||||
String movedSource( "moved" );
|
||||
Variant movedValue( std::move( movedSource ) );
|
||||
EXPECT_TRUE( movedValue.asString() == String( "moved" ) );
|
||||
|
||||
Variant moved( std::move( value ) );
|
||||
EXPECT_TRUE( !value.isValid() );
|
||||
EXPECT_TRUE( moved.asString() == source );
|
||||
|
||||
Variant copyAssigned( String( "capacity" ) );
|
||||
copyAssigned = copied;
|
||||
EXPECT_TRUE( copyAssigned.asString() == source );
|
||||
|
||||
Variant moveAssigned( String( "old" ) );
|
||||
moveAssigned = std::move( copied );
|
||||
EXPECT_TRUE( !copied.isValid() );
|
||||
EXPECT_TRUE( moveAssigned.asString() == source );
|
||||
|
||||
moveAssigned.reset();
|
||||
EXPECT_TRUE( !moveAssigned.isValid() );
|
||||
}
|
||||
|
||||
UTEST( Variant, borrowedStringsRemainBorrowed ) {
|
||||
std::string stdString = "borrowed std::string";
|
||||
String string = "borrowed String";
|
||||
|
||||
Variant stdPtr = Variant::fromRef( stdString );
|
||||
EXPECT_TRUE( stdPtr.is( Variant::Type::StdStringPtr ) );
|
||||
EXPECT_EQ( &stdPtr.asStdStringPtr(), &stdString );
|
||||
EXPECT_TRUE( stdPtr.asStdStringView() == stdString );
|
||||
EXPECT_TRUE( stdPtr.toString() == stdString );
|
||||
EXPECT_EQ( stdPtr.size(), stdString.size() );
|
||||
|
||||
Variant stdPtrCopy( stdPtr );
|
||||
Variant stdPtrMove( std::move( stdPtrCopy ) );
|
||||
EXPECT_TRUE( !stdPtrCopy.isValid() );
|
||||
EXPECT_EQ( &stdPtrMove.asStdStringPtr(), &stdString );
|
||||
stdPtrMove.reset();
|
||||
EXPECT_TRUE( stdString == "borrowed std::string" );
|
||||
|
||||
Variant stringPtr( &string );
|
||||
EXPECT_TRUE( stringPtr.is( Variant::Type::StringPtr ) );
|
||||
EXPECT_EQ( &stringPtr.asStringPtr(), &string );
|
||||
EXPECT_TRUE( stringPtr.toString() == string.toUtf8() );
|
||||
stringPtr.reset();
|
||||
EXPECT_TRUE( string == String( "borrowed String" ) );
|
||||
|
||||
Variant cstr( "borrowed cstr" );
|
||||
EXPECT_TRUE( cstr.is( Variant::Type::cstr ) );
|
||||
EXPECT_TRUE( cstr.asStdStringView() == "borrowed cstr" );
|
||||
EXPECT_EQ( cstr.size(), 13u );
|
||||
}
|
||||
|
||||
UTEST( Variant, stringComparisonAndOrdering ) {
|
||||
std::string borrowed = "beta";
|
||||
Variant owned( std::string( "beta" ) );
|
||||
Variant pointer = Variant::fromRef( borrowed );
|
||||
Variant cstr( "beta" );
|
||||
Variant alpha( std::string( "alpha" ) );
|
||||
|
||||
EXPECT_TRUE( owned == pointer );
|
||||
EXPECT_TRUE( pointer == cstr );
|
||||
EXPECT_TRUE( alpha < pointer );
|
||||
EXPECT_TRUE( owned.toString() == "beta" );
|
||||
}
|
||||
|
||||
UTEST( Variant, valueAndPointerAlternatives ) {
|
||||
int pointedValue = 42;
|
||||
void* data = &pointedValue;
|
||||
auto* icon = reinterpret_cast<UIIcon*>( &pointedValue );
|
||||
const Vector2f vector( 10.f, 20.f );
|
||||
const Rectf rect( 1.f, 2.f, 3.f, 4.f );
|
||||
|
||||
Variant boolValue( true );
|
||||
Variant floatValue( 1.5f );
|
||||
Variant intValue( -42 );
|
||||
Variant uintValue( std::numeric_limits<unsigned int>::max() );
|
||||
Variant int64Value( Int64( -9000000000 ) );
|
||||
Variant uint64Value( Uint64( 18000000000ULL ) );
|
||||
Variant dataValue( data );
|
||||
Variant iconValue( icon );
|
||||
Variant vectorValue( vector );
|
||||
Variant rectValue( rect );
|
||||
|
||||
EXPECT_TRUE( boolValue.asBool() );
|
||||
EXPECT_EQ( floatValue.asFloat(), 1.5f );
|
||||
EXPECT_EQ( intValue.asInt(), -42 );
|
||||
EXPECT_EQ( uintValue.asUint(), std::numeric_limits<unsigned int>::max() );
|
||||
EXPECT_EQ( int64Value.asInt64(), Int64( -9000000000 ) );
|
||||
EXPECT_EQ( uint64Value.asUint64(), Uint64( 18000000000ULL ) );
|
||||
EXPECT_EQ( dataValue.asDataPtr(), data );
|
||||
EXPECT_EQ( iconValue.asIcon(), icon );
|
||||
EXPECT_TRUE( vectorValue.asVector2f() == vector );
|
||||
EXPECT_TRUE( rectValue.asRectf() == rect );
|
||||
EXPECT_EQ( vectorValue.size(), sizeof( Vector2f ) );
|
||||
EXPECT_EQ( rectValue.size(), sizeof( Rectf ) );
|
||||
|
||||
Variant copied( vectorValue );
|
||||
EXPECT_TRUE( copied.asVector2f() == vector );
|
||||
Variant moved( std::move( copied ) );
|
||||
EXPECT_TRUE( !copied.isValid() );
|
||||
EXPECT_TRUE( moved.asVector2f() == vector );
|
||||
moved = rectValue;
|
||||
EXPECT_TRUE( moved.is( Variant::Type::Rectf ) );
|
||||
EXPECT_TRUE( moved.asRectf() == rect );
|
||||
}
|
||||
|
||||
UTEST( Variant, typeTransitions ) {
|
||||
Variant value( std::string( "text" ) );
|
||||
value = Variant( 42 );
|
||||
EXPECT_TRUE( value.is( Variant::Type::Int ) );
|
||||
EXPECT_EQ( value.asInt(), 42 );
|
||||
|
||||
value = Variant( String( "unicode" ) );
|
||||
EXPECT_TRUE( value.is( Variant::Type::String ) );
|
||||
value = Variant( DrawablePtr{} );
|
||||
EXPECT_TRUE( value.is( Variant::Type::Drawable ) );
|
||||
value.reset();
|
||||
EXPECT_TRUE( !value.isValid() );
|
||||
|
||||
value = Variant( Vector2f( 2.f, 3.f ) );
|
||||
value = Variant( std::string( "again" ) );
|
||||
EXPECT_TRUE( value.is( Variant::Type::StdString ) );
|
||||
EXPECT_TRUE( value.asStdString() == "again" );
|
||||
}
|
||||
|
||||
UTEST( Variant, drawableOwnershipLifecycle ) {
|
||||
auto drawable = makeResource<RectangleDrawable>( Vector2f( 1.f, 2.f ), Sizef( 3.f, 4.f ) );
|
||||
ASSERT_TRUE( drawable != nullptr );
|
||||
const auto initialRefs = drawable.use_count();
|
||||
|
||||
Variant original( drawable );
|
||||
EXPECT_EQ( drawable.use_count(), initialRefs + 1 );
|
||||
{
|
||||
Variant copied( original );
|
||||
EXPECT_EQ( drawable.use_count(), initialRefs + 2 );
|
||||
Variant moved( std::move( copied ) );
|
||||
EXPECT_TRUE( !copied.isValid() );
|
||||
EXPECT_EQ( drawable.use_count(), initialRefs + 2 );
|
||||
Variant moveAssigned( DrawablePtr{} );
|
||||
moveAssigned = std::move( moved );
|
||||
EXPECT_TRUE( !moved.isValid() );
|
||||
EXPECT_EQ( drawable.use_count(), initialRefs + 2 );
|
||||
|
||||
Variant assigned( DrawablePtr{} );
|
||||
assigned = original;
|
||||
EXPECT_EQ( drawable.use_count(), initialRefs + 3 );
|
||||
assigned = Variant( 7 );
|
||||
EXPECT_EQ( drawable.use_count(), initialRefs + 2 );
|
||||
moveAssigned.reset();
|
||||
EXPECT_EQ( drawable.use_count(), initialRefs + 1 );
|
||||
}
|
||||
|
||||
original.reset();
|
||||
EXPECT_EQ( drawable.use_count(), initialRefs );
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <string_view>
|
||||
#if EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_MACOS || \
|
||||
EE_PLATFORM == EE_PLATFORM_BSD
|
||||
#include <unistd.h>
|
||||
@@ -153,6 +154,21 @@ const char* usernameClass( const ProcessInfo& process ) {
|
||||
return "eproc-process-username-other";
|
||||
}
|
||||
|
||||
void setCellClassEnabled( UITableCell& cell, std::string_view className, bool enabled ) {
|
||||
const bool hasClass = cell.hasClass( className );
|
||||
if ( enabled == hasClass )
|
||||
return;
|
||||
if ( enabled )
|
||||
cell.addClass( std::string{ className } );
|
||||
else
|
||||
cell.removeClass( std::string{ className } );
|
||||
}
|
||||
|
||||
constexpr std::array<std::string_view, 5> kUsernameClasses = {
|
||||
"eproc-process-username-ended", "eproc-process-username-traced", "eproc-process-username-own",
|
||||
"eproc-process-username-system", "eproc-process-username-other",
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
App::App() {
|
||||
@@ -174,7 +190,7 @@ App::App() {
|
||||
mApp = std::make_unique<UIApplication>( ws );
|
||||
if ( mApp->getUI() && mApp->getWindow() )
|
||||
mApp->getWindow()->setTitle(
|
||||
mApp->getUI()->i18n( "eproc_window_title", "System Monitor" ) );
|
||||
mApp->getUI()->i18n( "eproc_window_title", "eproc - System Monitor" ) );
|
||||
}
|
||||
|
||||
App::~App() {}
|
||||
@@ -504,6 +520,8 @@ void App::setupUI() {
|
||||
UIWidgetInspector::create( mApp->getUI() );
|
||||
}
|
||||
} );
|
||||
|
||||
mSearchInput->setFocus();
|
||||
}
|
||||
|
||||
void App::setupProcessTable() {
|
||||
@@ -515,23 +533,20 @@ void App::setupProcessTable() {
|
||||
mTableView->setColumnsHidden(
|
||||
std::vector<size_t>( kOptionalProcessColumns.begin(), kOptionalProcessColumns.end() ),
|
||||
true );
|
||||
mTableView->setOnUpdateCellCb( [this]( UITableCell* cell, Model* model ) {
|
||||
if ( !cell || !model )
|
||||
mTableView->setOnUpdateCellCb( [this]( UITableCell* cell, Model* ) {
|
||||
if ( !cell )
|
||||
return;
|
||||
|
||||
const ModelIndex index = cell->getCurIndex();
|
||||
const ProcessInfo* process = processForProxyIndex( index );
|
||||
const Variant columnClass = model->data( index, ModelRole::Class );
|
||||
std::vector<std::string> classes;
|
||||
if ( columnClass.isValid() )
|
||||
classes.emplace_back( columnClass.toString() );
|
||||
if ( process ) {
|
||||
if ( process->status == ProcessStatus::Ended )
|
||||
classes.emplace_back( "eproc-process-ended" );
|
||||
if ( index.column() == ProcessModel::ColUsername )
|
||||
classes.emplace_back( usernameClass( *process ) );
|
||||
}
|
||||
cell->setClasses( classes );
|
||||
setCellClassEnabled( *cell, "eproc-process-ended",
|
||||
process && process->status == ProcessStatus::Ended );
|
||||
|
||||
const std::string_view desiredUsernameClass =
|
||||
process && index.column() == ProcessModel::ColUsername ? usernameClass( *process )
|
||||
: std::string_view{};
|
||||
for ( const auto className : kUsernameClasses )
|
||||
setCellClassEnabled( *cell, className, className == desiredUsernameClass );
|
||||
} );
|
||||
mTableView->setRowHeight( 28 );
|
||||
// The flexible column is Name; the icon column is fixed so every row lines up.
|
||||
|
||||
@@ -193,15 +193,13 @@ Variant ProcessModel::data( const ModelIndex& index, ModelRole role ) const {
|
||||
|
||||
switch ( index.column() ) {
|
||||
case ColName:
|
||||
return Variant( proc->name );
|
||||
return Variant::fromRef( proc->name );
|
||||
case ColPid:
|
||||
return Variant( String::toString( static_cast<Int64>( proc->pid ) ) );
|
||||
case ColUsername:
|
||||
return Variant( proc->username );
|
||||
case ColCpu: {
|
||||
std::string cpu = proc->formatCpu();
|
||||
return Variant( cpu );
|
||||
}
|
||||
return Variant::fromRef( proc->username );
|
||||
case ColCpu:
|
||||
return Variant( proc->formatCpu() );
|
||||
case ColMemory:
|
||||
return Variant( proc->formatMemory() );
|
||||
case ColSharedMem:
|
||||
@@ -215,7 +213,7 @@ Variant ProcessModel::data( const ModelIndex& index, ModelRole role ) const {
|
||||
case ColUpload:
|
||||
return Variant( proc->formatUpload() );
|
||||
case ColCommand:
|
||||
return Variant( proc->command );
|
||||
return Variant::fromRef( proc->command );
|
||||
case ColTotalMemory:
|
||||
return Variant( formatKiB( proc->vmRSS ) );
|
||||
case ColVirtualSize:
|
||||
@@ -228,7 +226,7 @@ Variant ProcessModel::data( const ModelIndex& index, ModelRole role ) const {
|
||||
return Variant( proc->formatRelativeStartTime( mSystemInfo.uptimeSeconds,
|
||||
mSystemInfo.clockTicksPerSecond ) );
|
||||
case ColTty:
|
||||
return Variant( proc->tty );
|
||||
return Variant::fromRef( proc->tty );
|
||||
case ColIoRead:
|
||||
return Variant( formatBytes( proc->ioReadBytes ) );
|
||||
case ColIoWrite:
|
||||
|
||||
Reference in New Issue
Block a user