As succinctly as I can manage: Given that I need the layout information of a node (the actual height/width of a node as rendered) to compute my animation, how can I get that information before javafx draws a frame with it?
A little bit longer explanation:
I've got a TreeItem that has child items appearing in it (at the front). What I'd like to have is an animation to cause all existing children to slide down to make room for the new item which would slide in. Each child tree-items contents are different and only known at run-time, meaning the height of each child tree item cannot be expressed as a constant.
This got me writing code along these lines:
groupController.groupTreeItem.getChildren().addListener(
new ListChangeListener<TreeItem<Node>>() {
#Override public void onChanged(Change<? extends TreeItem<Node>> c) {
while(c.next()){
if ( ! c.wasAdded()){
continue;
}
TreeItem newItem = c.getAddedSublist().get(0)
new Timeline(
new KeyFrame(
seconds(0),
new KeyValue(view.translateYProperty(), -1 * newItem.getHeight())
),
new KeyFrame(
seconds(1),
new KeyValue(view.translateYProperty(), 0)
)
);
}
}
}
);
the issue here is that as when a treeItem is added to another, its components aren't laid out by the time the invalidation event is fired, meaning newItem.view.getHeight() returns 0.
My next thought was to then have the animation performed as a reaction to both a change in the list content and a sequential change to the height property, (which got me to write some really hideous code that I'd rather not share --listeners adding listeners is not something I really want to write). This almost works, except that javaFX will draw a single frame with the height property set but without the animations translation applied. I could hack down this road further and try to work something out with opacity being toggled and jobs being enqueued for later, but I figured that this would be the path to madness.
I'm wondering if there's some pseudo-class foo or some clever use of a layout property I could use to help me here. I've been poking around at various combinations of various properties, and haven't gotten anywhere. It seems that as soon as the component has a height, it is rendered, regardless of any listeners you put in or around that height assignment.
Any help much appreciated!
have you tried, overriding this
#Override
protected void updateBounds() {
super.updateBounds();
}
Related
Could someone tell me how to implement RichTextFX CodeArea with line numbers section extended till the end of the area?
This is how it looks like now:
I don't need line numbers after line 12 but I would like to see this grey bar to fill the entire text area.
Something like here:
P.S. I'm not sure if this is even possible.
I know this is a rather old question, but since I had the same problem, let me share the solution I came up with.
What I did is to smuggle in a rectangle and make sure it is the bottom-most element (i.e. basically part of the background). However, there are a few gotchas when doing this, because the underlying CodeArea is not aware of our new node. If you just insert the rectangle, it might get removed when the CodeArea decides to rebuild the nodes. And getting the right width is a bit tricky because the width of line numbers can basically change at any time and the line-number labels themselves fade in and out of existence whenever you scroll.
So, in order to address these issues, my code sits in the layoutChildren() method and is thus called whenever the nodes in the editor have changed. First we check that the rectangle is actually there as the bottom-most node or insert it if missing. Second, we set the width of the rectangle to the width of the first visible line-number label (which might fail if there are no paragraphs at the moment).
The code itself here is in Scala, but probably easy enough to be quickly adapted to Java.
class MyCodeArea extends CodeArea {
protected val gutterRect = new Rectangle()
gutterRect.heightProperty.bind(this.heightProperty)
gutterRect.getStyleClass.add("lineno")
override protected def layoutChildren(): Unit = {
try {
val children = getChildren
if (!(children.get(0) eq gutterRect))
children.add(0, gutterRect)
val index = visibleParToAllParIndex(0)
val wd = getParagraphGraphic(index).prefWidth(-1)
gutterRect.setWidth(wd)
} catch {
case _: Throwable =>
}
super.layoutChildren()
}
}
Unfortunately, the colour of the rectangle must be assimilated manually. The reason is that the Labels used for line numbers use -fx-background-color, whereas the Rectangle uses -fx-fill. Hence, just setting the same CSS class "lineno" (as I did in my code above) does little to get the colour right. But it allowed me to put both into the same CSS class and therefore have one place where I can change it:
.lineno {
-fx-fill: ivory; // or whatever colour you like
-fx-background-color: -fx-fill;
}
I can find neither a solution nor whether it is even possible to easily transform a Composite in SWT.
I'm trying to implement zooming in/out with CTRL+SCROLL over a Composite. What I found thus far is adding a PaintListener to a composite, like this:
this.viewingFrame = new Shell(new Display());
this.viewingFrame.setLayout(new GridLayout(2, false));
GridDataFactory.fillDefaults().grab(true, true).applyTo(viewingFrame);
c1 = new Composite(viewingFrame, SWT.NONE);
GridLayoutFactory.swtDefaults().applyTo(c1);
GridDataFactory.swtDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(c1);
c1.addPaintListener(new PaintListener() {
#Override
public void paintControl(PaintEvent e) {
Transform trans = new Transform(e.display);
e.gc.setAdvanced(true); // After this line a call to e.gc.getAdvance() returns true
trans.scale(1.5f, 1.5f);
e.gc.setTransform(trans);
trans.dispose();
}
});
// Of course there is more code. I have a KTable displaying a 2D map. I also tried adding the PaintListener to the shell, the composite c1 and the KTable. Same result.
However this doesn't work. While debugging I could see that the listener is called but there is no visible effect.
I'm a novice with regard to SWT but I learn fast so I'm willing to consider complex solutions.
The short answer is: scaling controls in SWT is not supported (directly). The Transform applies only to the drawing operations that are done on the GC for which it was created. It does not affect the controls that are contained in the Composite.
To emulate scaling to a cretain degree, you can change the font size of the container whose content should scale (i.e. the Shell or a Composite) and then re-layout. The font size is inherited by contained controls unless a font was explicitly set. In the latter case you would have to change the size of these fonts as well.
If you want the Shell to grow/shrink with its content you will have to pack() it. Otherwise you likely need to place a ScrolledComposite within the Shell that contains all the actual content.
BTW use the Canvas widget instead of a Composite if you actually want to draw something.
I’m pretty new in SWT/JFace technology and I’ve found a problem that it’s driving me crazy. In an Eclipse RCP application I have a view where I’ve placed a SWT tree with a JFace TreeViewer which provides the labels and the icons by means of a label provider. By requirements of the customer the background colour of the tree is dark blue and the font colour is white. This combination of colours results in a bad visualization of a node’s text when the node is selected, the text does not fit the tree region and we place the mouse pointer over the node. Somehow a “native highlighting” appears. This can be shown in the following image.
On the other side, this problem does not happen when the node where we place the mouse over is not selected. The highlighting changes the colour of the font to make it more visible. This can be shown in the following image.
After doing some research I’ve found that by adding a listener for the SWT.EraseItem event I am able to modify the background’s colour of a selected node and then disable the selection. This allows me to define my own selection background style and also disable the SWT.SELECTED flag of the event.detail in order to force the OS to highlight as the node is not selected.
private final class EraseItemListener implements Listener {
public void handleEvent(Event event) {
// Only perform the node highlight when it is selected.
if ((event.detail & SWT.SELECTED) == SWT.SELECTED) {
// Modify background, emulate Windows highlighting.
...
// Set Windows that we do not want to draw this item as a selection (we have already highlighted the item in our way).
event.detail &= ~SWT.SELECTED;
}
}
}
This “solution” can be reasonable. The main drawbacks I see is that my selection style only fits for the Windows 7 default visual themes. For those “Windows classic” or “High contrast” I’ll get visualization problems. Moreover (and this is the most annoying issue), the fact of adding a listener for the SWT.EraseItem (even without code to handle the event) produces two new problems.
This makes either SWT or JFace to draw the icon of the tree node in
the wrong place as you can see in the following image.
The highlight of the tree’s root node is completely wrong. As you
can see in the following image, the node seems to be highlighted in
2 different ways and the icon is repeated.
My questions are basically two.
Do you think there is an easier solution for the main problem? What
I would like is to show a selected node (the one of the first image)
in the same way as in the second image. I would like to change the
foreground colour of the selected node to make it more visible.
In case of using the SWT.EraseItem approach, is there any way of
showing the icons in the correct location? Is this behaviour a known
bug?
Thanks in advance.
If you use a label provider based on StyledCellLabelProvider (perhaps one based on DelegatingStyledCellLabelProvider) you can specify COLORS_ON_SELECTION to retain normal colors on selection.
If that is not good enough (don't have Windows here to test) you can override the paint method - again you can try using event.detail &= ~SWT.SELECTED to suppress the normal selection handling or even handle the paint yourself.
Just stumbled across this old post and realized it matches a bug that I opened recently on eclipse.
My workaround to have the text in the correct color was to paint over the text with a different color using a PaintListener (registered in the tree with SWT.PaintItem):
private static class TreePaintListener implements Listener {
#Override
public void handleEvent(Event event) {
boolean isSelected = (event.detail & SWT.SELECTED) != 0;
if (isSelected && event.item instanceof TreeItem) {
TreeItem treeItem = (TreeItem) event.item;
Tree parent = treeItem.getParent();
GC gc = event.gc;
Color foreground = null/* Some Color */;
gc.setForeground(foreground);
Rectangle imageBounds = treeItem.getImageBounds(0);
Rectangle textBounds = treeItem.getTextBounds(0);
Point stringExtent = gc.stringExtent(treeItem.getText());
int offsetX = imageBounds.width != 0 ? imageBounds.height - imageBounds.width : 0;
int offsetY = (textBounds.height - stringExtent.y) / 2;
int x = textBounds.x + offsetX;
int y = textBounds.y + offsetY;
if (gc.getClipping().contains(x, y)) {
gc.drawString(treeItem.getText(), x, y);
}
}
}
}
But it still misbehaves when there are multiple columns defined (only the first one is painted over), and depending on the operating system and DPI the calculations might be a bit off.
We are currently using Eclipse Draw2D/GEF for an information page describing a process in our application. This basically consists of a matrix of large squares, each containing a matrix of smaller squares. We originally had all the squares as GEF objects, but because of the large volume of them being shown, we found that this did not scale very well and the view took a very long time to open. We then changed it so that only the large squares are Figures and we then draw the smaller squares using the graphic in paintFigure.
The problem that we are running into is that we still want the tooltip to change depending on which small square you are hovering over. I tried to do this by adding a mouseMotionListener and setting the tooltip, through setTooltip, depending on where the mouse currently is. The problem is that once the tooltip is displayed, it does not change any more when setTooltip is called.
Does any one know of an alternative way of doing this? Is there a way of getting the viewpart's PopupHelper and using that? Any help would be appreciated.
Thanks
Hmnn.. interesting problem. Since you paint your own Grid within the Figure, I would think that you have two options.
Try posting SWT events to fool Eclipse. I'd try a focus lost followed by a focused gained, to trigger tooltip machinery, at which point you could get the coordinates and display the appropriate contents.
Don't use the Figure#getTooltip strategy at all. Just show your own composite.
To dynamically change the tooltip, you can hold an instance of the tooltip Figure in your parent Figure. In the constructor of the parent Figure, create a new tooltip Figure (e.g. a Label) and use setToolTip() method to set the tooltip Figure to parent Figure.
When data model is changed, the updated tooltip text/icon can be set to the tooltip Figure. Then you just call setToolTip(tooltipFigure) method again.
You can have a method like:
protected Label toolTipLabel;
protected void updateToolTip(String text, Image icon){
toolTipLabel.setText(text);
toolTipLabel.setIcon(icon);
setToolTip(toolTipLabel);
}
The updateToolTip() method can be invoked in parent Figure's conturctor to initialize the tooltip. And this method can be invoked each time after the data model is changed.
I encountered the same problem in my code and solved it with that method. In my code, I invoked the updateToolTip() in the parentFigure.paintFigure() method.
I have to use a GUI Element that draws a picture at a specific screen position.
If the user selects this picture there is a border drawn around the Image.
Now we want to include another border that identifies pictures with a specific value for the user.
At the moment the Element looks at his internal state if it is selected and then decides how to draw itself.
graphic.drawImage(icon, x, y, null);
if (selected) {
drawBorder();
}
I don't like the idea of adding another if else to this drawing method.
I thought about creating a new class that inherits the behavior of the element and overwrites the draw method but that means duplicating the whole selected code in every inherited class.
Is there a nice possibility so solve this problem without creating a subclass?
Since you tagged this with design-patterns and you seem to be looking for a pattern-oriented approach, I'd suggest taking a look at the state pattern. The example on the wikipedia page even mentions keeping state while drawing a GUI. Unfortunately, this would mean you'd have to create another class with subclasses and overridden methods.
Is this going to be something that is likely to change? I.e. do you realistically think you're going to be adding new behavior to the drawing (e.g. if the user double clicks, draw a different type of border; if the user right clicks, change the color of the border), or is this it? If you see more behavior being added, I think going ahead and taking a more OO approach is wise. If it's just these two cases, I'd say just add and else if statement.
What do you have against if-else?
It makes less sense to me to create a whole new object for the selected item than to check a flag in the drawing function.
one possibility is to allow your drawBorder() method to take parameters:
private void drawBorder(boolean isSelected, boolean hasSpecialValue);
this method can determine which type of border to draw.