Affichage des articles dont le libellé est Menu. Afficher tous les articles
Affichage des articles dont le libellé est Menu. Afficher tous les articles

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