Simple question, hopefully there is a simple solution. I have a Label in my JavaFX application in which I would like a button to be at the end of a paragraph. By the end of the paragraph, I mean as if it were another character. I cannot set the X and Y to specific values because the length of the paragraph is not always the same, so the place where this button should be is not always the same.
Below, the red is where I would like a button to be. Any way to programmatically find this point?
Thanks
You can use a helper method to split your long text into a List of Text nodes that represent a single row of text. Then, after adding your Text to a FlowPane, just add the Button at the end.
Take a look at the following MCVE that demonstrates:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
public class TextFlowSample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
String longText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc nibh sapien, commodo in libero ac, feugiat accumsan odio. Aliquam erat volutpat. Integer accumsan sapien at elit aliquet sagittis. Praesent fermentum nec nunc ultrices mollis. Nam in odio ullamcorper, eleifend quam quis, aliquam sem. Nullam feugiat ex nec elit rutrum blandit. Etiam ut mauris magna. Proin est nunc, viverra quis tempus sed, dictum in lacus.";
// Create a FlowPane to hold our Text
FlowPane flowPane = new FlowPane();
// Now, our button
Button button = new Button("Click This!");
// Let's use our custom method to get a list of Text objects to add to the FlowPane
// Here we can set our max length and determine if we'll allow words to be broken up
flowPane.getChildren().addAll(getTextsFromString(longText, 75, false));
flowPane.getChildren().add(button);
root.getChildren().add(flowPane);
// Show the Stage
primaryStage.setWidth(425);
primaryStage.setHeight(300);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
/**
* Helper method to convert a long String into a List of Text objects
*/
private List<Text> getTextsFromString(String string, int maxLineLength, boolean breakWords) {
List<Text> textList = new ArrayList<>();
// If we are breaking up words, just cut into rows up to the max length
if (breakWords) {
int index = 0;
while (index < string.length()) {
textList.add(new Text(string.substring(index, Math.min(index + maxLineLength, string.length()))));
index += maxLineLength;
}
} else {
// First, let's insert linebreaks into the String when max length is reached. The tokenizer here
// allows us to split the string and keep the spaces, making it easy to loop through later.
StringTokenizer tokenizer = new StringTokenizer(string, " ", true);
StringBuilder sb = new StringBuilder();
int currentLength = 0;
while (tokenizer.hasMoreTokens()) {
String word = tokenizer.nextToken();
// If the next word would exceed our max length, add the new line character
if (currentLength + word.length() > maxLineLength) {
sb.append("\n");
currentLength = 0;
}
sb.append(word);
currentLength += word.length();
}
// Now we can split the string using the \n as a delimiter
List<String> rows = Arrays.asList(sb.toString().split("\n"));
for (String row : rows) {
textList.add(new Text(row));
}
}
return textList;
}
}
The Result:
I would think the easiest way to do this would be to create your own wrapping method to convert the paragraph string to an array of Text (or Label) objects based on the available width, and put these in a FlowPane with the Button at the end. I don't believe you can retrieve the actual end coords of the Label easily, as it's a rectangle covering the bounds of the Node.
Similar to the solution in this question in the sense of multiple Text objects joined together: JavaFX: setting background color for Text controls
Related
So I'm making like a digital brochure thing for Switzerland. I wanted to make it so that different images appear when pressing certain keys. Here's my code so far. When I press 1, 'mainpage' is displayed. However, when I press 4, 'government' isn't displayed.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main extends JFrame {
JFrame frame;
JLabel label;
JLabel image2;
JLabel title;
JLabel image;
JLabel footnote;
JPanel panel;
JScrollPane scrollPane;
ImageIcon mainpage;
ImageIcon government;
ImageIcon icon3;
int page = 1;
public Main() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel(new BorderLayout());
panel.setPreferredSize(new Dimension(800, 800));
label = new JLabel();
image2 = new JLabel();
title = new JLabel();
image = new JLabel();
footnote = new JLabel();
panel.add(image, BorderLayout.LINE_START);
panel.add(label, BorderLayout.LINE_END);
panel.add(image2, BorderLayout.CENTER);
panel.add(title, BorderLayout.PAGE_START);
panel.add(footnote, BorderLayout.PAGE_END);
title.setFont(new Font("Serif", Font.PLAIN, 20));
scrollPane = new JScrollPane(panel);
frame.add(scrollPane, BorderLayout.CENTER);
mainpage = new ImageIcon("mainpage.png");
government = new ImageIcon("Parliament-Building-Bern-Switzerland.png");
icon3 = new ImageIcon("6kcsr1gjpeia1.png");
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
char key = e.getKeyChar();
if (key == '1') {
image.setIcon(mainpage);
title.setText("La Communauté de Suisse");
}
if (key == '2') {
title.setText("<html><body style='width: 400px; text-align:justify'>La Cuisine de la Suisse</body></html>");
label.setText("<html><body style='width: 400px; text-align:justify'>Let us start by discussing Swiss cuisine. The foods of Switzerland is diverse and tasty. It ranges from the world-famous Swiss cheese to the delicious Swiss chocolate. Here are some Swiss delicacies that we curated and have decided to showcase.<br><br> Firstly, we have the cheese fondue - a type of melted cheese with bread cubes.</body></html>");
footnote.setText("Left - Cheese fondue. Right - Swiss cheese.");
}
if (key == '3') {
title.setText("<html><body style='width: 400px; text-align:justify'>L'écosystème de la Suisse</body></html>");
label.setText("<html><body style='width: 400px; text-align:justify'>Switzerland has over 230 natural environments, including meadows, moors, and forests. Forests. Altogether, they are home to around 64,000 species of plants and animals. Of these, only 45,890 are recorded, leaving much to be explored. It has been determined that such levels of biodiversity are due to Switzerland's inherently varied climate and landscape. </body></html>");
}
if (key == '4') {
title.setText("<html><body style='width: 400px; text-align:justify'>Le Gouvernement de la Suisse</body></html>");
image.setIcon(government);
label.setText("<html><body style='width: 400px; text-align:justify'> The government of Switzerland, like most states, is built from its constitutional framework. The Constitution of Switzerland was first drafted in 1848, where it heavily imitated the constitution of the USA. It was heavily revised in 1874, then again in 2000. It includes more than 200 articles, establishing the rights, powers, and privileges of the nation’s citizens and governors.<br><br>The government itself operates similarly to other democracies. The Federal government controls national and international affairs. It handles subjects like internal and external security, the military, and transportation. Legislative power is spread through the two halves of the National Assembly – the National Council, the lower house, which operates relatively similarly to the Canadian House of Commons, and the Council of Nations, the upper house and comparable to the Canadian senate.<br><br>Now, let us discuss the judicial system. It is based on the Napoleonic Code and follows a three-level court structure. Cantonal courts, situated at the first level, deal with most cases. The second level comprises higher courts that review the decisions of lower courts. The final stage is the Federal Supreme Court, which serves as the highest authority and hears appeals of lower courts' judgments. <br><br>Finally, on the executive side is the collegial (i.e., power is shared equally) Federal Council. It consists of seven members, and each presides over a certain department in the Federal government. </body></html>");
}
}
#Override
public void keyPressed(KeyEvent e) { }
#Override
public void keyReleased(KeyEvent e) { }
});
frame.setSize(500, 500);
frame.setVisible(true);
}
public static void main(String[] args) {
new Main();
}
}
I double-checked the image paths. Nope, nothing. Hell, I even asked ChatGPT, it offered no solution.
im new in java and my question might be stupid but i really dont know how to solve this problem, im making a Minesweeper and im using javafx, im using a class for the mines, so im using a "stackPane" and im puting it inside a grid, the problem i have is that i don't know hot to make multiples instances of it, and i have no clue how to start, im makinga 10*10 board and the mines are suposed to apear randomly, so im using "math.random" to decide if one space will be empti or if it will have a mine, i hope some one out there can help me
well i find a solution, but the thing i dont know how to do now is how to set a "setaction" for each button, im trying to put setonactio(new eventhandler() ...
here is my code:
package buscaminas2;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
*
* #author ALIENWARE R4
*/
public class Buscaminas2 extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("buscaminas");
GridPane mainGrid = new GridPane();
GridPane flowPane = new GridPane();
flowPane.setPadding(new Insets(2, 2, 2, 2));
flowPane.setVgap(2);
flowPane.setHgap(2);
//flowPane.setPrefWrapLength(210);
HBox score = new HBox();
Button btn = new Button();
for (int i = 0; i < 10; i++) {//-----------------bob the constructor------------------//
for (int j = 0; j < 10; j++)
{
int abc = (int)(Math.random()*10);
System.out.print(abc);
if( abc > 2)
{
btn = new Button(" ");
btn.setId(STYLESHEET_MODENA);
btn.setPrefSize(35, 35);
flowPane.add(btn, i, j);
flowPane.getChildren().get(i);
}
else
{
StackPane boomb = new StackPane();//---------------------bombs--------------------//
boomb.setPrefSize(35, 35);
Label num = new Label("X");
num.setPrefSize(40, 40);
boomb.getChildren().add(num);
Button xyz = new Button();
xyz.setPrefSize(40, 40);
boomb.getChildren().add(xyz);
flowPane.add(boomb, i, j);// agrega bombas en xy agregando "boomb" en el grid
}
}
}
Scene scene = new Scene(flowPane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I notice that if the TextBox is in a Page, then everything working perfectly. Whenever the TextBox is focused, it will scroll to the right position above the keyboard so that the user will be able to see the text as he is typing along. Thing is a little bit different for ContentDialog for whatever reason. TextBox is easily covered by the keyboard. Is there any obvious setting that I am missing?
I create a default ContentDialog and copied the code to a page. And get the following screenshots. Everything else is the same except that the upper level XAML elements is <ContentDialog> for the left column, <Page> for the right column.
Left Image - ContentDialog before keyboard pop up
Right Image - Page before keyboard pop up
Left Image - ContentDialog after keyboard pop up
Right Image - Page after keyboard pop up
Here is the related code:
<StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<TextBox Name="email" Header="Email address"/>
<PasswordBox Name="password" Header="Password"/>
<CheckBox Name="showPassword" Content="Show password"/>
<!-- Content body -->
<TextBlock Name="body" Style="{StaticResource MessageDialogContentStyle}" TextWrapping="Wrap">
<TextBlock.Text>
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</TextBlock.Text>
</TextBlock>
</StackPanel>
Why isn't the TextBox inside ContentDialog scrolled above the keyboard as it is like the in the Page?
Once I've faced similar problem with the TextBox and have found answer here. Basically, once the keyboard is shown, the focused element is not moved up, you can correct this behaviour by making additional transform:
// add this code somewhere to your constructor of your page or content dialog - where the problematic TextBox is located
Windows.UI.ViewManagement.InputPane.GetForCurrentView().Showing += (s, args) =>
{
const double extraHeightBuffer = 20.0;
UIElement focused = FocusManager.GetFocusedElement() as UIElement;
if (null != focused)
{
GeneralTransform gt = focused.TransformToVisual(this);
Point focusedPoint = gt.TransformPoint(new Point(0, focused.RenderSize.Height - 1));
double bottomOfFocused = focusedPoint.Y + extraHeightBuffer;
if (bottomOfFocused > args.OccludedRect.Top)
{
var trans = new TranslateTransform();
trans.Y = -(bottomOfFocused - args.OccludedRect.Top);
this.RenderTransform = trans;
}
args.EnsuredFocusedElementInView = true;
}
};
Windows.UI.ViewManagement.InputPane.GetForCurrentView().Hiding += (s, args) =>
{
var trans = new TranslateTransform();
trans.Y = 0;
this.RenderTransform = trans;
args.EnsuredFocusedElementInView = false;
};
I'm working on a card game based on the NetBeans platform and I'm struggling to get my head around dynamic images. Why dynamic? Well I want the cards to adjust at run time to changes to the page (i.e. name, text, cost, etc).
My first hack at it was creating a component (JPanel) with labels pre-placed where I loaded the text/image based on the card values. That seems to work fine but then it became troublesome when I thought about some pages having a different look in later editions (meaning not everything would be on the same place).
So I'm trying to get an idea about ways to do this based on some kind of template.
Any idea?
There's a follow-up question at: JList of cards?
Finally I got some time to get back to this and was able to figure out a way using Java 2D tutorial.
The pictures are not near what I will use in my application but serves as proof of concept.
package javaapplication3;
import java.awt.*; import java.awt.font.FontRenderContext; import
java.awt.font.LineBreakMeasurer; import java.awt.font.TextAttribute;
import java.awt.font.TextLayout; import java.awt.image.BufferedImage;
import java.io.File; import java.io.IOException; import
java.net.MalformedURLException; import java.net.URL; import
java.text.AttributedCharacterIterator; import
java.text.AttributedString; import java.util.ArrayList; import
java.util.HashMap; import java.util.logging.Level; import
java.util.logging.Logger; import javax.imageio.ImageIO;
/** * * #author Javier A. Ortiz Bultrón
*/ public class DefaultImageManager {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
try {
// TODO code application logic here
DefaultImageManager manager = new DefaultImageManager();
URL url = DefaultImageManager.class.getResource("weather-rain.png");
manager.getLayers().add(ImageIO.read(url));
url = DefaultImageManager.class.getResource("weather-sun.png");
manager.getLayers().add(ImageIO.read(url));
manager.addText(new Font("Arial", Font.PLAIN, 10), "Many people believe that Vincent van Gogh painted his best works "
+ "during the two-year period he spent in Provence. Here is where he "
+ "painted The Starry Night--which some consider to be his greatest "
+ "work of all. However, as his artistic brilliance reached new "
+ "heights in Provence, his physical and mental health plummeted. ",
200, 150, new Point(0, 0));
manager.generate();
} catch (MalformedURLException ex) {
Logger.getLogger(DefaultImageManager.class.getName()).log(Level.SEVERE,
null, ex);
} catch (IOException ex) {
Logger.getLogger(DefaultImageManager.class.getName()).log(Level.SEVERE,
null, ex);
}
}
/**
* Layers used to create the final image
*/
private ArrayList layers = new ArrayList();
private ArrayList textLayers = new ArrayList();
/**
* #return the layers
*/
public ArrayList<BufferedImage> getLayers() {
return layers;
}
private Dimension getMaxSize() {
int width = 0, height = 0;
for (BufferedImage img : getLayers()) {
if (img.getWidth() > width) {
width = img.getWidth();
}
if (img.getHeight() > height) {
height = img.getHeight();
}
}
return new Dimension(width, height);
}
public void addText(Font font, String text, int height, int width, Point location) {
BufferedImage textImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
HashMap<TextAttribute, Object> map =
new HashMap<TextAttribute, Object>();
map.put(TextAttribute.FAMILY, font.getFamily());
map.put(TextAttribute.SIZE, font.getSize());
map.put(TextAttribute.FOREGROUND, Color.BLACK);
AttributedString aString = new AttributedString(text, map);
AttributedCharacterIterator paragraph = aString.getIterator();
// index of the first character in the paragraph.
int paragraphStart = paragraph.getBeginIndex();
// index of the first character after the end of the paragraph.
int paragraphEnd = paragraph.getEndIndex();
Graphics2D graphics = textImage.createGraphics();
FontRenderContext frc = graphics.getFontRenderContext();
// The LineBreakMeasurer used to line-break the paragraph.
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
// Set break width to width of Component.
float breakWidth = width;
float drawPosY = 0;
// Set position to the index of the first character in the paragraph.
lineMeasurer.setPosition(paragraphStart);
// Get lines until the entire paragraph has been displayed.
while (lineMeasurer.getPosition() < paragraphEnd) {
// Retrieve next layout. A cleverer program would also cache
// these layouts until the component is re-sized.
TextLayout layout = lineMeasurer.nextLayout(breakWidth);
// Compute pen x position. If the paragraph is right-to-left we
// will align the TextLayouts to the right edge of the panel.
// Note: this won't occur for the English text in this sample.
// Note: drawPosX is always where the LEFT of the text is placed.
float drawPosX = layout.isLeftToRight()
? 0 : breakWidth - layout.getAdvance();
// Move y-coordinate by the ascent of the layout.
drawPosY += layout.getAscent();
// Draw the TextLayout at (drawPosX, drawPosY).
layout.draw(graphics, drawPosX, drawPosY);
// Move y-coordinate in preparation for next layout.
drawPosY += layout.getDescent() + layout.getLeading();
}
getTextLayers().add(textImage);
}
public void generate() throws IOException {
Dimension size = getMaxSize();
BufferedImage finalImage = new BufferedImage(size.width, size.height,
BufferedImage.TYPE_INT_ARGB);
for (BufferedImage img : getLayers()) {
finalImage.createGraphics().drawImage(img,
0, 0, size.width, size.height,
0, 0, img.getWidth(null),
img.getHeight(null),
null);
}
for(BufferedImage text: getTextLayers()){
finalImage.createGraphics().drawImage(text,
0, 0, text.getWidth(), text.getHeight(),
0, 0, text.getWidth(null),
text.getHeight(null),
null);
}
File outputfile = new File("saved.png");
ImageIO.write(finalImage, "png", outputfile);
}
/**
* #return the textLayers
*/
public ArrayList<BufferedImage> getTextLayers() {
return textLayers;
}
/**
* #param textLayers the textLayers to set
*/
public void setTextLayers(ArrayList<BufferedImage> textLayers) {
this.textLayers = textLayers;
} }
It still needs some refining specially on the placement of the text but it works. I guess I can implement a xml format to store all this information so is easily configurable. In the example below suns are drawn on top of rain, and the text is on top of all that. For my application each layer will build together the page I want.
Here are the images I used:
And the final result:
I have a Spark TextArea:
<s:TextArea id="editor">
<s:textFlow>
<s:TextFlow id="_tf" >
<s:p>Lorem ipsum etc.</s:p>
<s:p>
<s:img source="http://example.com/example.jpg" />
</s:p>
<s:p>Aliquam tincidunt tempor etc.</s:p>
</s:TextFlow>
</s:textFlow>
</s:TextArea>
And a button to add an image:
<s:Button id="imageBtn"
label="insert image"
width="89"
click="imageBtn_clickHandler(event);" />
The following script works:
import flashx.textLayout.elements.*;
import mx.events.FlexEvent;
protected function imageBtn_clickHandler(evt:MouseEvent):void {
var img:InlineGraphicElement = new InlineGraphicElement();
img.source = "http://example.com/example.jpg";
var p:ParagraphElement = new ParagraphElement();
p.addChild(img);
_tf.addChild(p);
_tf.flowComposer.updateAllControllers();
editor.setFocus();
}
but only appends to the end of the TextFlow. How can I insert the InlineGraphicElement at the active caret in the TextArea? My first thought is something like:
import flashx.textLayout.elements.*;
import mx.events.FlexEvent;
protected function imageBtn_clickHandler(evt:MouseEvent):void {
var img:InlineGraphicElement = new InlineGraphicElement();
img.source = "http://example.com/example.jpg";
var p:ParagraphElement;
//pseudocode
p = _tf.getCaret.AncestorThatIsParagraphElement;
// end pseudocode
p.addChild(img);
_tf.flowComposer.updateAllControllers();
editor.setFocus();
}
But this still would only append at the end of the current paragraph, assuming I could even find the current paragraph as an object to .addChild() with. So how can I insert the InlineGraphicElement in the middle of text (or even replacing text) in a child paragraph of a TextFlow object.
Thanks for your insight.
UPDATE: Getting closer.
I'm able to add to the beginning or end of a paragraph.
protected function imageBtn_clickHandler(evt:MouseEvent):void {
var img:InlineGraphicElement = new InlineGraphicElement();
img.source = "http://example.com/example.jpg";
var insertInto:ParagraphElement = new ParagraphElement();
insertInto = editor.textFlow.getChildAt(
editor.textFlow.findChildIndexAtPosition(
editor.selectionAnchorPosition
)).getParagraph();
insertInto.addChildAt(0, img); // inserts at beginning of paragraph
insertInto.addChildAt(1, img); // inserts at end of paragraph
_tf.flowComposer.updateAllControllers();
editor.setFocus();
}
But still no joy in inserting into the middle. Also, the above code wouldn't replace highlighted text, but that's not the real concern here.
SOLUTION:
Based on Eugene's link the key to this is EditManager.
The following code represents the working updated function:
protected function imageBtn_clickHandler(evt:MouseEvent):void {
var em:EditManager = editor.textFlow.interactionManager as EditManager;
em.selectRange(editor.selectionAnchorPosition, editor.selectionActivePosition);
em.insertInlineGraphic("http://example.com/example.jpg","auto","auto");
_tf.flowComposer.updateAllControllers();
editor.setFocus();
}
Use this code you get solution.
here image getting from your system folder
protected function addImg_clickHandler(event:MouseEvent):void {
_file_ref = new FileReference();
_file_ref.addEventListener( Event.SELECT, handleFileSelect,false,0,true );
var filter:FileFilter = new FileFilter("Images", "*.jpg;*.gif;*.png");
_file_ref.browse([filter]);
}
private function handleFileSelect(e:Event):void {
_file_ref.removeEventListener( Event.SELECT, handleFileSelect );
_file_ref.addEventListener(Event.COMPLETE, handleFileOpen,false,0,true );
_file_ref.load();
}
private function handleFileOpen(e:Event):void {
_file_ref.removeEventListener(Event.COMPLETE, handleFileOpen );
var data:ByteArray = _file_ref.data as ByteArray;
_img_loader= new Loader();
_img_loader.loadBytes(data);
_img_loader.contentLoaderInfo.addEventListener(Event.COMPLETE,imageLoadComplete,false,0,true);
}
protected function imageLoadComplete(e:Event):void{
_img_loader.contentLoaderInfo.removeEventListener(Event.COMPLETE,imageLoadComplete);
var bmd:BitmapData=Bitmap(_img_loader.content).bitmapData;
var bm:Bitmap=new Bitmap(bmd);
var em:EditManager = editorText.textFlow.interactionManager as EditManager;
em.selectRange(editorText.selectionAnchorPosition, editorText.selectionActivePosition);
em.insertInlineGraphic(bm,bm.width,bm.height);
editorText.textFlow.flowComposer.updateAllControllers();
editorText.setFocus();
}
protected function addTable_clickHandler(event:MouseEvent):void
{
var tblement:TableElement = new TableElement();
var em:EditManager = editorText.textFlow.interactionManager as EditManager;
em.selectRange(editorText.selectionAnchorPosition, editorText.selectionActivePosition);
em.insertTableElement(tblement);
editorText.textFlow.flowComposer.updateAllControllers();
editorText.setFocus();
}