-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rtl labels support #5771
Rtl labels support #5771
Conversation
Adjusted the text position and used its setter in order to construct the Label Adjusted tests accordinglt
In order to prevent the function from running on each update if no change was made to the text
Wow, very cool thanks for the nice contribution @hodbauer and @YonatanKra! @ggetz could you take a first look at this then pass it on to @bagnell for a final review and merge? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @hodbauer! I have a few comments.
Apps/Sandcastle/gallery/Labels.html
Outdated
@@ -157,6 +168,12 @@ | |||
scaleByDistance(); | |||
Sandcastle.highlight(scaleByDistance); | |||
} | |||
}, { | |||
text : 'Set rtl', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you be more descriptive for the button label? Maybe Set label with right-to-left language
CHANGES.md
Outdated
@@ -2,7 +2,7 @@ Change Log | |||
========== | |||
|
|||
### 1.37 - 2017-09-01 | |||
|
|||
* Added suppport for RTL labels |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this a little more descriptive, maybe Added support for right-to-left languages in labels
Source/Scene/Label.js
Outdated
}, | ||
set : function(value) { | ||
if (this._rtl !== value) { | ||
this._rtl = value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should call reverseRtl
here in addition to when setting the text.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @ggetz for your comments. I will fix them after the weekend.
I have a specific question about this comment. Why to call reverseRtl
here? If the text is written in rtl language, I don't see a reason to change the rtl flag while using it, the algorithm will work good even for ltr (left to right) languages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @hodbauer!
If I understand correctly, if rtl
is set to false, and you update text
to an rtl language, then set rtl
to true, it won't display correctly:
label.text = 'שלום';
label.rtl = true;
That may be confusing to people using this feature.
Source/Scene/Label.js
Outdated
* @param {String} text the text to parse and reorder | ||
* @returns {String} the text as rtl direction | ||
*/ | ||
Label.prototype.reverseRtl = function(value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function should be private, I don't think anyone should be calling this directly.
Specs/Scene/LabelCollectionSpec.js
Outdated
}); | ||
|
||
it('should reverse text when there is only hebrew characters and rtl is true', function() { | ||
var text = 'שלום'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add an additional tests that cover different scenarios like spaces and brackets in words?
Source/Scene/Label.js
Outdated
result += subText.Word; | ||
splicePointer = result.length; | ||
} | ||
else if (subText.Type === types.WEAK || subText.Type ===types.BRACKETS) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spacing after ===
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and on line 1354
Source/Scene/Label.js
Outdated
splicePointer += subText.Word.length; | ||
} | ||
else if (subText.Type === types.WEAK || subText.Type ===types.BRACKETS) { | ||
if (parsedText[wordIndex -1].Type === types.RTL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a space after the operator here and throughout.
* make `reverseRtl` private * fix multi-lines support * add complex test * fix some edge cases: * special characters after rtl characters when direction is not rtl * "Weak" characters after brackets at rtl direction
# Conflicts: # Apps/Sandcastle/gallery/Labels.html # CHANGES.md # CONTRIBUTORS.md # Source/Scene/Label.js # Specs/Scene/LabelCollectionSpec.js
Hi @hodbauer, I see you made updates. Are you ready for another look? |
Hi @ggetz |
@hodbauer, everything looks good except I still think you should reconsider the In the sandcastle example, if I run this:
The rtl portion of the text will be reversed, which is not the same results as this:
|
Ok @ggetz I'll check this case again and update the code according to the results |
I have a question @ggetz. var label = viewer.entities.add({
position : Cesium.Cartesian3.fromDegrees(-75.1641667, 39.9522222),
label : {
text : 'Hi'
}
});
label.text = 'Bye'; I was expect that the label at Cesium will be |
After another check at Sandcastle I've got the next solution.
now it just work fine. |
Are we okay with using |
* fix some edge cases * change the regex that identify right-to-left languages to use unicode instead of hebrew chars for js engines that can't parse this letters (like jscoverage)
…els-support # Conflicts: # CHANGES.md
summary after another iteration:
It tested on Chrome, firefox and edge
hopefully all the branches covered by adding more tests. When Iv'e added the tests I fix some edge cases. |
Sure, I'll try and get to this before the end of the week. |
* @private | ||
*/ | ||
function reverseRtl(value) { | ||
var rtlChars = /[\u05D0-\u05EA]/; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if I'm understanding this code correctly, this regex detects whether the letter is RTL (in this case specifically Hebrew) but otherwise the rest of the code is the same for other languages? So if we update this regex for other known RTL character sets (perhaps thise discussed here and here Is that correct? If so that's awesome and we can easily update this to handle the other languages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but otherwise the rest of the code is the same for other languages?
yes. it only necessary when writing a RTL language in Cesium.
we can easily update this to handle the other languages.
basically yes, I do not do that, because i cannot read other RTL languages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, we can merge this with just Hebrew and then I'll open a second PR with an updated regex to support additional languages.
Source/DataSources/LabelGraphics.js
Outdated
* @type {Property} | ||
* @default false | ||
*/ | ||
rightToLeft: createPropertyDescriptor('rightToLeft') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is basically an optimization, right? If the string doesn't contain RTL characters it is a no-op?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is just an optimization, how do you feel about having a single static Label.enableRightToLeftDetection
variable that an application sets once globally rather than having to set it on every label? It will save memory when there are many labels and I doubt it will have a performance impact in RTL apps since I would expect most of the labels to be RTL in that case. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds very interesting.
I'm not sure 100% how to implement it, but if it is the right way then I should do it as you offered
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
after a little bit of thinking I understand how to implement it, it will clean a lot of the work and it is nice.
my question is: how to declare the static variable so each application could manage it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just add a property directly on the Label
object. Something like this should work.
/**
* Enabled detection and support of right to left text.
*
* @type {Boolean}
* @default false
*
* @example
* //Set once at the start of the application
* Cesium.Label.enableRightToLeftDetection = true;
*
* var myLabelEntity = viewer.entities.add({
* label: {
* id: 'my label',
* text: 'זה טקסט בעברית \n ועכשיו יורדים שורה'
* }
* });
*/
Label.enableRightToLeftDetection = false;
This will show up as static in the documentation and users just set Cesium.Label.enableRightToLeftDetection = true
once in their app to use it. In your own code, you would just check Label.enableRightToLeftDetection
directly as well.
Thanks.
Source/Scene/Label.js
Outdated
@@ -1196,5 +1249,184 @@ define([ | |||
return false; | |||
}; | |||
|
|||
function declareTypes() { | |||
var TextTypes = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just declare this at the top of the file instead of creating it every time. Then just use TextTypes
everywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mramato If I understand you, you want me to change the code to something like:
var TextTypes = freezeObject({
LTR : 0,
RTL : 1,
WEAK : 2,
BRACKETS : 3
});
return TextTypes;
and put it inside the define near to the glyph functions as is (without a function scope)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, though you won't need the return
statement. You can just put the declaration at the top of the file (above rebindAllGlyphs
), it will be scoped to Label.js.
I merged in master for you and just have those few suggestions/questions. Thanks again for the awesome work here, I'm sure a huge section of our community will be excited to have RTL support at last. |
# Conflicts: # CHANGES.md
I don't know why but I've got this error while running travis ci:
Any help about this issue, it not part of my changes |
@hodbauer I fixed that test in master, so if you merge that into your branch CI should pass. Thanks! |
As @mramato offered, I changed the way to enable the right-to-left algorithm. Label.enableRightToLeftDetection = true;
var text = 'שלום';
var label = labels.add({
text : text
});
scene.renderForSpecs();
expect(label.text).not.toEqual(text);
expect(label.text).toEqual(text.split('').reverse().join('')); This test failed although I enable the right-to-left algorithm. moreover, when I've checked the sandecastle it worked as expected. |
It looks like your code should be activating it, so I'm not sure what's going on. I can dig in a little further and let you know what I find. |
The code was OK, the problem was that during creation of a Label nothing calls to the text setter, so nothing run the algorithm. this.text = defaultValue(options.text, ''); my friend @YonatanKra that help me with this PR done it before. and me, by mistake, removed it in the last changes. |
Source/Scene/Label.js
Outdated
return; | ||
} | ||
this._originalValue = value; | ||
value = reverseRtl(value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just noticed that we are mutating the value supplied by the user so that the getter is actually the reversed text. I think this is undesired behavior. With few exceptions, a simple setter/getter should always return the value assigned to it. I can submit a change to prevent this, but if you feel strongly about it, please let me know.
Awesome work here @hodbauer and @YonatanKra! I submitted the tweak I mentioned, but otherwise I think this is ready so I'm going to merge. If you find any issues or I accidentally broke something with my tweaks, please let me know. Thanks again! |
reference: #2543
For usage see Label documentation and Label sandcastle example.
Supports new lines
Currently supports Hebrew (should easily support other RTL languages)
Tasks:
משתמש10
)