Merge remote-tracking branch 'origin/GP-4691_ghidragon_tab_close_actions--SQUASHED'

This commit is contained in:
Ryan Kurtz
2026-02-02 13:19:57 -05:00
3 changed files with 131 additions and 51 deletions

View File

@@ -141,10 +141,10 @@
<BR><BR>
<H3><A name="Renaming_Windows"></A>Renaming Components</H3>
<H2><A name="Renaming_Windows"></A>Renaming Components</H3>
<BLOCKQUOTE>
<P>
<b>Transient</B> components (e.g., search windows) can be renamed. Right-click on
<B>Transient</B> components (e.g., search windows) can be renamed. Right-click on
either the title bar or tab of a transient window and a popup menu item will
appear that allows you to change the title of that component. This can be useful
when you wish to better identify search results when you have performed many
@@ -153,6 +153,29 @@
</BLOCKQUOTE>
<BR><BR>
<H2><A name="Tabbed_Components"></A>Tabbed Components</H3>
<BLOCKQUOTE>
<P>
When more than one component is stacked in the same window space, the components are presented as
tabbed components. This means that only one of these components is shown at any given
time and a series of tabs, one for each component in this space, is displayed at the
bottom of the main viewing area. Clicking any of these tabs will make that tab's
corresponding component become the visible component.</P>
<P>Right-clicking on a tab will present a popup menu with the following actions:</P>
<UL>
<LI><B>Rename</B> - This action allows the user to change the short name shown in the
tab. This action is only available on transient components such as search results.</LI>
<A name="Closing_Tabs"/>
<LI><B>Close Others</B> - This action closes all other tabs. (and their
corresponding components)</LI>
<LI><B>Close Tabs to the Left</B> - This action closes all tabs to the left of the
clicked-on tab. This action is not available for the first tab.</LI>
<LI><B>Close Tabs to the Right</B> - This action closes all tabs to the right of the
clicked-on tab. This action is not available for the last tab.</LI>
</U>
</BLOCKQUOTE>
<BR><BR>
<H2><A name="Windows_Menu"></A>Windows Menu</H2>
<BLOCKQUOTE>

View File

@@ -35,7 +35,10 @@ import help.HelpService;
* is active, then this node will create a tabbedPane object to contain the active components.
*/
class ComponentNode extends Node {
private static final HelpLocation RENAME_HELP =
new HelpLocation("DockingWindows", "Renaming_Windows");
private static final HelpLocation CLOSE_HELP =
new HelpLocation("DockingWindows", "Closing_Tabs");
private ComponentPlaceholder top;
private int lastActiveTabIndex;
private List<ComponentPlaceholder> windowPlaceholders;
@@ -267,7 +270,7 @@ class ComponentNode extends Node {
comp = top.getComponent();
comp.setBorder(BorderFactory.createRaisedBevelBorder());
installRenameMenu(top, null);
installRenameMenu(top);
}
else if (count > 1) {
JTabbedPane tabbedPane =
@@ -302,6 +305,8 @@ class ComponentNode extends Node {
for (int i = 0; i < count; i++) {
ComponentPlaceholder placeholder = activeComponents.get(i);
installRenameMenu(placeholder);
DockableComponent c = placeholder.getComponent();
c.setBorder(BorderFactory.createEmptyBorder());
@@ -314,8 +319,8 @@ class ComponentNode extends Node {
DockingTabRenderer tabRenderer =
createTabRenderer(tabbedPane, placeholder, fullTitle, tabText, component);
c.installDragDropTarget(tabbedPane);
tabRenderer.installPopupMenu(createTabPopupMenu(activeComponents, placeholder));
tabbedPane.setTabComponentAt(i, tabRenderer);
Icon icon = placeholder.getIcon();
@@ -372,30 +377,64 @@ class ComponentNode extends Node {
DockingTabRenderer tabRenderer =
new DockingTabRenderer(pane, title, tabText, e -> closeTab(component));
installRenameMenu(placeholder, tabRenderer);
return tabRenderer;
}
private void installRenameMenu(ComponentPlaceholder placeholder,
DockingTabRenderer tabRenderer) {
private JPopupMenu createTabPopupMenu(List<ComponentPlaceholder> activeProviders,
ComponentPlaceholder placeholder) {
JPopupMenu menu = new JPopupMenu();
// add rename action if applicable
ComponentProvider provider = placeholder.getProvider();
if (provider.isTransient() && !provider.isSnapshot()) {
ActionListener renameAction = new RenameProviderAction(placeholder);
JMenuItem rename = createMenuItem("Rename", renameAction, RENAME_HELP);
menu.add(rename);
}
// add close others action always (we know there is more than one or we wouldn't be here)
List<ComponentPlaceholder> closeList = new ArrayList<>(activeProviders);
closeList.remove(placeholder);
menu.add(createMenuItem("Close Others", new CloseProvidersAction(closeList), CLOSE_HELP));
// add close to the left if the current provider is not first
int index = activeProviders.indexOf(placeholder);
if (index > 0) {
closeList = new ArrayList<>(activeProviders.subList(0, index));
menu.add(createMenuItem("Close Tabs to the Left", new CloseProvidersAction(closeList),
CLOSE_HELP));
}
// add close to the right if the current provider is not last
if (index < activeProviders.size() - 1) {
closeList = new ArrayList<>(activeProviders.subList(index + 1, activeProviders.size()));
menu.add(createMenuItem("Close Tabs to the Right", new CloseProvidersAction(closeList),
CLOSE_HELP));
}
return menu;
}
private static JMenuItem createMenuItem(String name, ActionListener actionListener,
HelpLocation help) {
JMenuItem menuItem = new JMenuItem(name);
menuItem.addActionListener(actionListener);
HelpService helpService = DockingWindowManager.getHelpService();
helpService.registerHelp(menuItem, help);
return menuItem;
}
private void installRenameMenu(ComponentPlaceholder placeholder) {
final ComponentProvider provider = placeholder.getProvider();
if (!provider.isTransient() || provider.isSnapshot()) {
return; // don't muck with the title of 'real' providers--only transients, like search
}
MouseAdapter listener = new RenameMouseListener(placeholder);
// for use on the header
DockableComponent dockableComponent = placeholder.getComponent();
DockableHeader header = dockableComponent.getHeader();
header.installRenameAction(listener);
// for use on the tab
if (tabRenderer != null) {
tabRenderer.installRenameAction(listener);
}
header.installRenameAction(new RenameMouseListener(placeholder));
}
@Override
@@ -601,15 +640,12 @@ class ComponentNode extends Node {
//==================================================================================================
// Inner Classes
//==================================================================================================
private static class RenameMouseListener extends MouseAdapter {
private static final HelpLocation RENAME_HELP =
new HelpLocation("DockingWindows", "Renaming_Windows");
private ComponentPlaceholder placeholder;
private ActionListener renameAction;
RenameMouseListener(ComponentPlaceholder placeholder) {
this.placeholder = placeholder;
renameAction = new RenameProviderAction(placeholder);
}
@Override
@@ -617,36 +653,57 @@ class ComponentNode extends Node {
// Note: we don't really care about the type of mouse event; we just want the location.
// (the event may not actually be a clicked event, depending on the platform)
JMenuItem menuItem = new JMenuItem("Rename");
menuItem.addActionListener(new RenameActionListener());
HelpService helpService = DockingWindowManager.getHelpService();
helpService.registerHelp(menuItem, RENAME_HELP);
JMenuItem rename = createMenuItem("Rename", renameAction, RENAME_HELP);
JPopupMenu menu = new JPopupMenu();
menu.add(menuItem);
menu.add(rename);
menu.show(e.getComponent(), e.getX(), e.getY());
}
private class RenameActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent event) {
ComponentProvider provider = placeholder.getProvider();
JComponent component = provider.getComponent();
String currentTabText = provider.getTabText();
String newName = OptionDialog.showInputSingleLineDialog(component, "Rename Tab",
"New name:", currentTabText);
if (newName == null || newName.isEmpty()) {
return; // cancelled
}
}
// If the user changes the name, then we want to replace all of the parts of the
// title with that name. We do not supply a custom subtitle, as that doesn't make
// sense in this case, but we clear it so the user's title is the only thing
// visible. This means that providers can still update the subtitle later.
provider.setCustomTitle(newName); // title on window
provider.setSubTitle(""); // part after the title
provider.setCustomTabText(newName); // text on the tab
private static class RenameProviderAction implements ActionListener {
private ComponentPlaceholder placeholder;
RenameProviderAction(ComponentPlaceholder placeHolder) {
this.placeholder = placeHolder;
}
@Override
public void actionPerformed(ActionEvent event) {
ComponentProvider provider = placeholder.getProvider();
JComponent component = provider.getComponent();
String currentTabText = provider.getTabText();
String newName = OptionDialog.showInputSingleLineDialog(component, "Rename Tab",
"New name:", currentTabText);
if (newName == null || newName.isEmpty()) {
return; // cancelled
}
// If the user changes the name, then we want to replace all of the parts of the
// title with that name. We do not supply a custom subtitle, as that doesn't make
// sense in this case, but we clear it so the user's title is the only thing
// visible. This means that providers can still update the subtitle later.
provider.setCustomTitle(newName); // title on window
provider.setSubTitle(""); // part after the title
provider.setCustomTabText(newName); // text on the tab
}
}
private static class CloseProvidersAction implements ActionListener {
private List<ComponentPlaceholder> placeholdersToClose;
public CloseProvidersAction(List<ComponentPlaceholder> placeholdersToClose) {
this.placeholdersToClose = placeholdersToClose;
}
@Override
public void actionPerformed(ActionEvent e) {
for (ComponentPlaceholder placeholder : placeholdersToClose) {
placeholder.getProvider().closeComponent();
}
}
}
}

View File

@@ -39,7 +39,7 @@ public class DockingTabRenderer extends JPanel {
private HierarchyListener hierarchyListener;
private TabContainerForwardingMouseListener forwardingListener;
private MouseListener renameListener;
private JPopupMenu popupMenu;
public DockingTabRenderer(final JTabbedPane tabbedPane, String fullTitle, String tabText,
ActionListener closeListener) {
@@ -126,8 +126,8 @@ public class DockingTabRenderer extends JPanel {
return title;
}
public void installRenameAction(MouseListener listener) {
this.renameListener = listener;
public void installPopupMenu(JPopupMenu popupMenu) {
this.popupMenu = popupMenu;
}
public void setIcon(Icon icon) {
@@ -222,7 +222,7 @@ public class DockingTabRenderer extends JPanel {
}
private boolean consumePopup(MouseEvent e) {
if (renameListener == null) {
if (popupMenu == null) {
return false;
}
@@ -230,7 +230,7 @@ public class DockingTabRenderer extends JPanel {
return false;
}
renameListener.mouseClicked(e);
popupMenu.show(e.getComponent(), e.getX(), e.getY());
return true;
}