Merge pull request #2349 from SiboVG/other-dark-mode

Add high-contrast dark theme
This commit is contained in:
Sibo Van Gool 2023-10-03 22:39:39 +02:00 committed by GitHub
commit 13bf22cca3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 472 additions and 23 deletions

View File

@ -410,6 +410,7 @@ PreferencesOptionPanel.checkbox.windowInfo.ttip = If unchecked, window informati
UITheme.Auto = Auto (detect) UITheme.Auto = Auto (detect)
UITheme.Light = Light (default) UITheme.Light = Light (default)
UITheme.Dark = Dark UITheme.Dark = Dark
UITheme.DarkContrast = Dark, high-contrast
! Welcome dialog ! Welcome dialog
welcome.dlg.title = Welcome to OpenRocket welcome.dlg.title = Welcome to OpenRocket

View File

@ -1,5 +1,8 @@
package net.sf.openrocket.gui.components; package net.sf.openrocket.gui.components;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.UITheme;
import javax.swing.BoundedRangeModel; import javax.swing.BoundedRangeModel;
import javax.swing.JSlider; import javax.swing.JSlider;
import javax.swing.plaf.basic.BasicSliderUI; import javax.swing.plaf.basic.BasicSliderUI;
@ -27,7 +30,11 @@ public class BasicSlider extends JSlider {
setOrientation(orientation); setOrientation(orientation);
setInverted(inverted); setInverted(inverted);
setFocusable(false); setFocusable(false);
setUI(new BasicSliderUI(this)); if (UITheme.isLightTheme(GUIUtil.getUITheme())) {
setUI(new BasicSliderUI(this));
} else {
setUI(new DarkBasicSliderUI(this));
}
} }
} }

View File

@ -0,0 +1,51 @@
package net.sf.openrocket.gui.components;
import javax.swing.JSlider;
import javax.swing.plaf.basic.BasicSliderUI;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
/**
* BasicSliderUI for dark theme UI.
*/
public class DarkBasicSliderUI extends BasicSliderUI {
private static final Color trackColor = new Color(159, 159, 159);
private static final Color thumbColor = new Color(82, 82, 82);
private static final Color thumbBorderColor = new Color(166, 166, 166);
public DarkBasicSliderUI(JSlider b) {
super(b);
}
@Override
public void paintTrack(Graphics g) {
g.setColor(trackColor);
super.paintTrack(g);
}
@Override
public void paintThumb(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Rectangle thumbBounds = thumbRect;
int w = thumbBounds.width;
int h = thumbBounds.height;
int borderInset = 2; // Adjust this value to change the border thickness
// Draw the border
g2d.setColor(thumbBorderColor);
g2d.fillRect(thumbBounds.x, thumbBounds.y, w, h);
// Draw the thumb fill
g2d.setColor(thumbColor);
g2d.fillRect(
thumbBounds.x + borderInset - 1,
thumbBounds.y + borderInset - 1,
w - 2 * borderInset + 1,
h - 2 * borderInset + 1
);
}
}

View File

@ -9,6 +9,7 @@ import javax.swing.JFrame;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.border.Border;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.components.DescriptionArea; import net.sf.openrocket.gui.components.DescriptionArea;
@ -17,6 +18,7 @@ import net.sf.openrocket.gui.components.StyledLabel.Style;
import net.sf.openrocket.gui.components.URLLabel; import net.sf.openrocket.gui.components.URLLabel;
import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.gui.util.Icons;
import net.sf.openrocket.gui.util.UITheme;
import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BuildProperties; import net.sf.openrocket.util.BuildProperties;
@ -85,6 +87,12 @@ public class AboutDialog extends JDialog {
"Enhanced components database for OpenRocket" + href("https://github.com/dbcook/openrocket-database", true, true) + "Enhanced components database for OpenRocket" + href("https://github.com/dbcook/openrocket-database", true, true) +
"</center></html>"; "</center></html>";
private static Border border;
static {
initColors();
}
private String href(String url, boolean delimiters, boolean leadingSpace) { private String href(String url, boolean delimiters, boolean leadingSpace) {
return (leadingSpace ? " " : "") + (delimiters ? "(" : "") + "<a href=\"" + url + "\">" + url + "</a>" + (delimiters ? ")" : ""); return (leadingSpace ? " " : "") + (delimiters ? "(" : "") + "<a href=\"" + url + "\">" + url + "</a>" + (delimiters ? ")" : "");
} }
@ -142,6 +150,7 @@ public class AboutDialog extends JDialog {
DescriptionArea info = new DescriptionArea(5); DescriptionArea info = new DescriptionArea(5);
info.setBorder(border);
info.setText(CREDITS); info.setText(CREDITS);
info.setTextFont(UIManager.getFont("Label.font")); info.setTextFont(UIManager.getFont("Label.font"));
panel.add(info, "newline, width 10px, height 250lp, pushy, grow, spanx, wrap para"); panel.add(info, "newline, width 10px, height 250lp, pushy, grow, spanx, wrap para");
@ -170,4 +179,13 @@ public class AboutDialog extends JDialog {
GUIUtil.setDisposableDialogOptions(this, close); GUIUtil.setDisposableDialogOptions(this, close);
} }
private static void initColors() {
updateColors();
UITheme.Theme.addUIThemeChangeListener(AboutDialog::updateColors);
}
private static void updateColors() {
border = GUIUtil.getUITheme().getBorder();
}
} }

View File

@ -47,6 +47,7 @@ class MotorInformationPanel extends JPanel {
private static Color NO_COMMENT_COLOR; private static Color NO_COMMENT_COLOR;
private static Color WITH_COMMENT_COLOR; private static Color WITH_COMMENT_COLOR;
private static Color textColor; private static Color textColor;
private static Color dimTextColor;
private static Border border; private static Border border;
// Motors in set // Motors in set
@ -103,7 +104,7 @@ class MotorInformationPanel extends JPanel {
this.add(totalImpulseLabel, "split"); this.add(totalImpulseLabel, "split");
classificationLabel = new JLabel(); classificationLabel = new JLabel();
classificationLabel.setEnabled(false); // Gray out classificationLabel.setForeground(dimTextColor);
this.add(classificationLabel, "gapleft unrel, wrap"); this.add(classificationLabel, "gapleft unrel, wrap");
//// Avg. thrust: //// Avg. thrust:
@ -194,8 +195,8 @@ class MotorInformationPanel extends JPanel {
// Add the data and formatting to the plot // Add the data and formatting to the plot
XYPlot plot = chart.getXYPlot(); XYPlot plot = chart.getXYPlot();
changeLabelFont(plot.getRangeAxis(), -2); changeLabelFont(plot.getRangeAxis(), -2, textColor);
changeLabelFont(plot.getDomainAxis(), -2); changeLabelFont(plot.getDomainAxis(), -2, textColor);
//// Thrust curve: //// Thrust curve:
TextTitle title = new TextTitle(trans.get("TCMotorSelPan.title.Thrustcurve"), this.getFont()); TextTitle title = new TextTitle(trans.get("TCMotorSelPan.title.Thrustcurve"), this.getFont());
@ -257,6 +258,7 @@ class MotorInformationPanel extends JPanel {
NO_COMMENT_COLOR = GUIUtil.getUITheme().getDimTextColor(); NO_COMMENT_COLOR = GUIUtil.getUITheme().getDimTextColor();
WITH_COMMENT_COLOR = GUIUtil.getUITheme().getTextColor(); WITH_COMMENT_COLOR = GUIUtil.getUITheme().getTextColor();
textColor = GUIUtil.getUITheme().getTextColor(); textColor = GUIUtil.getUITheme().getTextColor();
dimTextColor = GUIUtil.getUITheme().getDimTextColor();
border = GUIUtil.getUITheme().getBorder(); border = GUIUtil.getUITheme().getBorder();
} }
@ -372,10 +374,11 @@ class MotorInformationPanel extends JPanel {
comment.setCaretPosition(0); comment.setCaretPosition(0);
} }
void changeLabelFont(ValueAxis axis, float size) { void changeLabelFont(ValueAxis axis, float size, Color color) {
Font font = axis.getTickLabelFont(); Font font = axis.getTickLabelFont();
font = font.deriveFont(font.getSize2D() + size); font = font.deriveFont(font.getSize2D() + size);
axis.setTickLabelFont(font); axis.setTickLabelFont(font);
axis.setTickLabelPaint(color);
} }
/** /**

View File

@ -609,7 +609,12 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
public static Color getColor(int index) { public static Color getColor(int index) {
return (Color) CURVE_COLORS[index % CURVE_COLORS.length]; Color color = (Color) CURVE_COLORS[index % CURVE_COLORS.length];
if (UITheme.isLightTheme(GUIUtil.getUITheme())) {
return color;
} else {
return color.brighter().brighter();
}
} }

View File

@ -1404,21 +1404,26 @@ public class SimulationPanel extends JPanel {
* Focus on the simulation table and maintain the previous row selection(s). * Focus on the simulation table and maintain the previous row selection(s).
*/ */
public void takeTheSpotlight() { public void takeTheSpotlight() {
simulationTable.requestFocusInWindow(); SwingUtilities.invokeLater(new Runnable() {
if (simulationTable.getRowCount() == 0 || simulationTable.getSelectedRows().length > 0) { @Override
return; public void run() {
} simulationTable.requestFocusInWindow();
if (previousSelection == null || previousSelection.length == 0) { if (simulationTable.getRowCount() == 0 || simulationTable.getSelectedRows().length > 0) {
simulationTable.getSelectionModel().setSelectionInterval(0, 0); return;
} else {
simulationTable.clearSelection();
for (int row : previousSelection) {
if (row < 0 || row >= simulationTable.getRowCount()) {
continue;
} }
simulationTable.addRowSelectionInterval(row, row); if (previousSelection == null || previousSelection.length == 0) {
simulationTable.getSelectionModel().setSelectionInterval(0, 0);
} else {
simulationTable.clearSelection();
for (int row : previousSelection) {
if (row < 0 || row >= simulationTable.getRowCount()) {
continue;
}
simulationTable.addRowSelectionInterval(row, row);
}
}
updateActions();
} }
} });
updateActions();
} }
} }

View File

@ -66,6 +66,7 @@ class SimulationOptionsPanel extends JPanel {
JMenu extensionMenuCopyExtension; JMenu extensionMenuCopyExtension;
private static Color textColor; private static Color textColor;
private static Color dimTextColor;
private static Border border; private static Border border;
static { static {
@ -232,6 +233,7 @@ class SimulationOptionsPanel extends JPanel {
private static void updateColors() { private static void updateColors() {
textColor = GUIUtil.getUITheme().getTextColor(); textColor = GUIUtil.getUITheme().getTextColor();
dimTextColor = GUIUtil.getUITheme().getDimTextColor();
border = GUIUtil.getUITheme().getBorder(); border = GUIUtil.getUITheme().getBorder();
} }
@ -349,7 +351,7 @@ class SimulationOptionsPanel extends JPanel {
if (simulation.getSimulationExtensions().isEmpty()) { if (simulation.getSimulationExtensions().isEmpty()) {
StyledLabel l = new StyledLabel(trans.get("simedtdlg.SimExt.noExtensions"), Style.ITALIC); StyledLabel l = new StyledLabel(trans.get("simedtdlg.SimExt.noExtensions"), Style.ITALIC);
l.setForeground(Color.DARK_GRAY); l.setForeground(dimTextColor);
currentExtensions.add(l, "growx, pad 5 5 5 5, wrap"); currentExtensions.add(l, "growx, pad 5 5 5 5, wrap");
} else { } else {
for (SimulationExtension e : simulation.getSimulationExtensions()) { for (SimulationExtension e : simulation.getSimulationExtensions()) {

View File

@ -2,6 +2,7 @@ package net.sf.openrocket.gui.util;
import com.github.weisj.darklaf.LafManager; import com.github.weisj.darklaf.LafManager;
import com.github.weisj.darklaf.theme.DarculaTheme; import com.github.weisj.darklaf.theme.DarculaTheme;
import com.github.weisj.darklaf.theme.OneDarkTheme;
import com.jthemedetecor.OsThemeDetector; import com.jthemedetecor.OsThemeDetector;
import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
@ -157,6 +158,9 @@ public class UITheme {
} }
public enum Themes implements Theme { public enum Themes implements Theme {
/*
Standard light theme
*/
LIGHT { LIGHT {
private final String displayName = trans.get("UITheme.Light"); private final String displayName = trans.get("UITheme.Light");
@ -502,8 +506,12 @@ public class UITheme {
return "tracker"; return "tracker";
} }
}, },
/*
Dark theme
*/
DARK { DARK {
private final String displayName = trans.get("UITheme.Dark"); private final String displayName = trans.get("UITheme.Dark");
@Override @Override
public void applyTheme() { public void applyTheme() {
final SwingPreferences prefs = (SwingPreferences) Application.getPreferences(); final SwingPreferences prefs = (SwingPreferences) Application.getPreferences();
@ -537,7 +545,7 @@ public class UITheme {
@Override @Override
public Color getDimTextColor() { public Color getDimTextColor() {
return new Color(162, 162, 162); return new Color(182, 182, 182);
} }
@Override @Override
@ -845,6 +853,355 @@ public class UITheme {
return "tracker_dark"; return "tracker_dark";
} }
}, },
/*
High-contrast dark theme
*/
DARK_CONTRAST {
private final String displayName = trans.get("UITheme.DarkContrast");
@Override
public void applyTheme() {
final SwingPreferences prefs = (SwingPreferences) Application.getPreferences();
LafManager.install(new OneDarkTheme());
setGlobalFontSize(prefs.getUIFontSize());
}
@Override
public String getDisplayName() {
return displayName;
}
@Override
public Color getBackgroundColor() {
return new Color(43, 45, 51);
}
@Override
public Color getBorderColor() {
return new Color(163, 163, 163, 204);
}
@Override
public Color getTextColor() {
return UIManager.getColor("Label.foreground");
}
@Override
public Color getDimTextColor() {
return new Color(165, 171, 184);
}
@Override
public Color getTextSelectionForegroundColor() {
return Color.WHITE;
}
@Override
public Color getTextSelectionBackgroundColor() {
return new Color(62, 108, 173);
}
@Override
public Color getWarningColor() {
return new Color(255, 173, 173);
}
@Override
public Color getDarkWarningColor() {
return new Color(255, 178, 178);
}
@Override
public Color getRowBackgroundLighterColor() {
return new Color(43, 49, 58);
}
@Override
public Color getRowBackgroundDarkerColor() {
return new Color(34, 37, 44);
}
@Override
public Color getFlightDataTextActiveColor() {
return new Color(212, 230, 255);
}
@Override
public Color getFlightDataTextInactiveColor() {
return new Color(170, 201, 255, 127);
}
@Override
public Color getMultiCompEditColor() {
return new Color(255, 165, 200);
}
@Override
public String getDefaultBodyComponentColor() {
return "150,175,255";
}
@Override
public String getDefaultTubeFinSetColor() {
return "150,184,254";
}
@Override
public String getDefaultFinSetColor() {
return "150,184,255";
}
@Override
public String getDefaultLaunchLugColor() {
return "142,153,238";
}
@Override
public String getDefaultRailButtonColor() {
return "142,153,238";
}
@Override
public String getDefaultInternalComponentColor() {
return "181,128,151";
}
@Override
public String getDefaultMassObjectColor() {
return "210,210,210";
}
@Override
public String getDefaultRecoveryDeviceColor() {
return "220,90,90";
}
@Override
public String getDefaultPodSetColor() {
return "190,190,235";
}
@Override
public String getDefaultParallelStageColor() {
return "210,180,195";
}
@Override
public Color getMotorBorderColor() {
return new Color(255, 255, 255, 200);
}
@Override
public Color getMotorFillColor() {
return new Color(0, 0, 0, 70);
}
@Override
public Color getCGColor() {
return new Color(85, 133, 253);
}
@Override
public Color getCPColor() {
return new Color(255, 72, 106);
}
@Override
public Color getURLColor() {
return new Color(171, 185, 255);
}
@Override
public Color getComponentTreeBackgroundColor() {
return getBackgroundColor();
}
@Override
public Color getComponentTreeForegroundColor() {
return getTextColor();
}
@Override
public Color getFinPointGridMajorLineColor() {
return new Color(164, 164, 224, 197);
}
@Override
public Color getFinPointGridMinorLineColor() {
return new Color(134, 134, 201, 69);
}
@Override
public Color getFinPointPointColor() {
return new Color(242, 121, 121, 255);
}
@Override
public Color getFinPointSelectedPointColor() {
return new Color(232, 78, 78, 255);
}
@Override
public Color getFinPointBodyLineColor() {
return Color.WHITE;
}
@Override
public Icon getMassOverrideIcon() {
return Icons.MASS_OVERRIDE_DARK;
}
@Override
public Icon getMassOverrideSubcomponentIcon() {
return Icons.MASS_OVERRIDE_SUBCOMPONENT_DARK;
}
@Override
public Icon getCGOverrideIcon() {
return Icons.CG_OVERRIDE_DARK;
}
@Override
public Icon getCGOverrideSubcomponentIcon() {
return Icons.CG_OVERRIDE_SUBCOMPONENT_DARK;
}
@Override
public Icon getCDOverrideIcon() {
return Icons.CD_OVERRIDE_DARK;
}
@Override
public Icon getCDOverrideSubcomponentIcon() {
return Icons.CD_OVERRIDE_SUBCOMPONENT_DARK;
}
@Override
public Border getBorder() {
return BorderFactory.createLineBorder(getBorderColor());
}
@Override
public void formatScriptTextArea(RSyntaxTextArea textArea) {
try {
org.fife.ui.rsyntaxtextarea.Theme theme = org.fife.ui.rsyntaxtextarea.Theme.load(getClass().getResourceAsStream(
"/org/fife/ui/rsyntaxtextarea/themes/monokai.xml"));
theme.apply(textArea);
} catch (IOException ioe) {
log.warn("Unable to load RSyntaxTextArea theme", ioe);
}
}
@Override
public String getComponentIconNoseCone() {
return DARK.getComponentIconNoseCone();
}
@Override
public String getComponentIconBodyTube() {
return DARK.getComponentIconBodyTube();
}
@Override
public String getComponentIconTransition() {
return DARK.getComponentIconTransition();
}
@Override
public String getComponentIconTrapezoidFinSet() {
return DARK.getComponentIconTrapezoidFinSet();
}
@Override
public String getComponentIconEllipticalFinSet() {
return DARK.getComponentIconEllipticalFinSet();
}
@Override
public String getComponentIconFreeformFinSet() {
return DARK.getComponentIconFreeformFinSet();
}
@Override
public String getComponentIconTubeFinSet() {
return DARK.getComponentIconTubeFinSet();
}
@Override
public String getComponentIconLaunchLug() {
return DARK.getComponentIconLaunchLug();
}
@Override
public String getComponentIconRailButton() {
return DARK.getComponentIconRailButton();
}
@Override
public String getComponentIconInnerTube() {
return DARK.getComponentIconInnerTube();
}
@Override
public String getComponentIconTubeCoupler() {
return DARK.getComponentIconTubeCoupler();
}
@Override
public String getComponentIconCenteringRing() {
return DARK.getComponentIconCenteringRing();
}
@Override
public String getComponentIconBulkhead() {
return DARK.getComponentIconBulkhead();
}
@Override
public String getComponentIconEngineBlock() {
return DARK.getComponentIconEngineBlock();
}
@Override
public String getComponentIconParachute() {
return DARK.getComponentIconParachute();
}
@Override
public String getComponentIconStreamer() {
return DARK.getComponentIconStreamer();
}
@Override
public String getComponentIconShockCord() {
return DARK.getComponentIconShockCord();
}
@Override
public String getComponentIconMass() {
return DARK.getComponentIconMass();
}
@Override
public String getComponentIconStage() {
return DARK.getComponentIconStage();
}
@Override
public String getComponentIconBoosters() {
return DARK.getComponentIconBoosters();
}
@Override
public String getComponentIconPods() {
return DARK.getComponentIconPods();
}
@Override
public String getComponentIconMassAltimeter() {
return DARK.getComponentIconMassAltimeter();
}
@Override
public String getComponentIconMassBattery() {
return DARK.getComponentIconMassBattery();
}
@Override
public String getComponentIconMassDeploymentCharge() {
return DARK.getComponentIconMassDeploymentCharge();
}
@Override
public String getComponentIconMassPayload() {
return DARK.getComponentIconMassPayload();
}
@Override
public String getComponentIconMassFlightComp() {
return DARK.getComponentIconMassFlightComp();
}
@Override
public String getComponentIconMassRecoveryHardware() {
return DARK.getComponentIconMassRecoveryHardware();
}
@Override
public String getComponentIconMassTracker() {
return DARK.getComponentIconMassTracker();
}
},
/*
Detect best theme based on operating system theme
*/
AUTO { AUTO {
private final String displayName = trans.get("UITheme.Auto"); private final String displayName = trans.get("UITheme.Auto");
@ -853,7 +1210,7 @@ public class UITheme {
final OsThemeDetector detector = OsThemeDetector.getDetector(); final OsThemeDetector detector = OsThemeDetector.getDetector();
final boolean isDarkThemeUsed = detector.isDark(); final boolean isDarkThemeUsed = detector.isDark();
if (isDarkThemeUsed) { if (isDarkThemeUsed) {
return Themes.DARK; return Themes.DARK_CONTRAST;
} else { } else {
return Themes.LIGHT; return Themes.LIGHT;
} }