Results 1 to 8 of 8

Thread: ListView selection not changing with use of arrow keys

  1. #1
    Sencha Premium Member
    Join Date
    Oct 2011
    Posts
    16

    Default ListView selection not changing with use of arrow keys

    I have a ListView defined as follows:

    Code:
    this.pickListView = new ListView<PickListConfig,String>( new ListStore<PickListConfig>( props.key() ), props.pickListName());
    this.pickListView.getSelectionModel().setSelectionMode( SelectionMode.SINGLE );
    this.pickListView.getSelectionModel().setVertical( true );  // should not be needed, but done just in case
    
    ContentPanel definedListsPanel = new ContentPanel();
    definedListsPanel.add( this.pickListView, new VerticalLayoutData( 300, 400 ) );
    In this list there are about 60 entries, so there is a vertical scroll bar on the display. When I select an entry in the list and attempt to use the up/down arrow keys to navigate the items, the scrollbar moves up/down but the list selection does not. Even when the scrollbar is set at the top or bottom of the list, arrow key navigation does not occur.

    I removed the scrollbar using Firebug and arrow key navigation still did not occur. Am I missing something or is the key navigation just not working? I haven't seen any other posts about navigation not working, so please help if you can. Thanks!

    This is with GXT 3.0.3. in both Firefox and Chrome.

    (originally posted in GXT Discussion, but updated and reposted here)

  2. #2
    Sencha Premium Member
    Join Date
    Sep 2007
    Posts
    13,976

    Default

    I tried to reproduce this in the following example: http://www.sencha.com/examples/#ExamplePlace:listview


    Pressing up and down arrows does move the selection for me. Do you have a fully working testcase implementing EntryPoint?

  3. #3
    Sencha Premium Member
    Join Date
    Oct 2011
    Posts
    16

    Default

    Thank you for replying. I used my code and created a test case that implemented EntryPoint. The test case worked fine. Guess I will need to do some additional investigation to see why it works in the test case but not the original code.

  4. #4
    Sencha Premium Member
    Join Date
    Oct 2011
    Posts
    16

    Default

    I've attached an EntryPoint module that duplicates the error I'm having. The actual application has several containers, the hierarchy of which I've tried to replicate in the example.

    It appears the problem occurs when I call the select() method on the list view's selection model as in:

    Code:
            if ((lastSelected != null) && (pickListView.getStore().indexOf( lastSelected ) > 0)) {
                pickListView.getSelectionModel().select( false, lastSelected );
            }
            else if (pickListView.getStore().size() > 0) {
                pickListView.getSelectionModel().select( 0, false );
            }
    In the actual application the selection call shown above is performed after an RPC call to gather the data for the ListView. Once the data is set in the ListView store, the first item in the list is selected.

    After several tests and re-tests, the issue of key navigation not working appears to happen rather inconsistently with the attached example, but happens nearly 100% of the time in the real application. Perhaps there is a timing issue occurring somewhere?

    For example, when the list is displayed the first item is selected all the time. Using the mouse and clicking on the first entry, then using the arrow keys to navigate will sometimes result in the selection moving to the next entry (as expected), but sometimes will result in the scrollbar moving and the selection staying on the first item. Clicking another item in the list, then using the navigation keys generally results in navigation, but also may result in the scrollbar moving.

    At any rate, attached is an example that I can get to fail at least some of the time.

    OK... after trying at least 10 times to do a file upload and having it repeatidly tell me the file was invalid, I'm including the source below:


    Code:
    package com.gwttest.client;
    
    import java.util.ArrayList;
    import java.util.Comparator;
    import java.util.List;
    
    import com.google.gwt.core.client.EntryPoint;
    import com.google.gwt.core.client.GWT;
    import com.google.gwt.core.client.Scheduler;
    import com.google.gwt.editor.client.Editor.Path;
    import com.google.gwt.user.client.ui.Composite;
    import com.google.gwt.user.client.ui.Grid;
    import com.google.gwt.user.client.ui.RootLayoutPanel;
    import com.google.gwt.user.client.ui.Widget;
    import com.sencha.gxt.core.client.ValueProvider;
    import com.sencha.gxt.core.client.Style.SelectionMode;
    import com.sencha.gxt.core.client.util.Margins;
    import com.sencha.gxt.data.shared.LabelProvider;
    import com.sencha.gxt.data.shared.ListStore;
    import com.sencha.gxt.data.shared.ModelKeyProvider;
    import com.sencha.gxt.data.shared.PropertyAccess;
    import com.sencha.gxt.widget.core.client.ContentPanel;
    import com.sencha.gxt.widget.core.client.ListView;
    import com.sencha.gxt.widget.core.client.container.BorderLayoutContainer;
    import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
    import com.sencha.gxt.widget.core.client.container.Viewport;
    import com.sencha.gxt.widget.core.client.container.BorderLayoutContainer.BorderLayoutData;
    import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
    import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent;
    import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent.SelectionChangedHandler;
    
    
    /**
     * Entry point classes define <code>onModuleLoad()</code>.
     */
    public class Gwttests implements EntryPoint {
        private ListView<PickListConfig,String> pickListView = null;
        private PickListConfig lastSelected = null;
        private boolean addingNewConfig = false;
        private PickListConfig currentEdit = null;
        
        
        /**
         * This is the entry point method.
         */
        public void onModuleLoad() {
            Grid mainPanel = new Grid( 1, 1 );
            PickListComposite compositePanel = null;
            ContentPanel manageListPanelWrapper = new ContentPanel();
            VerticalLayoutContainer manageListsPanel = new VerticalLayoutContainer();
            ContentPanel definedListsPanelWrapper = new ContentPanel();
            VerticalLayoutContainer definedListsPanel = new VerticalLayoutContainer();
            final ContentPanel detailsPanel = new ContentPanel();
            ContentPanel contentPanelWrapper = new ContentPanel();
            VerticalLayoutContainer contentPanel = new VerticalLayoutContainer();
            
            final PickListConfigProperties props = GWT.create( PickListConfigProperties.class );
            
            compositePanel = new PickListComposite();
            
            this.pickListView = new ListView<PickListConfig,String>(
                    new ListStore<PickListConfig>( props.key() ), props.pickListName());
            this.pickListView.getSelectionModel().setSelectionMode( SelectionMode.SINGLE );
            this.pickListView.getSelectionModel().setVertical( true );
            this.pickListView.getSelectionModel().addSelectionChangedHandler(
                    new SelectionChangedHandler<PickListConfig>() {
                        @Override
                        public void onSelectionChanged( SelectionChangedEvent<PickListConfig> event) {
                            if ((event.getSelection().size() > 0) && (event.getSelection().get( 0 ) != currentEdit)) {
                                lastSelected = event.getSelection().get( 0 );
                                //editObject( lastSelected );
                            }
                        }
                    });
            
            //definedListsPanel.setHeadingText( "Defined Pick Lists" );
            definedListsPanel.add( this.pickListView, new VerticalLayoutData( 300, 400 ) );
            definedListsPanelWrapper.setWidget( definedListsPanel );
            definedListsPanelWrapper.setHeight( 450 );
            
            BorderLayoutContainer mainContainer = new BorderLayoutContainer();
            BorderLayoutData layoutData = new BorderLayoutData( 300 );
            
            layoutData.setMargins( new Margins( 10 ) );
            
            mainContainer.setWestWidget( definedListsPanelWrapper, layoutData  );
            mainContainer.setCenterWidget( new ContentPanel(), layoutData );
            
            manageListsPanel.add( mainContainer );
            manageListPanelWrapper.setWidget( manageListsPanel );
            
            detailsPanel.add( manageListPanelWrapper );
            
            contentPanel.add( detailsPanel );
            contentPanelWrapper.setWidget( contentPanel );
            
            detailsPanel.setPixelSize( 1200, 700 );
            
            Viewport v = new Viewport();
            v.add( contentPanelWrapper );
            mainPanel.setWidget( 0, 0, v );
            
            compositePanel.initWidget( mainPanel );
            
            RootLayoutPanel rp = RootLayoutPanel.get();
            rp.clear();
            
            Viewport vp = new Viewport();
            //vp.setLayout( new FitLayout() );
            //vp.add( mainContentPanel );
            //vp.add( mainPanel );
            vp.add( compositePanel.asWidget() );
            //vp.setStyleAttribute( "margin", "auto" );
    
            rp.add( vp );
            
            // simulate an RPC call
            Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand () {
                public void execute () {
                    populateList();
                    detailsPanel.setPixelSize( 1200, 700 );
                    detailsPanel.forceLayout();
                }
            });
        }
        
        
        private class PickListComposite extends Composite {
            public void initWidget( Widget widget ) {
                super.initWidget( widget );
            }
        }
        
        private void populateList() {
            PickListConfig config = null;
            // this would normally be done after RPC call to get data from server
            for (int i=0; i<30;i++) {
                config = new PickListConfig( "Pick List " + i, "Some pick list" );
                config.setId( i );
                this.pickListView.getStore().add( config );
            }
            
            //  removing this code, key navigation works.  with code, it doesn't
            if ((lastSelected != null) && (pickListView.getStore().indexOf( lastSelected ) > 0)) {
                pickListView.getSelectionModel().select( false, lastSelected );
            }
            else if (pickListView.getStore().size() > 0) {
                pickListView.getSelectionModel().select( 0, false );
            }
            
        }
        
        
        public class PickListConfig implements Comparator<PickListConfig> {
    
            /** The internal ID for this pick list.  Used to detect name changes.  Not persistent between server runs. */
            private int id;
    
            /** The name of the pick list.  Must be unique within the system. */
            private String pickListName;
    
            /** The description for this pick list (optional) */
            private String description;
    
            /** The list of entries for this static pick list */
            private List<String> entryList;
    
            public PickListConfig(String pickListName, String description) {
                this.pickListName = pickListName;
                this.description = description;
                this.entryList = new ArrayList<String>();
                this.id = -1;
            }
            
    
            public String getPickListName() {
                return pickListName;
            }
    
            
            public void setPickListName(String pickListName) {
                this.pickListName = pickListName;
            }
    
            
            public String getDescription() {
                return description;
            }
    
            
            public void setDescription(String description) {
                this.description = description;
            }
    
            
            public List<String> getEntryList() {
                return entryList;
            }
    
            
            public void setEntryList(List<String> entryList) {
                this.entryList = entryList;
            }
    
            
            public void addEntry(String entry) {
                if (!entryList.contains(entry)) {
                    entryList.add(entry);
                }
            }
    
            /**
             * Compares its two arguments for order.  Returns a negative integer, zero, or a positive integer as the first
             * argument is less than, equal to, or greater than the second.
             *
             * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater
             *         than the second.
             */
            public int compare(PickListConfig config1, PickListConfig config2) {
                int comp = 0;
                
                if (config1 == null && config2 == null) {
                    comp = 0;
                }
                else if (config1 == null && config2 != null) {
                    comp = -1;
                }
                else if (config1 != null && config2 == null) {
                    comp = 1;
                }
                else {
                    //comp = (config1.getId() > config2.getId() ? 1 : (config1.getId() == config2.getId() ? 0 : -1));
                    comp = config1.getPickListName().compareTo(config2.getPickListName());
                }
                
                return comp;
            }
    
            
            /**
             * Returns a string representation of the object. In general, the <code>toString</code> method returns a string that
             * "textually represents" this object. The result should be a concise but informative representation that is easy for a
             * person to read. It is recommended that all subclasses override this method.
             *
             * @return a string representation of the object.
             */
            public String toString() {
                return pickListName;
            }
            
    
            public int getId() {
                return id;
            }
    
            
            public void setId(int id) {
                this.id = id;
            }
    
            
            
            public int hashCode() {
                int code = super.hashCode();
                if (this.pickListName != null) {
                    code = this.pickListName.hashCode();
                }
                return code;
                //return this.id;
            }
            
            
            public boolean equals(Object obj) {
                if (obj instanceof PickListConfig) {
                    PickListConfig otherPickList = (PickListConfig)obj;
                    return otherPickList.getPickListName().equals(pickListName);
                    //return otherPickList.getId() == this.getId();
                }
                return false;
            }
        }
        
        
        public interface PickListConfigProperties extends PropertyAccess<PickListConfig> {
    
            @Path("id")
            ModelKeyProvider<PickListConfig> key();
               
            @Path("pickListName")
            LabelProvider<PickListConfig> nameLabel();
             
            ValueProvider<PickListConfig, String> pickListName();
            ValueProvider<PickListConfig, String> description();
            ValueProvider<PickListConfig, List<String>> entryList();
        }
    }

  5. #5
    Sencha Premium Member
    Join Date
    Sep 2007
    Posts
    13,976

    Default

    Thanks. I will take a look, but as you said this is not always reproducable, it might take some time to come back to you.

    As you are saying you can reproduce it much better in your real app, you might want to open a real support ticket in your ticket system to schedule a remote desktop session. If you do so, please post the ticketnumber here.

  6. #6
    Sencha Premium Member
    Join Date
    Oct 2011
    Posts
    16

    Default

    OK. Thanks. I did notice, too, that in my test application and the ListView demo on the Sencha site:
    1. select an entry
    2. wait a second
    3. select the same entry again
    4. use arrow keys to navigate

    the scrollbar moves (in Sencha demo, nothing happens). But if I just:
    1. select an entry
    2. use arrow keys to navigate

    the key navigation works.

    Another item I'm looking at has to do with the Tab key. In my application where key navigation is not working, if I:
    1. select entry in list
    2. hit tab key
    3. use arrow key to navigate

    the key navigation will work--but only once (moves one entry up or down). Repeating the process of using Tab then arrow key I'm able to navigate up and down the list.

  7. #7
    Sencha Premium Member
    Join Date
    Oct 2011
    Posts
    16

    Default

    I've continued testing this issue and believe I finally found a fix. In my production code, when the user navigates up/down the list or selects a new entry, a save operation is performed and the entire list is recreated. The code below is called after each navigation, and the populateLists() call performs an RPC to load the list data (after each navigation or new list selection).

    Code:
    protected void handleActiveCardSaved( ActiveCardSavedEvent evt ) {
            this.lastSelected = ((EditPickListConfigPanel)this.cardPanel.getActiveCard()).getCurrentConfig();
            this.populateLists();
            this.addingNewConfig = false;
        }
    Here is the code in populateLists() that processes the return of the RPC call:
    Code:
            if (opResult.isSuccess()) {
                ArrayList<PickListConfigDto> pickListConfigDtos = opResult.getPickListConfigResults();
                ListStore<PickListConfig> store = ManageListsPanel.this.pickListView.getStore();
                ArrayList<PickListConfig> newConfigs = new ArrayList<PickListConfig>();
                                    
                // create List of PickListConfig instances
                if (pickListConfigDtos != null) {
                    for (PickListConfigDto dto : pickListConfigDtos) {
                        newConfigs.add( new PickListConfig( dto ) );
                    }
                }
                                    
                // replace current items in the store
                store.replaceAll( newConfigs );
                                    
                // without this focus() call, arrows move scrollbar
                pickListView.focus();
                                    
                // reselect the last selected one
                if ((lastSelected != null) && (pickListView.getStore().indexOf( lastSelected ) > 0)) {
                    pickListView.getSelectionModel().select( false, lastSelected );
                }
                else if (store.size() > 0) {
                    pickListView.getSelectionModel().select( 0, false );
                }
            }
    What I did to apparently fix the problem with the key navigation was:
    1. create the List of PickListConfig items (newConfigs) and use store.replaceAll( newConfigs ) instead of doing a store.clear() and adding each item individually
    2. added the call to pickListView.focus()before reselecting the item in the list

    Of all the different changes I tried, this combination is the only one that enabled key navigation on the list after the data was reloaded.

  8. #8
    Sencha Premium Member
    Join Date
    Sep 2007
    Posts
    13,976

    Default

    added the call to pickListView.focus()before reselecting the item in the list
    Thats correct. You can only use keyboad navigation after focussing the list itself.

    In the linked example at the top, focussing happens by clicking into the list.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •