Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] Docking anchors #260

Open
nitanmarcel opened this issue Jan 10, 2025 · 16 comments
Open

[Feature Request] Docking anchors #260

nitanmarcel opened this issue Jan 10, 2025 · 16 comments
Milestone

Comments

@nitanmarcel
Copy link

Would would this idea work:

Docking anchors will allow docks to be created dynamically no matter in what order they are creating. As a reference this discussion 247, even if Docking Region is set to WEST, SOUTH, EAST when creating docks in the order SOUTH, EAST, West the panels are arranged one after the other without respecting their docked region.

A dock anchor region will allow to enforce the starting region of a dock regarding of the order they are created

@andrewauclair
Copy link
Owner

I've thought about this as some sort of docking "placeholder" that would basically be a dockable with no title bar. Then when something is docked to it, the "placeholder" is hidden and replaced with the new dockable. When all of these dockables are closed, the original "placeholder" panel reappears. I think that is probably doable somehow in the API that I haven't prototyped yet.

What won't be possible, or something that I want to change, is how the region parameter of the dock functions is processed.

I'm going to try working out a solution. I think this idea might help address some issues I've had with defining where certain dockables should appear by default.

@nitanmarcel
Copy link
Author

I've thought about this as some sort of docking "placeholder" that would basically be a dockable with no title bar. Then when something is docked to it, the "placeholder" is hidden and replaced with the new dockable. When all of these dockables are closed, the original "placeholder" panel reappears. I think that is probably doable somehow in the API that I haven't prototyped yet.

What won't be possible, or something that I want to change, is how the region parameter of the dock functions is processed.

I'm going to try working out a solution. I think this idea might help address some issues I've had with defining where certain dockables should appear by default.

Oh the placeholder sounds like a good implementation to do this. One thing I would add is that the empty panel will be always hidden and only visible just before it being replaced with a new panel.

This raises a question. Is a panel that's set as isVisible = false removed from the UI or not rendered and the other panels expand over it?

@nitanmarcel
Copy link
Author

Looks like initializing a panel with isVisible = false does not get rid of id

image

@nitanmarcel
Copy link
Author

nitanmarcel commented Jan 11, 2025

I've thought about this as some sort of docking "placeholder" that would basically be a dockable with no title bar. Then when something is docked to it, the "placeholder" is hidden and replaced with the new dockable. When all of these dockables are closed, the original "placeholder" panel reappears. I think that is probably doable somehow in the API that I haven't prototyped yet.

What won't be possible, or something that I want to change, is how the region parameter of the dock functions is processed.

I'm going to try working out a solution. I think this idea might help address some issues I've had with defining where certain dockables should appear by default.

Also when tabbed together, the current docking implementation does not like if the target is undocked

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index 3 out of bounds for length 3
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
	at java.base/java.util.Objects.checkIndex(Objects.java:361)
	at java.base/java.util.ArrayList.get(ArrayList.java:427)
	at io.github.andrewauclair.moderndocking.internal.DockedTabbedPanel.stateChanged(DockedTabbedPanel.java:424)
	at java.desktop/javax.swing.JTabbedPane.fireStateChanged(JTabbedPane.java:445)
	at java.desktop/javax.swing.JTabbedPane$ModelListener.stateChanged(JTabbedPane.java:296)
	at java.desktop/javax.swing.DefaultSingleSelectionModel.fireStateChanged(DefaultSingleSelectionModel.java:148)
	at java.desktop/javax.swing.DefaultSingleSelectionModel.setSelectedIndex(DefaultSingleSelectionModel.java:79)
	at java.desktop/javax.swing.JTabbedPane.setSelectedIndexImpl(JTabbedPane.java:649)
	at java.desktop/javax.swing.JTabbedPane.setSelectedIndex(JTabbedPane.java:624)
	at java.desktop/javax.swing.plaf.basic.BasicTabbedPaneUI$Handler.mousePressed(BasicTabbedPaneUI.java:4101)
	at com.formdev.flatlaf.ui.FlatTabbedPaneUI$Handler.mousePressed(FlatTabbedPaneUI.java:2995)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
	at java.desktop/java.awt.Component.processMouseEvent(Component.java:6623)
	at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3389)
	at java.desktop/java.awt.Component.processEvent(Component.java:6391)
	at java.desktop/java.awt.Container.processEvent(Container.java:2266)
	at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5001)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4572)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
	at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
class EditorWindow : Window() {
    private val controller: EditorWindowController = EditorWindowController()

    private lateinit var consolePanelDock: ConsolePanelDock
    private lateinit var treePanelDock: TreePanelDock
    private lateinit var placeholderPanelDock: PlaceholderPanelDock

    override fun createPanel(): JPanel? {
        return null
    }

    override fun windowInit(stage: WindowStage) {
        if (stage != WindowStage.POST)
            return
        initDocking()
        createPlaceholderPanel()
        createConsolePanel()
        createTreePanel()
        createEditorPanel()
    }

    fun initDocking() {
        Docking.initialize(this)
        DockingUI.initialize()
        val rootDockingPanel = RootDockingPanel(this)
        add(rootDockingPanel)
    }

    fun createPlaceholderPanel() {
        placeholderPanelDock = PlaceholderPanelDock()
        Docking.dock(placeholderPanelDock.register(), this, DockingRegion.WEST)
    }


    fun createEditorPanel() {
        val e1 = EditorPanelDock("e1")
        val e2 = EditorPanelDock("e2")
        val e3 = EditorPanelDock("e3")

        Docking.dock(e1.register(), placeholderPanelDock, DockingRegion.CENTER)
        Docking.dock(e2.register(), e1, DockingRegion.CENTER)
        Docking.dock(e3.register(), e2, DockingRegion.CENTER)
        placeholderPanelDock.requestClose()
        Docking.undock(placeholderPanelDock)
    }

    fun createConsolePanel() {
        consolePanelDock = ConsolePanelDock()
        Docking.dock(consolePanelDock.register(), this, DockingRegion.SOUTH)
    }

    fun createTreePanel() {
        treePanelDock = TreePanelDock()
        Docking.dock(treePanelDock.register(), this, DockingRegion.EAST)
    }

    override fun pack() {}
}

@nitanmarcel
Copy link
Author

I've thought about this as some sort of docking "placeholder" that would basically be a dockable with no title bar. Then when something is docked to it, the "placeholder" is hidden and replaced with the new dockable. When all of these dockables are closed, the original "placeholder" panel reappears. I think that is probably doable somehow in the API that I haven't prototyped yet.
What won't be possible, or something that I want to change, is how the region parameter of the dock functions is processed.
I'm going to try working out a solution. I think this idea might help address some issues I've had with defining where certain dockables should appear by default.

Also when tabbed together, the current docking implementation does not like if the target is undocked

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index 3 out of bounds for length 3
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
	at java.base/java.util.Objects.checkIndex(Objects.java:361)
	at java.base/java.util.ArrayList.get(ArrayList.java:427)
	at io.github.andrewauclair.moderndocking.internal.DockedTabbedPanel.stateChanged(DockedTabbedPanel.java:424)
	at java.desktop/javax.swing.JTabbedPane.fireStateChanged(JTabbedPane.java:445)
	at java.desktop/javax.swing.JTabbedPane$ModelListener.stateChanged(JTabbedPane.java:296)
	at java.desktop/javax.swing.DefaultSingleSelectionModel.fireStateChanged(DefaultSingleSelectionModel.java:148)
	at java.desktop/javax.swing.DefaultSingleSelectionModel.setSelectedIndex(DefaultSingleSelectionModel.java:79)
	at java.desktop/javax.swing.JTabbedPane.setSelectedIndexImpl(JTabbedPane.java:649)
	at java.desktop/javax.swing.JTabbedPane.setSelectedIndex(JTabbedPane.java:624)
	at java.desktop/javax.swing.plaf.basic.BasicTabbedPaneUI$Handler.mousePressed(BasicTabbedPaneUI.java:4101)
	at com.formdev.flatlaf.ui.FlatTabbedPaneUI$Handler.mousePressed(FlatTabbedPaneUI.java:2995)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
	at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
	at java.desktop/java.awt.Component.processMouseEvent(Component.java:6623)
	at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3389)
	at java.desktop/java.awt.Component.processEvent(Component.java:6391)
	at java.desktop/java.awt.Container.processEvent(Container.java:2266)
	at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5001)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4572)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
	at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
class EditorWindow : Window() {
    private val controller: EditorWindowController = EditorWindowController()

    private lateinit var consolePanelDock: ConsolePanelDock
    private lateinit var treePanelDock: TreePanelDock
    private lateinit var placeholderPanelDock: PlaceholderPanelDock

    override fun createPanel(): JPanel? {
        return null
    }

    override fun windowInit(stage: WindowStage) {
        if (stage != WindowStage.POST)
            return
        initDocking()
        createPlaceholderPanel()
        createConsolePanel()
        createTreePanel()
        createEditorPanel()
    }

    fun initDocking() {
        Docking.initialize(this)
        DockingUI.initialize()
        val rootDockingPanel = RootDockingPanel(this)
        add(rootDockingPanel)
    }

    fun createPlaceholderPanel() {
        placeholderPanelDock = PlaceholderPanelDock()
        Docking.dock(placeholderPanelDock.register(), this, DockingRegion.WEST)
    }


    fun createEditorPanel() {
        val e1 = EditorPanelDock("e1")
        val e2 = EditorPanelDock("e2")
        val e3 = EditorPanelDock("e3")

        Docking.dock(e1.register(), placeholderPanelDock, DockingRegion.CENTER)
        Docking.dock(e2.register(), e1, DockingRegion.CENTER)
        Docking.dock(e3.register(), e2, DockingRegion.CENTER)
        placeholderPanelDock.requestClose()
        Docking.undock(placeholderPanelDock)
    }

    fun createConsolePanel() {
        consolePanelDock = ConsolePanelDock()
        Docking.dock(consolePanelDock.register(), this, DockingRegion.SOUTH)
    }

    fun createTreePanel() {
        treePanelDock = TreePanelDock()
        Docking.dock(treePanelDock.register(), this, DockingRegion.EAST)
    }

    override fun pack() {}
}

I did found that this works

        Docking.dock(e1.register(), placeholderPanelDock, DockingRegion.EAST)
        Docking.dock(e2.register(), e1, DockingRegion.CENTER)
        Docking.dock(e3.register(), e2, DockingRegion.CENTER)
        Docking.undock(placeholderPanelDock)

@andrewauclair
Copy link
Owner

How did you remove it?

@nitanmarcel
Copy link
Author

How did you remove it?

Here's the code I came up so far. Also posted it in the discussion we had

class EditorWindowController(): WindowController() {
    private val editors: MutableList<EditorPanelDock> = mutableListOf<EditorPanelDock>()
    val placeholderPanelDock: PlaceholderPanelDock = PlaceholderPanelDock()

    fun addEditor(id: String) {
        val editor = EditorPanelDock(id)

        editor.setOnCloseHandler {
            removeEditor(id)
        }

        if (editors.isEmpty())
        {
            Docking.dock(editor.register(), placeholderPanelDock, DockingRegion.EAST)
            Docking.undock(placeholderPanelDock)
        }
        else {
            Docking.dock(editor.register(), editors.last(), DockingRegion.CENTER)
        }
        editors.add(editor)
    }

    fun removeEditor(id: String) {
        val editor = editors.lastOrNull { it.persistentID == id }
        if (editor != null)
        {
            if (editors.size == 1)
                createPlaceholderPanel(editors.last())
            editors.remove(editor)
            Docking.undock(editor)
        }
    }

    fun createPlaceholderPanel(target: java.awt.Window) {
        Docking.dock(placeholderPanelDock.register(), target, DockingRegion.WEST)
    }
    fun createPlaceholderPanel(target: Dockable) {
        Docking.dock(placeholderPanelDock.register(), target, DockingRegion.WEST)
    }
}

The issue comes when re-adding the placeholder and closing the last window again

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
	at java.base/java.util.Objects.requireNonNull(Objects.java:209)
	at io.github.andrewauclair.moderndocking.api.DockingAPI.undock(DockingAPI.java:588)
	at io.github.andrewauclair.moderndocking.ui.HeaderController.close(HeaderController.java:106)
	at io.github.andrewauclair.moderndocking.ui.DefaultHeaderUI.lambda$init$2(DefaultHeaderUI.java:126)
	at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972)
	at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2313)
	at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
	at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
	at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
	at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:297)
	at java.desktop/java.awt.Component.processMouseEvent(Component.java:6626)
	at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3389)
	at java.desktop/java.awt.Component.processEvent(Component.java:6391)
	at java.desktop/java.awt.Container.processEvent(Container.java:2266)
	at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5001)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
	at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948)
	at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4575)
	at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516)
	at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
	at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
	at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
	at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Screencast.From.2025-01-11.14-37-25.webm

@nitanmarcel
Copy link
Author

Also instead of having empty panels, or anchors as I said. Wouldn't be better to let the user create those panels and just have a new api Docking.replace?

@andrewauclair
Copy link
Owner

This raises a question. Is a panel that's set as isVisible = false removed from the UI or not rendered and the other panels expand over it?

I'm honestly not sure, but a dockable will always be visible because ModernDocking uses another JPanel internally to wrap the panel from the application.

@andrewauclair
Copy link
Owner

Also instead of having empty panels, or anchors as I said. Wouldn't be better to let the user create those panels and just have a new api Docking.replace?

That makes me assume the user needs to know when the placeholder is docked.

@nitanmarcel
Copy link
Author

btw, I managed to remove and recreate dockables without triggering the null pointer excteption. The idea is to dock the source to the target that needs to be removed after in the center region.

    fun dockingReplace(source: Dockable, target: Dockable, region: DockingRegion) {
        Docking.dock(source, target, DockingRegion.CENTER)
        Docking.undock(target)
    }

@nitanmarcel
Copy link
Author

Also instead of having empty panels, or anchors as I said. Wouldn't be better to let the user create those panels and just have a new api Docking.replace?

This way could also help prototyping, as in creating a prototype layout of how the final layout would look like then have the ability to dinami

This raises a question. Is a panel that's set as isVisible = false removed from the UI or not rendered and the other panels expand over it?

I'm honestly not sure, but a dockable will always be visible because ModernDocking uses another JPanel internally to wrap the panel from the application.

It kept the dockable while removing the title and probably the content, I haven't checked this. Anyway the frame

Also instead of having empty panels, or anchors as I said. Wouldn't be better to let the user create those panels and just have a new api Docking.replace?

That makes me assume the user needs to know when the placeholder is docked.

Oh that's true too. Would be like, you have a dock in one of the regions and you can use that as a reference for other docks, which would probably allow for better placement, not sure to what extend. The way I see this a good thing to have is where you have a non floating/closable panel with a main content, or empty

@andrewauclair
Copy link
Owner

I'm not sure why you're calling requestClose. This function is intended to only be called by ModernDocking to ask the panel if it's ok to be closed. Most panels will simply return true, others might prompt the user if they want to close the panel and return that result. The function should not try to actually close the panel its self.

@nitanmarcel
Copy link
Author

I'm not sure why you're calling requestClose. This function is intended to only be called by ModernDocking to ask the panel if it's ok to be closed. Most panels will simply return true, others might prompt the user if they want to close the panel and return that result. The function should not try to actually close the panel its self.

Ah ignore that. It was there since I was testing ways to close the panel.

This is the actual method I ended up with

#260 (comment)

@andrewauclair
Copy link
Owner

anchor-test

Here's what I've experimented with so far. The anchor is a Dockable which is registered through a new Docking.registerDockingAnchor method. Once the anchor is registered, it can be used just the same as any other dockable as a source or target. A whole new set of internal classes and methods is required for this, but requires very minimal changes for an application, which is really nice. I have not tackled persistence yet or using the anchors in more complicated things like window layouts (maybe they would work without changes, I haven't tried it yet).

The internal panels all remember which anchor they were docked to and pick up this value when docked to other panels that remember their anchor. In the gif you can see that I initially docked three to the anchor, then two to three and one to two. Even though I closed them in the reverse order, the anchor was properly restored after they were all closed. I think there's still more edge cases I have to consider such as the pinning features.

public class Anchor extends JPanel implements Dockable {
    public Anchor() {
        setBorder(BorderFactory.createLineBorder(Color.RED));

        add(new JLabel("This is an anchor"));
    }

    @Override
    public String getPersistentID() {
        return "anchor";
    }

    @Override
    public String getTabText() {
        return "";
    }
}
Anchor anchor = new Anchor();

Docking.registerDockingAnchor(anchor);

Docking.dock(anchor, one, DockingRegion.EAST);

andrewauclair added a commit that referenced this issue Jan 13, 2025
Some experimental changes for adding docking anchors to the framework. There are still a lot of edge cases to consider and persistence hasn't been invested yet.
@nitanmarcel
Copy link
Author

anchor-test anchor-test

Here's what I've experimented with so far. The anchor is a Dockable which is registered through a new Docking.registerDockingAnchor method. Once the anchor is registered, it can be used just the same as any other dockable as a source or target. A whole new set of internal classes and methods is required for this, but requires very minimal changes for an application, which is really nice. I have not tackled persistence yet or using the anchors in more complicated things like window layouts (maybe they would work without changes, I haven't tried it yet).

The internal panels all remember which anchor they were docked to and pick up this value when docked to other panels that remember their anchor. In the gif you can see that I initially docked three to the anchor, then two to three and one to two. Even though I closed them in the reverse order, the anchor was properly restored after they were all closed. I think there's still more edge cases I have to consider such as the pinning features.

public class Anchor extends JPanel implements Dockable {
    public Anchor() {
        setBorder(BorderFactory.createLineBorder(Color.RED));

        add(new JLabel("This is an anchor"));
    }

    @Override
    public String getPersistentID() {
        return "anchor";
    }

    @Override
    public String getTabText() {
        return "";
    }
}
Anchor anchor = new Anchor();

Docking.registerDockingAnchor(anchor);

Docking.dock(anchor, one, DockingRegion.EAST);

Wow. I must agree that this looks amazing at the first glance

@andrewauclair andrewauclair added this to the 1.1 milestone Jan 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants