|
1 | 1 | /******************************************************************************* |
2 | | - * Copyright (c) 2017-2018 Oak Ridge National Laboratory. |
| 2 | + * Copyright (c) 2017-2025 Oak Ridge National Laboratory. |
3 | 3 | * All rights reserved. This program and the accompanying materials |
4 | 4 | * are made available under the terms of the Eclipse Public License v1.0 |
5 | 5 | * which accompanies this distribution, and is available at |
|
18 | 18 | import java.util.concurrent.atomic.AtomicReference; |
19 | 19 | import java.util.logging.Level; |
20 | 20 |
|
21 | | -import javafx.util.Pair; |
22 | | -import org.apache.commons.lang3.tuple.MutablePair; |
23 | 21 | import org.csstudio.display.builder.model.DirtyFlag; |
24 | 22 | import org.csstudio.display.builder.model.DisplayModel; |
25 | 23 | import org.csstudio.display.builder.model.UntypedWidgetPropertyListener; |
|
51 | 49 | * |
52 | 50 | * @author Kay Kasemir |
53 | 51 | */ |
54 | | -@SuppressWarnings("nls") |
55 | 52 | public class NavigationTabsRepresentation extends RegionBaseRepresentation<NavigationTabs, NavigationTabsWidget> |
56 | 53 | { |
57 | 54 | private final DirtyFlag dirty_sizes = new DirtyFlag(); |
58 | 55 | private final DirtyFlag dirty_tabs = new DirtyFlag(); |
59 | 56 | private final DirtyFlag dirty_tab_look = new DirtyFlag(); |
60 | 57 | private final DirtyFlag dirty_active_tab = new DirtyFlag(); |
61 | | - private class SelectedNavigationTabs extends MutablePair<Integer, HashMap<Pair<Integer, String>, HashMap<String, SelectedNavigationTabs>>> { |
62 | | - public SelectedNavigationTabs(int activeTab) { |
63 | | - left = activeTab; |
64 | | - right = new HashMap<>(); // 'right' is a mapping of the form: Tab Number & Tab Name (Integer, String) -> Name of Navigator Tab Widget (String) -> Opened Tab and Sub-Tabs (SelectedNavigationTabs) |
| 58 | + |
| 59 | + // Track the "active tab" of navigation tabs _inside_ this one. |
| 60 | + // As user toggles between tabs, this will allow restoring the active tab |
| 61 | + // of nested instances, which would otherwise start over at their default tab |
| 62 | + |
| 63 | + // Tab index and name as unique identifier for a tab |
| 64 | + private static record UniqueTabID(int index, String name) {} |
| 65 | + |
| 66 | + private static class SelectedNavigationTabs |
| 67 | + { |
| 68 | + // Index of active tab in this nav tabs widget |
| 69 | + int active_tab; |
| 70 | + |
| 71 | + // Map from UniqueTabID -> ( Map from Name of child Navigator Tab Widget -> Opened Tab and Sub-Tabs ) |
| 72 | + final HashMap<UniqueTabID, |
| 73 | + HashMap<String, SelectedNavigationTabs>> child_navtab_selections = new HashMap<>(); |
| 74 | + |
| 75 | + SelectedNavigationTabs(int activeTab) |
| 76 | + { |
| 77 | + active_tab = activeTab; |
65 | 78 | } |
66 | 79 | }; |
67 | 80 | protected SelectedNavigationTabs selectedNavigationTabs = new SelectedNavigationTabs(0); |
@@ -187,7 +200,7 @@ private void activeTabChanged(final WidgetProperty<Integer> property, final Inte |
187 | 200 | dirty_active_tab.mark(); |
188 | 201 | toolkit.scheduleUpdate(this); |
189 | 202 | tab_display_listener.propertyChanged(null, null, null); |
190 | | - selectedNavigationTabs.left = tab_index; |
| 203 | + selectedNavigationTabs.active_tab = tab_index; |
191 | 204 | } |
192 | 205 |
|
193 | 206 | /** Update to the next pending display |
@@ -244,37 +257,36 @@ private synchronized void updatePendingDisplay(final JobMonitor monitor) |
244 | 257 | }); |
245 | 258 | checkCompletion(model_widget, completion, "timeout representing new content"); |
246 | 259 |
|
247 | | - int tabNumber = model_widget.propActiveTab().getValue(); |
248 | | - String tabName = model_widget.propTabs().getValue().get(model_widget.propActiveTab().getValue()).name().getValue(); |
249 | | - Pair<Integer, String> tabNumberAndTabName = new Pair<>(tabNumber, tabName); |
| 260 | + final List<TabProperty> tabs = model_widget.propTabs().getValue(); |
| 261 | + final int tabNumber = Math.min(model_widget.propActiveTab().getValue(), tabs.size()-1); |
| 262 | + final String tabName = tabs.get(tabNumber).name().getValue(); |
| 263 | + final UniqueTabID tab_id = new UniqueTabID(tabNumber, tabName); |
250 | 264 |
|
251 | | - if (!selectedNavigationTabs.right.containsKey(tabNumberAndTabName)) { |
252 | | - selectedNavigationTabs.right.put(tabNumberAndTabName, new HashMap<>()); |
253 | | - } |
254 | | - HashMap<String, SelectedNavigationTabs> selectedNavigationTabsHashMapForCurrentTab = selectedNavigationTabs.right.get(tabNumberAndTabName); |
255 | | - |
256 | | - new_model.getChildren() |
257 | | - .stream() |
258 | | - .filter(widget -> widget instanceof NavigationTabsWidget) |
259 | | - .forEach(widget -> { |
260 | | - NavigationTabsWidget nestedNavigationTabsWidget = (NavigationTabsWidget) widget; |
261 | | - NavigationTabsRepresentation nestedNavigationTabsRepresentation = (NavigationTabsRepresentation) nestedNavigationTabsWidget.getUserData(Widget.USER_DATA_REPRESENTATION); |
262 | | - if (nestedNavigationTabsRepresentation != null) { |
263 | | - SelectedNavigationTabs nestedNavigationTabsRepresentation_selectedNavigationTabs; |
264 | | - |
265 | | - if (!selectedNavigationTabsHashMapForCurrentTab.containsKey(nestedNavigationTabsWidget.getName())) { |
266 | | - nestedNavigationTabsRepresentation_selectedNavigationTabs = new SelectedNavigationTabs(nestedNavigationTabsWidget.propActiveTab().getValue()); |
267 | | - selectedNavigationTabsHashMapForCurrentTab.put(nestedNavigationTabsWidget.getName(), nestedNavigationTabsRepresentation_selectedNavigationTabs); |
268 | | - } |
269 | | - else { |
270 | | - nestedNavigationTabsRepresentation_selectedNavigationTabs = selectedNavigationTabsHashMapForCurrentTab.get(nestedNavigationTabsWidget.getName()); |
271 | | - if (nestedNavigationTabsWidget.propTabs().size() > nestedNavigationTabsRepresentation_selectedNavigationTabs.left) { |
272 | | - nestedNavigationTabsWidget.propActiveTab().setValue(nestedNavigationTabsRepresentation_selectedNavigationTabs.left); |
273 | | - } |
274 | | - } |
275 | | - nestedNavigationTabsRepresentation.selectedNavigationTabs = nestedNavigationTabsRepresentation_selectedNavigationTabs; |
276 | | - } |
277 | | - }); |
| 265 | + // For this tab_id, create or update map of NavTabs children to their selected tab index and sub-navtabs |
| 266 | + final HashMap<String, SelectedNavigationTabs> selectedNavTabsMapForCurrentTab = selectedNavigationTabs.child_navtab_selections.computeIfAbsent(tab_id, tid -> new HashMap<>()); |
| 267 | + |
| 268 | + for (Widget w : new_model.getChildren()) |
| 269 | + if (w instanceof NavigationTabsWidget childNavTab) |
| 270 | + { |
| 271 | + final NavigationTabsRepresentation childRepr = childNavTab.getUserData(Widget.USER_DATA_REPRESENTATION); |
| 272 | + if (childRepr != null) |
| 273 | + { |
| 274 | + SelectedNavigationTabs childRepr_selectedNavigationTabs = selectedNavTabsMapForCurrentTab.get(childNavTab.getName()); |
| 275 | + if (childRepr_selectedNavigationTabs == null) |
| 276 | + { // See childNavTab for the first time? Create its SelectedNavigationTabs with its propActiveTab as start value |
| 277 | + childRepr_selectedNavigationTabs = new SelectedNavigationTabs(childNavTab.propActiveTab().getValue()); |
| 278 | + selectedNavTabsMapForCurrentTab.put(childNavTab.getName(), childRepr_selectedNavigationTabs); |
| 279 | + } |
| 280 | + // Known childNavTab? Re-select its last active tab (if valid index) |
| 281 | + else if (childRepr_selectedNavigationTabs.active_tab < childNavTab.propTabs().size()) |
| 282 | + childNavTab.propActiveTab().setValue(childRepr_selectedNavigationTabs.active_tab); |
| 283 | + |
| 284 | + // childRepr.selectedNavigationTabs was set when the childNavTabRepr got constructed, |
| 285 | + // but it's empty and its reference will be lost when we switch tabs. |
| 286 | + // Replace it with the one that's tracked by the parent navtab (this one) |
| 287 | + childRepr.selectedNavigationTabs = childRepr_selectedNavigationTabs; |
| 288 | + } |
| 289 | + } |
278 | 290 |
|
279 | 291 | model_widget.runtimePropEmbeddedModel().setValue(new_model); |
280 | 292 | } |
|
0 commit comments