Optimize UITableView and UITreeView by only allocating as many cells as needed to cover the viewport.

This commit is contained in:
Martín Lucas Golini
2024-02-11 13:45:18 -03:00
parent a09377f8e6
commit eccf1f1cba
10 changed files with 172 additions and 102 deletions
+2
View File
@@ -108,6 +108,8 @@ class EE_API Node : public Transformable {
Node* setVisible( const bool& visible );
Node* setChildsVisibility( bool visible, bool emitEventNotification = true );
bool isVisible() const;
bool isHided() const;
@@ -183,7 +183,7 @@ class EE_API UIAbstractTableView : public UIAbstractView {
virtual UITableRow* updateRow( const int& rowIndex, const ModelIndex& index,
const Float& yOffset );
virtual UIWidget* updateCell( const int& rowIndex, const ModelIndex& index,
virtual UIWidget* updateCell( const Vector2<Int64>& posIndex, const ModelIndex& index,
const size_t& indentLevel, const Float& yOffset );
virtual UIWidget* createCell( UIWidget* rowWidget, const ModelIndex& index );
+1 -1
View File
@@ -144,7 +144,7 @@ class EE_API UITreeView : public UIAbstractTableView {
virtual void onColumnSizeChange( const size_t& colIndex, bool fromUserInteraction = false );
virtual UIWidget* updateCell( const int& rowIndex, const ModelIndex& index,
virtual UIWidget* updateCell( const Vector2<Int64>& posIndex, const ModelIndex& index,
const size_t& indentLevel, const Float& yOffset );
virtual UIWidget* createCell( UIWidget* rowWidget, const ModelIndex& index );
+16
View File
@@ -181,6 +181,22 @@ Node* Node::setVisible( const bool& visible ) {
return this;
}
Node* Node::setChildsVisibility( bool visible, bool emitEventNotification ) {
Node* child = mChild;
if ( emitEventNotification ) {
while ( child ) {
child->setVisible( visible );
child = child->mNext;
}
} else {
while ( child ) {
child->mVisible = visible;
child = child->mNext;
}
}
return this;
}
bool Node::isVisible() const {
return mVisible;
}
+8 -6
View File
@@ -577,21 +577,23 @@ UIWidget* UIAbstractTableView::setupCell( UITableCell* widget, UIWidget* rowWidg
return widget;
}
UIWidget* UIAbstractTableView::updateCell( const int& rowIndex, const ModelIndex& index,
UIWidget* UIAbstractTableView::updateCell( const Vector2<Int64>& posIndex, const ModelIndex& index,
const size_t&, const Float& yOffset ) {
if ( rowIndex >= (int)mWidgets.size() )
mWidgets.resize( rowIndex + 1 );
auto* widget = mWidgets[rowIndex][index.column()];
if ( posIndex.y >= (int)mWidgets.size() )
mWidgets.resize( posIndex.y + 1 );
auto* widget = mWidgets[posIndex.y][posIndex.x];
if ( !widget ) {
UIWidget* rowWidget = updateRow( rowIndex, index, yOffset );
UIWidget* rowWidget = updateRow( posIndex.y, index, yOffset );
widget = createCell( rowWidget, index );
mWidgets[rowIndex][index.column()] = widget;
mWidgets[posIndex.y][posIndex.x] = widget;
widget->reloadStyle( true, true, true );
}
const auto& colData = columnData( index.column() );
if ( !colData.visible ) {
widget->setVisible( false );
return widget;
} else {
widget->setVisible( true );
}
widget->setPixelsSize( colData.width, getRowHeight() );
widget->setPixelsPosition( { getColumnPosition( index.column() ).x, 0 } );
+17 -8
View File
@@ -355,21 +355,30 @@ bool UIScrollableWidget::applyProperty( const StyleSheetProperty& attribute ) {
Uint32 UIScrollableWidget::onMessage( const NodeMessage* Msg ) {
switch ( Msg->getMsg() ) {
case NodeMessage::MouseUp: {
bool moved = false;
if ( mVScroll->isEnabled() ) {
if ( Msg->getFlags() & EE_BUTTON_WUMASK ) {
mVScroll->setValue( mVScroll->getValue() - mVScroll->getClickStep() );
return 1;
moved = true;
} else if ( Msg->getFlags() & EE_BUTTON_WDMASK ) {
mVScroll->setValue( mVScroll->getValue() + mVScroll->getClickStep() );
return 1;
} else if ( Msg->getFlags() & EE_BUTTON_WLMASK ) {
mHScroll->setValue( mHScroll->getValue() - mHScroll->getClickStep() );
return 1;
} else if ( Msg->getFlags() & EE_BUTTON_WRMASK ) {
mHScroll->setValue( mHScroll->getValue() + mHScroll->getClickStep() );
return 1;
moved = true;
}
}
if ( mHScroll->isEnabled() ) {
if ( Msg->getFlags() & EE_BUTTON_WLMASK ) {
mHScroll->setValue( mHScroll->getValue() - mHScroll->getClickStep() );
moved = true;
} else if ( Msg->getFlags() & EE_BUTTON_WRMASK ) {
mHScroll->setValue( mHScroll->getValue() + mHScroll->getClickStep() );
moved = true;
}
}
if ( moved )
return 1;
}
}
return UIWidget::onMessage( Msg );
+74 -61
View File
@@ -27,28 +27,48 @@ bool UITableView::isType( const Uint32& type ) const {
}
void UITableView::drawChilds() {
int realIndex = 0;
int realRowIndex = 0;
int realColIndex = 0;
ConditionalLock l( getModel() != nullptr, getModel() ? &getModel()->resourceMutex() : nullptr );
size_t start = mScrollOffset.y / getRowHeight();
size_t end =
eemin<size_t>( (size_t)eeceil( ( mScrollOffset.y + mSize.getHeight() ) / getRowHeight() ),
getItemCount() );
Float yOffset;
for ( size_t i = start; i < end; i++ ) {
yOffset = getHeaderHeight() + i * getRowHeight();
ModelIndex index( getModel()->index( i ) );
if ( yOffset - mScrollOffset.y > mSize.getHeight() )
break;
if ( yOffset - mScrollOffset.y + getRowHeight() < 0 )
continue;
for ( size_t colIndex = 0; colIndex < getModel()->columnCount(); colIndex++ ) {
updateCell( realIndex, getModel()->index( index.row(), colIndex, index.parent() ), 0,
yOffset );
if ( getModel() ) {
Float rowHeight = getRowHeight();
size_t start = mScrollOffset.y / rowHeight;
size_t end = eemin<size_t>(
(size_t)eeceil( ( mScrollOffset.y + mSize.getHeight() ) / rowHeight ), getItemCount() );
Float yOffset = 0;
Float xOffset;
auto colCount = getModel()->columnCount();
auto headerHeight = getHeaderHeight();
for ( size_t i = start; i < end; i++ ) {
xOffset = mPaddingPx.Left;
yOffset = headerHeight + i * rowHeight;
ModelIndex rowIndex( getModel()->index( i ) );
if ( yOffset - mScrollOffset.y > mSize.getHeight() )
break;
if ( yOffset - mScrollOffset.y + rowHeight < 0 )
continue;
UITableRow* rowNode = updateRow( realRowIndex, rowIndex, yOffset );
rowNode->setChildsVisibility( false, false );
realColIndex = 0;
for ( size_t colIndex = 0; colIndex < colCount; colIndex++ ) {
auto& colData = columnData( colIndex );
if ( !colData.visible || ( xOffset + colData.width ) - mScrollOffset.x < 0 ) {
if ( colData.visible )
xOffset += colData.width;
continue;
}
if ( xOffset - mScrollOffset.x > mSize.getWidth() )
break;
xOffset += colData.width;
updateCell( { realColIndex, realRowIndex },
getModel()->index( rowIndex.row(), colIndex, rowIndex.parent() ), 0,
yOffset );
realColIndex++;
}
rowNode->nodeDraw();
realRowIndex++;
}
updateRow( realIndex, index, yOffset )->nodeDraw();
realIndex++;
}
if ( mHeader && mHeader->isVisible() )
mHeader->nodeDraw();
if ( mHScroll->isVisible() )
@@ -61,49 +81,42 @@ Node* UITableView::overFind( const Vector2f& point ) {
ScopedOp op( [this] { mUISceneNode->setIsLoading( true ); },
[this] { mUISceneNode->setIsLoading( false ); } );
Node* pOver = NULL;
if ( mEnabled && mVisible ) {
ConditionalLock l( getModel() != nullptr,
getModel() ? &getModel()->resourceMutex() : nullptr );
updateWorldPolygon();
if ( mWorldBounds.contains( point ) && mPoly.pointInside( point ) ) {
writeNodeFlag( NODE_FLAG_MOUSEOVER_ME_OR_CHILD, 1 );
mSceneNode->addMouseOverNode( this );
if ( mHScroll->isVisible() && ( pOver = mHScroll->overFind( point ) ) )
return pOver;
if ( mVScroll->isVisible() && ( pOver = mVScroll->overFind( point ) ) )
return pOver;
if ( mHeader && ( pOver = mHeader->overFind( point ) ) )
return pOver;
int realIndex = 0;
Float yOffset;
size_t start = mScrollOffset.y / getRowHeight();
size_t end = eemin<size_t>(
(size_t)eeceil( ( mScrollOffset.y + mSize.getHeight() ) / getRowHeight() ),
getItemCount() );
for ( size_t i = start; i < end; i++ ) {
yOffset = getHeaderHeight() + i * getRowHeight();
ModelIndex index( getModel()->index( i ) );
if ( yOffset - mScrollOffset.y > mSize.getHeight() )
break;
if ( yOffset - mScrollOffset.y + getRowHeight() < 0 )
continue;
for ( size_t colIndex = 0; colIndex < getModel()->columnCount(); colIndex++ ) {
if ( columnData( colIndex ).visible ) {
updateCell( realIndex,
getModel()->index( index.row(), colIndex, index.parent() ), 0,
yOffset );
}
}
pOver = updateRow( realIndex, index, yOffset )->overFind( point );
if ( pOver )
break;
realIndex++;
}
if ( !pOver )
pOver = this;
if ( !mEnabled || !mVisible )
return pOver;
ConditionalLock l( getModel() != nullptr, getModel() ? &getModel()->resourceMutex() : nullptr );
updateWorldPolygon();
if ( mWorldBounds.contains( point ) && mPoly.pointInside( point ) ) {
writeNodeFlag( NODE_FLAG_MOUSEOVER_ME_OR_CHILD, 1 );
mSceneNode->addMouseOverNode( this );
if ( mHScroll->isVisible() && ( pOver = mHScroll->overFind( point ) ) )
return pOver;
if ( mVScroll->isVisible() && ( pOver = mVScroll->overFind( point ) ) )
return pOver;
if ( mHeader && ( pOver = mHeader->overFind( point ) ) )
return pOver;
Float rowHeight = getRowHeight();
Float headerHeight = getHeaderHeight();
Float itemCount = getItemCount();
int realIndex = 0;
Float yOffset;
size_t start = mScrollOffset.y / rowHeight;
size_t end = eemin<size_t>(
(size_t)eeceil( ( mScrollOffset.y + mSize.getHeight() ) / rowHeight ), itemCount );
for ( size_t i = start; i < end; i++ ) {
yOffset = headerHeight + i * rowHeight;
ModelIndex index( getModel()->index( i ) );
if ( yOffset - mScrollOffset.y > mSize.getHeight() )
break;
if ( yOffset - mScrollOffset.y + rowHeight < 0 )
continue;
pOver = updateRow( realIndex, index, yOffset )->overFind( point );
if ( pOver )
break;
realIndex++;
}
if ( !pOver )
pOver = this;
}
return pOver;
}
@@ -116,7 +129,7 @@ Float UITableView::getMaxColumnContentWidth( const size_t& colIndex, bool bestGu
[this] { mUISceneNode->setIsLoading( false ); } );
Float yOffset = getHeaderHeight();
auto worstCaseFunc = [&]( const ModelIndex& index ) {
UIWidget* widget = updateCell( index.row(), index, 0, yOffset );
UIWidget* widget = updateCell( { (Int64)0, (Int64)0 }, index, 0, yOffset );
if ( widget->isType( UI_TYPE_PUSHBUTTON ) ) {
Float w = widget->asType<UIPushButton>()->getContentSize().getWidth();
if ( w > lWidth )
+42 -24
View File
@@ -215,21 +215,23 @@ UIWidget* UITreeView::createCell( UIWidget* rowWidget, const ModelIndex& index )
return setupCell( widget, rowWidget, index );
}
UIWidget* UITreeView::updateCell( const int& rowIndex, const ModelIndex& index,
UIWidget* UITreeView::updateCell( const Vector2<Int64>& posIndex, const ModelIndex& index,
const size_t& indentLevel, const Float& yOffset ) {
if ( rowIndex >= (int)mWidgets.size() )
mWidgets.resize( rowIndex + 1 );
auto* widget = mWidgets[rowIndex][index.column()];
if ( posIndex.y >= (int)mWidgets.size() )
mWidgets.resize( posIndex.y + 1 );
auto* widget = mWidgets[posIndex.y][index.column()];
if ( !widget ) {
UIWidget* rowWidget = updateRow( rowIndex, index, yOffset );
UIWidget* rowWidget = updateRow( posIndex.y, index, yOffset );
widget = createCell( rowWidget, index );
mWidgets[rowIndex][index.column()] = widget;
mWidgets[posIndex.y][index.column()] = widget;
widget->reloadStyle( true, true, true );
}
const auto& colData = columnData( index.column() );
if ( !colData.visible ) {
widget->setVisible( false );
return widget;
} else {
widget->setVisible( true );
}
widget->setPixelsSize( colData.width, getRowHeight() );
widget->setPixelsPosition( { getColumnPosition( index.column() ).x, 0 } );
@@ -346,33 +348,48 @@ Sizef UITreeView::getContentSize() const {
}
void UITreeView::drawChilds() {
int realIndex = 0;
int realRowIndex = 0;
int realColIndex = 0;
Float rowHeight = getRowHeight();
traverseTree( [this, &realIndex, rowHeight]( const int&, const ModelIndex& index,
const size_t& indentLevel, const Float& yOffset ) {
traverseTree( [this, &realRowIndex, &realColIndex,
rowHeight]( const int&, const ModelIndex& index, const size_t& indentLevel,
const Float& yOffset ) {
if ( yOffset - mScrollOffset.y > mSize.getHeight() )
return IterationDecision::Stop;
if ( yOffset - mScrollOffset.y + rowHeight < 0 )
return IterationDecision::Continue;
Float xOffset = mPaddingPx.Left;
UITableRow* rowNode = updateRow( realRowIndex, index, yOffset );
rowNode->setChildsVisibility( false, false );
realColIndex = 0;
for ( size_t colIndex = 0; colIndex < getModel()->columnCount(); colIndex++ ) {
if ( columnData( colIndex ).visible ) {
if ( (Int64)colIndex != index.column() ) {
updateCell( realIndex,
getModel()->index( index.row(), colIndex, index.parent() ),
indentLevel, yOffset );
} else {
auto* cell = updateCell( realIndex, index, indentLevel, yOffset );
auto& colData = columnData( colIndex );
if ( !colData.visible || ( xOffset + colData.width ) - mScrollOffset.x < 0 ) {
if ( colData.visible )
xOffset += colData.width;
continue;
}
if ( xOffset - mScrollOffset.x > mSize.getWidth() )
break;
xOffset += colData.width;
if ( (Int64)colIndex != index.column() ) {
updateCell( { realColIndex, realRowIndex },
getModel()->index( index.row(), colIndex, index.parent() ), indentLevel,
yOffset );
} else {
auto* cell =
updateCell( { realColIndex, realRowIndex }, index, indentLevel, yOffset );
if ( mFocusSelectionDirty && index == getSelection().first() ) {
cell->setFocus();
mFocusSelectionDirty = false;
}
if ( mFocusSelectionDirty && index == getSelection().first() ) {
cell->setFocus();
mFocusSelectionDirty = false;
}
}
realColIndex++;
}
updateRow( realIndex, index, yOffset )->nodeDraw();
realIndex++;
rowNode->nodeDraw();
realRowIndex++;
return IterationDecision::Continue;
} );
@@ -511,8 +528,9 @@ Float UITreeView::getMaxColumnContentWidth( const size_t& colIndex, bool ) {
[this] { mUISceneNode->setIsLoading( false ); } );
traverseTree( [&, colIndex]( const int&, const ModelIndex& index, const size_t& indentLevel,
const Float& yOffset ) {
UIWidget* widget = updateCell(
0, getModel()->index( index.row(), colIndex, index.parent() ), indentLevel, yOffset );
UIWidget* widget = updateCell( { (Int64)0, (Int64)0 },
getModel()->index( index.row(), colIndex, index.parent() ),
indentLevel, yOffset );
if ( widget->isType( UI_TYPE_PUSHBUTTON ) ) {
Float w = widget->asType<UIPushButton>()->getContentSize().getWidth();
if ( w > lWidth )
+4 -1
View File
@@ -119,7 +119,9 @@ std::shared_ptr<Formula> FormulaParser::formula() {
nextToken();
double val = 0;
String::fromString( val, n );
return std::make_shared<Number>( val );
if ( lookahead.token == TokenType::EPSILON )
return std::make_shared<Number>( val );
break;
}
case TokenType::EQUALS:
nextToken();
@@ -129,6 +131,7 @@ std::shared_ptr<Formula> FormulaParser::formula() {
default:
return std::make_shared<Textual>( formulaString );
}
return nullptr;
}
std::shared_ptr<Formula> FormulaParser::parseFormula( std::string _formulaString ) {
@@ -86,6 +86,13 @@ GitPlugin::~GitPlugin() {
{ Lock l( mGitStatusMutex ); }
{ Lock l( mRepoMutex ); }
{ Lock l( mReposMutex ); }
// TODO: Add a signal for this waits
while ( mRunningUpdateStatus )
Sys::sleep( 1.f );
while ( mRunningUpdateBranches )
Sys::sleep( 1.f );
}
void GitPlugin::load( PluginManager* pluginManager ) {