I am implementing a widget board for controlling numerous widgets (extending JComponents) which must implement several featrues: resizable, draggable, closeable etc...
Null (or absolute) layout came to my mind because all other native Layout managers do not support the desired features (dragging a component and placing it on a specific position). But as we all know, using absolute manager is not even not recommended, but also forbidden in Netbeans RCP. So I am trying to implement my own layout manager which would be as close to absolute layout as possible and provide some additional features.
Here is the code of my layout prototype
public class WidgetLayout implements LayoutManager, java.io.Serializable {
private boolean usePreferredSize;
/**
* Convenience constructor
*/
public WidgetLayout() {
this(true);
}
/**
* Create a DragLayout and indicate how the component size is determined.
*
* #param usePreferred size see setPreferredSize() for values.
*/
public WidgetLayout(boolean usePreferredSize) {
setUsePreferredSize(usePreferredSize);
}
/**
* Set the use preferred size property
*
* #param usePreferredSize when true, use the preferred size of the
* component in layout calculations. When false, use the size of the
* component, unless the size is 0, in which case use the preferred size as
* a default.
*/
public void setUsePreferredSize(boolean usePreferredSize) {
this.usePreferredSize = usePreferredSize;
}
/**
* Get the use Preferred Size property
*
* #return the use preferred size property
*/
public boolean isUsePreferredSize() {
return usePreferredSize;
}
/**
* Adds the specified component with the specified name to the layout.
*
* #param name the name of the component
* #param comp the component to be added
*/
#Override
public void addLayoutComponent(String name, Component comp) {
}
/**
* Removes the specified component from the layout.
*
* #param comp the component to be removed
*/
#Override
public void removeLayoutComponent(Component component) {
}
/**
* Determine the preferred size on the Container
*
* #param parent the container in which to do the layout
* #return the preferred dimensions to lay out the subcomponents of the
* specified container
*/
#Override
public Dimension preferredLayoutSize(Container parent) {
synchronized (parent.getTreeLock()) {
return getLayoutSize(parent);
}
}
/**
* Determine the minimum size on the Container
*
* #param target the container in which to do the layout
* #return the minimum dimensions needed to lay out the subcomponents of the
* specified container
*/
#Override
public Dimension minimumLayoutSize(Container parent) {
synchronized (parent.getTreeLock()) {
return preferredLayoutSize(parent);
}
}
/**
* Lays out the specified container using this layout.
*
* #param target the container in which to do the layout
*/
#Override
public void layoutContainer(Container parent) {
}
/*
* The calculation for minimum/preferred size is the same.
*
* #param parent the container in which to do the layout
*/
private Dimension getLayoutSize(Container parent) {
Insets parentInsets = parent.getInsets();
int x = parentInsets.left;
int y = parentInsets.top;
int width = 0;
int height = 0;
// Get extreme values of the components on the container.
// The x/y values represent the starting point relative to the
// top/left of the container. The width/height values represent
// the bottom/right value within the container.
for (Component component : parent.getComponents()) {
if (component.isVisible()) {
Point p = component.getLocation();
Dimension d = component.getSize();
x = Math.min(x, p.x);
y = Math.min(y, p.y);
width = Math.max(width, p.x + d.width);
height = Math.max(height, p.y + d.height);
}
}
Dimension d = new Dimension(width, height);
return d;
}
/**
* Returns the string representation of this column layout's values.
*
* #return a string representation of this layout
*/
#Override
public String toString() {
return "["
+ getClass().getName()
+ "]";
}
}
It sort of works, but I have a problem with other components in my RCP application that surrounds this one.
There is a JPanel with this layout inside a TopComponent. return parent.getParent().getParent().getSize(); ensures that this panel has the same size as the topComponent istself (it is the only component there, it works as a canvas or a board). But when I want to increase size of the Explorer, for example, I cant. I can only make the board with my layout bigger, but not smaller. I think I know the reason (the panel with my layout has set preferred size as high as possible) but I am not sure how to correct this. Any tips?
EDIT: what I need is for this layout to has the same size as the TopComponent but the TopComponent should have the ability to increase and decrease its size...
EDIT2: Problem solved. I based some of the functions on DragLayout and now it behaves as an AbsoluteLayout but does not move other components when its size changes (unless the whole component above it changes size) - see the code above.
Also, the trick is to place the component implementing this layout inside some layout which does not force newly created components on the middle of the container, so I put this one into a GridLayout(1, 1), which fill the whole component by default :-)
Solved - see question EDIT. I will leave the question here, perhaps someone could make use of it when implementing absolute layout-like layout manager...
Related
I need to make the vaadin 14 textfield which can accept only numbers.The criterias for the textfield are as follows
1.The textfield must only accept numbers nothing else as I want to use that textfield as a mobile number field.
2.validate in such a way that if users tries to enter the alphabets nothing must be reflected in the textfield.Only numbers must be allowed to be entered in the textfield.
3.Any warning or errors must not be shown in the UI as we are specially making textfield for the mobile number.
Things I have tried is binders but that allows to enter the alphabets later on after the focus lost event they validate and provide the error message I dont want that behaviour.
Also tried vaadin number field but that allows character 'e'
Just simple and straight forward I am looking for the textfield which takes input only numbers.If user tries to input alphabets nothing must be reflected in the textfield.
Server-side
There are number of options you can do, the first one is the server side validator already mentioned in the answer by Alim Ă–zdemir:
binder.forField(textFieldForNumber)
.withValidator(new RegexpValidator("Only 1-9 allowed","\\d*"))
.bind(YourEntity::getNo, YourEntity::setNo);
Client-side
There is also possibility do the same checking and input filtering on client side using textField.setPattern(..) method, e.g.:
textFieldForNumber.setPattern("\\d*");
Furthermore it is possible to prevent input not matching the pattern alltogether by
textFieldForNumber.setPreventInvalidInput(true);
Alternate widget: NumberField
Third alternative is to use the NumberField component.
You could do a validation on the field with the binder like
binder.forField(textFieldForNumber)
.withValidator(new RegexpValidator("Only 1-9 allowed","\\d*"))
.bind(YourEntity::getNo, YourEntity::setNo);
I found the solution for my answer I extracted the source code for the Integer text field In Vaadin new beta version
The code goes as follows
#Tag("vaadin-integer-field")
#HtmlImport("frontend://bower_components/vaadin-text-field/src/vaadin-integer-field.html")
#JsModule("#vaadin/vaadin-text-field/src/vaadin-integer-field.js")
public class BigIntegerField extends AbstractNumberField<BigIntegerField, BigInteger> {
private static final SerializableFunction<String, BigInteger> PARSER = valueFormClient -> {
if (valueFormClient == null || valueFormClient.isEmpty()) {
return null;
}
try {
return new BigInteger(valueFormClient);
} catch (NumberFormatException e) {
return null;
}
};
private static final SerializableFunction<BigInteger, String> FORMATTER = valueFromModel -> valueFromModel == null
? ""
: valueFromModel.toString();
/**
* Constructs an empty {#code IntegerField}.
*/
public BigIntegerField() {
super(PARSER, FORMATTER, Double.MIN_VALUE, Double.MAX_VALUE);
// super(PARSER, FORMATTER, new BigInteger(String.valueOf(Integer.MIN_VALUE)), new BigInteger(String.valueOf(Integer.MAX_VALUE)));
}
/**
* Constructs an empty {#code IntegerField} with the given label.
*
* #param label
* the text to set as the label
*/
public BigIntegerField(String label) {
this();
setLabel(label);
}
/**
* Constructs an empty {#code IntegerField} with the given label and
* placeholder text.
*
* #param label
* the text to set as the label
* #param placeholder
* the placeholder text to set
*/
public BigIntegerField(String label, String placeholder) {
this(label);
setPlaceholder(placeholder);
}
/**
* Constructs an empty {#code IntegerField} with a value change listener.
*
* #param listener
* the value change listener
*
* #see #addValueChangeListener(ValueChangeListener)
*/
public BigIntegerField(
ValueChangeListener<? super ComponentValueChangeEvent<BigIntegerField, BigInteger>> listener) {
this();
addValueChangeListener(listener);
}
/**
* Constructs an empty {#code IntegerField} with a value change listener and
* a label.
*
* #param label
* the text to set as the label
* #param listener
* the value change listener
*
* #see #setLabel(String)
* #see #addValueChangeListener(ValueChangeListener)
*/
public BigIntegerField(String label,
ValueChangeListener<? super ComponentValueChangeEvent<BigIntegerField, BigInteger>> listener) {
this(label);
addValueChangeListener(listener);
}
/**
* Constructs a {#code IntegerField} with a value change listener, a label
* and an initial value.
*
* #param label
* the text to set as the label
* #param initialValue
* the initial value
* #param listener
* the value change listener
*
* #see #setLabel(String)
* #see #setValue(Object)
* #see #addValueChangeListener(ValueChangeListener)
*/
public BigIntegerField(String label, BigInteger initialValue,
ValueChangeListener<? super ComponentValueChangeEvent<BigIntegerField, BigInteger>> listener) {
this(label);
setValue(initialValue);
addValueChangeListener(listener);
}
/**
* Sets the minimum value of the field. Entering a value which is smaller
* than {#code min} invalidates the field.
*
* #param min
* the min value to set
*/
public void setMin(int min) {
super.setMin(min);
}
/**
* Gets the minimum allowed value of the field.
*
* #return the min property of the field
* #see #setMin(int)
*/
public int getMin() {
return (int) getMinDouble();
}
/**
* Sets the maximum value of the field. Entering a value which is greater
* than {#code max} invalidates the field.
*
* #param max
* the max value to set
*/
public void setMax(int max) {
super.setMax(max);
}
/**
* Gets the maximum allowed value of the field.
*
* #return the max property of the field
* #see #setMax(int)
*/
public int getMax() {
return (int) getMaxDouble();
}
/**
* Sets the allowed number intervals of the field. This specifies how much
* the value will be increased/decreased when clicking on the
* {#link #setHasControls(boolean) control buttons}. It is also used to
* invalidate the field, if the value doesn't align with the specified step
* and {#link #setMin(int) min} (if specified by user).
*
* #param step
* the new step to set
* #throws IllegalArgumentException
* if the argument is less or equal to zero.
*/
public void setStep(int step) {
if (step <= 0) {
throw new IllegalArgumentException("The step cannot be less or equal to zero.");
}
super.setStep(step);
}
/**
* Gets the allowed number intervals of the field.
*
* #return the step property of the field
* #see #setStep(int)
*/
public int getStep() {
return (int) getStepDouble();
}
}
This Actually solved my problem regarding the Mobile number inputs.
I have column headers that use a VerticalTextPainter.
If I set setCalculateByTextHeight and setCalculateByTextLength to true is resizes the columns to fit all of the text inside the cells correctly.
Sometimes the headers will have a lot of text in them so I would like them to have a maximum height.
If I stop usingsetCalculateByTextHeight and setCalculateByTextLength then the cells aren't resized at all so they just show ....
How could I go about doing this?
Update
#Override
protected void setNewMinLength(ILayerCell cell, int contentHeight) {
final ILayer layer = cell.getLayer();
final int cellLength = cell.getBounds().height;
if (contentHeight < MAXIMUM_HEIGHT && cellLength < contentHeight) {
layer.doCommand(new RowResizeCommand(layer, cell.getRowPosition(), contentHeight));
} else {
layer.doCommand(new RowResizeCommand(layer, cell.getRowPosition(), MAXIMUM_HEIGHT));
}
}
Override paintControl in NatTable
#Override
public void paintControl(final PaintEvent event) {
super.paintControl(event);
/**
* After first time rendering we stop column/row headers calculating their
* height/lengths. This allows the user to resize the column/row headers after
* the NatTable has been rendered.
*/
if (firstRender) {
columnHeaderPainter.setCalculateByTextHeight(false);
columnHeaderPainter.setCalculateByTextLength(false);
rowHeaderPainter.setCalculateByTextHeight(false);
rowHeaderPainter.setCalculateByTextLength(false);
firstRender = false;
}
}
There is no build in mechanism to specify a max height or width. Either you configure that the height should be calculated based on the content or set a fixed height.
I think you could achieve this by subclassing the VerticalTextPainter and overriding setNewMinLength() to only execute a RowResizeCommand if the contentHeight is bigger than the cellLength and the cellLength is not bigger than your maximum. And of course the RowResizeCommand should only resize to your specified maximum then.
Performing the check on any other place would probably result in an endless processing of cell height resizing.
I am working on a barchart for a school assignement. However I got stuck when trying to position the bar label at the left of the bar. I assume I have to use a method to get the width of the label text and then substract the value from the x position of the bar.
public class Bar {
// the numeric value of the bar
private int value;
// the rectangle representing the bar
private Rectangle view;
// The text label for this bar
private Text label;
// The value as a text object
private Text valueText;
/**
* Constructor for a bar object. The rectangle for the bar is
* also constructed here and its size is set to the given height.
* The value of the bar is initialised to 0.
* If there is a label for the bar, a Text object is created using the
* text of the label and the colour of the label set to black.
* The bar and the label are not visible until the display method
* is called.
*
* #param label The label for the bar.
* #param width The height of the bar.
*/
public Bar(String label, int height) {
view = new Rectangle();
view.changeSize (0,height);
this.label = new Text(label);
display();
}
public void display() {
label.makeVisible();
view.makeVisible();
}
public void setValue (int Newvalue){
value= Newvalue;
}
public void setPosition (int x, int y){
view.setPosition(x,y);
label.setPosition(x,y);
}
public int getTextWidth() {
return (int) ((Text)label).getWidth();
}
}
Edit:
I have attached a screenshot of of my BlueJ program so far and also the assignment description.
bar chart
assignment
I have created in the Public calss bar, a bar as a Rectangle named "view". The rectangle's (bar) width is initially set to zero, that is why on canvas appears as a line (see picture). I have created a label as Text, which must appear to the left of the bar( my rectangle) everytime I edit is width. The point is I don't know how to edit is width in the method Public void set value. When I call the method and I enter 100 as the parameter value, the value of the bar should change to 100 and the width of the view (rectangle) should change to 100 but it dosen't. And the label is in the wrong position. In the setPosition method I have to make the label appear to the left of the rectangle(bar). But how? And then when I call the display method I must see on the cavas the value representing the bar width. How? Many thanks.
Just so you know, I am an absolut beginner, only 3 weeks into my course.
So basically I want the value returned by getTextWidth() to substract it from the x coordinate of the bar (the text must appear at the left of the bar). Any help is much appreciated.
I have a pane (1440x800) and a node (400x300) which I need to move inside this pane. So for Pane I took StackPane and for Node I took VBox.
This is the controller which moves the node.
public class Controller extends AbstractController {
/**
* Mouse pressed X position.
*/
private double mousePressedX;
/**
* Mouse pressed Y position.
*/
private double mousePressedY;
/**
* Insets that were when user pressed mouse.
*/
private Insets mousePressedInsets;
/**
* Mouse pressed handler.
* #param event
*/
#FXML
public void handleTitlePaneMousePressed(final MouseEvent event) {
Node node = (Node) getView().getFxView();
mousePressedX = event.getScreenX();
mousePressedY = event.getScreenY();
mousePressedInsets = StackPane.getMargin(node);
if (mousePressedInsets == null) {
mousePressedInsets = new Insets(0, 0, 0, 0);
}
}
/**
* Moused dragged handler.
* #param event
*/
#FXML
public void handleTitlePaneMouseDragged(final MouseEvent event) {
Node node = (Node) getView().getFxView();
double deltaX = event.getScreenX() - mousePressedX;
double deltaY = event.getScreenY() - mousePressedY;
Insets newInsets =
new Insets(mousePressedInsets.getTop() + deltaY, 0, 0, mousePressedInsets.getLeft() + deltaX);
StackPane.setMargin(node, newInsets);
}
}
When user clicks on title of the node then pressed X and Y are saved and used in Moused dragged handler. The code works as I need. The only problem is that there is a little lag between mouse move and node mode.
I tried to add the following settings it JVM
-Djavafx.animation.fullspeed=true
-Dprism.vsync=false
But it didn't help, maybe because I use Linux - don't know.
Is there another more optimal way move absolute positioned Node in Pane in JavaFX?
Your code is constantly fighting against the layout mechanism of the StackPane. For absolut positioning of Nodes you should always use a Pane directly. In some situations it may also be possible to use an AnchorPane.
I have a simple extended JSplitPane that I set different panels to at different times when they are needed. Specifically, I split it into an upper and lower section, and I swap out the bottom section frequently. Each time I do, I reset the slider position to how I want it, but sometimes it jumps off at and re-positions itself to the top of the screen (not always).
Here's my code:
public class MainPanel extends JSplitPane{
public Screen screen;
public int height;
public ControlPanel curPanel;
public MainPanel(Screen screen, int height){
super(JSplitPane.VERTICAL_SPLIT);
this.screen = screen;
this.height = height;
setDividerSize(2);
setEnabled(false);
setTopComponent(screen);
setToInitControls();
}
public void setToInitControls(){
InitControls initCtrls = new InitControls(this);
setBottomComponent(initCtrls);
curPanel = initCtrls;
setDividerLocation(height / 4 * 3);
}
public void setToConfigControls(){
ConfigControls configCtrls = new ConfigControls(this);
setBottomComponent(configCtrls);
curPanel = configCtrls;
setDividerLocation(height / 4 * 3);
}
public void setToWaitControls(){
WaitControls waitCtrls = new WaitControls(this);
setBottomComponent(null);
setBottomComponent(waitCtrls);
curPanel = waitCtrls;
setDividerLocation(height / 4 * 3);
}
//and so on (I have more methods like these further down)
//OVERRIDES: I figured overriding these might help. It didn't.
#Override
public int getMinimumDividerLocation(){
return (height / 4 * 3);
}
#Override
public int getMaximumDividerLocation(){
return (height / 4 * 3);
}
}
Basically, I use the "setTo...Controls()" methods to swap bottom panels. Is there a way to tell the slider to stay put where I placed it regardless of the panel's preferred sizes, or if not, how do I make the panels know what to shape themselves to fit in? Thanks for any/all suggestions!
EDIT: I should note that these panels do not use layouts. They are custom panels that I use mouse/keyboard listeners on and use my own graphics to paint over them.
I found the solution, thanks to the links above. It's actually quite simple. Instead of using
setDividerLocation(height / 4 * 3);
for every time I added a component, I just replaced it with:
setResizeWeight(0.66);
Did that once inside the constructor, and it never bothered me again. 0.66 is the equivalent decimal position to h/4*3 (I just trial-and-errored it).