Add marquee for long cheat names

This commit is contained in:
Gericom
2026-03-01 16:18:06 +01:00
parent a9425eea7c
commit e2e42115e7
10 changed files with 162 additions and 36 deletions

View File

@@ -28,24 +28,28 @@ int nft2_findGlyphIdxForCharacter(const nft2_header_t* font, u16 character)
} }
static inline void renderGlyph(const nft2_header_t* font, const nft2_glyph_t* glyph, static inline void renderGlyph(const nft2_header_t* font, const nft2_glyph_t* glyph,
int xPos, int yPos, int width, int height, u8* dst, u32 stride, bool a5i3) int xPos, int yPos, const nft2_string_render_params_t* renderParams, u8* dst, u32 stride, bool a5i3)
{ {
int yOffset = glyph->spacingTop; int yOffset = glyph->spacingTop;
u32 xStart = xPos < 0 ? -xPos : 0; u32 xStart = xPos < 0 ? -xPos : 0;
u32 yStart = yPos + yOffset < 0 ? -(yPos + yOffset) : 0; u32 yStart = yPos + yOffset < 0 ? -(yPos + yOffset) : 0;
int xEnd = glyph->glyphWidth; int xEnd = glyph->glyphWidth;
if (xPos + xEnd > width) if (xPos + xEnd > (int)renderParams->width)
{
if (renderParams->onlyRenderWholeGlyphs)
{ {
// by returning we only render complete glyphs
return; return;
// old code for rendering partial glyphs }
// xEnd = width - xPos;
xEnd = renderParams->width - xPos;
} }
int yEnd = glyph->glyphHeight; int yEnd = glyph->glyphHeight;
if (yPos + yOffset + yEnd > height) if (yPos + yOffset + yEnd > (int)renderParams->height)
yEnd = height - (yPos + yOffset); // allow partial glyphs in the vertical direction {
yEnd = renderParams->height - (yPos + yOffset); // allow partial glyphs in the vertical direction
}
const u8* glyphData = &font->glyphDataPtr[glyph->dataOffset]; const u8* glyphData = &font->glyphDataPtr[glyph->dataOffset];
glyphData += yStart * ((glyph->glyphWidth + 1) >> 1); glyphData += yStart * ((glyph->glyphWidth + 1) >> 1);
@@ -97,15 +101,15 @@ static inline void renderGlyph(const nft2_header_t* font, const nft2_glyph_t* gl
} }
static ITCM_CODE void renderGlyphTiled(const nft2_header_t* font, const nft2_glyph_t* glyph, static ITCM_CODE void renderGlyphTiled(const nft2_header_t* font, const nft2_glyph_t* glyph,
int xPos, int yPos, int width, int height, u8* dst, u32 stride) int xPos, int yPos, const nft2_string_render_params_t* renderParams, u8* dst, u32 stride)
{ {
renderGlyph(font, glyph, xPos, yPos, width, height, dst, stride, false); renderGlyph(font, glyph, xPos, yPos, renderParams, dst, stride, false);
} }
static ITCM_CODE void renderGlyphA5I3(const nft2_header_t* font, const nft2_glyph_t* glyph, static ITCM_CODE void renderGlyphA5I3(const nft2_header_t* font, const nft2_glyph_t* glyph,
int xPos, int yPos, int width, int height, u8* dst, u32 stride) int xPos, int yPos, const nft2_string_render_params_t* renderParams, u8* dst, u32 stride)
{ {
renderGlyph(font, glyph, xPos, yPos, width, height, dst, stride, true); renderGlyph(font, glyph, xPos, yPos, renderParams, dst, stride, true);
} }
ITCM_CODE void nft2_renderString(const nft2_header_t* font, const char16_t* string, u8* dst, u32 stride, ITCM_CODE void nft2_renderString(const nft2_header_t* font, const char16_t* string, u8* dst, u32 stride,
@@ -134,18 +138,18 @@ ITCM_CODE void nft2_renderString(const nft2_header_t* font, const char16_t* stri
xPos += glyph->spacingLeft; xPos += glyph->spacingLeft;
if (a5i3) if (a5i3)
{ {
renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
else else
{ {
renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
xPos += glyph->glyphWidth; xPos += glyph->glyphWidth;
if (xPos > (int)textWidth) if (xPos > (int)textWidth)
textWidth = xPos; textWidth = xPos;
xPos += glyph->spacingRight; xPos += glyph->spacingRight;
} }
renderParams->textWidth = textWidth; renderParams->textWidth = textWidth - renderParams->x;
} }
ITCM_CODE void nft2_measureString(const nft2_header_t* font, const char16_t* string, u32& width, u32& height) ITCM_CODE void nft2_measureString(const nft2_header_t* font, const char16_t* string, u32& width, u32& height)
@@ -265,11 +269,11 @@ ITCM_CODE void nft2_renderStringEllipsis(const nft2_header_t* font, const char16
xPos += glyph->spacingLeft; xPos += glyph->spacingLeft;
if (a5i3) if (a5i3)
{ {
renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
else else
{ {
renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
xPos += glyph->glyphWidth; xPos += glyph->glyphWidth;
if (xPos > (int)textWidth) if (xPos > (int)textWidth)
@@ -287,11 +291,11 @@ ITCM_CODE void nft2_renderStringEllipsis(const nft2_header_t* font, const char16
xPos += glyph->spacingLeft; xPos += glyph->spacingLeft;
if (a5i3) if (a5i3)
{ {
renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
else else
{ {
renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
xPos += glyph->glyphWidth; xPos += glyph->glyphWidth;
if (xPos > (int)textWidth) if (xPos > (int)textWidth)
@@ -309,16 +313,16 @@ ITCM_CODE void nft2_renderStringEllipsis(const nft2_header_t* font, const char16
xPos += glyph->spacingLeft; xPos += glyph->spacingLeft;
if (a5i3) if (a5i3)
{ {
renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
else else
{ {
renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride);
} }
xPos += glyph->glyphWidth; xPos += glyph->glyphWidth;
if (xPos > (int)textWidth) if (xPos > (int)textWidth)
textWidth = xPos; textWidth = xPos;
xPos += glyph->spacingRight; xPos += glyph->spacingRight;
} }
renderParams->textWidth = textWidth; renderParams->textWidth = textWidth - renderParams->x;
} }

View File

@@ -39,6 +39,7 @@ struct nft2_string_render_params_t
u32 textWidth; u32 textWidth;
// u32 textHeight; // u32 textHeight;
bool a5i3; bool a5i3;
bool onlyRenderWholeGlyphs = true;
}; };
/// @brief Prepares the ntf2 data of the given \p font for runtime use. /// @brief Prepares the ntf2 data of the given \p font for runtime use.

View File

@@ -9,6 +9,10 @@
#include "gui/palette/GradientPalette.h" #include "gui/palette/GradientPalette.h"
#include "LabelView.h" #include "LabelView.h"
#define MARQUEE_START_FRAMES 60
#define MARQUEE_STEP_FRAMES 3
#define MARQUEE_END_FRAMES 90
LabelView::LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font, bool a5i3) LabelView::LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font, bool a5i3)
: _width(width), _height(height) : _width(width), _height(height)
, _maxStringLength(maxStringLength), _font(font) , _maxStringLength(maxStringLength), _font(font)
@@ -34,14 +38,30 @@ LabelView::LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_head
SetText(u""); SetText(u"");
} }
void LabelView::Update()
{
if (_ellipsisStyleChanged)
{
RestartMarquee();
UpdateTileBuffer();
_ellipsisStyleChanged = false;
}
else if (_ellipsisStyle == EllipsisStyle::Marquee)
{
UpdateMarquee();
}
}
void LabelView::SetTextBuffer(const char* text) void LabelView::SetTextBuffer(const char* text)
{ {
StringUtil::Copy(_textBuffer.get(), text, _maxStringLength + 1); StringUtil::Copy(_textBuffer.get(), text, _maxStringLength + 1);
RestartMarquee();
} }
void LabelView::SetTextBuffer(const char16_t* text) void LabelView::SetTextBuffer(const char16_t* text)
{ {
StringUtil::Copy(_textBuffer.get(), text, _maxStringLength + 1); StringUtil::Copy(_textBuffer.get(), text, _maxStringLength + 1);
RestartMarquee();
} }
void LabelView::SetTextBuffer(const char16_t* text, u32 length) void LabelView::SetTextBuffer(const char16_t* text, u32 length)
@@ -50,6 +70,7 @@ void LabelView::SetTextBuffer(const char16_t* text, u32 length)
if (copyLength > 0) if (copyLength > 0)
memcpy(_textBuffer.get(), text, copyLength * 2); memcpy(_textBuffer.get(), text, copyLength * 2);
_textBuffer[copyLength] = 0; _textBuffer[copyLength] = 0;
RestartMarquee();
} }
void LabelView::UpdateTileBuffer() void LabelView::UpdateTileBuffer()
@@ -63,10 +84,19 @@ void LabelView::UpdateTileBuffer()
renderParams.width = _width; renderParams.width = _width;
renderParams.height = _height; renderParams.height = _height;
renderParams.a5i3 = _a5i3; renderParams.a5i3 = _a5i3;
if (_ellipsis) if (_ellipsisStyle == EllipsisStyle::Ellipsis)
{
nft2_renderStringEllipsis(_font, _textBuffer.get(), _tileBuffer.get(), _actualWidth, &renderParams, u" ... "); nft2_renderStringEllipsis(_font, _textBuffer.get(), _tileBuffer.get(), _actualWidth, &renderParams, u" ... ");
}
else else
{
if (_ellipsisStyle == EllipsisStyle::Marquee)
{
renderParams.x = -_marqueeOffset;
renderParams.onlyRenderWholeGlyphs = false;
}
nft2_renderString(_font, _textBuffer.get(), _tileBuffer.get(), _actualWidth, &renderParams); nft2_renderString(_font, _textBuffer.get(), _tileBuffer.get(), _actualWidth, &renderParams);
}
_newStringWidth = renderParams.textWidth; _newStringWidth = renderParams.textWidth;
} }
else else
@@ -119,3 +149,54 @@ QueueTask<void> LabelView::SetTextAsync(TaskQueueBase* taskQueue, const char16_t
SetTextBuffer(text, length); SetTextBuffer(text, length);
return UpdateTileBufferAsync(taskQueue); return UpdateTileBufferAsync(taskQueue);
} }
void LabelView::RestartMarquee()
{
_marqueeOffset = 0;
_marqueeCounter = MARQUEE_START_FRAMES;
_marqueeState = MarqueeState::StartWait;
}
void LabelView::UpdateMarquee()
{
UpdateTileBuffer();
if (_newStringWidth <= _width)
{
_marqueeOffset = 0;
}
else
{
switch (_marqueeState)
{
case MarqueeState::StartWait:
{
if (--_marqueeCounter <= 0)
{
_marqueeState = MarqueeState::Moving;
_marqueeOffset = 0;
}
break;
}
case MarqueeState::Moving:
{
_marqueeOffset++;
if (_newStringWidth - _marqueeOffset < _width)
{
_marqueeState = MarqueeState::EndWait;
_marqueeCounter = MARQUEE_END_FRAMES;
}
break;
}
case MarqueeState::EndWait:
{
if (--_marqueeCounter <= 0)
{
_marqueeState = MarqueeState::StartWait;
_marqueeCounter = MARQUEE_START_FRAMES;
_marqueeOffset = 0;
}
break;
}
}
}
}

View File

@@ -14,6 +14,15 @@ class MaterialGraphicsContext;
class LabelView : public View class LabelView : public View
{ {
public: public:
enum class EllipsisStyle
{
None,
Ellipsis,
Marquee
};
void Update() override;
void SetText(const char* text); void SetText(const char* text);
void SetText(const char16_t* text); void SetText(const char16_t* text);
void SetText(const char16_t* text, u32 length); void SetText(const char16_t* text, u32 length);
@@ -43,7 +52,14 @@ public:
return Rectangle(_position, _width, _height); return Rectangle(_position, _width, _height);
} }
void SetEllipsis(bool ellipsis) { _ellipsis = ellipsis; } void SetEllipsisStyle(EllipsisStyle ellipsisStyle)
{
if (_ellipsisStyle != ellipsisStyle)
{
_ellipsisStyle = ellipsisStyle;
_ellipsisStyleChanged = true;
}
}
protected: protected:
u32 _width; u32 _width;
@@ -61,14 +77,30 @@ protected:
Rgb<8, 8, 8> _backgroundColor; Rgb<8, 8, 8> _backgroundColor;
Rgb<8, 8, 8> _foregroundColor; Rgb<8, 8, 8> _foregroundColor;
int _paletteRow = -1; int _paletteRow = -1;
bool _ellipsis = false; EllipsisStyle _ellipsisStyle = EllipsisStyle::None;
bool _a5i3; bool _a5i3;
LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font, bool a5i3);
void SetTextBuffer(const char* text); void SetTextBuffer(const char* text);
void SetTextBuffer(const char16_t* text); void SetTextBuffer(const char16_t* text);
void SetTextBuffer(const char16_t* text, u32 length); void SetTextBuffer(const char16_t* text, u32 length);
virtual void UpdateTileBuffer(); virtual void UpdateTileBuffer();
QueueTask<void> UpdateTileBufferAsync(TaskQueueBase* taskQueue); QueueTask<void> UpdateTileBufferAsync(TaskQueueBase* taskQueue);
LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font, bool a5i3); private:
enum class MarqueeState
{
StartWait,
Moving,
EndWait
};
MarqueeState _marqueeState = MarqueeState::StartWait;
int _marqueeOffset = 0;
int _marqueeCounter = 0;
bool _ellipsisStyleChanged = false;
void RestartMarquee();
void UpdateMarquee();
}; };

View File

@@ -22,7 +22,7 @@ MaterialFileInfoCardView::MaterialFileInfoCardView(const MaterialColorScheme* ma
AddChildTail(&_firstLine); AddChildTail(&_firstLine);
AddChildTail(&_secondLine); AddChildTail(&_secondLine);
AddChildTail(&_thirdLine); AddChildTail(&_thirdLine);
_filenameLabelView.SetEllipsis(true); _filenameLabelView.SetEllipsisStyle(LabelView::EllipsisStyle::Ellipsis);
AddChildTail(&_filenameLabelView); AddChildTail(&_filenameLabelView);
} }

View File

@@ -19,7 +19,7 @@ public:
void SetFirstLineAsync(TaskQueueBase* taskQueue, const char* firstLine, bool ellipsis) override void SetFirstLineAsync(TaskQueueBase* taskQueue, const char* firstLine, bool ellipsis) override
{ {
_firstLine.SetEllipsis(ellipsis); _firstLine.SetEllipsisStyle(ellipsis ? LabelView::EllipsisStyle::Ellipsis : LabelView::EllipsisStyle::None);
if (taskQueue) if (taskQueue)
_firstLine.SetTextAsync(taskQueue, firstLine); _firstLine.SetTextAsync(taskQueue, firstLine);
else else
@@ -28,7 +28,7 @@ public:
void SetFirstLineAsync(TaskQueueBase* taskQueue, const char16_t* firstLine, u32 length, bool ellipsis) override void SetFirstLineAsync(TaskQueueBase* taskQueue, const char16_t* firstLine, u32 length, bool ellipsis) override
{ {
_firstLine.SetEllipsis(ellipsis); _firstLine.SetEllipsisStyle(ellipsis ? LabelView::EllipsisStyle::Ellipsis : LabelView::EllipsisStyle::None);
if (taskQueue) if (taskQueue)
_firstLine.SetTextAsync(taskQueue, firstLine, length); _firstLine.SetTextAsync(taskQueue, firstLine, length);
else else

View File

@@ -14,7 +14,7 @@ CustomFileInfoView::CustomFileInfoView(const IFontRepository* fontRepository)
AddChildTail(&_firstLine); AddChildTail(&_firstLine);
AddChildTail(&_secondLine); AddChildTail(&_secondLine);
AddChildTail(&_thirdLine); AddChildTail(&_thirdLine);
_filenameLabelView.SetEllipsis(true); _filenameLabelView.SetEllipsisStyle(LabelView::EllipsisStyle::Ellipsis);
AddChildTail(&_filenameLabelView); AddChildTail(&_filenameLabelView);
} }

View File

@@ -16,7 +16,7 @@ public:
void SetFirstLineAsync(TaskQueueBase* taskQueue, const char* firstLine, bool ellipsis) override void SetFirstLineAsync(TaskQueueBase* taskQueue, const char* firstLine, bool ellipsis) override
{ {
_firstLine.SetEllipsis(ellipsis); _firstLine.SetEllipsisStyle(ellipsis ? LabelView::EllipsisStyle::Ellipsis : LabelView::EllipsisStyle::None);
if (taskQueue) if (taskQueue)
_firstLine.SetTextAsync(taskQueue, firstLine); _firstLine.SetTextAsync(taskQueue, firstLine);
else else
@@ -25,7 +25,7 @@ public:
void SetFirstLineAsync(TaskQueueBase* taskQueue, const char16_t* firstLine, u32 length, bool ellipsis) override void SetFirstLineAsync(TaskQueueBase* taskQueue, const char16_t* firstLine, u32 length, bool ellipsis) override
{ {
_firstLine.SetEllipsis(ellipsis); _firstLine.SetEllipsisStyle(ellipsis ? LabelView::EllipsisStyle::Ellipsis : LabelView::EllipsisStyle::None);
if (taskQueue) if (taskQueue)
_firstLine.SetTextAsync(taskQueue, firstLine, length); _firstLine.SetTextAsync(taskQueue, firstLine, length);
else else

View File

@@ -28,7 +28,7 @@ public:
void SetFirstLineAsync(TaskQueueBase* taskQueue, const char* firstLine, bool ellipsis) override void SetFirstLineAsync(TaskQueueBase* taskQueue, const char* firstLine, bool ellipsis) override
{ {
_firstLine->SetEllipsis(ellipsis); _firstLine->SetEllipsisStyle(ellipsis ? LabelView::EllipsisStyle::Ellipsis : LabelView::EllipsisStyle::None);
if (taskQueue) if (taskQueue)
_firstLine->SetTextAsync(taskQueue, firstLine); _firstLine->SetTextAsync(taskQueue, firstLine);
else else
@@ -37,7 +37,7 @@ public:
void SetFirstLineAsync(TaskQueueBase* taskQueue, const char16_t* firstLine, u32 length, bool ellipsis) override void SetFirstLineAsync(TaskQueueBase* taskQueue, const char16_t* firstLine, u32 length, bool ellipsis) override
{ {
_firstLine->SetEllipsis(ellipsis); _firstLine->SetEllipsisStyle(ellipsis ? LabelView::EllipsisStyle::Ellipsis : LabelView::EllipsisStyle::None);
if (taskQueue) if (taskQueue)
_firstLine->SetTextAsync(taskQueue, firstLine, length); _firstLine->SetTextAsync(taskQueue, firstLine, length);
else else

View File

@@ -14,11 +14,11 @@
CheatListItemView::CheatListItemView(const VramOffsets& vramOffsets, CheatListItemView::CheatListItemView(const VramOffsets& vramOffsets,
const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository) const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository)
: _nameLabel(200, 16, 64, fontRepository->GetFont(FontType::Regular10)) : _nameLabel(196, 16, 256, fontRepository->GetFont(FontType::Regular10))
, _vramOffsets(vramOffsets) , _vramOffsets(vramOffsets)
, _materialColorScheme(materialColorScheme) , _materialColorScheme(materialColorScheme)
{ {
_nameLabel.SetEllipsis(true); _nameLabel.SetEllipsisStyle(LabelView::EllipsisStyle::Ellipsis);
AddChildTail(&_nameLabel); AddChildTail(&_nameLabel);
} }
@@ -31,6 +31,14 @@ void CheatListItemView::Update()
? _vramOffsets.checkboxCheckedIconVramOffset ? _vramOffsets.checkboxCheckedIconVramOffset
: _vramOffsets.checkboxUncheckedIconVramOffset; : _vramOffsets.checkboxUncheckedIconVramOffset;
} }
if (IsFocused())
{
_nameLabel.SetEllipsisStyle(LabelView::EllipsisStyle::Marquee);
}
else
{
_nameLabel.SetEllipsisStyle(LabelView::EllipsisStyle::Ellipsis);
}
ViewContainer::Update(); ViewContainer::Update();
} }