JavaFX ContextMenu max size has no effect - javafx

If a ContextMenu has lots of items, it fills the entire screen. It seems that ContextMenu.setMaxSize has no effect whatsoever.
Is there a way to restrict the size of a ContextMenu, in a way that it is still scrollable via mouse wheel & and the up and down buttons appear?
I guess I could roll my own control with VBox & Scrollpane, but I'd like to avoid this if possible.

Unfortunately, limiting the size of popup is not supported: the Region that's responsible for showing the MenuItems is ContextMenuContent and implements its computeMaxHeight to return the screenHeight. That container is created by ContextMenuSkin and stored into a private final field, so there's no way to replace it with a custom implementation with a more intelligent implementation.
What we can do, though, is to access that region and set its maxHeight to the same value as the ContextMenu. To remain off the evil illegal reflective access to the private field, we can register a handler for the Menu.ON_SHOWING event and update the size as needed [*].
Something like
public class MaxSizedContextMenu extends ContextMenu {
public MaxSizedContextMenu() {
addEventHandler(Menu.ON_SHOWING, e -> {
Node content = getSkin().getNode();
if (content instanceof Region) {
((Region) content).setMaxHeight(getMaxHeight());
}
});
}
}
[*] update: to make this work, the ContextMenu must have a reasonable maxHeight (default is Double.MAX_VALUE), that is it must be set manually after instantiation. Furthermore, we have to use the ContextMenu's maxHeight in the eventHandler (vs. f.i. an arbitrary constant), otherwise vertical location of the popup is broken - the layout code still thinking, that it's filling the entire screen height.
ContextMenu menu = new MaxSizedContextMenu();
menu.setMaxHeight(200);

Related

JavaFX width/height properties fire only once

I'm new to JavaFX and am trying to monitor the resize events on a window so that I can trigger a recalculation of the layout.
If I create a stage and set the scene like in the example below I only get each resize event to be fired once. No matter how many times I resize the window.
Stage stage = new Stage();
stage.setScene(someScene);
stage.setTitle("Some Title");
stage.initModality(Modality.APPLICATION_MODAL);
stage.show();
stage.widthProperty().addListener(observable -> {
System.out.println("Width changed");
});
stage.heightProperty().addListener(observable -> {
System.out.println("Height changed");
});
First note:
I'm trying to monitor the resize events on a window so that I can
trigger a recalculation of the layout.
This is almost certainly the wrong approach. Layout recalculations will be triggered automatically when the stage and scene change size. If you use standard layout panes, there is no need to register listeners. If you really need a custom layout (which is highly unlikely), you should subclass Pane and override layoutChildren() (and other appropriate methods) to hook into the same layout system.
However, in the interests of explaining what you're observing:
You're registering an InvalidationListener with each property, which gets notified when the property goes from a valid state to an invalid state.
The invalid state only becomes valid again if you actually request the value of the property (e.g. stage.getWidth()). Since you never do that, the property never becomes valid, and hence never goes from valid to invalid again.
Instead, register a ChangeListener with each property:
stage.widthProperty().addListener((observable, oldWidth, newWidth) -> {
System.out.println("Width changed");
});
stage.heightProperty().addListener((observable, oldHeight, newHeight) -> {
System.out.println("Height changed");
});
Alternatively, you can force validation by requesting the value (though I think the change listener above actually gets to the point of what you are trying to do):
stage.widthProperty().addListener(observable -> {
System.out.println("Width changed: "+stage.getWidth());
});
etc.

How to detect SizeChanged event after using .HeightRequest

I noticed that after using the .HeightRequest property on a View in Xamarin.Forms that a method I have assigned to the SizeChanged event is no longer called.
The content within the View that had called .HeightRequest is a Label that contains text which can be zoomed in. When zooming in, the text now exceeds the "bounds" of the View's height, and the height is no longer adjusting as it once was.
public ExpandableEntryView(...)
{
InitializeComponent();
// my beautiful code
SizeChanged += ExpandableEntryView_SizeChanged;
UpdateUI();
}
private void ExpandableEntryView_SizeChanged(object sender, EventArgs e)
{
// this is never called once .HeightRequest is assigned a value
}
private void SomeFunction() {
sectionsStackLayout.HeightRequest = 0; // this causes SizeChanged to no longer work
}
I essentially need this View to minimize its height to 0, and then return to its original height when I tell it to. I am also using IsVisible, but the Height property is required for an animation I am trying to do.
Thank you in advance.
Nevermind, I was able to figure it out. The reason why the SizeChanged event is no longer called is because the view is no longer responsible for keeping track of its size after assigning a value to HeightRequest. So, if you would like for your View to return to being able to determine its own size once again, (i.e. if a child element in that View happens to change size/scale) you need to assign the HeightRequest to -1. This will then reenable the SizeChanged event.
Hope this helps somebody!

JavaFX TreeView: remove expand / collapse button (disclosure node) & functionality

I want to have a TreeView that has all of its children permanently expanded, and I don't want the user to be able to expand or collapse any of the children.
To do this I've found that I need to do the following:
Remove icon with CSS (Done)
Change expand and collapse image TreeView JavaFX 2.2
[edit] Above link should be used to change image; to remove completely, use this solution: https://stackoverflow.com/a/27831191/4430591
Remove double click functionality (Done)
Disable TreeItem's default expand/collapse on double click JavaFX 2.2
[edit] Remove ability to collapse / expand using keyboard arrrow keys (Done)
Given in José Pereda's solution below ( https://stackoverflow.com/a/27831085/4430591 )
[edit] Remove ability to right click for a ContextMenu (Done)
Given in José Pereda's solution below ( https://stackoverflow.com/a/27831085/4430591 )
Remove icon's clickablity (How do I do this?)
[edit] solution: https://stackoverflow.com/a/27831191/4430591
Even though the icon is no longer visible, it's still clickable. I don't see any way of filtering this; I only see ways to be able to respond to it after the fact.
Also, if I'm missing anything else that I need to do to ensure this functionality, please let me know.
I feel quite silly. I think this was mostly just a matter of not knowing what that darn arrow was called. Apparently it's a disclosureNode? Maybe that's common knowledge.
In the custom defined TreeCell, all I did was add this line in the updateItem method:
setDisclosureNode(null);
The solution to avoid modifying the skin or the default behavior is more simple if we trap the clicks before they are dispatched, and consume the right ones.
For that we can use an EventDispatcher, to filter both the mouse pressed and the right click over the arrows, which are StackPane nodes:
class CellEventDispatcher implements EventDispatcher {
private final EventDispatcher original;
public CellEventDispatcher(EventDispatcher original) {
this.original = original;
}
#Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
if (event.getEventType().equals(MouseEvent.MOUSE_PRESSED) ||
event.getEventType().equals(ContextMenuEvent.ANY)){
event.consume();
}
if(event instanceof KeyEvent && event.getEventType().equals(KeyEvent.KEY_PRESSED)){
if((((KeyEvent)event).getCode().equals(KeyCode.LEFT) ||
((KeyEvent)event).getCode().equals(KeyCode.RIGHT))){
event.consume();
}
}
return original.dispatchEvent(event, tail);
}
}
Now we apply our custom dispatcher to the tree view:
#Override
public void start(Stage primaryStage) {
TreeView<String> tree = new TreeView<>();
...
EventDispatcher treeOriginal = tree.getEventDispatcher();
tree.setEventDispatcher(new CellEventDispatcher(treeOriginal));
Scene scene = new Scene(tree);
primaryStage.setScene(scene);
primaryStage.show();
}
This will consume any click (left or right) over the arrows on the tree.
EDIT
Added to the event dispatcher class the case where the user uses the keyboard to traverse the tree view, consuming the collapse/expand events with arrow LEFT or RIGHT.

flex- Drag and drop

I am trying to drag and drop an object across the SkinnableContainer- am coming across a very strange issue
The drop occurs only at a few places- elsewhere it just shows the "X" sign and on dropping there, reverts to original position. I have used very standard commands... from function 2 to function 3, the call occurs very rarely as seen in trace statements- any guidance on why this happens?
I added the following code to SkinnableContainer: dragEnter="dragEnterHandler(event);" dragDrop="dragDropHandler(event);
(1):
private function mouseMoveHandler(event:MouseEvent):void
{
var dragInitiator:Image = Image(event.currentTarget);
var ds:DragSource = new DragSource();
ds.addData(dragInitiator,"img"); //made change here
DragManager.doDrag(dragInitiator, ds, event);
}
(2):
private function dragEnterHandler(event:DragEvent):void {
if (event.dragSource.hasFormat("img"))
{
trace("came here"); //comes here for each mouse move
DragManager.acceptDragDrop(SkinnableContainer(event.currentTarget));
}
(3):
private function dragDropHandler(event:DragEvent):void {
trace("in drag drop handler"); //doesn't come here for most places
According to the Using Flex 4 reference:
To use a container as a drop target, you must use the backgroundColor property of the container to set a color. Otherwise, the background color of the container is transparent, and the Drag and Drop Manager is unable to detect that the mouse pointer is on a possible drop target.
In the subsequent example, they use an mx container (Canvas), but I checked the AS3 reference, and spark.components.SkinnableContainer does have a style backgroundColor.
I haven't tried this myself, so please confirm whether it is the issue. From your description that only certain parts of the container are registering the dragEnter event, this seems like a consideration that would lead to such effects.

How do I change the State in an itemRenderer based on an action in another itemRenderer?

I have a DataGridColumn with an ItemRenderer that extends the Box component. The default display is a Text component. When the user clicks on the text component, I change the State to add a PopUpMenuButton child, and make the Text component invisible. This works fine. However, I only want to allow one PopUpMenuButton to be visible in the DataGrid at a time (similar to how an itemEditor works). I don't want to use an itemEditor, because I've run into too many problems trying to get that to work in this instance.
I am implementing IDropInListItemRenderer in my itemRenderer, in order to access the listData property, which will give me the owner (DataGrid), but I don't know how to "turn off" the "editing" state in other itemRenderers in the DataGrid.
How can I accomplish this?
Thanks.
Here we go. I simply added an Listener for Change Events in the listData.owner - if it is triggered, I update the currentState to null. Works like a charm. Much easier than trying to access the itemRenderers in the column and resetting them all. Better on performance too.
private function label_clickHandler():void
{
showEditor();
}
private function showEditor():void
{
this.currentState = "editingMode";
var ownerListBase:ListBase = ListBase(listData.owner);
ownerListBase.addEventListener(ListEvent.CHANGE, ownerListBase_changeHandler);
}
private function ownerListBase_changeHandler(event:ListEvent):void
{
this.currentState = null;
var ownerListBase:ListBase = ListBase(listData.owner);
ownerListBase.removeEventListener(ListEvent.CHANGE, ownerListBase_changeHandler);
}

Resources