tags:

views:

2375

answers:

4

Grrr so close yet still failing...

I display this tree, in Flex, which contains two nodes types:

  1. Regular nodes, which are rendered normally like text (because they are!)
  2. Rich (HTML) nodes - that's where things get twisted

Note that my issue is when I dynamically add a new (html) node to my tree.


So... How do I display HTML nodes?

  1. I subclass TreeItemRenderer
  2. In that subclass, I override set data() and add a text child to my renderer

Therefore I now have:

[icon] [label]

[text component]

Why?

The default label is a pure text component, not HTML-capable, hence the extra component: I want to display the new guy and forget the default label.

  1. (continued) I override updateDisplayList() and, if the node is a rich one, I set label's height to zero, set my component's x and y to label'x and and y.

So...what am I missing? Ah, yes: I need to set my node's height since HTML text can be bigger or smaller than its text counterpart.

  1. (continued) I override measure()
  2. If my node is not a rich one, I simply invoke super.measure() and return
  3. If it is a rich one, I give my html component a width (htmlComponent.width = explicitWidth - super.label.x;) and its height should be automatically computed.

This gives me a fairly reliably unreliable result!

When I fold/unfold my tree, every other time, I seem to get a correct height for my HTML node. The other time I get a height of '4' which happens to be the HTML component's padding alone, without content.

I know that I must be doing something fairly stupid here...but I am not sure what. I will post my code if my rambling is too incoherent to make any sense of...

**** EDIT: here is the source code for my renderer As you can see, only 'notes' nodes use HTML. I add a 'htmlComponent' child that will display the rich text while the default label is zero-sized and disappears. It's definitely very raw code, as it's in progress!

    package com.voilaweb.tfd
{
 import mx.collections.*;
 import mx.controls.Text;
 import mx.controls.treeClasses.*;
 import mx.core.UITextField;
 import mx.core.UIComponent;
 import flash.text.TextLineMetrics;

 public class OutlinerRenderer extends TreeItemRenderer
 {
  private function get is_note():Boolean
  {
   return ('outlinerNodeNote' == XML(super.data).name().localName);
  }

  override public function set data(value:Object):void
  {
   super.data = value;
   var htmlComponent:Text = super.getChildByName("htmlComponent") as Text;
   if(!htmlComponent)
   {
    htmlComponent = new Text();
    htmlComponent.name = "htmlComponent";
    addChild(htmlComponent);
   }
   if(is_note)
    htmlComponent.htmlText = XML(super.data).attribute('nodeText');
   else
    htmlComponent.htmlText = null;
   setStyle('verticalAlign', 'top');
  }

 /*
  * Today we've learnt a valuable lesson: there is no guarantee of when createChildren() will be invoked.
  * Better be dirty and add children in set data()
  override protected function createChildren():void
  {
   super.createChildren();
   var htmlComponent:Text = new Text();
   htmlComponent.name = "htmlComponent";
   addChild(htmlComponent);
  }
*/
  override protected function measure():void
  {
    if(is_note)
    {
     super.measure();
     var htmlComponent:Text = super.getChildByName("htmlComponent") as Text;
   //Setting the width of the description field
   //causes the height calculation to happen
     htmlComponent.width = explicitWidth - super.label.x;
   //We add the measuredHeight to the renderers measured height
   //measuredHeight += (htmlComponent.measuredHeight - label.measuredHeight);
   // Note the silly trick here...hopefully in the future I figure out how to avoid it
   //
   // Here is what happens: we check if measuredHeight is equal to decoration such as margin, insets...rather than that + some height
   // If so, then we need to come up with an actual height which we do by adding textHeight to this height

   // Note that I care about text being equal to margin etc but do not have proper access to these
   // For instance UITextField.TEXT_HEIGHT_PADDING == 4 but is not accessible
   // I am going to check if "<10" that will cover this case...
   trace("For text " + htmlComponent.htmlText);
   trace("width = " + htmlComponent.getExplicitOrMeasuredWidth()+" x height = " + htmlComponent.getExplicitOrMeasuredHeight());
   var m:TextLineMetrics = htmlComponent.measureHTMLText(htmlComponent.htmlText);
   //if(10 > htmlComponent.measuredHeight && !isNaN(htmlComponent.explicitHeight))
   //htmlComponent.explicitHeight = m.height + htmlComponent.measuredHeight;
   //if(htmlComponent.measuredHeight < 10) htmlComponent.explicitHeight = 50;

   //measuredHeight += (htmlComponent.getExplicitOrMeasuredHeight() - super.label.getExplicitOrMeasuredHeight());
   measuredHeight += (htmlComponent.getExplicitOrMeasuredHeight() - label.getExplicitOrMeasuredHeight());
   trace("m:"+m.height+" Height: " + htmlComponent.getExplicitOrMeasuredHeight());
    }
    else
    {
   super.measure();
    }
  }     

  override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
  {
   super.updateDisplayList(unscaledWidth, unscaledHeight);
   label.height = label.getExplicitOrMeasuredHeight(); // If you tell me my height, then I shall use my variable height!

   graphics.clear();

   if(is_note)
   {
    label.height = 0;
      var htmlComponent:Text = super.getChildByName("htmlComponent") as Text;
    htmlComponent.x = label.x;
    htmlComponent.y = label.y;
    htmlComponent.height = htmlComponent.getExplicitOrMeasuredHeight();

    graphics.beginFill(0x555555);
    graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
    graphics.endFill();
   }

   var complete:XMLList = XML(super.data).attribute('complete');
   if(complete.length() > 0 && true == complete[0])
   {
    var startx:Number = data ? TreeListData(listData).indent : 0;
    if(disclosureIcon)
     startx += disclosureIcon.measuredWidth;
    if(icon)
     startx += icon.measuredWidth;
    graphics.lineStyle(3, getStyle("color"));
    var y:Number = label.y + label.getExplicitOrMeasuredHeight() / 2;
    graphics.moveTo(startx, y);
    graphics.lineTo(startx + label.getExplicitOrMeasuredWidth(), y);
   }
  }
 }
}
A: 

It would certainly help if you could post some code.

I was wondering though why you are using a custom html renderer. Is it because you want to display an icon next to the label since you mention [icon] [label]? If so, you're probably better off using an iconField or iconFunction.

Another thing that comes to mind is the variableRowHeight property. You might need to set this if your nodes have different heights.

Christophe Herreman
Posted code!No, it's not about the icon, see my code comments.
Fusion
A: 

Did you get an answer to this question?

Shef
No, unfortunately. And I haven't resolved it myself either.
Fusion
A: 

Try it.

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"
           width="100%" height="100%" verticalScrollPolicy="off"
           horizontalScrollPolicy="off">

<mx:Script>
   <![CDATA[

      import mx.core.UITextField;

      private var texto:UITextField;

      override protected function createChildren():void
      {
         super.createChildren();

         texto = new UITextField();

         texto.setColor(0xFFFFFF);

         texto.multiline = true;
         texto.wordWrap = true;

         texto.autoSize = TextFieldAutoSize.LEFT;

         this.addChild(texto);

         //texto.text = data.title;
      }


      override public function set data(value:Object):void
      {
         super.data = value;
         if (value)
         {
          texto.htmlText = value.title;
          this.invalidateDisplayList();
         }
      }

      override protected function measure():void
      {
        super.measure();
      }

      override protected function updateDisplayList(unscaledWidth:Number,
                                       unscaledHeight:Number):void
      {
         super.updateDisplayList(unscaledWidth, unscaledHeight);

         if (texto)
          texto.width = this.width;
      }

   ]]>
</mx:Script>   

</mx:Canvas>
javi
A: 

You made false assumption about label component in default renderer - it is capable of displaying html content. This renderer works for me:

    public class HtmlTreeItemRenderer extends TreeItemRenderer {
    override protected function commitProperties():void {
        super.commitProperties();
        label.htmlText = data? listData.label : "";
        invalidateDisplayList();
    }
}
Michał Samujło