diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/JFXStageRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/JFXStageRepresentation.java index af9ad164dc..84a10eba16 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/JFXStageRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/JFXStageRepresentation.java @@ -49,7 +49,6 @@ public JFXStageRepresentation(final Stage stage) public Parent configureStage(final DisplayModel model, final Consumer close_request_handler) { final String name = model.getDisplayName(); - stage.setTitle(name); // The following trick is necessary because keys cannot be longer than 80 characters. final String hexName = Integer.toHexString(name.hashCode()).toLowerCase(); diff --git a/core/ui/src/main/java/org/phoebus/ui/application/Messages.java b/core/ui/src/main/java/org/phoebus/ui/application/Messages.java index 4cc7451f2a..190de976b1 100644 --- a/core/ui/src/main/java/org/phoebus/ui/application/Messages.java +++ b/core/ui/src/main/java/org/phoebus/ui/application/Messages.java @@ -184,6 +184,7 @@ public class Messages public static String WebBrowser; public static String Welcome; public static String Window; + public static String WindowTitleFormat; static { diff --git a/core/ui/src/main/java/org/phoebus/ui/application/PhoebusApplication.java b/core/ui/src/main/java/org/phoebus/ui/application/PhoebusApplication.java index 5108576c30..a3466b3f33 100644 --- a/core/ui/src/main/java/org/phoebus/ui/application/PhoebusApplication.java +++ b/core/ui/src/main/java/org/phoebus/ui/application/PhoebusApplication.java @@ -80,11 +80,7 @@ import org.phoebus.ui.dialog.DialogHelper; import org.phoebus.ui.dialog.ListPickerDialog; import org.phoebus.ui.dialog.OpenFileDialog; -import org.phoebus.ui.docking.DockItem; -import org.phoebus.ui.docking.DockItemWithInput; -import org.phoebus.ui.docking.DockPane; -import org.phoebus.ui.docking.DockPaneListener; -import org.phoebus.ui.docking.DockStage; +import org.phoebus.ui.docking.*; import org.phoebus.ui.help.OpenAbout; import org.phoebus.ui.help.OpenHelp; import org.phoebus.ui.internal.MementoHelper; @@ -547,6 +543,8 @@ private void startUI(final MementoTree memento, final JobMonitor monitor) throws closeMainStage(); }); + StageTitleManager.bindStageTitlesToActiveTabs(main_stage); + DockPane.addListener(dock_pane_listener); DockPane.setActiveDockPane(DockStage.getDockPanes(main_stage).get(0)); monitor.done(); diff --git a/core/ui/src/main/java/org/phoebus/ui/docking/DockPane.java b/core/ui/src/main/java/org/phoebus/ui/docking/DockPane.java index ea05ada189..8e2475c0bd 100644 --- a/core/ui/src/main/java/org/phoebus/ui/docking/DockPane.java +++ b/core/ui/src/main/java/org/phoebus/ui/docking/DockPane.java @@ -520,11 +520,6 @@ private void doAutoHideTabs(final Scene scene) throw new IllegalStateException("Expected DockItem, got " + tab); stage.titleProperty().bind(((DockItem)tab).labelTextProperty()); } - else - { // Fixed title - stage.titleProperty().unbind(); - stage.setTitle(Messages.FixedTitle); - } } /** @param tabs One or more tabs to add */ @@ -533,6 +528,8 @@ public void addTab(final DockItem... tabs) getTabs().addAll(tabs); // Select the newly added tab getSelectionModel().select(getTabs().size()-1); + // Set this as the active dock pane + setActiveDockPane(this); } /** @return All {@link DockItem}s in this pane (safe copy) */ diff --git a/core/ui/src/main/java/org/phoebus/ui/docking/DockStage.java b/core/ui/src/main/java/org/phoebus/ui/docking/DockStage.java index 4207028c26..a88fe69c68 100644 --- a/core/ui/src/main/java/org/phoebus/ui/docking/DockStage.java +++ b/core/ui/src/main/java/org/phoebus/ui/docking/DockStage.java @@ -164,7 +164,6 @@ else if(layout.getChildren().get(0) instanceof SplitPane){ final Scene scene = new Scene(layout, geometry.width, geometry.height); stage.setScene(scene); - stage.setTitle(Messages.FixedTitle); // Set position stage.setX(geometry.x); stage.setY(geometry.y); diff --git a/core/ui/src/main/java/org/phoebus/ui/docking/SplitDock.java b/core/ui/src/main/java/org/phoebus/ui/docking/SplitDock.java index f8482cd417..d7949e471d 100644 --- a/core/ui/src/main/java/org/phoebus/ui/docking/SplitDock.java +++ b/core/ui/src/main/java/org/phoebus/ui/docking/SplitDock.java @@ -184,6 +184,16 @@ else if (dock_parent instanceof SplitPane) { // "dock_parent instanceof SplitPan return; } + // If the dock pane that has just been merged is active, + // switch the active dock pane to the first pane found in the other child + if (empty_dock == DockPane.getActiveDockPane()) { + if (child instanceof DockPane dockPane) { + DockPane.setActiveDockPane(dockPane); + } else if (child instanceof SplitDock splitDock) { + splitDock.findAndActivateDockPane(); + } + } + // Tell child about its new dock_parent if (child instanceof DockPane) ((DockPane)child).setDockParent(dock_parent); @@ -261,6 +271,18 @@ private boolean isEmptyDock(final Node item) return false; } + private void findAndActivateDockPane() { + // switch the active dock pane to the first pane found in this split or in any nested split + for (Node child : getItems()) { + if (child instanceof DockPane dockPane) { + DockPane.setActiveDockPane(dockPane); + return; + } else if (child instanceof SplitDock splitDock) { + splitDock.findAndActivateDockPane(); + } + } + } + @Override public String toString() { diff --git a/core/ui/src/main/java/org/phoebus/ui/docking/StageTitleManager.java b/core/ui/src/main/java/org/phoebus/ui/docking/StageTitleManager.java new file mode 100644 index 0000000000..c90a6c3f25 --- /dev/null +++ b/core/ui/src/main/java/org/phoebus/ui/docking/StageTitleManager.java @@ -0,0 +1,68 @@ +package org.phoebus.ui.docking; + +import javafx.beans.binding.Bindings; +import javafx.beans.binding.StringExpression; +import javafx.stage.Stage; +import org.phoebus.ui.application.Messages; + +import java.util.List; +import java.util.logging.Logger; + +/** + * Helper class to manage the window title. + */ +public class StageTitleManager { + + private static final DockPaneListener listener = StageTitleManager::setStageTitleToTabTitle; + + // This class is only a static utility, so it should not be instantiated. + private StageTitleManager() {} + + public static void bindStageTitlesToActiveTabs(Stage mainStage) { + // this will listen to all tab focus events and set the stage title + // of the stage in which the tab is located to the formatted title. + DockPane.addListener(listener); + + // This listener is passed down to all child panes when a split occurs, + // so it fires when any pane in the main stage becomes empty. + // Therefore, we have to check if all panes are empty. + DockStage.getDockPanes(mainStage).get(0).addDockPaneEmptyListener(() -> { + List panes = DockStage.getDockPanes(mainStage); + for (DockPane pane : panes) { + if (!pane.getDockItems().isEmpty()) { + // If any pane has items, we don't need to set the title to default + return; + } + } + // If all panes are empty, set the stage title to the default + mainStage.titleProperty().unbind(); + setStageTitleToDefault(mainStage); + }); + } + + private static void setStageTitleToTabTitle(DockItem dockItem) { + if (dockItem == null) { + return; + } + DockPane pane = dockItem.getDockPane(); + // I don't think this is ever true, + // but better to have it and not need it than the other way around + if (pane == null) { + return; + } + pane.deferUntilInScene(scene -> { + if (scene.getWindow() instanceof Stage stage) { + if (DockPane.getActiveDockPane() != pane) { + // If the dock pane is not active, don't do anything + return; + } + StringExpression exp = Bindings.format(Messages.WindowTitleFormat, dockItem.labelTextProperty()); + stage.titleProperty().bind(exp); + } + }); + } + + private static void setStageTitleToDefault(Stage stage) { + stage.setTitle(Messages.FixedTitle); + } +} diff --git a/core/ui/src/main/resources/org/phoebus/ui/application/messages.properties b/core/ui/src/main/resources/org/phoebus/ui/application/messages.properties index 5582505f2a..5149546077 100644 --- a/core/ui/src/main/resources/org/phoebus/ui/application/messages.properties +++ b/core/ui/src/main/resources/org/phoebus/ui/application/messages.properties @@ -170,3 +170,4 @@ UnsavedChanges_wouldYouLikeToSaveAnyChangesBeforeReplacingTheLayout=Would you li WebBrowser=Web Browser Welcome=Welcome Window=Window +WindowTitleFormat=CS-Studio: %s diff --git a/core/ui/src/main/resources/org/phoebus/ui/application/messages_fr.properties b/core/ui/src/main/resources/org/phoebus/ui/application/messages_fr.properties index c4f660bf6a..49bfc08ca2 100644 --- a/core/ui/src/main/resources/org/phoebus/ui/application/messages_fr.properties +++ b/core/ui/src/main/resources/org/phoebus/ui/application/messages_fr.properties @@ -168,3 +168,4 @@ UnsavedChanges_wouldYouLikeToSaveAnyChangesBeforeReplacingTheLayout=Voulez-vous WebBrowser=Navigateur Web Welcome=Bienvenue Window=Fen\u00EAtre +WindowTitleFormat=CS-Studio: %s