Circular Wrapping text needs slight adjustment - java

You may not be able to tell that much but for some reason a few of the characters are being offset somehow, due to a flaw in my algorithm...If someone could figure out what is causing it, I would really appreciate it and any critique is welcome as I'm still very new at java.
Edit: If you look at the image above it's the E that is offset in WE on the left and right side
Edit: I think it may be in my calculation of the size of text vs size of circle
Edit: Ok so when I enter 600 for width and height everything seems to fall in place, but as it gets smaller from say 250 for example the characters start becoming more offset and overlapping
Main class:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
/**
* Created by John on 7/11/2014.
*/
public class Prog14_05 extends Application {
#Override
public void start(Stage primaryStage) {
// Create Pane
circularText phrase = new circularText("WE ARE ANONYMOUS, " +
"WE ARE LEGION, WE DO NOT FORGIVE, WE DO NOT FORGET ",
480, 480);
// Place clock and label in border pane
GridPane pane = new GridPane();
pane.setPadding(new Insets(phrase.getTextSize() * 2));
pane.setAlignment(Pos.CENTER);
pane.setStyle("-fx-background-color: black");
pane.getChildren().add(phrase);
// Create a scene and place it in the stage
Scene scene = new Scene(pane);
primaryStage.setTitle("Exercise14_05");
primaryStage.setScene(scene);
primaryStage.show();
}
}
circularText Class:
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
/**
* Created by John on 7/11/2014.
*/
public class circularText extends Pane {
double textSize = 30;
String string = "";
String fontName = "";
Font font;
// Pane's width and height
private double w = 250, h = 250;
/** Create Constructor */
public circularText (String phrase, double w, double h) {
this.w = w;
this.h = h;
this.string = phrase;
textSize = (this.w / this.string.length()) * 2;
Font font = new Font("Times Roman", textSize);
paintText(this.string, this.font);
}
/** Set new font */
public void setFont(String name) {
Font font = new Font(name, textSize);
this.font = font;
this.fontName = name;
paintText(this.string, this.font);
}
/** Return textSize */
public double getTextSize() {
return this.textSize;
}
/** Set textSize */
public void setTextSize(double textSize) {
this.textSize = textSize;
Font font = new Font(fontName, textSize);
this.font = font;
paintText(this.string, this.font);
}
/** Return pane's width */
public double getW() {
return w;
}
/** Set pane's width */
public void setW(double w) {
this.w = w;
textSize = (this.w / this.string.length()) * 2;
paintText(this.string, this.font);
}
/** Return pane's height */
public double getH() {
return h;
}
/** Set pane's height */
public void setH(double h) {
this.h = h;
textSize = (this.w / this.string.length()) * 2;
paintText(this.string, this.font);
}
/** Paint the Letters */
protected void paintText(String phrase, Font font) {
// Initialize parameters
double radius = Math.min(w, h) * 0.8 * 0.5;
double centerX = w / 2;
double centerY = h / 2;
double size = radius / 4 - this.getTextSize();
// Draw circle
Circle circle = new Circle(centerX - size - textSize, centerY - size,
radius);
circle.setFill(null);
circle.setStroke(null);
getChildren().clear();
getChildren().add(circle);
// Place text in a circular pattern
int i = 0;
double degree = 360 / phrase.length();
for (double degrees = 0; i < phrase.length(); i++, degrees += degree) {
double pointX = circle.getCenterX() + circle.getRadius() *
Math.cos(Math.toRadians(degrees));
double pointY = circle.getCenterY() + circle.getRadius() *
Math.sin(Math.toRadians(degrees));
Text letter = new Text(pointX, pointY, phrase.charAt(i) + "");
letter.setFont(font);
letter.setFill(Color.LIME);
letter.setRotate(degrees + 90);
getChildren().add(letter);
}
}
}

My trig isn't very good so I can't help you there. I'm thinking the "W" may be offset, not the "E". I know in other versions of Swing the "W" has caused painting problems before, but I don't remember the details. So I might suggest trying different characters to see if you still have the same problem at those two locations.
Here is another example of circular painting that I found on the web a long time ago. I tried your text and the "WE" is overlapped. I changed the "W" to an "R" and it seems to work ok, so maybe this validates my above statement?
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Font;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GradientPaint;
import java.awt.RenderingHints;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.geom.AffineTransform;
//import com.sun.awt.AWTUtilities;
public class SplashPortalPanel6 extends JPanel
{
private static final long serialVersionUID = 1L;
// private static final char[] MESSAGE = " SplashPortal.net".toCharArray();
private static final char[] MESSAGE = " WE ARE ANONYMOUS, WE ARE LEGION, WE DO NOT FORGIVE, WE DO NOT FORGET ".toCharArray();
// private static final char[] MESSAGE = " RE ARE ANONYMOUS, RE ARE LEGION, RE DO NOT FORGIVE, RE DO NOT FORGET ".toCharArray();
private static final double R90 = Math.toRadians(90);
private static final double R_90 = Math.toRadians(-90);
private AffineTransform cumalativeRotation = new AffineTransform();
private double rotation = Math.toRadians(360.0 / MESSAGE.length);
private Font font = new Font("Impact",Font.ITALIC,40);
private final Timer timer = new Timer(1000/76, new ActionListener() {
public void actionPerformed(ActionEvent e) {
repaint();//just repaint
}
});
public SplashPortalPanel6() {
setPreferredSize(new java.awt.Dimension(600, 600));
setOpaque(false);
}
//This method is called when the panel is connected to a native
//screen resource. It's an indication we can now start painting.
public void addNotify() {
super.addNotify();
timer.start();
}
public void removeNotify() {
super.removeNotify();
timer.stop();
}
private static final GradientPaint gradient = new GradientPaint(0F, 0F, Color.BLUE, 5F, 10F, Color.CYAN, true);
private static final int x = 0, y = 0, w = 100, h = 100;
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setFont(font);
g2.translate( getWidth()/2, getHeight()/2 );
cumalativeRotation.rotate(rotation/50);
g2.transform( cumalativeRotation );
for(int i = 0; i < MESSAGE.length; i++) {
// fill the rectangle
g2.translate(250, 0);
g2.rotate(R90);
g2.setColor(Color.BLACK);
// g2.fillRect(x,y,w,h);
// draw the border
g2.setColor(Color.WHITE);
// g2.drawRect(x,y,w,h);
// draw the character
g2.setPaint(gradient);
g2.drawChars(MESSAGE,i, 1, x+30, y+50);
g2.rotate(R_90);
g2.translate(-250, 0);
g2.rotate(rotation);
}
}
public static void createAndShowSplashScreen() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
frame.setContentPane(new SplashPortalPanel6());
frame.pack();
frame.setLocationRelativeTo(null);
// AWTUtilities.setWindowOpaque(frame, false);
//frame.setAlwaysOnTop(true);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowSplashScreen();
}
});
}
}
Note, if you uncomment the "fillRect" and "drawRect" statements you will see the original implementation of the code. Of course you will need to use the shorter first message string to see the effect.

Add
letter.setTextAlignment(TextAlignment.CENTER);
letter.setWrappingWidth(100);
Not sure what is going on with JavaFX Text rendering.
The math appears correct. For clarity when coding some suggest adding explicit typing to ensure your not mixing doubles with floats with ints. So instead of
double centerY = h / 2;
do
double centerY = h / 2.0d;
(Also take out extra "this." clutter, many methods are not being used like "setH", and make class name upper case CircularText)

Alright here is what I got so far, let me know what you all think and what can be improved upon...
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
/**
* Created by John on 7/11/2014.
*/
public class CircularText extends Pane {
private double circleWidth;
private double circleHeight;
private double textSize;
private double textStartDegree;
private double textRotate;
private double gapSpacing;
private double offSetX;
private double offSetY;
private Font font;
private Paint textFill;
private String fontName;
final String text;
/** Default Constructor */
public CircularText(String text) {
this.circleWidth = 250;
this.circleHeight = 250;
this.text = text;
textSize = (this.circleWidth / this.text.length()) * 2;
this.font = new Font("Times Roman", textSize);
this.textFill = Color.BLACK;
this.textStartDegree = 240;
this.textRotate = 90;
this.gapSpacing = 0.975;
this.offSetX = 4;
this.offSetY = 3;
paintText(this.text, this.font);
}
/** Create Constructor */
public CircularText (String text, double w, double h) {
this.circleWidth = w;
this.circleHeight = h;
this.text = text;
textSize = (this.circleWidth / (this.text.length()) * 2);
this.font = new Font("Times Roman", textSize);
this.textFill = Color.BLACK;
this.textStartDegree = 240;
this.textRotate = 90;
this.gapSpacing = 0.975;
this.offSetX = 4;
this.offSetY = 3;
paintText(this.text, this.font);
}
/** Get font color */
public Paint getTextFill() {
return textFill;
}
/** Set font color */
public void setTextFill(Paint textFill) {
this.textFill = textFill;
this.font = new Font(fontName, textSize);
paintText(this.text, this.font);
}
/** Get starting position for text */
public double getTextStartDegree() {
return textStartDegree;
}
/** Set starting position for text */
public void setTextStartDegree(double textStartDegree) {
this.textStartDegree = textStartDegree;
this.font = new Font(fontName, textSize);
paintText(this.text, this.font);
}
/** Get letter rotation */
public double getTextRotate() {
return textRotate;
}
/** Set letter rotation */
public void setTextRotate(double textRotate) {
this.textRotate = textRotate;
this.font = new Font(fontName, textSize);
paintText(this.text, this.font);
}
/** Get spacing between ending and beginning of phrase */
public double getGapSpacing() {
return gapSpacing;
}
/** Set spacing between ending and beginning of phrase */
public void setGapSpacing(double gapSpacing) {
this.gapSpacing = gapSpacing;
this.font = new Font(fontName, textSize);
paintText(this.text, this.font);
}
/** Get current font */
public Font getFont() {
return this.font;
}
/** Set new font */
public void setFont(String name) {
this.font = new Font(name, textSize);
this.fontName = name;
paintText(this.text, this.font);
}
/** Return textSize */
public double getTextSize() {
return this.textSize;
}
/** Set textSize */
public void setTextSize(double textSize, double offSetX, double offSetY) {
this.textSize = textSize;
this.offSetX = offSetX;
this.offSetY = offSetY;
this.font = new Font(fontName, textSize);
paintText(this.text, this.font);
}
/** Return circle's width */
public double getCircleWidth() {
return circleWidth;
}
/** Set circle's width */
public void setCircleWidth(double w) {
this.circleWidth = w;
textSize = (this.circleWidth / this.text.length()) * 2;
paintText(this.text, this.font);
}
/** Return circle's height */
public double getCircleHeight() {
return circleHeight;
}
/** Set circle's height */
public void setCircleHeight(double h) {
this.circleHeight = h;
textSize = (this.circleWidth / this.text.length()) * 2;
paintText(this.text, this.font);
}
/** Paint the Letters */
protected void paintText(String text, Font font) {
getChildren().clear();
// Initialize parameters
double radius = Math.min(circleWidth, circleHeight) * 0.8 * 0.5;
double centerX = circleWidth / 2;
double centerY = circleHeight / 2;
// Place text in a circular pattern
int i = 0;
double degree = 360.0 / (text.length() / this.gapSpacing);
for (double degrees = this.textStartDegree;
i < text.length(); i++, degrees += degree) {
double pointX = centerX + radius *
Math.cos(Math.toRadians(degrees)) - (this.textSize) *
this.offSetX;
double pointY = centerY + radius *
Math.sin(Math.toRadians(degrees)) - (this.textSize) *
this.offSetY;
Text letter = new Text(pointX, pointY,
String.valueOf(text.charAt(i)));
letter.setFont(font);
letter.setFill(this.textFill);
letter.setRotate(degrees + this.textRotate);
letter.setTextAlignment(TextAlignment.CENTER);
getChildren().add(letter);
}
}
}
After testing this with Courier New font, it appears to render flawlessly. I also tested this with other fonts and everything still rendered correctly. It appears the flaw in my code was correlated with the Circle object I had created for troubleshooting and for some reason decided to use in my algorithm. After removing this Circle object and fixing small flaws in my code and adding flexibility, everything works perfectly :)
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* Created by John on 7/11/2014.
*/
public class Prog14_05 extends Application {
#Override
public void start(Stage primaryStage) {
// Create Pane
CircularText phrase = new CircularText("TESTING MY CIRCULAR" +
"TEXT OBJECT CLASS",
500, 500);
phrase.setFont("Courier New");
phrase.setTextFill(Color.LIME);
phrase.setTextSize(20);
// Place clock and label in border pane
GridPane pane = new GridPane();
pane.setPadding(new Insets(phrase.getTextSize()));
pane.setAlignment(Pos.CENTER);
pane.setStyle("-fx-background-color: black");
pane.getChildren().add(phrase);
// Create a scene and place it in the stage
Scene scene = new Scene(pane);
primaryStage.setTitle("Exercise14_05");
primaryStage.setScene(scene);
primaryStage.show();
}
}
P.S Test my code and let me know if you find any bugs, Thanks for everyone's help

Related

Full screen scaling application JavaFX 8

this is going to be a second part to my first post about full screening.
Scaling start screen with full screen in JavaFX
With a little bit of tweaking the start screen scales when full screen but now, I can't seem to get the game itself to scale correctly.
package application.console;
import java.util.List;
import application.areas.startingArea.SA;
import application.areas.vanguardForest.VFCmds;
import application.areas.vanguardForest.VFNavi;
import application.areas.vanguardForest.VFPkups;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
public class Console extends Region {
public static double WIDTH = 990;
public static double HEIGHT = 525;
private final Font cinzel;
private final BorderPane root;
private final VBox console;
private final ScrollPane scroll;
private final HBox inputBar;
private final TextField input;
private final Text carrot;
public Console() {
this.root = new BorderPane();
root.setStyle("-fx-background-color: #232323;");
this.cinzel = Font.loadFont("file:fonts/static/Cinzel-Medium.ttf", 16);
this.console = new VBox();
console.setPrefWidth(WIDTH);
console.setPrefHeight(HEIGHT);
this.scroll = new ScrollPane();
scroll.setContent(console);
scroll.setStyle("-fx-background: #232323;"
+ "-fx-background-color: transparent;"
+ "-fx-border-color: #232323;"
+ "-fx-focus-color: #232323;");
scroll.setHbarPolicy(ScrollBarPolicy.NEVER);
scroll.setVbarPolicy(ScrollBarPolicy.NEVER);
scroll.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, null, null)));
console.setStyle("-fx-background-color: #232323;"
+ "-fx-focus-color: #232323;");
console.heightProperty().addListener(new ChangeListener<Object>() {
#Override
public void changed(ObservableValue<?> observable, Object oldValue, Object newValue) {
scroll.setVvalue((Double)newValue);
}
});
this.inputBar = new HBox();
inputBar.setPrefSize(WIDTH, 16);
this.input = new TextField();
input.setStyle("-fx-background-color: transparent;"
+ "-fx-text-fill: #FFFFFF;"
+ "-fx-highlight-fill: #FFFFFF;"
+ "-fx-highlight-text-fill: #232323;"
);
input.setFont(cinzel);
input.setPrefWidth(WIDTH);
this.carrot = new Text(" > ");
carrot.setFont(Font.loadFont("file:fonts/static/Cinzel-Medium.ttf", 24));
carrot.setFill(Color.WHITE);
inputBar.getChildren().addAll(carrot, input);
root.setMinSize(WIDTH, (HEIGHT - input.getHeight()));
input.setOnAction(e -> {
String s = (input.getText()).stripTrailing();
if ((SA.isBuried || SA.inVF) && SS.gameStart) {
Cmds.commands(s); //has general functions
VFCmds.commands(s); //doesn't have function until specific command
VFPkups.pickUp(s); //commands that allows pickups to be made within the game
VFNavi.commands(s); //commands navigation for selected area
} else {
Cmds.commands(s);
}
input.clear();
});
root.getChildren().addAll(console, scroll, inputBar);
root.setBottom(inputBar);
}
#Override
protected void layoutChildren() {
double xScale = getWidth() / root.getWidth();
double yScale = getHeight() / root.getHeight();
double scale = Math.min(xScale, yScale);
for (BorderPane bp : List.of(root)) {
scaleAndCenter(bp, scale);
}
for (VBox vb : List.of(console)) {
scaleAndCenter(vb, scale);
}
for (HBox hb : List.of(inputBar)) {
scaleAndCenter(hb, scale);
}
for (ScrollPane sp : List.of(scroll)) {
scaleAndCenter(sp, scale);
}
for (TextField tf : List.of(input)) {
scaleAndCenter(tf, scale);
}
for (Text text : List.of(carrot)) {
scaleAndCenter(text, scale);
}
}
private void scaleAndCenter(BorderPane root, double scale) {
double w = scale * root.getWidth();
double h = scale * root.getHeight();
root.setPrefSize(w, h);
root.relocate((getWidth() - w) / 2, (getHeight() - h) / 2);
}
private void scaleAndCenter(VBox vb, double scale) {
double w = scale * vb.getWidth();
double h = scale * vb.getHeight();
vb.setPrefSize(w, h);
vb.relocate((getWidth() - w) / 2, (getHeight() - h) / 2);
}
private void scaleAndCenter(HBox hb, double scale) {
double w = scale * hb.getWidth();
double h = scale * hb.getHeight();
hb.setPrefSize(w, h);
hb.relocate((getWidth() - w) / 2, (getHeight() - h) / 2);
}
private void scaleAndCenter(TextField input, double scale) {
double w = scale * input.getWidth();
double h = scale * input.getHeight();
input.setPrefSize(w, 16);
input.relocate((getWidth() - w) / 2, (getHeight() - h) / 2);
}
private void scaleAndCenter(ScrollPane scroll, double scale) {
double w = scale * scroll.getWidth();
double h = scale * scroll.getHeight();
scroll.setPrefSize(w, h);
scroll.relocate((getWidth() - w) / 2, (getHeight() - h) / 2);
}
private void scaleAndCenter(Text text, double scale) {
double w = scale * text.getLayoutBounds().getWidth();
double h = scale * text.getLayoutBounds().getHeight();
double size = scale * text.getFont().getSize();
text.setFont(Font.font(
text.getFont().getFamily(),
size));
text.relocate((getWidth() - w) / 2, (getHeight() - h) / 2);
}
}
Doing it this way, I get an error saying
Caused by: java.lang.RuntimeException: Exception in Application start method
at javafx.graphics#18.0.1/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:901)
at javafx.graphics#18.0.1/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.IllegalArgumentException: Children: duplicate children added: parent = BorderPane#56dca201
If I were to remove the BorderPane from the class, it'll just show up a white window.
package application.console;
import javafx.scene.Parent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
public class game {
public static Parent runGame() {
Console c = new Console();
AnchorPane anchor = new AnchorPane(c);
AnchorPane.setTopAnchor(c, 0.0);
AnchorPane.setRightAnchor(c, 0.0);
AnchorPane.setBottomAnchor(c, 0.0);
AnchorPane.setLeftAnchor(c, 0.0);
BorderPane root = new BorderPane();
root.setCenter(anchor);
return root;
}
}
Reasoning onto why making it a Parent: It allows me to turn it into a scene.(though there could be many different ways to do that) How would I implement full screening to the console and get it to show?
There is a lot of code in your question, most is irrelevant to your issue, so I won’t discuss it here. In particular your problem has nothing to do with scaling.
The error message tells you what you did wrong:
duplicate children added: parent = BorderPane
The code which caused that is this:
this.root = new BorderPane();
// . . .
root.getChildren().addAll(
console,
scroll,
inputBar
);
root.setBottom(inputBar);
You added inputBar to the root BorderPane twice.
I don’t advise adding to the children of a border pane directly either. Instead, use the positional setters on the borderPane to set the positions of the children (those setters will internally add the set nodes as children). Study the BorderPane documentation.

Automatically resize Canvas to fill the enclosing Parent

I recently wanted to create an animated background in JavaFX, similar to the Swing example seen here. I used a Canvas on which to draw, as shown in Working with the Canvas API, and an AnimationTimer for the drawing loop, as shown in Animation Basics. Unfortunately, I'm not sure how to resize the Canvas automatically as the enclosing Stage is resized. What is a good approach?
A similar question is examined in How to make canvas Resizable in javaFX?, but the accepted answer there lacks the binding illustrated in the accepted answer here.
In the example below, the static nested class CanvasPane wraps an instance of Canvas in a Pane and overrides layoutChildren() to make the canvas dimensions match the enclosing Pane. Note that Canvas returns false from isResizable(), so "the parent cannot resize it during layout," and Pane "does not perform layout beyond resizing resizable children to their preferred sizes." The width and height used to construct the canvas become its initial size. A similar approach is used in the Ensemble particle simulation, FireworksApp, to scale a background image while retaining its aspect ratio.
As an aside, note the difference from using fully saturated colors compared to the original. These related examples illustrate placing controls atop the animated background.
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/a/31761362/230513
* #see https://stackoverflow.com/a/8616169/230513
*/
public class Baubles extends Application {
private static final int MAX = 64;
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private static final Random RND = new Random();
private final Queue<Bauble> queue = new LinkedList<>();
private Canvas canvas;
#Override
public void start(Stage stage) {
CanvasPane canvasPane = new CanvasPane(WIDTH, HEIGHT);
canvas = canvasPane.getCanvas();
BorderPane root = new BorderPane(canvasPane);
CheckBox cb = new CheckBox("Animate");
cb.setSelected(true);
root.setBottom(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
for (int i = 0; i < MAX; i++) {
queue.add(randomBauble());
}
AnimationTimer loop = new AnimationTimer() {
#Override
public void handle(long now) {
GraphicsContext g = canvas.getGraphicsContext2D();
g.setFill(Color.BLACK);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
for (Bauble b : queue) {
g.setFill(b.c);
g.fillOval(b.x, b.y, b.d, b.d);
}
queue.add(randomBauble());
queue.remove();
}
};
loop.start();
cb.selectedProperty().addListener((Observable o) -> {
if (cb.isSelected()) {
loop.start();
} else {
loop.stop();
}
});
}
private static class Bauble {
private final double x, y, d;
private final Color c;
public Bauble(double x, double y, double r, Color c) {
this.x = x - r;
this.y = y - r;
this.d = 2 * r;
this.c = c;
}
}
private Bauble randomBauble() {
double x = RND.nextDouble() * canvas.getWidth();
double y = RND.nextDouble() * canvas.getHeight();
double r = RND.nextDouble() * MAX + MAX / 2;
Color c = Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75);
return new Bauble(x, y, r, c);
}
private static class CanvasPane extends Pane {
private final Canvas canvas;
public CanvasPane(double width, double height) {
canvas = new Canvas(width, height);
getChildren().add(canvas);
}
public Canvas getCanvas() {
return canvas;
}
#Override
protected void layoutChildren() {
super.layoutChildren();
final double x = snappedLeftInset();
final double y = snappedTopInset();
// Java 9 - snapSize is deprecated, use snapSizeX() and snapSizeY() accordingly
final double w = snapSize(getWidth()) - x - snappedRightInset();
final double h = snapSize(getHeight()) - y - snappedBottomInset();
canvas.setLayoutX(x);
canvas.setLayoutY(y);
canvas.setWidth(w);
canvas.setHeight(h);
}
}
public static void main(String[] args) {
launch(args);
}
}
I combined both prior solutions ( #trashgod and #clataq's ) by putting the canvas in a Pane and binding it to it:
private static class CanvasPane extends Pane {
final Canvas canvas;
CanvasPane(double width, double height) {
setWidth(width);
setHeight(height);
canvas = new Canvas(width, height);
getChildren().add(canvas);
canvas.widthProperty().bind(this.widthProperty());
canvas.heightProperty().bind(this.heightProperty());
}
}
Couldn't you do this with a Binding as well? The following seems to produce the same results without having to add the derived class.
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.DoubleBinding;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* #see http://stackoverflow.com/a/31761362/230513
* #see http://stackoverflow.com/a/8616169/230513
*/
public class Baubles extends Application {
private static final int MAX = 64;
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private static final Random RND = new Random();
private final Queue<Bauble> queue = new LinkedList<>();
private Canvas canvas;
#Override
public void start(Stage stage) {
canvas = new Canvas(WIDTH, HEIGHT);
BorderPane root = new BorderPane(canvas);
CheckBox cb = new CheckBox("Animate");
cb.setSelected(true);
root.setBottom(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
// Create bindings for resizing.
DoubleBinding heightBinding = root.heightProperty()
.subtract(root.bottomProperty().getValue().getBoundsInParent().getHeight());
canvas.widthProperty().bind(root.widthProperty());
canvas.heightProperty().bind(heightBinding);
for (int i = 0; i < MAX; i++) {
queue.add(randomBauble());
}
AnimationTimer loop = new AnimationTimer() {
#Override
public void handle(long now) {
GraphicsContext g = canvas.getGraphicsContext2D();
g.setFill(Color.BLACK);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
for (Bauble b : queue) {
g.setFill(b.c);
g.fillOval(b.x, b.y, b.d, b.d);
}
queue.add(randomBauble());
queue.remove();
}
};
loop.start();
cb.selectedProperty().addListener((Observable o) -> {
if (cb.isSelected()) {
loop.start();
} else {
loop.stop();
}
});
}
private static class Bauble {
private final double x, y, d;
private final Color c;
public Bauble(double x, double y, double r, Color c) {
this.x = x - r;
this.y = y - r;
this.d = 2 * r;
this.c = c;
}
}
private Bauble randomBauble() {
double x = RND.nextDouble() * canvas.getWidth();
double y = RND.nextDouble() * canvas.getHeight();
double r = RND.nextDouble() * MAX + MAX / 2;
Color c = Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75);
return new Bauble(x, y, r, c);
}
public static void main(String[] args) {
launch(args);
}
}

java awt Canvas getInstance undefined (newbie)

Trying the exercise in lesson2 of the Udacity course. Despite importing the classes (I'm at java.awt.* now, but I also tried java.awt.Color and java.awt.Canvas separately (also need Shape))..
package com.jul.udacity.lesson2;
public class TestRectangle {
public static void main(String[] args) {
// TODO Auto-generated method stub
Rectangle rect1 = new Rectangle(100.0, 100.0, 200.0, 100.0);
rect1.draw();
}
}
And the class is copied from there and java.awt import added. Any help will be great. Thanks!
package com.jul.udacity.lesson2;
//HIDE
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
//import java.awt.Color;
//import java.awt.Shape;
//import java.awt.Canvas;
import java.awt.*;
public class Rectangle implements Shape
{
private Color color = Color.BLACK;
private boolean filled = false;
private double x;
private double y;
private double width;
private double height;
/**
Constructs an empty rectangle.
*/
public Rectangle()
{
x = 0;
y = 0;
width = 0;
height = 0;
}
/**
Constructs a rectangle.
#param x the leftmost x-coordinate
#param y the topmost y-coordinate
#param width the width
#param height the height
*/
public Rectangle(double x, double y, double width, double height)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
/**
Gets the leftmost x-position of this rectangle.
#return the leftmost x-position
*/
public int getX()
{
return (int) Math.round(x);
}
/**
Gets the topmost y-position of this rectangle.
#return the topmost y-position
*/
public int getY()
{
return (int) Math.round(y);
}
/**
Gets the width of this rectangle.
#return the width
*/
public int getWidth()
{
return (int) Math.round(width);
}
/**
Gets the height of this rectangle.
#return the height
*/
public int getHeight()
{
return (int) Math.round(height);
}
/**
Moves this rectangle by a given amount.
#param dx the amount by which to move in x-direction
#param dy the amount by which to move in y-direction
*/
public void translate(double dx, double dy)
{
x += dx;
y += dy;
Canvas.getInstance().repaint();
}
/**
Resizes this rectangle both horizontally and vertically.
#param dw the amount by which to resize the width on each side
#param dw the amount by which to resize the height on each side
*/
public void grow(double dw, double dh)
{
width += 2 * dw;
height += 2 * dh;
x -= dw;
y -= dh;
Canvas.getInstance().repaint();
}
/**
Sets the color of this rectangle.
#param newColor the new color
*/
public void setColor(Color newColor)
{
color = newColor;
Canvas.getInstance().repaint();
}
/**
Draws this rectangle.
*/
public void draw()
{
filled = false;
Canvas.getInstance().show(this);
}
/**
Fills this rectangle.
*/
public void fill()
{
filled = true;
Canvas.getInstance().show(this);
}
public String toString()
{
return "Rectangle[x=" + getX() + ",y=" + getY() + ",width=" + getWidth() + ",height=" + getHeight() + "]";
}
public void paintShape(Graphics2D g2)
{
Rectangle2D.Double rect = new Rectangle2D.Double(getX(), getY(),
getWidth(), getHeight());
g2.setColor(new java.awt.Color((int) color.getRed(), (int) color.getGreen(), (int) color.getBlue()));
if (filled)
{
g2.fill(rect);
}
else
{
g2.draw(rect);
}
}
}
You might want to check the lesson directions carefully. java.awt.Canvas has no getInstance() method. You just use new to make a Canvas. So you either didn't read carefully and are using the wrong Canvas, or there's something else going on.
Also the show() methods are deprecated, so I'm leaning towards you are supposed to be using a different Canvas class.
Also, Swing is not thread safe. Read up on how to use Swing objects
markspace is correct: Canvas here is not the class from java.awt - the Udacity instructors are using their own class called Canvas. I suggest grabbing the lesson files and using those. Here's the Canvas class from that Intro To Java Course:
import java.awt.image.BufferedImage;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.RescaleOp;
import java.io.IOException;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class Canvas
{
private static Canvas canvas = new Canvas();
private ArrayList<Shape> shapes = new ArrayList<Shape>();
private BufferedImage background;
private JFrame frame;
private CanvasComponent component;
private static final int MIN_SIZE = 100;
private static final int MARGIN = 10;
private static final int LOCATION_OFFSET = 120;
class CanvasComponent extends JComponent
{
public void paintComponent(Graphics g)
{
g.setColor(java.awt.Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(java.awt.Color.BLACK);
if (background != null)
{
g.drawImage(background, 0, 0, null);
}
for (Shape s : new ArrayList<Shape>(shapes))
{
Graphics2D g2 = (Graphics2D) g.create();
s.paintShape(g2);
g2.dispose();
}
}
public Dimension getPreferredSize()
{
int maxx = MIN_SIZE;
int maxy = MIN_SIZE;
if (background != null)
{
maxx = Math.max(maxx, background.getWidth());
maxy = Math.max(maxx, background.getHeight());
}
for (Shape s : shapes)
{
maxx = (int) Math.max(maxx, s.getX() + s.getWidth());
maxy = (int) Math.max(maxy, s.getY() + s.getHeight());
}
return new Dimension(maxx + MARGIN, maxy + MARGIN);
}
}
private Canvas()
{
component = new CanvasComponent();
if (System.getProperty("com.horstmann.codecheck") == null)
{
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(component);
frame.pack();
frame.setLocation(LOCATION_OFFSET, LOCATION_OFFSET);
frame.setVisible(true);
}
else
{
final String SAVEFILE ="canvas.png";
final Thread currentThread = Thread.currentThread();
Thread watcherThread = new Thread()
{
public void run()
{
try
{
final int DELAY = 10;
while (currentThread.getState() != Thread.State.TERMINATED)
{
Thread.sleep(DELAY);
}
saveToDisk(SAVEFILE);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
};
watcherThread.start();
}
}
public static Canvas getInstance()
{
return canvas;
}
public void show(Shape s)
{
if (!shapes.contains(s))
{
shapes.add(s);
}
repaint();
}
public void repaint()
{
if (frame == null) return;
Dimension dim = component.getPreferredSize();
if (dim.getWidth() > component.getWidth()
|| dim.getHeight() > component.getHeight())
{
frame.pack();
}
else
{
frame.repaint();
}
}
/**
* Pauses so that the user can see the picture before it is transformed.
*/
public void pause()
{
if (frame == null) return;
JOptionPane.showMessageDialog(frame, "Click Ok to continue");
}
/**
* Takes a snapshot of the screen, fades it, and sets it as the background.
*/
public static void snapshot()
{
Dimension dim = getInstance().component.getPreferredSize();
java.awt.Rectangle rect = new java.awt.Rectangle(0, 0, dim.width, dim.height);
BufferedImage image = new BufferedImage(rect.width, rect.height, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(java.awt.Color.WHITE);
g.fillRect(0, 0, rect.width, rect.height);
g.setColor(java.awt.Color.BLACK);
getInstance().component.paintComponent(g);
float factor = 0.8f;
float base = 255f * (1f - factor);
RescaleOp op = new RescaleOp(factor, base, null);
BufferedImage filteredImage
= new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
op.filter(image, filteredImage);
getInstance().background = filteredImage;
getInstance().component.repaint();
}
public void saveToDisk(String fileName)
{
Dimension dim = component.getPreferredSize();
java.awt.Rectangle rect = new java.awt.Rectangle(0, 0, dim.width, dim.height);
BufferedImage image = new BufferedImage(rect.width, rect.height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) image.getGraphics();
g.setColor(java.awt.Color.WHITE);
g.fill(rect);
g.setColor(java.awt.Color.BLACK);
component.paintComponent(g);
String extension = fileName.substring(fileName.lastIndexOf('.') + 1);
try
{
ImageIO.write(image, extension, new File(fileName));
}
catch(IOException e)
{
System.err.println("Was unable to save the image to " + fileName);
}
g.dispose();
}
}

Custom button is shrinked when in a BoxLayout

I have a custom button class that I created that extends JButton. When I add this to a JFrame, I get this:
But when I place this custom button into the frame with BoxLayout, the button becomes smaller and is not desirable this way:
Here is my code for the frame:
Test.java
import javax.swing.BoxLayout;
import javax.swing.JFrame;
public class Test {
public static void main(String[] args) {
JFrame frame = new JFrame();
//When you uncomment this, the button is sized really small
//frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(),BoxLayout.PAGE_AXIS));
frame.add(new CButton("Hello"));
frame.pack();
frame.setVisible(true);
}
}
And here is the code for the custom button, CButton:
CButton.java
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JToolTip;
public class CButton extends JButton implements ComponentListener {
protected static final int BORDER_WIDTH = 5;
private static final Insets INSETS_MARGIN = new Insets(2, 5, 2, 5);
private static final long serialVersionUID = 1L;
protected Area m_areaDraw = null;
private Area m_areaFill = null;
private double m_dHeightDraw = 0d;
private double m_dHeightFill = 0d;
private double m_dWidthDraw = 0d;
private double m_dWidthFill = 0d;
private int m_nMinHeight = 0;
private int m_nMinWidth = 0;
private int m_nStringHeightMax = 0;
private int m_nStringWidthMax = 0;
private RoundRectangle2D m_rrect2dDraw = null;
private RoundRectangle2D m_rrect2dFill = null;
private Shape m_shape = null;
public CButton(String strLabel) {
setContentAreaFilled(false);
setMargin(INSETS_MARGIN);
setFocusPainted(false);
addComponentListener(this);
setText(strLabel);
}
#Override
public void componentHidden(ComponentEvent e) {
}
#Override
public void componentMoved(ComponentEvent e) {
}
// Needed if we want this button to resize
#Override
public void componentResized(ComponentEvent e) {
m_shape = new Rectangle2D.Float(0, 0, getBounds().width,
getBounds().height);
m_dWidthFill = (double) getBounds().width - 1;
m_dHeightFill = (double) getBounds().height - 1;
m_dWidthDraw = ((double) getBounds().width - 1)
- (CButton.BORDER_WIDTH - 1);
m_dHeightDraw = ((double) getBounds().height - 1)
- (CButton.BORDER_WIDTH - 1);
setShape();
repaint();
}
#Override
public void componentShown(ComponentEvent e) {
}
#Override
public boolean contains(int nX, int nY) {
if ((null == m_shape) || m_shape.getBounds().equals(getBounds())) {
m_shape = new Rectangle2D.Float(0, 0, this.getBounds().width,
this.getBounds().height);
}
return m_shape.contains(nX, nY);
}
#Override
public void paintBorder(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHints(hints);
g2.setColor(Color.black);
Stroke strokeOld = g2.getStroke();
g2.setStroke(new BasicStroke(CButton.BORDER_WIDTH,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
if (getModel().isRollover()) {
g2.setColor(Color.ORANGE);
}
if (!getModel().isEnabled()) {
g2.setColor(Color.GRAY);
}
g2.draw(m_areaDraw);
g2.setStroke(strokeOld);
};
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHints(hints);
if (getModel().isArmed()) {
g2.setColor(Color.CYAN.darker());
} else {
g2.setColor(Color.CYAN);
}
g2.fill(m_areaFill);
super.paintComponent(g2);
}
private void setShape() {
// Area
double dArcLengthFill = Math.min(m_dWidthFill, m_dHeightFill);
m_rrect2dFill = new RoundRectangle2D.Double(0d, 0d, m_dWidthFill,
m_dHeightFill, dArcLengthFill, dArcLengthFill);
// WARNING: arclength and archeight are divided by 2
// when they get into the roundedrectangle shape
m_areaFill = new Area(m_rrect2dFill);
// Border
double dArcLengthDraw = Math.min(m_dWidthDraw, m_dHeightDraw);
m_rrect2dDraw = new RoundRectangle2D.Double(
(CButton.BORDER_WIDTH - 1) / 2, (CButton.BORDER_WIDTH - 1) / 2,
m_dWidthDraw, m_dHeightDraw, dArcLengthDraw, dArcLengthDraw);
m_areaDraw = new Area(m_rrect2dDraw);
}
#Override
public void setText(final String strText) {
super.setText(strText);
Frame frame = JOptionPane.getRootFrame();
FontMetrics fm = frame.getFontMetrics(getFont());
m_nStringWidthMax = fm.stringWidth(getText());
m_nStringWidthMax = Math.max(m_nStringWidthMax,
fm.stringWidth(getText()));
// WARNING: use getMargin. it refers to dist btwn text and border.
// Also use getInsets. it refers to the width of the border
int nWidth = Math.max(m_nMinWidth, m_nStringWidthMax + getMargin().left
+ getInsets().left + getMargin().right + getInsets().right);
m_nStringHeightMax = fm.getHeight();
// WARNING: use getMargin. it refers to dist btwn text and border.
// Also use getInsets. it refers to the width of the border
int nHeight = Math.max(m_nMinHeight, m_nStringHeightMax
+ getMargin().left + getInsets().left + getMargin().right
+ getInsets().right);
setPreferredSize(new Dimension(
nWidth + ((2 * getFont().getSize()) / 5), nHeight
+ ((2 * getFont().getSize()) / 5)));
// Set the initial draw and fill dimensions
setShape();
}
}
Is there a way I can fix this and make it the full size?
Override the getPreferredSize(). BoxLayout respects preferred sizes. If you don't the preferred size will be determined by the text. It is bigger in the frame because the default BorderLayout of the frame doesn't respect preferred size, and will stretch the button. Another thing you can do instead is set bigger margins and/or bigger font, as that will also increase the preferred size. So you have a few options/things to consider
Have a look here as which layout respect preferred sizes and which ones don't. Another always good resources is Laying out Components Within a Container

Java layouts not updating component size when custom button changes text

I am making a button where when you click on it it changes the text. But when I click on the button and change the text, the button does not change size according to the text. Instead, it gets smaller and attempts to make that "..." thing when it does not have enough room. Here is my code:
Text.java
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args){
JFrame frame = new JFrame();
JPanel panel = new JPanel();
frame.add(panel);
final CButton button = new CButton("");
panel.add(button);
button.addMouseListener(new MouseListener(){
#Override
public void mouseClicked(MouseEvent arg0) {
button.setText(button.getText()+"f");
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
});
frame.setSize(500,500);
frame.setVisible(true);
}
}
CButton.java
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JButton;
import javax.swing.JOptionPane;
public class CButton extends JButton implements ComponentListener, KeyListener {
protected static final int BORDER_WIDTH = 5;
private static final Font font = new Font("Arial", Font.PLAIN, 30);
private static final Insets INSETS_MARGIN = new Insets(2, 5, 2, 5);
private static final long serialVersionUID = 1L;
protected Area m_areaDraw = null;
private Area m_areaFill = null;
private double m_dHeightDraw = 0d;
private double m_dHeightFill = 0d;
private double m_dWidthDraw = 0d;
private double m_dWidthFill = 0d;
private int m_nMinHeight = 0;
private int m_nMinWidth = 0;
private int m_nStringHeightMax = 0;
private int m_nStringWidthMax = 0;
private RoundRectangle2D m_rrect2dDraw = null;
private RoundRectangle2D m_rrect2dFill = null;
private Shape m_shape = null;
public CButton(String strLabel) {
super(strLabel);
setContentAreaFilled(false);
setMargin(CButton.INSETS_MARGIN);
setFocusPainted(false);
addComponentListener(this);
addKeyListener(this);
// Determine the buttons initial size
setFont(CButton.font);
Frame frame = JOptionPane.getRootFrame();
FontMetrics fm = frame.getFontMetrics(getFont());
m_nStringWidthMax = fm.stringWidth(getText());
m_nStringWidthMax = Math.max(m_nStringWidthMax,
fm.stringWidth(getText()));
// WARNING: use getMargin. it refers to dist btwn text and border.
// Also use getInsets. it refers to the width of the border
int nWidth = Math.max(m_nMinWidth, m_nStringWidthMax + getMargin().left
+ this.getInsets().left + getMargin().right
+ this.getInsets().right);
m_nStringHeightMax = fm.getHeight();
// WARNING: use getMargin. it refers to dist btwn text and border.
// Also use getInsets. it refers to the width of the border
int nHeight = Math.max(m_nMinHeight, m_nStringHeightMax
+ getMargin().left + this.getInsets().left + getMargin().right
+ this.getInsets().right);
setPreferredSize(new Dimension(
nWidth + ((2 * getFont().getSize()) / 5), nHeight
+ ((2 * getFont().getSize()) / 5)));
// Set the initial draw and fill dimensions
setShape();
}
#Override
public void componentHidden(ComponentEvent e) {
}
#Override
public void componentMoved(ComponentEvent e) {
}
// Needed if we want this button to resize
#Override
public void componentResized(ComponentEvent e) {
m_shape = new Rectangle2D.Float(0, 0, getBounds().width,
getBounds().height);
m_dWidthFill = (double) getBounds().width - 1;
m_dHeightFill = (double) getBounds().height - 1;
m_dWidthDraw = ((double) getBounds().width - 1)
- (CButton.BORDER_WIDTH - 1);
m_dHeightDraw = ((double) getBounds().height - 1)
- (CButton.BORDER_WIDTH - 1);
setShape();
repaint();
}
#Override
public void componentShown(ComponentEvent e) {
}
#Override
public boolean contains(int nX, int nY) {
if ((null == m_shape) || m_shape.getBounds().equals(getBounds())) {
m_shape = new Rectangle2D.Float(0, 0, this.getBounds().width,
this.getBounds().height);
}
return m_shape.contains(nX, nY);
}
// This is so the button is triggered when it has focus
// and we press the Enter key.
#Override
public void keyPressed(KeyEvent e) {
if ((e.getSource() == this) && (e.getKeyCode() == KeyEvent.VK_ENTER)) {
doClick();
}
};
#Override
public void keyReleased(KeyEvent e) {
};
#Override
public void keyTyped(KeyEvent e) {
};
#Override
public void paintBorder(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHints(hints);
g2.setColor(Color.black);
Stroke strokeOld = g2.getStroke();
g2.setStroke(new BasicStroke(CButton.BORDER_WIDTH,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.draw(m_areaDraw);
if (getModel().isRollover()) {
g2.setColor(Color.GRAY);
g2.draw(m_areaDraw);
}
g2.setStroke(strokeOld);
};
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHints(hints);
if (getModel().isArmed()) {
g2.setColor(Color.CYAN.darker());
} else {
g2.setColor(Color.CYAN);
}
g2.fill(m_areaFill);
super.paintComponent(g2);
}
private void setShape() {
// Area
double dArcLengthFill = Math.min(m_dWidthFill, m_dHeightFill);
m_rrect2dFill = new RoundRectangle2D.Double(0d, 0d, m_dWidthFill,
m_dHeightFill, dArcLengthFill, dArcLengthFill);
// WARNING: arclength and archeight are divided by 2
// when they get into the roundedrectangle shape
m_areaFill = new Area(m_rrect2dFill);
// Border
double dArcLengthDraw = Math.min(m_dWidthDraw, m_dHeightDraw);
m_rrect2dDraw = new RoundRectangle2D.Double(
(CButton.BORDER_WIDTH - 1) / 2, (CButton.BORDER_WIDTH - 1) / 2,
m_dWidthDraw, m_dHeightDraw, dArcLengthDraw, dArcLengthDraw);
m_areaDraw = new Area(m_rrect2dDraw);
}
#Override
public void setText(String strText) {
super.setText(strText);
int nWidth = Math.max(m_nMinWidth, m_nStringWidthMax + getInsets().left
+ getInsets().right);
int nHeight = Math.max(0, getPreferredSize().height);
setPreferredSize(new Dimension(nWidth, nHeight));
m_dWidthFill = getBounds().width - 1;
m_dHeightFill = getBounds().height - 1;
if ((m_dWidthFill <= 0) || (m_dHeightFill <= 0)) {
m_dWidthFill = (double) getPreferredSize().width - 1;
m_dHeightFill = (double) getPreferredSize().height - 1;
}
m_dWidthDraw = m_dWidthFill - (CButton.BORDER_WIDTH - 1);
m_dHeightDraw = m_dHeightFill - (CButton.BORDER_WIDTH - 1);
setShape();
}
}
You call
setPreferredSize(new Dimension(
nWidth + ((2 * getFont().getSize()) / 5), nHeight
+ ((2 * getFont().getSize()) / 5)));
in the constructor, but never change it. This means that each time the layout manager asks the component what size it would like to be, it always gets the same value.
A preferred solution would be to override getPreferredSize and calculate the size there, if it's not to complicated or time consuming
The next question is why are you going to all this extra effort. Basically, you should simply allow the parent JButton to provide its preferred size and add your requirements around it, or simply use the margins properties or even a Border
You're KeyListener also seems useless as this is the default behaviour of the button anyway and in any case, key bindings would be a preferred solution
My first thought was that it had to do with your humongous CButton implementation. At least, copy/paste of your code created a working application. That's a big plus in my book.
When I simply replaced the strange nWidth calculation in the setText() method with:
int nWidth = 100;
The size was increased after a click and showed an "f", which I assume is what you wanted.
So, I can't really say how to calculate the width, but at least you know where to look and which line to change.

Categories

Resources