flatlaf UI theme

This commit is contained in:
Bane Stojkovic
2026-10-15 15:00:56 +02:00
parent 89a75bd94d
commit f334385967
22 changed files with 174 additions and 21 deletions
+5
View File
@@ -96,6 +96,11 @@
<artifactId>commons-compiler</artifactId>
<version>3.1.12</version>
</dependency>
<dependency>
<groupId>com.formdev</groupId>
<artifactId>flatlaf</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>net.java.balloontip</groupId>
<artifactId>balloontip</artifactId>
@@ -25,7 +25,6 @@ import java.util.Arrays;
import java.util.Properties;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.diylc.appframework.miscutils.ConfigurationManager;
@@ -42,6 +41,7 @@ import org.diylc.serialization.ProjectFileManager;
import org.diylc.swing.gui.MainFrame;
import org.diylc.swing.gui.TemplateDialog;
import org.diylc.swing.plugins.file.ProjectDrawingProvider;
import org.diylc.utils.LookAndFeelUtils;
/**
* Main class that runs DIYLC.
@@ -136,7 +136,7 @@ public class DIYLCStarter {
LOG.info("Configuration dump end.");
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
LookAndFeelUtils.applyLookAndFeel();
// Apply configurable font scaling from VM argument: -Dorg.diylc.fontScaleFactor=X
org.diylc.utils.FontScalingUtils.applyFontScaling();
@@ -34,6 +34,7 @@ import javax.swing.SwingConstants;
import javax.swing.border.MatteBorder;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.appframework.miscutils.IConfigListener;
import org.diylc.utils.LookAndFeelUtils;
/***
* Panel that has a clickable line that can used to collapse and expand the contents of the panel.
@@ -49,7 +50,8 @@ public class CollapsiblePanel extends JPanel {
private MatteBorderEx border;
private String visibilityConfigKey;
private Color defaultColor = Color.lightGray;
private Color defaultColor =
LookAndFeelUtils.themeColor(Color.lightGray, new Color(85, 88, 90));
private Color highlightColor = Color.decode("#66CCFF");
/**
@@ -24,11 +24,13 @@ import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.swingframework.ButtonDialog;
import org.diylc.common.IPlugInPort;
import org.diylc.utils.IconLoader;
import org.diylc.utils.LookAndFeelUtils;
public class InfoDialog extends ButtonDialog {
@@ -53,9 +55,10 @@ public class InfoDialog extends ButtonDialog {
protected JComponent getMainComponent() {
JLabel label = new JLabel("<html>" + message + "</html>");
label.setIcon(IconLoader.Help.getIcon());
label.setBackground(Color.white);
label.setBackground(UIManager.getColor("List.background"));
label.setOpaque(true);
label.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.lightGray), BorderFactory.createEmptyBorder(16, 16, 16, 16)));
label.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(0, 0, 1, 0,
LookAndFeelUtils.themeColor(Color.lightGray, new Color(85, 88, 90))), BorderFactory.createEmptyBorder(16, 16, 16, 16)));
label.setIconTextGap(8);
return label;
}
@@ -60,6 +60,7 @@ import org.diylc.common.IPlugInPort;
import org.diylc.common.PropertyWrapper;
import org.diylc.core.IView;
import org.diylc.presenter.Presenter;
import org.diylc.utils.LookAndFeelUtils;
public class TemplateDialog extends JDialog {
@@ -201,7 +202,8 @@ public class TemplateDialog extends JDialog {
public JPanel getInfoPanel() {
if (infoPanel == null) {
infoPanel = new JPanel();
infoPanel.setBackground(Color.decode("#FFFFCC"));
infoPanel.setBackground(
LookAndFeelUtils.themeColor(Color.decode("#FFFFCC"), Color.decode("#4B4B3A")));
infoPanel.setBorder(new JTextField().getBorder());
JLabel infoLabel =
new JLabel(
@@ -36,6 +36,7 @@ import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ITask;
@@ -91,7 +92,7 @@ public class CommentDialog extends JDialog {
private JPanel getListPanel() {
if (listPanel == null) {
listPanel = new JPanel(new GridBagLayout());
listPanel.setBackground(Color.white);
listPanel.setBackground(UIManager.getColor("List.background"));
for (CommentEntity c : comments) {
addComment(c);
@@ -107,7 +108,7 @@ public class CommentDialog extends JDialog {
private JPanel getMainPanel() {
if (mainPanel == null) {
mainPanel = new JPanel(new GridBagLayout());
mainPanel.setBackground(Color.white);
mainPanel.setBackground(UIManager.getColor("List.background"));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
@@ -21,7 +21,6 @@
*/
package org.diylc.swing.plugins.cloud.view.browser;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
@@ -37,6 +36,7 @@ import javax.swing.JRootPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.log4j.Logger;
@@ -167,7 +167,7 @@ public class CloudBrowserFrame extends JFrame implements ISimpleView {
if (searchPanel == null) {
searchPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
searchPanel.setBackground(Color.white);
searchPanel.setBackground(UIManager.getColor("List.background"));
gbc.anchor = GridBagConstraints.NORTH;
gbc.insets = new Insets(2, 2, 2, 2);
gbc.gridx = 0;
@@ -18,7 +18,6 @@
*/
package org.diylc.swing.plugins.cloud.view.browser;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.Graphics;
@@ -48,6 +47,7 @@ import javax.swing.JTextArea;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.text.DefaultCaret;
import org.apache.log4j.Logger;
import org.diylc.appframework.miscutils.InMemoryConfigurationManager;
@@ -507,7 +507,7 @@ public class ResultsScrollPanel extends JScrollPane {
}
});
buttonPanel.setBackground(Color.white);
buttonPanel.setBackground(UIManager.getColor("List.background"));
buttonPanel.add(downloadButton);
if (showEditControls) {
buttonPanel.add(editButton);
@@ -651,7 +651,7 @@ public class ResultsScrollPanel extends JScrollPane {
private JPanel getResultsPanel() {
if (resultsPanel == null) {
resultsPanel = new JPanel(new GridBagLayout());
resultsPanel.setBackground(Color.white);
resultsPanel.setBackground(UIManager.getColor("List.background"));
}
return resultsPanel;
}
@@ -21,7 +21,6 @@
*/
package org.diylc.swing.plugins.cloud.view.browser;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
@@ -31,6 +30,7 @@ import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import org.diylc.utils.IconLoader;
@@ -48,7 +48,7 @@ public class SearchHeaderPanel extends JPanel {
public SearchHeaderPanel() {
setLayout(new GridBagLayout());
this.setBackground(Color.white);
this.setBackground(UIManager.getColor("List.background"));
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.LINE_START;
gbc.insets = new Insets(2, 2, 2, 2);
@@ -43,6 +43,7 @@ import org.diylc.lang.Language;
import org.diylc.swing.ActionFactory;
import org.diylc.swing.ISwingUI;
import org.diylc.utils.IconLoader;
import org.diylc.utils.LookAndFeelUtils;
import org.diylc.utils.ResourceUtils;
/**
@@ -56,6 +57,7 @@ public class ConfigPlugin implements IPlugIn {
private static final String CONFIG_MENU = "Config";
private static final String THEME_MENU = "Theme";
private static final String UI_THEME_MENU = "UI Theme";
private static final String RULER_MENU = "Ruler Inch Subdivision...";
private static final String SNAP_MENU = "Snap To";
private static final String CATEGORIES_MENU = "Component Categories";
@@ -68,6 +70,7 @@ public class ConfigPlugin implements IPlugIn {
private static final String LANGUAGE_MENU = LangUtil.translate("Language");
private static final String LANG_RESTART = LangUtil.translate("Language selection will be applied after the application is restarted.");
private static final String UI_THEME_RESTART = LangUtil.translate("UI theme selection will be applied after the application is restarted.");
public static final String THEMES_DIR = "themes";
private ISwingUI swingUI;
@@ -138,6 +141,14 @@ public class ConfigPlugin implements IPlugIn {
} catch (Exception e) {
LOG.error("Error while setting up language menu", e);
}
swingUI.injectSubmenu(UI_THEME_MENU, IconLoader.WindowColors.getIcon(), CONFIG_MENU);
for (String uiTheme : new String[] {LookAndFeelUtils.UI_THEME_SYSTEM,
LookAndFeelUtils.UI_THEME_LIGHT, LookAndFeelUtils.UI_THEME_DARK}) {
swingUI.injectMenuAction(ActionFactory.getInstance().createToggleAction(uiTheme,
LookAndFeelUtils.UI_THEME_KEY, UI_THEME_MENU, LookAndFeelUtils.UI_THEME_DEFAULT),
UI_THEME_MENU);
}
swingUI.injectMenuAction(
@@ -231,6 +242,16 @@ public class ConfigPlugin implements IPlugIn {
swingUI.showMessage(LANG_RESTART, LANGUAGE_MENU, ISwingUI.INFORMATION_MESSAGE);
}
});
ConfigurationManager.getInstance().addConfigListener(LookAndFeelUtils.UI_THEME_KEY,
new IConfigListener() {
@Override
public void valueChanged(String key, Object value) {
swingUI.showMessage(UI_THEME_RESTART, LangUtil.translate(UI_THEME_MENU),
ISwingUI.INFORMATION_MESSAGE);
}
});
}
@Override
@@ -47,6 +47,7 @@ import org.diylc.swing.gui.ComponentPopupMenu;
import org.diylc.swing.gui.DragDropList;
import org.diylc.swing.gui.IDragDropListListener;
import org.diylc.swing.gui.SearchTextField;
import org.diylc.utils.LookAndFeelUtils;
public class ExplorerPane extends JPanel {
@@ -588,6 +589,9 @@ public class ExplorerPane extends JPanel {
private Map<String, ComponentType> componentTypes;
private String layerColor = LookAndFeelUtils.themeHtmlColor("#c0c0c0", "#707070");
private String valueColor = LookAndFeelUtils.themeHtmlColor("#666666", "#a0a0a0");
public ComponentListCellRenderer(Map<String, List<ComponentType>> componentTypeMap) {
this.componentTypes = componentTypeMap.values().stream().flatMap(x -> x.stream())
.collect(Collectors.toMap(x -> x.getInstanceClass().getCanonicalName(), x -> x));
@@ -624,12 +628,12 @@ public class ExplorerPane extends JPanel {
}
} else {
if (valueForDisplay == null || valueForDisplay.trim().isEmpty()) {
renderer.setText(String.format("<html><font color='#c0c0c0'>[L%s]</font> %s</html>",
layerId, component.getName()));
renderer.setText(String.format("<html><font color='%s'>[L%s]</font> %s</html>",
layerColor, layerId, component.getName()));
} else {
renderer.setText(String.format(
"<html><font color='#c0c0c0'>[L%s]</font> %s (<font color='#666666'>%s</font>)</html>",
layerId, component.getName(), valueForDisplay));
"<html><font color='%s'>[L%s]</font> %s (<font color='%s'>%s</font>)</html>",
layerColor, layerId, component.getName(), valueColor, valueForDisplay));
}
}
} else {
@@ -656,8 +660,8 @@ public class ExplorerPane extends JPanel {
renderer.setText(String.format("<html>[L%s] <b>%s</b> (<b>%s</b>)</html>", maxLayerId, groupName, componentCount));
} else {
renderer.setText(String.format(
"<html><font color='#c0c0c0'>[L%s]</font> %s (<font color='#666666'>%s</font>)</html>",
maxLayerId, groupName, componentCount));
"<html><font color='%s'>[L%s]</font> %s (<font color='%s'>%s</font>)</html>",
layerColor, maxLayerId, groupName, valueColor, componentCount));
}
}
@@ -0,0 +1,85 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.utils;
import java.awt.Color;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import com.formdev.flatlaf.FlatDarkLaf;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
import com.formdev.flatlaf.themes.FlatMacLightLaf;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.appframework.miscutils.Utils;
/**
* Installs the Swing look and feel selected under Config > UI Theme. The native look and feel
* remains the default; the light and dark themes are provided by FlatLaf.
*
* @author Branislav Stojkovic
*/
public class LookAndFeelUtils {
public static final String UI_THEME_KEY = "uiTheme";
public static final String UI_THEME_SYSTEM = "System";
public static final String UI_THEME_LIGHT = "Light";
public static final String UI_THEME_DARK = "Dark";
public static final String UI_THEME_DEFAULT = UI_THEME_SYSTEM;
/**
* Installs the configured look and feel. Must run before any UI component is created, since
* switching requires an application restart.
*/
public static void applyLookAndFeel() throws ClassNotFoundException, InstantiationException,
IllegalAccessException, UnsupportedLookAndFeelException {
String theme = ConfigurationManager.getInstance().readString(UI_THEME_KEY, UI_THEME_DEFAULT);
if (UI_THEME_DARK.equalsIgnoreCase(theme)) {
UIManager.setLookAndFeel(Utils.isMac() ? new FlatMacDarkLaf() : new FlatDarkLaf());
} else if (UI_THEME_LIGHT.equalsIgnoreCase(theme)) {
UIManager.setLookAndFeel(Utils.isMac() ? new FlatMacLightLaf() : new FlatLightLaf());
} else {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
}
public static boolean isDark() {
return FlatLaf.isLafDark();
}
/**
* Picks between two variants of a color that the look and feel does not provide, such as an
* accent or a highlight.
*/
public static Color themeColor(Color light, Color dark) {
return isDark() ? dark : light;
}
/**
* Same as {@link #themeColor(Color, Color)} for colors embedded into HTML, in "#rrggbb" form.
*/
public static String themeHtmlColor(String light, String dark) {
return isDark() ? dark : light;
}
}
@@ -668,3 +668,6 @@ Include stuck|Angeheftete einbeziehen
Enter group name:|Gruppennamen eingeben:
Group Components|Komponenten gruppieren
Could not apply changes to the selection. Check the log for details.|Änderungen an der Auswahl konnten nicht angewendet werden. Überprüfen Sie die Protokolldatei auf Details.
UI Theme|UI-Design
System|System
UI theme selection will be applied after the application is restarted.|Das gewählte UI-Design wird nach Neustart der Anwendung verwendet.
@@ -661,3 +661,6 @@ Include stuck
Enter group name:
Group Components
Could not apply changes to the selection. Check the log for details.
UI Theme
System
UI theme selection will be applied after the application is restarted.
@@ -664,3 +664,6 @@ Include stuck|Incluir adheridos
Enter group name:|Introduzca el nombre del grupo:
Group Components|Agrupar Componentes
Could not apply changes to the selection. Check the log for details.|No se pudieron aplicar los cambios a la selección. Verifique el registro para más detalles.
UI Theme|Tema de la interfaz
System|Sistema
UI theme selection will be applied after the application is restarted.|La selección del tema de la interfaz se aplicará después de reiniciar la aplicación.
@@ -663,3 +663,6 @@ Include stuck|Inclure les éléments collés
Enter group name:|Saisissez le nom du groupe :
Group Components|Grouper les composants
Could not apply changes to the selection. Check the log for details.|Impossible d'appliquer les modifications à la sélection. Consultez le journal pour plus de détails.
UI Theme|Thème de l'interface
System|Système
UI theme selection will be applied after the application is restarted.|La sélection du thème de l'interface sera appliquée après le redémarrage de l'application.
@@ -665,3 +665,6 @@ Include stuck|Includi agganciati
Enter group name:|Inserisci il nome del gruppo:
Group Components|Raggruppa componenti
Could not apply changes to the selection. Check the log for details.|Impossibile applicare le modifiche alla selezione. Controlla il registro per i dettagli.
UI Theme|Tema dell'interfaccia
System|Sistema
UI theme selection will be applied after the application is restarted.|La selezione del tema dell'interfaccia verrà applicata dopo il riavvio dell'applicazione.
@@ -669,3 +669,6 @@ Include stuck|Vastgeplakte meenemen
Enter group name:|Voer groepsnaam in:
Group Components|Componenten groeperen
Could not apply changes to the selection. Check the log for details.|Kon wijzigingen niet toepassen op de selectie. Controleer het logbestand voor details.
UI Theme|Interfacethema
System|Systeem
UI theme selection will be applied after the application is restarted.|Interfacethema wordt toegepast nadat het programma opnieuw is opgestart.
@@ -664,3 +664,6 @@ Include stuck|Incluir grudados
Enter group name:|Digite o nome do grupo:
Group Components|Agrupar Componentes
Could not apply changes to the selection. Check the log for details.|Não foi possível aplicar as alterações à seleção. Verifique o registro para mais detalhes.
UI Theme|Tema da interface
System|Sistema
UI theme selection will be applied after the application is restarted.|A seleção do tema da interface se aplicará depois de reiniciar o aplicativo.
@@ -660,3 +660,6 @@ Include stuck|Uključi zalepljene
Enter group name:|Unesite naziv grupe:
Group Components|Grupiši Komponente
Could not apply changes to the selection. Check the log for details.|Nije moguće primeniti izmene na izabrane komponente. Proverite log za više informacija.
UI Theme|Tema interfejsa
System|Sistemska
UI theme selection will be applied after the application is restarted.|Odabir teme interfejsa će biti primenjen nakon sledećeg pokretanja aplikacije.
@@ -662,3 +662,6 @@ Include stuck|चिपके हुए घटक शामिल करें
Enter group name:|समूह का नाम दर्ज करें:
Group Components|घटकों को समूहित करें
Could not apply changes to the selection. Check the log for details.|चयन पर परिवर्तन लागू नहीं कर सके। विवरण के लिए लॉग जांचें।
UI Theme|UI थीम
System|सिस्टम
UI theme selection will be applied after the application is restarted.|UI थीम चयन एप्लिकेशन रीस्टार्ट होने के बाद लागू होगा।
@@ -662,3 +662,6 @@ Include stuck|包括粘附的元件
Enter group name:|输入组名称:
Group Components|组合元件
Could not apply changes to the selection. Check the log for details.|无法将更改应用到所选内容。请查看日志了解详情。
UI Theme|界面主题
System|系统
UI theme selection will be applied after the application is restarted.|界面主题选择将在应用程序重启后生效。