Skip to content

Commit eee5696

Browse files
[MOD] GUI, FlatLaf: Dark-mode support
1 parent e0f899e commit eee5696

35 files changed

+205
-187
lines changed

basex-core/src/main/java/org/basex/gui/GUIConstants.java

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.basex.gui;
22

3+
import static org.basex.gui.GUICommand.*;
34
import static org.basex.gui.GUIMenuCmd.*;
45

56
import java.awt.*;
@@ -184,48 +185,41 @@ public enum Msg {
184185
}
185186
}
186187

187-
// DUMMY OBJECTS ============================================================
188-
189-
/** Dummy text field. */
190-
private static final JTextField TEXTFIELD = new JTextField();
191-
/** Dummy label, used for size calculations. */
192-
private static final JLabel LABEL = new JLabel();
193-
194188
// COLORS =======================================================================================
195189

190+
/** Indicates if a dark color mode is used. */
191+
public static boolean dark;
192+
196193
/** Background color. */
197-
public static final Color BACK = new Color(TEXTFIELD.getBackground().getRGB());
194+
public static Color backColor;
198195
/** Text color. */
199-
public static final Color TEXT = new Color(TEXTFIELD.getForeground().getRGB());
196+
public static Color textColor;
200197
/** Panel color. */
201-
public static final Color PANEL = new Color(LABEL.getBackground().getRGB());
202-
203-
/** Dark theme. */
204-
private static final boolean INVERT = BACK.getRed() + BACK.getGreen() + BACK.getBlue() < 384;
198+
public static Color panelColor;
205199

206200
/** Color: red. */
207-
public static final Color RED = color(224, 0, 0);
201+
public static Color red;
208202
/** Color: light red. */
209-
public static final Color LRED = color(255, 216, 216);
203+
public static Color lightRed;
210204
/** Color: green. */
211-
public static final Color GREEN = color(32, 160, 32);
205+
public static Color green;
212206
/** Color: cyan. */
213-
public static final Color BLUE = color(32, 96, 192);
207+
public static Color blue;
214208
/** Color: purple. */
215-
public static final Color PURPLE = color(160, 32, 160);
216-
/** Color: gray. */
217-
public static final Color GRAY = color(0, 160, 160);
218-
/** Color: dark gray. */
219-
public static final Color DGRAY = color(112, 112, 112);
209+
public static Color purple;
210+
/** Color: cyan. */
211+
public static Color cyan;
212+
/** Color: brown. */
213+
public static Color brown;
220214

221215
/** Cell color. */
222-
public static Color lgray;
216+
public static Color lightGray;
223217
/** Button color. */
224218
public static Color gray;
225219
/** Middle gray color. */
226-
public static Color mgray;
227-
/** Background color. */
228-
public static Color dgray;
220+
public static Color middleGray;
221+
/** Dark gray color. */
222+
public static Color darkGray;
229223

230224
/** Cached color gradient. */
231225
private static final Color[] COLORS = new Color[100];
@@ -308,9 +302,10 @@ public static String[] fonts() {
308302
* @return fonts
309303
*/
310304
public static String[] monoFonts() {
305+
final JTextField text = new JTextField();
311306
final StringList monos = new StringList();
312307
for(final String name : fonts()) {
313-
if(isMono(LABEL.getFontMetrics(new Font(name, Font.PLAIN, 100)))) monos.add(name);
308+
if(isMono(text.getFontMetrics(new Font(name, Font.PLAIN, 100)))) monos.add(name);
314309
}
315310
return monos.finish();
316311
}
@@ -354,10 +349,25 @@ private GUIConstants() { }
354349
* @param opts gui options
355350
*/
356351
public static synchronized void init(final GUIOptions opts) {
357-
lgray = color(224, 224, 224);
352+
final JTextField text = new JTextField();
353+
final JLabel label = new JLabel();
354+
backColor = new Color(text.getBackground().getRGB());
355+
textColor = new Color(text.getForeground().getRGB());
356+
panelColor = new Color(label.getBackground().getRGB());
357+
dark = backColor.getRed() + backColor.getGreen() + backColor.getBlue() < 384;
358+
359+
red = color(224, 0, 0);
360+
lightRed = color(255, 216, 216);
361+
green = color(32, 160, 32);
362+
blue = color(32, 96, 192);
363+
purple = color(160, 32, 160);
364+
cyan = color(0, 160, 160);
365+
brown = color(112, 112, 112);
366+
367+
lightGray = color(224, 224, 224);
358368
gray = color(160, 160, 160);
359-
mgray = color(128, 128, 128);
360-
dgray = color(64, 64, 64);
369+
middleGray = color(128, 128, 128);
370+
darkGray = color(64, 64, 64);
361371

362372
// create color array
363373
final int r = opts.get(GUIOptions.COLORRED);
@@ -391,7 +401,7 @@ public static synchronized void init(final GUIOptions opts) {
391401
font = new Font(name, Font.PLAIN, fontSize);
392402
mfont = new Font(opts.get(GUIOptions.MONOFONT), Font.PLAIN, fontSize);
393403
bfont = new Font(name, Font.BOLD, fontSize);
394-
dmfont = new Font(opts.get(GUIOptions.MONOFONT), Font.PLAIN, LABEL.getFont().getSize());
404+
dmfont = new Font(opts.get(GUIOptions.MONOFONT), Font.PLAIN, label.getFont().getSize());
395405
}
396406

397407
/**
@@ -416,27 +426,31 @@ private static int darker(final int c, final int f) {
416426
}
417427

418428
/**
419-
* Returns the specified color with the specified RGB values, or its inverted version
420-
* if {@link #INVERT} is true.
429+
* Returns the specified color with the specified RGB values, or a version suited for dark mode.
421430
* @param r red component
422431
* @param g green component
423432
* @param b blue component
424433
* @return converted color
425434
*/
426435
private static Color color(final int r, final int g, final int b) {
427-
return INVERT ? new Color(255 - r, 255 - g, 255 - b) : new Color(r, g, b);
436+
return color(r, g, b, 255);
428437
}
429438

430439
/**
431-
* Returns the specified color with the specified RGB and alpha values, or its inverted version
432-
* if {@link #INVERT} is true.
440+
* Returns the specified color with the specified RGB and alpha values,
441+
* or a version suited for dark mode.
433442
* @param r red component
434443
* @param g green component
435444
* @param b blue component
436445
* @param a alpha component
437446
* @return converted color
438447
*/
439448
private static Color color(final int r, final int g, final int b, final int a) {
440-
return INVERT ? new Color(255 - r, 255 - g, 255 - b, 255 - a) : new Color(r, g, b, a);
449+
if(dark) {
450+
final double l = .2126 * r + .7152 * g + .0722 * b, m = (255 - l) / l;
451+
return new Color((int) Math.min(255, r * m), (int) Math.min(255, g * m),
452+
(int) Math.min(255, b * m), a);
453+
}
454+
return new Color(r, g, b, a);
441455
}
442456
}

basex-core/src/main/java/org/basex/gui/GUIStatus.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ public final class GUIStatus extends BaseXPanel {
4040
*/
4141
public void setText(final String txt, final boolean ok) {
4242
label.setText(txt);
43-
label.setForeground(ok ? GUIConstants.TEXT : GUIConstants.RED);
43+
label.setForeground(ok ? GUIConstants.textColor : GUIConstants.red);
4444
}
4545
}

basex-core/src/main/java/org/basex/gui/dialog/DialogAbout.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ private DialogAbout(final GUI gui) {
4343

4444
pp.add(new BaseXLabel(TITLE, false, true));
4545
final BaseXLabel url = new BaseXLabel("<html><u>" + PUBLIC_URL + "</u></html>");
46-
url.setForeground(GUIConstants.BLUE);
46+
url.setForeground(GUIConstants.blue);
4747
url.setCursor(GUIConstants.CURSORHAND);
4848
url.addMouseListener((MouseClickedListener) e -> BaseXDialog.browse(gui, PUBLIC_URL));
4949

basex-core/src/main/java/org/basex/gui/dialog/DialogInstallURL.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ final class DialogInstallURL extends BaseXDialog {
3535
info = new BaseXLabel(" ");
3636

3737
final BaseXLabel link = new BaseXLabel("<html><u>" + REPO_URL + "</u></html>");
38-
link.setForeground(GUIConstants.BLUE);
38+
link.setForeground(GUIConstants.blue);
3939
link.setCursor(GUIConstants.CURSORHAND);
4040
link.addMouseListener((MouseClickedListener) e -> BaseXDialog.browse(gui, REPO_URL));
4141

basex-core/src/main/java/org/basex/gui/layout/BaseXCombo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ public void setSelectedItem(final Object object) {
269269
*/
270270
public synchronized void highlight(final boolean hits) {
271271
final BaseXTextField tf = textField();
272-
(tf != null ? tf : this).setBackground(hits ? GUIConstants.BACK : GUIConstants.LRED);
272+
(tf != null ? tf : this).setBackground(hits ? GUIConstants.backColor : GUIConstants.lightRed);
273273
}
274274

275275
@Override

basex-core/src/main/java/org/basex/gui/layout/BaseXDSlider.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,9 @@ public void paintComponent(final Graphics g) {
205205
final int s = 4;
206206

207207
final boolean focus = hasFocus();
208-
g.setColor(BACK);
208+
g.setColor(backColor);
209209
g.fillRect(0, hc - s, w, s << 1);
210-
g.setColor(TEXT);
210+
g.setColor(textColor);
211211
g.drawLine(0, hc - s, w - 1, hc - s);
212212
g.drawLine(0, hc - s, 0, hc + s);
213213
g.setColor(color2);
@@ -221,7 +221,7 @@ public void paintComponent(final Graphics g) {
221221
g.setColor(color4);
222222
g.drawLine(r.xs + ARROW, 3, r.xs + ARROW, h - 4);
223223
g.drawLine(r.xe - 1, 3, r.xe - 1, h - 4);
224-
g.setColor(BACK);
224+
g.setColor(backColor);
225225
if(r.xs + ARROW + 2 < r.xe) {
226226
g.drawLine(r.xs + ARROW + 1, 4, r.xs + ARROW + 1, h - 5);
227227
g.drawLine(r.xe, 4, r.xe, h - 5);
@@ -239,18 +239,18 @@ public void paintComponent(final Graphics g) {
239239
pol.xpoints = new int[] { r.xe + 5, r.xe + 12, r.xe + 12, r.xe + 5 };
240240
g.fillPolygon(pol);
241241

242-
g.setColor(focus ? TEXT : dgray);
242+
g.setColor(focus ? textColor : darkGray);
243243
g.drawLine(r.xs + 11, hc - 5, r.xs + 11, hc + 4);
244244
g.drawLine(r.xs + 11, hc - 5, r.xs + 6, hc - 1);
245245
g.drawLine(r.xe + 5, hc - 5, r.xe + 5, hc + 4);
246246
g.drawLine(r.xe + 5, hc - 5, r.xe + 11, hc - 1);
247247

248-
g.setColor(BACK);
248+
g.setColor(backColor);
249249
g.drawLine(r.xs + 10, hc + 4, r.xs + 6, hc + 1);
250250
g.drawLine(r.xe + 6, hc + 4, r.xe + 11, hc + 1);
251251

252252
// draw range info
253-
g.setColor(TEXT);
253+
g.setColor(textColor);
254254
final double mn = (long) (currMin * 100) / 100.0;
255255
final double mx = (long) (currMax * 100) / 100.0;
256256

basex-core/src/main/java/org/basex/gui/layout/BaseXHeader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public final class BaseXHeader extends BaseXLabel {
1515
*/
1616
public BaseXHeader(final String string) {
1717
super(string, true, false);
18-
setForeground(GUIConstants.dgray);
18+
setForeground(GUIConstants.darkGray);
1919
resize(1.7f);
2020
border(0, 0, 4, 4);
2121
}

basex-core/src/main/java/org/basex/gui/layout/BaseXImages.java

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import javax.swing.*;
1111
import javax.swing.filechooser.*;
1212

13+
import org.basex.gui.*;
1314
import org.basex.index.resource.*;
1415
import org.basex.io.*;
1516
import org.basex.util.*;
@@ -32,24 +33,6 @@ public final class BaseXImages {
3233
/** System icons. */
3334
private static final FileSystemView FS = FileSystemView.getFileSystemView();
3435

35-
/** Icon for XML resources. */
36-
private static final Icon DB_XML = icon("db_xml");
37-
/** Icon for binary resources. */
38-
private static final Icon DB_BIN = icon("db_bin");
39-
/** Icon for value resources. */
40-
private static final Icon DB_VAL = icon("db_val");
41-
42-
/** Icon for closed directories. */
43-
private static final Icon DIR_CLOSED = icon("dir_closed");
44-
/** Icon for opened directories. */
45-
private static final Icon DIR_OPENED = icon("dir_opened");
46-
/** Icon for textual files. */
47-
private static final Icon FILE_TEXT = icon("file_text");
48-
/** Icon for XML/XQuery file types. */
49-
private static final Icon FILE_XML = icon("file_xml");
50-
/** Icon for XML/XQuery file types. */
51-
private static final Icon FILE_XQUERY = icon("file_xquery");
52-
5336
/** Private constructor. */
5437
private BaseXImages() { }
5538

@@ -67,7 +50,8 @@ public static Image get(final String name) {
6750
final URL url = BaseXImages.class.getResource(path);
6851
if(url == null) throw Util.notExpected("Image missing: " + path);
6952
try {
70-
images[i] = ImageIO.read(url);
53+
final BufferedImage image = ImageIO.read(url);
54+
images[i] = GUIConstants.dark ? invert(image) : image;
7155
} catch(final IOException ex) {
7256
throw Util.notExpected(ex);
7357
}
@@ -77,6 +61,25 @@ public static Image get(final String name) {
7761
return IMAGES.get(name);
7862
}
7963

64+
/**
65+
* Inverts gray-scale images.
66+
* @param image image
67+
* @return inverted image, or original image if it contains colors
68+
*/
69+
private static BufferedImage invert(final BufferedImage image) {
70+
final BufferedImage tmp = new BufferedImage(image.getWidth(), image.getHeight(),
71+
BufferedImage.TYPE_INT_ARGB);
72+
for(int x = 0; x < image.getWidth(); x++) {
73+
for(int y = 0; y < image.getHeight(); y++) {
74+
final int rgb = image.getRGB(x, y);
75+
final int r = (rgb >> 16) & 0xFF, g = (rgb >> 8) & 0xFF, b = rgb & 0xFF;
76+
if(r != g || r != b) return image;
77+
tmp.setRGB(x, y, rgb ^ 0x00FFFFFF);
78+
}
79+
}
80+
return tmp;
81+
}
82+
8083
/**
8184
* Returns the specified image as icon.
8285
* @param name name of icon
@@ -92,7 +95,7 @@ public static ImageIcon icon(final String name) {
9295
* @return icon
9396
*/
9497
public static Icon dir(final boolean opened) {
95-
return opened ? DIR_OPENED : DIR_CLOSED;
98+
return icon(opened ? "dir_opened" : "dir_closed");
9699
}
97100

98101
/**
@@ -101,7 +104,8 @@ public static Icon dir(final boolean opened) {
101104
* @return icon
102105
*/
103106
public static Icon resource(final ResourceType type) {
104-
return type == ResourceType.XML ? DB_XML : type == ResourceType.BINARY ? DB_BIN : DB_VAL;
107+
return icon(type == ResourceType.XML ? "db_xml" :
108+
type == ResourceType.BINARY ? "db_bin" : "db_val");
105109
}
106110

107111
/**
@@ -110,27 +114,27 @@ public static Icon resource(final ResourceType type) {
110114
* @return icon
111115
*/
112116
public static Icon file(final IOFile file) {
113-
if(file == null) return FILE_TEXT;
114-
115-
// fallback code for displaying icons
116-
final String path = file.path();
117-
final MediaType type = MediaType.get(path);
118-
if(type.isXml()) return FILE_XML;
119-
if(type.isXQuery()) return FILE_XQUERY;
120-
121-
if(Prop.WIN) {
122-
// retrieve system icons (only supported on Windows)
123-
final int p = path.lastIndexOf(path, '.');
124-
final String suffix = p == -1 ? null : path.substring(p + 1);
125-
Icon icon = null;
126-
if(suffix != null) icon = FILES.get(suffix);
127-
if(icon == null) {
128-
icon = FS.getSystemIcon(file.file());
129-
if(suffix != null) FILES.put(suffix, icon);
117+
String name = "file_text";
118+
if(file != null) {
119+
final String path = file.path();
120+
final MediaType type = MediaType.get(path);
121+
if(type.isXml()) {
122+
name = "file_xml";
123+
} else if(type.isXQuery()) {
124+
name = "file_xquery";
125+
} else if(Prop.WIN) {
126+
// retrieve system icons (only supported on Windows)
127+
final int p = path.lastIndexOf(path, '.');
128+
final String suffix = p == -1 ? null : path.substring(p + 1);
129+
Icon icon = null;
130+
if(suffix != null) icon = FILES.get(suffix);
131+
if(icon == null) {
132+
icon = FS.getSystemIcon(file.file());
133+
if(suffix != null) FILES.put(suffix, icon);
134+
}
135+
return icon;
130136
}
131-
return icon;
132137
}
133-
// default icon
134-
return FILE_TEXT;
138+
return icon(name);
135139
}
136140
}

0 commit comments

Comments
 (0)