Introduction to the problem
It seams impossible to change a Veriable in a event (like so for example button.setOnAction(e -> { x = x+2});) and I can see why, but what if that's exactly what I need? It's actually not the first time that I needed to do that, but last time I got around it by saving the changed property directly to a file.
This time I used a really tricky trick to create a 'fake variable' by using the text in an invisibe TextField as my veriable. It works, that's not the problem, but I'd like to have a more 'elegant' salution :D
Friuts of my research
The only two salutions I could find were to either declare a Variable as a class member (which I don't want to do seeing that I already have six of them and don't want to tripple that amount) or to use seperate classes. (I found that here: How to change a variable when a button has been clicked in JavaFx )
The programm
Here is the code of my popup window (I'll spare you of the rest of my stupid programm ^^ ), I'll explain what it does after the codebox. (Note that props is a properties object and one of the mentioned class members as well as propPath (meaning I declared it at the very top of the programm so that it can be accessed from every function)):
private void createNewBuff() throws Exception {
Stage popupStage = new Stage(StageStyle.UTILITY);
popupStage.initModality(Modality.APPLICATION_MODAL);
popupStage.setTitle("Creating a new Buff");
popupStage.setMinWidth(400);
popupStage.setMinHeight(300);
GridPane layoutGP = new GridPane();
layoutGP.getColumnConstraints().addAll(
new ColumnConstraints(200), //TextField column col 0
new ColumnConstraints(10), //Gab column col 1
new ColumnConstraints(60), //tf manips column col 2
new ColumnConstraints(30), //Gab column col 3
new ColumnConstraints(80)); //Main buttons col col 4
TextField tfName = new TextField("Unknown Buff");
tfName.setPromptText("Name of the Buff, e.g. \"Unknown Buff\"");
Label lblName = new Label("Name");
layoutGP.add(tfName, 0, 0);
layoutGP.add(lblName, 2, 0);
TextField varTF = new TextField();
TextField tfEffect = new TextField();
tfEffect.setPromptText("Effect of the Buff, e.g. \"+10 LP\"");
Button btnAdd = new Button("Add");
btnAdd.setOnAction(e -> {
String input = tfEffect.getText();
String[] comps = input.split(" "); //components
if (Array.getLength(comps) == 2) {
if (input.contains("+")) {
varTF.setText(varTF.getText() + input + ";");
layoutGP.add(new Label(input), 0, 1 + Array.getLength(varTF.getText().split(";")));
} else if (input.contains("-")) {
varTF.setText(varTF.getText() + input + ";");
layoutGP.add(new Label(input), 0, 1 + Array.getLength(varTF.getText().split(";")));
}
}
});
layoutGP.add(tfEffect, 0, 1);
layoutGP.add(btnAdd, 2, 1);
Button btnDone = new Button("Done");
btnDone.setOnAction(e -> {
int buffNumber = 1;
while (props.containsKey("Buff-" + buffNumber + "-name"))
buffNumber++;
props.setProperty("Buff-" + buffNumber + "-name", tfName.getText());
props.setProperty("Buff-" + buffNumber + "-effect", varTF.getText());
try {
FileOutputStream streamOut = new FileOutputStream(propPath);
props.store(streamOut, null);
streamOut.close();
popupStage.close();
}catch (IOException someE){/*something meaningful*/}
});
btnDone.setMaxWidth(Double.MAX_VALUE);
Button btnCancel = new Button("Cancel");
btnCancel.setOnAction(e -> popupStage.close());
btnCancel.setMaxWidth(Double.MAX_VALUE);
layoutGP.add(btnDone, 4, 0);
layoutGP.add(btnCancel, 4, 1);
layoutGP.setAlignment(Pos.TOP_CENTER);
Scene popupScene = new Scene(layoutGP);
popupStage.setScene(popupScene);
popupStage.showAndWait();
}
I wanted to post a picture there of how it looks once you start it and another of how it looks after you used it, but my reputation is still to low ^^
Therefor I'll provide the links to the pictures: Befor something was done http://i.imgur.com/Wx3nIEX.png After it's used http://i.imgur.com/5QNmWP6.png
What the programm does
It's pretty strait forward actually: my main programm is a character sheet and with that popup you can add a buff to it (later I'll make it so that the effects of the buff get calculated into the values of the sheed, but one step at a time ^^ ). You can set the name of the buff you want to create ("Unknown Buff" is the standart in case you forgett to set one) and then you can simply add defferent effects by writing tem into the TextField and then pressing the 'Add' button. They will list themself top to bottom in the free space. They will also, behind the scenes, save themself as one large string into a hidden TextField to make figuring out in which line each individual Effect has to be displayed and to save it to the properties file afterwards. (and that salution with the undisplayed textfield is exactly the trick I want to remove and turn into a more 'elegant' salution)
Detailed explanaition of the code
At first the Stage is created. Then the layout is made in form of a gridpane und the column sizes are set (every second column is for spacing reasons). After that I'm creating the line for the name (the label and the textfield) and also my tricky textfield that's used as a String variable. Now I'm creating the effect textfield and add button, as well as the event for the button: at first it turns the input from the textfield into a easier to read name and splits it into it's components (at first I used tfEffect.getText() and tfEffect.getText().split(" ") inside the code below, but that was really messy) now I have if statements to verify it and make sure that the Effect is in the right format (that format beeing the amount a certain stat is raised or lowerd, consisting of either a plus or a minus symbol at the beginning and a number after that; followed by a space and then the index of the stat (I haven't been able to implement verifying if the typed thing actually is a valid stat, maybe that'll be content of a future question ;D )). After verifying the programm it adds the effect below all the others. If you're done and happy with you're buff you can click the Done button, it will first look up how many Buffs already exist, then assign the new on a number and save it all to the properties file. The cancel button with of course just cancel it and at the bottom I'm setting the scene and calling the stage.
What I'm hoping for with this question
Now I think you have a pretty clear idea of what I need to do and why I couldn't find a better salution then using a hidden TextField as a variable and I hope it's because I'm a beginner and not because there actually is no better way of doing it ^^
Also if you actually read all of this you may found one or two things I can improve besides that, but that would be the special bonus ;D
Related
I need to show three things in a row in a sort of table. The first column should have a fixed width of say 15% of the screen. The third one should be right aligned and take its preferred width. The second one should take all the remaining space (I'll need to add some spacing, but that's another story).
This happens in start:
final Container list = new Container(BoxLayout.y());
list.setScrollableY(true);
final String[][] lines = {
{"19", "Some text", "123,00"},
{"20", "Some very very very very looong text", "1,00"},
};
for(final String[] line : lines) list.add(createContainer(line));
form.add(list);
The container is rather trivial:
private Container createContainer(String[] line) {
final TableLayout tableLayout = new TableLayout(1, 3);
tableLayout.setGrowHorizontally(true);
final Container result = new Container(tableLayout);
{
final Label l = new Label(line[0]);
l.getAllStyles().setFgColor(0x0000FF);
result.add(tableLayout.createConstraint().widthPercentage(15), l);
}
{
final Label l = new Label(emptyToSpace(line[1]));
l.getAllStyles().setFont(Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
result.add(tableLayout.createConstraint().widthPercentage(-2), l);
}
{
final Label l = new Label(line[2]);
l.getAllStyles().setFont(Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE));
l.getAllStyles().setFgColor(0x00FF00);
result.add(tableLayout.createConstraint().widthPercentage(-1).horizontalAlign(Component.RIGHT), l);
}
return result;
}
According to the javadoc, -1 means preferred size and -2 means "remaining space". It sort of works, but there seem to be a miscalculation.
The problem happens in the simulator, no matter what device I choose. I may be doing it all wrong, as I'm new to codenameone layouts.
The -2 flag is mostly optimized for the last column so this looks like a bug but might be hard to workaround. I don't see a need to use table layout here since you don't use one table which would provide alignment between the rows.
A simpler approach would be border layout e.g.:
Container c = BorderLayout.centerEastWest(new Label(emptyToSpace(line[1])),
rightText, leftText);
If you want the left column to align just use Component.setSameWidth() on the entire column.
Im using a JTable , loading on it a different data depending on the button pressed.
The problem is : when one of the data is loaded, if i try to load the other one, and pass ther mouse over the header or a cell, it updates the header/cell with the data from the first input, if there is data on the header/cell selected.
Any ideas on how to solve it? That's the code im using.
private static void setCompromissosTable(Object[][] data, Object[] header){
try{
compromissosTable.removeAll();
} catch(Exception e){
e.printStackTrace();
}
compromissosTable = new JTable(data, header);
compromissosTable.setRowSelectionAllowed(true);
// Make the entire row selectable, but not editable
int columnMax = compromissosTable.getColumnCount();
for(int column = 0; column < columnMax; column++){
Class<?> col_class = compromissosTable.getColumnClass(column);
compromissosTable.setDefaultEditor(col_class, null);
}
scrollPane = new JScrollPane(compromissosTable);
pane.add(scrollPane);
scrollPane.setBounds(btnAddCompromisso.getX(),
btnAddCompromisso.getHeight() + btnAddCompromisso.getY() + 5
, frame1.getWidth() - 20
, frame1.getHeight() - 20);
compromissosTable.revalidate();
compromissosTable.repaint();
compromissosTable.addMouseListener(new MouseAdapter() {}
//Change mouse behavior.
);
}
This is suspicious...
compromissosTable = new JTable(data, header);
//...
scrollPane = new JScrollPane(compromissosTable);
pane.add(scrollPane);
Basically, assuming that each time you want to switch data sets, you are calling this method, you are creating a new JTable and JScrollPane each time and then are adding it onto the UI...
What about the previous JTable?
Next is this...
scrollPane.setBounds(btnAddCompromisso.getX(),
btnAddCompromisso.getHeight() + btnAddCompromisso.getY() + 5
, frame1.getWidth() - 20
, frame1.getHeight() - 20);
This looks like you're using a null layout. Basically what it "looks" like is happening, is you're just stacking the JScrollPanes ontop of each other, which would explain, in part, the graphics glitches, as the components are actually been added at the same z-deepthness (essentially) and are competing with each other then they are updated.
Two simple answers...
Don't use null layouts. Sure they "seem" like a good idea, but they have a tendency to turn around and bite you in strange and wonderful ways which are hard to diagnose and fix. Use the layout management API which Swing was designed around
Update the JTables model instead of creating a new JTable/JScrollPane each time
See How to use tables and Laying Out Components Within a Container
i have done a small test on LibGdx, on Multi-line Label, it seems that i cant get the wrapped line's height. Following is the code. Theoretically, height for aLebel should be > bLabel. But the result appear the same.
code:
aLabel.setText("this is a super long long long text that need wrapping."); // line wrapped into 3 lines
aLabel.setWrap(true);
aLabel.setWidth(470);
doLog("aLabel.getHeight(): " + aLabel.getHeight());
bLabel.setText("this is short."); // unwrapped line
bLabel.setWrap(true);
bLabel.setWidth(470);
doLog("bLabel.getHeight(): " + bLabel.getHeight());
result:
aLabel.getHeight(): 45.0
bLabel.getHeight(): 45.0
Do anyone have any idea how to get the actual multi-line height in LibGdx? Thanks in advance.
I had this issue for years and accidentally solved it by setting the width and packing the label twice. Note that multiline labels were never intended to figure out their own width, so you have to set them externally, preferably from it's parent.
public Label createLabel() {
// Create label and set wrap
Label label = new Label("Some long string here...", skin);
label.setWrap(true);
// Pack label
label.pack(); // This might not be necessary, unless you're changing other attributes such as font scale.
// Manual sizing
label.setWidth(textWidth); // Set the width directly
label.pack(); // Label calculates it's height here, but resets width to 0 (bug?)
label.setWidth(textWidth); // Set width again
return label;
}
LibGDX version used: 1.6.4
Pack sizes the widget to its pref size, nothing more. Pref width of a label with wrapping is 0.
Label label = new Label(...);
label.setWrap(true);
label.setWidth(123);
label.setHeight(label.getPrefHeight());
I had the same issue and it seems there doesn't exist a method in Label class to solve this. Also, I agree with you, the getHeight() method should return the real height of the Actor, so I don't know if that's a bug or there is a reasoning behind that behaviour.
Anyways, how I solved the issue is by using BitmapFont's getWrappedBounds method. It's not short, but for your example it would be the following:
doLog("aLabel.getHeight(): " + aLabel.getStyle().font.getWrappedBounds(aLabel.getText(), aLabel.getWidth()).height);
This could be done by adding a restriction to the cell that contains the Label in the Table:
Label label = new Label("Example", new Label.LabelStyle(font, Color.WHITE));
label.setWrap(true);
Table table = new Table();
table.add(label).width(WITH);
For more information about how to use Table go to: https://github.com/libgdx/libgdx/wiki/Table
I recently decided to start using GridLayout because FlowLayout seems somewhat amateur. However, I need help. The parameters when creating the GridLayout are (rows,columns,row space,column space). I have a variable for the row amount and 4 for the column amount, but when I try to add a JButton after everything else, there are 5 columns.
Here is my code:
byte i = 0;
while(i < main.componentNum)
{
comp[i] = new JLabel("component #" + (i+1));
box[i] = new JComboBox();
field[i] = new JTextField(5);
edit[i] = new JButton("edit");
comp[i].setBackground(Color.WHITE);
box[i].setBackground(Color.WHITE);
field[i].setBackground(Color.WHITE);
edit[i].setBackground(Color.WHITE);
add(comp[i]);
add(box[i]);
add(field[i]);
add(edit[i]);
i++;
}
When I run the above code, I get four columns and it works fine. But when I add a button to the end, I get five. Can anyone tell me how to give one button an entire row?
From the Java Docs
One, but not both, of rows and cols can be zero, which means that any
number of objects can be placed in a row or in a column.
Now, without your actual code the sets up the GridLayout, it's difficult to say, but, if your after maintaining only 4 columns, I would create a GridLayout as follows, new GridLayout(0, 4)
If you want something more flexible, look into GridBagLayout
I would like to customize JTableHeader so it would offer serval actions (for example 2 buttons which one of them would sort column and second show properties of this column etc). Unfortunately it is not possible to set CellEditor for JTableHeader so i'm stuck with using mouse adapter. But maybe it is possible to dispatch event from this particular JTableHeader component so it will show up a popup menu which will contains all options i desire and it would dispatch event if option other than sorting would be chosen. This way standard JTable sorting operation will be available, along with my operations and it will maintain a decent visual apperance. So my question is - Is it possible and how it should be done.
In response to trashgod comment - i understand that you mean to treat defaultheader as an ordinary component and just use "add" function to add Components. It doesnt work well with JTableHeader. After reading trashgod example i wrote this:
private class mouseList extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
TableColumnModel thisColumnModel = thisTable.getColumnModel();
int xCor = e.getX();
//int Cols = thisColumnModel.getColumnCount();
int thisColNum = thisColumnModel.getColumnIndexAtX(xCor);
int prevWidth=0;
for(int i = 0 ;i<thisColNum;i++)
{
prevWidth+=thisColumnModel.getColumn(i).getWidth();
}
int width = xCor-prevWidth;
/////////////////////////////////////////////////////////////////////////////////////
customHeader thisHeader = (customHeader)((JTableHeader)e.getSource()).getDefaultRenderer();
System.out.println(thisHeader.mainB.getText() + " text of thisHeader");
//////////////////////////////////////////////////
test thisTest = new test(null,false,thisHeader);
thisTest.setVisible(true);
///////////////////////////////////////////////////////////////
//System.out.println(width + " width of the header");
Object thisComp = thisHeader.getComponentAt(width, e.getY());
System.out.println(thisComp + "\n" + width + " + " + e.getY() +"\n" + thisHeader.getMainButton().getText());
((JTableHeader)e.getSource()).repaint();
if(thisComp instanceof JButton)
{
//System.out.println("sdfdsf");
String name = ((JButton)thisComp).getName();
if(name.equals("mainB"))
{
System.out.println("its working on main");
((JButton)thisComp).doClick(1000);
}else{
System.out.println("its working on menu");
((JButton)thisComp).doClick(1000);
}
}
((JTableHeader)e.getSource()).repaint();
}
}
MouseListener is applied to JTableHeader. HeaderRender is an extension of JPanel that contains 2 JButtons. Strange thing happens in line
Object thisComp = thisHeader.getComponentAt(width, e.getY());
When i left lines
test thisTest = new test(null,false,thisHeader);
thisTest.setVisible(true);
(This dialog shows selected component)
uncommented, function "getComponentAt" seems to work allmost fine (allmost because it never goes for else condition even when mouse is targeting second button, and it does not repaint clicked buttons[Strangely its repainting buttons in test dialog window]),otherwise it allways returns null object.
I dont know if it is important but i set Header renderer globally by invoking "setDefaultRenderer" on JTableHeader.
Im pretty much running out of ideas so i would appreciate any help.
This example shows the basic infrastructure, while this answer offers several important caveats regarding usability. This example shows how to change the RowFilter dynamically, but changing the RowSorter is similar. Both examples use JToggleButton to manage two states, but a JComboBox could be used to select from among more alternatives.