1

I've started creating a GUI for a March Madness bracket generator by displaying all 64 teams for round1 as Labels and now I'm trying to create a ComboBox dropdown menu for each match.

I've created a ComboBox for 2 matches and now I want to create a new ComboBox that pulls its options from the other two ComboBox's before it. So in the example diagram below, the new ComboBox should have the options Duke and VCU for the user to choose from.

           (2 combo boxes)        (new combo box)

Duke------
               Duke ---   
ND St. ---

                                        X

VCU -----
               VCU ---
UCF -----  

How can I do so?

public class ControlPanel extends Application
{

    @Override
    public void start(Stage primaryStage) {

        primaryStage.setTitle("March Madness 2019 Generator");

        BorderPane componentLayout = new BorderPane();
        componentLayout.setPadding(new Insets(20,0,20,20));

        final FlowPane choicePane = new FlowPane();
        choicePane.setHgap(100);
        Label choiceLbl = new Label("Match1");

        ArrayList<Team> round1 = new ArrayList<Team>();

        round1.add(new Team("Duke", 0.670, 1));                    //0
        round1.add(new Team("North Dakota St", 0.495, 16));
        round1.add(new Team("VCU", 0.609, 8));
        round1.add(new Team("UCF", 0.606, 9));


        //The choicebox is populated from an observableArrayList
        ChoiceBox r2Match1 = new ChoiceBox(FXCollections.observableArrayList(  match(round1, 0, 1)   ));

        //Add the label and choicebox to the flowpane
        choicePane.getChildren().add(choiceLbl);
        choicePane.getChildren().add(r2Match1);

        //put the flowpane in the top area of the BorderPane
        componentLayout.setTop(choicePane);

        //Add the BorderPane to the Scene
        Scene appScene = new Scene(componentLayout,500,500);
        //Add the Scene to the Stage
        primaryStage.setScene(appScene);
        primaryStage.show();
    }

    private ArrayList<Team> match(ArrayList<Team> roundPullFrom, int team1, int team2) {
        ArrayList<Team> temp = new ArrayList<Team>();
        temp.add(roundPullFrom.get(team1));
        temp.add(roundPullFrom.get(team2));
        return temp;
    }

}

Wolfgang Fahl
  • 15,016
  • 11
  • 93
  • 186

2 Answers2

0

The structure of your problem is a tree. So you might want to have your solution support that structure. Either you use a Binary Tree data structure to resemble the tournament or you create such a structure by e.g. having classes like:

class Team {
   String name;
}

class Match {
   Team teamA;
   Team teamB;
   String where;
   Date when;

   public Team selectWinner() { 
     ...
   }
}

class Tournament {
   List<Team> teams;
   List<Match> getMatches(int round,List<Team> teams) {
     List<Match> matches=new ArrayList<Match>)();
     if (round==1) {
       for (teamIndex=1;teamIndex<=teams.size();teamIndex+=2) {
         Match match=new Match(teams[teamIndex-1],teams(teamIndex)];
         matches.add(match);
       }
     } else { 
       List<Team> winners=new ArrayList<Team>();
       for (Match match:getMatches(round-1)) {
         winners.add(match.selectWinner());
       }
       return getMatches(1,winners);
     }
   }
}

From this structure you can then derive the necessary gui components to make the selection dynamic and let the GUI components take their values from the Tournament, Match and Team classes.

Wolfgang Fahl
  • 15,016
  • 11
  • 93
  • 186
  • What is the selectWinner method supposed to do? I want the user to choose the winner of the method, not a method. – user128912901 Jun 05 '19 at 06:54
  • This was just to show the principle. E.g. you could simply return an instance variable Team winner and if it is null allow to select it with a button and if it is not null display it as a label. The idea is that you might want to take the MVC approach and separate the model / structure of your problem from the gui representation with labels and comboboxes. To derive a label from a Match and a combobox from a Match is very straightforward ... You need solve the case for upcoming matches. For that a Team can be "winner of ..." for the time being and that case needs to be covered. – Wolfgang Fahl Jun 05 '19 at 08:03
  • I appreciate your comment, but I'm looking for just a basic way to pull the value from a combobox and use it as an option for another combobox – user128912901 Jun 05 '19 at 08:46
0

Combine ComboBoxes pairwise using the approach posted in my previous answer until you're left with a single ComboBox.

The following code layouts the nodes in something that resembles a tree structure too, but you could easily decouple the layout by keeping every round in a data structure instead of overwriting the values of a single array. (Since you'll want to access the data, you should store the combos in a proper data structure anyways.)

private static ComboBox<String> createCombo(double x, double y, double width) {
    ComboBox<String> comboBox = new ComboBox<>();
    comboBox.setLayoutX(x);
    comboBox.setLayoutY(y);
    comboBox.setMaxWidth(Region.USE_PREF_SIZE);
    comboBox.setMinWidth(Region.USE_PREF_SIZE);
    comboBox.setPrefWidth(width);

    return comboBox;
}

private static Label createLabel(String text, double maxWidth) {
    Label label = new Label(text);
    label.setMaxWidth(maxWidth);
    return label;
}

@Override
public void start(Stage primaryStage) {
    String[] teams = new String[64];
    for (int i = 0; i < teams.length; i++) {
        teams[i] = Integer.toString(i);
    }
    final double offsetY = 30;
    final double offsetX = 100;
    final double width = 90;

    Pane root = new Pane();

    // array storing the comboboxes
    // combos for previous round are at the lowest indices
    ComboBox<String>[] combos = new ComboBox[teams.length / 2];

    // create initial team labels & comboboxes
    for (int i = 0, offsetTeams = 0; i < combos.length; i++, offsetTeams += 2) {
        Label label = createLabel(teams[offsetTeams], width);
        double y = offsetTeams * offsetY;
        label.setLayoutY(y);
        root.getChildren().add(label);

        label = createLabel(teams[offsetTeams+1], width);
        label.setLayoutY(y+offsetY);

        ComboBox<String> comboBox = createCombo(offsetX, y + offsetY / 2, width);
        comboBox.getItems().addAll(teams[offsetTeams], teams[offsetTeams+1]);
        combos[i] = comboBox;

        root.getChildren().addAll(label, comboBox);
    }

    double x = 2 * offsetX;
    int count = combos.length / 2; // combos still left for the next round

    for (; count > 0; count /= 2, x += offsetX) { // for each round
        // create comboboxes combining the combos from previous round pairwise
        for (int i = 0, ci = 0; i < count; i++, ci+=2) {
            // get combos pairwise
            ComboBox<String> c1 = combos[ci];
            ComboBox<String> c2 = combos[ci+1];

            ComboBox<String> combo = createCombo(x, (c1.getLayoutY() + c2.getLayoutY()) / 2, width) ;

            // combine data from previous round
            ChangeListener<String> listener = (o, oldValue, newValue) -> {
                final List<String> items = combo.getItems();
                int index = items.indexOf(oldValue);
                if (index >= 0) {
                    if (newValue == null) {
                        items.remove(index);
                    } else {
                        items.set(index, newValue);
                    }
                } else if (newValue != null) {
                    items.add(newValue);
                }
            };
            c1.valueProperty().addListener(listener);
            c2.valueProperty().addListener(listener);

            root.getChildren().add(combo);
            combos[i] = combo;
        }
    }

    primaryStage.setScene(new Scene(new ScrollPane(root), 600, 400));
    primaryStage.show(); 
}
fabian
  • 80,457
  • 12
  • 86
  • 114
  • Thank you. I'll try it right now. I know its proper to store combos in data structures but I have no experience using data structures before so I wasn't sure how to do so – user128912901 Jun 05 '19 at 15:04
  • @user128912901 the `teams` array would contain the team names in a real application. Here I'm not hardcoding any team names, but simply use strings containing numbers 0, ..., 63 instead to simplify the code. – fabian Jun 05 '19 at 15:08