Custom Border for Callout Popup
Werner Lehmann
lehmann at media-interactive.de
Tue May 8 08:24:18 PDT 2012
Hi Richard,
I tried to do what you suggested. It works... sort of. After quite some
code reading I ended up with the code below. The remaining main problem
is that I have to use the tip of the arrow/tail as hotspot to position
the callout popup.
In my original attempt I showed it offscreen to get the actual size,
then moved it to the right spot. With separate control/skin classes this
has become more difficult because only the skin knows how to compute
that hotspot but the control does not know the skin (intentionally, I
assume). Furthermore, the skin instance does not even exist before the
control shows.
So, how can I measure the width before it is even visible on screen? In
the skin constructor the popup width is still zero. I need the width to
move the popup origin to the left because the arrow/tail hotspot is on
the right side.
I have been moving code back and forth and back but could not figure
this out.
Thanks...
Werner
-------
.mint-callout-popup {
-fx-skin: "mint.javafx.stage.MintCalloutPopup2Skin";
-fx-padding: 15 5 0 5;
}
-------
public class MintCalloutPopup2 extends PopupControl
{
private static final String DEFAULT_STYLE_CLASS =
"mint-callout-popup";
static {
StyleManager.getInstance().addUserAgentStylesheet(
MintCalloutPopup2.class.getResource(
"MintCalloutPopup2.css").toExternalForm());
}
private final ObjectProperty<Node> contentNode =
new SimpleObjectProperty<Node>(this, "contentNode");
private final ObjectProperty<Node> target =
new SimpleObjectProperty<Node>(this, "target");
public MintCalloutPopup2()
{
getStyleClass().setAll(DEFAULT_STYLE_CLASS);
setAutoHide(true);
setHideOnEscape(true);
}
public ObjectProperty<Node> contentNodeProperty() { return
contentNode; }
public Node getContentNode() { return contentNode.get(); }
public void setContentNode(Node value) { contentNode.set(value); }
public ObjectProperty<Node> targetProperty() { return target; }
public Node getTarget() { return target.get(); }
public void setTarget(Node value) { target.set(value); }
public void showAtTarget()
{
show(getTarget().getScene().getWindow());
}
}
-------
public class MintCalloutPopup2Skin implements Skin<MintCalloutPopup2>
{
private MintCalloutPopup2 callout;
private StackPane root;
private StackPane content;
public MintCalloutPopup2Skin(MintCalloutPopup2 callout)
{
this.callout = callout;
content = new StackPane();
replaceContent();
callout.contentNodeProperty().addListener(
new InvalidationListener() {
@Override public void invalidated(Observable observable)
{ replaceContent(); }
});
root = new StackPane();
root.getChildren().addAll(createCalloutShape(), content);
root.getStyleClass().setAll(callout.getStyleClass());
root.setStyle(callout.getStyle());
root.setId(callout.getId());
// <------------- the popup bounds are unknown here, how
// can I relocate it "right/top aligned"?
}
private void replaceContent()
{
if (callout.getContentNode() == null)
content.getChildren().clear();
else
content.getChildren().setAll(callout.getContentNode());
}
@Override public MintCalloutPopup2 getSkinnable() { return callout; }
@Override public Node getNode() { return root; }
@Override public void dispose() { callout = null; }
private Shape createCalloutShape()
{
MoveTo e0 ... // bound to widthProperty/heightProperty
// ...
ClosePath e7 = new ClosePath();
return PathBuilder.create()
.managed(false)
.strokeWidth(1)
.smooth(false)
.strokeType(StrokeType.OUTSIDE)
.fill(javafx.scene.paint.Color.WHITE)
.elements(e0, e1, e2, e3, e4, e5, e6, e7)
.build();
}
}
On 07.05.2012 23:54, Richard Bair wrote:
> I think what I would have done would be to extend from PopupControl.
> Then write a new Skin for your Callout. The skin would add the path
> as one of its children, and whatever content is added to the content
> would then be added after the path. The path would have "managed" set
> to false. Then, in the layoutChildren() method of your Callout, you
> would reposition the path based on the current bounds and then call
> super.layoutChildren() which would position and layout the content.
>
> So something like:
>
> public class Callout extends PopupControl { ... }
>
> // Doh! SkinBase is a private class, but you can copy/paste it into
> your code if it is GPL+classpath..., otherwise just see what it does
> and write a fresh Skin that does the same public class CalloutSkin
> extends SkinBase<Callout> { private Path path; ... }
>
> Richard
>
>
>
> On May 7, 2012, at 12:32 PM, Werner Lehmann wrote:
>
>> Hi,
>>
>> I am creating a popup for a callout. The callout is basically a
>> rectangle with a small arrow/tail on the top right corner and a
>> single node for its content. That node may resize later and the
>> callout has to adjust. http://i49.tinypic.com/21crux3.jpg
>>
>> First I tried to do this with -fx-shape and an svg path but without
>> luck: could not make this work, never saw that shape. Unfortunately
>> I also did not find any example... I suppose even an SVG path
>> border would not resize automatically when the content changes -
>> otherwise this would be exactly what I need ;-)
>>
>> Plan B is to use a Popup with a Path in the appropriate shape. This
>> works but I am battling two things:
>>
>> 1. I have to know the popup content layout bounds before I can
>> position the popup "right aligned". Currently solved by showing the
>> popup offscreen (at -10000, 0) to get valid layoutBounds. Then I
>> move it to the correct position. Is there a better way to get
>> layoutBounds before showing a window?
>>
>> 2. When the content size changes, a similar problem arises: now I
>> have to determine the new popup size, change the border Path to
>> match the new size (PathElement binding might work here) and
>> reposition the window. A resize animation would be nice, and some
>> clipping will be needed also...
>>
>> This is when I got an idea: is it possible to create and use a
>> custom border class? Looks as if I could extend
>> com.sun.javafx.scene.layout.region.Border... In this way I would
>> get content clipping and resizing (position of arrow/tail must be
>> adjusted) for free.
>>
>> Any thoughts?
>>
>> Thanks. Werner
>>
>
--
--------------------------------------------------------
MINT MEDIA INTERACTIVE Software Systems GmbH
Kiel Science Centre / Wissenschaftszentrum
Fraunhoferstr. 13
D-24118 Kiel, Germany
Phone: +49 (0)431 530215-0
Fax: +49 (0)431 5302090
Mail: lehmann at media-interactive.de
Web: www.media-interactive.de
--------------------------------------------------------
--------------------------------------------------------
Sitz der Gesellschaft / Corporate HQ: Kiel
Reg.-eintragung / Registration:
Amtsgericht Kiel HRB 4860
Ust-ID Nr. DE 197455787
Geschäftsführer / Managing Director: Jörg Latteier
--------------------------------------------------------
More information about the openjfx-dev
mailing list