Python Tkinter 3D Domino Piece

How to Create a 3D Domino Piece in Python Tkinter

Python Tkinter 3D Domino Piece


In this Python Tutorial we will see How to Create a 3D Domino with 3D rotations, floating animations, and mouse interactions Using Python Tkinter.

What We Are Gonna Use In This Project:

- Python Programming Language.
- Tkinter (GUI).
- VS Editor.






Project Source Code:



import tkinter as tk
import math
from tkinter import ttk


class Domino(tk.Tk):
def __init__(self):
super().__init__()

# Window setup with centering
self.title("3D Domino")
self.geometry("800x700")
self.configure(bg="#121212")
# Center the window on the screen
self.center_window()
# Domino visual properties
self.domino_width = 120
self.domino_height = 240
self.domino_depth = 25
self.domino_color = "#fafafa"
self.dot_color = "#141414"
self.divider_color = "#282828"
# Rotation angles - current and target for smooth animation
self.current_rotation_x = -30
self.current_rotation_y = 45
self.current_rotation_z = 0
self.target_rotation_x = -30
self.target_rotation_y = 45
self.target_rotation_z = 0
# Animation properties
self.rotation_speed = 0.15 # Speed of rotation interpolation
self.float_offset = 0 # Vertical floating offset
self.floating_up = True # Direction of floating animation
# Mouse interaction properties
self.is_dragging = False
self.previous_x = 0
self.previous_y = 0
self.drag_sensitivity = 0.3 # Reduced sensitivity for smoother control
# Domino face values (1-6)
self.top_value = 6
self.bottom_value = 3
# Canvas dimensions - will be updated during rendering
self.canvas_width = 800
self.canvas_height = 600
# Initialize UI components
self.setup_ui()
# Initialize 3D geometry
self.init_face_vertices()
self.init_dot_positions()
# Start the animation loop
self.animate()



def center_window(self):
"""Center the window on the screen"""
# Update window to ensure geometry is calculated
self.update_idletasks()
# Get screen dimensions
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
# Get window dimensions
window_width = 800
window_height = 700
# Calculate center position
center_x = int(screen_width/2 - window_width/2)
center_y = int(screen_height/2 - window_height/2)
# Set window position
self.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}")


def setup_ui(self):
"""Initialize the user interface components"""
# Configure style for buttons
self.style = ttk.Style()
self.style.theme_use('clam')
self.style.configure("TButton",
background="#3d5afe",
foreground="white",
padding=8,
font=("Segoe UI", 10),
borderwidth=0)
self.style.map("TButton",
background=[('active', '#536dfe')])
# Create main container frame
main_frame = tk.Frame(self, bg="#121212")
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Create 3D rendering canvas
self.canvas = tk.Canvas(
main_frame,
bg="#121212",
highlightthickness=0,
width=800,
height=600
)
self.canvas.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
# Bind mouse events for 3D rotation interaction
self.canvas.bind("<ButtonPress-1>", self.on_mouse_down)
self.canvas.bind("<ButtonRelease-1>", self.on_mouse_up)
self.canvas.bind("<B1-Motion>", self.on_mouse_drag)
self.canvas.bind("<Enter>", lambda e: self.canvas.config(cursor="hand2"))
self.canvas.bind("<Leave>", lambda e: self.canvas.config(cursor=""))
# Bind canvas resize event to update dimensions
self.canvas.bind("<Configure>", self.on_canvas_resize)
# Create control buttons frame
controls_frame = tk.Frame(main_frame, bg="#1e1e1e", height=80)
controls_frame.pack(fill=tk.X)
# Define control buttons with their actions
buttons = [
("Rotate X", self.rotate_x),
("Rotate Y", self.rotate_y),
("Reset View", self.reset_view),
("Change Top", self.change_top),
("Change Bottom", self.change_bottom)
]
# Create and pack buttons
for text, command in buttons:
btn = ttk.Button(controls_frame, text=text, command=command, width=18)
btn.pack(side=tk.LEFT, padx=8, pady=10)


def on_canvas_resize(self, event):
"""Handle canvas resize events to maintain proper rendering"""
self.canvas_width = event.width
self.canvas_height = event.height

def init_face_vertices(self):
"""Initialize 3D coordinates for all six faces of the domino cube"""
self.face_vertices = []
# Face index constants for clarity
self.FRONT = 0 # Front face (shows domino values)
self.BACK = 1 # Back face (shows domino values reversed)
self.RIGHT = 2 # Right side face
self.LEFT = 3 # Left side face
self.TOP = 4 # Top face
self.BOTTOM = 5 # Bottom face
# Half dimensions for centered cube
w = self.domino_width / 2.0 # Half width
h = self.domino_height / 2.0 # Half height
d = self.domino_depth / 2.0 # Half depth
# Define vertices for each face
# FRONT face (facing viewer)
self.face_vertices.append([[-w, -h, d], [w, -h, d], [w, h, d], [-w, h, d]])
# BACK face (away from viewer)
self.face_vertices.append([[w, -h, -d], [-w, -h, -d], [-w, h, -d], [w, h, -d]])
# RIGHT face
self.face_vertices.append([[w, -h, d], [w, -h, -d], [w, h, -d], [w, h, d]])
# LEFT face
self.face_vertices.append([[-w, -h, -d], [-w, -h, d], [-w, h, d], [-w, h, -d]])
# TOP face
self.face_vertices.append([[-w, -h, -d], [w, -h, -d], [w, -h, d], [-w, -h, d]])
# BOTTOM face
self.face_vertices.append([[-w, h, d], [w, h, d], [w, h, -d], [-w, h, -d]])

def init_dot_positions(self):
"""Initialize dot patterns for domino values 1-6"""
# Relative positions (0-1 range) for dots on each domino face half
self.dot_positions = [
[], # 0 (not used)
[(0.5, 0.5)], # 1: center dot
[(0.25, 0.25), (0.75, 0.75)], # 2: diagonal dots
[(0.25, 0.25), (0.5, 0.5), (0.75, 0.75)], # 3: diagonal + center
[(0.25, 0.25),(0.25, 0.75),(0.75, 0.25),(0.75, 0.75)], # 4: four corners
# 5: four corners + center
[(0.25, 0.25), (0.25, 0.75), (0.5, 0.5), (0.75, 0.25), (0.75, 0.75)],
# 6: two columns
[(0.25, 0.25),(0.25, 0.5),(0.25, 0.75),(0.75, 0.25),(0.75, 0.5),(0.75, 0.75)]
]



# Mouse interaction event handlers
def on_mouse_down(self, event):
"""Handle mouse button press - start dragging"""
self.is_dragging = True
self.previous_x = event.x
self.previous_y = event.y


def on_mouse_up(self, event):
"""Handle mouse button release - stop dragging"""
self.is_dragging = False


def on_mouse_drag(self, event):
"""Handle mouse drag movement - rotate domino smoothly"""
if self.is_dragging:
# Calculate mouse movement delta
delta_x = event.x - self.previous_x
delta_y = event.y - self.previous_y
# Apply rotation based on mouse movement with reduced sensitivity
# Horizontal mouse movement rotates around Y axis (left-right rotation)
self.target_rotation_y += delta_x * self.drag_sensitivity
# Vertical mouse movement rotates around X axis (up-down rotation)
self.target_rotation_x += delta_y * self.drag_sensitivity
# Clamp rotation values to prevent excessive spinning
self.target_rotation_x = max(-90, min(90, self.target_rotation_x))
# Update previous mouse position
self.previous_x = event.x
self.previous_y = event.y


# Button action handlers
def rotate_x(self):
"""Rotate domino 90 degrees around X axis"""
self.target_rotation_x += 90


def rotate_y(self):
"""Rotate domino 90 degrees around Y axis"""
self.target_rotation_y += 90


def reset_view(self):
"""Reset domino to default viewing angle"""
self.target_rotation_x = -30
self.target_rotation_y = 45
self.target_rotation_z = 0


def change_top(self):
"""Cycle through top half values (1-6)"""
self.top_value = (self.top_value % 6) + 1


def change_bottom(self):
"""Cycle through bottom half values (1-6)"""
self.bottom_value = (self.bottom_value % 6) + 1



def animate(self):
"""Main animation loop - handles floating and rotation interpolation"""
# Handle floating animation (gentle up-down movement)
if self.floating_up:
self.float_offset += 0.15
if self.float_offset >= 8:
self.floating_up = False
else:
self.float_offset -= 0.15
if self.float_offset <= -8:
self.floating_up = True
# Smooth rotation interpolation - gradually move current rotation towards target
# This creates smooth animations instead of instant jumps
rotation_threshold = 0.1 # Minimum difference before stopping interpolation
if abs(self.target_rotation_x - self.current_rotation_x) > rotation_threshold:
self.current_rotation_x += (
self.target_rotation_x - self.current_rotation_x) * self.rotation_speed
else:
self.current_rotation_x = self.target_rotation_x
if abs(self.target_rotation_y - self.current_rotation_y) > rotation_threshold:
self.current_rotation_y += (
self.target_rotation_y - self.current_rotation_y) * self.rotation_speed
else:
self.current_rotation_y = self.target_rotation_y
if abs(self.target_rotation_z - self.current_rotation_z) > rotation_threshold:
self.current_rotation_z += (
self.target_rotation_z - self.current_rotation_z) * self.rotation_speed
else:
self.current_rotation_z = self.target_rotation_z
# Render the current frame
self.render_domino()
# Schedule next frame
self.after(16, self.animate)


def render_domino(self):
"""Main rendering function - draws the complete 3D domino"""
# Clear previous frame
self.canvas.delete("all")
# Get current canvas dimensions
width = self.canvas.winfo_width()
height = self.canvas.winfo_height()
# Ensure valid dimensions (fallback if canvas not ready)
if width < 10:
width = self.canvas_width
if height < 10:
height = self.canvas_height
# Calculate center point for domino positioning
center_x = width // 2
center_y = height // 2
# Apply scale effect for floating animation
scale_float = 1.0 + abs(self.float_offset) / 100.0
# Calculate which faces are visible and in what order
face_visibility_order = self.calculate_face_visibility()
# Draw faces in back-to-front order (painter's algorithm)
for face_idx in face_visibility_order:
self.draw_face(center_x, center_y + self.float_offset, scale_float, face_idx)


def calculate_face_visibility(self):
"""Calculate face drawing order using painter's algorithm (back to front)"""
face_z_values = []
# Calculate average Z coordinate for each face after rotation
for face_idx in range(6):
total_z = 0
for vertex in self.face_vertices[face_idx]:
rotated_vertex = self.rotate_point(vertex)
total_z += rotated_vertex[2]
# Store average Z value for this face
average_z = total_z / 4
face_z_values.append((face_idx, average_z))
# Sort faces by Z coordinate (back to front for painter's algorithm)
face_z_values.sort(key=lambda x: x[1])
# Return face indices in drawing order
return [face_idx for face_idx, z in face_z_values]
def draw_face(self, center_x, center_y, scale, face_idx):
"""Draw a single face of the domino cube"""
# Project 3D vertices to 2D screen coordinates
projected_points = []
for vertex in self.face_vertices[face_idx]:
rotated_vertex = self.rotate_point(vertex)
# Apply perspective scaling
projection_scale = 1.2 + rotated_vertex[2] / 1000.0
screen_x = center_x + rotated_vertex[0] * scale * projection_scale
screen_y = center_y + rotated_vertex[1] * scale * projection_scale
projected_points.append([screen_x, screen_y])
# Flatten points for polygon drawing
polygon_points = []
for point in projected_points:
polygon_points.extend(point)
# Determine face color based on orientation (lighting simulation)
face_colors = {
self.FRONT: "#ffffff", # Pure white for front face
self.BACK: "#ffffff", # Pure white for back face
self.LEFT: "#f0f0f0", # Slightly darker for sides
self.RIGHT: "#f0f0f0", # Slightly darker for sides
self.TOP: "#e6e6e6", # Darker for top
self.BOTTOM: "#d9d9d9" # Darkest for bottom
}
face_color = face_colors.get(face_idx, self.domino_color)
# Determine outline color (highlight front/back faces)
outline_color = "#3d5afe" if face_idx in [self.FRONT, self.BACK] else "#1a237e"
# Draw face polygon
self.canvas.create_polygon(
polygon_points,
fill=face_color,
outline=outline_color,
width=2
)
# Draw domino details only on front and back faces
if face_idx in [self.FRONT, self.BACK]:
self.draw_domino_details(projected_points, face_idx)

def draw_domino_details(self, projected_points, face_idx):
"""Draw divider line and dots on front/back faces"""
# Calculate middle points for horizontal divider
left_middle = [
(projected_points[0][0] + projected_points[3][0]) / 2,
(projected_points[0][1] + projected_points[3][1]) / 2
]
right_middle = [
(projected_points[1][0] + projected_points[2][0]) / 2,
(projected_points[1][1] + projected_points[2][1]) / 2
]
# Draw divider line
self.canvas.create_line(
left_middle[0], left_middle[1],
right_middle[0], right_middle[1],
fill="#3d5afe",
width=3
)

# Draw dots on appropriate halves
if face_idx == self.FRONT:
# Front face: top value on top, bottom value on bottom
self.draw_dots(
projected_points[0], projected_points[1],
right_middle, left_middle,
self.top_value
)
self.draw_dots(
left_middle, right_middle,
projected_points[2], projected_points[3],
self.bottom_value
)
else: # BACK face
# Back face: values are reversed
self.draw_dots(
projected_points[0], projected_points[1],
right_middle, left_middle,
self.bottom_value
)
self.draw_dots(
left_middle, right_middle,
projected_points[2], projected_points[3],
self.top_value
)

def draw_dots(self, top_left, top_right, bottom_right, bottom_left, value):
"""Draw dots pattern for given domino value on specified face area"""
if value < 1 or value > 6:
return
# Calculate face dimensions for dot sizing
width = math.sqrt((top_right[0] - top_left[0])**2 + (top_right[1] - top_left[1])**2)
height = math.sqrt((bottom_left[0] - top_left[0])**2 + (bottom_left[1] - top_left[1])**2)
# Scale dot size based on face size
dot_size = min(width, height) * 0.15
# Draw each dot in the pattern
for pos in self.dot_positions[value]:
# Calculate absolute position using bilinear interpolation
x = (top_left[0] + (top_right[0] - top_left[0]) * pos[0] +
(bottom_left[0] - top_left[0]) * pos[1])
y = (top_left[1] + (top_right[1] - top_left[1]) * pos[0] +
(bottom_left[1] - top_left[1]) * pos[1])
# Draw main dot circle
self.canvas.create_oval(
x - dot_size/2, y - dot_size/2,
x + dot_size/2, y + dot_size/2,
fill="#212121",
outline="#3d5afe",
width=1
)
# Add highlight for 3D effect
highlight_size = dot_size / 3
self.canvas.create_oval(
x - highlight_size, y - highlight_size,
x - highlight_size/3, y - highlight_size/3,
fill="#3d5afe",
outline=""
)

def rotate_point(self, point):
"""Apply 3D rotation transformations to a point"""
# Start with original point coordinates
result = [0, 0, 0]
temp = point.copy()
# Convert rotation angles to radians
rad_x = math.radians(self.current_rotation_x)
rad_y = math.radians(self.current_rotation_y)
rad_z = math.radians(self.current_rotation_z)
# Apply X rotation (pitch)
result[0] = temp[0]
result[1] = temp[1] * math.cos(rad_x) - temp[2] * math.sin(rad_x)
result[2] = temp[1] * math.sin(rad_x) + temp[2] * math.cos(rad_x)
temp = result.copy()
# Apply Y rotation (yaw)
result[0] = temp[0] * math.cos(rad_y) + temp[2] * math.sin(rad_y)
result[1] = temp[1]
result[2] = -temp[0] * math.sin(rad_y) + temp[2] * math.cos(rad_y)
temp = result.copy()
# Apply Z rotation (roll)
result[0] = temp[0] * math.cos(rad_z) - temp[1] * math.sin(rad_z)
result[1] = temp[0] * math.sin(rad_z) + temp[1] * math.cos(rad_z)
result[2] = temp[2]
return result



# Application entry point
if __name__ == "__main__":
# Create and run the domino application
app = Domino()
app.mainloop()



The Final Result:

Python Tkinter 3D Domino Piece

Python Tkinter 3D Domino Piece

Python Tkinter 3D Domino Piece

Python Tkinter 3D Domino Piece

Python Tkinter 3D Domino Piece







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