diff --git a/arm9/source/gui/font/nitroFont2.cpp b/arm9/source/gui/font/nitroFont2.cpp index 7734e7b..2594d08 100644 --- a/arm9/source/gui/font/nitroFont2.cpp +++ b/arm9/source/gui/font/nitroFont2.cpp @@ -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, - 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; u32 xStart = xPos < 0 ? -xPos : 0; u32 yStart = yPos + yOffset < 0 ? -(yPos + yOffset) : 0; int xEnd = glyph->glyphWidth; - if (xPos + xEnd > width) + if (xPos + xEnd > (int)renderParams->width) { - // by returning we only render complete glyphs - return; - // old code for rendering partial glyphs - // xEnd = width - xPos; + if (renderParams->onlyRenderWholeGlyphs) + { + return; + } + + xEnd = renderParams->width - xPos; } int yEnd = glyph->glyphHeight; - if (yPos + yOffset + yEnd > height) - yEnd = height - (yPos + yOffset); // allow partial glyphs in the vertical direction + if (yPos + yOffset + yEnd > (int)renderParams->height) + { + yEnd = renderParams->height - (yPos + yOffset); // allow partial glyphs in the vertical direction + } const u8* glyphData = &font->glyphDataPtr[glyph->dataOffset]; 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, - 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, - 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, @@ -134,18 +138,18 @@ ITCM_CODE void nft2_renderString(const nft2_header_t* font, const char16_t* stri xPos += glyph->spacingLeft; if (a5i3) { - renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); + renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride); } else { - renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); + renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride); } xPos += glyph->glyphWidth; if (xPos > (int)textWidth) textWidth = xPos; 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) @@ -265,11 +269,11 @@ ITCM_CODE void nft2_renderStringEllipsis(const nft2_header_t* font, const char16 xPos += glyph->spacingLeft; if (a5i3) { - renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); + renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride); } else { - renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); + renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride); } xPos += glyph->glyphWidth; if (xPos > (int)textWidth) @@ -287,11 +291,11 @@ ITCM_CODE void nft2_renderStringEllipsis(const nft2_header_t* font, const char16 xPos += glyph->spacingLeft; if (a5i3) { - renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); + renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride); } else { - renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); + renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride); } xPos += glyph->glyphWidth; if (xPos > (int)textWidth) @@ -309,16 +313,16 @@ ITCM_CODE void nft2_renderStringEllipsis(const nft2_header_t* font, const char16 xPos += glyph->spacingLeft; if (a5i3) { - renderGlyphA5I3(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); + renderGlyphA5I3(font, glyph, xPos, yPos, renderParams, dst, stride); } else { - renderGlyphTiled(font, glyph, xPos, yPos, renderParams->width, renderParams->height, dst, stride); + renderGlyphTiled(font, glyph, xPos, yPos, renderParams, dst, stride); } xPos += glyph->glyphWidth; if (xPos > (int)textWidth) textWidth = xPos; xPos += glyph->spacingRight; } - renderParams->textWidth = textWidth; + renderParams->textWidth = textWidth - renderParams->x; } \ No newline at end of file diff --git a/arm9/source/gui/font/nitroFont2.h b/arm9/source/gui/font/nitroFont2.h index f11f4b8..936a43a 100644 --- a/arm9/source/gui/font/nitroFont2.h +++ b/arm9/source/gui/font/nitroFont2.h @@ -39,6 +39,7 @@ struct nft2_string_render_params_t u32 textWidth; // u32 textHeight; bool a5i3; + bool onlyRenderWholeGlyphs = true; }; /// @brief Prepares the ntf2 data of the given \p font for runtime use. diff --git a/arm9/source/gui/views/LabelView.cpp b/arm9/source/gui/views/LabelView.cpp index 542ad75..0a541ad 100644 --- a/arm9/source/gui/views/LabelView.cpp +++ b/arm9/source/gui/views/LabelView.cpp @@ -9,6 +9,10 @@ #include "gui/palette/GradientPalette.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) : _width(width), _height(height) , _maxStringLength(maxStringLength), _font(font) @@ -34,14 +38,30 @@ LabelView::LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_head SetText(u""); } +void LabelView::Update() +{ + if (_ellipsisStyleChanged) + { + RestartMarquee(); + UpdateTileBuffer(); + _ellipsisStyleChanged = false; + } + else if (_ellipsisStyle == EllipsisStyle::Marquee) + { + UpdateMarquee(); + } +} + void LabelView::SetTextBuffer(const char* text) { StringUtil::Copy(_textBuffer.get(), text, _maxStringLength + 1); + RestartMarquee(); } void LabelView::SetTextBuffer(const char16_t* text) { StringUtil::Copy(_textBuffer.get(), text, _maxStringLength + 1); + RestartMarquee(); } 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) memcpy(_textBuffer.get(), text, copyLength * 2); _textBuffer[copyLength] = 0; + RestartMarquee(); } void LabelView::UpdateTileBuffer() @@ -63,10 +84,19 @@ void LabelView::UpdateTileBuffer() renderParams.width = _width; renderParams.height = _height; renderParams.a5i3 = _a5i3; - if (_ellipsis) + if (_ellipsisStyle == EllipsisStyle::Ellipsis) + { nft2_renderStringEllipsis(_font, _textBuffer.get(), _tileBuffer.get(), _actualWidth, &renderParams, u" ... "); + } else + { + if (_ellipsisStyle == EllipsisStyle::Marquee) + { + renderParams.x = -_marqueeOffset; + renderParams.onlyRenderWholeGlyphs = false; + } nft2_renderString(_font, _textBuffer.get(), _tileBuffer.get(), _actualWidth, &renderParams); + } _newStringWidth = renderParams.textWidth; } else @@ -119,3 +149,54 @@ QueueTask LabelView::SetTextAsync(TaskQueueBase* taskQueue, const char16_t SetTextBuffer(text, length); 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; + } + } + } +} diff --git a/arm9/source/gui/views/LabelView.h b/arm9/source/gui/views/LabelView.h index 42cc199..0fee587 100644 --- a/arm9/source/gui/views/LabelView.h +++ b/arm9/source/gui/views/LabelView.h @@ -14,6 +14,15 @@ class MaterialGraphicsContext; class LabelView : public View { public: + enum class EllipsisStyle + { + None, + Ellipsis, + Marquee + }; + + void Update() override; + void SetText(const char* text); void SetText(const char16_t* text); void SetText(const char16_t* text, u32 length); @@ -43,7 +52,14 @@ public: return Rectangle(_position, _width, _height); } - void SetEllipsis(bool ellipsis) { _ellipsis = ellipsis; } + void SetEllipsisStyle(EllipsisStyle ellipsisStyle) + { + if (_ellipsisStyle != ellipsisStyle) + { + _ellipsisStyle = ellipsisStyle; + _ellipsisStyleChanged = true; + } + } protected: u32 _width; @@ -61,14 +77,30 @@ protected: Rgb<8, 8, 8> _backgroundColor; Rgb<8, 8, 8> _foregroundColor; int _paletteRow = -1; - bool _ellipsis = false; + EllipsisStyle _ellipsisStyle = EllipsisStyle::None; bool _a5i3; + LabelView(u32 width, u32 height, u32 maxStringLength, const nft2_header_t* font, bool a5i3); + void SetTextBuffer(const char* text); void SetTextBuffer(const char16_t* text); void SetTextBuffer(const char16_t* text, u32 length); virtual void UpdateTileBuffer(); QueueTask 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(); }; \ No newline at end of file diff --git a/arm9/source/romBrowser/Theme/Material/MaterialFileInfoCardView.cpp b/arm9/source/romBrowser/Theme/Material/MaterialFileInfoCardView.cpp index bef2d3b..27ec115 100644 --- a/arm9/source/romBrowser/Theme/Material/MaterialFileInfoCardView.cpp +++ b/arm9/source/romBrowser/Theme/Material/MaterialFileInfoCardView.cpp @@ -22,7 +22,7 @@ MaterialFileInfoCardView::MaterialFileInfoCardView(const MaterialColorScheme* ma AddChildTail(&_firstLine); AddChildTail(&_secondLine); AddChildTail(&_thirdLine); - _filenameLabelView.SetEllipsis(true); + _filenameLabelView.SetEllipsisStyle(LabelView::EllipsisStyle::Ellipsis); AddChildTail(&_filenameLabelView); } diff --git a/arm9/source/romBrowser/Theme/Material/MaterialFileInfoCardView.h b/arm9/source/romBrowser/Theme/Material/MaterialFileInfoCardView.h index 1ce4386..d81ec2a 100644 --- a/arm9/source/romBrowser/Theme/Material/MaterialFileInfoCardView.h +++ b/arm9/source/romBrowser/Theme/Material/MaterialFileInfoCardView.h @@ -19,7 +19,7 @@ public: void SetFirstLineAsync(TaskQueueBase* taskQueue, const char* firstLine, bool ellipsis) override { - _firstLine.SetEllipsis(ellipsis); + _firstLine.SetEllipsisStyle(ellipsis ? LabelView::EllipsisStyle::Ellipsis : LabelView::EllipsisStyle::None); if (taskQueue) _firstLine.SetTextAsync(taskQueue, firstLine); else @@ -28,7 +28,7 @@ public: 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) _firstLine.SetTextAsync(taskQueue, firstLine, length); else diff --git a/arm9/source/romBrowser/Theme/custom/CustomFileInfoView.cpp b/arm9/source/romBrowser/Theme/custom/CustomFileInfoView.cpp index 95176f9..ea9b4ec 100644 --- a/arm9/source/romBrowser/Theme/custom/CustomFileInfoView.cpp +++ b/arm9/source/romBrowser/Theme/custom/CustomFileInfoView.cpp @@ -14,7 +14,7 @@ CustomFileInfoView::CustomFileInfoView(const IFontRepository* fontRepository) AddChildTail(&_firstLine); AddChildTail(&_secondLine); AddChildTail(&_thirdLine); - _filenameLabelView.SetEllipsis(true); + _filenameLabelView.SetEllipsisStyle(LabelView::EllipsisStyle::Ellipsis); AddChildTail(&_filenameLabelView); } diff --git a/arm9/source/romBrowser/Theme/custom/CustomFileInfoView.h b/arm9/source/romBrowser/Theme/custom/CustomFileInfoView.h index fea30f9..46457df 100644 --- a/arm9/source/romBrowser/Theme/custom/CustomFileInfoView.h +++ b/arm9/source/romBrowser/Theme/custom/CustomFileInfoView.h @@ -16,7 +16,7 @@ public: void SetFirstLineAsync(TaskQueueBase* taskQueue, const char* firstLine, bool ellipsis) override { - _firstLine.SetEllipsis(ellipsis); + _firstLine.SetEllipsisStyle(ellipsis ? LabelView::EllipsisStyle::Ellipsis : LabelView::EllipsisStyle::None); if (taskQueue) _firstLine.SetTextAsync(taskQueue, firstLine); else @@ -25,7 +25,7 @@ public: 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) _firstLine.SetTextAsync(taskQueue, firstLine, length); else diff --git a/arm9/source/romBrowser/views/BannerListItemView.h b/arm9/source/romBrowser/views/BannerListItemView.h index 6ee844a..2df16cb 100644 --- a/arm9/source/romBrowser/views/BannerListItemView.h +++ b/arm9/source/romBrowser/views/BannerListItemView.h @@ -28,7 +28,7 @@ public: void SetFirstLineAsync(TaskQueueBase* taskQueue, const char* firstLine, bool ellipsis) override { - _firstLine->SetEllipsis(ellipsis); + _firstLine->SetEllipsisStyle(ellipsis ? LabelView::EllipsisStyle::Ellipsis : LabelView::EllipsisStyle::None); if (taskQueue) _firstLine->SetTextAsync(taskQueue, firstLine); else @@ -37,7 +37,7 @@ public: 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) _firstLine->SetTextAsync(taskQueue, firstLine, length); else diff --git a/arm9/source/romBrowser/views/cheats/CheatListItemView.cpp b/arm9/source/romBrowser/views/cheats/CheatListItemView.cpp index ff01dda..41eb6b5 100644 --- a/arm9/source/romBrowser/views/cheats/CheatListItemView.cpp +++ b/arm9/source/romBrowser/views/cheats/CheatListItemView.cpp @@ -14,11 +14,11 @@ CheatListItemView::CheatListItemView(const VramOffsets& vramOffsets, const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository) - : _nameLabel(200, 16, 64, fontRepository->GetFont(FontType::Regular10)) + : _nameLabel(196, 16, 256, fontRepository->GetFont(FontType::Regular10)) , _vramOffsets(vramOffsets) , _materialColorScheme(materialColorScheme) { - _nameLabel.SetEllipsis(true); + _nameLabel.SetEllipsisStyle(LabelView::EllipsisStyle::Ellipsis); AddChildTail(&_nameLabel); } @@ -31,6 +31,14 @@ void CheatListItemView::Update() ? _vramOffsets.checkboxCheckedIconVramOffset : _vramOffsets.checkboxUncheckedIconVramOffset; } + if (IsFocused()) + { + _nameLabel.SetEllipsisStyle(LabelView::EllipsisStyle::Marquee); + } + else + { + _nameLabel.SetEllipsisStyle(LabelView::EllipsisStyle::Ellipsis); + } ViewContainer::Update(); }