Finished dark design and implementing important Components

This commit is contained in:
UnlegitDqrk
2026-02-14 22:09:04 +01:00
parent 379aa08ed6
commit 56b79f9129
18 changed files with 851 additions and 55 deletions

View File

@@ -6,7 +6,7 @@
<groupId>org.openautonomousconnection</groupId> <groupId>org.openautonomousconnection</groupId>
<artifactId>OACSwing</artifactId> <artifactId>OACSwing</artifactId>
<version>1.0.0-BETA.1.0</version> <version>1.0.0-BETA.1.1</version>
<organization> <organization>
<name>Open Autonomous Connection</name> <name>Open Autonomous Connection</name>
<url>https://open-autonomous-connection.org/</url> <url>https://open-autonomous-connection.org/</url>

View File

@@ -5,8 +5,10 @@
package org.openautonomousconnection.oacswing.component; package org.openautonomousconnection.oacswing.component;
import lombok.NonNull; import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*; import java.awt.*;
import java.util.Vector; import java.util.Vector;
@@ -27,6 +29,40 @@ public class OACComboBox<E> extends JComboBox<E> implements OACComponent {
super(); super();
} }
@Override
public void init() {
setOpaque(true);
setRenderer(createDesignRenderer());
}
@Override
public void updateUI() {
super.updateUI();
setRenderer(createDesignRenderer());
}
private ListCellRenderer<? super E> createDesignRenderer() {
Color fallbackBg = getBackground() != null ? getBackground() : UIManager.getColor("ComboBox.background");
Color fallbackFg = getForeground() != null ? getForeground() : UIManager.getColor("ComboBox.foreground");
if (fallbackBg == null) fallbackBg = Color.DARK_GRAY;
if (fallbackFg == null) fallbackFg = Color.LIGHT_GRAY;
Color bg = DesignManager.resolveBackground(OACComboBox.class, fallbackBg);
Color fg = DesignManager.resolveForeground(OACComboBox.class, fallbackFg);
Color selBg = DesignManager.resolveHovered(OACButton.class, bg.darker());
Color selFg = fg;
DefaultListCellRenderer base = new DefaultListCellRenderer();
return (list, value, index, isSelected, cellHasFocus) -> {
JLabel label = (JLabel) base.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
label.setOpaque(true);
label.setBorder(new EmptyBorder(4, 8, 4, 8));
label.setBackground(isSelected ? selBg : bg);
label.setForeground(isSelected ? selFg : fg);
return label;
};
}
@Override @Override
public Component add(Component comp) { public Component add(Component comp) {
return OACComponent.super.add(comp); return OACComponent.super.add(comp);

View File

@@ -2,12 +2,30 @@ package org.openautonomousconnection.oacswing.component;
import lombok.NonNull; import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager; import org.openautonomousconnection.oacswing.component.design.DesignManager;
import org.openautonomousconnection.oacswing.component.design.OACColor;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import java.awt.*; import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class OACDialog extends JDialog implements OACComponent { public class OACDialog extends JDialog implements OACComponent {
private static final int TITLE_HEIGHT = 30;
private static final EmptyBorder TITLE_INSET = new EmptyBorder(TITLE_HEIGHT, 0, 0, 0);
private static final String CONTENT_BASE_BORDER_PROPERTY = "oac.dialog.content.baseBorder";
private static final String CONTENT_BORDER_CAPTURED_PROPERTY = "oac.dialog.content.baseBorderCaptured";
private OACPanel titleRoot;
private OACLabel titleLabel;
private boolean titleBarInstalled;
private boolean titleHandlersInstalled;
private Point dragStartOnScreen;
private Point dragStartDialogLocation;
public OACDialog() { public OACDialog() {
super(); super();
initDialog(); initDialog();
@@ -54,10 +72,151 @@ public class OACDialog extends JDialog implements OACComponent {
} }
private void initDialog() { private void initDialog() {
setUndecorated(true);
DesignManager.apply(this); DesignManager.apply(this);
setBackground(OACColor.DARK_BACKGROUND.getColor()); Color dialogBackground = DesignManager.resolveBackground(OACDialog.class, getBackground());
getContentPane().setBackground(OACColor.DARK_BACKGROUND.getColor()); Color dialogForeground = DesignManager.resolveForeground(OACDialog.class, getForeground());
getContentPane().setForeground(OACColor.DARK_TEXT.getColor()); setBackground(dialogBackground);
getContentPane().setBackground(dialogBackground);
getContentPane().setForeground(dialogForeground);
installTitleHandlersIfNeeded();
updateTitleBar();
}
private void installTitleHandlersIfNeeded() {
if (titleHandlersInstalled) {
return;
}
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
if (titleRoot != null) {
titleRoot.setBounds(0, 0, getWidth(), TITLE_HEIGHT);
}
}
@Override
public void componentShown(ComponentEvent e) {
if (titleRoot != null) {
titleRoot.setBounds(0, 0, getWidth(), TITLE_HEIGHT);
}
}
});
titleHandlersInstalled = true;
}
private void ensureTitleComponents() {
if (titleRoot != null && titleLabel != null) {
return;
}
titleRoot = new OACPanel(new BorderLayout());
titleRoot.setOpaque(true);
titleRoot.setBorder(new EmptyBorder(6, 12, 6, 12));
titleLabel = new OACLabel();
titleRoot.add(titleLabel, BorderLayout.WEST);
MouseAdapter drag = new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
dragStartOnScreen = e.getLocationOnScreen();
dragStartDialogLocation = getLocation();
}
@Override
public void mouseDragged(MouseEvent e) {
if (dragStartOnScreen == null || dragStartDialogLocation == null) {
return;
}
Point now = e.getLocationOnScreen();
int dx = now.x - dragStartOnScreen.x;
int dy = now.y - dragStartOnScreen.y;
setLocation(dragStartDialogLocation.x + dx, dragStartDialogLocation.y + dy);
}
};
titleRoot.addMouseListener(drag);
titleRoot.addMouseMotionListener(drag);
}
private void updateTitleBar() {
String title = getTitle();
boolean hasTitle = title != null && !title.isBlank();
JLayeredPane layeredPane = getLayeredPane();
if (!hasTitle) {
if (titleBarInstalled && titleRoot != null && layeredPane != null) {
layeredPane.remove(titleRoot);
layeredPane.revalidate();
layeredPane.repaint();
titleBarInstalled = false;
}
applyContentInset(false);
return;
}
ensureTitleComponents();
Color bg = DesignManager.resolveBackground(OACDialog.class, getBackground());
Color fg = DesignManager.resolveForeground(OACDialog.class, getForeground());
titleRoot.setBackground(bg);
titleLabel.setForeground(fg);
titleLabel.setText(title);
if (!titleBarInstalled && layeredPane != null) {
layeredPane.add(titleRoot, JLayeredPane.DRAG_LAYER);
titleBarInstalled = true;
}
titleRoot.setBounds(0, 0, getWidth(), TITLE_HEIGHT);
applyContentInset(true);
}
private void applyContentInset(boolean hasTitle) {
Container content = super.getContentPane();
if (!(content instanceof JComponent jContent)) {
return;
}
if (!Boolean.TRUE.equals(jContent.getClientProperty(CONTENT_BORDER_CAPTURED_PROPERTY))) {
jContent.putClientProperty(CONTENT_BASE_BORDER_PROPERTY, jContent.getBorder());
jContent.putClientProperty(CONTENT_BORDER_CAPTURED_PROPERTY, Boolean.TRUE);
}
Border baseBorder = (Border) jContent.getClientProperty(CONTENT_BASE_BORDER_PROPERTY);
if (hasTitle) {
jContent.setBorder(baseBorder == null ? TITLE_INSET : new CompoundBorder(TITLE_INSET, baseBorder));
} else {
jContent.setBorder(baseBorder);
}
jContent.revalidate();
jContent.repaint();
}
@Override
public void setContentPane(Container contentPane) {
super.setContentPane(contentPane);
if (contentPane instanceof Component component) {
component.setBackground(DesignManager.resolveBackground(OACDialog.class, component.getBackground()));
component.setForeground(DesignManager.resolveForeground(OACDialog.class, component.getForeground()));
}
if (contentPane instanceof JComponent jContent) {
jContent.putClientProperty(CONTENT_BORDER_CAPTURED_PROPERTY, Boolean.FALSE);
jContent.putClientProperty(CONTENT_BASE_BORDER_PROPERTY, null);
}
if (contentPane instanceof OACComponent oacComponent) {
oacComponent.initDesign();
}
updateTitleBar();
}
@Override
public void setTitle(String title) {
super.setTitle(title);
updateTitleBar();
} }
@Override @Override

View File

@@ -5,6 +5,7 @@
package org.openautonomousconnection.oacswing.component; package org.openautonomousconnection.oacswing.component;
import lombok.NonNull; import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -27,6 +28,30 @@ public class OACList<E> extends JList<E> implements OACComponent {
super(); super();
} }
@Override
public void init() {
applyDesignColors();
}
@Override
public void updateUI() {
super.updateUI();
applyDesignColors();
}
private void applyDesignColors() {
Color bg = DesignManager.resolveBackground(OACList.class, getBackground());
Color fg = DesignManager.resolveForeground(OACList.class, getForeground());
Color selBg = DesignManager.resolveHovered(OACButton.class, bg.darker());
Color selFg = fg;
setOpaque(true);
setBackground(bg);
setForeground(fg);
setSelectionBackground(selBg);
setSelectionForeground(selFg);
}
@Override @Override
public Component add(Component comp) { public Component add(Component comp) {
this.initOther(comp); this.initOther(comp);

View File

@@ -4,10 +4,18 @@
package org.openautonomousconnection.oacswing.component; package org.openautonomousconnection.oacswing.component;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.ContainerAdapter;
import java.awt.event.ContainerEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
public class OACMenu extends JMenu implements OACComponent { public class OACMenu extends JMenu implements OACComponent {
private JPopupMenu observedPopup;
public OACMenu() { public OACMenu() {
super(); super();
} }
@@ -24,6 +32,61 @@ public class OACMenu extends JMenu implements OACComponent {
super(s, b); super(s, b);
} }
@Override
public void init() {
applyDesignColors();
setOpaque(true);
installPopupAutoApply();
}
@Override
public void updateUI() {
super.updateUI();
applyDesignColors();
installPopupAutoApply();
}
private void applyDesignColors() {
setBackground(DesignManager.resolveBackground(OACMenu.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACMenu.class, getForeground()));
applyPopupDesign();
}
private void installPopupAutoApply() {
JPopupMenu popup = getPopupMenu();
if (observedPopup == popup) {
return;
}
popup.addContainerListener(new ContainerAdapter() {
@Override
public void componentAdded(ContainerEvent e) {
applyPopupDesign();
}
});
popup.addComponentListener(new ComponentAdapter() {
@Override
public void componentShown(ComponentEvent e) {
applyPopupDesign();
}
});
observedPopup = popup;
}
private void applyPopupDesign() {
JPopupMenu popup = getPopupMenu();
popup.setBackground(DesignManager.resolveBackground(OACPopupMenu.class, popup.getBackground()));
popup.setForeground(DesignManager.resolveForeground(OACPopupMenu.class, popup.getForeground()));
for (Component child : popup.getComponents()) {
if (child instanceof OACComponent oac) {
oac.initDesign();
} else if (child instanceof JComponent jc) {
jc.setOpaque(true);
jc.setBackground(DesignManager.resolveBackground(OACMenuItem.class, jc.getBackground()));
jc.setForeground(DesignManager.resolveForeground(OACMenuItem.class, jc.getForeground()));
}
}
}
@Override @Override
public Component add(Component comp) { public Component add(Component comp) {
this.initOther(comp); this.initOther(comp);

View File

@@ -5,6 +5,7 @@
package org.openautonomousconnection.oacswing.component; package org.openautonomousconnection.oacswing.component;
import lombok.NonNull; import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -14,6 +15,20 @@ public class OACMenuBar extends JMenuBar implements OACComponent {
super(); super();
} }
@Override
public void init() {
setOpaque(true);
setBackground(DesignManager.resolveBackground(OACMenuBar.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACMenuBar.class, getForeground()));
}
@Override
public void updateUI() {
super.updateUI();
setBackground(DesignManager.resolveBackground(OACMenuBar.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACMenuBar.class, getForeground()));
}
@Override @Override
public Component add(Component comp) { public Component add(Component comp) {
this.initOther(comp); this.initOther(comp);

View File

@@ -5,6 +5,7 @@
package org.openautonomousconnection.oacswing.component; package org.openautonomousconnection.oacswing.component;
import lombok.NonNull; import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -34,6 +35,23 @@ public class OACMenuItem extends JMenuItem implements OACComponent {
super(text, mnemonic); super(text, mnemonic);
} }
@Override
public void init() {
applyDesignColors();
setOpaque(true);
}
@Override
public void updateUI() {
super.updateUI();
applyDesignColors();
}
private void applyDesignColors() {
setBackground(DesignManager.resolveBackground(OACMenuItem.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACMenuItem.class, getForeground()));
}
@Override @Override
public Component add(Component comp) { public Component add(Component comp) {
this.initOther(comp); this.initOther(comp);

View File

@@ -59,7 +59,7 @@ public class OACOptionPane extends JOptionPane implements OACComponent {
Object[] options, Object[] options,
Object initialValue) throws HeadlessException { Object initialValue) throws HeadlessException {
AtomicInteger result = new AtomicInteger(CLOSED_OPTION); AtomicInteger result = new AtomicInteger(CLOSED_OPTION);
OACDialog dialog = new OACDialog(JOptionPane.getFrameForComponent(parentComponent), title, true); OACDialog dialog = new OACDialog(JOptionPane.getFrameForComponent(parentComponent), null, true);
dialog.setUndecorated(true); dialog.setUndecorated(true);
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
dialog.setContentPane(buildDialogContent(title, message, icon, optionType, options, result, dialog)); dialog.setContentPane(buildDialogContent(title, message, icon, optionType, options, result, dialog));
@@ -78,6 +78,71 @@ public class OACOptionPane extends JOptionPane implements OACComponent {
return result.get(); return result.get();
} }
public static int showConfirmDialog(Component parentComponent, Object message) throws HeadlessException {
return showConfirmDialog(parentComponent, message, "Select an Option", YES_NO_CANCEL_OPTION);
}
public static int showConfirmDialog(Component parentComponent,
Object message,
String title,
int optionType) throws HeadlessException {
return showConfirmDialog(parentComponent, message, title, optionType, QUESTION_MESSAGE);
}
public static int showConfirmDialog(Component parentComponent,
Object message,
String title,
int optionType,
int messageType) throws HeadlessException {
return showConfirmDialog(parentComponent, message, title, optionType, messageType, null);
}
public static int showConfirmDialog(Component parentComponent,
Object message,
String title,
int optionType,
int messageType,
Icon icon) throws HeadlessException {
return showOptionDialog(
parentComponent,
message,
title,
optionType,
messageType,
icon != null ? icon : resolveDefaultIcon(messageType),
null,
null
);
}
public static void showMessageDialog(Component parentComponent, Object message) throws HeadlessException {
showMessageDialog(parentComponent, message, "Message", INFORMATION_MESSAGE);
}
public static void showMessageDialog(Component parentComponent,
Object message,
String title,
int messageType) throws HeadlessException {
showMessageDialog(parentComponent, message, title, messageType, null);
}
public static void showMessageDialog(Component parentComponent,
Object message,
String title,
int messageType,
Icon icon) throws HeadlessException {
showOptionDialog(
parentComponent,
message,
title,
DEFAULT_OPTION,
messageType,
icon != null ? icon : resolveDefaultIcon(messageType),
new Object[]{"OK"},
"OK"
);
}
private static Container buildDialogContent(String title, private static Container buildDialogContent(String title,
Object message, Object message,
Icon icon, Icon icon,
@@ -87,7 +152,6 @@ public class OACOptionPane extends JOptionPane implements OACComponent {
OACDialog dialog) { OACDialog dialog) {
OACPanel root = new OACPanel(new BorderLayout()); OACPanel root = new OACPanel(new BorderLayout());
Color background = DesignManager.resolveBackground(OACOptionPane.class, root.getBackground()); Color background = DesignManager.resolveBackground(OACOptionPane.class, root.getBackground());
Color headerBackground = DesignManager.resolveBackground(OACTitleBar.class, background.darker());
Color foreground = DesignManager.resolveForeground(OACOptionPane.class, Color.LIGHT_GRAY); Color foreground = DesignManager.resolveForeground(OACOptionPane.class, Color.LIGHT_GRAY);
Color borderColor = DesignManager.resolveBorderColor(foreground.darker()); Color borderColor = DesignManager.resolveBorderColor(foreground.darker());
@@ -95,12 +159,8 @@ public class OACOptionPane extends JOptionPane implements OACComponent {
root.setBorder(BorderFactory.createLineBorder(borderColor, 1)); root.setBorder(BorderFactory.createLineBorder(borderColor, 1));
OACPanel header = new OACPanel(new BorderLayout()); OACPanel header = new OACPanel(new BorderLayout());
header.setBorder(new EmptyBorder(10, 12, 10, 12)); header.setBorder(new EmptyBorder(0, 12, 8, 12));
header.setBackground(headerBackground); header.setBackground(background);
JSeparator separator = new JSeparator(SwingConstants.HORIZONTAL);
separator.setForeground(borderColor);
separator.setBackground(borderColor);
header.add(separator, BorderLayout.SOUTH);
OACLabel titleLabel = new OACLabel(title == null ? "" : title); OACLabel titleLabel = new OACLabel(title == null ? "" : title);
titleLabel.setForeground(foreground); titleLabel.setForeground(foreground);
@@ -116,14 +176,24 @@ public class OACOptionPane extends JOptionPane implements OACComponent {
center.add(iconLabel, BorderLayout.WEST); center.add(iconLabel, BorderLayout.WEST);
} }
JTextArea messageArea = new JTextArea(String.valueOf(message)); if (message instanceof Component messageComponent) {
messageArea.setEditable(false); if (messageComponent instanceof OACComponent oacComponent) {
messageArea.setLineWrap(true); oacComponent.initDesign();
messageArea.setWrapStyleWord(true); } else {
messageArea.setOpaque(false); messageComponent.setBackground(background);
messageArea.setForeground(foreground); messageComponent.setForeground(foreground);
messageArea.setBorder(null); }
center.add(messageArea, BorderLayout.CENTER); center.add(messageComponent, BorderLayout.CENTER);
} else {
JTextArea messageArea = new JTextArea(String.valueOf(message));
messageArea.setEditable(false);
messageArea.setLineWrap(true);
messageArea.setWrapStyleWord(true);
messageArea.setOpaque(false);
messageArea.setForeground(foreground);
messageArea.setBorder(null);
center.add(messageArea, BorderLayout.CENTER);
}
OACPanel buttons = new OACPanel(new FlowLayout(FlowLayout.RIGHT, 8, 8)); OACPanel buttons = new OACPanel(new FlowLayout(FlowLayout.RIGHT, 8, 8));
buttons.setBackground(background); buttons.setBackground(background);
@@ -177,6 +247,15 @@ public class OACOptionPane extends JOptionPane implements OACComponent {
}; };
} }
private static Icon resolveDefaultIcon(int messageType) {
return switch (messageType) {
case ERROR_MESSAGE -> UIManager.getIcon("OptionPane.errorIcon");
case WARNING_MESSAGE -> UIManager.getIcon("OptionPane.warningIcon");
case QUESTION_MESSAGE -> UIManager.getIcon("OptionPane.questionIcon");
default -> UIManager.getIcon("OptionPane.informationIcon");
};
}
@Override @Override
public Component add(Component comp) { public Component add(Component comp) {
this.initOther(comp); this.initOther(comp);

View File

@@ -5,6 +5,7 @@
package org.openautonomousconnection.oacswing.component; package org.openautonomousconnection.oacswing.component;
import lombok.NonNull; import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -18,6 +19,20 @@ public class OACPopupMenu extends JPopupMenu implements OACComponent {
super(label); super(label);
} }
@Override
public void init() {
setOpaque(true);
setBackground(DesignManager.resolveBackground(OACPopupMenu.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACPopupMenu.class, getForeground()));
}
@Override
public void updateUI() {
super.updateUI();
setBackground(DesignManager.resolveBackground(OACPopupMenu.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACPopupMenu.class, getForeground()));
}
@Override @Override
public Component add(Component comp) { public Component add(Component comp) {
this.initOther(comp); this.initOther(comp);

View File

@@ -5,8 +5,10 @@
package org.openautonomousconnection.oacswing.component; package org.openautonomousconnection.oacswing.component;
import lombok.NonNull; import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*; import javax.swing.*;
import javax.swing.plaf.basic.BasicScrollBarUI;
import java.awt.*; import java.awt.*;
public class OACScrollBar extends JScrollBar implements OACComponent { public class OACScrollBar extends JScrollBar implements OACComponent {
@@ -22,6 +24,90 @@ public class OACScrollBar extends JScrollBar implements OACComponent {
super(); super();
} }
@Override
public void init() {
applyDesignColors();
}
@Override
public void updateUI() {
super.updateUI();
setUI(createDesignUI());
applyDesignColors();
}
private void applyDesignColors() {
setBackground(DesignManager.resolveBackground(OACScrollBar.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACScrollBar.class, getForeground()));
setOpaque(true);
setBorder(null);
}
private BasicScrollBarUI createDesignUI() {
Color track = DesignManager.resolveBackground(OACScrollBar.class, getBackground());
Color thumb = DesignManager.resolveHovered(OACButton.class, track.darker());
if (thumb == null || thumb.equals(track)) {
thumb = DesignManager.resolveBorderColor(track.brighter());
}
final Color trackColor = track;
final Color thumbColor = thumb;
return new BasicScrollBarUI() {
@Override
protected JButton createDecreaseButton(int orientation) {
return createZeroButton();
}
@Override
protected JButton createIncreaseButton(int orientation) {
return createZeroButton();
}
@Override
protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) {
g.setColor(trackColor);
g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, trackBounds.height);
}
@Override
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
if (thumbBounds.isEmpty() || !scrollbar.isEnabled()) {
return;
}
Graphics2D g2 = (Graphics2D) g.create();
try {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(thumbColor);
int arc = scrollbar.getOrientation() == Adjustable.VERTICAL ? thumbBounds.width : thumbBounds.height;
g2.fillRoundRect(
thumbBounds.x + 2,
thumbBounds.y + 2,
Math.max(0, thumbBounds.width - 4),
Math.max(0, thumbBounds.height - 4),
arc,
arc
);
} finally {
g2.dispose();
}
}
private JButton createZeroButton() {
JButton button = new JButton();
button.setOpaque(false);
button.setFocusable(false);
button.setBorderPainted(false);
button.setContentAreaFilled(false);
button.setBorder(null);
Dimension zero = new Dimension(0, 0);
button.setPreferredSize(zero);
button.setMinimumSize(zero);
button.setMaximumSize(zero);
return button;
}
};
}
@Override @Override
public Component add(Component comp) { public Component add(Component comp) {
this.initOther(comp); this.initOther(comp);

View File

@@ -5,8 +5,10 @@
package org.openautonomousconnection.oacswing.component; package org.openautonomousconnection.oacswing.component;
import lombok.NonNull; import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*; import javax.swing.*;
import javax.swing.plaf.UIResource;
import java.awt.*; import java.awt.*;
public class OACScrollPane extends JScrollPane implements OACComponent { public class OACScrollPane extends JScrollPane implements OACComponent {
@@ -26,6 +28,142 @@ public class OACScrollPane extends JScrollPane implements OACComponent {
super(); super();
} }
@Override
public JScrollBar createVerticalScrollBar() {
return new OACScrollBar(JScrollBar.VERTICAL);
}
@Override
public JScrollBar createHorizontalScrollBar() {
return new OACScrollBar(JScrollBar.HORIZONTAL);
}
@Override
protected JViewport createViewport() {
return new OACViewport();
}
@Override
public void init() {
applyDesignColors();
ensureOACSubcomponents();
}
@Override
public void updateUI() {
super.updateUI();
applyDesignColors();
ensureOACSubcomponents();
}
private void applyDesignColors() {
setOpaque(true);
Color bg = DesignManager.resolveBackground(OACScrollPane.class, getBackground());
Color fg = DesignManager.resolveForeground(OACScrollPane.class, getForeground());
setBackground(bg);
setForeground(fg);
setBorder(null);
setViewportBorder(null);
JViewport viewport = getViewport();
if (viewport != null) {
viewport.setOpaque(true);
viewport.setBackground(bg);
viewport.setForeground(fg);
}
JScrollBar vertical = getVerticalScrollBar();
if (vertical != null) {
vertical.setBackground(DesignManager.resolveBackground(OACScrollBar.class, bg));
vertical.setForeground(DesignManager.resolveForeground(OACScrollBar.class, fg));
}
JScrollBar horizontal = getHorizontalScrollBar();
if (horizontal != null) {
horizontal.setBackground(DesignManager.resolveBackground(OACScrollBar.class, bg));
horizontal.setForeground(DesignManager.resolveForeground(OACScrollBar.class, fg));
}
setCorner(UPPER_LEFT_CORNER, createCorner(bg));
setCorner(UPPER_RIGHT_CORNER, createCorner(bg));
setCorner(LOWER_LEFT_CORNER, createCorner(bg));
setCorner(LOWER_RIGHT_CORNER, createCorner(bg));
applyViewportViewDesign(bg, fg);
}
private void ensureOACSubcomponents() {
if (!(getVerticalScrollBar() instanceof OACScrollBar)) {
JScrollBar old = getVerticalScrollBar();
OACScrollBar replacement = new OACScrollBar(JScrollBar.VERTICAL);
replacement.setModel(old.getModel());
setVerticalScrollBar(replacement);
}
if (!(getHorizontalScrollBar() instanceof OACScrollBar)) {
JScrollBar old = getHorizontalScrollBar();
OACScrollBar replacement = new OACScrollBar(JScrollBar.HORIZONTAL);
replacement.setModel(old.getModel());
setHorizontalScrollBar(replacement);
}
if (!(getViewport() instanceof OACViewport)) {
JViewport oldViewport = getViewport();
OACViewport replacement = new OACViewport();
replacement.setView(oldViewport.getView());
setViewport(replacement);
}
if (getVerticalScrollBar() instanceof OACComponent oacVertical) {
oacVertical.initDesign();
}
if (getHorizontalScrollBar() instanceof OACComponent oacHorizontal) {
oacHorizontal.initDesign();
}
if (getViewport() instanceof OACComponent oacViewport) {
oacViewport.initDesign();
}
applyViewportViewDesign(
DesignManager.resolveBackground(OACScrollPane.class, getBackground()),
DesignManager.resolveForeground(OACScrollPane.class, getForeground())
);
}
private static JComponent createCorner(Color color) {
JPanel corner = new JPanel(new BorderLayout());
corner.setOpaque(true);
corner.setBackground(color);
corner.setBorder(null);
return corner;
}
private void applyViewportViewDesign(Color bg, Color fg) {
JViewport viewport = getViewport();
if (viewport == null) {
return;
}
Component view = viewport.getView();
if (view == null) {
return;
}
if (view instanceof OACComponent oacView) {
oacView.initDesign();
return;
}
if (view instanceof JComponent jView) {
if (jView.getBackground() == null || jView.getBackground() instanceof UIResource) {
jView.setBackground(bg);
}
if (jView.getForeground() == null || jView.getForeground() instanceof UIResource) {
jView.setForeground(fg);
}
return;
}
view.setBackground(bg);
view.setForeground(fg);
}
@Override @Override
public Component add(Component comp) { public Component add(Component comp) {
this.initOther(comp); this.initOther(comp);

View File

@@ -5,11 +5,15 @@
package org.openautonomousconnection.oacswing.component; package org.openautonomousconnection.oacswing.component;
import lombok.NonNull; import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*; import javax.swing.*;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import java.awt.*; import java.awt.*;
public class OACTabbedPane extends JTabbedPane implements OACComponent { public class OACTabbedPane extends JTabbedPane implements OACComponent {
private boolean selectionSyncInstalled;
public OACTabbedPane() { public OACTabbedPane() {
super(); super();
} }
@@ -22,22 +26,136 @@ public class OACTabbedPane extends JTabbedPane implements OACComponent {
super(tabPlacement, tabLayoutPolicy); super(tabPlacement, tabLayoutPolicy);
} }
@Override
public void init() {
applyDesignColors();
}
@Override
public void updateUI() {
super.updateUI();
applyDesignColors();
}
private void applyDesignColors() {
Color bg = DesignManager.resolveBackground(OACTabbedPane.class, getBackground());
Color fg = DesignManager.resolveForeground(OACTabbedPane.class, getForeground());
Color selectedBg = DesignManager.resolveHovered(OACButton.class, bg.darker());
Color selectedFg = fg;
Color border = DesignManager.resolveBorderColor(bg.darker());
setOpaque(true);
setBackground(bg);
setForeground(fg);
setUI(new DesignTabbedPaneUI(bg, selectedBg, border));
applyTabItemColors(bg, fg, selectedBg, selectedFg);
installSelectionSyncIfNeeded();
}
private void applyTabItemColors(Color bg, Color fg, Color selectedBg, Color selectedFg) {
int selectedIndex = getSelectedIndex();
for (int i = 0; i < getTabCount(); i++) {
boolean selected = i == selectedIndex;
setBackgroundAt(i, selected ? selectedBg : bg);
setForegroundAt(i, selected ? selectedFg : fg);
}
repaint();
}
private void installSelectionSyncIfNeeded() {
if (selectionSyncInstalled) {
return;
}
addChangeListener(e -> {
Color bg = DesignManager.resolveBackground(OACTabbedPane.class, getBackground());
Color fg = DesignManager.resolveForeground(OACTabbedPane.class, getForeground());
Color selectedBg = DesignManager.resolveHovered(OACButton.class, bg.darker());
applyTabItemColors(bg, fg, selectedBg, fg);
});
selectionSyncInstalled = true;
}
private static final class DesignTabbedPaneUI extends BasicTabbedPaneUI {
private final Color background;
private final Color selectedBackground;
private final Color borderColor;
private DesignTabbedPaneUI(Color background, Color selectedBackground, Color borderColor) {
this.background = background;
this.selectedBackground = selectedBackground;
this.borderColor = borderColor;
}
@Override
protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex,
int x, int y, int w, int h, boolean isSelected) {
g.setColor(isSelected ? selectedBackground : background);
g.fillRect(x, y, w, h);
}
@Override
protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
int x, int y, int w, int h, boolean isSelected) {
g.setColor(borderColor);
g.drawRect(x, y, w, h);
}
@Override
protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects,
int tabIndex, Rectangle iconRect, Rectangle textRect, boolean isSelected) {
// no-op to avoid bright LAF focus ring
}
@Override
protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
int width = tabPane.getWidth();
int height = tabPane.getHeight();
Insets insets = tabPane.getInsets();
int x = insets.left;
int y = insets.top;
int w = width - insets.right - insets.left;
int h = height - insets.top - insets.bottom;
switch (tabPlacement) {
case LEFT -> {
x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
w -= (x - insets.left);
}
case RIGHT -> w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
case BOTTOM -> h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
default -> {
y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
h -= (y - insets.top);
}
}
g.setColor(background);
g.fillRect(x, y, w, h);
g.setColor(borderColor);
g.drawRect(x, y, Math.max(0, w - 1), Math.max(0, h - 1));
}
}
@Override @Override
public void addTab(String title, Component component) { public void addTab(String title, Component component) {
this.initOther(component); this.initOther(component);
super.addTab(title, component); super.addTab(title, component);
applyDesignColors();
} }
@Override @Override
public void addTab(String title, Icon icon, Component component) { public void addTab(String title, Icon icon, Component component) {
this.initOther(component); this.initOther(component);
super.addTab(title, icon, component); super.addTab(title, icon, component);
applyDesignColors();
} }
@Override @Override
public void addTab(String title, Icon icon, Component component, String tip) { public void addTab(String title, Icon icon, Component component, String tip) {
this.initOther(component); this.initOther(component);
super.addTab(title, icon, component, tip); super.addTab(title, icon, component, tip);
applyDesignColors();
} }
@Override @Override

View File

@@ -38,6 +38,7 @@ public class OACTextArea extends JTextArea implements OACComponent {
@Override @Override
public void init() { public void init() {
applyDesignColors();
setOpaque(true); setOpaque(true);
setMargin(new Insets(6, 10, 6, 10)); setMargin(new Insets(6, 10, 6, 10));
setBorder(DesignManager.createTextComponentBorder()); setBorder(DesignManager.createTextComponentBorder());
@@ -46,6 +47,18 @@ public class OACTextArea extends JTextArea implements OACComponent {
setSelectedTextColor(getBackground().brighter()); setSelectedTextColor(getBackground().brighter());
} }
@Override
public void updateUI() {
super.updateUI();
applyDesignColors();
setBorder(DesignManager.createTextComponentBorder());
}
private void applyDesignColors() {
setBackground(DesignManager.resolveBackground(OACTextArea.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACTextArea.class, getForeground()));
}
@Override @Override
public Component add(Component comp) { public Component add(Component comp) {
this.initOther(comp); this.initOther(comp);

View File

@@ -36,6 +36,7 @@ public class OACTextField extends JTextField implements OACComponent {
@Override @Override
public void init() { public void init() {
applyDesignColors();
setOpaque(true); setOpaque(true);
applyInputBorder(); applyInputBorder();
setCaretColor(getForeground()); setCaretColor(getForeground());
@@ -47,9 +48,15 @@ public class OACTextField extends JTextField implements OACComponent {
@Override @Override
public void updateUI() { public void updateUI() {
super.updateUI(); super.updateUI();
applyDesignColors();
applyInputBorder(); applyInputBorder();
} }
private void applyDesignColors() {
setBackground(DesignManager.resolveBackground(OACTextField.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACTextField.class, getForeground()));
}
private void applyInputBorder() { private void applyInputBorder() {
setBorder(new CompoundBorder( setBorder(new CompoundBorder(
new RoundedBorder(DesignManager.resolveBorderColor(getForeground()), 10, 2), new RoundedBorder(DesignManager.resolveBorderColor(getForeground()), 10, 2),

View File

@@ -5,6 +5,7 @@
package org.openautonomousconnection.oacswing.component; package org.openautonomousconnection.oacswing.component;
import lombok.NonNull; import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -14,6 +15,20 @@ public class OACViewport extends JViewport implements OACComponent {
super(); super();
} }
@Override
public void init() {
setOpaque(true);
setBackground(DesignManager.resolveBackground(OACViewport.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACViewport.class, getForeground()));
}
@Override
public void updateUI() {
super.updateUI();
setBackground(DesignManager.resolveBackground(OACViewport.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACViewport.class, getForeground()));
}
@Override @Override
public Component add(Component comp) { public Component add(Component comp) {
this.initOther(comp); this.initOther(comp);

View File

@@ -42,27 +42,31 @@ public class DesignManager {
false false
)); ));
Design.DARK.getElements().put(OACCheckBox.class, new DesignFlags(OACColor.DARK_INPUT_FIELD)); Design.DARK.getElements().put(OACCheckBox.class, new DesignFlags(OACColor.DARK_INPUT_FIELD, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACCheckBoxMenuItem.class, new DesignFlags(OACColor.DARK_ITEM)); Design.DARK.getElements().put(OACCheckBoxMenuItem.class, new DesignFlags(OACColor.DARK_INPUT_BUTTON, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACTextField.class, new DesignFlags(OACColor.DARK_INPUT_FIELD, OACColor.DARK_TEXT)); Design.DARK.getElements().put(OACTextField.class, new DesignFlags(OACColor.DARK_INPUT_FIELD, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACTextArea.class, new DesignFlags(OACColor.DARK_INPUT_FIELD, OACColor.DARK_TEXT)); Design.DARK.getElements().put(OACTextArea.class, new DesignFlags(OACColor.DARK_INPUT_FIELD, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACColorChooser.class, new DesignFlags(OACColor.DARK_SECTION)); Design.DARK.getElements().put(OACColorChooser.class, new DesignFlags(OACColor.DARK_SECTION));
Design.DARK.getElements().put(OACComboBox.class, new DesignFlags(OACColor.DARK_INPUT_FIELD)); Design.DARK.getElements().put(OACComboBox.class, new DesignFlags(OACColor.DARK_INPUT_FIELD, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACTabbedPane.class, new DesignFlags(OACColor.DARK_SECTION, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACFrame.class, new DesignFlags(OACColor.DARK_BACKGROUND)); Design.DARK.getElements().put(OACFrame.class, new DesignFlags(OACColor.DARK_BACKGROUND));
Design.DARK.getElements().put(OACLabel.class, new DesignFlags(OACColor.DARK_TEXT)); Design.DARK.getElements().put(OACLabel.class, new DesignFlags(OACColor.DARK_BACKGROUND, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACLayeredPane.class, new DesignFlags(OACColor.DARK_BACKGROUND)); Design.DARK.getElements().put(OACLayeredPane.class, new DesignFlags(OACColor.DARK_BACKGROUND));
Design.DARK.getElements().put(OACList.class, new DesignFlags(OACColor.DARK_SECTION)); Design.DARK.getElements().put(OACList.class, new DesignFlags(OACColor.DARK_SECTION, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACMenu.class, new DesignFlags(OACColor.DARK_INPUT_BUTTON)); Design.DARK.getElements().put(OACMenu.class, new DesignFlags(OACColor.DARK_INPUT_BUTTON, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACMenuBar.class, new DesignFlags(OACColor.DARK_SECTION)); Design.DARK.getElements().put(OACMenuBar.class, new DesignFlags(OACColor.DARK_SECTION, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACMenuItem.class, new DesignFlags(OACColor.DARK_ITEM)); Design.DARK.getElements().put(OACMenuItem.class, new DesignFlags(OACColor.DARK_INPUT_BUTTON, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACOptionPane.class, new DesignFlags(OACColor.DARK_BACKGROUND)); Design.DARK.getElements().put(OACOptionPane.class, new DesignFlags(OACColor.DARK_BACKGROUND));
Design.DARK.getElements().put(OACPanel.class, new DesignFlags(OACColor.DARK_BACKGROUND, false)); Design.DARK.getElements().put(OACPanel.class, new DesignFlags(OACColor.DARK_BACKGROUND, false));
Design.DARK.getElements().put(OACPasswordField.class, new DesignFlags(OACColor.DARK_INPUT_FIELD, OACColor.DARK_TEXT)); Design.DARK.getElements().put(OACPasswordField.class, new DesignFlags(OACColor.DARK_INPUT_FIELD, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACPopupMenu.class, new DesignFlags(OACColor.DARK_BACKGROUND)); Design.DARK.getElements().put(OACPopupMenu.class, new DesignFlags(OACColor.DARK_BACKGROUND, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACProgressBar.class, new DesignFlags(OACColor.DARK_ITEM)); Design.DARK.getElements().put(OACProgressBar.class, new DesignFlags(OACColor.DARK_ITEM, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACRadioButton.class, new DesignFlags(OACColor.DARK_BUTTON)); Design.DARK.getElements().put(OACRadioButton.class, new DesignFlags(OACColor.DARK_BUTTON, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACDialog.class, new DesignFlags(OACColor.DARK_BACKGROUND)); Design.DARK.getElements().put(OACScrollPane.class, new DesignFlags(OACColor.DARK_SECTION, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACTitleBar.class, new DesignFlags(OACColor.DARK_HEADER)); Design.DARK.getElements().put(OACScrollBar.class, new DesignFlags(OACColor.DARK_SECTION, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACViewport.class, new DesignFlags(OACColor.DARK_SECTION, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACDialog.class, new DesignFlags(OACColor.DARK_BACKGROUND, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACTitleBar.class, new DesignFlags(OACColor.DARK_HEADER, OACColor.DARK_TEXT));
Design.DARK.border = new RoundedBorder(OACColor.DARK_BORDERS.getColor(), 18, 1); Design.DARK.border = new RoundedBorder(OACColor.DARK_BORDERS.getColor(), 18, 1);
globalDesign = Design.DARK; globalDesign = Design.DARK;
@@ -122,7 +126,12 @@ public class DesignManager {
if (hasBorder) { if (hasBorder) {
jComponent.setBorder(globalDesign.getBorder()); jComponent.setBorder(globalDesign.getBorder());
jComponent.setOpaque(false); jComponent.setOpaque(false);
} else if (!(jComponent instanceof OACTitleBar) && !(jComponent instanceof JTextComponent)) { } else if (!(jComponent instanceof OACTitleBar)
&& !(jComponent instanceof JTextComponent)
&& !(jComponent instanceof JViewport)
&& !(jComponent instanceof JScrollPane)
&& !(jComponent instanceof JScrollBar)
&& !(jComponent instanceof JTabbedPane)) {
Border currentBorder = jComponent.getBorder(); Border currentBorder = jComponent.getBorder();
if (currentBorder == null || hasZeroInsets(currentBorder, jComponent)) { if (currentBorder == null || hasZeroInsets(currentBorder, jComponent)) {
jComponent.setBorder(INVISIBLE_BORDER); jComponent.setBorder(INVISIBLE_BORDER);

View File

@@ -20,9 +20,9 @@ public class AnimationTests {
public synchronized void testSimpleAnimatedPanel() throws InterruptedException { public synchronized void testSimpleAnimatedPanel() throws InterruptedException {
DesignManager.setGlobalDesign(Design.DARK); DesignManager.setGlobalDesign(Design.DARK);
OACFrame frame = TestUtils.mockOacFrame(); // OACFrame frame = TestUtils.mockOacFrame();
AnimationPath animationPath = new AnimationPath(50); // AnimationPath animationPath = new AnimationPath(50);
// This test was too simple // This test was too simple
// animationPath.add(new KeyFrame(new Point(400, 400), 400, 400)); // animationPath.add(new KeyFrame(new Point(400, 400), 400, 400));
@@ -30,18 +30,18 @@ public class AnimationTests {
// animationPath.add(new KeyFrame(new Point(100, 100), 400, 400)); // animationPath.add(new KeyFrame(new Point(100, 100), 400, 400));
// animationPath.add(new KeyFrame(new Point(400, 400), 400, 400)); // animationPath.add(new KeyFrame(new Point(400, 400), 400, 400));
animationPath.add(new KeyFrame(new Point(400, 400), 400, 400, KeyFrame.PathMethod.EASE_OUT)); // animationPath.add(new KeyFrame(new Point(400, 400), 400, 400, KeyFrame.PathMethod.EASE_OUT));
animationPath.add(new KeyFrame(new Point(400, 300), 400, 400)); // animationPath.add(new KeyFrame(new Point(400, 300), 400, 400));
JAnimatedPanel animatedPanel = new JAnimatedPanel(animationPath); // JAnimatedPanel animatedPanel = new JAnimatedPanel(animationPath);
animatedPanel.setBackground(Color.BLACK); // animatedPanel.setBackground(Color.BLACK);
frame.add(animatedPanel); // frame.add(animatedPanel);
animatedPanel.play(5, true); // animatedPanel.play(5, true);
frame.setVisible(true); // frame.setVisible(true);
wait(10000); wait(10000);
} }

View File

@@ -30,16 +30,16 @@ public class CustomizedTests {
frame.add(navBar); frame.add(navBar);
frame.setVisible(true); frame.setVisible(true);
Object[] options = {"Continue", "Cancel"}; Object[] options = {"Continue", "Cancel"};
OACOptionPane.showOptionDialog( // OACOptionPane.showOptionDialog(
frame, // frame,
"You never connected to this INS before!\n" + // "You never connected to this INS before!\n" +
"Fingerprint: " + "caFingerprint" + "\nDo you want to connect?", // "Fingerprint: " + "caFingerprint" + "\nDo you want to connect?",
"INS Connection", // "INS Connection",
OACOptionPane.YES_NO_OPTION, // OACOptionPane.YES_NO_OPTION,
OACOptionPane.INFORMATION_MESSAGE, // OACOptionPane.INFORMATION_MESSAGE,
null, // null,
options, // options,
options[0] // options[0]
); wait(15000); // ); wait(15000);
} }
} }