Skip to content

version 2.20.1#604

Merged
jfboeve merged 69 commits intomasterfrom
dev
Feb 5, 2026
Merged

version 2.20.1#604
jfboeve merged 69 commits intomasterfrom
dev

Conversation

@jfboeve
Copy link
Contributor

@jfboeve jfboeve commented Feb 5, 2026

No description provided.

elsassph and others added 30 commits April 10, 2025 09:46
Fix advanced renderer text alignment mirroring
- allow disabling truncation (Arabic)
- WIP wordBreak (normal renderer)
- WIP unit tests
Bidirectional text rendering (RTL)
Fix canvas 2d renderer mirroring
Feature: expose lines layout in renderInfo
Copilot AI review requested due to automatic review settings February 5, 2026 11:21
@rdkcmf-jenkins
Copy link
Contributor

b'## Blackduck scan failure details

Summary: 0 violations, 0 files pending approval, 1 file pending identification.

  • Protex Server Path: /home/blackduck/github/Lightning/604/rdkcentral/Lightning

  • Commit: 278b0e7

Report detail: gist'

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces comprehensive Right-to-Left (RTL) layout support and significantly refactors the text rendering system in Lightning Core. The changes enable proper rendering and layout of RTL languages like Arabic and Hebrew, including bidirectional text support.

Changes:

  • Added RTL layout support with automatic mirroring of element positioning and input handling
  • Complete refactoring of text rendering system from .mjs to TypeScript with new modular architecture
  • New bidirectional text tokenization system with optional bidi-js integration
  • Performance improvements including optimized idle loop handling and alpha-based rendering optimization

Reviewed changes

Copilot reviewed 51 out of 53 changed files in this pull request and generated 22 comments.

Show a summary per file
File Description
package.json Version update to 2.17.0-beta.6, added bidi-js dependency and bidiTokenizer export
vite.config.js Added BUILD_BIDI_TOKENIZER build configuration and test exclusions
src/tree/core/ElementCore.mjs Implemented RTL layout calculations and direction propagation
src/tree/Element.mjs Added RTL property getter/setter and direction change hooks
src/application/Application.mjs Added direction-aware event routing for Left/Right key swapping
src/textures/TextTexture*.ts Complete rewrite of text rendering in TypeScript with modular architecture
src/textures/bidiTokenizer.ts New bidirectional text tokenizer for RTL language support
src/textures/TextTokenizer.ts New pluggable text tokenization system
src/renderer/c2d/shaders/DefaultShader.mjs Added texture mirroring support for Canvas2D renderer
src/platforms/browser/WebPlatform.mjs Optimized idle loop handling and removed redundant code
tests/* Added comprehensive tests for RTL layout, text mirroring, and bidirectional rendering
docs/* Added RTL documentation and updated text rendering guides
playwright.config.ts Added font rendering flags for consistent screenshot testing

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

app.children = [element];
const texture = app.tag("Item").texture;
stage.drawFrame();
console.log(texture.source.renderInfo.lines);
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Console.log statements should be removed before merging to production. These appear to be debug statements left in the test code.

Copilot uses AI. Check for mistakes.
for (let i = n - 1; i >= 0; i--) {
const event = path[i]._getMostSpecificHandledMember(events);
const target = path[i];
const events = isHorizontalDirection && target.rtl ? eventsRtl : eventsLtr;
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable shadowing: the events parameter is shadowed by the local events variable on line 336. This makes the code confusing and could lead to bugs. Consider renaming the local variable to something like targetEvents or resolvedEvents.

Copilot uses AI. Check for mistakes.
app.children = [element];
const texture = app.tag("Item").texture;
stage.drawFrame();
console.log(texture.source.renderInfo.lines);
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Console.log statements should be removed before merging to production. These appear to be debug statements left in the test code.

Copilot uses AI. Check for mistakes.
Comment on lines +87 to +102
let mirroring = sourceW < 0 || sourceH < 0;
if (mirroring) {
ctx.save();
if (sourceW < 0) {
drawSourceX = sourceX + sourceW;
drawSourceW = -sourceW;
ctx.scale(-1, 1);
destX = -vc.w;
}
if (sourceH < 0) {
drawSourceY = sourceY + sourceH;
drawSourceH = -sourceH;
ctx.scale(1, -1);
destY = -vc.h;
}
}
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mirroring logic uses sourceW < 0 || sourceH < 0 to detect mirroring, but doesn't handle the case where the texture dimensions might legitimately be negative for other reasons (if that's possible). Additionally, when both width and height are negative, the scale operations ctx.scale(-1, 1) and ctx.scale(1, -1) are applied separately. These should be combined into a single ctx.scale(-1, -1) for clarity and potential performance improvement.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No textures aren't meant to have negative dimensions

stage.drawFrame();
chai.assert(texture.source.renderInfo.lines.length === 10);
chai.assert(texture.source.renderInfo.lines.slice(-1)[0].substr(-6) == 'eget..');
chai.assert(getLineText(texture.source.renderInfo.lines.slice(-1)[0]).substr(-5) == 'neq..');
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expected suffix changed from 'eget..' (6 characters) to 'neq..' (5 characters), but the substr(-6) was changed to substr(-5). This suggests the expected text changed. Please verify this is the correct expected value for this test case, as this appears to be a behavioral change rather than just a refactoring.

Copilot uses AI. Check for mistakes.
Comment on lines +355 to +363
let { text, width, style, rtl } = last;
if (style) {
ctx.font = style.font;
}
const reversed = primaryRtl !== rtl;
do {
text = reversed ? trimWordStart(text, rtl) : trimWordEnd(text, rtl);
width = measureText(ctx, text, letterSpacing);
} while (width > maxWidth);
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial value of width is unused, since it is always overwritten.

Suggested change
let { text, width, style, rtl } = last;
if (style) {
ctx.font = style.font;
}
const reversed = primaryRtl !== rtl;
do {
text = reversed ? trimWordStart(text, rtl) : trimWordEnd(text, rtl);
width = measureText(ctx, text, letterSpacing);
} while (width > maxWidth);
let { text, style, rtl } = last;
if (style) {
ctx.font = style.font;
}
const reversed = primaryRtl !== rtl;
let width = measureText(ctx, text, letterSpacing);
while (width > maxWidth) {
text = reversed ? trimWordStart(text, rtl) : trimWordEnd(text, rtl);
width = measureText(ctx, text, letterSpacing);
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we want to use the value in last instead of calling measureText for the initial value

entry = resolve(__dirname, 'src/textures/bidiTokenizer.ts');
outputBase = 'bidiTokenizer';
sourcemap = true;
useDts = true;
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value assigned to useDts here is unused.

Copilot uses AI. Check for mistakes.

// https://www.unicode.org/reports/tr9/
const reZeroWidthSpace = /[\u200B\u200E\u200F\u061C]/;
const reDirectionalFormat = /[\u202A\u202B\u202C\u202D\u202E\u202E\u2066\u2067\u2068\u2069]/;
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Character '\u202E' is repeated in the same character class.

Copilot uses AI. Check for mistakes.
break;
}
}
}
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid automated semicolon insertion (97% of all statements in the enclosing function have an explicit semicolon).

Copilot uses AI. Check for mistakes.
};
default:
return {
suffix: textOverflow || maxLinesSuffix,
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This use of variable 'textOverflow' always evaluates to true.

Suggested change
suffix: textOverflow || maxLinesSuffix,
suffix: textOverflow,

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@michielvandergeest michielvandergeest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@michielvandergeest
Copy link
Contributor

I see we have a blackduck violation, assuming it's not an actual violation I'll see into getting it waivered

Copilot AI review requested due to automatic review settings February 5, 2026 15:23
@jfboeve jfboeve changed the title version 2.20.0 version 2.20.1 Feb 5, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 51 out of 53 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +336 to +337
const events = isHorizontalDirection && target.rtl ? eventsRtl : eventsLtr;
const event = target._getMostSpecificHandledMember(events);
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable name collision: On line 336, the variable events shadows the parameter events from line 326. This could lead to confusion. Consider renaming the local variable to targetEvents to match the pattern used in focusTopDownEvent on line 310.

Suggested change
const events = isHorizontalDirection && target.rtl ? eventsRtl : eventsLtr;
const event = target._getMostSpecificHandledMember(events);
const targetEvents = isHorizontalDirection && target.rtl ? eventsRtl : eventsLtr;
const event = target._getMostSpecificHandledMember(targetEvents);

Copilot uses AI. Check for mistakes.
Comment on lines +126 to +137
if (wordBreak && wordWidth > wrapWidth) {
const broken = breakWord(context, word, wrapWidth, letterSpacing);
let last = broken.pop()!;
for (const k of broken) {
resultLines.push({
text: k.text,
width: k.width,
});
}
result = last.text;
wordWidth = last.width;
}
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The word breaking logic on line 126-137 can produce more lines than maxLines, as noted in the comment. After breaking words, the code should check if resultLines.length >= maxLines and break early if needed, to avoid exceeding the specified limit.

Copilot uses AI. Check for mistakes.
const maxWidth = maxLineWidth - line.width;

if (allowTruncation && maxWidth > 0) {
let { text, width, style, rtl } = last;
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial value of width is unused, since it is always overwritten.

Suggested change
let { text, width, style, rtl } = last;
let { text, style, rtl } = last;
let width: number;

Copilot uses AI. Check for mistakes.
@michielvandergeest michielvandergeest self-requested a review February 5, 2026 15:32
@jfboeve jfboeve merged commit 461fe00 into master Feb 5, 2026
12 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Feb 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants