diff --git a/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java b/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java index 7441383..33467b6 100644 --- a/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java +++ b/src/main/java/com/axis/innovators/box/AxisInnovatorsBox.java @@ -889,10 +889,12 @@ public class AxisInnovatorsBox { for (WindowsJDialog windowsJDialog : windowsJDialogList) { windowsJDialog.getContentPane().removeAll(); windowsJDialog.initUI(); + windowsJDialog.updateTheme(); windowsJDialog.revalidate(); windowsJDialog.repaint(); } ex.initUI(); + ex.updateTheme(); ex.revalidate(); RegistrationSettingsItem.overloading(); isWindow = true; diff --git a/src/main/java/com/axis/innovators/box/window/MainWindow.java b/src/main/java/com/axis/innovators/box/window/MainWindow.java index 9f33c61..d13ccc0 100644 --- a/src/main/java/com/axis/innovators/box/window/MainWindow.java +++ b/src/main/java/com/axis/innovators/box/window/MainWindow.java @@ -10,6 +10,7 @@ import org.apache.logging.log4j.Logger; import javax.swing.*; import javax.swing.Timer; +import javax.swing.border.Border; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.plaf.FontUIResource; @@ -60,20 +61,20 @@ public class MainWindow extends JFrame { private WindowsJDialog dialog; // 侧边栏颜色(比面板背景稍暗) - private static final Color SIDEBAR_COLOR = Optional.ofNullable(UIManager.getColor("Panel.background")) + private static Color SIDEBAR_COLOR = Optional.ofNullable(UIManager.getColor("Panel.background")) .orElse(new Color(0x3C3F41)); // 卡片背景(深色模式适配,比侧边栏稍亮) - private static final Color CARD_BG = Optional.ofNullable(UIManager.getColor("control")) + private static Color CARD_BG = Optional.ofNullable(UIManager.getColor("control")) .map(bg -> ThemeColors.brighten(bg, 0.1f)) .orElse(new Color(0x4A4D50)); // 卡片边框(使用系统边框色或稍亮颜色) - private static final Color CARD_BORDER = Optional.ofNullable(UIManager.getColor("controlHighlight")) + private static Color CARD_BORDER = Optional.ofNullable(UIManager.getColor("controlHighlight")) .orElse(new Color(0x5C5F61)); // 文本颜色(直接使用系统主题的文本颜色) - private static final Color TEXT_COLOR = Optional.ofNullable(UIManager.getColor("textText")) + private static Color TEXT_COLOR = Optional.ofNullable(UIManager.getColor("textText")) .orElse(new Color(0xE0E0E0)); public MainWindow() { @@ -99,6 +100,8 @@ public class MainWindow extends JFrame { UIManager.put("TextArea.font", fontRes); UIManager.put("TabbedPane.font", fontRes); UIManager.put("TitledBorder.font", fontRes); + UIManager.put("ScrollPane.border", BorderFactory.createEmptyBorder()); + UIManager.put("Panel.border", BorderFactory.createEmptyBorder()); // 图标 setIconImage(LoadIcon.loadIcon("logo.png", 32).getImage()); @@ -122,6 +125,8 @@ public class MainWindow extends JFrame { } } }); + + setLocationRelativeTo(null); } /** @@ -135,13 +140,26 @@ public class MainWindow extends JFrame { * 初始化并显示 UI */ public void initUI() { + categoryScrollPanes.clear(); + categoryToolPanels.clear(); + sideButtons.clear(); + cardScales.clear(); + cardElevations.clear(); + currentCategoryId = null; + + // 清除现有UI组件 + if (layeredPane != null) { + layeredPane.removeAll(); + } + getContentPane().removeAll(); + setTitle(LanguageManager.getLoadedLanguages().getText("mainWindow.title")); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setSize(1200, 800); - setLocationRelativeTo(null); // 主容器 JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBorder(BorderFactory.createEmptyBorder()); mainPanel.setOpaque(true); Color panelBg = UIManager.getColor("Panel.background"); if (panelBg == null) panelBg = new Color(245, 246, 248); @@ -221,6 +239,87 @@ public class MainWindow extends JFrame { // 更新 UI 字体样式(确保生效) SwingUtilities.updateComponentTreeUI(this); + + } + + /** + * 更新主题 + */ + public void updateTheme() { + // 1. 更新UI管理器默认值 + Font defaultFont = selectFont(new String[]{"Microsoft YaHei", "微软雅黑", "PingFang SC", "SimHei", "宋体", "新宋体", "SansSerif"}, 14); + FontUIResource fontRes = new FontUIResource(defaultFont); + UIManager.put("Label.font", fontRes); + UIManager.put("Button.font", fontRes); + UIManager.put("TextField.font", fontRes); + UIManager.put("TextArea.font", fontRes); + UIManager.put("TabbedPane.font", fontRes); + UIManager.put("TitledBorder.font", fontRes); + + // 2. 强制更新所有UI组件 + SwingUtilities.updateComponentTreeUI(this); + + // 3. 手动更新关键组件 + + // 更新搜索框 + if (searchField != null) { + searchField.defaultBorderColor = UIManager.getColor("TextField.borderColor"); + if (searchField.defaultBorderColor == null) { + searchField.defaultBorderColor = new Color(180, 180, 180); + } + searchField.repaint(); + } + + // 4. 特殊处理卡片颜色 + for (JComponent card : cardScales.keySet()) { + card.repaint(); + } + + // 5. 更新分类滚动面板 + for (JScrollPane scrollPane : categoryScrollPanes.values()) { + scrollPane.getVerticalScrollBar().setUI(new CustomScrollBarUI()); + scrollPane.repaint(); + } + + // 6. 更新主面板背景 + Component content = getContentPane(); + if (content instanceof JComponent) { + JComponent contentPane = (JComponent) content; + Color panelBg = UIManager.getColor("Panel.background"); + if (panelBg == null) panelBg = new Color(245, 246, 248); + contentPane.setBackground(panelBg); + contentPane.repaint(); + } + + // 7. 更新所有按钮状态 + for (JButton btn : sideButtons.values()) { + btn.setForeground(Optional.ofNullable(UIManager.getColor("textText")) + .orElse(new Color(0xE0E0E0))); + btn.repaint(); + } + + // 8. 更新当前分类显示 + if (currentCategoryId != null) { + JScrollPane currentPane = categoryScrollPanes.get(currentCategoryId); + if (currentPane != null) { + cardsLayout.show(cardsPanel, currentCategoryId); + currentPane.repaint(); + } + } + + if (sideBar != null) { + sideBar.setBackground(getSidebarColor()); + sideBar.repaint(); + } + + // 9. 确保窗口正确重绘 + revalidate(); + repaint(); + } + + private Color getSidebarColor() { + return Optional.ofNullable(UIManager.getColor("Panel.background")) + .orElse(new Color(0x3C3F41)); } private void syncLayeredBounds() { @@ -363,6 +462,14 @@ public class MainWindow extends JFrame { }); } + public void updateThemeColors() { + defaultBorderColor = UIManager.getColor("TextField.borderColor"); + if (defaultBorderColor == null) { + defaultBorderColor = new Color(180, 180, 180); + } + repaint(); + } + @Override protected void paintComponent(Graphics g) { super.paintComponent(g); @@ -433,19 +540,21 @@ public class MainWindow extends JFrame { sidebar.setOpaque(true); sidebar.setBackground(SIDEBAR_COLOR); sidebar.setPreferredSize(new Dimension(220, getHeight())); - sidebar.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + sidebar.setBorder(null); // top: logo + app name JPanel top = new JPanel(new BorderLayout()); top.setOpaque(false); - JLabel logo = new JLabel(LoadIcon.loadIcon("logo.png", 36)); - logo.setBorder(BorderFactory.createEmptyBorder(8, 6, 8, 6)); - JLabel appName = new JLabel(LanguageManager.getLoadedLanguages().getText("mainWindow.title.2")); - appName.setFont(new Font(selectFont("Segoe UI", "Microsoft YaHei", "SansSerif", 15).getName(), Font.BOLD, 15)); - appName.setForeground(Color.WHITE); - appName.setBorder(BorderFactory.createEmptyBorder(8, 6, 8, 6)); - top.add(logo, BorderLayout.WEST); - top.add(appName, BorderLayout.CENTER); + + JPanel centerPanel = new JPanel(); + centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS)); + centerPanel.setOpaque(false); + + JLabel logo = new JLabel(LoadIcon.loadIcon("logo.png", 60)); + logo.setBorder(BorderFactory.createEmptyBorder(10, 6, 10 + 10, 6)); + logo.setAlignmentX(Component.CENTER_ALIGNMENT); + centerPanel.add(logo); + top.add(centerPanel, BorderLayout.CENTER); sidebar.add(top, BorderLayout.NORTH); // list of categories @@ -468,7 +577,7 @@ public class MainWindow extends JFrame { listScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); listScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); listScroll.getVerticalScrollBar().setUI(new CustomScrollBarUI()); - listScroll.setBackground(new Color(0,0,0,0)); + listScroll.setBackground(new Color(0, 0, 0, 0)); sidebar.add(listScroll, BorderLayout.CENTER); @@ -478,59 +587,148 @@ public class MainWindow extends JFrame { bottom.setLayout(new BoxLayout(bottom, BoxLayout.Y_AXIS)); bottom.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6)); - //JButton settingsButton = new JButton("设置"); - //styleSideSmallButton(settingsButton); - //settingsButton.addActionListener(e -> showSettings()); - //settingsButton.setBackground(SIDEBAR_COLOR); - //bottom.add(settingsButton); - //bottom.add(Box.createVerticalStrut(8)); -// - //JButton aboutButton = new JButton("关于"); - //styleSideSmallButton(aboutButton); - //aboutButton.addActionListener(e -> JOptionPane.showMessageDialog(this, "作者: tzdwindows7")); - //bottom.add(aboutButton); - sidebar.add(bottom, BorderLayout.SOUTH); return sidebar; } private JButton createSideButton(ToolCategory category) { + // 竖杠距离按钮左侧位置 + final int BAR_X = 8; + // 竖杠宽度 + final int BAR_WIDTH = 5; + // 竖杠到图标的空隙 + final int GAP_BAR_TO_ICON = 12; + final Color SELECT_FILL = new Color(0, 120, 215, 20); + Color HOVER_FILL;// 悬停颜色 + if (isDarkTheme()) { + HOVER_FILL = new Color(0x535360); + } else { + HOVER_FILL = new Color(0xD0E0F6); + } + JButton button = new JButton(category.getName()); + button.setMaximumSize(new Dimension(Integer.MAX_VALUE, 44)); button.setHorizontalAlignment(SwingConstants.LEFT); - // icon 使用白色版本如果可用(你可以自己准备深色侧栏专用图标) - ImageIcon ic = category.getIconImage() != null ? category.getIconImage() : LoadIcon.loadIcon(category.getIcon(), 18); - button.setIcon(ic); - button.setIconTextGap(12); - button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - button.setForeground(TEXT_COLOR); - button.setBackground(TEXT_COLOR); - button.setOpaque(false); - button.setBorder(BorderFactory.createEmptyBorder(8, 12, 8, 12)); - button.setFocusPainted(false); - button.setFont(new Font(selectFont("Segoe UI", "Microsoft YaHei", "SansSerif", 13).getName(), Font.PLAIN, 13)); - button.addActionListener(e -> { - // 切换分类时清空搜索并触发动画切换 - searchField.setText(""); - switchCategory(category.getId().toString(), false); - }); + // 原始图标 + ImageIcon rawIcon = category.getIconImage() != null + ? category.getIconImage() + : LoadIcon.loadIcon(category.getIcon(), 18); + int rawIconWidth = (rawIcon != null) ? rawIcon.getIconWidth() : 18; + final int ICON_PADDING_LEFT = BAR_X + BAR_WIDTH + GAP_BAR_TO_ICON; // 左侧为图标预留的偏移 - // hover 效果(浅背景) - button.addMouseListener(new MouseAdapter() { - @Override public void mouseEntered(MouseEvent e) { - if (!Objects.equals(currentCategoryId, category.getId().toString())) { - button.setOpaque(true); - button.setBackground(new Color(0x4A4D50)); // 深灰微亮 + // 包装一个带左侧内边距的 Icon,使图标整体右移 + Icon paddedIcon = new Icon() { + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + if (rawIcon != null) { + int dy = (getIconHeight() - rawIcon.getIconHeight()) / 2; + rawIcon.paintIcon(c, g, x + ICON_PADDING_LEFT, y + dy); } } - @Override public void mouseExited(MouseEvent e) { - if (!Objects.equals(currentCategoryId, category.getId().toString())) { - button.setOpaque(false); - button.setBackground(new Color(0,0,0,0)); + + @Override + public int getIconWidth() { + return ICON_PADDING_LEFT + rawIconWidth; + } + + @Override + public int getIconHeight() { + return (rawIcon != null) ? rawIcon.getIconHeight() : 18; + } + }; + + button.setIcon(paddedIcon); + button.setHorizontalTextPosition(SwingConstants.RIGHT); + button.setIconTextGap(16); + button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + + // 自定义 Border:只在当前被选中时绘制左侧竖杠 + Border indicatorBorder = new Border() { + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + if (Objects.equals(currentCategoryId, category.getId().toString())) { + Graphics2D g2 = (Graphics2D) g.create(); + try { + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setColor(new Color(0x0162F1)); + g2.fillRect(BAR_X, y, BAR_WIDTH, height); + } finally { + g2.dispose(); + } } } + + @Override + public Insets getBorderInsets(Component c) { + return new Insets(0, 0, 0, 0); + } + + @Override + public boolean isBorderOpaque() { + return false; + } + }; + + // 将自定义竖杠 Border 和 内边距 EmptyBorder 组合 + button.setBorder(BorderFactory.createCompoundBorder( + indicatorBorder, + BorderFactory.createEmptyBorder(8, 8, 8, 20) + )); + + // 样式 + button.setForeground(TEXT_COLOR); + // 初始设为不填充,由 updateSelection 决定是否填充背景 + button.setOpaque(false); + button.setContentAreaFilled(true); // 允许根据 opaque/background 填充(mouse/selected 状态会切换 opaque) + button.setFocusPainted(false); + button.setFont(new Font( + selectFont("Segoe UI", "Microsoft YaHei", "SansSerif", 13).getName(), + Font.PLAIN, 13 + )); + + // helper: 更新选中/未选中时的外观(填充透明浅蓝或透明) + Runnable updateSelection = () -> { + boolean selected = Objects.equals(currentCategoryId, category.getId().toString()); + if (selected) { + button.setOpaque(true); + button.setBackground(SELECT_FILL); + } else { + button.setOpaque(false); + // 为避免残留,仍设置透明背景(透明颜色) + button.setBackground(new Color(0, 0, 0, 0)); + } + button.repaint(); + }; + + // 初始状态 + updateSelection.run(); + + // 点击 + button.addActionListener(e -> { + searchField.setText(""); + switchCategory(category.getId().toString(), false); + // 更新自己(外部如果也改变 currentCategoryId,外部应确保调用所有按钮的 repaint) + updateSelection.run(); + }); + + // 悬停:仅在未选中时显示 hover 填充;离开时恢复选中/未选中外观 + button.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + if (!Objects.equals(currentCategoryId, category.getId().toString())) { + button.setOpaque(true); + button.setBackground(HOVER_FILL); + button.repaint(); + } + } + @Override + public void mouseExited(MouseEvent e) { + // 恢复到选中/未选中状态 + updateSelection.run(); + } }); return button; @@ -570,39 +768,37 @@ public class MainWindow extends JFrame { @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g.create(); - int w = getWidth(), h = getHeight(); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + try { + int w = getWidth(), h = getHeight(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - // 轻微阴影 - g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.06f)); - g2.setColor(Color.BLACK); - for (int i = 0; i < 3; i++) { - g2.fill(new RoundRectangle2D.Float(i, i, w - i*2, h - i*2, 12, 12)); + // 动态获取当前主题颜色 + Color cardBg = getCardBg(); + Color cardBorder = getCardBorder(); + Color shadowColor = isDarkTheme() ? new Color(30, 30, 30) : Color.BLACK; + + // 1. 绘制阴影(根据主题调整透明度) + g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, isDarkTheme() ? 0.12f : 0.06f)); + g2.setColor(shadowColor); + for (int i = 0; i < 3; i++) { + g2.fill(new RoundRectangle2D.Float(i, i, w - i*2, h - i*2, 12, 12)); + } + + // 2. 绘制卡片背景(动态适应主题) + g2.setComposite(AlphaComposite.SrcOver); + g2.setColor(cardBg); + g2.fill(new RoundRectangle2D.Float(0, 0, w, h, 12, 12)); + + // 3. 绘制边框(优化为单次绘制) + g2.setColor(cardBorder); + g2.setStroke(new BasicStroke(1f)); + g2.draw(new RoundRectangle2D.Float(0.5f, 0.5f, w - 1, h - 1, 12, 12)); + + // 4. 绘制内容(通过super.paintComponent) + super.paintComponent(g); + } finally { + g2.dispose(); } - - // 卡片背景:使用浅灰而非突兀白色,跟 FlatLightLaf 保持协调 - g2.setComposite(AlphaComposite.SrcOver); - Color bg = new Color(250, 250, 250); - g2.setColor(bg); - g2.fill(new RoundRectangle2D.Float(0, 0, w, h, 12, 12)); - - // 修改卡片背景和边框颜色 - g2.setColor(CARD_BG); - g2.fill(new RoundRectangle2D.Float(0, 0, w, h, 12, 12)); - - // 边框 - g2.setColor(CARD_BORDER); - g2.setStroke(new BasicStroke(1f)); - g2.draw(new RoundRectangle2D.Float(0.5f, 0.5f, w - 1, h - 1, 12, 12)); - - // 边框 - g2.setColor(CARD_BORDER); - g2.setStroke(new BasicStroke(1f)); - g2.draw(new RoundRectangle2D.Float(0.5f, 0.5f, w - 1, h - 1, 12, 12)); - - g2.dispose(); - - super.paintComponent(g); } @Override public boolean isOpaque() { return false; } @@ -681,6 +877,21 @@ public class MainWindow extends JFrame { return card; } + private boolean isDarkTheme() { + return AxisInnovatorsBox.getMain().getRegistrationTopic().isDarkMode(); + } + + private Color getCardBg() { + return Optional.ofNullable(UIManager.getColor("control")) + .map(bg -> ThemeColors.brighten(bg, 0.1f)) + .orElse(new Color(0x4A4D50)); + } + + private Color getCardBorder() { + return Optional.ofNullable(UIManager.getColor("controlHighlight")) + .orElse(new Color(0x5C5F61)); + } + private String createToolTipHTML(ToolItem tool) { return "" + "

" + escapeHtml(tool.getName()) + "

" + @@ -1018,8 +1229,11 @@ public class MainWindow extends JFrame { // ---------- 自定义滚动条 ---------- public static class CustomScrollBarUI extends BasicScrollBarUI { @Override protected void configureScrollBarColors() { - this.thumbColor = new Color(160, 160, 180); - this.trackColor = new Color(245, 245, 248); + this.thumbColor = UIManager.getColor("ScrollBar.thumb"); + if (thumbColor == null) thumbColor = new Color(160, 160, 180); + + this.trackColor = UIManager.getColor("ScrollBar.track"); + if (trackColor == null) trackColor = new Color(245, 245, 248); } @Override protected JButton createDecreaseButton(int orientation) { return createInvisibleButton(); } @Override protected JButton createIncreaseButton(int orientation) { return createInvisibleButton(); } diff --git a/src/main/java/com/axis/innovators/box/window/WindowsJDialog.java b/src/main/java/com/axis/innovators/box/window/WindowsJDialog.java index 5260764..13cb9b9 100644 --- a/src/main/java/com/axis/innovators/box/window/WindowsJDialog.java +++ b/src/main/java/com/axis/innovators/box/window/WindowsJDialog.java @@ -60,4 +60,11 @@ public class WindowsJDialog extends JDialog { RegistrationTopic registrationTopic = axisInnovatorsBox.getRegistrationTopic(); return registrationTopic.isDarkMode(); } + + /** + * 在当前窗口中更新主题 + */ + public void updateTheme() { + SwingUtilities.updateComponentTreeUI(this); + } } diff --git a/src/main/resources/icons/ai/ai.png b/src/main/resources/icons/ai/ai.png index 2609d03..394501e 100644 Binary files a/src/main/resources/icons/ai/ai.png and b/src/main/resources/icons/ai/ai.png differ diff --git a/src/main/resources/icons/debug/debug.png b/src/main/resources/icons/debug/debug.png index f7f530f..9ae2479 100644 Binary files a/src/main/resources/icons/debug/debug.png and b/src/main/resources/icons/debug/debug.png differ diff --git a/src/main/resources/icons/logo.png b/src/main/resources/icons/logo.png index 15d6b5d..9674d6f 100644 Binary files a/src/main/resources/icons/logo.png and b/src/main/resources/icons/logo.png differ diff --git a/src/main/resources/icons/programming/programming.png b/src/main/resources/icons/programming/programming.png index e052812..071fbbe 100644 Binary files a/src/main/resources/icons/programming/programming.png and b/src/main/resources/icons/programming/programming.png differ