views:

4443

answers:

10

I have a custom ItemRenderer that displays 5 text inputs in each of 3 panels:

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    height="300"
    width="800"
    creationComplete="onCreationComplete()"
>
    <!-- code-behind -->
    <mx:Script source="ChainListRenderer.mxml.as" />

    <mx:Label text="{data.title}" fontSize="25" fontWeight="bold" width="100%" textAlign="center" />
    <mx:HBox>
     <mx:Panel id="triggerPanel" title="Trigger" width="260">
      <mx:VBox id="tpBoxes" width="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
       <mx:TextInput id="trigger1" width="100%" textAlign="left" tabIndex="0" tabEnabled="true" />
       <mx:TextInput id="trigger2" width="100%" textAlign="left" tabIndex="1" tabEnabled="true" />
       <mx:TextInput id="trigger3" width="100%" textAlign="left" tabIndex="2" tabEnabled="true" />
       <mx:TextInput id="trigger4" width="100%" textAlign="left" tabIndex="3" tabEnabled="true" />
       <mx:TextInput id="trigger5" width="100%" textAlign="left" tabIndex="4" tabEnabled="true" />
      </mx:VBox>
     </mx:Panel>
     <mx:Panel id="linkPanel" title="Link" width="260">
      <mx:VBox id="lpBoxes" width="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
       <mx:TextInput id="link1" width="100%" textAlign="left" tabIndex="5" tabEnabled="true" />
       <mx:TextInput id="link2" width="100%" textAlign="left" tabIndex="6" tabEnabled="true" />
       <mx:TextInput id="link3" width="100%" textAlign="left" tabIndex="7" tabEnabled="true" />
       <mx:TextInput id="link4" width="100%" textAlign="left" tabIndex="8" tabEnabled="true" />
       <mx:TextInput id="link5" width="100%" textAlign="left" tabIndex="9" tabEnabled="true" />
      </mx:VBox>
     </mx:Panel>
     <mx:Panel id="answerPanel" title="Answer" width="260">
      <mx:VBox id="apBoxes" width="100%" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
       <mx:TextInput id="answer1" width="100%" textAlign="left" tabIndex="10" tabEnabled="true" />
       <mx:TextInput id="answer2" width="100%" textAlign="left" tabIndex="11" tabEnabled="true" />
       <mx:TextInput id="answer3" width="100%" textAlign="left" tabIndex="12" tabEnabled="true" />
       <mx:TextInput id="answer4" width="100%" textAlign="left" tabIndex="13" tabEnabled="true" />
       <mx:TextInput id="answer5" width="100%" textAlign="left" tabIndex="14" tabEnabled="true" />
      </mx:VBox>
     </mx:Panel>
    </mx:HBox>
</mx:VBox>

Unfortunately, when used as an ItemRenderer, tabbing between the text inputs doesn't work, even with the tabIndex values above. If I copy this code to an MXML application of its own, tabbing between text inputs works as expected.

Does anyone know how to restore tabbing in this scenario? It will be a shame if I have to release this app without such a simple usability element.

I suppose I may need to implement mx.managers.IFocusManagerComponent, but I can't find any examples on how to do that, and the FocusManager docs aren't helping either.

A: 

I think I may be moving in the right direction, but I'm not entirely there yet.

I have my main application, with the HorizontalList using a custom ItemRenderer:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
    xmlns:mx="http://www.adobe.com/2006/mxml" 
    xmlns:com="ventures.view.component.*"
    layout="absolute"
    backgroundColor="#ffffff"
    preinitialize="onPreInitialize()"
    click="onClick(event)"
>
    <!-- code-behind -->
    <mx:Script source="ConsumptionChain.as" />

    <!-- show chain links -->
    <mx:HorizontalList
     id="ChainList"
     direction="horizontal"
     dataProvider="{chainData}"
     rollOverColor="#ffffff"
     selectionColor="#ffffff"
     horizontalScrollPolicy="off"
     left="20"
     right="20"
     top="20"
     height="300"
     minWidth="802"
     rowHeight="300"
     columnWidth="800"
     tabChildren="false"
     itemRenderer="ventures.view.ItemRenderer.ChainListRenderer"
    />

</mx:Application>

I've updated the code sample in the original question to contain the entire ItemRenderer MXML (applicable portions); and here's the ActionScript code-behind:

/*
 * ChainListRenderer.mxml.as -- code-behind for ChainListRenderer.mxml
 */

import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;

//used to combine all textboxes in a single array to make looping through them with the TAB key easier
private var allBoxes:Array;

public function expandPanel(e:Event):void {
    trace(e.currentTarget);                
    var state : String = e.currentTarget.parent.parent.id;                
    if (state != this.currentState)
        this.currentState = state;
}

private function onCreationComplete():void{
    //this function will be run on each object via the map function
    function forEach(o:Object, index:int, ar:Array):void{
        o.addEventListener(FocusEvent.FOCUS_IN, expandPanel)
     o.addEventListener(MouseEvent.CLICK, stopBubble);   //don't propagate click events (which return to base state)
     o.addEventListener(KeyboardEvent.KEY_DOWN, customTabbing); //fix tabbing between text fields
    }

    this.addEventListener(KeyboardEvent.KEY_DOWN, customTabbing);

    //loop over textboxes and add the focusIn event listener
    tpBoxes.getChildren().map(forEach);
    lpBoxes.getChildren().map(forEach);
    apBoxes.getChildren().map(forEach);

    //create an "allBoxes" array that is used by the customTabbing function
    allBoxes = tpBoxes.getChildren();
    function forEachTabbing(o:Object, index:int, ar:Array):void {
     allBoxes.splice(allBoxes.length, 0, o);
    }
    lpBoxes.getChildren().map(forEachTabbing);
    apBoxes.getChildren().map(forEachTabbing);
}

//this function is used to prevent event bubbling
private function stopBubble(e:Event):void {
    e.stopPropagation();
}

//this function re-implements tabbing between text fields, which is broken when inside an itemRenderer
public function customTabbing(e:KeyboardEvent):void {
    trace('keyCode: ' && e.keyCode.toString());
    if (e.keyCode == flash.ui.Keyboard.TAB){
     trace(focusManager.getFocus());
     //loop over array of all text-boxes
     for (var i:int = 0; i < allBoxes.length; i++){
      trace(i.toString());
      //if event (keypress) current target is the current TextInput from the array
      if (e.currentTarget == allBoxes[i]){
       //then focus the NEXT TextInput, and wrap if at last one
       allBoxes[((i+1) % allBoxes.length)].setFocus();
       //break out of the loop
       break;
      }
     }

     //prevent arrow keys from navigating the horizontal list
     e.stopPropagation();
    }
}

Essentially, what I'm attempting to do here is re-implement tabbing by checking for the TAB key on key_down event of each text field, and using that to move focus to the next TextInput (wrapping to the first TextInput from the last). This doesn't have the desired effect though, and the focus still jumps out to the next control outside of this HorizontalList.

Any ideas where to go from here?

Adam Tuttle
A: 

Why are you doing tabChildren="false"? Don't you want to tab the children of the HorziontalList? since the itemRenderer is a child of the list....

Shua
I wasn't originally. It was something I tried out of desperation, and didn't seem to have any effect, so it got left in. I've abandoned the whole HorizontalList with a custom itemRenderer approach, though, and just gone for a custom component, which has enabled tabbing. I'm leaving the question up so that others who run into the same issue can at least find what I've found so far.
Adam Tuttle
A: 

This just doesn't seem to be possible with the method I was using. Instead of using a list with a custom Item Renderer I switched to a single-item view component and a separate list to show a summary of all items, and this let me fix my problem.

Adam Tuttle
A: 

I am having the EXACT same problem and it is driving me crazy! I think I read somewhere that this occurs because you have an itemRenderer and not an itemEditor. You can use the property "rendererIsEditor" and then you need to handle the itemEditComplete I believe. This solution was not good for me, but it might work for you.

Anyway, if there is a way that you find to elegantly tab inside an itemRenderer I would love to know about it!

Niel Ruben
A: 

I was using an mx:VBox as a custom itemRenderer with rendererIsEditor="true" for my datagrid, and I was running into the tab order issue as well.

What I figured out is that the itemRenderer needs to implement IFocusManagerComponent in order for the main application's FocusManager() to be able to tab correctly to it. I tried implementing that interface:

<?xml version="1.0"?>
<mx:VBox implements="mx.managers.IFocusManagerComponent" ...>
 [the rest of my itemRenderer code]
</mx:VBox>

...and it turns out to be rather complex to do...lots of interface functions to implement.

However in my case I was lucky; I only had one TextInput element in my itemRenderer (the rest was all just custom code, validators & formatters) so I converted my itemRenderer from mx:VBox to mx:TextInput (which already implements the IFocusManagerComponent):

<?xml version="1.0"?>
<mx:TextInput ...>
 [the rest of my itemRenderer code]
</mx:TextInput>

Voila! My tab order issue was fixed.

I suppose the conclusion for those of you with more complex itemRenderers is you'll need to either fully implement the IFocusManagerComponent interface in your class...which is probably good because it looks like it would tell flex how to custom-tab through your itemRenderer fields. Or perhaps you could change your top level tag to something which already implements the interface, eg: could you nest the mx:VBox inside something like:

<mx:Container focusIn="FocusManager.setFocus(trigger1)">

...and have it work perhaps? Someone with more complex code than I should give it a try and see what happens.

datico
Hi. For what it's worth, UIComponent implements all the required methods of IFocusManagerComponent, but just not the interface (because some UIComponents aren't supposed to get focus). So, to make a component focus-able, you simply add the interface declaration (no need to implement any of the methods - UIComponent does that for you. http://www.adobe.com/livedocs/flex/201/langref/mx/managers/IFocusManagerComponent.html
Marty Pitt
A: 

I'm stuck on the same thing. Implementing IFocusManagerComponent does not appear to be the answer: UIComponent already implements much of that interface and filling in the blanks in addition to formally declaring that my class implement IFocusManagerComponent has not helped.

The List class appears to interpose itself in the focus tree in such a way that, deep inside the FocusManager code, my subcomponents (all TextFields) are observed as the "next focus element", but then the focus manager uses an index into a focusableItems[] array and concludes that the next object is outside the component - the next object after the list. This is something to do with "player level focus" vs "component level focus". Frankly, I'm stumped at the moment and can't believe this is so hard... Clues appreciated!

verveguy
As I mentioned in my comment on Shua's answer, I've just abandoned this approach altogether and gone with a custom component instead of trying to shoe-horn into the list component. Maybe this will be addressed with v4 of the framework, but I don't know. Good luck!
Adam Tuttle
A: 

I had the same problem, solved it by trying to reimplement the behavior of the tab button. The clue to success is simply using the event.preventdefault() method. The code used is shown ahead.

private function initFocusMap():void {
    focusMap = {
        InNom:benefPersona.InApePat,
        InApePat:benefPersona.InApeMat,
        InApeMat:benefPersona.InFecNacimiento,
        InFecNacimiento:benefPersona.InRFC,
        InRFC:benefPersona.InCURP,
        InCURP:benefPersona.InIdSexo,
        InIdSexo:benefPersona.InIdParentesco,
        InIdParentesco:benefPersona.InPorc,
        InPorc:domBeneficiario.InCalle,
        InCalle:domBeneficiario.InNumExterior,
        InNumExterior:domBeneficiario.InNumInterior,
        InNumInterior:domBeneficiario.InCP,
        InCP:domBeneficiario.InColonia,
        InColonia:domBeneficiario.InIdPais,
        InIdPais:domBeneficiario.InIdEdo,
        InIdEdo:domBeneficiario.InIdCiudad,
        InIdCiudad:benefPersona.InNom                   
    }
}

private function kfcHandler(event:FocusEvent):void {
    var id:String = ""
    if (event.target is AperClsDateField || event.target is AperClsCombo) {
        id = event.target.id;
    } else {
        id = event.target.parent.id;
    }
    if (id != "InIdDelegacionMunic") {
        event.preventDefault();             
        focusManager.setFocus(focusMap[id]);
    }
}
Julio
A: 

Hi all,

I ran into the same problem with an itemRender used in a "ListBase derived" component. I found that all "ListBase derived" components wrap all the item rendereres in a ListBaseContentHolder.

From the ListBase source:

/**
 *  An internal display object that parents all of the item renderers,
 *  selection and highlighting indicators and other supporting graphics.
 *  This is roughly equivalent to the <code>contentPane</code> in the 
 *  Container class, and is used for managing scrolling.
 */
protected var listContent:ListBaseContentHolder;

The tabChildren and tabEnabled properties of this class are set to false by default. The only workaround I could find was to create a MyList component deriving from the List and override the createChildren method (where the listContent is initialized) this way:

import flash.display.DisplayObject;
import mx.controls.List;

public class MyList extends List {
    override protected function createChildren():void {
            super.createChildren();
            this.listContent.tabChildren = this.tabChildren
            this.listContent.tabEnabled = this.tabEnabled
        }
    }

Then using "<MyList/>" instead of the "<mx:List/>" component gave me back the tabbing functionnality in the ItemRender.

Hope it helps,

Bastien Aracil
A: 

Thanks it worked both with custom List component and tabChildren = "true";

Divya
A: 

This is how it's down with a ComboBox itemEditor:

http://blog.flexmonkeypatches.com/2008/02/18/simple-datagrid-combobox-as-item-editor-example/

thargenediad