From ef0432c1f97aea58f9d694731cc3b896cfd43547 Mon Sep 17 00:00:00 2001 From: Andrew Auclair Date: Fri, 26 Sep 2025 13:10:08 -0400 Subject: [PATCH 1/2] Issue #350 Persisting the size of hidden panels in toolbars. --- demo-single-app/src/basic/MainFrame.java | 3 +- .../moderndocking/api/DockingAPI.java | 2 + .../moderndocking/api/DockingStateAPI.java | 6 +++ .../api/LayoutPersistenceAPI.java | 51 +++++++++++++++---- .../internal/DockableToolbar.java | 16 ++++++ .../internal/DockedAutoHidePanel.java | 22 +++++++- .../internal/InternalRootDockingPanel.java | 16 ++++++ .../moderndocking/layouts/DockingLayouts.java | 16 ++++++ .../moderndocking/layouts/WindowLayout.java | 14 ++++- 9 files changed, 133 insertions(+), 13 deletions(-) diff --git a/demo-single-app/src/basic/MainFrame.java b/demo-single-app/src/basic/MainFrame.java index 40dd482d..fefdd685 100644 --- a/demo-single-app/src/basic/MainFrame.java +++ b/demo-single-app/src/basic/MainFrame.java @@ -42,9 +42,11 @@ of this software and associated documentation files (the "Software"), to deal import io.github.andrewauclair.moderndocking.app.LayoutsMenu; import io.github.andrewauclair.moderndocking.app.RootDockingPanel; import io.github.andrewauclair.moderndocking.app.WindowLayoutBuilder; +import io.github.andrewauclair.moderndocking.event.DockingEvent; import io.github.andrewauclair.moderndocking.event.NewFloatingFrameListener; import io.github.andrewauclair.moderndocking.exception.DockingLayoutException; import io.github.andrewauclair.moderndocking.ext.ui.DockingUI; +import io.github.andrewauclair.moderndocking.internal.DockingListeners; import io.github.andrewauclair.moderndocking.layouts.ApplicationLayout; import io.github.andrewauclair.moderndocking.layouts.DockingLayouts; import io.github.andrewauclair.moderndocking.settings.Settings; @@ -113,7 +115,6 @@ public MainFrame(File layoutFile) { Docking.initialize(this); DockingUI.initialize(); - Docking.addNewFloatingFrameListener(new NewFloatingFrameListener() { @Override public void newFrameCreated(JFrame frame, RootDockingPanelAPI root) { diff --git a/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingAPI.java b/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingAPI.java index 9889d6ff..d3ca5c58 100644 --- a/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingAPI.java +++ b/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingAPI.java @@ -526,6 +526,7 @@ public void dock(Dockable source, Dockable target, DockingRegion region, double wrapper.getParent().dock(source, region, dividerProportion); internals.getWrapper(source).setWindow(wrapper.getWindow()); + internals.getWrapper(source).setRoot(internals.getRootPanels().get(wrapper.getWindow())); DockingListeners.fireDockedEvent(source); @@ -918,6 +919,7 @@ public void autoHideDockable(Dockable dockable, ToolbarLocation location, Window // reset the window, undocking the dockable sets it to null internals.getWrapper(dockable).setWindow(window); + internals.getWrapper(dockable).setRoot(root); internals.getWrapper(dockable).setHidden(true); internalRoot.setDockableHidden(dockable, location); diff --git a/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingStateAPI.java b/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingStateAPI.java index 81369699..4a40302a 100644 --- a/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingStateAPI.java +++ b/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingStateAPI.java @@ -231,6 +231,8 @@ public void restoreWindowLayout(Window window, WindowLayout layout) { root.setDockableHidden(dockable, ToolbarLocation.WEST); root.hideHiddenPanels(); getWrapper(dockable).setHidden(true); + + root.setSlidePosition(dockable, (int) (layout.slidePosition(id) * window.getWidth())); } for (String id : layout.getEastAutoHideToolbarIDs()) { @@ -238,6 +240,8 @@ public void restoreWindowLayout(Window window, WindowLayout layout) { root.setDockableHidden(dockable, ToolbarLocation.EAST); root.hideHiddenPanels(); getWrapper(dockable).setHidden(true); + + root.setSlidePosition(dockable, (int) (layout.slidePosition(id) * window.getHeight())); } for (String id : layout.getSouthAutoHideToolbarIDs()) { @@ -245,6 +249,8 @@ public void restoreWindowLayout(Window window, WindowLayout layout) { root.setDockableHidden(dockable, ToolbarLocation.SOUTH); root.hideHiddenPanels(); getWrapper(dockable).setHidden(true); + + root.setSlidePosition(dockable, (int) (layout.slidePosition(id) * window.getHeight())); } if (layout.getMaximizedDockable() != null) { diff --git a/docking-api/src/io/github/andrewauclair/moderndocking/api/LayoutPersistenceAPI.java b/docking-api/src/io/github/andrewauclair/moderndocking/api/LayoutPersistenceAPI.java index 251a3d40..4d24b3ab 100644 --- a/docking-api/src/io/github/andrewauclair/moderndocking/api/LayoutPersistenceAPI.java +++ b/docking-api/src/io/github/andrewauclair/moderndocking/api/LayoutPersistenceAPI.java @@ -49,6 +49,7 @@ of this software and associated documentation files (the "Software"), to deal import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamConstants; @@ -68,6 +69,11 @@ public class LayoutPersistenceAPI { private final XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); private final XMLInputFactory inputFactory = XMLInputFactory.newInstance(); + private class ToolbarDockable { + String id; + double slidePosition; + }; + /** * Create a new instance of the layout persistence API * @@ -134,6 +140,11 @@ public void saveLayoutToOutputStream(final OutputStream out, final ApplicationL if (!docking.isDocked(dockable)) { DockableWrapper wrapper = DockingInternal.get(docking).getWrapper(dockable); + if (wrapper.getRoot() != null) { + int slidePosition = wrapper.getRoot().getSlidePosition(dockable); +// layout.getMainFrameLayout().siz + System.out.println("slidePosition = " + slidePosition); + } writeSimpleNodeToFile(writer, new DockingSimplePanelNode(docking, dockable.getPersistentID(), dockable.getClass().getTypeName(), "", dockable.getTitleText(), dockable.getTabText(), DockableProperties.saveProperties(wrapper))); } } @@ -309,6 +320,7 @@ private void saveLayoutToFile(XMLStreamWriter writer, WindowLayout layout, boole for (String id : layout.getSouthAutoHideToolbarIDs()) { writer.writeStartElement("dockable"); writer.writeAttribute("id", id); + writer.writeAttribute("slidePosition", String.valueOf(layout.slidePosition(id))); writer.writeEndElement(); writer.writeCharacters(NL); } @@ -512,37 +524,56 @@ private WindowLayout readLayoutFromReader(XMLStreamReader reader) throws XMLStre Point location = new Point(Integer.parseInt(locStr.substring(0, locStr.indexOf(","))), Integer.parseInt(locStr.substring(locStr.indexOf(",") + 1))); Dimension size = new Dimension(Integer.parseInt(sizeStr.substring(0, sizeStr.indexOf(","))), Integer.parseInt(sizeStr.substring(sizeStr.indexOf(",") + 1))); - java.util.List westToolbar = readToolbarFromFile(reader, "westToolbar"); - java.util.List eastToolbar = readToolbarFromFile(reader, "eastToolbar"); - java.util.List southToolbar = readToolbarFromFile(reader, "southToolbar"); + java.util.List westToolbar = readToolbarFromFile(reader, "westToolbar"); + java.util.List eastToolbar = readToolbarFromFile(reader, "eastToolbar"); + java.util.List southToolbar = readToolbarFromFile(reader, "southToolbar"); WindowLayout layout = new WindowLayout(isMainFrame, location, size, state, readNodeFromFile(reader, "layout")); - layout.setWestAutoHideToolbarIDs(westToolbar); - layout.setEastAutoHideToolbarIDs(eastToolbar); - layout.setSouthAutoHideToolbarIDs(southToolbar); + layout.setWestAutoHideToolbarIDs(westToolbar.stream().map(toolbarDockable -> toolbarDockable.id).collect(Collectors.toList())); + layout.setEastAutoHideToolbarIDs(eastToolbar.stream().map(toolbarDockable -> toolbarDockable.id).collect(Collectors.toList())); + layout.setSouthAutoHideToolbarIDs(southToolbar.stream().map(toolbarDockable -> toolbarDockable.id).collect(Collectors.toList())); + + for (ToolbarDockable dockable : westToolbar) { + layout.setSlidePosition(dockable.id, dockable.slidePosition); + } + + for (ToolbarDockable dockable : eastToolbar) { + layout.setSlidePosition(dockable.id, dockable.slidePosition); + } + + for (ToolbarDockable dockable : southToolbar) { + layout.setSlidePosition(dockable.id, dockable.slidePosition); + } layout.setMaximizedDockable(maximizedDockable); return layout; } - private java.util.List readToolbarFromFile(XMLStreamReader reader, String name) throws XMLStreamException { - List ids = new ArrayList<>(); + private java.util.List readToolbarFromFile(XMLStreamReader reader, String name) throws XMLStreamException { + List dockables = new ArrayList<>(); while (reader.hasNext()) { int next = reader.nextTag(); if (next == XMLStreamConstants.START_ELEMENT) { if (reader.getLocalName().equals("dockable")) { - ids.add(reader.getAttributeValue(null, "id")); + ToolbarDockable dockable = new ToolbarDockable(); + dockable.id = reader.getAttributeValue(null, "id"); + String slidePosition = reader.getAttributeValue(null, "slidePosition"); + + if (slidePosition != null) { + dockable.slidePosition = Double.parseDouble(slidePosition); + } + dockables.add(dockable); } } else if (next == XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals(name)) { break; } } - return ids; + return dockables; } private DockingLayoutNode readNodeFromFile(XMLStreamReader reader, String name) throws XMLStreamException { diff --git a/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockableToolbar.java b/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockableToolbar.java index 6ec53a1f..5d2d4dc5 100644 --- a/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockableToolbar.java +++ b/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockableToolbar.java @@ -326,6 +326,22 @@ public List getPersistentIDs() { .collect(Collectors.toList()); } + public int getSlidePosition(Dockable dockable) { + for (Entry entry : dockables) { + if (entry.dockable == dockable) { + return entry.panel.getSlidePosition(); + } + } + return 0; + } + + public void setSlidePosition(Dockable dockable, int position) { + for (Entry entry : dockables) { + if (entry.dockable == dockable) { + entry.panel.setSize(entry.panel.getWidth(), position); + } + } + } @Override public void componentResized(ComponentEvent e) { } diff --git a/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockedAutoHidePanel.java b/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockedAutoHidePanel.java index 5c72fa0f..e52cc6fe 100644 --- a/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockedAutoHidePanel.java +++ b/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockedAutoHidePanel.java @@ -49,6 +49,7 @@ public class DockedAutoHidePanel extends JPanel implements ComponentListener, Mo * The toolbar that contains the dockable in this auto hide panel */ private final DockableToolbar toolbar; + private final SlideBorder slideBorder; /** * Flag indicating if the panel has been configured. Configuration doesn't occur until the panel is setVisible(true) @@ -78,7 +79,7 @@ public DockedAutoHidePanel(DockingAPI docking, Dockable dockable, RootDockingPan DockableWrapper wrapper = DockingInternal.get(docking).getWrapper(dockable); DockedSimplePanel panel = new DockedSimplePanel(docking, wrapper, "", wrapper.getDisplayPanel(), false); - SlideBorder slideBorder = new SlideBorder(toolbar.getDockedLocation()); + slideBorder = new SlideBorder(toolbar.getDockedLocation()); if (toolbar.getDockedLocation() == ToolbarLocation.SOUTH) { gbc.weightx = 1.0; @@ -111,10 +112,22 @@ else if (toolbar.getDockedLocation() == ToolbarLocation.EAST) { gbc.gridx++; add(slideBorder, gbc); } + } + + @Override + public void addNotify() { + super.addNotify(); slideBorder.addMouseMotionListener(this); } + @Override + public void removeNotify() { + slideBorder.removeMouseMotionListener(this); + + super.removeNotify(); + } + @Override public void setVisible(boolean visible) { super.setVisible(visible); @@ -126,6 +139,13 @@ public void setVisible(boolean visible) { } } + public int getSlidePosition() { + if (toolbar.getDockedLocation() == ToolbarLocation.SOUTH) { + return getHeight(); + } + return getWidth(); + } + private void setLocationAndSize(int widthDifference) { Point toolbarLocation = toolbar.getLocation(); SwingUtilities.convertPointToScreen(toolbarLocation, toolbar.getParent()); diff --git a/docking-api/src/io/github/andrewauclair/moderndocking/internal/InternalRootDockingPanel.java b/docking-api/src/io/github/andrewauclair/moderndocking/internal/InternalRootDockingPanel.java index fdacc1f1..f3a07fb0 100644 --- a/docking-api/src/io/github/andrewauclair/moderndocking/internal/InternalRootDockingPanel.java +++ b/docking-api/src/io/github/andrewauclair/moderndocking/internal/InternalRootDockingPanel.java @@ -305,6 +305,22 @@ public java.util.List hiddenPersistentIDs(ToolbarLocation location) { return Collections.emptyList(); } + public int getSlidePosition(Dockable dockable) { + if (southToolbar.hasDockable(dockable)) { + return southToolbar.getSlidePosition(dockable); + } + else if (westToolbar.hasDockable(dockable)) { + return westToolbar.getSlidePosition(dockable); + } + return eastToolbar.getSlidePosition(dockable); + } + + public void setSlidePosition(Dockable dockable, int position) { + if (southToolbar.hasDockable(dockable)) { + southToolbar.setSlidePosition(dockable, position); + } + } + private void createContents() { removeAll(); diff --git a/docking-api/src/io/github/andrewauclair/moderndocking/layouts/DockingLayouts.java b/docking-api/src/io/github/andrewauclair/moderndocking/layouts/DockingLayouts.java index 33a82014..03a09810 100644 --- a/docking-api/src/io/github/andrewauclair/moderndocking/layouts/DockingLayouts.java +++ b/docking-api/src/io/github/andrewauclair/moderndocking/layouts/DockingLayouts.java @@ -36,6 +36,7 @@ of this software and associated documentation files (the "Software"), to deal import io.github.andrewauclair.moderndocking.internal.DockingInternal; import io.github.andrewauclair.moderndocking.internal.DockingPanel; import io.github.andrewauclair.moderndocking.internal.InternalRootDockingPanel; +import java.awt.Dimension; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -157,6 +158,21 @@ public static WindowLayout layoutFromRoot(DockingAPI docking, RootDockingPanelAP layout.setEastAutoHideToolbarIDs(internalRoot.getEastAutoHideToolbarIDs()); layout.setSouthAutoHideToolbarIDs(internalRoot.getSouthAutoHideToolbarIDs()); + DockingInternal internal = DockingInternal.get(docking); + + Dimension size = root.getSize(); + + for (String id : internalRoot.getWestAutoHideToolbarIDs()) { + layout.setSlidePosition(id, internalRoot.getSlidePosition(internal.getDockable(id)) / (double) size.width); + } + + for (String id : internalRoot.getEastAutoHideToolbarIDs()) { + layout.setSlidePosition(id, internalRoot.getSlidePosition(internal.getDockable(id)) / (double) size.width); + } + + for (String id : internalRoot.getSouthAutoHideToolbarIDs()) { + layout.setSlidePosition(id, internalRoot.getSlidePosition(internal.getDockable(id)) / (double) size.height); + } return layout; } diff --git a/docking-api/src/io/github/andrewauclair/moderndocking/layouts/WindowLayout.java b/docking-api/src/io/github/andrewauclair/moderndocking/layouts/WindowLayout.java index 013a5ec3..b0a712b0 100644 --- a/docking-api/src/io/github/andrewauclair/moderndocking/layouts/WindowLayout.java +++ b/docking-api/src/io/github/andrewauclair/moderndocking/layouts/WindowLayout.java @@ -27,7 +27,9 @@ of this software and associated documentation files (the "Software"), to deal import java.awt.Point; import java.awt.Window; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.swing.JDialog; import javax.swing.JFrame; @@ -49,7 +51,9 @@ public class WindowLayout { private final List westAutoHideToolbarIDs = new ArrayList<>(); private final List eastAutoHideToolbarIDs = new ArrayList<>(); private final List southAutoHideToolbarIDs = new ArrayList<>(); - + + private final Map toolbarSlidePositions = new HashMap<>(); + /** * Create a new WindowLayout from an existing root node * @@ -259,6 +263,14 @@ public List getSouthAutoHideToolbarIDs() { return southAutoHideToolbarIDs; } + public void setSlidePosition(String id, double slidePosition) { + toolbarSlidePositions.put(id, slidePosition); + } + + public double slidePosition(String id) { + return toolbarSlidePositions.getOrDefault(id, 0.0); + } + /** * Check if this window layout has information about the size and location of the window * From eb2440e427f09ffcd1a6ff8197986e663b0a59b8 Mon Sep 17 00:00:00 2001 From: Andrew Auclair Date: Sat, 25 Oct 2025 11:55:41 -0400 Subject: [PATCH 2/2] Persist the location of the auto hidden panel divider. --- .../moderndocking/internal/DockedAutoHidePanel.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockedAutoHidePanel.java b/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockedAutoHidePanel.java index e52cc6fe..56ea5ce2 100644 --- a/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockedAutoHidePanel.java +++ b/docking-api/src/io/github/andrewauclair/moderndocking/internal/DockedAutoHidePanel.java @@ -41,7 +41,8 @@ of this software and associated documentation files (the "Software"), to deal * Special JPanel used to contain a dockable within a docking toolbar */ public class DockedAutoHidePanel extends JPanel implements ComponentListener, MouseMotionListener { - /** + private final DockingAPI docking; + /** * The root that this auto hide panel belongs to */ private final RootDockingPanelAPI root; @@ -65,7 +66,8 @@ public class DockedAutoHidePanel extends JPanel implements ComponentListener, Mo * @param toolbar The toolbar this panel is in */ public DockedAutoHidePanel(DockingAPI docking, Dockable dockable, RootDockingPanelAPI root, DockableToolbar toolbar) { - this.root = root; + this.docking = docking; + this.root = root; this.toolbar = toolbar; root.addComponentListener(this); @@ -234,6 +236,8 @@ else if (toolbar.getDockedLocation() == ToolbarLocation.WEST) { else { setLocationAndSize(-e.getX()); } + + docking.getAppState().persist(); } @Override