6 Replies Latest reply on Jan 4, 2011 9:50 AM by Michal Kujawski

    Custom multiline HBox component

    K-kOo Level 1

      Hello everyone,

       

      I am trying to create my own component based on a VBox. What i would like to do is create a new "row" (a HBox) when there is not enough space anymore to display the last child (right side).

       

       -------------
      | XXXX XXX    |
       -------------
      
      trying to add a new element of this size XXXXXX would give :
      
       -------------
      | XXXX XXX    |
      | XXXXXX      |
       -------------
      

       

      The overall result would look like some kind of tilelist except that we can have items with different width.

      What I have right now is this (doesn't work) :

       

      public class MultilineHBox extends VBox
      {
      
            // private var, public var ....
      
           public function MultilineHBox()
           {
                super();
                horizontalScrollPolicy = "off";
           }
           
           override protected function createChildren():void
           {
                // first we add the very first row. 
                addRow();
                super.createChildren();
           }
           
           override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
           {
                super.updateDisplayList(unscaledWidth, unscaledHeight);
      
                var w:Number = currentRow.width;
                if (w > unscaledWidth)
                {
                   var i:int;
                         // how many child are going over the right limit ?
                       for (i = currentRow.getChildren().length - 1; w > unscaledWidth; --i)
                         w -= UIComponent(currentRow.getChildAt(i)).measuredWidth;
                       var previousRow:HBox = currentRow;
                         addRow();
                         // move them to the new row
                       for (; i < previousRow.getChildren().length; ++i)
                         currentRow.addChild(previousRow.getChildAt(i));
                       invalidateDisplayList();
                }
           }
           
           override public function addChild(child:DisplayObject):DisplayObject
           {
                if (addingRow)
                {
                     addingRow = false;
                     return super.addChild(child);
                }
                
                currentRow.addChild(child);
                invalidateDisplayList();                    
                return child;
           }
                
           private function addRow(firstChild:DisplayObject=null):void
           {
                currentRow = instanciateRow(); // create a configured HBox
                if (firstChild)
                     currentRow.addChild(firstChild);
                
                addingRow = true;
                addChild(currentRow);
                invalidateDisplayList;
           }
      
             // other unrelevant methods
      }
      

       

      And Im using it this way

       

      <common:MultilineHBox2 id="tagList" width="245" height="33" top="48" left="10">
           <mx:Button label="lol"/>
           <mx:Button label="toto 1"/>
           <mx:Button label="lol 2"/>
           <mx:Button label="kikoo 3"/>
           <mx:Button label="lol 4"/>
           <mx:Button label="kikoo 5"/>               
      </common:MultilineHBox2>
      

       

      If i run this I can only see 1 line, and the buttons are goigne beyond the right limits of course.

      Is someone comfortable enough with the famous "3 phases" of invalidation to explain me what am I supposed to do ?

       

      Thank you !

        • 1. Re: Custom multiline HBox component
          leybniz Level 4

          package {

          import mx.containers.HBox;
          import mx.containers.VBox;
          import mx.core.ScrollPolicy;
          import mx.core.UIComponent;
          import mx.events.ResizeEvent;

          public class mBox extends VBox {
           
            protected var rendered:Boolean;
            protected var originalItems:Array;
           
            function mBox() {
             horizontalScrollPolicy = ScrollPolicy.OFF;
             addEventListener(ResizeEvent.RESIZE, onResize);
            }
           
            protected function render():void {
            
             function newRow():HBox {
              currentRow = new HBox();
              addChild(currentRow);
              return currentRow;
             } 
            
             if (!rendered) {   
              var currentRow:HBox;
              var sum:int = 0;
              var subWidth:int = 0;

              if (!originalItems)
               originalItems = getChildren();

              removeAllChildren();
             
              newRow();
              for each (var d:UIComponent in originalItems) {
               
               subWidth = d.measuredWidth;
               // count up horizontalGap
               subWidth += currentRow.getChildren().length > 0 ? currentRow.getStyle('horizontalGap') : 0;
              
               if (sum + subWidth < width) {
                sum += subWidth;     
                currentRow.addChild(d);
               } else {
                newRow();
                currentRow.addChild(d);
                sum = d.measuredWidth;
               }
               
              }
             
              rendered = true;
             }
            }
           
            protected function onResize(e:ResizeEvent):void {  
             rendered = false;
            }
           
            override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
             render();
             super.updateDisplayList(unscaledWidth, unscaledHeight);
            }
           
          }
          }

          Here is what I could get in an hour with your idea, hope this helps

          • 2. Re: Custom multiline HBox component
            K-kOo Level 1

            Well I guess this is it ...

            What made you decide to use render instead of the updateDisplaylist ?

             

            I think my problem was that i'm still not use to "erase all" each time we try to reDisplay the allthing. I have to keep that in mind

            • 3. Re: Custom multiline HBox component
              leybniz Level 4

              I came out with separate method for rendering just to clean up the updateDisplaylist itself,

              I'm in love with clean and separate logic

              • 4. Re: Custom multiline HBox component
                K-kOo Level 1

                Sorry for my previous answer. I just got up from my bed ... I didnt even see that your were using updateDisplayList I though for some reason render was a existing flex method. And I forgot to say Thank you

                 

                Am I right with the "erase all" thing. This is how it should work in updateDisplayList. Doing as if it's the first time we display the component (but still need to clear the previous screen) ?

                • 5. Re: Custom multiline HBox component
                  leybniz Level 4

                  not at all , I just need this kind of container for myself, so it's a win-win code

                  sure, "erase all" is a must have since the container can be resized further and all internal original childs needs to be re-rendered.

                  • 6. Re: Custom multiline HBox component
                    Michal Kujawski

                    Hi,

                    thank you Alexander for creating this component although it has some issues. This code (fully based on yours) should work just fine:

                     

                    package
                    {

                    import mx.containers.HBox;
                    import mx.containers.VBox;
                    import mx.controls.Button;
                    import mx.core.ScrollPolicy;
                    import mx.core.UIComponent;
                    import mx.events.ResizeEvent;

                        public class MBox extends VBox
                        {
                        
                            protected var rendered:Boolean;
                            protected var originalItems:Array;
                        
                            function MBox()
                            {
                                horizontalScrollPolicy = ScrollPolicy.OFF;
                                addEventListener(ResizeEvent.RESIZE, onResize);
                            }
                        
                            protected function render():void
                            {
                               
                            function newRow():HBox
                            {
                                currentRow = new HBox();
                                addChild(currentRow);
                                return currentRow;
                            }
                         
                            if (!rendered)
                            {  
                                var currentRow:HBox;
                                var sum:int = 0;
                                var subWidth:int = 0;
                               
                                if (!originalItems) originalItems = getChildren();
                               
                                removeAllChildren();
                                  
                                newRow();
                               
                                for each (var d:UIComponent in originalItems)
                                {
                                    subWidth = d.width;
                                    // count up horizontalGap
                                    subWidth += currentRow.getChildren().length > 0 ? currentRow.getStyle('horizontalGap') : 0;
                                   
                                    if (sum + subWidth < width)
                                    {
                                        sum += subWidth;    
                                        currentRow.addChild(d);
                                    }
                                    else
                                    {
                                        newRow();
                                        currentRow.addChild(d);
                                        sum = d.width;
                                    }
                                }
                                rendered = true;
                            }
                        }
                        
                            protected function onResize(e:ResizeEvent):void
                            { 
                                rendered = false;
                            }
                        
                            override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
                            {
                                render();
                                super.updateDisplayList(unscaledWidth, unscaledHeight);
                            }
                        }
                    }

                     

                    There was a problem with objects' "measuredWidth" which was not available at the time when it was needed, switching it to the "width" did the trick.

                     

                    Also if you're planning on adding or removing dynamically some objects to this component's child list, you should rethink this part of the code:

                    if (!originalItems) originalItems = getChildren();

                     

                    As it will probably break everything when objects are added/removed to the child list.