JTabbedPane unable to get tab pages

(coming out of my PieceWindow question from earlier this week)

This is likely not the best way to get the information I’m looking for, so if the answer is “it just doesn’t work”, that’s ok. It’s just this is the easies way for me without asking the module maintainer to change things, so I wanted to explore it fully!

I’ve written some recursive code to drill into the PieceWindow Swing components and try to find the particular one I’m looking for. The goal is to find the title of a specific tabbed panel, and then find a specific list item on a JList somewhere within it.

The problem is that when I get to the JTabbedPane I want, I’m unable to go deeper. getComponentAt (which should be correct) returns an empty JPanel, and getTabComponentAt returns null.

Thanks! And again, it’s ok if this can’t be easily solved, I’m half-way to deserting this feature already. :slight_smile:


Some details:

Get the PieceWindow, start a recursive search for a certain thing.

for (final PieceWindow window : GameModule.getGameModule().getAllDescendantComponentsOf(PieceWindow.class)) {
    if("Game pieces".equals(window.getConfigureName())){
        var component = window.getComponent();
            if(component instanceof Container)
                searchContainers("Acclamator I-class Assault Ship", (Container) component, null);
    }
}

The recursive search, when it finds a tab with a specific name it does the rest of the search with that name.

    private void searchContainers(String searchFor, Container container, String lastFoundFactionName) {
        // getTotalCount and getComponent count abstract away the differences between JTabbedPane and Container, 
        // I don't think they are important for the example.
        for (var k = 0; k < getTotalCount(container); k++) {
            var component = getComponent(container, k);
            if (component instanceof JTabbedPane) {
                var tabbedPane = (JTabbedPane) component;
                for (int i = 0; i < tabbedPane.getComponentCount(); i++) {
                    var tabTitle = tabbedPane.getTitleAt(i);
                    var tabComponent = tabbedPane.getComponentAt(i);
                    // TODO: Repro problem:
                    // 1. Put a breakpoint on the next line.
                    // 2. Continue until tabbedPane.getTitleAt(i) == "Republic"
                    // 3. Check both tabbedPane.getComponentAt(i) and tabbedPane.getTabComponentAt(i)
                    // Expected: One of them to be a JSplitPane, as seen in tabbedPane.pages
                    // Actual: getComponentAt returns an empty JPanel, getTabComponentAt returns null
                    if(tabComponent instanceof Container) {
                        if (Arrays.stream(FactionNames).anyMatch(name -> name.equals(tabTitle)))
                            searchContainers(searchFor, (Container) tabComponent, tabTitle);
                        else
                            searchContainers(searchFor, (Container) tabComponent, lastFoundFactionName);
                    }
                }
            } else if(component instanceof JList) {
              // not important for this example
            } else if (component instanceof Container)
                searchContainers(searchFor, (Container) component, lastFoundFactionName);
        }
    }

I’ve attached a screenshot of the tabbedPane, along with the component list I actually want to get access to. There is a private field pages that I would have thought would be returned by getComponantAt(i).

What are you actually trying to do? Why do you want this?

I’m writing an extension to an existing module, which I don’t maintain and have no relationship to the maintainer.

The module has game pieces sorted into 4 trees such that the children of each tree belong to the faction named by that tree. I want to find out which faction a certain piece belongs to by searching through the tree for that piece, and taking the title from the root of it.

This could be much easier solved by getting a relationship with the maintainer and having him put “Faction” as a property on the pieces, but I thought I had enough to solve the problem without going that direction and then ran into a weirdness in code that I thought I’d ask about.

Sorry, thinking about it more, I think this question might be a waste of your time, which I don’t mean to do. I ask it for curiousity because it seemed like the results weren’t consistent with what I would have expected.

I would suggest going through the VASSAL components tree rather than the (urk!) Swing components :slight_smile:

The individual pieces would be in PieceSlot objects (so PieceSlot.class in the get-descendant-components call, PieceSlot.java for source code).

Remind me what module is this that you’re working on?

Brian

How would I get started looked at the VASSAL components tree?

The module is Star Wars Armada.

Thanks!

So if you want to literally “look at” it, open up the VASSAL Editor on the module in question (select Edit Module from the menu). The lower of the two windows that opens will have a hierarchical tree structure that you can browse through. The top level item is the “Game Module” component corresponding to the “GameModule” java object and then beneath it you will find all the other components, e.g. Maps and Piece Windows (the latter called “Game Piece Palette” – unfortunately for the usual types of historical reasons not everything is named the same way in the Editor as it is in the Code … the way you can sift your way through that is that PieceWindow has a method named getConfigureTypeName() which in turn points to a localizable string in the Editor.properties text file which contains the string Game Piece Palette). But anyway I digress – in the Editor you can browse the hierarchy. This the same hierarchy that is contained in the buildFile.xml of the module (inside the zipped structure of the .vmod file), as it is build from that .xml file and then written back to it by the Editor.

If you want to get hold of instances of these objects while you’re running e.g. java custom code, then the method you’re using in your code example above is one of the ways:
GameModule.getGameModule().getAllDescendantComponentsOf(PieceWindow.class))

And so using your first example as the outer part, one could continue, e.g.:

for (final PieceWindow window : GameModule.getGameModule().getAllDescendantComponentsOf(PieceWindow.class)) {
      if ("Game pieces".equals(window.getConfigureName())) {
          for (final PanelWidget panelWidget : window.getAllDescendantComponentsOf(PanelWidget.class)) {       
              // Now searching through all the "panels" which are the sub-components of the TabWidget children of the PieceWindow
              if ("Republic".equals(panelWidget.getConfigureName()) {
                  for (final PieceSlot pieceSlot : window.getAllDescendantComponentsOf(PieceSlot.class)) {
                      // Now searching through all the "Single piece" items (individual pieces) on the "Republic" tab.
                      ... now you do stuff w/r/t the pieces
                  }
              }
          }
      }
  }

Hopefully that gets you further?

Brian

That did it!

A couple minor changes, TabWidget.class instead of PanelWidget.class, and the last for should use the widget in the for outside it, but I found what I was looking for!

Thanks so much!

Excellent - yes I was “hand coding from memory in a text entry field” so glad I got close. Also I was imagining the widget structure of my module rather than yours which may be different. Let me know if you find any more questions!