77import java .io .IOException ;
88import java .util .Comparator ;
99import java .util .Objects ;
10- import java .util .Optional ;
1110
1211import org .apache .commons .lang3 .StringUtils ;
1312import org .checkerframework .checker .nullness .qual .NonNull ;
@@ -62,6 +61,35 @@ public class PropertyCollectionView extends VBox implements ApplicationComponent
6261 ValueExtractor .addObservableValueExtractor (c -> c instanceof ListCell , c -> ((ListCell ) c ).itemProperty ());
6362 }
6463
64+ /*
65+ Note: nesting a popover within a popover is error-prone: JavaFX
66+ doesn't support a proper focus model for PopupWindows (used by PopOver)
67+
68+ This means, multiple open popovers maintain independent focus models
69+ and react to every event of the stage. Ie there's no stage that has
70+ primary focus. Eg if you focus a textfield in a popover and carry
71+ on editing somewhere else in the main stage, both places will see
72+ the update.
73+
74+ This also means that when eg here, you click the "add property" button,
75+ the button doesn't lose focus in its popover. When editing something in
76+ the new popover, this will send an event to the edited text field AND
77+ to the "add property" button.
78+
79+ This is why the action handlers explicitly request focus on some
80+ innocuous control (eg listview) after they're done processing.
81+
82+ This is also why we can't let the edit popover hover around in
83+ detached mode.
84+
85+
86+ See:
87+
88+ https://github.com/openjdk/jfx/blob/be22e8535cb3f98069c0a0216b056cfe8ef037e8/modules/javafx.graphics/src/main/java/javafx/stage/PopupWindow.java#L531-L535
89+
90+
91+ */
92+
6593
6694 // for scenebuilder
6795 @ SuppressWarnings ("ConstantConditions" ) // suppress nullability issue
@@ -90,7 +118,10 @@ public PropertyCollectionView(@NamedArg("designerRoot") DesignerRoot root) {
90118
91119 ControlUtil .anchorFirmly (addProperty );
92120
93- addProperty .setOnAction (e -> addNewProperty (getUniqueNewName ()));
121+ addProperty .setOnAction (e -> {
122+ addNewProperty (getUniqueNewName ());
123+ view .requestFocus ();
124+ });
94125 footer .getChildren ().addAll (addProperty );
95126 this .getChildren ().addAll (view , footer );
96127
@@ -175,7 +206,6 @@ private PopOver rebindPopover(PropertyDescriptorSpec newSpec, PopOver pop) {
175206 // create it
176207 return detailsPopOver (newSpec );
177208 }
178- Optional .ofNullable (pop .getOnHiding ()).ifPresent (it -> it .handle (null ));
179209
180210 pop .titleProperty ().bind (newSpec .nameProperty ()
181211 .filter (StringUtils ::isNotBlank )
@@ -279,6 +309,7 @@ protected Pair<Node, Subscription> getNonEditingGraphic(PropertyDescriptorSpec s
279309 edit .setOnAction (e -> {
280310 myEditPopover .rebind (spec );
281311 myEditPopover .showOrFocus (p -> PopOverUtil .showAt (p , owner , this ));
312+ view .requestFocus ();
282313 });
283314
284315 sub = sub .and (() -> edit .setOnAction (null ));
@@ -293,6 +324,7 @@ protected Pair<Node, Subscription> getNonEditingGraphic(PropertyDescriptorSpec s
293324 myEditPopover .rebind (null );
294325 myEditPopover .hide ();
295326 }
327+ view .requestFocus ();
296328 });
297329
298330 sub = sub .and (() -> delete .setOnAction (null ));
0 commit comments