diff --git a/demo-single-app/src/basic/MainFrame.java b/demo-single-app/src/basic/MainFrame.java index fefdd685..8f9b3208 100644 --- a/demo-single-app/src/basic/MainFrame.java +++ b/demo-single-app/src/basic/MainFrame.java @@ -31,6 +31,7 @@ of this software and associated documentation files (the "Software"), to deal import io.github.andrewauclair.moderndocking.DockableStyle; import io.github.andrewauclair.moderndocking.DockableTabPreference; import io.github.andrewauclair.moderndocking.DockingRegion; +import io.github.andrewauclair.moderndocking.Property; import io.github.andrewauclair.moderndocking.api.RootDockingPanelAPI; import io.github.andrewauclair.moderndocking.api.WindowLayoutBuilderAPI; import io.github.andrewauclair.moderndocking.app.AppState; @@ -42,19 +43,19 @@ 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.layouts.DynamicDockableCreationListener; import io.github.andrewauclair.moderndocking.settings.Settings; import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.io.File; +import java.util.Map; import java.util.Objects; import java.util.Random; import java.util.concurrent.Callable; @@ -72,359 +73,365 @@ of this software and associated documentation files (the "Software"), to deal import picocli.CommandLine; public class MainFrame extends JFrame implements Callable { - private final File layoutFile; - @CommandLine.Option(names = "--laf", defaultValue = "light", description = "look and feel to use. one of: system, light, dark, github-dark or solarized-dark") - String lookAndFeel; + private final File layoutFile; + @CommandLine.Option(names = "--laf", defaultValue = "light", description = "look and feel to use. one of: system, light, dark, github-dark or solarized-dark") + String lookAndFeel; - @CommandLine.Option(names = "--enable-edt-violation-detector", defaultValue = "false", description = "enable the Event Dispatch Thread (EDT) violation checker") - boolean edtViolationDetector; + @CommandLine.Option(names = "--enable-edt-violation-detector", defaultValue = "false", description = "enable the Event Dispatch Thread (EDT) violation checker") + boolean edtViolationDetector; - @CommandLine.Option(names = "--ui-scale", defaultValue = "1", description = "scale to use for the FlatLaf.uiScale value") - int uiScale; + @CommandLine.Option(names = "--ui-scale", defaultValue = "1", description = "scale to use for the FlatLaf.uiScale value") + int uiScale; - @CommandLine.Option(names = "--always-use-tabs", defaultValue = "false", description = "always use tabs, even when there is only 1 dockable in the tab group") - boolean alwaysUseTabs; + @CommandLine.Option(names = "--always-use-tabs", defaultValue = "false", description = "always use tabs, even when there is only 1 dockable in the tab group") + boolean alwaysUseTabs; - @CommandLine.Option(names = "--tab-location", defaultValue = "NONE", description = "Location to display tabs. values: ${COMPLETION-CANDIDATES}") - DockableTabPreference tabLocation; + @CommandLine.Option(names = "--tab-location", defaultValue = "NONE", description = "Location to display tabs. values: ${COMPLETION-CANDIDATES}") + DockableTabPreference tabLocation; - @CommandLine.Option(names = "--create-docking-instance", defaultValue = "false", description = "create a separate instance of the framework for this MainFrame") - boolean createDockingInstance; + @CommandLine.Option(names = "--create-docking-instance", defaultValue = "false", description = "create a separate instance of the framework for this MainFrame") + boolean createDockingInstance; - public MainFrame(File layoutFile) { - this.layoutFile = layoutFile; + public MainFrame(File layoutFile) { + this.layoutFile = layoutFile; - setSize(800, 600); + setSize(800, 600); - setTitle("Modern Docking Basic Demo"); + setTitle("Modern Docking Basic Demo"); - if (alwaysUseTabs) { - if (tabLocation == DockableTabPreference.TOP) { - Settings.setDefaultTabPreference(DockableTabPreference.TOP_ALWAYS); - } - else { - Settings.setDefaultTabPreference(DockableTabPreference.BOTTOM_ALWAYS); - } - } - else { - Settings.setDefaultTabPreference(tabLocation); - } + if (alwaysUseTabs) { + if (tabLocation == DockableTabPreference.TOP) { + Settings.setDefaultTabPreference(DockableTabPreference.TOP_ALWAYS); + } else { + Settings.setDefaultTabPreference(DockableTabPreference.BOTTOM_ALWAYS); + } + } else { + Settings.setDefaultTabPreference(tabLocation); + } - Settings.setActiveHighlighterEnabled(false); + Settings.setActiveHighlighterEnabled(false); - Docking.initialize(this); - DockingUI.initialize(); + Docking.initialize(this); + DockingUI.initialize(); - Docking.addNewFloatingFrameListener(new NewFloatingFrameListener() { - @Override - public void newFrameCreated(JFrame frame, RootDockingPanelAPI root) { - frame.setTitle("Testing New Floating Frame Listener"); - } + Docking.addNewFloatingFrameListener(new NewFloatingFrameListener() { + @Override + public void newFrameCreated(JFrame frame, RootDockingPanelAPI root) { + frame.setTitle("Testing New Floating Frame Listener"); + } + + @Override + public void newFrameCreated(JFrame frame, RootDockingPanelAPI root, Dockable dockable) { + frame.setTitle("Testing New Floating Frame Listener"); + } + }); - @Override - public void newFrameCreated(JFrame frame, RootDockingPanelAPI root, Dockable dockable) { - frame.setTitle("Testing New Floating Frame Listener"); - } - }); + Docking.setUserDynamicDockableCreationListener(new DynamicDockableCreationListener() { + @Override + public Dockable createDockable(String persistentID, String className, String titleText, String tabText, Map properties) { + SimplePanel test = new SimplePanel("this was custom", "user dynamic dockable creation", persistentID); + return test; + } + }); + JMenuBar menuBar = new JMenuBar(); + setJMenuBar(menuBar); - JMenuBar menuBar = new JMenuBar(); - setJMenuBar(menuBar); - - JMenu file = new JMenu("File"); - menuBar.add(file); - - JCheckBoxMenuItem persistOn = new JCheckBoxMenuItem("Auto Persist Layout"); - file.add(persistOn); - - persistOn.setSelected(true); - - persistOn.addActionListener(e -> AppState.setAutoPersist(persistOn.isSelected())); - - JMenuItem saveLayout = new JMenuItem("Save Layout to File..."); - file.add(saveLayout); - - saveLayout.addActionListener(e -> { - JFileChooser chooser = new JFileChooser(); - int result = chooser.showSaveDialog(MainFrame.this); - - if (result == JFileChooser.APPROVE_OPTION) { - File selectedFile = chooser.getSelectedFile(); - - ApplicationLayout layout = DockingState.getApplicationLayout(); - - try { - LayoutPersistence.saveLayoutToFile(selectedFile, layout); - } - catch (DockingLayoutException ex) { - ex.printStackTrace(); - - JOptionPane.showMessageDialog(MainFrame.this, "Failed to save layout"); - } - } - }); - - JMenuItem loadLayout = new JMenuItem("Load Layout from File..."); - file.add(loadLayout); - - JMenuItem createPanel = new JMenuItem("Create Panel..."); - createPanel.addActionListener(e -> { - String panelName = JOptionPane.showInputDialog("Panel name"); - - SimplePanel panel = new SimplePanel(panelName, panelName, panelName); - Docking.dock(panel, MainFrame.this, DockingRegion.EAST); - }); - file.add(createPanel); - - loadLayout.addActionListener(e -> { - JFileChooser chooser = new JFileChooser(); - int result = chooser.showOpenDialog(MainFrame.this); - - if (result == JFileChooser.APPROVE_OPTION) { - File selectedFile = chooser.getSelectedFile(); - - ApplicationLayout layout = null; - try { - layout = LayoutPersistence.loadApplicationLayoutFromFile(selectedFile); - } - catch (DockingLayoutException ex) { - ex.printStackTrace(); - } - - if (layout != null) { - DockingState.restoreApplicationLayout(layout); - } - } - }); - - JMenu window = new JMenu("Window"); - window.add(new LayoutsMenu()); - - menuBar.add(window); - - setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - - SimplePanel one = new SimplePanel("one", "Panel One", "one"); - SimplePanel two = new SimplePanel("two", "Panel Two", "two"); - SimplePanel three = new SimplePanel("three", "Panel Three", "three"); - SimplePanel four = new SimplePanel("four", "Panel Four", "four"); - SimplePanel five = new SimplePanel("five", "Panel Five", "five"); - SimplePanel six = new SimplePanel("six", "Panel Six", "six"); - SimplePanel seven = new SimplePanel("seven", "Panel Seven", "seven", DockableStyle.CENTER_ONLY); - SimplePanel eight = new SimplePanel("eight", "Panel Eight", "eight"); - ToolPanel explorer = new ToolPanel("Explorer", "explorer", DockableStyle.VERTICAL, new ImageIcon(Objects.requireNonNull(getClass().getResource("/icons/light/icons8-vga-16.png")))); - ToolPanel output = new OutputPanel("Output", "output", DockableStyle.HORIZONTAL, new ImageIcon(Objects.requireNonNull(getClass().getResource("/icons/light/icons8-vga-16.png")))); - AlwaysDisplayedPanel alwaysDisplayed = new AlwaysDisplayedPanel("always displayed", "always-displayed"); - ScrollingWithToolbarPanel scrolling = new ScrollingWithToolbarPanel(); - FixedSizePanel fixedSize = new FixedSizePanel(); - - PropertiesDemoPanel propertiesDemoPanel = new PropertiesDemoPanel(); - - ThemesPanel themes = new ThemesPanel(); - - one.setTitleBackground(new Color(0xa1f2ff)); - two.setTitleBackground(new Color(0xdda1ff)); - three.setTitleBackground(new Color(0xffaea1)); - four.setTitleBackground(new Color(0xc3ffa1)); - - one.setTitleForeground(Color.black); - two.setTitleForeground(Color.black); - three.setTitleForeground(Color.black); - four.setTitleForeground(Color.black); - - JMenuItem changeText = new JMenuItem("Change tab text"); - changeText.addActionListener(e -> { - String rand = generateString("abcdefg", 4); - one.setTabText(rand); - Docking.updateTabInfo("one"); - }); - - JMenu view = new JMenu("View"); - menuBar.add(view); - - JMenuItem createNewDockable = new JMenuItem("Generate Random Dockable"); - createNewDockable.addActionListener(e -> { - SimplePanel rand = new SimplePanel("rand", generateString("alpha", 6), generateString("abcdefg", 10)); - Docking.dock(rand, one, DockingRegion.WEST); - }); - view.add(createNewDockable); - - JMenuItem createDynamicDockable = new JMenuItem("Create Dynamic Dockable"); - createDynamicDockable.addActionListener(e -> { - IncorrectDynamicDockable incorrect = new IncorrectDynamicDockable(); - Docking.dock(incorrect, one, DockingRegion.CENTER); - }); - view.add(createDynamicDockable); - - view.add(actionListenDock(one)); - JMenuItem oneToCenter = new JMenuItem("one (to center of window)"); - oneToCenter.addActionListener(e -> Docking.dock("one", this)); - view.add(oneToCenter); - view.add(actionListenDock(two)); - view.add(actionListenDock(three)); - view.add(actionListenDock(four)); - view.add(actionListenDock(five)); - view.add(actionListenDock(six)); - view.add(actionListenDock(seven)); - view.add(actionListenDock(eight)); - view.add(actionListenDock(explorer)); - view.add(actionListenDock(output)); - view.add(actionListenDock(fixedSize)); - view.add(new DockableMenuItem("non-existent-dockable", "Does Not Exist")); - view.add(actionListenDock(propertiesDemoPanel)); - view.add(new DockableMenuItem(() -> ((Dockable) alwaysDisplayed).getPersistentID(), ((Dockable) alwaysDisplayed).getTabText())); - view.add(changeText); - view.add(actionListenDock(themes)); - view.add(actionListenDock(scrolling)); - - Anchor anchor = new Anchor(); - - Docking.registerDockingAnchor(anchor); - - JMenuItem createAnchor = new JMenuItem("Create Anchor"); - createAnchor.addActionListener(e -> { - - - Docking.dock(anchor, one, DockingRegion.EAST); - }); - view.add(createAnchor); - - JMenuItem storeCurrentLayout = new JMenuItem("Store Current Layout..."); - storeCurrentLayout.addActionListener(e -> { - String layoutName = JOptionPane.showInputDialog("Name of Layout"); - - DockingLayouts.addLayout(layoutName, DockingState.getApplicationLayout()); - }); - window.add(storeCurrentLayout); - - JMenuItem restoreDefaultLayout = new ApplicationLayoutMenuItem("default", "Restore Default Layout"); - window.add(restoreDefaultLayout); - - setLayout(new GridBagLayout()); - - GridBagConstraints gbc = new GridBagConstraints(); - - gbc.gridy++; - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.fill = GridBagConstraints.BOTH; - - RootDockingPanel dockingPanel = new RootDockingPanel(this); + JMenu file = new JMenu("File"); + menuBar.add(file); + + JCheckBoxMenuItem persistOn = new JCheckBoxMenuItem("Auto Persist Layout"); + file.add(persistOn); + + persistOn.setSelected(true); + + persistOn.addActionListener(e -> AppState.setAutoPersist(persistOn.isSelected())); + + JMenuItem saveLayout = new JMenuItem("Save Layout to File..."); + file.add(saveLayout); + + saveLayout.addActionListener(e -> { + JFileChooser chooser = new JFileChooser(); + int result = chooser.showSaveDialog(MainFrame.this); + + if (result == JFileChooser.APPROVE_OPTION) { + File selectedFile = chooser.getSelectedFile(); + + ApplicationLayout layout = DockingState.getApplicationLayout(); + + try { + LayoutPersistence.saveLayoutToFile(selectedFile, layout); + } catch (DockingLayoutException ex) { + ex.printStackTrace(); + + JOptionPane.showMessageDialog(MainFrame.this, "Failed to save layout"); + } + } + }); + + JMenuItem loadLayout = new JMenuItem("Load Layout from File..."); + file.add(loadLayout); + + JMenuItem createPanel = new JMenuItem("Create Panel..."); + createPanel.addActionListener(e -> { + String panelName = JOptionPane.showInputDialog("Panel name"); + + SimplePanel panel = new SimplePanel(panelName, panelName, panelName); + Docking.dock(panel, MainFrame.this, DockingRegion.EAST); + }); + file.add(createPanel); + + loadLayout.addActionListener(e -> { + JFileChooser chooser = new JFileChooser(); + int result = chooser.showOpenDialog(MainFrame.this); + + if (result == JFileChooser.APPROVE_OPTION) { + File selectedFile = chooser.getSelectedFile(); + + ApplicationLayout layout = null; + try { + layout = LayoutPersistence.loadApplicationLayoutFromFile(selectedFile); + } catch (DockingLayoutException ex) { + ex.printStackTrace(); + } + + if (layout != null) { + DockingState.restoreApplicationLayout(layout); + } + } + }); + + JMenu window = new JMenu("Window"); + window.add(new LayoutsMenu()); + + menuBar.add(window); + + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + + SimplePanel one = new SimplePanel("one", "Panel One", "one"); + SimplePanel two = new SimplePanel("two", "Panel Two", "two"); + SimplePanel three = new SimplePanel("three", "Panel Three", "three"); + SimplePanel four = new SimplePanel("four", "Panel Four", "four"); + SimplePanel five = new SimplePanel("five", "Panel Five", "five"); + SimplePanel six = new SimplePanel("six", "Panel Six", "six"); + SimplePanel seven = new SimplePanel("seven", "Panel Seven", "seven", DockableStyle.CENTER_ONLY); + SimplePanel eight = new SimplePanel("eight", "Panel Eight", "eight"); + ToolPanel explorer = new ToolPanel("Explorer", "explorer", DockableStyle.VERTICAL, new ImageIcon(Objects.requireNonNull(getClass().getResource("/icons/light/icons8-vga-16.png")))); + ToolPanel output = new OutputPanel("Output", "output", DockableStyle.HORIZONTAL, new ImageIcon(Objects.requireNonNull(getClass().getResource("/icons/light/icons8-vga-16.png")))); + AlwaysDisplayedPanel alwaysDisplayed = new AlwaysDisplayedPanel("always displayed", "always-displayed"); + ScrollingWithToolbarPanel scrolling = new ScrollingWithToolbarPanel(); + FixedSizePanel fixedSize = new FixedSizePanel(); + + PropertiesDemoPanel propertiesDemoPanel = new PropertiesDemoPanel(); + + ThemesPanel themes = new ThemesPanel(); + + one.setTitleBackground(new Color(0xa1f2ff)); + two.setTitleBackground(new Color(0xdda1ff)); + three.setTitleBackground(new Color(0xffaea1)); + four.setTitleBackground(new Color(0xc3ffa1)); + + one.setTitleForeground(Color.black); + two.setTitleForeground(Color.black); + three.setTitleForeground(Color.black); + four.setTitleForeground(Color.black); + + JMenuItem changeText = new JMenuItem("Change tab text"); + changeText.addActionListener(e -> { + String rand = generateString("abcdefg", 4); + one.setTabText(rand); + Docking.updateTabInfo("one"); + }); + + JMenu view = new JMenu("View"); + menuBar.add(view); + + JMenuItem bringtofront = new JMenuItem("Bring One to Front"); + bringtofront.addActionListener(e -> { + Docking.bringToFront("one"); + }); + view.add(bringtofront); + + JMenuItem createNewDockable = new JMenuItem("Generate Random Dockable"); + createNewDockable.addActionListener(e -> { + SimplePanel rand = new SimplePanel("rand", generateString("alpha", 6), generateString("abcdefg", 10)); + Docking.dock(rand, one, DockingRegion.WEST); + }); + view.add(createNewDockable); + + JMenuItem createDynamicDockable = new JMenuItem("Create Dynamic Dockable"); + createDynamicDockable.addActionListener(e -> { + IncorrectDynamicDockable incorrect = new IncorrectDynamicDockable(); + Docking.dock(incorrect, one, DockingRegion.CENTER); + }); + view.add(createDynamicDockable); + + view.add(actionListenDock(one)); + JMenuItem oneToCenter = new JMenuItem("one (to center of window)"); + oneToCenter.addActionListener(e -> Docking.dock("one", this)); + view.add(oneToCenter); + view.add(actionListenDock(two)); + view.add(actionListenDock(three)); + view.add(actionListenDock(four)); + view.add(actionListenDock(five)); + view.add(actionListenDock(six)); + view.add(actionListenDock(seven)); + view.add(actionListenDock(eight)); + view.add(actionListenDock(explorer)); + view.add(actionListenDock(output)); + view.add(actionListenDock(fixedSize)); + view.add(new DockableMenuItem("non-existent-dockable", "Does Not Exist")); + view.add(actionListenDock(propertiesDemoPanel)); + view.add(new DockableMenuItem(() -> ((Dockable) alwaysDisplayed).getPersistentID(), ((Dockable) alwaysDisplayed).getTabText())); + view.add(changeText); + view.add(actionListenDock(themes)); + view.add(actionListenDock(scrolling)); + + Anchor anchor = new Anchor(); + + Docking.registerDockingAnchor(anchor); + + JMenuItem createAnchor = new JMenuItem("Create Anchor"); + createAnchor.addActionListener(e -> { + + + Docking.dock(anchor, one, DockingRegion.EAST); + }); + view.add(createAnchor); + + JMenuItem storeCurrentLayout = new JMenuItem("Store Current Layout..."); + storeCurrentLayout.addActionListener(e -> { + String layoutName = JOptionPane.showInputDialog("Name of Layout"); + + DockingLayouts.addLayout(layoutName, DockingState.getApplicationLayout()); + }); + window.add(storeCurrentLayout); + + JMenuItem restoreDefaultLayout = new ApplicationLayoutMenuItem("default", "Restore Default Layout"); + window.add(restoreDefaultLayout); + + setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridy++; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; + + RootDockingPanel dockingPanel = new RootDockingPanel(this); // dockingPanel.setPinningSupported(false); - gbc.insets = new Insets(0, 5, 5, 5); + gbc.insets = new Insets(0, 5, 5, 5); - add(dockingPanel, gbc); + add(dockingPanel, gbc); - gbc.gridy++; - gbc.weighty = 0; - gbc.fill = GridBagConstraints.NONE; + gbc.gridy++; + gbc.weighty = 0; + gbc.fill = GridBagConstraints.NONE; - WindowLayoutBuilderAPI layoutBuilder = new WindowLayoutBuilder(alwaysDisplayed.getPersistentID()) - .dock(one.getPersistentID(), alwaysDisplayed.getPersistentID()) - .dock(two.getPersistentID(), one.getPersistentID(), DockingRegion.SOUTH) - .dockToRoot(three.getPersistentID(), DockingRegion.WEST) - .dock(four.getPersistentID(), two.getPersistentID(), DockingRegion.CENTER) - .dock(propertiesDemoPanel.getPersistentID(), four.getPersistentID(), DockingRegion.CENTER) - .dockToRoot(output.getPersistentID(), DockingRegion.SOUTH) - .dockToRoot(themes.getPersistentID(), DockingRegion.EAST) - .dock(explorer.getPersistentID(), themes.getPersistentID(), DockingRegion.CENTER) - .display(themes.getPersistentID()); + WindowLayoutBuilderAPI layoutBuilder = new WindowLayoutBuilder(alwaysDisplayed.getPersistentID()) + .dock(one.getPersistentID(), alwaysDisplayed.getPersistentID()) + .dock(two.getPersistentID(), one.getPersistentID(), DockingRegion.SOUTH) + .dockToRoot(three.getPersistentID(), DockingRegion.WEST) + .dock(four.getPersistentID(), two.getPersistentID(), DockingRegion.CENTER) + .dock(propertiesDemoPanel.getPersistentID(), four.getPersistentID(), DockingRegion.CENTER) + .dockToRoot(output.getPersistentID(), DockingRegion.SOUTH) + .dockToRoot(themes.getPersistentID(), DockingRegion.EAST) + .dock(explorer.getPersistentID(), themes.getPersistentID(), DockingRegion.CENTER) + .display(themes.getPersistentID()); - layoutBuilder.setProperty(one.getPersistentID(), SimplePanel.STRING_TEST_PROP, "value"); - layoutBuilder.setProperty(one.getPersistentID(), SimplePanel.TEST_INT_1_PROP, 100); + layoutBuilder.setProperty(one.getPersistentID(), SimplePanel.STRING_TEST_PROP, "value"); + layoutBuilder.setProperty(one.getPersistentID(), SimplePanel.TEST_INT_1_PROP, 100); - ApplicationLayout defaultLayout = layoutBuilder.buildApplicationLayout(); + ApplicationLayout defaultLayout = layoutBuilder.buildApplicationLayout(); - DockingLayouts.addLayout("default", defaultLayout); + DockingLayouts.addLayout("default", defaultLayout); // AppState.setDefaultApplicationLayout(defaultLayout); - } - - static Random rng = new Random(); - public static String generateString(String characters, int length) - { - - - char[] text = new char[length]; - for (int i = 0; i < length; i++) - { - text[i] = characters.charAt(rng.nextInt(characters.length())); - } - return new String(text); - } - - private JMenuItem actionListenDock(Dockable dockable) { - return new DockableMenuItem(dockable.getPersistentID(), dockable.getTabText()); - } - - public static void main(String[] args) { - SwingUtilities.invokeLater(() -> new CommandLine(new MainFrame(new File("basic_demo_layout.xml"))).execute(args)); - } - - private void configureLookAndFeel() { - try { - FlatLaf.registerCustomDefaultsSource("docking"); - - System.setProperty("flatlaf.uiScale", String.valueOf(uiScale)); - - switch (lookAndFeel) { - case "light": - UIManager.setLookAndFeel(new FlatLightLaf()); - break; - case "dark": - UIManager.setLookAndFeel(new FlatDarkLaf()); - break; - case "github-dark": - UIManager.setLookAndFeel(new FlatMTGitHubDarkIJTheme()); - break; - case "solarized-dark": - UIManager.setLookAndFeel(new FlatSolarizedDarkIJTheme()); - break; - default: - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | - UnsupportedLookAndFeelException ex) { - throw new RuntimeException(ex); - } - break; - } - FlatLaf.updateUI(); - } - catch (Exception e) { - e.printStackTrace(); - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } - catch (ClassNotFoundException | InstantiationException | IllegalAccessException | - UnsupportedLookAndFeelException ex) { - throw new RuntimeException(ex); - } - } - UIManager.getDefaults().put("TabbedPane.contentBorderInsets", new Insets(0,0,0,0)); - UIManager.getDefaults().put("TabbedPane.tabsOverlapBorder", true); - - if (edtViolationDetector) { - // this is an app to test the docking framework, we want to make sure we detect EDT violations as soon as possible - FailOnThreadViolationRepaintManager.install(); - } - } - - @Override - public Integer call() { - configureLookAndFeel(); - - // now that the main frame is set up with the defaults, we can restore the layout - AppState.setPersistFile(layoutFile); - - try { - AppState.restore(); - } catch (DockingLayoutException e) { - // something happened trying to load the layout file, record it here - e.printStackTrace(); - } - - AppState.setAutoPersist(true); - SwingUtilities.invokeLater(() -> setVisible(true)); - return 0; - } + } + + static Random rng = new Random(); + + public static String generateString(String characters, int length) { + + + char[] text = new char[length]; + for (int i = 0; i < length; i++) { + text[i] = characters.charAt(rng.nextInt(characters.length())); + } + return new String(text); + } + + private JMenuItem actionListenDock(Dockable dockable) { + return new DockableMenuItem(dockable.getPersistentID(), dockable.getTabText()); + } + + public static void main(String[] args) { + SwingUtilities.invokeLater(() -> new CommandLine(new MainFrame(new File("basic_demo_layout.xml"))).execute(args)); + } + + private void configureLookAndFeel() { + try { + FlatLaf.registerCustomDefaultsSource("docking"); + + System.setProperty("flatlaf.uiScale", String.valueOf(uiScale)); + + switch (lookAndFeel) { + case "light": + UIManager.setLookAndFeel(new FlatLightLaf()); + break; + case "dark": + UIManager.setLookAndFeel(new FlatDarkLaf()); + break; + case "github-dark": + UIManager.setLookAndFeel(new FlatMTGitHubDarkIJTheme()); + break; + case "solarized-dark": + UIManager.setLookAndFeel(new FlatSolarizedDarkIJTheme()); + break; + default: + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | + UnsupportedLookAndFeelException ex) { + throw new RuntimeException(ex); + } + break; + } + FlatLaf.updateUI(); + } catch (Exception e) { + e.printStackTrace(); + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | + UnsupportedLookAndFeelException ex) { + throw new RuntimeException(ex); + } + } + UIManager.getDefaults().put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0)); + UIManager.getDefaults().put("TabbedPane.tabsOverlapBorder", true); + + if (edtViolationDetector) { + // this is an app to test the docking framework, we want to make sure we detect EDT violations as soon as possible + FailOnThreadViolationRepaintManager.install(); + } + } + + @Override + public Integer call() { + configureLookAndFeel(); + + // now that the main frame is set up with the defaults, we can restore the layout + AppState.setPersistFile(layoutFile); + + try { + AppState.restore(); + } catch (DockingLayoutException e) { + // something happened trying to load the layout file, record it here + e.printStackTrace(); + } + + AppState.setAutoPersist(true); + SwingUtilities.invokeLater(() -> setVisible(true)); + return 0; + } } 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 e01d640d..4c3a8ad2 100644 --- a/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingAPI.java +++ b/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingAPI.java @@ -40,6 +40,7 @@ of this software and associated documentation files (the "Software"), to deal import io.github.andrewauclair.moderndocking.internal.DockingPanel; import io.github.andrewauclair.moderndocking.internal.FloatingFrame; import io.github.andrewauclair.moderndocking.internal.InternalRootDockingPanel; +import io.github.andrewauclair.moderndocking.layouts.DynamicDockableCreationListener; import io.github.andrewauclair.moderndocking.layouts.WindowLayout; import io.github.andrewauclair.moderndocking.settings.Settings; import io.github.andrewauclair.moderndocking.ui.ToolbarLocation; @@ -1009,4 +1010,13 @@ public void addNewFloatingFrameListener(NewFloatingFrameListener listener) { public void removeNewFloatingFrameListener(NewFloatingFrameListener listener) { DockingListeners.removeNewFloatingFrameListener(listener); } + + /** + * Set the listener used to create dynamic dockables + * + * @param listener User defined listener + */ + public void setUserDynamicDockableCreationListener(DynamicDockableCreationListener listener) { + dockingState.setUserDynamicDockableCreationListener(listener); + } } 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 4a40302a..d4b041d2 100644 --- a/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingStateAPI.java +++ b/docking-api/src/io/github/andrewauclair/moderndocking/api/DockingStateAPI.java @@ -24,6 +24,7 @@ of this software and associated documentation files (the "Software"), to deal import io.github.andrewauclair.moderndocking.Dockable; import io.github.andrewauclair.moderndocking.DockableTabPreference; import io.github.andrewauclair.moderndocking.DynamicDockableParameters; +import io.github.andrewauclair.moderndocking.Property; import io.github.andrewauclair.moderndocking.exception.DockableNotFoundException; import io.github.andrewauclair.moderndocking.exception.RootDockingPanelNotFoundException; import io.github.andrewauclair.moderndocking.internal.DockableProperties; @@ -40,12 +41,14 @@ of this software and associated documentation files (the "Software"), to deal import io.github.andrewauclair.moderndocking.internal.FloatingFrame; import io.github.andrewauclair.moderndocking.internal.InternalRootDockingPanel; import io.github.andrewauclair.moderndocking.layouts.ApplicationLayout; +import io.github.andrewauclair.moderndocking.layouts.DefaultDynamicDockableCreationListener; import io.github.andrewauclair.moderndocking.layouts.DockingAnchorPanelNode; import io.github.andrewauclair.moderndocking.layouts.DockingLayoutNode; import io.github.andrewauclair.moderndocking.layouts.DockingLayouts; import io.github.andrewauclair.moderndocking.layouts.DockingSimplePanelNode; import io.github.andrewauclair.moderndocking.layouts.DockingSplitPanelNode; import io.github.andrewauclair.moderndocking.layouts.DockingTabPanelNode; +import io.github.andrewauclair.moderndocking.layouts.DynamicDockableCreationListener; import io.github.andrewauclair.moderndocking.layouts.WindowLayout; import io.github.andrewauclair.moderndocking.settings.Settings; import io.github.andrewauclair.moderndocking.ui.ToolbarLocation; @@ -58,6 +61,7 @@ of this software and associated documentation files (the "Software"), to deal import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -82,6 +86,9 @@ public class DockingStateAPI { private final DockingAPI docking; + private final DynamicDockableCreationListener defaultDynamicDockableCreation; + private DynamicDockableCreationListener userDynamicDockableCreation = null; + /** * Create a new instance for the given docking instance * @@ -89,6 +96,7 @@ public class DockingStateAPI { */ protected DockingStateAPI(DockingAPI docking) { this.docking = docking; + defaultDynamicDockableCreation = new DefaultDynamicDockableCreationListener(docking); } /** @@ -326,7 +334,7 @@ private DockingPanel restoreTabbed(DockingAPI docking, DockingTabPanelNode node, Dockable dockable = getDockable(docking, simpleNode.getPersistentID()); if (dockable instanceof FailedDockable) { - dockable = createDynamicDockable(dockable, simpleNode.getPersistentID(), simpleNode.getClassName(), simpleNode.getTitleText(), simpleNode.getTabText()); + dockable = createDynamicDockable(dockable, simpleNode.getPersistentID(), simpleNode.getClassName(), simpleNode.getTitleText(), simpleNode.getTabText(), simpleNode.getProperties()); } if (dockable == null) { @@ -363,7 +371,7 @@ private DockingPanel restoreAnchor(DockingAPI docking, DockingAnchorPanelNode no Dockable dockable = getDockable(docking, node.getPersistentID()); if (dockable instanceof FailedDockable) { - dockable = createDynamicDockable(dockable, node.getPersistentID(), node.getClassName(), "", ""); + dockable = createDynamicDockable(dockable, node.getPersistentID(), node.getClassName(), "", "", Collections.emptyMap()); } if (dockable == null) { @@ -384,7 +392,7 @@ private DockingPanel restoreSimple(DockingAPI docking, DockingSimplePanelNode no Dockable dockable = getDockable(docking, node.getPersistentID()); if (dockable instanceof FailedDockable) { - dockable = createDynamicDockable(dockable, node.getPersistentID(), node.getClassName(), node.getTitleText(), node.getTabText()); + dockable = createDynamicDockable(dockable, node.getPersistentID(), node.getClassName(), node.getTitleText(), node.getTabText(), node.getProperties()); } if (dockable == null) { @@ -410,50 +418,64 @@ private DockingPanel restoreSimple(DockingAPI docking, DockingSimplePanelNode no return new DockedSimplePanel(docking, wrapper, node.getAnchor()); } - private Dockable createDynamicDockable(Dockable dockable, String persistentID, String className, String titleText, String tabText) { - boolean foundNewConstructor = false; - - try { - Class aClass = Class.forName(className); - Constructor constructor = aClass.getConstructor(DynamicDockableParameters.class); - - // the failed dockable is registered with the persistentID we want to use - docking.deregisterDockable(dockable); + private Dockable createDynamicDockable(Dockable dockable, String persistentID, String className, String titleText, String tabText, Map properties) { + // the failed dockable is registered with the persistentID we want to use + docking.deregisterDockable(dockable); - constructor.newInstance(new DynamicDockableParameters(persistentID, tabText, titleText)); + dockable = null; - foundNewConstructor = true; + if (userDynamicDockableCreation != null) { + dockable = userDynamicDockableCreation.createDockable(persistentID, className, titleText, tabText, properties); } - catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | - InvocationTargetException e) { - logger.log(Level.INFO, "Failed to create instance of dynamic dockable with DynamicDockableParameters constructor. Falling back on (String, String)"); - logger.log(Level.INFO, e.getMessage(), e); - } - - if (!foundNewConstructor) { - try { - Class aClass = Class.forName(className); - Constructor constructor = aClass.getConstructor(String.class, String.class); - // the failed dockable is registered with the persistentID we want to use - docking.deregisterDockable(dockable); - - // create the instance, this should register the dockable and let us look it up - constructor.newInstance(persistentID, persistentID); - } - catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | - InvocationTargetException e) { - logger.log(Level.INFO, e.getMessage(), e); - return null; - } + if (dockable == null) { + dockable = defaultDynamicDockableCreation.createDockable(persistentID, className, titleText, tabText, properties); } - dockable = getDockable(docking, persistentID); - - if (dockable instanceof FailedDockable) { - return null; - } return dockable; +// boolean foundNewConstructor = false; +// +// try { +// Class aClass = Class.forName(className); +// Constructor constructor = aClass.getConstructor(DynamicDockableParameters.class); +// +// // the failed dockable is registered with the persistentID we want to use +// docking.deregisterDockable(dockable); +// +// constructor.newInstance(new DynamicDockableParameters(persistentID, tabText, titleText)); +// +// foundNewConstructor = true; +// } +// catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | +// InvocationTargetException e) { +// logger.log(Level.INFO, "Failed to create instance of dynamic dockable with DynamicDockableParameters constructor. Falling back on (String, String)"); +// logger.log(Level.INFO, e.getMessage(), e); +// } +// +// if (!foundNewConstructor) { +// try { +// Class aClass = Class.forName(className); +// Constructor constructor = aClass.getConstructor(String.class, String.class); +// +// // the failed dockable is registered with the persistentID we want to use +// docking.deregisterDockable(dockable); +// +// // create the instance, this should register the dockable and let us look it up +// constructor.newInstance(persistentID, persistentID); +// } +// catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | +// InvocationTargetException e) { +// logger.log(Level.INFO, e.getMessage(), e); +// return null; +// } +// } +// +// dockable = getDockable(docking, persistentID); +// +// if (dockable instanceof FailedDockable) { +// return null; +// } +// return dockable; } private Dockable getDockable(DockingAPI docking, String persistentID) { @@ -574,4 +596,8 @@ public void hierarchyChanged(HierarchyEvent e) { }); } } + + public void setUserDynamicDockableCreationListener(DynamicDockableCreationListener userDynamicDockableCreation) { + this.userDynamicDockableCreation = userDynamicDockableCreation; + } } diff --git a/docking-api/src/io/github/andrewauclair/moderndocking/layouts/DefaultDynamicDockableCreationListener.java b/docking-api/src/io/github/andrewauclair/moderndocking/layouts/DefaultDynamicDockableCreationListener.java new file mode 100644 index 00000000..dce08cfd --- /dev/null +++ b/docking-api/src/io/github/andrewauclair/moderndocking/layouts/DefaultDynamicDockableCreationListener.java @@ -0,0 +1,74 @@ +package io.github.andrewauclair.moderndocking.layouts; + +import io.github.andrewauclair.moderndocking.Dockable; +import io.github.andrewauclair.moderndocking.DynamicDockableParameters; +import io.github.andrewauclair.moderndocking.Property; +import io.github.andrewauclair.moderndocking.api.DockingAPI; +import io.github.andrewauclair.moderndocking.exception.DockableNotFoundException; +import io.github.andrewauclair.moderndocking.internal.DockingInternal; +import io.github.andrewauclair.moderndocking.internal.FailedDockable; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DefaultDynamicDockableCreationListener implements DynamicDockableCreationListener { + private static final Logger logger = Logger.getLogger(DefaultDynamicDockableCreationListener.class.getPackageName()); + + private final DockingAPI docking; + + public DefaultDynamicDockableCreationListener(DockingAPI docking) { + this.docking = docking; + } + + @Override + public Dockable createDockable(String persistentID, String className, String titleText, String tabText, Map properties) { + boolean foundNewConstructor = false; + + try { + Class aClass = Class.forName(className); + Constructor constructor = aClass.getConstructor(DynamicDockableParameters.class); + + constructor.newInstance(new DynamicDockableParameters(persistentID, tabText, titleText)); + + foundNewConstructor = true; + } + catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | + InvocationTargetException e) { + logger.log(Level.INFO, "Failed to create instance of dynamic dockable with DynamicDockableParameters constructor. Falling back on (String, String)"); + logger.log(Level.INFO, e.getMessage(), e); + } + + if (!foundNewConstructor) { + try { + Class aClass = Class.forName(className); + Constructor constructor = aClass.getConstructor(String.class, String.class); + + // create the instance, this should register the dockable and let us look it up + constructor.newInstance(persistentID, persistentID); + } + catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | + InvocationTargetException e) { + logger.log(Level.INFO, e.getMessage(), e); + return null; + } + } + + Dockable dockable = getDockable(docking, persistentID); + + if (dockable instanceof FailedDockable) { + return null; + } + return dockable; + } + + private Dockable getDockable(DockingAPI docking, String persistentID) { + try { + return DockingInternal.get(docking).getDockable(persistentID); + } + catch (DockableNotFoundException ignore) { + } + return new FailedDockable(docking, persistentID); + } +} diff --git a/docking-api/src/io/github/andrewauclair/moderndocking/layouts/DynamicDockableCreationListener.java b/docking-api/src/io/github/andrewauclair/moderndocking/layouts/DynamicDockableCreationListener.java new file mode 100644 index 00000000..c340ba73 --- /dev/null +++ b/docking-api/src/io/github/andrewauclair/moderndocking/layouts/DynamicDockableCreationListener.java @@ -0,0 +1,9 @@ +package io.github.andrewauclair.moderndocking.layouts; + +import io.github.andrewauclair.moderndocking.Dockable; +import io.github.andrewauclair.moderndocking.Property; +import java.util.Map; + +public interface DynamicDockableCreationListener { + Dockable createDockable(String persistentID, String className, String titleText, String tabText, Map properties); +} diff --git a/docking-single-app/src/io/github/andrewauclair/moderndocking/app/Docking.java b/docking-single-app/src/io/github/andrewauclair/moderndocking/app/Docking.java index 16b0b4c2..45e49cbe 100644 --- a/docking-single-app/src/io/github/andrewauclair/moderndocking/app/Docking.java +++ b/docking-single-app/src/io/github/andrewauclair/moderndocking/app/Docking.java @@ -30,6 +30,7 @@ of this software and associated documentation files (the "Software"), to deal import io.github.andrewauclair.moderndocking.event.NewFloatingFrameListener; import io.github.andrewauclair.moderndocking.internal.DockingInternal; import io.github.andrewauclair.moderndocking.internal.DockingListeners; +import io.github.andrewauclair.moderndocking.layouts.DynamicDockableCreationListener; import io.github.andrewauclair.moderndocking.ui.ToolbarLocation; import java.awt.Dimension; import java.awt.Point; @@ -620,6 +621,10 @@ public static void removeNewFloatingFrameListener(NewFloatingFrameListener liste DockingListeners.removeNewFloatingFrameListener(listener); } + public static void setUserDynamicDockableCreationListener(DynamicDockableCreationListener listener) { + instance.setUserDynamicDockableCreationListener(listener); + } + public static DockingAPI getSingleInstance() { if (instance == null) { throw new RuntimeException("No docking instance available.");