I Hate Swing, Volume XXIX

The Java graphics system has always been a headache to me. I finally gave up on any kind of layout system and now rely instead on just hard-coding the positions of the various graphical elements. Even then, the damn thing is impossible. Here’s my latest tear-my-hair-out problem:

I am building the “Anecdote” system, which is quite simple. It requires the presentation of some text and some radio buttons representing options available to the player, like so:

Here’s the kicker: when I click on “Do it!” the code must calculate a reaction and present the text representing the reaction. Here’s the result:

Where in the hell did all that gray come from? I have plastered “setBackground(Color.white) on every graphical object in the program, and it STILL comes back gray. I even tried painting the entire frame white, even AFTER repainting all the Swing components — it still didn’t get rid of the gray.

On a hunch, I tried shortening the width of the lower text area (“She glares back…”). Here’s the result:

This gives us some insight into the twisted minds of the designers of the Swing system. The gray area is defined by the upper JTextArea; its width is the same. For some reason, the system is extending the upper JTextArea down to the bottom of the JFrame, even though there’s nothing in it and I defined it to be only as high as the white area shown. This suggests a possible solution: Define the upper JText Area to run all the way down to the bottom of the JFrame, then write all the other stuff on top of the upper JTextArea. I tried that, and all I accomplished was to white out everything else. Nice try, though. 

OK, so maybe I should try to go the full-court Swing route. I dread this, but I think I have no choice.

I assigned a layout to the JFrame — BoxLayout.Y_AXIS. That means that the elements are stacked vertically from top to bottom. I remove all the position assignments from the existing Containers and run it. It crashes when it tries to add the first JTextArea. Do I have to add the components to the JFrame’s internal ContentPane? I try it; it crashes again. Perhaps I should try the GlassPane, the LayeredPane, or the RootPane; it’s difficult to remember which Pane does what. Wouldn’t it be lovely to just use the damn JFrame itself? 

Dialogs
No, I just realized that another factor constrains my options. I don’t want to use a JFrame in the first place; I want a modal Dialog. That insures that the player MUST deal with the dialog BEFORE returning to the game. But there’s a problem with that: the model Dialog must be assigned to the game window. 

But that would require me to call the anecdotes from the Storyteller code, not the Engine code, where it currently resides. This makes sense only if the anecdotes are to be called just before or after something else happens to the player. But that’s much messier, because I can’t have an anecdote with actor X while in the middle of a conversation with actor Y. Indeed, I cannot permit anecdotes to take place within conversations; they must be confined to take place BETWEEN interactions with other actors. I suppose that I could figure out a way to do this from within Storyteller. Unfortunately, the Storyteller extends JFrame, so it’s the calling class, but it has no paint() method! Isn’t that lovely: a JFrame that I cannot identify!

Here I am fighting with Java code that is too sophisticated for me to understand. The code here consists of a number of threads, and I have never understood how threads work in Java. The connections running from the Engine to the Storyteller are shrouded in layers and threadings necessary to permit the client-server system to run properly, but they are much too arcane for me to figure out. 

So what now? I suppose that I’ll have to remain with the original JFrame plan and convert it to a Dialog at some later point in time after Louis has cleaned out all the client-server intermediaries. Which takes me back to the conundrums I was facing just before the section entitled “Dialogs”. 

Back to the JFrame
So this time I’m going to create a “holding panel”, which I’ll call MainPanel. I’ll stuff all the existing components into MainPanel, then assign MainPanel as the contentPane for the JFrame. You’ll never guess what happened when I ran this corrected version. Surprise, surprise: it crashed again! This time the crash came when I added the first JTextArea to the JPanel. Horrors! Just to be sure, I added a JLabel (the simplest type of component) to the mainPanel, and sure enough, the code crashed when I tried to add the JLabel. Obviously, something is seriously broken here. 

So I go back to the basics, setting up a proper hierarchy of JPanels with their BoxLayouts. Topmost in the hierarchy is mainPanel, which is setup in BoxLayout with horizontal layout. Inside it are a 50-pixel horizontal strut, the centerPanel, and another 50-pixel horizontal strut. These two struts serve only to provide some margin on either side of the text in the centerPanel. 

The centerPanel has a BoxLayout with vertical layout. It has a vertical strut of 20 pixels at the top to provide a top margin, then the JTextArea containing the main anecdote text. Next is the set of JRadioButtons covering the player’s options. Just below that is the text presenting the reaction of the interlocutor (when it’s ready). At the bottom comes the “Execute” button.

But lo! For some reason, Swing decides to push the JRadioButtons as far down towards the bottom as possible, leaving a big gap of white space between the text and the buttons:

Anecdote4

I faintly recall something along these lines from my first studies of layouts. But why, oh why, does the damn layout manager put all the extra vertical space between the main text box and the radio buttons? Why doesn’t it put some between the buttons and the bottom button? Are these questions whose answers were never meant to be known by mere mortals? 

But then I remember an old trick I learned years ago: set background colors to reveal what’s actually going on. Here’s the result:

Anecdote 5

Well, well, well, it looks as if the main text JTextArea is hogging all the space! Now all I have to do is figure out how to tell it to be less imperialistic. So I try this:

mainText.setMaximumSize(new Dimension(700,200));

And here’s what that does:

Anecdote6

The red box (mainText) is now 400 pixels wide. I set its maximum size at 700 pixels. And now there’s gray in the background again — who put that in there? Ah, the joys of Java Swing!

So I go through all sorts of combinations of setMaximumSize, setMinimumSize, and setPreferredSize, none of which make any sense at all. I think that Java Swing takes those input values and shoves them into a random algorithm to determine what it will do. There’s no discernible pattern in its behavior. So I abandon use of the various Set___Size commands.

I have a new debugging idea: what if I put the text in a JLabel instead of a JTextArea? I try it; nothing happens. Nothing appears. Besides, digging into JLabels reveals that they do not wrap lines of text. If I want multiple lines of text, I must use a JTextArea. It’s the only option (unless I go back to a hand-drawn system. Hmm… naw)

Ta-da! I found a solution! I put a huge vertical strut between the radio buttons and the “Do it!” button. That puts the stupid layout manager into a jam from which the only solution is to stop wasting space on the JTextArea. This works:

Anecdote7

But I’m still not out of the woods; when I click on “Do it!”, I get this:

The changes that were execute in response to the “Do it!” button press are:

for (Option op: theAnecdote.options) {

    op.rButton.setVisible(false);

}

optionButtons.get(iOption).setVisible(true);

.

.

reactionText.setText(decodeText(bestReaction.text));

reactionText.setVisible(true);

Do you see anything in these lines of code that could cause such a big change? I don’t. It’s entirely arbitrary. Java Swing just randomly slaps things onto the screen without any reason at all. It’s completely arbitrary. Yes, I know it sounds crazy. But can YOU explain this behavior?

I know, I know, it’s all my fault. Somewhere I failed to do something that was necessary for the proper functioning. There’s some hidden command that I failed to give the system. Perhaps I should have revalidated something. (I tried that; it didn’t help.) Perhaps I left out centralPanel.renormalizeLayout(System, dayOfMonth, phaseOfMoon, this). Or was it centralPanel.renormalizeLayout(phaseOfMoon, this, dayOfMonth, System)?  Who knows? Java has a thousand libraries and a million methods. Somewhere in there is the answer. All I have to do is read a few thousand more pages of documentation and sift through a thousand pages of advice on the web. It’s all my fault for not knowing what I’m doing.

I’m sick of fighting this crap; it’s late and I’m going to bed. In the morning I’ll start all over in BASIC.