summaryrefslogtreecommitdiff
path: root/base/src/main/java/bjc/utils/gui/panels/CollapsiblePanel.java
diff options
context:
space:
mode:
Diffstat (limited to 'base/src/main/java/bjc/utils/gui/panels/CollapsiblePanel.java')
-rw-r--r--base/src/main/java/bjc/utils/gui/panels/CollapsiblePanel.java217
1 files changed, 217 insertions, 0 deletions
diff --git a/base/src/main/java/bjc/utils/gui/panels/CollapsiblePanel.java b/base/src/main/java/bjc/utils/gui/panels/CollapsiblePanel.java
new file mode 100644
index 0000000..6b4453b
--- /dev/null
+++ b/base/src/main/java/bjc/utils/gui/panels/CollapsiblePanel.java
@@ -0,0 +1,217 @@
+package bjc.utils.gui.panels;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+/**
+ * A JPanel with a clickable header that can collapse/expand its content.
+ *
+ * Features:
+ * - Title + arrow (▶ / ▼)
+ * - Optional custom header component on the right side (e.g., buttons, filters)
+ * - Add children like a normal JPanel (they go into the content area)
+ */
+public class CollapsiblePanel extends JPanel {
+ private static final long serialVersionUID = -5941733171067755926L;
+
+ private final JPanel headerPanel;
+ private final JPanel headerClickableArea;
+ private final JPanel headerExtrasPanel;
+ private final JButton toggleButton;
+ private final JLabel titleLabel;
+ private final JPanel contentPanel;
+
+ private boolean collapsed = false;
+ private boolean internalAdd = false; // guard for addImpl
+
+ /**
+ * Create a new collapsible panel.
+ *
+ * @param title The title for the panel
+ */
+ public CollapsiblePanel(String title) {
+ this(title, new FlowLayout(FlowLayout.LEFT, 4, 4));
+ }
+
+ /**
+ * Create a new collapsible panel, using the following layout manager for the content.
+ *
+ * @param title The title for the panel
+ * @param contentLayout The layout manager for the panel
+ */
+ public CollapsiblePanel(String title, LayoutManager contentLayout) {
+ super(new BorderLayout());
+
+ // === Header ===
+ headerPanel = new JPanel(new BorderLayout(4, 0));
+ headerPanel.setBorder(new EmptyBorder(2, 4, 2, 4));
+ headerPanel.setOpaque(false);
+
+ // Left side: clickable region (arrow + title)
+ headerClickableArea = new JPanel(new BorderLayout(4, 0));
+ headerClickableArea.setOpaque(false);
+
+ toggleButton = new JButton("\u25BC"); // ▼ expanded
+ toggleButton.setMargin(new Insets(0, 4, 0, 4));
+ toggleButton.setFocusable(false);
+ toggleButton.setBorderPainted(false);
+ toggleButton.setContentAreaFilled(false);
+
+ titleLabel = new JLabel(title);
+ titleLabel.setBorder(new EmptyBorder(0, 2, 0, 0));
+ titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD));
+
+ headerClickableArea.add(toggleButton, BorderLayout.WEST);
+ headerClickableArea.add(titleLabel, BorderLayout.CENTER);
+
+ // Right side: custom header component container
+ headerExtrasPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 4, 0));
+ headerExtrasPanel.setOpaque(false);
+
+ headerPanel.add(headerClickableArea, BorderLayout.CENTER);
+ headerPanel.add(headerExtrasPanel, BorderLayout.EAST);
+
+ // === Content ===
+ contentPanel = new JPanel(contentLayout);
+
+ // === Wiring: toggle behavior ===
+ toggleButton.addActionListener(e -> setCollapsed(!isCollapsed()));
+
+ MouseAdapter headerClick = new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (SwingUtilities.isLeftMouseButton(e)) {
+ setCollapsed(!isCollapsed());
+ }
+ }
+ };
+ // Only the left side (arrow + title) toggles; extras panel is not clickable for
+ // toggle
+ headerClickableArea.addMouseListener(headerClick);
+ titleLabel.addMouseListener(headerClick);
+ toggleButton.addMouseListener(headerClick);
+
+ // Add header + content to this panel
+ internalAdd = true;
+ add(headerPanel, BorderLayout.NORTH);
+ add(contentPanel, BorderLayout.CENTER);
+ internalAdd = false;
+ }
+
+ /**
+ * Returns the content panel; you can use this if you want direct access.
+ *
+ * @return The content panel
+ */
+ public JPanel getContentPanel() {
+ return contentPanel;
+ }
+
+ /**
+ * Returns the panel that holds extra header components (right side).
+ *
+ * @return The header extras panel
+ */
+ public JPanel getHeaderExtrasPanel() {
+ return headerExtrasPanel;
+ }
+
+ /**
+ * Replace any existing header extras with a single component. Pass null to
+ * clear.
+ *
+ * @param comp The header component to use
+ */
+ public void setHeaderComponent(Component comp) {
+ headerExtrasPanel.removeAll();
+ if (comp != null) {
+ headerExtrasPanel.add(comp);
+ }
+ headerExtrasPanel.revalidate();
+ headerExtrasPanel.repaint();
+ }
+
+ /**
+ * Add an extra component to the header (right side) without clearing existing
+ * ones.
+ *
+ *
+ * @param comp The component to add
+ */
+ public void addHeaderComponent(Component comp) {
+ headerExtrasPanel.add(comp);
+ headerExtrasPanel.revalidate();
+ headerExtrasPanel.repaint();
+ }
+
+ /**
+ * Set the header title text.
+ *
+ * @param title The header title
+ */
+ public void setTitle(String title) {
+ titleLabel.setText(title);
+ }
+
+ /**
+ * Get the header title
+ *
+ * @return The header title
+ */
+ public String getTitle() {
+ return titleLabel.getText();
+ }
+
+ /**
+ * Collapse or expand the content.
+ *
+ * @param collapsed True to collapse the panel, false to expand it
+ */
+ public void setCollapsed(boolean collapsed) {
+ if (this.collapsed == collapsed)
+ return;
+ this.collapsed = collapsed;
+ contentPanel.setVisible(!collapsed);
+ toggleButton.setText(collapsed ? "\u25B6" : "\u25BC"); // ▶ / ▼
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Check if the panel is collapsed or not.
+ *
+ * @return Is the panel collapsed or not?
+ */
+ public boolean isCollapsed() {
+ return collapsed;
+ }
+
+ /**
+ * Override addImpl so that user-added components go into the content area
+ * instead of directly into the outer BorderLayout.
+ */
+ @Override
+ protected void addImpl(Component comp, Object constraints, int index) {
+ if (internalAdd) {
+ super.addImpl(comp, constraints, index);
+ } else {
+ contentPanel.add(comp, constraints, index);
+ }
+ }
+
+ /**
+ * Convenience factory for a titled, initially-collapsed panel.
+ *
+ * @param title The title for the panel
+ * @param layout The layout manager for the content
+ * @return The panel created
+ */
+ public static CollapsiblePanel collapsed(String title, LayoutManager layout) {
+ CollapsiblePanel p = new CollapsiblePanel(title, layout);
+ p.setCollapsed(true);
+ return p;
+ }
+}