mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-09-29 01:34:13 +03:00
Optimization to reduce almost to zero allocations during frame rendering in ecode (this gives around 6% perf gains).
This commit is contained in:
@@ -899,6 +899,60 @@ class EE_API String {
|
||||
|
||||
String::View view() const;
|
||||
|
||||
// No allocation int to str
|
||||
template <typename IntType, typename StrType>
|
||||
static const StrType* intToStrBuf( IntType value, StrType* buffer, size_t bufferSize,
|
||||
size_t padding, StrType padChar ) {
|
||||
static_assert( std::is_integral<IntType>::value, "Type must be an integer" );
|
||||
|
||||
if ( bufferSize <= 1 ) {
|
||||
if ( bufferSize == 1 )
|
||||
*buffer = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
StrType* ptr = buffer;
|
||||
StrType* endPtr = buffer + bufferSize - 1;
|
||||
|
||||
if constexpr ( std::is_signed<IntType>::value ) {
|
||||
if ( value < 0 ) {
|
||||
if ( ptr == endPtr ) {
|
||||
*buffer = '\0';
|
||||
return buffer;
|
||||
}
|
||||
*ptr++ = '-';
|
||||
value = -value;
|
||||
padding--;
|
||||
}
|
||||
}
|
||||
|
||||
StrType* start = ptr;
|
||||
do {
|
||||
if ( ptr == endPtr ) { // Out of buffer space
|
||||
*buffer = '\0';
|
||||
return buffer;
|
||||
}
|
||||
*ptr++ = '0' + ( value % 10 );
|
||||
value /= 10;
|
||||
padding--;
|
||||
} while ( value > 0 );
|
||||
|
||||
while ( padding-- > 0 ) {
|
||||
if ( ptr == endPtr ) {
|
||||
*buffer = '\0';
|
||||
return buffer;
|
||||
}
|
||||
*ptr++ = padChar;
|
||||
}
|
||||
|
||||
StrType* reverseEnd = ptr - 1;
|
||||
while ( start < reverseEnd )
|
||||
std::swap( *start++, *reverseEnd-- );
|
||||
|
||||
*ptr = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
friend EE_API bool operator==( const String& left, const String& right );
|
||||
friend EE_API bool operator<( const String& left, const String& right );
|
||||
|
||||
@@ -56,6 +56,8 @@ class EE_API ActionManager {
|
||||
std::vector<Action*> mActionsRemoveList;
|
||||
mutable Mutex mMutex;
|
||||
std::atomic<bool> mUpdating;
|
||||
|
||||
void update( const Time& time, Action** actions, size_t count );
|
||||
};
|
||||
|
||||
}} // namespace EE::Scene
|
||||
|
||||
@@ -196,6 +196,8 @@ class EE_API UIAbstractTableView : public UIAbstractView {
|
||||
virtual UIWidget* updateCell( const Vector2<Int64>& posIndex, const ModelIndex& index,
|
||||
const size_t& indentLevel, const Float& yOffset );
|
||||
|
||||
virtual void updateTableCellData( UITableCell* cell, const ModelIndex& index );
|
||||
|
||||
virtual UIWidget* createCell( UIWidget* rowWidget, const ModelIndex& index );
|
||||
|
||||
virtual UIWidget* setupCell( UITableCell* widget, UIWidget* rowWidget,
|
||||
|
||||
@@ -141,8 +141,6 @@ class EE_API UITreeView : public UIAbstractTableView {
|
||||
const Float& )>
|
||||
TreeViewCallback;
|
||||
|
||||
void traverseTree( TreeViewCallback ) const;
|
||||
|
||||
mutable std::unordered_map<void*, MetadataForIndex> mViewMetadata;
|
||||
|
||||
virtual size_t getItemCount() const;
|
||||
@@ -168,6 +166,19 @@ class EE_API UITreeView : public UIAbstractTableView {
|
||||
virtual void onModelSelectionChange();
|
||||
|
||||
virtual void bindNavigationClick( UIWidget* widget );
|
||||
|
||||
struct TraverseTreeVars {
|
||||
UITreeView::TreeViewCallback callback;
|
||||
const Model& model;
|
||||
int indentLevel = 0;
|
||||
Float yOffset = 0;
|
||||
int rowIndex = -1;
|
||||
Float rowHeight = 0;
|
||||
};
|
||||
|
||||
IterationDecision traverseIndex( TraverseTreeVars& v, const ModelIndex& index ) const;
|
||||
|
||||
void traverseTree( TreeViewCallback ) const;
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -166,6 +166,8 @@ class EE_API UIWidget : public UINode {
|
||||
/** Resets all classes and assign a class */
|
||||
UIWidget* setClass( const std::string& cls );
|
||||
|
||||
UIWidget* setClass( std::string&& cls );
|
||||
|
||||
/** Resets all classes and assign vector of classes */
|
||||
UIWidget* setClasses( const std::vector<std::string>& classes );
|
||||
|
||||
@@ -177,13 +179,13 @@ class EE_API UIWidget : public UINode {
|
||||
|
||||
UIWidget* removeClasses( const std::vector<std::string>& classes );
|
||||
|
||||
bool hasClass( const std::string& cls ) const;
|
||||
bool hasClass( const std::string_view& cls ) const;
|
||||
|
||||
void toggleClass( const std::string& cls );
|
||||
|
||||
void setElementTag( const std::string& tag );
|
||||
|
||||
const std::vector<std::string> getClasses() const;
|
||||
const std::vector<std::string>& getClasses() const;
|
||||
|
||||
const std::string& getElementTag() const;
|
||||
|
||||
|
||||
@@ -9,6 +9,22 @@
|
||||
using namespace EE::System;
|
||||
using namespace EE::Window;
|
||||
|
||||
#ifdef EE_MEMORY_MANAGER
|
||||
|
||||
static bool sHasInit = false;
|
||||
/*
|
||||
void* operator new( std::size_t n ) {
|
||||
return malloc( n );
|
||||
}
|
||||
|
||||
void operator delete( void* p ) throw() {
|
||||
free( p );
|
||||
}
|
||||
*/
|
||||
#else
|
||||
static bool sHasInit = true;
|
||||
#endif
|
||||
|
||||
namespace EE {
|
||||
|
||||
static AllocatedPointerMap sMapPointers;
|
||||
@@ -26,9 +42,13 @@ AllocatedPointer::AllocatedPointer( void* data, const std::string& file, int lin
|
||||
mTrack = track;
|
||||
}
|
||||
|
||||
void* MemoryManager::allocate( size_t size ) { return malloc( size ); }
|
||||
void* MemoryManager::allocate( size_t size ) {
|
||||
return malloc( size );
|
||||
}
|
||||
|
||||
void* MemoryManager::reallocate( void* ptr, size_t size ) { return realloc( ptr, size ); }
|
||||
void* MemoryManager::reallocate( void* ptr, size_t size ) {
|
||||
return realloc( ptr, size );
|
||||
}
|
||||
|
||||
void* MemoryManager::addPointerInPlace( void* place, const AllocatedPointer& aAllocatedPointer ) {
|
||||
AllocatedPointerMapIt it = sMapPointers.find( place );
|
||||
@@ -41,7 +61,7 @@ void* MemoryManager::addPointerInPlace( void* place, const AllocatedPointer& aAl
|
||||
}
|
||||
|
||||
void* MemoryManager::addPointer( const AllocatedPointer& aAllocatedPointer ) {
|
||||
Lock l( sAlloMutex );
|
||||
ConditionalLock l( sHasInit, &sAlloMutex );
|
||||
|
||||
sMapPointers.insert(
|
||||
AllocatedPointerMap::value_type( aAllocatedPointer.mData, aAllocatedPointer ) );
|
||||
@@ -60,6 +80,10 @@ void* MemoryManager::addPointer( const AllocatedPointer& aAllocatedPointer ) {
|
||||
eePRINTL( "Allocating pointer %p at '%s' %d", aAllocatedPointer.mData,
|
||||
aAllocatedPointer.mFile.c_str(), aAllocatedPointer.mLine );
|
||||
|
||||
#ifdef EE_MEMORY_MANAGER
|
||||
sHasInit = true;
|
||||
#endif
|
||||
|
||||
return aAllocatedPointer.mData;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,23 +105,11 @@ bool ActionManager::removeActionsByTagFromTarget( Node* target, const Action::Un
|
||||
return !removeList.empty();
|
||||
}
|
||||
|
||||
void ActionManager::update( const Time& time ) {
|
||||
if ( isEmpty() )
|
||||
return;
|
||||
|
||||
void ActionManager::update( const Time& time, Action** actions, size_t count ) {
|
||||
std::vector<Action*> removeList;
|
||||
|
||||
mUpdating = true;
|
||||
|
||||
// Actions can be added during action updates, we need to only iterate the current actions
|
||||
std::vector<Action*> actions;
|
||||
{
|
||||
Lock l( mMutex );
|
||||
actions = mActions;
|
||||
}
|
||||
|
||||
for ( auto it = actions.begin(); it != actions.end(); ++it ) {
|
||||
Action* action = *it;
|
||||
for ( size_t i = 0; i < count; i++ ) {
|
||||
Action* action = actions[i];
|
||||
|
||||
if ( std::find( mActionsRemoveList.begin(), mActionsRemoveList.end(), action ) !=
|
||||
mActionsRemoveList.end() )
|
||||
@@ -147,6 +135,44 @@ void ActionManager::update( const Time& time ) {
|
||||
removeAction( *it );
|
||||
}
|
||||
|
||||
void ActionManager::update( const Time& time ) {
|
||||
if ( isEmpty() )
|
||||
return;
|
||||
|
||||
mUpdating = true;
|
||||
size_t size;
|
||||
|
||||
{
|
||||
Lock l( mMutex );
|
||||
size = mActions.size();
|
||||
}
|
||||
|
||||
// Micro-optimization to avoid heap allocations during updates (which are done usually at 60 hz)
|
||||
if ( size <= 8 ) {
|
||||
Action* actions[8];
|
||||
{
|
||||
Lock l( mMutex );
|
||||
std::copy( mActions.begin(), mActions.end(), actions );
|
||||
}
|
||||
update( time, actions, size );
|
||||
} else if ( size <= 16 ) {
|
||||
Action* actions[16];
|
||||
{
|
||||
Lock l( mMutex );
|
||||
std::copy( mActions.begin(), mActions.end(), actions );
|
||||
}
|
||||
update( time, actions, size );
|
||||
} else {
|
||||
// Actions can be added during action updates, we need to only iterate the current actions
|
||||
std::vector<Action*> actions;
|
||||
{
|
||||
Lock l( mMutex );
|
||||
actions = mActions;
|
||||
}
|
||||
update( time, actions.data(), size );
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t ActionManager::count() const {
|
||||
Lock l( mMutex );
|
||||
return mActions.size();
|
||||
|
||||
@@ -1227,7 +1227,7 @@ void Node::clearEventListener() {
|
||||
|
||||
void Node::sendEvent( const Event* event ) {
|
||||
if ( 0 != mEvents.count( event->getType() ) ) {
|
||||
std::map<Uint32, EventCallback> eventMap = mEvents[event->getType()];
|
||||
auto& eventMap = mEvents[event->getType()];
|
||||
if ( eventMap.begin() != eventMap.end() ) {
|
||||
std::map<Uint32, EventCallback>::iterator it;
|
||||
for ( it = eventMap.begin(); it != eventMap.end(); ++it ) {
|
||||
|
||||
@@ -582,47 +582,7 @@ UIWidget* UIAbstractTableView::updateCell( const Vector2<Int64>& posIndex, const
|
||||
widget->setPixelsPosition( { getColumnPosition( index.column() ).x, 0 } );
|
||||
if ( widget->isType( UI_TYPE_TABLECELL ) ) {
|
||||
UITableCell* cell = widget->asType<UITableCell>();
|
||||
cell->setCurIndex( index );
|
||||
|
||||
if ( getModel()->classModelRoleEnabled() ) {
|
||||
bool needsReloadStyle = false;
|
||||
Variant cls( getModel()->data( index, ModelRole::Class ) );
|
||||
cell->setLoadingState( true );
|
||||
if ( cls.isValid() ) {
|
||||
std::string clsStr( cls.toString() );
|
||||
needsReloadStyle = cell->getClasses().empty() || cell->getClasses().size() != 1 ||
|
||||
clsStr != cell->getClasses()[0];
|
||||
cell->setClass( clsStr );
|
||||
} else {
|
||||
needsReloadStyle = !cell->getClasses().empty();
|
||||
cell->resetClass();
|
||||
}
|
||||
cell->setLoadingState( false );
|
||||
if ( needsReloadStyle )
|
||||
cell->reportStyleStateChangeRecursive();
|
||||
}
|
||||
|
||||
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() );
|
||||
}
|
||||
}
|
||||
|
||||
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() );
|
||||
}
|
||||
updateTableCellData( cell, index );
|
||||
|
||||
bool isVisible = false;
|
||||
Variant icon( getModel()->data( index, ModelRole::Icon ) );
|
||||
@@ -650,6 +610,59 @@ UIWidget* UIAbstractTableView::updateCell( const Vector2<Int64>& posIndex, const
|
||||
return widget;
|
||||
}
|
||||
|
||||
void UIAbstractTableView::updateTableCellData( UITableCell* cell, const ModelIndex& index ) {
|
||||
cell->setCurIndex( index );
|
||||
|
||||
if ( getModel()->classModelRoleEnabled() ) {
|
||||
bool needsReloadStyle = false;
|
||||
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() );
|
||||
|
||||
needsReloadStyle =
|
||||
cell->getClasses().empty() || cell->getClasses().size() != 1 || !hasClass;
|
||||
|
||||
if ( !hasClass )
|
||||
cell->setClass( cls.toString() );
|
||||
} else {
|
||||
needsReloadStyle = !cell->getClasses().empty();
|
||||
cell->resetClass();
|
||||
}
|
||||
cell->setLoadingState( false );
|
||||
if ( needsReloadStyle )
|
||||
cell->reportStyleStateChangeRecursive();
|
||||
}
|
||||
|
||||
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() );
|
||||
}
|
||||
}
|
||||
|
||||
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() );
|
||||
}
|
||||
}
|
||||
|
||||
void UIAbstractTableView::moveSelection( int steps ) {
|
||||
if ( !getModel() )
|
||||
return;
|
||||
|
||||
@@ -3837,19 +3837,22 @@ void UICodeEditor::drawLineNumbers( const DocumentLineRange& lineRange, const Ve
|
||||
for ( int i = lineRange.first; i <= lineRange.second; i++ ) {
|
||||
if ( !mDocView.isLineVisible( i ) )
|
||||
continue;
|
||||
String pos;
|
||||
String::StringBaseType pos[16];
|
||||
|
||||
if ( mShowLinesRelativePosition && selection.start().line() != i ) {
|
||||
pos = String( String::toString( eeabs( i - selection.start().line() ) ) )
|
||||
.padLeft( lineNumberDigits, ' ' );
|
||||
String::intToStrBuf<int, String::StringBaseType>( eeabs( i - selection.start().line() ),
|
||||
pos, 16, lineNumberDigits, ' ' );
|
||||
} else {
|
||||
pos = String( String::toString( i + 1 ) ).padLeft( lineNumberDigits, ' ' );
|
||||
String::intToStrBuf<int, String::StringBaseType>( i + 1, pos, 16, lineNumberDigits,
|
||||
' ' );
|
||||
}
|
||||
|
||||
auto lnPos(
|
||||
Vector2f( screenStart.x + mLineNumberPaddingLeft,
|
||||
startScroll.y + mDocView.getLineYOffset( i, lineHeight ) + lineOffset ) );
|
||||
|
||||
if ( mShowLineNumber ) {
|
||||
Text::draw( pos, lnPos, mFontStyleConfig.Font, fontSize,
|
||||
Text::draw( String::View{ pos }, lnPos, mFontStyleConfig.Font, fontSize,
|
||||
( i >= selection.start().line() && i <= selection.end().line() )
|
||||
? mLineNumberActiveFontColor
|
||||
: mLineNumberFontColor,
|
||||
@@ -4343,6 +4346,19 @@ Rectf UICodeEditor::getMinimapRect( const Vector2f& start ) const {
|
||||
|
||||
static const SyntaxStyleType SYNTAX_NORMAL = SyntaxStyleTypes::Normal;
|
||||
|
||||
struct MinimapTextRangesVars {
|
||||
BatchRenderer* BR;
|
||||
Float minimapStart;
|
||||
Float charHeight;
|
||||
Int64 minimapStartLine;
|
||||
Float lineSpacing;
|
||||
Rectf rect;
|
||||
Float minimapCutoffX;
|
||||
Float widthScale;
|
||||
Int64 endidx;
|
||||
Int64 maxVisibleColumn;
|
||||
};
|
||||
|
||||
void UICodeEditor::drawMinimap( const Vector2f& start, const DocumentLineRange&,
|
||||
const DocumentViewLineRange& visibleLineRange ) {
|
||||
Float charHeight = PixelDensity::getPixelDensity() * mMinimapConfig.scale;
|
||||
@@ -4437,15 +4453,17 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const DocumentLineRange&,
|
||||
|
||||
Float minimapStart = rect.Left + gutterWidth;
|
||||
|
||||
const auto drawTextRanges = [this, &BR, &minimapStart, &charHeight, &minimapStartLine,
|
||||
&lineSpacing, &rect, &minimapCutoffX, &widthScale, &endidx,
|
||||
&maxVisibleColumn]( const TextRanges& ranges,
|
||||
const Color& backgroundColor,
|
||||
bool drawCompleteLine = false ) {
|
||||
BR->quadsSetColor( backgroundColor );
|
||||
// Avoid heap allocating the lambda
|
||||
MinimapTextRangesVars v{ BR, minimapStart, charHeight, minimapStartLine, lineSpacing,
|
||||
rect, minimapCutoffX, widthScale, endidx, maxVisibleColumn };
|
||||
|
||||
const auto drawMinimapTextRanges = [this, &v]( const TextRanges& ranges,
|
||||
const Color& backgroundColor,
|
||||
bool drawCompleteLine = false ) {
|
||||
v.BR->quadsSetColor( backgroundColor );
|
||||
Int64 lineSkip = -1;
|
||||
for ( const auto& range : ranges ) {
|
||||
if ( !mDocView.isWrapEnabled() && range.start().column() > maxVisibleColumn )
|
||||
if ( !mDocView.isWrapEnabled() && range.start().column() > v.maxVisibleColumn )
|
||||
continue;
|
||||
|
||||
auto rangeStart = mDocView.getVisibleLineRange( range.start() );
|
||||
@@ -4456,39 +4474,39 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const DocumentLineRange&,
|
||||
if ( rangeEnd.visibleIndex == VisibleIndex::invalid )
|
||||
continue;
|
||||
|
||||
if ( minimapStartLine > static_cast<Int64>( rangeEnd.visibleIndex ) ||
|
||||
endidx < static_cast<Int64>( rangeStart.visibleIndex ) )
|
||||
if ( v.minimapStartLine > static_cast<Int64>( rangeEnd.visibleIndex ) ||
|
||||
v.endidx < static_cast<Int64>( rangeStart.visibleIndex ) )
|
||||
continue;
|
||||
|
||||
if ( ranges.isSorted() && static_cast<Int64>( rangeStart.visibleIndex ) > endidx &&
|
||||
static_cast<Int64>( rangeEnd.visibleIndex ) > endidx )
|
||||
if ( ranges.isSorted() && static_cast<Int64>( rangeStart.visibleIndex ) > v.endidx &&
|
||||
static_cast<Int64>( rangeEnd.visibleIndex ) > v.endidx )
|
||||
break;
|
||||
|
||||
if ( lineSkip == static_cast<Int64>( rangeStart.visibleIndex ) )
|
||||
continue;
|
||||
|
||||
auto selRects = getTextRangeRectangles(
|
||||
range, { 0, rect.Top - minimapStartLine * lineSpacing }, {}, lineSpacing,
|
||||
DocumentViewLineRange{ static_cast<VisibleIndex>( minimapStartLine ),
|
||||
static_cast<VisibleIndex>( endidx ) } );
|
||||
range, { 0, v.rect.Top - v.minimapStartLine * v.lineSpacing }, {}, v.lineSpacing,
|
||||
DocumentViewLineRange{ static_cast<VisibleIndex>( v.minimapStartLine ),
|
||||
static_cast<VisibleIndex>( v.endidx ) } );
|
||||
|
||||
for ( auto& selRect : selRects ) {
|
||||
auto curLine = eefloor( selRect.Top / lineSpacing );
|
||||
auto curLine = eefloor( selRect.Top / v.lineSpacing );
|
||||
|
||||
if ( curLine == lineSkip )
|
||||
continue;
|
||||
|
||||
selRect.Bottom = selRect.Top + charHeight;
|
||||
selRect.Bottom = selRect.Top + v.charHeight;
|
||||
if ( drawCompleteLine ) {
|
||||
selRect.Left = minimapStart;
|
||||
selRect.Right = minimapStart + rect.getWidth();
|
||||
selRect.Left = v.minimapStart;
|
||||
selRect.Right = v.minimapStart + v.rect.getWidth();
|
||||
} else {
|
||||
selRect.Left = minimapStart + selRect.Left * widthScale;
|
||||
selRect.Right = minimapStart + selRect.Right * widthScale;
|
||||
selRect.Left = v.minimapStart + selRect.Left * v.widthScale;
|
||||
selRect.Right = v.minimapStart + selRect.Right * v.widthScale;
|
||||
}
|
||||
|
||||
if ( selRect.Left <= minimapCutoffX ) {
|
||||
BR->batchQuad( selRect );
|
||||
if ( selRect.Left <= v.minimapCutoffX ) {
|
||||
v.BR->batchQuad( selRect );
|
||||
} else {
|
||||
lineSkip = curLine;
|
||||
}
|
||||
@@ -4496,7 +4514,7 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const DocumentLineRange&,
|
||||
}
|
||||
};
|
||||
|
||||
auto drawWordMatch = [this, &drawTextRanges]( const String& text, const Int64& ln ) {
|
||||
auto drawWordMatch = [this, &drawMinimapTextRanges]( const String& text, const Int64& ln ) {
|
||||
size_t pos = 0;
|
||||
const String& line( mDoc->line( ln ).getText() );
|
||||
if ( line.size() > 300 )
|
||||
@@ -4507,7 +4525,8 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const DocumentLineRange&,
|
||||
Int64 startCol = pos;
|
||||
Int64 endCol = pos + text.size();
|
||||
TextRange range( { ln, startCol }, { ln, endCol } );
|
||||
drawTextRanges( { range }, Color( mMinimapHighlightColor ).blendAlpha( mAlpha ) );
|
||||
drawMinimapTextRanges( { range },
|
||||
Color( mMinimapHighlightColor ).blendAlpha( mAlpha ) );
|
||||
pos = endCol;
|
||||
} else {
|
||||
break;
|
||||
@@ -4538,7 +4557,8 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const DocumentLineRange&,
|
||||
|
||||
if ( !mHighlightWord.isEmpty() ) {
|
||||
Lock l( mHighlightWordCacheMutex );
|
||||
drawTextRanges( mHighlightWordCache, Color( mMinimapHighlightColor ).blendAlpha( mAlpha ) );
|
||||
drawMinimapTextRanges( mHighlightWordCache,
|
||||
Color( mMinimapHighlightColor ).blendAlpha( mAlpha ) );
|
||||
}
|
||||
|
||||
Int64 minimapStartDocLine =
|
||||
@@ -4552,7 +4572,7 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const DocumentLineRange&,
|
||||
for ( auto* plugin : mPlugins ) {
|
||||
plugin->minimapDrawBefore( this, docRange, docViewRange, { rect.Left, lineY },
|
||||
{ rect.getWidth(), charHeight }, charSpacing, gutterWidth,
|
||||
drawTextRanges );
|
||||
drawMinimapTextRanges );
|
||||
}
|
||||
|
||||
for ( Int64 line = minimapStartDocLine; line <= endDocIdx; line++ ) {
|
||||
@@ -4690,17 +4710,17 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const DocumentLineRange&,
|
||||
for ( auto* plugin : mPlugins ) {
|
||||
plugin->minimapDrawAfter( this, docRange, docViewRange, { rect.Left, lineY },
|
||||
{ rect.getWidth(), charHeight }, charSpacing, gutterWidth,
|
||||
drawTextRanges );
|
||||
drawMinimapTextRanges );
|
||||
}
|
||||
|
||||
if ( mHighlightTextRange.isValid() && mHighlightTextRange.hasSelection() ) {
|
||||
drawTextRanges( { mHighlightTextRange },
|
||||
Color( mMinimapSelectionColor ).blendAlpha( mAlpha ) );
|
||||
drawMinimapTextRanges( { mHighlightTextRange },
|
||||
Color( mMinimapSelectionColor ).blendAlpha( mAlpha ) );
|
||||
}
|
||||
|
||||
if ( mDoc->hasSelection() ) {
|
||||
drawTextRanges( mDoc->getSelectionsSorted(),
|
||||
Color( mMinimapSelectionColor ).blendAlpha( mAlpha ) );
|
||||
drawMinimapTextRanges( mDoc->getSelectionsSorted(),
|
||||
Color( mMinimapSelectionColor ).blendAlpha( mAlpha ) );
|
||||
}
|
||||
|
||||
for ( size_t i = 0; i < mDoc->getSelections().size(); ++i ) {
|
||||
|
||||
+49
-93
@@ -44,45 +44,44 @@ UITreeView::MetadataForIndex& UITreeView::getIndexMetadata( const ModelIndex& in
|
||||
return mViewMetadata[index.internalData()];
|
||||
}
|
||||
|
||||
UITreeView::IterationDecision UITreeView::traverseIndex( TraverseTreeVars& v,
|
||||
const ModelIndex& index ) const {
|
||||
if ( index.isValid() ) {
|
||||
const auto& metadata = getIndexMetadata( index );
|
||||
v.rowIndex++;
|
||||
IterationDecision decision = v.callback( v.rowIndex, index, v.indentLevel, v.yOffset );
|
||||
if ( decision == IterationDecision::Break || decision == IterationDecision::Stop )
|
||||
return decision;
|
||||
v.yOffset += v.rowHeight;
|
||||
if ( !metadata.open ) {
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
}
|
||||
if ( v.indentLevel >= 0 && !index.isValid() )
|
||||
return IterationDecision::Continue;
|
||||
++v.indentLevel;
|
||||
int rowCount = v.model.rowCount( index );
|
||||
for ( int i = 0; i < rowCount; ++i ) {
|
||||
IterationDecision decision =
|
||||
traverseIndex( v, v.model.index( i, v.model.treeColumn(), index ) );
|
||||
if ( decision == IterationDecision::Break || decision == IterationDecision::Stop )
|
||||
return decision;
|
||||
}
|
||||
--v.indentLevel;
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
void UITreeView::traverseTree( TreeViewCallback callback ) const {
|
||||
if ( !getModel() )
|
||||
return;
|
||||
Lock l( const_cast<Model*>( getModel() )->resourceMutex() );
|
||||
auto& model = *getModel();
|
||||
int indentLevel = 0;
|
||||
Float yOffset = getHeaderHeight();
|
||||
int rowIndex = -1;
|
||||
Float rowHeight = getRowHeight();
|
||||
std::function<IterationDecision( const ModelIndex& )> traverseIndex =
|
||||
[&]( const ModelIndex& index ) {
|
||||
if ( index.isValid() ) {
|
||||
const auto& metadata = getIndexMetadata( index );
|
||||
rowIndex++;
|
||||
IterationDecision decision = callback( rowIndex, index, indentLevel, yOffset );
|
||||
if ( decision == IterationDecision::Break || decision == IterationDecision::Stop )
|
||||
return decision;
|
||||
yOffset += rowHeight;
|
||||
if ( !metadata.open ) {
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
}
|
||||
if ( indentLevel >= 0 && !index.isValid() )
|
||||
return IterationDecision::Continue;
|
||||
++indentLevel;
|
||||
int rowCount = model.rowCount( index );
|
||||
for ( int i = 0; i < rowCount; ++i ) {
|
||||
IterationDecision decision =
|
||||
traverseIndex( model.index( i, model.treeColumn(), index ) );
|
||||
if ( decision == IterationDecision::Break || decision == IterationDecision::Stop )
|
||||
return decision;
|
||||
}
|
||||
--indentLevel;
|
||||
return IterationDecision::Continue;
|
||||
};
|
||||
int rootCount = model.rowCount();
|
||||
TraverseTreeVars v{
|
||||
std::move( callback ), *getModel(), 0, getHeaderHeight(), -1, getRowHeight(),
|
||||
};
|
||||
int rootCount = v.model.rowCount();
|
||||
for ( int rootIndex = 0; rootIndex < rootCount; ++rootIndex ) {
|
||||
IterationDecision decision =
|
||||
traverseIndex( model.index( rootIndex, model.treeColumn(), ModelIndex() ) );
|
||||
traverseIndex( v, v.model.index( rootIndex, v.model.treeColumn(), ModelIndex() ) );
|
||||
if ( decision == IterationDecision::Break || decision == IterationDecision::Stop )
|
||||
break;
|
||||
}
|
||||
@@ -238,54 +237,7 @@ UIWidget* UITreeView::updateCell( const Vector2<Int64>& posIndex, const ModelInd
|
||||
widget->setPixelsPosition( { getColumnPosition( index.column() ).x, 0 } );
|
||||
if ( widget->isType( UI_TYPE_TABLECELL ) ) {
|
||||
UITableCell* cell = widget->asType<UITableCell>();
|
||||
cell->setCurIndex( index );
|
||||
|
||||
if ( getModel()->classModelRoleEnabled() ) {
|
||||
bool needsReloadStyle = false;
|
||||
Variant cls( getModel()->data( index, ModelRole::Class ) );
|
||||
cell->setLoadingState( true );
|
||||
if ( cls.isValid() ) {
|
||||
std::string clsStr( cls.toString() );
|
||||
needsReloadStyle = cell->getClasses().empty() || cell->getClasses().size() != 1 ||
|
||||
clsStr != cell->getClasses()[0];
|
||||
cell->setClass( clsStr );
|
||||
} else {
|
||||
needsReloadStyle = !cell->getClasses().empty();
|
||||
cell->resetClass();
|
||||
}
|
||||
cell->setLoadingState( false );
|
||||
if ( needsReloadStyle )
|
||||
cell->reportStyleStateChangeRecursive();
|
||||
}
|
||||
|
||||
if ( getModel()->tooltipModelRoleEnabled() ) {
|
||||
Variant cls( getModel()->data( index, ModelRole::TooltipClass ) );
|
||||
if ( cls.isValid() ) {
|
||||
cell->createTooltip()->setClass( cls.toString() );
|
||||
} else {
|
||||
cell->createTooltip()->resetClass();
|
||||
}
|
||||
|
||||
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() );
|
||||
}
|
||||
}
|
||||
|
||||
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() );
|
||||
}
|
||||
updateTableCellData( cell, index );
|
||||
|
||||
bool hasChilds = false;
|
||||
|
||||
@@ -373,22 +325,26 @@ Sizef UITreeView::getContentSize() const {
|
||||
return mContentSize;
|
||||
}
|
||||
|
||||
void UITreeView::drawChilds() {
|
||||
struct DrawTraverseTreeVars {
|
||||
UITreeView* tree;
|
||||
int realRowIndex = 0;
|
||||
int realColIndex = 0;
|
||||
Float rowHeight = getRowHeight();
|
||||
Float rowHeight = 0;
|
||||
};
|
||||
|
||||
traverseTree( [this, &realRowIndex, &realColIndex,
|
||||
rowHeight]( const int&, const ModelIndex& index, const size_t& indentLevel,
|
||||
const Float& yOffset ) {
|
||||
void UITreeView::drawChilds() {
|
||||
DrawTraverseTreeVars v{ this, 0, 0, getRowHeight() }; // To avoid allocating the lambda
|
||||
|
||||
traverseTree( [this, &v]( 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 )
|
||||
if ( yOffset - mScrollOffset.y + v.rowHeight < 0 )
|
||||
return IterationDecision::Continue;
|
||||
Float xOffset = 0;
|
||||
UITableRow* rowNode = updateRow( realRowIndex, index, yOffset );
|
||||
UITableRow* rowNode = updateRow( v.realRowIndex, index, yOffset );
|
||||
rowNode->setChildsVisibility( false, false );
|
||||
realColIndex = 0;
|
||||
v.realColIndex = 0;
|
||||
for ( size_t colIndex = 0; colIndex < getModel()->columnCount(); colIndex++ ) {
|
||||
auto& colData = columnData( colIndex );
|
||||
if ( !colData.visible || ( xOffset + colData.width ) - mScrollOffset.x < 0 ) {
|
||||
@@ -400,22 +356,22 @@ void UITreeView::drawChilds() {
|
||||
break;
|
||||
xOffset += colData.width;
|
||||
if ( (Int64)colIndex != index.column() ) {
|
||||
updateCell( { realColIndex, realRowIndex },
|
||||
updateCell( { v.realColIndex, v.realRowIndex },
|
||||
getModel()->index( index.row(), colIndex, index.parent() ), indentLevel,
|
||||
yOffset );
|
||||
} else {
|
||||
auto* cell =
|
||||
updateCell( { realColIndex, realRowIndex }, index, indentLevel, yOffset );
|
||||
updateCell( { v.realColIndex, v.realRowIndex }, index, indentLevel, yOffset );
|
||||
|
||||
if ( mFocusSelectionDirty && index == getSelection().first() ) {
|
||||
cell->setFocus();
|
||||
mFocusSelectionDirty = false;
|
||||
}
|
||||
}
|
||||
realColIndex++;
|
||||
v.realColIndex++;
|
||||
}
|
||||
rowNode->nodeDraw();
|
||||
realRowIndex++;
|
||||
v.realRowIndex++;
|
||||
return IterationDecision::Continue;
|
||||
} );
|
||||
|
||||
|
||||
@@ -863,6 +863,26 @@ UIWidget* UIWidget::setClass( const std::string& cls ) {
|
||||
return this;
|
||||
}
|
||||
|
||||
UIWidget* UIWidget::setClass( std::string&& cls ) {
|
||||
size_t oldClassesCount = mClasses.size();
|
||||
if ( mClasses.size() != 1 || mClasses[0] != cls ) {
|
||||
bool isSet = false;
|
||||
mClasses.clear();
|
||||
if ( !cls.empty() ) {
|
||||
mClasses.emplace_back( std::move( cls ) );
|
||||
isSet = true;
|
||||
|
||||
if ( !isSceneNodeLoading() && !isLoadingState() ) {
|
||||
getUISceneNode()->invalidateStyle( this );
|
||||
getUISceneNode()->invalidateStyleState( this );
|
||||
}
|
||||
}
|
||||
if ( oldClassesCount != mClasses.size() || isSet )
|
||||
onClassChange();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
UIWidget* UIWidget::setClasses( const std::vector<std::string>& classes ) {
|
||||
if ( mClasses != classes ) {
|
||||
mClasses = classes;
|
||||
@@ -949,7 +969,7 @@ UIWidget* UIWidget::removeClasses( const std::vector<std::string>& classes ) {
|
||||
return this;
|
||||
}
|
||||
|
||||
bool UIWidget::hasClass( const std::string& cls ) const {
|
||||
bool UIWidget::hasClass( const std::string_view& cls ) const {
|
||||
return std::find( mClasses.begin(), mClasses.end(), cls ) != mClasses.end();
|
||||
}
|
||||
|
||||
@@ -993,7 +1013,7 @@ void UIWidget::setElementTag( const std::string& tag ) {
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string> UIWidget::getClasses() const {
|
||||
const std::vector<std::string>& UIWidget::getClasses() const {
|
||||
return mClasses;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,23 +46,27 @@ TerminalKeyMap::TerminalKeyMap( const TerminalKey keys[], size_t keysLen,
|
||||
const TerminalShortcut shortcuts[], size_t shortcutsLen,
|
||||
const TerminalMouseShortcut mouseShortcuts[],
|
||||
size_t mouseShortcutsLen ) {
|
||||
mKeyMap.reserve( keysLen );
|
||||
for ( size_t i = 0; i < keysLen; i++ ) {
|
||||
auto& e = mKeyMap[keys[i].keysym];
|
||||
e.push_back( { keys[i].mask, keys[i].string, keys[i].appkey, keys[i].appcursor } );
|
||||
}
|
||||
|
||||
mPlatformKeyMap.reserve( platformKeysLen );
|
||||
for ( size_t i = 0; i < platformKeysLen; i++ ) {
|
||||
auto& e = mPlatformKeyMap[platformKeys[i].scancode];
|
||||
e.push_back( { platformKeys[i].mask, platformKeys[i].string, platformKeys[i].appkey,
|
||||
platformKeys[i].appcursor } );
|
||||
}
|
||||
|
||||
mShortcuts.reserve( shortcutsLen );
|
||||
for ( size_t i = 0; i < shortcutsLen; i++ ) {
|
||||
auto& e = mShortcuts[shortcuts[i].keysym];
|
||||
e.push_back( { shortcuts[i].mask, shortcuts[i].action, shortcuts[i].appkey,
|
||||
shortcuts[i].appcursor, shortcuts[i].altscrn } );
|
||||
}
|
||||
|
||||
mMouseShortcuts.reserve( mouseShortcutsLen );
|
||||
for ( size_t i = 0; i < mouseShortcutsLen; i++ ) {
|
||||
auto& e = mMouseShortcuts[mouseShortcuts[i].button];
|
||||
e.push_back( { mouseShortcuts[i].mask, mouseShortcuts[i].action, mouseShortcuts[i].appkey,
|
||||
|
||||
@@ -258,8 +258,11 @@ void GitPlugin::initModelStyler() {
|
||||
auto model = static_cast<const FileSystemModel*>( index.model() );
|
||||
auto node = static_cast<const FileSystemModel::Node*>( data );
|
||||
Lock l( mGitStatusFileCacheMutex );
|
||||
auto found =
|
||||
mGitStatusFilesCache.find( std::string{ model->getNodeRelativePath( node ) } );
|
||||
std::string_view nodePath = model->getNodeRelativePath( node );
|
||||
auto found = std::find_if( mGitStatusFilesCache.begin(), mGitStatusFilesCache.end(),
|
||||
[&nodePath]( const std::string& key ) {
|
||||
return std::string_view{ key } == nodePath;
|
||||
} );
|
||||
if ( found != mGitStatusFilesCache.end() )
|
||||
return Variant( STYLE_MODIFIED );
|
||||
return Variant( STYLE_NONE );
|
||||
|
||||
@@ -1905,12 +1905,9 @@ void LSPClientPlugin::drawTop( UICodeEditor* editor, const Vector2f& screenStart
|
||||
|
||||
Float fontSize = editor->getUISceneNode()->getUIThemeManager()->getDefaultFontSize();
|
||||
|
||||
bool isPath = true;
|
||||
std::string path( editor->getDocument().getFilePath() );
|
||||
if ( path.empty() ) {
|
||||
path = editor->getDocument().getFilename();
|
||||
isPath = false;
|
||||
}
|
||||
bool isPath = !editor->getDocument().getFilePath().empty();
|
||||
std::string path( !isPath ? editor->getDocument().getFilename()
|
||||
: editor->getDocument().getFilePath() );
|
||||
|
||||
Color textColor( editor->getColorScheme().getEditorColor(
|
||||
mHoveringBreadcrumb == editor ? SyntaxStyleTypes::Text : SyntaxStyleTypes::LineNumber2 ) );
|
||||
|
||||
Reference in New Issue
Block a user