张世豪
8 天以前 c57cb0cd9feb4495c89246b2faec6d5e45c23c30
src/yaokong/RemoteControlDialog.java
@@ -20,6 +20,14 @@
    private JLabel moveJoystickValueLabel;
    private JLabel turnJoystickValueLabel;
    private Timer speedUpdateTimer;  // 速度更新定时器
    // 摇杆控制定时器:持续发送控制指令
    private Timer forwardControlTimer;  // 前进/后退控制定时器
    private Timer steeringControlTimer;  // 转向控制定时器
    private int targetForwardSpeed = 0;  // 目标前进/后退速度
    private int targetSteeringSpeed = 0;  // 目标转向速度
    private List<JButton> bladeButtons = new ArrayList<>();  // 存储刀盘控制按钮,用于清理定时器
    private String bladeUpDefaultText = "刀盘升";  // 刀盘升按钮默认文字
    private String bladeDownDefaultText = "刀盘降";  // 刀盘降按钮默认文字
    
    public RemoteControlDialog(Component parent, Color themeColor, JLabel speedLabel) {
        super(parent != null ? (JFrame) SwingUtilities.getWindowAncestor(parent) : null,
@@ -31,7 +39,7 @@
        Control03.resetSpeeds();
        Control03.sendNeutralCommandIfDebugSerialOpen();
        int dialogWidth = UIConfig.DIALOG_WIDTH;  // 与首页宽度一致
        int dialogHeight = (int) (UIConfig.DIALOG_HEIGHT / 3.0 * 0.7) + 50 + 40;  // 增加40像素
        int dialogHeight = (int) (UIConfig.DIALOG_HEIGHT / 3.0 * 0.7) +150;  // 增加90像素(原40+新增40+再增10)
        initializeDialog(dialogWidth, dialogHeight);
        initializeRemoteContent();
        startSpeedUpdateTimer();  // 启动速度更新定时器
@@ -54,13 +62,69 @@
        setSize(width, height);
        setLocationRelativeTo(getOwner());
        setResizable(false);
        getContentPane().setBackground(new Color(26, 26, 46)); // 深色背景
        Container contentPane = getContentPane();
        if (contentPane instanceof JComponent) {
            JComponent jContentPane = (JComponent) contentPane;
            jContentPane.setBackground(new Color(0, 0, 0, 0)); // 透明背景
            jContentPane.setOpaque(false);
        }
    }
    
    private void initializeRemoteContent() {
        JPanel contentPanel = new JPanel(new BorderLayout());
        contentPanel.setBorder(BorderFactory.createEmptyBorder(16, 16, 16, 16));
        contentPanel.setBackground(Color.WHITE);
        contentPanel.setOpaque(false); // 设置为透明
        // 创建顶部面板,包含按钮和数值显示
        JPanel topPanel = new JPanel(new BorderLayout());
        topPanel.setOpaque(false);
        // 创建按钮面板(4个按钮,间隔30像素)
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 30, 0));
        buttonPanel.setOpaque(false);
        buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0)); // 底部间距8像素
        // 按钮1:off.png <-> starton.png,文字:启动 <-> 熄火
        JLabel label1 = new JLabel("启动", SwingConstants.CENTER);
        label1.setFont(new Font("微软雅黑", Font.PLAIN, 12));
        label1.setForeground(new Color(40, 40, 40));
        JButton button1 = createIconButtonWithLabel("image/off.png", "image/starton.png", 27, 27,
                                                     label1, "启动", "熄火", "POWER");
        JPanel panel1 = createButtonWithLabel(button1, label1);
        buttonPanel.add(panel1);
        // 按钮2:dengoff.png <-> dengon.png,文字:开灯 <-> 关灯
        JLabel label2 = new JLabel("开灯", SwingConstants.CENTER);
        label2.setFont(new Font("微软雅黑", Font.PLAIN, 12));
        label2.setForeground(new Color(40, 40, 40));
        JButton button2 = createIconButtonWithLabel("image/dengoff.png", "image/dengon.png", 30, 30,
                                                     label2, "开灯", "关灯", "LIGHT");
        JPanel panel2 = createButtonWithLabel(button2, label2);
        buttonPanel.add(panel2);
        // 按钮3:xia1.png(固定图标),文字:刀盘升
        JButton button3 = createBladeControlButton("image/xia1.png", "image/xia20.png", 30, 30, true);
        JLabel label3 = new JLabel("刀盘升", SwingConstants.CENTER);
        label3.setFont(new Font("微软雅黑", Font.PLAIN, 12));
        label3.setForeground(new Color(40, 40, 40));
        button3.putClientProperty("label", label3);
        button3.putClientProperty("defaultText", bladeUpDefaultText);
        bladeButtons.add(button3);  // 添加到列表以便清理
        JPanel panel3 = createButtonWithLabel(button3, label3);
        buttonPanel.add(panel3);
        // 按钮4:xia2.png(固定图标),文字:刀盘降
        JButton button4 = createBladeControlButton("image/xia2.png", "image/xia10.png", 30, 30, false);
        JLabel label4 = new JLabel("刀盘降", SwingConstants.CENTER);
        label4.setFont(new Font("微软雅黑", Font.PLAIN, 12));
        label4.setForeground(new Color(40, 40, 40));
        button4.putClientProperty("label", label4);
        button4.putClientProperty("defaultText", bladeDownDefaultText);
        bladeButtons.add(button4);  // 添加到列表以便清理
        JPanel panel4 = createButtonWithLabel(button4, label4);
        buttonPanel.add(panel4);
        topPanel.add(buttonPanel, BorderLayout.NORTH);
        // 在每个摇杆上方分别显示其状态/数值(分两列)
        JPanel valuesPanel = new JPanel(new GridLayout(1, 2, 20, 0));
@@ -80,14 +144,16 @@
        valuesPanel.add(moveJoystickValueLabel);
        valuesPanel.add(turnJoystickValueLabel);
        contentPanel.add(valuesPanel, BorderLayout.NORTH);
        topPanel.add(valuesPanel, BorderLayout.CENTER);
        contentPanel.add(topPanel, BorderLayout.NORTH);
        // 创建摇杆面板
        JPanel joystickPanel = new JPanel(new GridLayout(1, 2, 20, 0));
        joystickPanel.setOpaque(false);
        // 移动摇杆(绿色主题)
        moveJoystick = new ModernJoystickComponent("前进/后退",
        moveJoystick = new ModernJoystickComponent(" ",
            new Color(46, 204, 113), true);  // 绿色主题
        moveJoystick.setJoystickListener(new JoystickListener() {
            @Override
@@ -98,10 +164,15 @@
                // 限制在 [-100, 100]
                forwardVal = Math.max(-100, Math.min(100, forwardVal));
                // 更新目标速度
                targetForwardSpeed = forwardVal;
                if (Math.abs(y) > 0.1) {
                    applyForwardSpeed(forwardVal);
                    // 摇杆不在中心位置,启动持续发送定时器
                    startForwardControlTimer();
                } else {
                    // 前后
                    // 摇杆回到中心位置,停止发送
                    stopForwardControlTimer();
                    stopForward();
                }
@@ -111,7 +182,7 @@
            }
        });
        // 转向摇杆(蓝色主题)
        turnJoystick = new ModernJoystickComponent("左转/右转",
        turnJoystick = new ModernJoystickComponent("",
            new Color(52, 152, 219), false);  // 蓝色主题
        turnJoystick.setJoystickListener(new JoystickListener() {
            @Override
@@ -121,10 +192,15 @@
                int steeringVal = (int) Math.round(x * 100.0);
                steeringVal = Math.max(-100, Math.min(100, steeringVal));
                // 更新目标速度
                targetSteeringSpeed = steeringVal;
                if (Math.abs(x) > 0.1) {
                    applySteeringSpeed(steeringVal);
                    // 摇杆不在中心位置,启动持续发送定时器
                    startSteeringControlTimer();
                } else {
                    // 左右
                    // 摇杆回到中心位置,停止发送
                    stopSteeringControlTimer();
                    stopSteering();
                }
@@ -142,6 +218,360 @@
        getContentPane().add(contentPanel);
    }
    /**
     * 创建带标签的按钮面板(垂直布局:按钮在上,标签在下)
     * @param button 按钮
     * @param label 标签
     * @return 包含按钮和标签的面板
     */
    private JPanel createButtonWithLabel(JButton button, JLabel label) {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setOpaque(false);
        panel.add(button, BorderLayout.CENTER);
        panel.add(label, BorderLayout.SOUTH);
        panel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
        return panel;
    }
    /**
     * 创建可切换图标的按钮(带标签文字切换)
     * @param defaultIconPath 默认图标路径
     * @param clickedIconPath 点击后的图标路径
     * @param width 图标宽度
     * @param height 图标高度
     * @param label 标签组件
     * @param defaultText 默认文字
     * @param clickedText 点击后的文字
     * @param controlType 控制指令类型:"POWER"表示启动/熄火,"LIGHT"表示开灯/关灯,null表示无控制指令
     * @return 配置好的按钮
     */
    private JButton createIconButtonWithLabel(String defaultIconPath, String clickedIconPath,
                                               int width, int height, JLabel label,
                                               String defaultText, String clickedText, String controlType) {
        JButton button = new JButton();
        button.setPreferredSize(new Dimension(width, height));
        button.setMinimumSize(new Dimension(width, height));
        button.setMaximumSize(new Dimension(width, height));
        button.setContentAreaFilled(false);
        button.setBorderPainted(false);
        button.setFocusPainted(false);
        // 加载默认图标和点击图标
        ImageIcon defaultIcon = loadIcon(defaultIconPath, width, height);
        ImageIcon clickedIcon = loadIcon(clickedIconPath, width, height);
        if (defaultIcon != null) {
            button.setIcon(defaultIcon);
        }
        // 使用 clientProperty 来跟踪按钮状态(false=默认图标,true=点击图标)
        button.putClientProperty("isClicked", false);
        button.putClientProperty("defaultIcon", defaultIcon);
        button.putClientProperty("clickedIcon", clickedIcon);
        button.putClientProperty("label", label);
        button.putClientProperty("defaultText", defaultText);
        button.putClientProperty("clickedText", clickedText);
        button.putClientProperty("controlType", controlType);
        // 添加点击事件,切换图标和文字,并发送控制指令
        button.addActionListener(e -> {
            Boolean isClicked = (Boolean) button.getClientProperty("isClicked");
            ImageIcon defaultIconRef = (ImageIcon) button.getClientProperty("defaultIcon");
            ImageIcon clickedIconRef = (ImageIcon) button.getClientProperty("clickedIcon");
            JLabel labelRef = (JLabel) button.getClientProperty("label");
            String defaultTextRef = (String) button.getClientProperty("defaultText");
            String clickedTextRef = (String) button.getClientProperty("clickedText");
            String controlTypeRef = (String) button.getClientProperty("controlType");
            if (isClicked == null || !isClicked) {
                // 当前是默认图标,切换到点击图标
                if (clickedIconRef != null) {
                    button.setIcon(clickedIconRef);
                    button.putClientProperty("isClicked", true);
                }
                // 更新标签文字
                if (labelRef != null && clickedTextRef != null) {
                    labelRef.setText(clickedTextRef);
                }
                // 发送控制指令(开启)
                if ("POWER".equals(controlTypeRef)) {
                    boolean success = Control05.sendPowerOnIfDebugSerialOpen();
                    if (!success) {
                        showSerialClosedWarning();
                    }
                } else if ("LIGHT".equals(controlTypeRef)) {
                    boolean success = Control07.sendLightOnIfDebugSerialOpen();
                    if (!success) {
                        showSerialClosedWarning();
                    }
                }
            } else {
                // 当前是点击图标,切换回默认图标
                if (defaultIconRef != null) {
                    button.setIcon(defaultIconRef);
                    button.putClientProperty("isClicked", false);
                }
                // 更新标签文字
                if (labelRef != null && defaultTextRef != null) {
                    labelRef.setText(defaultTextRef);
                }
                // 发送控制指令(关闭)
                if ("POWER".equals(controlTypeRef)) {
                    boolean success = Control05.sendPowerOffIfDebugSerialOpen();
                    if (!success) {
                        showSerialClosedWarning();
                    }
                } else if ("LIGHT".equals(controlTypeRef)) {
                    boolean success = Control07.sendLightOffIfDebugSerialOpen();
                    if (!success) {
                        showSerialClosedWarning();
                    }
                }
            }
        });
        return button;
    }
    /**
     * 创建可切换图标的按钮
     * @param defaultIconPath 默认图标路径
     * @param clickedIconPath 点击后的图标路径
     * @param width 图标宽度
     * @param height 图标高度
     * @return 配置好的按钮
     */
    private JButton createIconButton(String defaultIconPath, String clickedIconPath, int width, int height) {
        JButton button = new JButton();
        button.setPreferredSize(new Dimension(width, height));
        button.setMinimumSize(new Dimension(width, height));
        button.setMaximumSize(new Dimension(width, height));
        button.setContentAreaFilled(false);
        button.setBorderPainted(false);
        button.setFocusPainted(false);
        // 加载默认图标和点击图标
        ImageIcon defaultIcon = loadIcon(defaultIconPath, width, height);
        ImageIcon clickedIcon = loadIcon(clickedIconPath, width, height);
        if (defaultIcon != null) {
            button.setIcon(defaultIcon);
        }
        // 使用 clientProperty 来跟踪按钮状态(false=默认图标,true=点击图标)
        button.putClientProperty("isClicked", false);
        button.putClientProperty("defaultIcon", defaultIcon);
        button.putClientProperty("clickedIcon", clickedIcon);
        // 添加点击事件,切换图标
        button.addActionListener(e -> {
            Boolean isClicked = (Boolean) button.getClientProperty("isClicked");
            ImageIcon defaultIconRef = (ImageIcon) button.getClientProperty("defaultIcon");
            ImageIcon clickedIconRef = (ImageIcon) button.getClientProperty("clickedIcon");
            if (isClicked == null || !isClicked) {
                // 当前是默认图标,切换到点击图标
                if (clickedIconRef != null) {
                    button.setIcon(clickedIconRef);
                    button.putClientProperty("isClicked", true);
                }
            } else {
                // 当前是点击图标,切换回默认图标
                if (defaultIconRef != null) {
                    button.setIcon(defaultIconRef);
                    button.putClientProperty("isClicked", false);
                }
            }
        });
        return button;
    }
    /**
     * 创建固定图标的按钮
     * @param iconPath 图标路径
     * @param width 图标宽度
     * @param height 图标高度
     * @return 配置好的按钮
     */
    private JButton createFixedIconButton(String iconPath, int width, int height) {
        JButton button = new JButton();
        button.setPreferredSize(new Dimension(width, height));
        button.setMinimumSize(new Dimension(width, height));
        button.setMaximumSize(new Dimension(width, height));
        button.setContentAreaFilled(false);
        button.setBorderPainted(false);
        button.setFocusPainted(false);
        ImageIcon icon = loadIcon(iconPath, width, height);
        if (icon != null) {
            button.setIcon(icon);
        }
        return button;
    }
    /**
     * 创建刀盘升降控制按钮(支持点击和长按)
     * @param defaultIconPath 默认图标路径
     * @param clickedIconPath 点击/长按时的图标路径
     * @param width 图标宽度
     * @param height 图标高度
     * @param isUp true表示刀盘升,false表示刀盘降
     * @return 配置好的按钮
     */
    private JButton createBladeControlButton(String defaultIconPath, String clickedIconPath,
                                             int width, int height, boolean isUp) {
        JButton button = new JButton();
        button.setPreferredSize(new Dimension(width, height));
        button.setMinimumSize(new Dimension(width, height));
        button.setMaximumSize(new Dimension(width, height));
        button.setContentAreaFilled(false);
        button.setBorderPainted(false);
        button.setFocusPainted(false);
        // 加载图标
        ImageIcon defaultIcon = loadIcon(defaultIconPath, width, height);
        ImageIcon clickedIcon = loadIcon(clickedIconPath, width, height);
        if (defaultIcon != null) {
            button.setIcon(defaultIcon);
        }
        button.putClientProperty("defaultIcon", defaultIcon);
        button.putClientProperty("clickedIcon", clickedIcon);
        button.putClientProperty("isUp", isUp);
        button.putClientProperty("isPressed", false);
        // 长按定时器
        Timer longPressTimer = new Timer(100, e -> {  // 每100ms执行一次
            if (button.getClientProperty("isPressed") == Boolean.TRUE) {
                boolean isUpButton = (Boolean) button.getClientProperty("isUp");
                int currentHeight = Control06.getCurrentBladeHeight();
                // 使用Control06的方法发送指令
                boolean success;
                if (isUpButton) {
                    success = Control06.sendBladeUpIfDebugSerialOpen(1);
                } else {
                    success = Control06.sendBladeDownIfDebugSerialOpen(1);
                }
                if (!success) {
                    showSerialClosedWarning();
                } else {
                    // 更新按钮标签显示当前数值
                    updateBladeButtonLabel(button);
                }
            }
        });
        longPressTimer.setInitialDelay(500);  // 长按500ms后开始连续变化
        button.putClientProperty("longPressTimer", longPressTimer);  // 存储定时器引用
        // 鼠标按下事件
        button.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                button.putClientProperty("isPressed", true);
                // 立即切换图标
                ImageIcon clickedIconRef = (ImageIcon) button.getClientProperty("clickedIcon");
                if (clickedIconRef != null) {
                    button.setIcon(clickedIconRef);
                }
                // 点击时立即增加/减少10,使用Control06的方法发送指令
                boolean isUpButton = (Boolean) button.getClientProperty("isUp");
                boolean success;
                if (isUpButton) {
                    success = Control06.sendBladeUpIfDebugSerialOpen(10);
                } else {
                    success = Control06.sendBladeDownIfDebugSerialOpen(10);
                }
                if (!success) {
                    showSerialClosedWarning();
                } else {
                    // 更新按钮标签显示当前数值
                    updateBladeButtonLabel(button);
                }
                // 启动长按定时器
                longPressTimer.start();
            }
            @Override
            public void mouseReleased(MouseEvent e) {
                button.putClientProperty("isPressed", false);
                // 停止长按定时器
                longPressTimer.stop();
                // 恢复默认图标
                ImageIcon defaultIconRef = (ImageIcon) button.getClientProperty("defaultIcon");
                if (defaultIconRef != null) {
                    button.setIcon(defaultIconRef);
                }
                // 恢复默认文字
                JLabel labelRef = (JLabel) button.getClientProperty("label");
                String defaultTextRef = (String) button.getClientProperty("defaultText");
                if (labelRef != null && defaultTextRef != null) {
                    labelRef.setText(defaultTextRef);
                }
            }
        });
        return button;
    }
    /**
     * 更新刀盘按钮标签显示当前数值
     * @param button 按钮
     */
    private void updateBladeButtonLabel(JButton button) {
        JLabel labelRef = (JLabel) button.getClientProperty("label");
        if (labelRef != null) {
            String defaultTextRef = (String) button.getClientProperty("defaultText");
            int currentHeight = Control06.getCurrentBladeHeight();
            String displayText = defaultTextRef + " " + currentHeight;
            labelRef.setText(displayText);
        }
    }
    /**
     * 加载并缩放图标
     * @param iconPath 图标路径
     * @param width 目标宽度
     * @param height 目标高度
     * @return 缩放后的图标
     */
    private ImageIcon loadIcon(String iconPath, int width, int height) {
        try {
            java.net.URL imgURL = getClass().getClassLoader().getResource(iconPath);
            if (imgURL == null) {
                // 尝试从文件系统加载
                java.io.File imgFile = new java.io.File(iconPath);
                if (imgFile.exists()) {
                    ImageIcon originalIcon = new ImageIcon(imgFile.getAbsolutePath());
                    Image scaledImage = originalIcon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH);
                    ImageIcon scaledIcon = new ImageIcon(scaledImage);
                    scaledIcon.setDescription(iconPath);
                    return scaledIcon;
                }
            } else {
                ImageIcon originalIcon = new ImageIcon(imgURL);
                Image scaledImage = originalIcon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH);
                ImageIcon scaledIcon = new ImageIcon(scaledImage);
                scaledIcon.setDescription(iconPath);
                return scaledIcon;
            }
        } catch (Exception e) {
            System.err.println("无法加载图标: " + iconPath + " - " + e.getMessage());
        }
        return null;
    }
    private JPanel createStatusPanel(String label, String value, Color color) {
        JPanel panel = new JPanel(new BorderLayout(0, 5));
        panel.setOpaque(false);
@@ -235,6 +665,102 @@
        }
    }
    /**
     * 启动前进/后退控制定时器,持续发送控制指令
     */
    private void startForwardControlTimer() {
        // 如果定时器已经在运行,先停止它
        if (forwardControlTimer != null && forwardControlTimer.isRunning()) {
            forwardControlTimer.stop();
        }
        // 创建新的定时器,每100ms发送一次指令
        forwardControlTimer = new Timer(100, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 持续发送目标速度的指令
                applyForwardSpeedContinuously(targetForwardSpeed);
            }
        });
        forwardControlTimer.setInitialDelay(0);
        forwardControlTimer.start();
    }
    /**
     * 停止前进/后退控制定时器
     */
    private void stopForwardControlTimer() {
        if (forwardControlTimer != null && forwardControlTimer.isRunning()) {
            forwardControlTimer.stop();
        }
    }
    /**
     * 启动转向控制定时器,持续发送控制指令
     */
    private void startSteeringControlTimer() {
        // 如果定时器已经在运行,先停止它
        if (steeringControlTimer != null && steeringControlTimer.isRunning()) {
            steeringControlTimer.stop();
        }
        // 创建新的定时器,每100ms发送一次指令
        steeringControlTimer = new Timer(100, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 持续发送目标速度的指令
                applySteeringSpeedContinuously(targetSteeringSpeed);
            }
        });
        steeringControlTimer.setInitialDelay(0);
        steeringControlTimer.start();
    }
    /**
     * 停止转向控制定时器
     */
    private void stopSteeringControlTimer() {
        if (steeringControlTimer != null && steeringControlTimer.isRunning()) {
            steeringControlTimer.stop();
        }
    }
    /**
     * 持续发送前进/后退速度指令
     */
    private void applyForwardSpeedContinuously(int targetSpeed) {
        int currentSpeed = Control03.getCurrentForwardSpeed();
        int currentSteeringSpeed = Control03.getCurrentSteeringSpeed();
        // 如果已经达到目标速度,直接发送一次以保持状态
        if (currentSpeed == targetSpeed) {
            // 直接发送目标速度指令以保持状态(即使速度相同也要发送)
            Control03.setAndSendSpeeds(currentSteeringSpeed, targetSpeed);
        } else {
            // 逐步调整到目标速度
            int delta = targetSpeed > currentSpeed ? 10 : -10;
            Control03.adjustForwardSpeed(delta);
        }
    }
    /**
     * 持续发送转向速度指令
     */
    private void applySteeringSpeedContinuously(int targetSpeed) {
        int currentSpeed = Control03.getCurrentSteeringSpeed();
        int currentForwardSpeed = Control03.getCurrentForwardSpeed();
        // 如果已经达到目标速度,直接发送一次以保持状态
        if (currentSpeed == targetSpeed) {
            // 直接发送目标速度指令以保持状态(即使速度相同也要发送)
            Control03.setAndSendSpeeds(targetSpeed, currentForwardSpeed);
        } else {
            // 逐步调整到目标速度
            int delta = targetSpeed > currentSpeed ? 15 : -15;
            Control03.adjustSteeringSpeed(delta);
        }
    }
    // 更新顶部显示的摇杆数值(在 EDT 上调用),文字根据数值映射为方向描述
    private void updateJoystickValues(int forwardVal, int steeringVal) {
        // 计算移动/转向描述文本
@@ -330,6 +856,15 @@
    @Override
    public void dispose() {
        // 停止并清理控制定时器
        stopForwardControlTimer();
        stopSteeringControlTimer();
        if (forwardControlTimer != null) {
            forwardControlTimer = null;
        }
        if (steeringControlTimer != null) {
            steeringControlTimer = null;
        }
        // 前后速度更新定时器
        if (speedUpdateTimer != null) {
            speedUpdateTimer.stop();
@@ -349,6 +884,14 @@
        if (turnJoystick != null) {
            turnJoystick.dispose();
        }
        // 停止并清理刀盘按钮的定时器
        for (JButton bladeButton : bladeButtons) {
            Timer timer = (Timer) bladeButton.getClientProperty("longPressTimer");
            if (timer != null && timer.isRunning()) {
                timer.stop();
            }
        }
        bladeButtons.clear();
        super.dispose();
    }