diff --git a/app/alarm/logging-ui/src/main/java/org/phoebus/applications/alarm/logging/ui/AlarmLogTableController.java b/app/alarm/logging-ui/src/main/java/org/phoebus/applications/alarm/logging/ui/AlarmLogTableController.java index 21abe8c271..8843345dbe 100644 --- a/app/alarm/logging-ui/src/main/java/org/phoebus/applications/alarm/logging/ui/AlarmLogTableController.java +++ b/app/alarm/logging-ui/src/main/java/org/phoebus/applications/alarm/logging/ui/AlarmLogTableController.java @@ -35,6 +35,7 @@ import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.layout.GridPane; + import javafx.util.Duration; import org.phoebus.applications.alarm.AlarmSystem; import org.phoebus.applications.alarm.logging.ui.AlarmLogTableQueryUtil.Keys; @@ -44,6 +45,7 @@ import org.phoebus.framework.selection.SelectionService; import org.phoebus.ui.application.ContextMenuHelper; import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog; +import org.phoebus.ui.focus.FocusUtility; import org.phoebus.ui.javafx.ImageCache; import org.phoebus.ui.javafx.JFXUtil; import org.phoebus.util.time.TimeParser; @@ -569,7 +571,8 @@ public void createContextMenu() { // search for other context menu actions registered for AlarmLogTableType SelectionService.getInstance().setSelection("AlarmLogTable", tableView.getSelectionModel().getSelectedItems()); - ContextMenuHelper.addSupportedEntries(tableView, contextMenu); + + ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(tableView), contextMenu); tableView.setContextMenu(contextMenu); diff --git a/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/AlarmContextMenuHelper.java b/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/AlarmContextMenuHelper.java index cffca22e08..d37ffb0047 100644 --- a/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/AlarmContextMenuHelper.java +++ b/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/AlarmContextMenuHelper.java @@ -7,12 +7,12 @@ *******************************************************************************/ package org.phoebus.applications.alarm.ui; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - +import javafx.scene.Node; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; +import javafx.scene.control.SeparatorMenuItem; import org.phoebus.applications.alarm.AlarmSystem; import org.phoebus.applications.alarm.client.AlarmClient; import org.phoebus.applications.alarm.client.AlarmClientLeaf; @@ -24,13 +24,13 @@ import org.phoebus.framework.selection.SelectionService; import org.phoebus.ui.application.ContextMenuHelper; import org.phoebus.ui.dialog.DialogHelper; +import org.phoebus.ui.focus.FocusUtility; -import javafx.scene.Node; -import javafx.scene.control.Alert; -import javafx.scene.control.Alert.AlertType; -import javafx.scene.control.ContextMenu; -import javafx.scene.control.MenuItem; -import javafx.scene.control.SeparatorMenuItem; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; /** Helper for adding guidance, displays, commands to context menu * @author Kay Kasemir @@ -132,13 +132,13 @@ public void addSupportedEntries(final Node node, { menu_items.add(new SeparatorMenuItem()); SelectionService.getInstance().setSelection("AlarmUI", pvnames); - ContextMenuHelper.addSupportedEntries(node, menu); + ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(node), menu); } else { // search for other context menu actions registered for AlarmTreeItem SelectionService.getInstance().setSelection("AlarmUI", selection); - ContextMenuHelper.addSupportedEntries(node, menu); + ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(node), menu); } } diff --git a/app/databrowser/src/main/java/org/csstudio/trends/databrowser3/ui/properties/TracesTab.java b/app/databrowser/src/main/java/org/csstudio/trends/databrowser3/ui/properties/TracesTab.java index 327b558063..78a7437c42 100644 --- a/app/databrowser/src/main/java/org/csstudio/trends/databrowser3/ui/properties/TracesTab.java +++ b/app/databrowser/src/main/java/org/csstudio/trends/databrowser3/ui/properties/TracesTab.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.logging.Level; import java.util.stream.Collectors; import org.csstudio.javafx.rtplot.LineStyle; @@ -36,6 +37,7 @@ import org.phoebus.ui.application.ContextMenuHelper; import org.phoebus.ui.dialog.AlertWithToggle; import org.phoebus.ui.dialog.DialogHelper; +import org.phoebus.ui.focus.FocusUtility; import org.phoebus.ui.undo.UndoableActionManager; import org.phoebus.util.time.SecondsParser; @@ -740,7 +742,7 @@ private void createContextMenu() if (pvs.size() > 0) { SelectionService.getInstance().setSelection(this, pvs); - ContextMenuHelper.addSupportedEntries(trace_table, menu); + ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(trace_table), menu); } menu.show(trace_table.getScene().getWindow(), event.getScreenX(), event.getScreenY()); diff --git a/app/databrowser/src/main/java/org/csstudio/trends/databrowser3/ui/search/SearchView.java b/app/databrowser/src/main/java/org/csstudio/trends/databrowser3/ui/search/SearchView.java index 910339b921..a58f3d108d 100644 --- a/app/databrowser/src/main/java/org/csstudio/trends/databrowser3/ui/search/SearchView.java +++ b/app/databrowser/src/main/java/org/csstudio/trends/databrowser3/ui/search/SearchView.java @@ -22,6 +22,7 @@ import org.phoebus.framework.selection.SelectionService; import org.phoebus.ui.application.ContextMenuHelper; import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog; +import org.phoebus.ui.focus.FocusUtility; import org.phoebus.ui.undo.UndoableActionManager; import javafx.application.Platform; @@ -52,6 +53,8 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; +import static org.phoebus.ui.application.PhoebusApplication.logger; + /** Panel for searching the archive * @author Kay Kasemir */ @@ -161,9 +164,8 @@ private void updateContextMenu(final ContextMenuEvent event) { menu.getItems().setAll(new AddToPlotAction(channel_table, model, undo, selection), new SeparatorMenuItem()); - SelectionService.getInstance().setSelection(channel_table, selection); - ContextMenuHelper.addSupportedEntries(channel_table, menu); + ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(channel_table), menu); menu.show(channel_table.getScene().getWindow(), event.getScreenX(), event.getScreenY()); } } diff --git a/app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/ContextMenuSupport.java b/app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/ContextMenuSupport.java index 29839538f4..9bd21e5c90 100644 --- a/app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/ContextMenuSupport.java +++ b/app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/ContextMenuSupport.java @@ -43,6 +43,8 @@ import org.phoebus.ui.application.ContextMenuHelper; import org.phoebus.ui.application.ContextMenuService; import org.phoebus.ui.application.SaveSnapshotAction; +import org.phoebus.ui.docking.DockItem; +import org.phoebus.ui.docking.DockPane; import org.phoebus.ui.javafx.ImageCache; import org.phoebus.ui.javafx.PrintAction; import org.phoebus.ui.spi.ContextMenuEntry; @@ -79,7 +81,25 @@ class ContextMenuSupport { @Override public void handleContextMenu(final Widget widget, final int screen_x, final int screen_y) { final Node node = JFXBaseRepresentation.getJFXNode(widget); - fillMenu(node, widget); + + Runnable setFocus; + { + // Set the DockPane that contains 'widget' as the active + // DockPane, so that applications are launched in the + // same DockPane: + DisplayModel displayModel; + try { + displayModel = widget.getTopDisplayModel(); + } catch (Exception e) { + throw new RuntimeException(e); + } + DisplayRuntimeInstance displayRuntimeInstance = DisplayRuntimeInstance.ofDisplayModel(displayModel); + DockItem dockItem = displayRuntimeInstance.getDockItem(); + DockPane dockPane = dockItem.getDockPane(); + setFocus = () -> DockPane.setActiveDockPane(dockPane); + } + + fillMenu(setFocus, widget); // Use window, not node, to show menu for two reasons: // 1) menu.show(node, ..) means menu is attached to node, // inheriting styles of nodes. For widgets that change background color @@ -95,10 +115,10 @@ public void handleContextMenu(final Widget widget, final int screen_x, final int /** Fill context menu with items for widget * - * @param node + * @param setFocus * @param widget */ - private void fillMenu(final Node node, final Widget widget) + private void fillMenu(Runnable setFocus, final Widget widget) { final ObservableList items = menu.getItems(); items.setAll(new WidgetInfoAction(widget)); @@ -189,7 +209,7 @@ private void fillMenu(final Node node, final Widget widget) // Set the 'selection' to the PV of this widget SelectionService.getInstance().setSelection(DisplayRuntimeApplication.NAME, processVariables); // Add PV-based menu entries - ContextMenuHelper.addSupportedEntries(node, menu); + ContextMenuHelper.addSupportedEntries(setFocus, menu); items.add(new SeparatorMenuItem()); } diff --git a/app/pace/src/main/java/org/csstudio/display/pace/gui/GUI.java b/app/pace/src/main/java/org/csstudio/display/pace/gui/GUI.java index 4ff58f1806..2a1c323ea6 100644 --- a/app/pace/src/main/java/org/csstudio/display/pace/gui/GUI.java +++ b/app/pace/src/main/java/org/csstudio/display/pace/gui/GUI.java @@ -38,6 +38,7 @@ import javafx.scene.control.TablePosition; import javafx.scene.control.TableView; import javafx.scene.layout.BorderPane; +import org.phoebus.ui.focus.FocusUtility; /** GUI for PACE {@link Model} * @author Kay Kasemir @@ -200,7 +201,7 @@ private void createContextMenu() { items.add(new SeparatorMenuItem()); SelectionService.getInstance().setSelection("AlarmUI", pvnames); - ContextMenuHelper.addSupportedEntries(table, menu); + ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(table), menu); } }); diff --git a/app/probe/src/main/java/org/phoebus/applications/probe/view/ProbeController.java b/app/probe/src/main/java/org/phoebus/applications/probe/view/ProbeController.java index 21bf79ced3..51103248da 100644 --- a/app/probe/src/main/java/org/phoebus/applications/probe/view/ProbeController.java +++ b/app/probe/src/main/java/org/phoebus/applications/probe/view/ProbeController.java @@ -8,6 +8,8 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; +import javafx.stage.Stage; +import javafx.stage.Window; import org.epics.vtype.Alarm; import org.epics.vtype.AlarmSeverity; import org.epics.vtype.Display; @@ -21,6 +23,10 @@ import org.phoebus.pv.PV; import org.phoebus.pv.PVPool; import org.phoebus.ui.application.ContextMenuHelper; +import org.phoebus.ui.application.PhoebusApplication; +import org.phoebus.ui.docking.DockStage; +import org.phoebus.ui.focus.FocusUtility; +import org.phoebus.ui.javafx.FocusUtil; import org.phoebus.ui.javafx.JFXUtil; import org.phoebus.ui.pv.SeverityColors; import org.phoebus.ui.vtype.FormatOption; @@ -172,7 +178,8 @@ public void initialize() { SelectionService.getInstance().setSelection("Probe", List.of(new ProcessVariable(txtPVName.getText().trim()))); } - ContextMenuHelper.addSupportedEntries(txtPVName, menu); + + ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(txtAlarm), menu); menu.show(txtPVName.getScene().getWindow(), event.getScreenX(), event.getScreenY()); }); diff --git a/app/pvtable/src/main/java/org/phoebus/applications/pvtable/ui/PVTable.java b/app/pvtable/src/main/java/org/phoebus/applications/pvtable/ui/PVTable.java index e0b25f0a32..b4d448cd46 100644 --- a/app/pvtable/src/main/java/org/phoebus/applications/pvtable/ui/PVTable.java +++ b/app/pvtable/src/main/java/org/phoebus/applications/pvtable/ui/PVTable.java @@ -35,6 +35,7 @@ import org.phoebus.ui.dialog.DialogHelper; import org.phoebus.ui.dialog.NumericInputDialog; import org.phoebus.ui.dnd.DataFormats; +import org.phoebus.ui.focus.FocusUtility; import org.phoebus.ui.javafx.PrintAction; import org.phoebus.ui.javafx.Screenshot; import org.phoebus.ui.javafx.ToolbarHelper; @@ -82,6 +83,8 @@ import javafx.scene.paint.Color; import javafx.util.converter.DefaultStringConverter; +import static org.phoebus.ui.application.PhoebusApplication.logger; + /** PV Table and its toolbar * @author Kay Kasemir @@ -689,7 +692,7 @@ private void createContextMenu() } // Add PV entries - if (ContextMenuHelper.addSupportedEntries(table, menu)) + if (ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(table), menu)) menu.getItems().add(new SeparatorMenuItem()); menu.getItems().add(new PrintAction(this)); diff --git a/app/pvtree/src/main/java/org/phoebus/applications/pvtree/ui/FXTree.java b/app/pvtree/src/main/java/org/phoebus/applications/pvtree/ui/FXTree.java index c9bc60fbcf..030c02b95b 100644 --- a/app/pvtree/src/main/java/org/phoebus/applications/pvtree/ui/FXTree.java +++ b/app/pvtree/src/main/java/org/phoebus/applications/pvtree/ui/FXTree.java @@ -28,6 +28,7 @@ import org.phoebus.ui.application.ContextMenuHelper; import org.phoebus.ui.application.ContextMenuService; import org.phoebus.ui.application.SaveSnapshotAction; +import org.phoebus.ui.focus.FocusUtility; import org.phoebus.ui.javafx.PrintAction; import org.phoebus.ui.javafx.Screenshot; import org.phoebus.ui.javafx.TreeHelper; @@ -137,7 +138,8 @@ private void createContextMenu() tree_view.setOnContextMenuRequested(event -> { menu.getItems().clear(); - if (ContextMenuHelper.addSupportedEntries(tree_view, menu)) + + if (ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(tree_view), menu)) menu.getItems().add(new SeparatorMenuItem()); menu.getItems().add(new PrintAction(tree_view)); menu.getItems().add(new SaveSnapshotAction(tree_view)); diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java index 11587af979..b1667d607d 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/configuration/ConfigurationController.java @@ -34,6 +34,7 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.util.Callback; + import org.phoebus.applications.saveandrestore.Messages; import org.phoebus.applications.saveandrestore.SaveAndRestoreApplication; import org.phoebus.applications.saveandrestore.model.*; @@ -44,6 +45,7 @@ import org.phoebus.framework.selection.SelectionService; import org.phoebus.ui.application.ContextMenuHelper; import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog; +import org.phoebus.ui.focus.FocusUtility; import org.phoebus.ui.javafx.ImageCache; import org.phoebus.util.time.TimestampFormats; @@ -188,7 +190,8 @@ public void updateItem(String item, boolean empty) { .map(tableEntry -> new ProcessVariable(tableEntry.getPvName())) .collect(Collectors.toList()); SelectionService.getInstance().setSelection(SaveAndRestoreApplication.NAME, selectedPVList); - ContextMenuHelper.addSupportedEntries(cell, pvNameContextMenu); + + ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(cell), pvNameContextMenu); } pvNameContextMenu.show(cell, event.getScreenX(), event.getScreenY()); }); diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/BaseSnapshotTableViewController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/BaseSnapshotTableViewController.java index 730371fb1a..57b91e1c94 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/BaseSnapshotTableViewController.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/BaseSnapshotTableViewController.java @@ -30,6 +30,7 @@ import javafx.scene.input.MouseEvent; import javafx.scene.text.Font; import javafx.scene.text.Text; + import org.epics.vtype.VType; import org.phoebus.applications.saveandrestore.Messages; import org.phoebus.applications.saveandrestore.SaveAndRestoreApplication; @@ -38,6 +39,7 @@ import org.phoebus.core.types.TimeStampedProcessVariable; import org.phoebus.framework.selection.SelectionService; import org.phoebus.ui.application.ContextMenuHelper; +import org.phoebus.ui.focus.FocusUtility; import org.phoebus.util.time.TimestampFormats; import java.lang.reflect.Field; @@ -46,9 +48,12 @@ import java.time.Instant; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import static org.phoebus.ui.application.PhoebusApplication.logger; + /** * Base controller class for the snapshot table view. It handles common items (UI components, methods) needed * by all subclasses. @@ -193,7 +198,8 @@ protected void updateItem(TableEntry item, boolean empty) { contextMenu.hide(); contextMenu.getItems().clear(); SelectionService.getInstance().setSelection(SaveAndRestoreApplication.NAME, selectedPVList); - ContextMenuHelper.addSupportedEntries(this, contextMenu); + + ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(this), contextMenu); contextMenu.getItems().add(new SeparatorMenuItem()); MenuItem toggle = new MenuItem(); toggle.setText(item.readOnlyProperty().get() ? Messages.makeRestorable : Messages.makeReadOnly); diff --git a/core/ui/src/main/java/org/phoebus/ui/application/ContextMenuHelper.java b/core/ui/src/main/java/org/phoebus/ui/application/ContextMenuHelper.java index a80006efcd..58e4baebb3 100644 --- a/core/ui/src/main/java/org/phoebus/ui/application/ContextMenuHelper.java +++ b/core/ui/src/main/java/org/phoebus/ui/application/ContextMenuHelper.java @@ -41,24 +41,12 @@ public class ContextMenuHelper * after adding application specific menu entries, * to add entries based on the current selection. * - * @param parent_node Parent node, usually owner of the context menu + * @param setFocus Sets the correct focus (typically on a DockPane or Window) before running the action associated with a menu entry * @param menu Menu where selection-based entries will be added * @return true if a supported entry was added. */ - public static boolean addSupportedEntries(final Node parent_node, final ContextMenu menu) + public static boolean addSupportedEntries(Runnable setFocus, final ContextMenu menu) { - final Window window = parent_node.getScene().getWindow(); - if (! (window instanceof Stage)) - { - logger.log(Level.WARNING, "Expected 'Stage' for context menu, got " + window); - return false; - } - final Stage stage = (Stage) window; - // Assert that this window's dock pane is the active one. - // (on Mac and Linux, invoking the context menu will not - // always activate the stage) - DockStage.setActiveDockStage(stage); - final List entries = ContextMenuService.getInstance().listSupportedContextMenuEntries(); if (entries.isEmpty()) return false; @@ -72,6 +60,7 @@ public static boolean addSupportedEntries(final Node parent_node, final ContextM item.setGraphic(new ImageView(icon)); item.setOnAction(e -> { + setFocus.run(); try { List selection = new ArrayList<>(); diff --git a/core/ui/src/main/java/org/phoebus/ui/focus/FocusUtility.java b/core/ui/src/main/java/org/phoebus/ui/focus/FocusUtility.java new file mode 100644 index 0000000000..9ad58d6226 --- /dev/null +++ b/core/ui/src/main/java/org/phoebus/ui/focus/FocusUtility.java @@ -0,0 +1,36 @@ +package org.phoebus.ui.focus; + +import javafx.scene.Node; +import javafx.stage.Stage; +import javafx.stage.Window; +import org.phoebus.ui.application.PhoebusApplication; +import org.phoebus.ui.docking.DockStage; + +import java.util.logging.Level; + +/** + * A utility class which provides support for handling Focus + */ +public class FocusUtility { + + /** + * Create a Runnable which when called sets the focus on the first DockPane of the Stage hosting the provided Node + * @param node A node + * @return A Runnable to set the Focus on the first DockPane of the Stage which holds the Node + */ + public static Runnable setFocusOn(final Node node){ + { + Window window = node.getScene().getWindow(); + if (window instanceof Stage) + { + final Stage stage = (Stage) window; + return () -> DockStage.setActiveDockStage(stage); + } else + { + PhoebusApplication.logger.log(Level.WARNING, "Expected 'Stage' for context menu, got " + window); + return () -> { + }; + } + } + } +} diff --git a/core/ui/src/main/java/org/phoebus/ui/pv/PVList.java b/core/ui/src/main/java/org/phoebus/ui/pv/PVList.java index 6957ba77ce..94999d105e 100644 --- a/core/ui/src/main/java/org/phoebus/ui/pv/PVList.java +++ b/core/ui/src/main/java/org/phoebus/ui/pv/PVList.java @@ -25,6 +25,7 @@ import org.phoebus.pv.RefCountMap.ReferencedEntry; import org.phoebus.ui.application.ContextMenuHelper; import org.phoebus.ui.application.Messages; +import org.phoebus.ui.focus.FocusUtility; import org.phoebus.ui.javafx.ImageCache; import javafx.application.Platform; @@ -55,6 +56,8 @@ import org.phoebus.ui.vtype.FormatOption; import org.phoebus.ui.vtype.FormatOptionHandler; +import static org.phoebus.ui.application.PhoebusApplication.logger; + /** Table that lists PVs, their reference count etc. * @author Kay Kasemir */ @@ -187,7 +190,8 @@ private void createContextMenu() table.setOnContextMenuRequested(event -> { menu.getItems().clear(); - ContextMenuHelper.addSupportedEntries(table, menu); + + ContextMenuHelper.addSupportedEntries(FocusUtility.setFocusOn(table), menu); menu.show(table.getScene().getWindow()); }); table.setContextMenu(menu);