Rename TextLayouter and move the static functions to TextLayout.

Several fixes in AI Assistant chat UI.
TextLayout cache is now returned as a shared pointer to avoid copying the struct each time.
Some minor changes in FontTrueType and FontManager.
ShapedGlyph now stores the TextDirection of each glyph.
This commit is contained in:
Martín Lucas Golini
2025-11-17 00:23:58 -03:00
parent c704e00833
commit e3ca29842f
16 changed files with 228 additions and 133 deletions

View File

@@ -0,0 +1 @@
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 😀

View File

@@ -47,6 +47,8 @@ class EE_API FontManager : public ResourceManager<Font> {
void setAntialiasing( FontAntialiasing antialiasing );
Font* getByInternalId( Uint32 internalId ) const;
protected:
Font* mColorEmojiFont{ nullptr };
Font* mEmojiFont{ nullptr };

View File

@@ -9,12 +9,21 @@ namespace EE::Graphics {
class FontTrueType;
enum class TextDirection : Uint8 {
Unspecified = 0, //!< Unspecified
LeftToRight = 4, //!< Left-to-right
RightToLeft, //!< Right-to-left
TopToBottom, //!< Top-to-bottom
BottomToTop //!< Bottom-to-top
};
struct ShapedGlyph {
FontTrueType* font{ nullptr };
Uint32 glyphIndex{ 0 };
Uint32 stringIndex{ 0 };
Vector2f position;
Vector2f advance;
TextDirection direction;
};
} // namespace EE::Graphics

View File

@@ -4,7 +4,7 @@
#include <eepp/graphics/font.hpp>
#include <eepp/graphics/fontstyleconfig.hpp>
#include <eepp/graphics/pixeldensity.hpp>
#include <eepp/graphics/textlayouter.hpp>
#include <eepp/graphics/textlayout.hpp>
#include <eepp/graphics/texttransform.hpp>
#include <optional>

View File

@@ -0,0 +1,44 @@
#pragma once
#include <eepp/core/string.hpp>
#include <eepp/graphics/shapedglyph.hpp>
#include <eepp/math/size.hpp>
#include <memory>
#include <optional>
#include <vector>
namespace EE::Graphics {
class Font;
class EE_API TextLayout {
public:
using Cache = std::shared_ptr<const TextLayout>;
std::vector<ShapedGlyph> shapedGlyphs;
std::vector<Float> linesWidth;
Sizef size;
TextDirection direction{ TextDirection::Unspecified };
bool isRTL() const { return direction == TextDirection::RightToLeft; }
static Cache layout( const String& string, Font* font, const Uint32& fontSize,
const Uint32& style, const Uint32& tabWidth = 4,
const Float& outlineThickness = 0.f, std::optional<Float> tabOffset = {},
Uint32 textDrawHints = 0 );
static Cache layout( const String::View& string, Font* font, const Uint32& fontSize,
const Uint32& style, const Uint32& tabWidth = 4,
const Float& outlineThickness = 0.f, std::optional<Float> tabOffset = {},
Uint32 textDrawHints = 0 );
protected:
template <typename StringType>
static Cache layout( const StringType& string, Font* font, const Uint32& fontSize,
const Uint32& style, const Uint32& tabWidth = 4,
const Float& outlineThickness = 0.f, std::optional<Float> tabOffset = {},
Uint32 textDrawHints = 0 );
};
} // namespace EE::Graphics

View File

@@ -1,41 +0,0 @@
#pragma once
#include <eepp/core/string.hpp>
#include <eepp/graphics/shapedglyph.hpp>
#include <eepp/math/size.hpp>
#include <optional>
#include <vector>
namespace EE::Graphics {
class Font;
struct TextLayout {
std::vector<ShapedGlyph> shapedGlyphs;
std::vector<Float> linesWidth;
Sizef size;
bool isRTL{ false };
};
class EE_API TextLayouter {
public:
static TextLayout layout( const String& string, Font* font, const Uint32& fontSize,
const Uint32& style, const Uint32& tabWidth = 4,
const Float& outlineThickness = 0.f,
std::optional<Float> tabOffset = {}, Uint32 textDrawHints = 0 );
static TextLayout layout( const String::View& string, Font* font, const Uint32& fontSize,
const Uint32& style, const Uint32& tabWidth = 4,
const Float& outlineThickness = 0.f,
std::optional<Float> tabOffset = {}, Uint32 textDrawHints = 0 );
protected:
template <typename StringType>
static TextLayout layout( const StringType& string, Font* font, const Uint32& fontSize,
const Uint32& style, const Uint32& tabWidth = 4,
const Float& outlineThickness = 0.f,
std::optional<Float> tabOffset = {}, Uint32 textDrawHints = 0 );
};
} // namespace EE::Graphics

View File

@@ -174,7 +174,7 @@ class EE_API Http : NonCopyable {
///< target resource.
Patch, ///< The PATCH method is used to apply partial modifications to a resource.
Connect ///< The CONNECT method starts two-way communications with the requested
///< resource. It can be used to open a tunnel.
///< resource. It can be used to open a tunnel.
};
/** @brief Enumerate the available states for a request */
@@ -315,9 +315,22 @@ class EE_API Http : NonCopyable {
/** Sets a progress callback */
void setProgressCallback( const ProgressCallback& progressCallback );
/** Definition of the cancel callback
* @param http The http client
* @param request The http request
*/
typedef std::function<void( const Http& http, const Http::Request& request )>
CancelCallback;
/** Sets a cancel callback */
void setCancelCallback( const CancelCallback& cancelCb );
/** Get the progress callback */
const ProgressCallback& getProgressCallback() const;
/** Get the cancel callback */
const CancelCallback& getCancelCallback() const;
/** Cancels the current request if being processed */
void cancel();
@@ -375,6 +388,7 @@ class EE_API Http : NonCopyable {
mutable bool mCancel; ///< Cancel state of current request
bool mVerbose{ false }; ///< Enable/Disable verbosity
ProgressCallback mProgressCallback; ///< Progress callback
CancelCallback mCancelCallback; ///< Cancel callback
unsigned int mMaxRedirections; ///< Maximum number of redirections allowed
mutable unsigned int mRedirectionCount; ///< Number of redirections followed by the request
URI mProxy; ///< Proxy information
@@ -477,7 +491,7 @@ class EE_API Http : NonCopyable {
/** @brief Sends the request and creates a new thread, when got the response informs the result
** to the callback. * This function does not lock the caller thread.
** @see sendRequest
** @return Unique Id of the request added */
** @return Unique Id of the request added */
Uint64 sendAsyncRequest( const AsyncResponseCallback& cb, const Http::Request& request,
Time timeout = Time::Zero );
@@ -759,6 +773,8 @@ class EE_API Http : NonCopyable {
void removeAsyncRequest( AsyncRequest* req );
Request prepareFields( const Http::Request& request );
void onCancel( const Http::Request& request );
};
}} // namespace EE::Network

View File

@@ -9,9 +9,9 @@ FontManager::FontManager() {}
FontManager::~FontManager() {}
Graphics::Font* FontManager::add( Graphics::Font* Font ) {
eeASSERT( NULL != Font );
return ResourceManager<Graphics::Font>::add( Font );
Graphics::Font* FontManager::add( Graphics::Font* font ) {
eeASSERT( NULL != font );
return ResourceManager<Graphics::Font>::add( font );
}
void FontManager::setColorEmojiFont( Font* font ) {
@@ -88,4 +88,13 @@ void FontManager::setAntialiasing( FontAntialiasing antialiasing ) {
}
}
Font* FontManager::getByInternalId( Uint32 internalId ) const {
for ( auto [_, font] : mResources ) {
if ( font->getType() == FontType::TTF &&
static_cast<FontTrueType*>( font )->getFontInternalId() == internalId )
return font;
}
return nullptr;
}
}} // namespace EE::Graphics

View File

@@ -1207,21 +1207,8 @@ Glyph FontTrueType::loadGlyphByIndex( Uint32 index, unsigned int characterSize,
Uint8* current = pixelPtr;
Uint8* end = current + bufferSize;
if ( bitmap.pixel_mode == FT_PIXEL_MODE_LCD ) {
while ( current != end ) {
( *current++ ) = 0;
( *current++ ) = 0;
( *current++ ) = 0;
( *current++ ) = 0;
}
} else {
while ( current != end ) {
( *current++ ) = 255;
( *current++ ) = 255;
( *current++ ) = 255;
( *current++ ) = 0;
}
}
std::fill( (Uint32*)pixelPtr, (Uint32*)end,
bitmap.pixel_mode == FT_PIXEL_MODE_LCD ? 0x00000000 : 0x00FFFFFF );
// Extract the glyph's pixels from the bitmap
const Uint8* pixels = bitmap.buffer;

View File

@@ -302,10 +302,10 @@ Sizef Text::draw( const StringType& string, const Vector2f& pos, Font* font, Flo
!canSkipShaping( textDrawHints ) ) {
FontTrueType* rFont = static_cast<FontTrueType*>( font );
auto layout = TextLayouter::layout( string, rFont, fontSize, style, tabWidth,
auto layout = TextLayout::layout( string, rFont, fontSize, style, tabWidth,
outlineThickness, tabOffset, textDrawHints );
for ( const ShapedGlyph& sg : layout.shapedGlyphs ) {
for ( const ShapedGlyph& sg : layout->shapedGlyphs ) {
auto ch = string[sg.stringIndex];
auto gpos( ( sg.position + pos ).trunc() );
@@ -373,7 +373,7 @@ Sizef Text::draw( const StringType& string, const Vector2f& pos, Font* font, Flo
BR->drawOpt();
return layout.size;
return layout->size;
}
#endif
@@ -911,9 +911,9 @@ Float Text::getTextWidth( Font* font, const Uint32& fontSize, const StringType&
#ifdef EE_TEXT_SHAPER_ENABLED
if ( TextShaperEnabled && font->getType() == FontType::TTF &&
!canSkipShaping( textDrawHints ) ) {
return TextLayouter::layout( string, static_cast<FontTrueType*>( font ), fontSize, style,
return TextLayout::layout( string, static_cast<FontTrueType*>( font ), fontSize, style,
tabWidth, outlineThickness, tabOffset, textDrawHints )
.size.getWidth();
->size.getWidth();
}
#endif
@@ -956,10 +956,10 @@ std::size_t Text::findLastCharPosWithinLength( Font* font, const Uint32& fontSiz
#ifdef EE_TEXT_SHAPER_ENABLED
if ( TextShaperEnabled && font->getType() == FontType::TTF && !canSkipShaping( textHints ) ) {
auto layout =
TextLayouter::layout( string, static_cast<FontTrueType*>( font ), fontSize, style,
TextLayout::layout( string, static_cast<FontTrueType*>( font ), fontSize, style,
tabWidth, outlineThickness, tabOffset /* , textDrawHints */ );
size_t lastStringIndex = 0;
for ( const ShapedGlyph& sg : layout.shapedGlyphs ) {
for ( const ShapedGlyph& sg : layout->shapedGlyphs ) {
Glyph metrics =
sg.font->getGlyphByIndex( sg.glyphIndex, fontSize, bold, italic, outlineThickness );
if ( sg.position.x + metrics.advance > maxWidth )
@@ -1014,7 +1014,7 @@ Vector2f Text::findCharacterPos( std::size_t index, Font* font, const Uint32& fo
#ifdef EE_TEXT_SHAPER_ENABLED
if ( TextShaperEnabled && font->getType() == FontType::TTF &&
!canSkipShaping( textDrawHints ) ) {
auto layout = TextLayouter::layout( string, font, fontSize, style, tabWidth,
auto layout = TextLayout::layout( string, font, fontSize, style, tabWidth,
outlineThickness, tabOffset );
Uint32 maxStringIndex = 0;
Uint32 closestDist = std::numeric_limits<Uint32>::max();
@@ -1022,7 +1022,7 @@ Vector2f Text::findCharacterPos( std::size_t index, Font* font, const Uint32& fo
const ShapedGlyph* msg = nullptr;
const ShapedGlyph* csg = nullptr;
for ( const ShapedGlyph& sg : layout.shapedGlyphs ) {
for ( const ShapedGlyph& sg : layout->shapedGlyphs ) {
if ( sg.stringIndex >= maxStringIndex ) {
maxStringIndex = std::max( maxStringIndex, sg.stringIndex );
msg = &sg;
@@ -1035,13 +1035,14 @@ Vector2f Text::findCharacterPos( std::size_t index, Font* font, const Uint32& fo
}
if ( sg.stringIndex == index ) {
if ( layout.isRTL )
if ( layout->isRTL() )
return ( sg.position + sg.advance ).trunc();
return sg.position.trunc();
}
}
if ( !layout.shapedGlyphs.empty() && !layout.isRTL && index >= maxStringIndex + 1 && msg ) {
if ( !layout->shapedGlyphs.empty() && !layout->isRTL() && index >= maxStringIndex + 1 &&
msg ) {
Glyph metrics = msg->font->getGlyphByIndex( msg->glyphIndex, fontSize, bold, italic,
outlineThickness );
if ( string[msg->stringIndex] == '\t' ) {
@@ -1128,18 +1129,18 @@ Int32 Text::findCharacterFromPos( const Vector2i& pos, bool returnNearest, Font*
#ifdef EE_TEXT_SHAPER_ENABLED
if ( TextShaperEnabled && font->getType() == FontType::TTF &&
!canSkipShaping( textDrawHints ) ) {
auto layout = TextLayouter::layout( string, font, fontSize, style, tabWidth,
auto layout = TextLayout::layout( string, font, fontSize, style, tabWidth,
outlineThickness, tabOffset );
auto sgs = layout.shapedGlyphs.size();
auto sgs = layout->shapedGlyphs.size();
if ( sgs == 0 )
return 0;
if ( pos.x < 0 )
return layout.isRTL ? tSize : 0;
return layout->isRTL() ? tSize : 0;
for ( size_t i = 0; i < sgs; i++ ) {
const ShapedGlyph* sg = &layout.shapedGlyphs[i];
const ShapedGlyph* sg = &layout->shapedGlyphs[i];
Float charLeft = sg->position.x;
Float charTop = sg->position.y;
@@ -1148,31 +1149,31 @@ Int32 Text::findCharacterFromPos( const Vector2i& pos, bool returnNearest, Font*
auto curStringIndex = sg->stringIndex;
// Expand bounds over the whole cluster (multiple glyphs for one string index)
while ( i + 1 < sgs && layout.shapedGlyphs[i + 1].stringIndex == curStringIndex ) {
while ( i + 1 < sgs && layout->shapedGlyphs[i + 1].stringIndex == curStringIndex ) {
i++;
sg = &layout.shapedGlyphs[i];
sg = &layout->shapedGlyphs[i];
charBottom = sg->position.y + vspace;
charRight = sg->position.x + sg->advance.x;
};
bool isLastOnLine =
i + 1 == sgs ||
( !layout.isRTL && layout.shapedGlyphs[i + 1].position.y != sg->position.y );
( !layout->isRTL() && layout->shapedGlyphs[i + 1].position.y != sg->position.y );
if ( fpos.y >= charTop && fpos.y <= charBottom ) {
auto findNextInsertionIndex = [&]() -> Int32 {
if ( layout.isRTL ) {
if ( layout->isRTL() ) {
if ( i > 0 ) {
for ( Int64 j = (Int64)i - 1; j >= 0; j-- ) {
if ( layout.shapedGlyphs[j].stringIndex > sg->stringIndex )
return layout.shapedGlyphs[j].stringIndex;
if ( layout->shapedGlyphs[j].stringIndex > sg->stringIndex )
return layout->shapedGlyphs[j].stringIndex;
}
}
return 0;
} else {
for ( size_t j = i + 1; j < sgs; ++j ) {
if ( layout.shapedGlyphs[j].stringIndex > sg->stringIndex )
return layout.shapedGlyphs[j].stringIndex;
if ( layout->shapedGlyphs[j].stringIndex > sg->stringIndex )
return layout->shapedGlyphs[j].stringIndex;
}
return tSize;
}
@@ -1310,11 +1311,11 @@ void Text::updateWidthCache() {
#ifdef EE_TEXT_SHAPER_ENABLED
if ( TextShaperEnabled && mFontStyleConfig.Font->getType() == FontType::TTF &&
!canSkipShaping( mTextHints ) ) {
auto layout = TextLayouter::layout( mString, mFontStyleConfig.Font,
auto layout = TextLayout::layout( mString, mFontStyleConfig.Font,
mFontStyleConfig.CharacterSize, mFontStyleConfig.Style,
mTabWidth, mFontStyleConfig.OutlineThickness );
mLinesWidth = std::move( layout.linesWidth );
mCachedWidth = layout.size.getWidth();
mLinesWidth = layout->linesWidth;
mCachedWidth = layout->size.getWidth();
return;
}
#endif
@@ -1627,14 +1628,14 @@ void Text::ensureGeometryUpdate() {
if ( TextShaperEnabled && mFontStyleConfig.Font->getType() == FontType::TTF &&
!canSkipShaping( mTextHints ) ) {
FontTrueType* rFont = static_cast<FontTrueType*>( mFontStyleConfig.Font );
auto layout = TextLayouter::layout( mString, rFont, mFontStyleConfig.CharacterSize,
auto layout = TextLayout::layout( mString, rFont, mFontStyleConfig.CharacterSize,
mFontStyleConfig.Style, mTabWidth,
mFontStyleConfig.OutlineThickness );
mLinesWidth = std::move( layout.linesWidth );
mCachedWidth = layout.size.getWidth();
mLinesWidth = layout->linesWidth;
mCachedWidth = layout->size.getWidth();
for ( const ShapedGlyph& sg : layout.shapedGlyphs ) {
for ( const ShapedGlyph& sg : layout->shapedGlyphs ) {
if ( mString[sg.stringIndex] == '\t' )
continue;

View File

@@ -1,7 +1,7 @@
#include <eepp/core/lrucache.hpp>
#include <eepp/graphics/fonttruetype.hpp>
#include <eepp/graphics/text.hpp>
#include <eepp/graphics/textlayouter.hpp>
#include <eepp/graphics/textlayout.hpp>
#include <eepp/graphics/textshaperun.hpp>
#ifdef EE_TEXT_SHAPER_ENABLED
@@ -13,7 +13,7 @@
namespace EE::Graphics {
using LRULayoutCache = LRUCache<2048, Uint64, TextLayout>;
using LRULayoutCache = LRUCache<2048, Uint64, TextLayout::Cache>;
#ifdef EE_TEXT_SHAPER_ENABLED
@@ -110,8 +110,10 @@ static void segmentString( TextLayout& result, String::View input, Callable cb )
SBScriptLocatorReset( scriptLocator );
}
if ( runCount > 0 && paragraphOffset == 0 )
result.isRTL = ( runArray[0].level % 2 ) != 0;
if ( runCount > 0 && paragraphOffset == 0 ) {
result.direction = ( runArray[0].level % 2 ) != 0 ? TextDirection::RightToLeft
: TextDirection::LeftToRight;
}
SBLineRelease( line );
SBParagraphRelease( paragraph );
@@ -205,16 +207,16 @@ static inline Uint64 textLayoutHash( const StringType& string, Font* font,
}
template <typename StringType>
TextLayout TextLayouter::layout( const StringType& string, Font* font, const Uint32& characterSize,
const Uint32& style, const Uint32& tabWidth,
const Float& outlineThickness, std::optional<Float> tabOffset,
Uint32 textDrawHints ) {
TextLayout::Cache TextLayout::layout( const StringType& string, Font* font,
const Uint32& characterSize, const Uint32& style,
const Uint32& tabWidth, const Float& outlineThickness,
std::optional<Float> tabOffset, Uint32 textDrawHints ) {
static LRULayoutCache sLayoutCache;
TextLayout result;
if ( !font || string.empty() ) {
result.size = { 0.f, font ? (Float)font->getFontHeight( characterSize ) : 0.f };
return result;
auto layout = std::make_shared<TextLayout>();
layout->size = { 0.f, font ? (Float)font->getFontHeight( characterSize ) : 0.f };
return layout;
}
Uint64 hash = 0;
@@ -235,6 +237,9 @@ TextLayout TextLayouter::layout( const StringType& string, Font* font, const Uin
Vector2f pen;
Float maxWidth = 0;
auto resultPtr = std::make_shared<TextLayout>();
TextLayout& result = *resultPtr;
#ifdef EE_TEXT_SHAPER_ENABLED
if ( Text::TextShaperEnabled && font->getType() == FontType::TTF &&
!Text::canSkipShaping( textDrawHints ) ) {
@@ -269,6 +274,7 @@ TextLayout TextLayouter::layout( const StringType& string, Font* font, const Uin
sg.stringIndex = segment.offset + run.pos() + cluster;
sg.position = pen;
sg.advance = { advance, 0 };
sg.direction = (TextDirection)segment.direction;
result.shapedGlyphs.emplace_back( std::move( sg ) );
pen.x += advance;
@@ -290,6 +296,7 @@ TextLayout TextLayouter::layout( const StringType& string, Font* font, const Uin
sg.glyphIndex = glyphInfo[i].codepoint;
sg.stringIndex = segment.offset + run.pos() + glyphInfo[i].cluster;
sg.advance = { currentGlyph.advance, 0 };
sg.direction = (TextDirection)segment.direction;
sg.position.x = pen.x + ( glyphPos[i].x_offset / 64.f );
sg.position.y = pen.y - ( glyphPos[i].y_offset / 64.f );
result.shapedGlyphs.emplace_back( std::move( sg ) );
@@ -311,6 +318,7 @@ TextLayout TextLayouter::layout( const StringType& string, Font* font, const Uin
sg.glyphIndex = glyphInfo[i].codepoint;
sg.stringIndex = segment.offset + run.pos() + cluster;
sg.advance = { advance, 0 };
sg.direction = (TextDirection)segment.direction;
sg.position = pen;
result.shapedGlyphs.emplace_back( std::move( sg ) );
@@ -324,6 +332,7 @@ TextLayout TextLayouter::layout( const StringType& string, Font* font, const Uin
sg.glyphIndex = glyphInfo[i].codepoint;
sg.stringIndex = segment.offset + run.pos() + glyphInfo[i].cluster;
sg.advance = { glyphPos[i].x_advance / 64.f, glyphPos[i].y_advance / 64.f };
sg.direction = (TextDirection)segment.direction;
sg.position.x = std::round( pen.x + ( glyphPos[i].x_offset / 64.f ) );
sg.position.y = std::round( pen.y - ( glyphPos[i].y_offset / 64.f ) );
result.shapedGlyphs.emplace_back( std::move( sg ) );
@@ -378,6 +387,7 @@ TextLayout TextLayouter::layout( const StringType& string, Font* font, const Uin
: std::optional<Float>{} )
: std::optional<Float>{} ),
0 };
sg.direction = TextDirection::LeftToRight;
sg.position = pen;
pen.x += sg.advance.x;
result.shapedGlyphs.emplace_back( std::move( sg ) );
@@ -391,6 +401,7 @@ TextLayout TextLayouter::layout( const StringType& string, Font* font, const Uin
sg.advance = {
font->getGlyph( curChar, characterSize, bold, italic, outlineThickness ).advance,
0 };
sg.direction = TextDirection::LeftToRight;
sg.position = pen;
pen.x += sg.advance.x;
result.shapedGlyphs.emplace_back( std::move( sg ) );
@@ -405,24 +416,24 @@ TextLayout TextLayouter::layout( const StringType& string, Font* font, const Uin
maxWidth = eemax( maxWidth, result.linesWidth[result.linesWidth.size() - 1] );
result.size = { maxWidth, std::ceil( pen.y ) };
sLayoutCache.put( hash, result );
return result;
sLayoutCache.put( hash, resultPtr );
return resultPtr;
}
TextLayout TextLayouter::layout( const String& string, Font* font, const Uint32& fontSize,
const Uint32& style, const Uint32& tabWidth,
const Float& outlineThickness, std::optional<Float> tabOffset,
Uint32 textDrawHints ) {
return TextLayouter::layout<String>( string, font, fontSize, style, tabWidth, outlineThickness,
tabOffset, textDrawHints );
TextLayout::Cache TextLayout::layout( const String& string, Font* font, const Uint32& fontSize,
const Uint32& style, const Uint32& tabWidth,
const Float& outlineThickness, std::optional<Float> tabOffset,
Uint32 textDrawHints ) {
return TextLayout::layout<String>( string, font, fontSize, style, tabWidth, outlineThickness,
tabOffset, textDrawHints );
}
TextLayout TextLayouter::layout( const String::View& string, Font* font, const Uint32& fontSize,
const Uint32& style, const Uint32& tabWidth,
const Float& outlineThickness, std::optional<Float> tabOffset,
Uint32 textDrawHints ) {
return TextLayouter::layout<String::View>( string, font, fontSize, style, tabWidth,
outlineThickness, tabOffset, textDrawHints );
TextLayout::Cache TextLayout::layout( const String::View& string, Font* font,
const Uint32& fontSize, const Uint32& style,
const Uint32& tabWidth, const Float& outlineThickness,
std::optional<Float> tabOffset, Uint32 textDrawHints ) {
return TextLayout::layout<String::View>( string, font, fontSize, style, tabWidth,
outlineThickness, tabOffset, textDrawHints );
}
} // namespace EE::Graphics

View File

@@ -173,10 +173,18 @@ void Http::Request::setProgressCallback( const Http::Request::ProgressCallback&
mProgressCallback = progressCallback;
}
void Http::Request::setCancelCallback( const Http::Request::CancelCallback& cancelCallback ) {
mCancelCallback = cancelCallback;
}
const Http::Request::ProgressCallback& Http::Request::getProgressCallback() const {
return mProgressCallback;
}
const Http::Request::CancelCallback& Http::Request::getCancelCallback() const {
return mCancelCallback;
}
void Http::Request::cancel() {
mCancel = true;
setProgressCallback( {} );
@@ -208,6 +216,11 @@ std::string Http::Request::prepareTunnel( const Http& http ) {
return out.str();
}
void Http::onCancel( const Http::Request& request ) {
if ( request.getCancelCallback() )
request.getCancelCallback()( *this, request );
}
bool Http::Request::isVerbose() const {
return mVerbose;
}
@@ -795,8 +808,10 @@ Http::Response Http::downloadRequest( const Http::Request& request, IOStream& wr
// Prepare the response
Response received;
if ( request.isCancelled() )
if ( request.isCancelled() ) {
onCancel( request );
return Response();
}
// If not connected, try to connect to the server
if ( mConnection && !mConnection->isConnected() ) {
@@ -1128,6 +1143,9 @@ Http::Response Http::downloadRequest( const Http::Request& request, IOStream& wr
}
}
if ( request.isCancelled() )
onCancel( request );
// Close the connection
if ( mConnection && !mConnection->isKeepAlive() ) {
mConnection->disconnect();

View File

@@ -157,7 +157,7 @@ EE_MAIN_FUNC int main( int, char*[] ) {
Text::TextShaperOptimizations = false;
UIApplication app( WindowSettings( 1024, 650, "eepp - TextEdit", WindowStyle::Default,
WindowBackend::Default, 32, {}, 1, false, true ),
UIApplication::Settings( {}, 1.5f ) );
UIApplication::Settings( {}, 1.33f ) );
FileSystem::changeWorkingDirectory( Sys::getProcessPath() );
auto ll = UILinearLayout::NewVertical();
ll->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent );
@@ -168,19 +168,18 @@ EE_MAIN_FUNC int main( int, char*[] ) {
editor->setFontSize( PixelDensity::dpToPx( 12 ) );
FontManager::instance()->addFallbackFont(
FontTrueType::New( "arabic", "unit_tests/assets/fonts/NotoNaskhArabic-Regular.ttf" ) );
// FontManager::instance()->addFallbackFont( FontTrueType::New(
// "NotoSerifBengali-Regular", "unit_tests/assets/fonts/NotoSansBengali-Regular.ttf" ) );
// editor->setLineWrapMode( LineWrapMode::Word );
editor->setFont( FontManager::instance()->getByName( "monospace" ) );
FontManager::instance()->addFallbackFont( FontTrueType::New(
"NotoSerifBengali-Regular", "unit_tests/assets/fonts/NotoSansBengali-Regular.ttf" ) );
editor->setLineWrapMode( LineWrapMode::Word );
// editor->setFont( FontManager::instance()->getByName( "monospace" ) );
// editor->loadFromFile( "unit_tests/assets/textfiles/test-arabic-simple.uext" );
editor->loadFromFile( "unit_tests/assets/textfiles/test-arabic.uext" );
// editor->loadFromFile( "unit_tests/assets/textfiles/test-arabic.uext" );
// editor->loadFromFile( "unit_tests/assets/textfiles/test-bengali.uext" );
// editor->loadFromFile( "unit_tests/assets/textfiles/test-flags.uext" );
// editor->loadFromFile( "unit_tests/assets/textformat/english.utf8.lf.nobom.txt" );
// editor->loadFromFile( "unit_tests/assets/textfiles/test-arabic-mixed.uext" );
// editor->getDocument().textInput( "اسمي..." );
// editor->getDocument().textInput( " হ্যাঁ " );
// editor->getDocument().textInput( "I'm from...: আমি ... থেকে এসেছি।" );
// editor->loadFromFile( "unit_tests/assets/textfiles/test-mixed-text.uext" );
editor->loadFromFile( "unit_tests/assets/textfiles/lorem-ipsum.uext" );
editor->setFont( app.getUI()->getUIThemeManager()->getDefaultFont() );
editor->on( Event::KeyUp, [&]( const Event* event ) {

View File

@@ -301,8 +301,11 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
setCmd( "ai-prompt", [this] {
// "ai-prompt-stop"
if ( mRequest ) {
mRequest->cancel();
return;
if ( !mRequest->isCancelled() ) {
mRequest->cancel();
return;
} else
mRequest.reset();
}
auto chats = findAllByClass( "llm_conversation" );
@@ -1021,10 +1024,14 @@ void LLMChatUI::doRequest() {
mChatStop->setVisible( true )->setEnabled( true );
UIWidget* chat = addChatUI( LLMChat::Role::Assistant );
toggleEnableChats( false );
toggleEnableChats( false ); // editor is not disabled (to allow copy with locked document)
auto model = mCurModel;
auto* editor = chat->findByClass<UICodeEditor>( "data_ui" );
// new editor must be disabled because even on locked document it's possible to move the cursor
editor->setEnabled( false );
auto* thinking = editor->findByClass<UIImage>( "thinking" );
auto thinkingID = String::hash( String::format( "thinking-%p", thinking ) );
thinking->setVisible( true );
@@ -1052,9 +1059,15 @@ void LLMChatUI::doRequest() {
} );
};
mRequest->cancelCb = [this, thinking, thinkingID]( const LLMChatCompletionRequest& ) {
mRequest->cancelCb = [this, thinking, thinkingID, editor]( const LLMChatCompletionRequest& ) {
thinking->removeActionsByTag( thinkingID );
thinking->setVisible( false );
mChatStop->setVisible( false )->setEnabled( false );
mChatRun->setVisible( true )->setEnabled( true );
toggleEnableChats( true );
editor->setEnabled( true );
if ( editor->hasFocus() )
mChatInput->setFocus();
removeLastChat();
};
@@ -1086,6 +1099,7 @@ void LLMChatUI::doRequest() {
}
mRequest.reset();
toggleEnableChats( true );
editor->setEnabled( true );
mChatStop->setVisible( false )->setEnabled( false );
mChatRun->setVisible( true )->setEnabled( true );

View File

@@ -19,13 +19,14 @@ LLMChatCompletionRequest::LLMChatCompletionRequest( const std::string& uri, cons
mRequest.setBody( reqBody );
mRequest.setFollowRedirect( true );
mRequest.setMethod( Http::Request::Method::Post );
mRequest.setCancelCallback( [this]( const Http&, const Http::Request& ) { onCancel(); } );
mRequest.setProgressCallback( [this]( const Http&, const Http::Request&, const Http::Response&,
const Http::Request::Status& status, size_t, size_t ) {
if ( mCancel ) {
if ( cancelCb )
cancelCb( *this );
onCancel();
return false;
}
mHadProgress = true;
if ( status != Http::Request::ContentReceived )
return true;
std::string chunk =
@@ -106,12 +107,18 @@ LLMChatCompletionRequest::~LLMChatCompletionRequest() {
}
void LLMChatCompletionRequest::request() {
mCancel = false;
mCancelled = false;
mHadProgress = false;
Http::Response res = mHttp->downloadRequest( mRequest, mStream, Seconds( 5 ) );
if ( doneCb )
doneCb( *this, res );
}
void LLMChatCompletionRequest::requestAsync() {
mCancel = false;
mCancelled = false;
mHadProgress = false;
mRequestId = mHttp->downloadAsyncRequest(
[this]( const Http&, Http::Request&, Http::Response& res ) {
if ( doneCb )
@@ -124,10 +131,22 @@ void LLMChatCompletionRequest::cancel() {
mCancel = true;
if ( mRequestId )
mHttp->setCancelRequest( mRequestId );
if ( !mHadProgress )
onCancel();
}
const std::string& LLMChatCompletionRequest::getStream() const {
return mStream.getStream();
}
bool LLMChatCompletionRequest::isCancelled() const {
return mCancelled;
}
void LLMChatCompletionRequest::onCancel() {
if ( !mCancelled && cancelCb )
cancelCb( *this );
mCancelled = true;
}
} // namespace ecode

View File

@@ -38,6 +38,8 @@ class LLMChatCompletionRequest {
void cancel();
bool isCancelled() const;
const std::string& getStream() const;
const std::string& getResponse() const { return mResponse; }
@@ -53,9 +55,13 @@ class LLMChatCompletionRequest {
std::string mResponse;
size_t mReadBytes{ 0 };
bool mCancel{ false };
bool mCancelled{ false };
bool mFirstMessage{ true };
bool mReasoning{ false };
bool mHadProgress{ false };
Uint64 mRequestId{ 0 };
void onCancel();
};
} // namespace ecode