Author Topic: UILabel disappears (Dynamic font, OnFontChanged)  (Read 7317 times)

dafunker

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 7
    • View Profile
UILabel disappears (Dynamic font, OnFontChanged)
« on: September 24, 2015, 10:00:45 AM »
NGUI 3.9.2, Unity 4.6.8f1 (because Unity 5.x is !@#$% for the moment)

Test case
Create an empty scene, insert a NGUI Input with a dynamic font.
Set an important font size, ~100
Launch and write some letters, UILabel will render an empty string at one moment.
This moment is when UIFont TextureAtlas is not enough big to store all requested glyphs.
So OnFontChanged is called while we are in NGUIText.WrapText().
This method refreshes UIPanel of every UILabel linked to the same Dynamic Font, including the one who triggers the OnFontChanged, so the current UILabel is rendered as en empty string and its mChanged value is set to false.
WrapText complete but mChanged = false, so the UILabel is not updated.

Temporary fix applied
I added this just after NGUIText.WrapText in UILabel.cs, line 1281.
  1. mChanged = true;
  2.  

Another fix would consist in modifying OnFontChanged handler and avoiding updating panel of current UILabel being processed.
Is it possible ArenMook ?

dafunker

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 7
    • View Profile
Re: UILabel disappears (Dynamic font, OnFontChanged)
« Reply #1 on: September 24, 2015, 06:38:35 PM »
  1.         /// <summary>
  2.         /// Notification called when the Unity's font's texture gets rebuilt.
  3.         /// Unity's font has a nice tendency to simply discard other characters when the texture's dimensions change.
  4.         /// By requesting them inside the notification callback, we immediately force them back in.
  5.         /// Originally I was subscribing each label to the font individually, but as it turned out
  6.         /// mono's delegate system causes an insane amount of memory allocations when += or -= to a delegate.
  7.         /// So... queue yet another work-around.
  8.         /// </summary>
  9.  

This workaround is a disaster.
When Unity's developers make us develop dangerous optimizations...

Another bug detected, still a UILabel that vanish.
If a UILabel triggers a OnFontChanged, other UILabels around that use the same font will update instantaneously via UIPanel.Refresh(). This immediat update will reset NGUIText static class' properties (including dynamicFont), then the UIlabel that was processing won't have a good wrap and will render an empty string.

dafunker

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 7
    • View Profile
Re: UILabel disappears (Dynamic font, OnFontChanged)
« Reply #2 on: September 24, 2015, 06:47:06 PM »
By the way, here is my fix. (it replace the older one)

UILabel.cs / L1277
  1.         // Wrap the text
  2.         bool fits = NGUIText.WrapText(mText, out mProcessedText, true, false, mOverflowEllipsis && mOverflow == Overflow.ClampContent);
  3.         // Fucking fix for the fucking UILabels that fucking disappear.
  4.         // (Thank you static NGUIText... shared between all UILabel's instances)
  5.         if (NGUIText.dynamicFont == null)
  6.         {
  7.                 ProcessText(legacyMode, full);
  8.                 return ;
  9.         }
  10.  

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile

dafunker

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 7
    • View Profile
Re: UILabel disappears (Dynamic font, OnFontChanged)
« Reply #4 on: September 28, 2015, 07:34:52 AM »
Why are UILabels broken ?

Because what does UILabel.OnFontChanged's is bad.

If a UILabel triggers a OnFontChanged, other UILabels around that use the same font will update instantaneously via UIPanel.Refresh(). This immediat update will reset NGUIText static class' properties (including dynamicFont), then the UIlabel that was processing won't have a good wrap and will render an empty string.

Moreover, when updating Font size via UnityEditor, you would see
Quote
Destroying GameObjects immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.
Because, this OnFontChanged follows a OnValidate().

In my opinion, Refreshing UIPanels during a OnFontChanged callback may not be necessary.
The others UILabels are already rendered, the current UILabel being changed gonna be rendered well after this callback.
ArenMook, is there a reason to those UIPanel updates ?

If refreshing UIPanels is mandatory, this had to be done differently, maybe by doing it in a LateUpdate or something else.

If you are experiencing a UILabel issue with NGUI 3.9.2, i would strongly recommend you try to replace UILabel.cs OnFontChanged handler, line970 :

  1.         static void OnFontChanged (Font font)
  2.         {
  3.                 for (int i = 0; i < mList.size; ++i)
  4.                 {
  5.                         UILabel lbl = mList[i];
  6.  
  7.                         if (lbl != null)
  8.                         {
  9.                                 Font fnt = lbl.trueTypeFont;
  10.  
  11.                                 if (fnt == font)
  12.                                 {
  13.                                         fnt.RequestCharactersInTexture(lbl.mText, lbl.mFinalFontSize, lbl.mFontStyle);
  14.                                         lbl.RemoveFromPanel();
  15.                                         lbl.CreatePanel();
  16.  
  17.                                         if (mTempPanelList == null)
  18.                                                 mTempPanelList = new List<UIPanel>();
  19.  
  20.                                         if (!mTempPanelList.Contains(lbl.panel))
  21.                                                 mTempPanelList.Add(lbl.panel);
  22.                                 }
  23.                         }
  24.                 }
  25.  
  26.                 if (mTempPanelList != null)
  27.                 {
  28.                         for (int i = 0, imax = mTempPanelList.Count; i < imax; ++i)
  29.                         {
  30.                                 UIPanel p = mTempPanelList[i];
  31.                                 p.Refresh();
  32.                         }
  33.                         mTempPanelList.Clear();
  34.                 }
  35.         }
  36.  

by

  1.         static void OnFontChanged (Font font)
  2.         {
  3.                 for (int i = 0; i < mList.size; ++i)
  4.                 {
  5.                         UILabel lbl = mList[i];
  6.  
  7.                         if (lbl != null)
  8.                         {
  9.                                 Font fnt = lbl.trueTypeFont;
  10.  
  11.                                 if (fnt == font)
  12.                                 {
  13.                                         fnt.RequestCharactersInTexture(lbl.mText, lbl.mFinalFontSize, lbl.mFontStyle);
  14.                                         lbl.RemoveFromPanel();
  15.                                         lbl.CreatePanel();
  16.                                 }
  17.                         }
  18.                 }
  19.         }
  20.  

mdeletrain

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 71
    • View Profile
Re: UILabel disappears (Dynamic font, OnFontChanged)
« Reply #5 on: September 29, 2015, 07:16:42 AM »
Aren added this refresh as per my request, because when labels are updated, they are just marked as dirty, and their actual update takes place the next frame.
That was used to cause garbage to be visible during one frame : http://www.tasharen.com/forum/index.php?topic=13414.msg60206#msg60206

Alas this fix triggers another bug due to the use of a single static NGUIText instance everywhere : http://www.tasharen.com/forum/index.php?topic=13479.0
The fix proposed here also has some issues, and there is still no proper solution for this.

Since I really need dynamic font to be working I'm quite interesting in getting it working fine. I'll probably spent some time this week to fix all this once and for all.

taorui

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 4
    • View Profile
Re: UILabel disappears (Dynamic font, OnFontChanged)
« Reply #6 on: October 02, 2015, 06:03:42 AM »
I am trying to update my NGUI up to 3.9.3 in Unity 4.6.7f1 and also got a problem on OnFontChanged().
The error occurs at line 985 in UILabel.cs
  1. for (int i = 0, imax = mTempPanelList.Count; i < imax; ++i)
  2. {
  3.      UIPanel p = mTempPanelList[i];
  4.      p.Refresh();
  5. }
  6.  
which told me that the index i is invalid and Unity throws out an ArgumentOutOfRangeException.
It seems that the mTempPanelList has been changed during the for loop performing.

When I found this post I tried to replace OnFontChanged as same as dafunker posted above, then everything seems work well.
I am not clearly know why it happens but thanks to dafunker for his/her effort.
Looking forward a better solution come out.

mdeletrain

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 71
    • View Profile
Re: UILabel disappears (Dynamic font, OnFontChanged)
« Reply #7 on: October 02, 2015, 07:37:26 AM »
That's because OnFontChanged callback is called inside itself, and mTempPanelList is a static list which gets used by different nested calls.
One possible temporarily fix would be to have this list be created inside the OnFontChanged callback.
Final fix would also need to avoid nested OnFontChanged callback calls by reclaiming all used characters at once... I plan to work on this very soon.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile