JFrame Resize Issues (Redux)

K

kc

Greetings,

I decided to make a new topic out of this to start fresh. First, let's
get to the problem. I am developing Java on a Windows platform (that's
not the problem) and I am using a JFrame with a component inside that
must be sized according a certain aspect ratio. The problem I am
encountering is when I decorate the frames with a LookAndFeel
(javax.JFrame.setDefaultLookAndFeelDecorated(true)) the
componentResize() behavior changes drastically. On Windows, the user
can resize to their hearts' content and when they let go of the mouse
button, the resize event is sent and the window resizes to aspect
ratio. However, when the LookAndFeel is set, Java's Window Manager
takes over and the componentResize event is sent while the user is
resizing, say every pixel or two.

Now, that wouldn't be a problem (in fact, it would be nice if the
window maintained aspect ratio while the user dragged it) if it wasn't
so ugly and hard to control. For that I'll let you run the source code
that I prepared below. (it is 247 lines, and compiles in Borland
JBuilder X - and again, that isn't the problem). Now the reason this
is vitally important is because the application is fully intended to be
cross platform, so I can't always depend on things working the Windows
Window Manager way, so instead I'm ditching that and trying to just do
things the Java WM way.

A couple of notes before you run:

1. Two windows will pop up when you run the app. One will be decorated,
the other not. If you close one, it will close both and the app will
end.
2. A big red X goes across where "video" will be showing. I use no
"special" components for the video, my application doesn't even really
play video, it just controls the hardware that overlays it, so that
isn't an issue.
3. I'm using the swing.plaf.metal look and feel. If your debug area
says, "Er... crap." then you failed to load that component. You may
choose to use your own look and feel provided that it has its own
window frame decoration (for instance, Motif does not I don't think).

Anyway, thank you for your help, it is greatly appreciated. Without
further adeiux (sp?), here's the code:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class videoGui
extends JFrame
implements ActionListener {
public static videoGui videoControl;
public static videoGui videoControl2;
public int fvnumber; /* Ufg ProcId */
public int mixernumber; /* Mixer ProcId */

public String titleLabel = "";
public String window_char;

public int OffSetX = 0;
public int OffSetY = 0;
public Rectangle normalbounds = new Rectangle();
public String popups = "centerscreen";

// Window size values
public int source_width;
public int source_height;
private Rectangle positRectangle;
private int diffHeight;
private int diffWidth;
private int oldHeight;
private int oldWidth;
public float ratio;
private boolean first_time = true;
private boolean mouse_down = false;

// Create objects inside the video gui
public GUICanvas videoCanvas;
private JPanel forButtons;
private JMenuBar topPanel;

public videoGui(int ufg, int mix) {
fvnumber = ufg;
mixernumber = mix;
initGUI();
}

static public void main(String[] args) {
videoControl = new videoGui(0, 0);
videoControl.setTitle("Good");
videoControl.setVisible(true);
try {

UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
javax.swing.JFrame.setDefaultLookAndFeelDecorated(true);
}
catch (Exception E) {
System.out.println("Er... crap.");
}
videoControl2 = new videoGui(0, 0);
videoControl2.setTitle("Bad");
videoControl2.setVisible(true);
videoControl2.setLocation(30, 30);
}

private void initGUI() {
GetInfo();
createMenuBar();
doTheLayout();

this.setFocusable(true);

this.addComponentListener(new ComponentAdapter() {
public void componentHidden(ComponentEvent evt) {

}

public void componentMoved(ComponentEvent evt) {
setPortPosition();
}

public void componentResized(ComponentEvent evt) {
resizeVideo();
setPortPosition();
}

public void componentShown(ComponentEvent evt) {
resizeVideo();
setPortPosition();
}
});

this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
closeout();
}

public void windowClosed(WindowEvent evt) {
closeout();
}

public void windowDeiconified(WindowEvent evt) {
setPortPosition();
}

public void windowIconified(WindowEvent evt) {
}

public void windowActivated(WindowEvent evt) {
setPortPosition();
}

public void windowDeactivated(WindowEvent evt) {
}

public void windowOpened(WindowEvent evt) {
setPortPosition();
}
});
}

public void GetInfo() {
source_width = 640;
source_height = 480;
ratio = ( ( float ) source_width ) / ( ( float ) source_height
);
}


public JMenuBar createMenuBar() {
topPanel = new JMenuBar();

JLabel label = new JLabel("Moo");
topPanel.add(label);

forButtons = new JPanel();
forButtons.setLayout(new GridLayout(1, 7, 0, 0));
forButtons.add(topPanel);
return topPanel;
}

private void doTheLayout() {
videoCanvas = new GUICanvas(this);
videoCanvas.setSize(source_width, source_height);
videoCanvas.setOpaque(true);

this.getContentPane().setLayout(new BorderLayout());

this.getContentPane().add(videoCanvas, "Center");
this.getContentPane().add(forButtons, "South");
this.setSize(source_width, source_height);

String popupBehavior = "centerscreen";
popups = popupBehavior;
}

public void setPortPosition() {
Point newLoc;
positRectangle = getBounds();
if (this.isVisible()) {
newLoc = videoCanvas.getLocationOnScreen();
}
else {
newLoc = new Point(0, 0);
}
int AbsoluteX = newLoc.x - OffSetX;
int AbsoluteY = newLoc.y - OffSetY;
}

private void resizeVideo() {
int newWidth = this.getWidth();
int newHeight = this.getHeight();
int vidWidth = videoCanvas.getWidth();
int vidHeight = videoCanvas.getHeight();
int inHeight;
int inWidth;

if (first_time) {
oldHeight = newHeight;
oldWidth = newWidth;
diffHeight = oldHeight - vidHeight;
diffWidth = oldWidth - vidWidth;
oldHeight = source_height + diffHeight;
oldWidth = source_width + diffWidth;

newHeight = oldHeight;
newWidth = oldWidth;

first_time = false;
}
/* Check to see if we want to maintain aspect ratio.
In this case it is permenantly YES */
if ("a".equals("a")) {
if (newHeight != oldHeight || newWidth != oldWidth) {
// Keep aspect ratio
if (java.lang.Math.abs(oldHeight - newHeight) >=
java.lang.Math.abs(oldWidth - newWidth)) {
oldHeight = newHeight;
inHeight = oldHeight - diffHeight;
inWidth = (int) (ratio * ( (float) inHeight));
oldWidth = inWidth + diffWidth;
}
else {
oldWidth = newWidth;
inWidth = oldWidth - diffWidth;
inHeight = (int) ( ( (float) inWidth) / ratio);
oldHeight = inHeight + diffHeight;
}
}
this.setSize(oldWidth, oldHeight);
}
else {
oldHeight = newHeight;
oldWidth = newWidth;
}
/*fvmain.SetSize( fvnumber,
this.videoCanvas.getWidth(),this.videoCanvas.getHeight() );*/
}

private void closeout() {
System.exit(0);
}

public void actionPerformed(ActionEvent evt) {
}

// First panel which opens Video Panel
public class GUICanvas
extends JComponent {

private String msg = "";
public int mouseX = 0;
public int mouseY = 0;
public int prevX = 0;
public int prevY = 0;
public boolean rubberBand = true;
public Color canvasColor = new java.awt.Color(0, 0, 0);
private videoGui videowindow;

public GUICanvas(videoGui v) {
videowindow = v;
repaint();
}

public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor( canvasColor );
g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
g2d.setColor(Color.red);
g2d.drawLine(0, 0, this.getWidth(), this.getHeight());
g2d.drawLine(this.getWidth(), 0, 0, this.getHeight());
}
}
}
 
A

Andrew Thompson

Hi KC!

I just ran your GUI and noticed something that surprised me.

I had always thought you meant to resize the inner vidoe component to
'fill to the maximum of either height or width that is available,
then PAD the other'[1] (e.i. Stretch to maximum height and pad width,
or fill to width and pad height)

It seems that might be[2] both easier to do, and less likely to
interfere with the window sizing.

Would 'pad with black[3]' work for your application?

[1] The red 'X' certainly helped!
[2] Untested hunch.
[3] Choose your color, ..any color.
 
K

kc

Hello,

Thanks for the reply. In a sort of roundabout way I kind of do what
you're suggesting. That is, I resize to the aspect ratio based on one
of either height or width. It is a little less predictable than I
would like, but it has worked fine in the past. At any rate, I has
halfway through a Dairy Queen Blizzard yesterday when an idea hit me
and it seems to have (somewhat) fixed my problem, and in turn a new
problem has surfaced. The new problem is not nearly as bad, however,
and if it isn't fixable then that is okay.

The fix is to detect a mousedown use a semaphore that basically says,
"Don't you dare do that aspect ratio thing right now!" Instead, when
the user resizes while the mouse button is down, the componentResize
event sets yet another semaphore, in this case mouse_resize to true
which basically says, "While the mouse button was down, the user
resized". Now when the user releases the mouse button, it calls the
resizeVideo() function if the mouse_resize flag is true.

The problem is that while the user is resizing, some flickering, grey
striping occurs. I am guessing that this is because the JFrame is not
double buffered (the striping goes all the way up through the title bar
and such). My GUICanvas is double buffered, so I thought it wouldn't
affect this, but it does... So alas, I am unhappy. If you have any
idea what may be causing this flickering effect and any way to fix it,
please let me know.

If you want to add in my fix to the test application I sent along,
simply replace the initGUI() method with this one and add the
appropriate variables at the top:
private boolean mouse_down = false;
private boolean mouse_resized = false;

private void initGUI() {
GetInfo();
createMenuBar();
doTheLayout();

this.setFocusable(true);

this.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent me) {

}
public void mouseEntered(MouseEvent me) {

}
public void mouseExited(MouseEvent me) {

}
public void mousePressed(MouseEvent me) {
mouse_down = true;
}
public void mouseReleased(MouseEvent me) {
if ( mouse_resized = true ) {
resizeVideo();
setPortPosition();
mouse_resized=false;
mouse_down=false;
}
}
});
this.addComponentListener(new ComponentAdapter() {
public void componentHidden(ComponentEvent evt) {

}

public void componentMoved(ComponentEvent evt) {
setPortPosition();
}

public void componentResized(ComponentEvent evt) {
if ( mouse_down == false ) {
resizeVideo();
setPortPosition();
} else {
mouse_resized = true;
}
}

public void componentShown(ComponentEvent evt) {
resizeVideo();
setPortPosition();
}
});

this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
closeout();
}

public void windowClosed(WindowEvent evt) {
closeout();
}

public void windowDeiconified(WindowEvent evt) {
setPortPosition();
}

public void windowIconified(WindowEvent evt) {
}

public void windowActivated(WindowEvent evt) {
setPortPosition();
}

public void windowDeactivated(WindowEvent evt) {
}

public void windowOpened(WindowEvent evt) {
setPortPosition();
}
});
}

Thanks for the help!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top