Collapsing Sidebar Menu in Java Swing

How to Create a Collapsing Sidebar Menu in Java Netbeans

How to Create a Collapsing Sidebar Menu in Java Netbeans


In this Java Tutorial we will see How To Create a collapsing sidebar in Java Swing with smooth animations, hover effects and custom icons, ideal for dashboards and admin panels. Built using NetBeans. 

What We Will Make:
    - Expand And Collapse animations.
    - Custom button.
    - Hover animations.
    - Custom tooltip with arrow pointers.
    - Icons design using Java2D graphics.

What We Are Gonna Use In This Project:

- Java Programming Language.
- NetBeans Editor.





Project Source Code:



/**
 *
 * @author 1BestCsharp
 */
public class Collapsing_Sidebar extends JPanel{
    
    
    // These arrays control the hover animation for each menu item
    private final Timer[] hoverTimers = new Timer[6]; // One timer for each menu item to control animation timing
    private final float[] hoverScales = new float[6]; // Track scale (size) of each menu item during hover animation
    private final float MAX_SCALE = 1.05f; // Maximum size increase when hovering (5% larger)

    // Constants for sidebar dimensions and animation
    private static final int EXPANDED_WIDTH = 280; // Width of sidebar when expanded
    private static final int COLLAPSED_WIDTH = 70; // Width of sidebar when collapsed
    private static final int ANIMATION_DURATION = 250; // How long the collapse/expand animation takes (in milliseconds)
    
    // Main components of the interface
    private final JPanel sidebar; // The sidebar panel containing menu items
    private JButton collapseButton; // Button to collapse/expand the sidebar
    private JButton selectedButton; // Currently selected menu button
    private final JPanel contentPanel; // The main content area
    private Timer sidebarAnimationTimer; // Controls the animation when collapsing/expanding sidebar
    private boolean isSidebarCollapsed; // Tracks if sidebar is currently collapsed
    private final JWindow tooltipWindow; // Custom tooltip window that appears when hovering over collapsed menu items
    private final Map<String, Color> iconColors; // Colors for each menu icon
    
    
    // Color scheme for the application
    private final Color primaryColor = new Color(23, 25, 35); // Dark blue-gray for primary backgrounds
    private final Color secondaryColor = new Color(32, 34, 45); // Slightly lighter blue-gray
    private final Color accentColor = new Color(103, 118, 240); // Bright purple-blue for highlights
    private final Color backgroundColor = new Color(245, 246, 250); // Light gray background
    private final Color textColor = new Color(255, 255, 255); // White text
    private final Color tooltipBackground = new Color(45, 47, 57, 230); // Semi-transparent dark background for tooltips
    private final Color menuActiveColor = new Color(103, 118, 240, 100); // Semi-transparent accent for active menu items

    
    /**
     * Constructor: sets up the main layout and initializes components
     */
    public Collapsing_Sidebar(){
        // Set up the main panel with BorderLayout (divides screen into North, South, East, West, Center regions)
        setLayout(new BorderLayout());
        setBackground(backgroundColor);
        
        // Initialize colors for each icon
        iconColors = initializeIconColors();
        
        // Create the tooltip window (appears when hovering over collapsed sidebar items)
        tooltipWindow = createTooltipWindow();

        // Create the sidebar and content panels
        sidebar = createSidebar();
        contentPanel = createContentPanel();
        
        // Add sidebar to the west (left) and content to the center
        add(sidebar, BorderLayout.WEST);
        add(contentPanel, BorderLayout.CENTER);
    }
    
    
    
    /**
     * Define colors for each icon in the sidebar
     */
    private Map<String, Color> initializeIconColors() {
        Map<String, Color> colors = new HashMap<>();
        colors.put("dashboard", new Color(255, 184, 108)); // Orange
        colors.put("menu", new Color(132, 255, 147));      // Green
        colors.put("orders", new Color(255, 129, 129));    // Red
        colors.put("customers", new Color(129, 236, 255)); // Blue
        colors.put("analytics", new Color(255, 162, 255)); // Pink
        colors.put("settings", new Color(255, 231, 129));  // Yellow
        return colors;
    }
    
    
    /**
     * Creates the sidebar panel containing logo, menu items, and collapse button
     */
    private JPanel createSidebar() {
        // Create sidebar panel with custom background painting
        JPanel sidebarPanel = new JPanel(new BorderLayout()) {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                        RenderingHints.VALUE_ANTIALIAS_ON);
                
                // Create gradient background from primary to secondary color
                GradientPaint gp = new GradientPaint(
                    0, 0, primaryColor,             // Top color
                    0, getHeight(), secondaryColor  // Bottom color
                );
                g2d.setPaint(gp);
                g2d.fillRect(0, 0, getWidth(), getHeight());
                
                // Add subtle horizontal lines pattern overlay
                g2d.setColor(new Color(255, 255, 255, 5)); // Very transparent white
                for (int i = 0; i < getHeight(); i += 4) {
                    g2d.drawLine(0, i, getWidth(), i);
                }
                
                g2d.dispose();
                
            }

        };
        
        // Set initial size and padding
        sidebarPanel.setPreferredSize(new Dimension(EXPANDED_WIDTH, getHeight()));
        sidebarPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0));

        // Add logo at the top
        JPanel logoPanel = createLogoPanel();
        sidebarPanel.add(logoPanel, BorderLayout.NORTH);

        // Add menu items in the center
        JPanel menuPanel = createMenuPanel();
        sidebarPanel.add(menuPanel, BorderLayout.CENTER);

        // Add collapse button at bottom
        collapseButton = createCollapseButton();
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
        buttonPanel.setOpaque(false);
        buttonPanel.add(collapseButton);
        sidebarPanel.add(buttonPanel, BorderLayout.SOUTH);

        return sidebarPanel;
    }
    
    
    /**
     * Creates the button that collapses/expands the sidebar
     */
    private JButton createCollapseButton() {
        
        // Create button with custom painting
        JButton button = new JButton() {
            @Override
            protected void paintComponent(Graphics g) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                        RenderingHints.VALUE_ANTIALIAS_ON);
                
                int width = getWidth();
                int height = getHeight();
                
                // Draw background with gradient
                GradientPaint gp = new GradientPaint(
                    0, 0, new Color(
                            accentColor.getRed(), 
                            accentColor.getGreen(), 
                            accentColor.getBlue(), 
                            80),
                    width, height, new Color(accentColor.getRed(), 
                            accentColor.getGreen(), 
                            accentColor.getBlue(), 60)
                );
                g2d.setPaint(gp);
                g2d.fill(new RoundRectangle2D.Double(0, 0, 
                        width, height, height, height)); // Fully rounded ends
                
                // Draw arrow icon
                g2d.setColor(textColor);
                g2d.setStroke(new BasicStroke(1.5f, BasicStroke.CAP_ROUND, 
                        BasicStroke.JOIN_ROUND));
                
                int arrowSize = 8;
                int centerX = width / 2;
                int centerY = height / 2;
                
                // Create arrow path based on sidebar state
                Path2D.Double arrow = new Path2D.Double();
                if (isSidebarCollapsed) {
                    // Right-pointing arrow when collapsed (will expand)
                    arrow.moveTo(centerX - arrowSize/2, centerY - arrowSize/2);
                    arrow.lineTo(centerX + arrowSize/2, centerY);
                    arrow.lineTo(centerX - arrowSize/2, centerY + arrowSize/2);
                }  else {
                    // Left-pointing arrow when expanded (will collapse)
                    arrow.moveTo(centerX + arrowSize/2, centerY - arrowSize/2);
                    arrow.lineTo(centerX - arrowSize/2, centerY);
                    arrow.lineTo(centerX + arrowSize/2, centerY + arrowSize/2);
                }
                
                g2d.draw(arrow);
                
            }
        };
        
        // Set button properties
        button.setPreferredSize(new Dimension(50, 35));
        button.setBorderPainted(false);
        button.setContentAreaFilled(false);
        button.setFocusPainted(false);
        button.setCursor(new Cursor(Cursor.HAND_CURSOR));
        
        // Add action to toggle sidebar when clicked
        button.addActionListener(e -> toggleSidebar());

        return button;
    }
    
    
    // This method handles the animation of the sidebar expanding and collapsing
    private void toggleSidebar() {
        
        // If an animation is already running, don't start another one
        if (sidebarAnimationTimer != null && sidebarAnimationTimer.isRunning()) {
            return;
        }
        
        // Determine the target width based on current state
        int targetWidth = isSidebarCollapsed ? EXPANDED_WIDTH : COLLAPSED_WIDTH;
        int currentWidth = sidebar.getWidth(); // Current width of sidebar
        int totalSteps = 15; // Number of animation steps
        
        // Create a timer that will fire events to animate the sidebar
        // ANIMATION_DURATION is divided by totalSteps to get the delay between steps
        sidebarAnimationTimer = new Timer(ANIMATION_DURATION / totalSteps, null);
        final int[] step = {0}; // Use array to hold step counter so it can be modified in lambda
        
        // Add the action that happens on each timer tick
        sidebarAnimationTimer.addActionListener(e -> {
            step[0]++; // Increment step counter
            
            if (step[0] <= totalSteps) {
                // Calculate progress as a value from 0 to 1
                double progress = step[0] / (double) totalSteps;
                
                // Apply easing function for smoother animation
                // This makes the animation start and end slowly, but move quickly in the middle
                double easeProgress = progress < 0.5 
                    ? 2 * progress * progress // Accelerate in first half
                    : 1 - Math.pow(-2 * progress + 2, 2) / 2; // Decelerate in second half
                    
                // Calculate the new width based on progress
                int newWidth = currentWidth + (int) (easeProgress * (targetWidth - currentWidth));
                // Apply the new width to the sidebar
                sidebar.setPreferredSize(new Dimension(newWidth, getHeight()));
                sidebar.revalidate(); // Tell layout manager to recalculate layout
                sidebar.repaint(); // Request a repaint of the sidebar
                
            } else {
                // Animation is complete, set final width
                sidebar.setPreferredSize(new Dimension(targetWidth, getHeight()));
                ((Timer) e.getSource()).stop(); // Stop the timer
                isSidebarCollapsed = !isSidebarCollapsed; // Toggle the collapsed state
                updateSidebarContent(); // Update sidebar contents for new state
                collapseButton.repaint(); // Repaint the collapse button
            }
            
            
        });

        // Start the animation
        sidebarAnimationTimer.start();
    }
    
    
    // This method updates the content of the sidebar when it expands or collapses
    private void updateSidebarContent() {
        // Get all components in the sidebar
        Component[] components = sidebar.getComponents();
        // Loop through each component
        for (Component component : components) {
            // Check if the component is a JPanel
            if (component instanceof JPanel) {
                // Get all components inside this panel
                Component[] panelComponents = ((JPanel) component).getComponents();
                // Loop through each component in the panel
                for (Component panelComponent : panelComponents) {
                    // Check if component is a button but not the collapse button
                    if (panelComponent instanceof JButton && panelComponent != collapseButton) {
                        JButton button = (JButton) panelComponent;
                        // Get the original text stored as a property on the button
                        String originalText = (String) button.getClientProperty("originalText");
                        
                        // Create a timer for fade animation of text
                        Timer fadeTimer = new Timer(20, null);
                        // Use array to hold opacity value so it can be modified in lambda
                        float[] opacity = {button.getForeground().getAlpha() / 255f};
                        // Create color with current opacity
                        Color textColorWithAlpha = new Color(
                            textColor.getRed(), 
                            textColor.getGreen(), 
                            textColor.getBlue(), 
                            (int) (opacity[0] * 255)
                        );
                        button.setForeground(textColorWithAlpha);

                        // Add actions for each timer tick to animate text fading
                        fadeTimer.addActionListener(evt -> {
                            if (isSidebarCollapsed) {
                                // If sidebar is collapsing, reduce opacity
                                opacity[0] -= 0.1f; // Decrease by 10%
                                if (opacity[0] <= 0) {
                                    // Text fully transparent, remove it
                                    opacity[0] = 0;
                                    button.setText(""); // Clear text
                                    button.setHorizontalAlignment(SwingConstants.CENTER); // Center icon
                                    fadeTimer.stop(); // Stop animation
                                }
                            }  else {
                                // If sidebar is expanding, increase opacity
                                if (opacity[0] == 0) {
                                    // If starting from zero, restore text and left alignment
                                    button.setText(originalText);
                                    button.setHorizontalAlignment(SwingConstants.LEFT);
                                }
                                opacity[0] += 0.1f; // Increase by 10%
                                if (opacity[0] >= 1) {
                                    // Text fully visible
                                    opacity[0] = 1;
                                    fadeTimer.stop(); // Stop animation
                                }
                            }
                            
                            // Apply the new opacity to the text color
                            button.setForeground(new Color(
                                textColor.getRed(),
                                textColor.getGreen(),
                                textColor.getBlue(),
                                (int) (opacity[0] * 255) // Convert 0-1 to 0-255
                            ));
                            
                        });
                        
                        // Start the fade animation
                        fadeTimer.start();
                    }
                    
                }

            }
            
        }
        
        // Tell the layout manager to recalculate layout
        sidebar.revalidate();
        // Request a repaint of the sidebar
        sidebar.repaint();
        
    }
    
    /**
     * Creates the logo panel at the top of the sidebar
     */
    private JPanel createLogoPanel() {
        JPanel logoPanel = new JPanel(new BorderLayout());
        logoPanel.setOpaque(false); // Make transparent to show sidebar background
        logoPanel.setBorder(new EmptyBorder(25, 25, 25, 25)); // Add padding

        // Create and style the logo text
        JLabel logoLabel = new JLabel("My App Logo");
        logoLabel.setFont(new Font("Inter", Font.BOLD, 22));
        logoLabel.setForeground(textColor);
        logoPanel.add(logoLabel, BorderLayout.CENTER);

        return logoPanel;
    }
    
    
    
    /**
     * Creates the panel containing menu buttons
     */
    private JPanel createMenuPanel() {
        // Create panel with vertical box layout (components stacked top to bottom)
        JPanel menuPanel = new JPanel();
        menuPanel.setLayout(new BoxLayout(menuPanel, BoxLayout.Y_AXIS));
        menuPanel.setOpaque(false); // Transparent to show sidebar background
        menuPanel.setBorder(BorderFactory.createEmptyBorder(15, 0, 15, 0)); // Add padding

        // Menu items to display
        String[] menuItems = {"Dashboard", "Menu", "Orders", "Customers", "Analytics", "Settings"};

        // Create and add each menu button
        for (int i = 0; i < menuItems.length; i++) {
            // Create the button (first one is selected by default)
            JButton menuButton = createMenuButton(menuItems[i], i == 0);
            if (i == 0) {
                selectedButton = menuButton; // Track the initially selected button
            }
            
            menuPanel.add(menuButton);
            menuPanel.add(Box.createRigidArea(new Dimension(0, 8))); // Add spacing between buttons
            
            // Add action listener to handle selection
            menuButton.addActionListener(e -> {
                // Deselect previous button
                if (selectedButton != null) {
                    selectedButton.putClientProperty("selected", false);
                    selectedButton.repaint();
                }
                // Select this button
                selectedButton = menuButton;
                menuButton.putClientProperty("selected", true);
                menuButton.repaint();
            });
            
        }
        
        return menuPanel;
        
    }
    
    
    /**
     * Creates a menu button with custom styling and animation effects
     */
    private JButton createMenuButton(String text, boolean isSelected) {
        
        // Create a custom button that overrides paintComponent for custom rendering
        JButton menuButton = new JButton(text) {
            private float glowOpacity = 1f; // Controls glow effect intensity
            
            @Override
            protected void paintComponent(Graphics g) {
                // Use Graphics2D for better quality rendering
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                        RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_RENDERING, 
                        RenderingHints.VALUE_RENDER_QUALITY);
                
                // Find which button this is (by index) to get its scale value
                int buttonIndex = Arrays.asList(new String[]{"Dashboard", "Menu", "Orders", 
                    "Customers", "Analytics", "Settings"}).indexOf(text);
                float currentScale = hoverScales[buttonIndex];
                
                // Apply scaling transformation if button is hovered (scale > 1.0)
                if (currentScale != 1.0f) {
                    double scaleX = currentScale;
                    double scaleY = currentScale;
                    double centerX = getWidth() / 2.0;
                    double centerY = getHeight() / 2.0;
                    // Move to center, scale, then move back
                    g2d.translate(centerX, centerY);
                    g2d.scale(scaleX, scaleY);
                    g2d.translate(-centerX, -centerY);                 
                }
                
                boolean isButtonSelected = Boolean.TRUE.equals(getClientProperty("selected"));
                
                // Draw glow effect when selected
                if (isButtonSelected) {
                    // Create glow effect with decreasing opacity for outer rings
                    int glowSize = 12;
                    for (int i = glowSize; i > 0; i--) {
                        float alpha = (glowOpacity * 0.3f) * (1.0f - (float)i / glowSize);
                        g2d.setColor(new Color(
                            accentColor.getRed()/255f,
                            accentColor.getGreen()/255f,
                            accentColor.getBlue()/255f,
                            alpha
                        ));
                        g2d.fillRoundRect(
                            8 - i/2,
                            4 - i/2,
                            getWidth() - 16 + i,
                            getHeight() - 8 + i,
                            12 + i, // Corner radius X
                            12 + i  // Corner radius Y
                        );
                    }
                    
                }
                
                
                // Draw button background (only when pressed or selected)
                if (getModel().isPressed() || isButtonSelected) {
                    g2d.setColor(menuActiveColor); // Active color for selected/pressed
                } 
                
                if (getModel().isPressed() || isButtonSelected) {
                    // Draw rounded rectangle background
                    g2d.fillRoundRect(8, 4, getWidth() - 16, getHeight() - 8, 12, 12);
                }
                
                // Let the parent class draw the text and icon
                super.paintComponent(g2d);
                g2d.dispose(); // Clean up graphics resources
                
            }

        };

        // Initialize hover animation properties for this button
        int buttonIndex = Arrays.asList(new String[]{"Dashboard", "Menu", "Orders", 
            "Customers", "Analytics", "Settings"}).indexOf(text);
        hoverScales[buttonIndex] = 1.0f; // Start at normal scale (no zoom)
        
        
        // Add mouse listeners for hover effects
        menuButton.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseEntered(MouseEvent e) {
                // Show tooltip if sidebar is collapsed
                if (isSidebarCollapsed) {
                    showTooltip((String) menuButton.getClientProperty("originalText"), menuButton);
                }
                // Start hover scale animation (grow)
                startHoverAnimation(buttonIndex, true);
            }
            
            @Override
            public void mouseExited(MouseEvent e) {
                // Hide tooltip
                tooltipWindow.dispose();
                // Start hover scale animation (shrink back)
                startHoverAnimation(buttonIndex, false);
            }
            
        });
        
        // Set button appearance properties
        menuButton.setForeground(textColor);
        menuButton.setFont(new Font("Inter", Font.BOLD, 14));
        menuButton.setAlignmentX(Component.LEFT_ALIGNMENT); // Align to left side of container
        menuButton.setMaximumSize(new Dimension(240, 45)); // Limit maximum size
        menuButton.setBorderPainted(false); // Don't paint the default button border
        menuButton.setContentAreaFilled(false); // Don't fill content area (we do custom painting)
        menuButton.setFocusPainted(false); // Don't show focus rectangle
        menuButton.setHorizontalAlignment(SwingConstants.LEFT); // Align text to left
        menuButton.setBorder(new EmptyBorder(8, 16, 8, 16)); // Add padding
        menuButton.setCursor(new Cursor(Cursor.HAND_CURSOR)); // Show hand cursor on hover

        // Create and set the icon based on button name
        ImageIcon icon = createModernIcon(text.toLowerCase());
        menuButton.setIcon(icon);
        menuButton.setIconTextGap(16); // Space between icon and text
        
        // Store original text and selected state as client properties
        menuButton.putClientProperty("originalText", text);
        menuButton.putClientProperty("selected", isSelected);
        
        return menuButton;
    }
    
     /**
     * Creates custom icons for the sidebar menu items
     * @param iconName The name of the icon to create
     * @return An ImageIcon with the drawn icon
     */
    private ImageIcon createModernIcon(String iconName) {
        int size = 24; // Size of the icon in pixels
        
        // Create a blank transparent image
        BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = image.createGraphics();
        
        // Enable anti-aliasing for smoother edges
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                RenderingHints.VALUE_ANTIALIAS_ON);
        
        // Get color for this icon (or use default text color)
        Color iconColor = iconColors.getOrDefault(iconName, textColor);
        g2d.setColor(iconColor);
        
        // Set stroke style (line thickness and cap style)
        g2d.setStroke(new BasicStroke(1.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));

        // Draw different icons based on the name
        switch (iconName) {
            case "dashboard" -> {
                // Draw 4 small squares arranged in a grid
                int padding = 3;
                int gridSize = (size - 2 * padding) / 2;
                g2d.drawRoundRect(padding, padding, gridSize, gridSize, 4, 4);
                g2d.drawRoundRect(padding + gridSize + 2, padding, gridSize - 2, gridSize, 4, 4);
                g2d.drawRoundRect(padding, padding + gridSize + 2, gridSize, gridSize - 2, 4, 4);
                g2d.drawRoundRect(padding + gridSize + 2, padding + gridSize + 2, gridSize - 2, gridSize - 2, 4, 4);
            }
            case "menu" -> {
                // Draw 3 horizontal lines (hamburger menu)
                RoundRectangle2D.Double rect1 = new RoundRectangle2D.Double(3, 5, size - 6, 3, 2, 2);
                RoundRectangle2D.Double rect2 = new RoundRectangle2D.Double(3, 11, size - 6, 3, 2, 2);
                RoundRectangle2D.Double rect3 = new RoundRectangle2D.Double(3, 17, size - 6, 3, 2, 2);
                g2d.fill(rect1);
                g2d.fill(rect2);
                g2d.fill(rect3);
            }
            case "orders" -> {
                // Draw a document with lines
                g2d.drawRoundRect(3, 3, size - 6, size - 6, 6, 6);
                g2d.drawLine(7, 9, size - 7, 9);    // First line
                g2d.drawLine(7, 15, size - 7, 15);  // Second line
                g2d.drawLine(7, 21, size - 11, 21); // Third line (shorter)
            }
            case "customers" -> {
                // Draw a person icon (circle for head, arc for body)
                g2d.fillOval(7, 3, 10, 10);                   // Head
                g2d.fillArc(3, 11, 18, 12, 0, 180); // Body
            }
            case "analytics" -> {
                // Draw bar chart icon
                g2d.drawLine(3, size - 3, size - 3, size - 3); // X-axis
                g2d.drawLine(3, size - 3, 3, 3);               // Y-axis
                
                int barWidth = 3;
                // Draw 3 bars of different heights
                g2d.fillRoundRect(8, 14, barWidth, 7, 2, 2);   // Short bar
                g2d.fillRoundRect(14, 8, barWidth, 13, 2, 2);  // Tall bar
                g2d.fillRoundRect(20, 11, barWidth, 10, 2, 2); // Medium bar
            }
            case "settings" -> {
                // Draw gear icon
                int centerX = size / 2;
                int centerY = size / 2;
                int gearRadius = 8;
                
                // Draw gear teeth (8 spokes)
                for (int i = 0; i < 8; i++) {
                    double angle = i * Math.PI / 4; // 45 degree spacing
                    int x1 = centerX + (int) (gearRadius * Math.cos(angle));
                    int y1 = centerY + (int) (gearRadius * Math.sin(angle));
                    int x2 = centerX + (int) ((gearRadius + 4) * Math.cos(angle));
                    int y2 = centerY + (int) ((gearRadius + 4) * Math.sin(angle));
                    g2d.drawLine(x1, y1, x2, y2);
                }
                
                // Draw center circle of the gear
                g2d.drawOval(centerX - 4, centerY - 4, 8, 8);
            }
            
        }

        g2d.dispose(); // Clean up graphics resources
        return new ImageIcon(image);
        
    }
    
    
    
    /**
     * Animates the hover effect (scaling) of menu buttons
     * @param buttonIndex Index of the button to animate
     * @param mouseEntered True if mouse entered (grow), false if exited (shrink)
     */
    private void startHoverAnimation(int buttonIndex, boolean mouseEntered) {
        // Stop any existing animation for this button
        if (hoverTimers[buttonIndex] != null && hoverTimers[buttonIndex].isRunning()) {
            hoverTimers[buttonIndex].stop();
        }
        
        // Set target scale based on whether mouse entered or exited
        float targetScale = mouseEntered ? MAX_SCALE : 1.0f;
        float currentScale = hoverScales[buttonIndex];
        
        // Create a timer for animation 
        hoverTimers[buttonIndex] = new Timer(16, null);
        final float[] progress = {0f}; // Track animation progress
        
        hoverTimers[buttonIndex].addActionListener(e -> {
            progress[0] += 0.15f; // Increase progress by 15% each frame
            
            if (progress[0] >= 1f) {
                // Animation complete
                hoverScales[buttonIndex] = targetScale;
                ((Timer)e.getSource()).stop();
            } else {
                // Calculate eased progress for smooth animation
                // easeInOutQuad gives acceleration at start and deceleration at end
                float easedProgress = progress[0] < 0.5f
                    ? 2 * progress[0] * progress[0] // First half: quadratic easing in
                    : 1 - (float)Math.pow(-2 * progress[0] + 2, 2) / 2; // Second half: quadratic easing out
                
                // Calculate new scale based on progress
                hoverScales[buttonIndex] = currentScale + (targetScale - currentScale) * easedProgress;
            }
            
            // Repaint the sidebar so the button gets redrawn with new scale
            sidebar.repaint();
        });
        
        hoverTimers[buttonIndex].start();
    }
    
    
    /**
     * Creates an empty, transparent window for custom tooltips
     */
    private JWindow createTooltipWindow() {
        JWindow window = new JWindow();
        window.setBackground(new Color(0, 0, 0, 0)); // Fully transparent background
        return window;
    }

    /**
     * Shows a tooltip with the given text next to the specified component
     */
    private void showTooltip(String text, Component component) {
        // Create a panel for the tooltip with custom painting
        JPanel tooltipPanel = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                // Cast to Graphics2D for better rendering quality
                Graphics2D g2d = (Graphics2D) g.create();
                // Enable anti-aliasing for smoother edges
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                        RenderingHints.VALUE_ANTIALIAS_ON);
                
                int arrowSize = 8;
                
                
                // Create a custom shape for the tooltip with an arrow pointing left
                Path2D path = new Path2D.Double();
                path.moveTo(arrowSize, 0);
                path.lineTo(getWidth(), 0);
                path.lineTo(getWidth(), getHeight());
                path.lineTo(arrowSize, getHeight());
                path.lineTo(0, getHeight()/2.0); // The arrow point
                path.closePath();
                
                // Draw shadow effect slightly offset
                g2d.setColor(new Color(0, 0, 0, 50));
                g2d.translate(2, 2); // Move 2 pixels right and down
                g2d.fill(path);
                g2d.translate(-2, -2); // Move back to original position
                
                // Draw the tooltip background
                g2d.setColor(tooltipBackground);
                g2d.fill(path);
                
                // Add subtle gradient overlay
                GradientPaint gp = new GradientPaint(
                    0, 0, new Color(255, 255, 255, 25), // Light top
                    0, getHeight(), new Color(255, 255, 255, 5) // Darker bottom
                );
                g2d.setPaint(gp);
                g2d.fill(path);
                
                g2d.dispose();
                
            }
        };
        
        tooltipPanel.setLayout(new BorderLayout());
        tooltipPanel.setOpaque(false); // Make panel transparent
        
        // Create and style the text label
        JLabel label = new JLabel(text);
        label.setForeground(textColor);
        label.setFont(new Font("Inter", Font.BOLD, 13));
        label.setBorder(BorderFactory.createEmptyBorder(8, 16, 8, 16)); // Add padding
        tooltipPanel.add(label);

        // Set the tooltip content
        tooltipWindow.setContentPane(tooltipPanel);
        tooltipWindow.pack(); // Size the window to fit the content

        // Position tooltip next to the component that triggered it
        Point locationOnScreen = component.getLocationOnScreen();
        tooltipWindow.setLocation(
            locationOnScreen.x + component.getWidth() + 5, // 5px to the right of component
            locationOnScreen.y + (component.getHeight() - tooltipWindow.getHeight()) / 2 // Vertically centered
        );
        
        
        // Add fade-in animation for the tooltip
        tooltipWindow.setOpacity(0.0f); // Start fully transparent
        tooltipWindow.setVisible(true);
        
        Timer fadeTimer = new Timer(20, null); // 20ms interval for smooth animation
        float[] opacity = {0.0f};
        fadeTimer.addActionListener(e -> {
            opacity[0] += 0.1f; // Increase opacity by 10% each step
            if (opacity[0] >= 1.0f) {
                opacity[0] = 1.0f;
                fadeTimer.stop(); // Stop the timer when fully visible
            }
            tooltipWindow.setOpacity(opacity[0]);
        });
        fadeTimer.start();
        
    }
    
    
    
    /**
     * Creates the main content panel where app content would go
     */
    private JPanel createContentPanel() {
        // Create panel with custom background
        JPanel panel = new JPanel(new BorderLayout()) {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(
                        RenderingHints.KEY_ANTIALIASING, 
                        RenderingHints.VALUE_ANTIALIAS_ON
                );
                
                // Create subtle gradient background
                GradientPaint gp = new GradientPaint(
                    0, 0, backgroundColor,
                    getWidth(), getHeight(), 
                        new Color(
                                backgroundColor.getRed() - 105, 
                                backgroundColor.getGreen() - 5, 
                                backgroundColor.getBlue() - 5
                        )
                );
                g2d.setPaint(gp);
                g2d.fillRect(0, 0, getWidth(), getHeight());
                
                g2d.dispose();
                
            }
            
        };
        
        // Add some sample content
        JPanel contentArea = new JPanel(new GridBagLayout());
        contentArea.setOpaque(false);
        
        JLabel welcomeLabel = new JLabel("Welcome to The Application");
        welcomeLabel.setFont(new Font("Inter", Font.BOLD, 24));
        welcomeLabel.setForeground(new Color(23, 25, 35));
        
        contentArea.add(welcomeLabel);
        panel.add(contentArea, BorderLayout.CENTER);
        
        // Add close button to the top right of content panel
        JPanel headerPanel = new JPanel(new BorderLayout());
        headerPanel.setOpaque(false);
        
        JButton closeButton = createCloseButton();
        JPanel closeButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        closeButtonPanel.setOpaque(false);
        closeButtonPanel.add(closeButton);
        headerPanel.add(closeButtonPanel, BorderLayout.EAST);
        
        panel.add(headerPanel, BorderLayout.NORTH);

        return panel;
    }
    
    
    // This method creates a custom close button with an X symbol
    // It uses Java Swing graphics to draw the button rather than using a standard button
    private JButton createCloseButton() {
        
        // Create a new button and override its paintComponent method to customize its appearance
        JButton button = new JButton() {
            @Override
            protected void paintComponent(Graphics g) {
                // Create a Graphics2D object which gives more advanced drawing capabilities
                Graphics2D g2d = (Graphics2D) g.create();
                // Turn on anti-aliasing to make the lines look smoother
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                        RenderingHints.VALUE_ANTIALIAS_ON);
                
                // Get the current width and height of the button
                int width = getWidth();
                int height = getHeight();

                // Draw the X mark
                g2d.setColor(secondaryColor); // Use the app's primary or accentColor color 
                // Set line style: 2 pixels thick with rounded ends and corners
                g2d.setStroke(new BasicStroke(2f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
                 
                // Calculate where to start/end lines with padding from edges
                int padding = 8;        
                // Draw first diagonal line (top-left to bottom-right)
                g2d.drawLine(padding, padding, width-padding, height-padding);
                // Draw second diagonal line (top-right to bottom-left)
                g2d.drawLine(width-padding, padding, padding, height-padding);
                
                g2d.dispose();
                
            }
        };
        
        // Set button properties
        button.setPreferredSize(new Dimension(30, 30)); // Set size to 30x30 pixels
        button.setBorderPainted(false);  // Don't show a border
        button.setContentAreaFilled(false); // Don't fill the background (we're custom drawing it)
        button.setFocusPainted(false); // Don't show focus outline
        button.setCursor(new Cursor(Cursor.HAND_CURSOR)); // Show hand cursor on hover
        button.setToolTipText("Close"); // Show "Close" tooltip on hover

        // Add action listener - defines what happens when button is clicked
        button.addActionListener(e -> {
            // Find the window that contains this button
            Window window = SwingUtilities.getWindowAncestor(button);
            if (window != null) {
                window.dispose(); // Close the window
            }
        });
        
        return button;
    }
    
    
    public static void main(String[] args) {
         try {
            // Set system look and feel
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            
           } catch (Exception e) {
            e.printStackTrace();
        }
         
         
        SwingUtilities.invokeLater(() -> {
            
            JFrame frame = new JFrame("Dashboard");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(1000, 600);
            frame.setLocationRelativeTo(null);
            frame.setContentPane(new Collapsing_Sidebar());
            
            // Add window decoration
            frame.setUndecorated(true);
            frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
            
            frame.setVisible(true);
        });

    }
    
}


  


The Final Result:

Expanding Sidebar Menu in Java Swing

Collapsing Sidebar Menu in Java Swing

Expanding And Collapsing Sidebar Menu in Java Swing