Updated colors and design

This commit is contained in:
UnlegitDqrk
2026-02-14 18:57:57 +01:00
parent 9d391c893e
commit 2d3a3fa1f1
11 changed files with 438 additions and 23 deletions

View File

@@ -81,6 +81,7 @@ public class OACButton extends JButton implements OACPressable {
setContentAreaFilled(false);
setOpaque(false);
setFocusPainted(false);
setBorderPainted(false);
if (!isEnabled() && getDisabledColor() != null) {
super.setBackground(getDisabledColor());
@@ -182,4 +183,4 @@ public class OACButton extends JButton implements OACPressable {
g2.dispose();
}
}
}

View File

@@ -3,6 +3,8 @@ package org.openautonomousconnection.oacswing.component;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
@@ -64,6 +66,7 @@ public class OACFrame extends JFrame {
setBackground(new Color(0, 0, 0, 1));
roundedRoot.setLayout(new BorderLayout());
roundedRoot.setBorder(new EmptyBorder(TITLE_BAR_HEIGHT, 0, 0, 0));
setContentPane(roundedRoot);
titleBar = new OACTitleBar(this);
@@ -79,16 +82,20 @@ public class OACFrame extends JFrame {
@Override
public void componentResized(ComponentEvent e) {
titleRoot.setBounds(0, 0, getWidth(), TITLE_BAR_HEIGHT);
updateWindowChrome();
roundedRoot.repaint();
}
@Override
public void componentShown(ComponentEvent e) {
titleRoot.setBounds(0, 0, getWidth(), TITLE_BAR_HEIGHT);
updateWindowChrome();
roundedRoot.repaint();
}
});
addWindowStateListener(e -> updateWindowChrome());
setSize(900, 600);
SwingUtilities.invokeLater(() -> {
@@ -99,6 +106,7 @@ public class OACFrame extends JFrame {
installResizeHandling();
DesignManager.apply(this);
updateWindowChrome();
}
@Override
@@ -142,18 +150,29 @@ public class OACFrame extends JFrame {
MouseAdapter adapter = new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
if (isInFullscreenState()) {
resizeCursor = Cursor.DEFAULT_CURSOR;
setCursor(Cursor.getDefaultCursor());
return;
}
resizeCursor = getResizeCursor(e);
setCursor(Cursor.getPredefinedCursor(resizeCursor));
}
@Override
public void mousePressed(MouseEvent e) {
if (isInFullscreenState()) {
dragStart = null;
startBounds = null;
return;
}
dragStart = e.getLocationOnScreen();
startBounds = getBounds();
}
@Override
public void mouseDragged(MouseEvent e) {
if (isInFullscreenState()) return;
if (resizeCursor == Cursor.DEFAULT_CURSOR) return;
Point dragNow = e.getLocationOnScreen();
@@ -207,6 +226,10 @@ public class OACFrame extends JFrame {
}
private int getResizeCursor(MouseEvent e) {
if (isInFullscreenState()) {
return Cursor.DEFAULT_CURSOR;
}
int x = e.getX();
int y = e.getY();
int w = e.getComponent().getWidth();
@@ -229,13 +252,29 @@ public class OACFrame extends JFrame {
return Cursor.DEFAULT_CURSOR;
}
private boolean isInFullscreenState() {
return (getExtendedState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH;
}
/**
* Root panel that paints a near-transparent full-window layer (alpha=1)
* to prevent click-through, then paints the rounded background and clips children.
*/
private void updateWindowChrome() {
boolean fullscreen = isInFullscreenState();
roundedRoot.setArc(fullscreen ? 0 : CORNER_ARC);
if (fullscreen) {
getRootPane().setBorder(new LineBorder(DesignManager.resolveBorderColor(Color.GRAY), 1));
setCursor(Cursor.getDefaultCursor());
} else {
getRootPane().setBorder(new EmptyBorder(0, 0, 0, 0));
}
repaint();
}
private static final class RoundedRootPanel extends OACPanel {
private final int arc;
private int arc;
private RoundedRootPanel(int arc) {
super(new BorderLayout());
@@ -243,6 +282,10 @@ public class OACFrame extends JFrame {
setOpaque(false);
}
private void setArc(int arc) {
this.arc = Math.max(0, arc);
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
@@ -292,4 +335,4 @@ public class OACFrame extends JFrame {
}
}
}
}
}

View File

@@ -5,9 +5,14 @@
package org.openautonomousconnection.oacswing.component;
import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.atomic.AtomicInteger;
public class OACOptionPane extends JOptionPane implements OACComponent {
public OACOptionPane() {
@@ -38,6 +43,140 @@ public class OACOptionPane extends JOptionPane implements OACComponent {
super(message, messageType, optionType, icon, options, initialValue);
}
@Override
public void init() {
setOpaque(true);
setBackground(DesignManager.resolveBackground(OACOptionPane.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACOptionPane.class, getForeground()));
}
public static int showOptionDialog(Component parentComponent,
Object message,
String title,
int optionType,
int messageType,
Icon icon,
Object[] options,
Object initialValue) throws HeadlessException {
AtomicInteger result = new AtomicInteger(CLOSED_OPTION);
OACDialog dialog = new OACDialog(JOptionPane.getFrameForComponent(parentComponent), title, true);
dialog.setUndecorated(true);
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
dialog.setContentPane(buildDialogContent(title, message, icon, optionType, options, result, dialog));
dialog.setResizable(false);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
result.set(CLOSED_OPTION);
}
});
dialog.pack();
dialog.setMinimumSize(new Dimension(420, dialog.getHeight()));
dialog.setLocationRelativeTo(parentComponent);
dialog.setVisible(true);
dialog.dispose();
return result.get();
}
private static Container buildDialogContent(String title,
Object message,
Icon icon,
int optionType,
Object[] options,
AtomicInteger result,
OACDialog dialog) {
OACPanel root = new OACPanel(new BorderLayout());
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 borderColor = DesignManager.resolveBorderColor(foreground.darker());
root.setBackground(background);
root.setBorder(BorderFactory.createLineBorder(borderColor, 1));
OACPanel header = new OACPanel(new BorderLayout());
header.setBorder(new EmptyBorder(10, 12, 10, 12));
header.setBackground(headerBackground);
JSeparator separator = new JSeparator(SwingConstants.HORIZONTAL);
separator.setForeground(borderColor);
separator.setBackground(borderColor);
header.add(separator, BorderLayout.SOUTH);
OACLabel titleLabel = new OACLabel(title == null ? "" : title);
titleLabel.setForeground(foreground);
header.add(titleLabel, BorderLayout.WEST);
OACPanel center = new OACPanel(new BorderLayout(10, 0));
center.setBackground(background);
center.setBorder(new EmptyBorder(14, 12, 12, 12));
if (icon != null) {
OACLabel iconLabel = new OACLabel(icon);
iconLabel.setForeground(foreground);
center.add(iconLabel, BorderLayout.WEST);
}
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));
buttons.setBackground(background);
Object[] displayOptions = resolveOptions(optionType, options);
for (int i = 0; i < displayOptions.length; i++) {
final int index = i;
OACButton button = new OACButton(String.valueOf(displayOptions[i]));
button.initDesign();
button.setBackground(DesignManager.resolveBackground(OACButton.class, button.getBackground()));
button.setForeground(DesignManager.resolveForeground(OACButton.class, button.getForeground()));
button.setHoveredColor(DesignManager.resolveHovered(OACButton.class, button.getBackground().brighter()));
button.setPressedColor(DesignManager.resolvePressed(OACButton.class, button.getBackground().darker()));
button.setBorder(BorderFactory.createLineBorder(borderColor, 1, true));
button.setPreferredSize(new Dimension(100, 32));
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
button.addActionListener(e -> {
result.set(mapResult(optionType, options, index));
dialog.dispose();
});
buttons.add(button);
}
root.add(header, BorderLayout.NORTH);
root.add(center, BorderLayout.CENTER);
root.add(buttons, BorderLayout.SOUTH);
return root;
}
private static Object[] resolveOptions(int optionType, Object[] options) {
if (options != null && options.length > 0) {
return options;
}
return switch (optionType) {
case YES_NO_OPTION -> new Object[]{"Yes", "No"};
case YES_NO_CANCEL_OPTION -> new Object[]{"Yes", "No", "Cancel"};
case OK_CANCEL_OPTION -> new Object[]{"OK", "Cancel"};
default -> new Object[]{"OK"};
};
}
private static int mapResult(int optionType, Object[] providedOptions, int clickedIndex) {
if (providedOptions != null && providedOptions.length > 0) {
return clickedIndex;
}
return switch (optionType) {
case YES_NO_OPTION -> clickedIndex == 0 ? YES_OPTION : NO_OPTION;
case YES_NO_CANCEL_OPTION -> clickedIndex == 0 ? YES_OPTION : (clickedIndex == 1 ? NO_OPTION : CANCEL_OPTION);
case OK_CANCEL_OPTION -> clickedIndex == 0 ? OK_OPTION : CANCEL_OPTION;
default -> OK_OPTION;
};
}
@Override
public Component add(Component comp) {
this.initOther(comp);

View File

@@ -5,8 +5,10 @@
package org.openautonomousconnection.oacswing.component;
import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.text.Document;
import java.awt.*;
@@ -31,6 +33,39 @@ public class OACPasswordField extends JPasswordField implements OACComponent {
super(doc, txt, columns);
}
@Override
public void init() {
setOpaque(true);
applyInputBorder();
setCaretColor(getForeground());
setSelectionColor(getForeground().darker());
setSelectedTextColor(getBackground().brighter());
setPreferredSize(new Dimension(Math.max(160, getPreferredSize().width), 34));
}
@Override
public void updateUI() {
super.updateUI();
applyInputBorder();
}
private void applyInputBorder() {
setBorder(new EmptyBorder(6, 10, 6, 10));
}
@Override
protected void paintBorder(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
try {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(DesignManager.resolveBorderColor(getForeground()));
g2.setStroke(new BasicStroke(2f));
g2.drawRoundRect(1, 1, getWidth() - 3, getHeight() - 3, 10, 10);
} finally {
g2.dispose();
}
}
@Override
public Component add(Component comp) {
this.initOther(comp);

View File

@@ -5,6 +5,7 @@
package org.openautonomousconnection.oacswing.component;
import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*;
import javax.swing.text.Document;
@@ -35,6 +36,16 @@ public class OACTextArea extends JTextArea implements OACComponent {
super(doc, text, rows, columns);
}
@Override
public void init() {
setOpaque(true);
setMargin(new Insets(6, 10, 6, 10));
setBorder(DesignManager.createTextComponentBorder());
setCaretColor(getForeground());
setSelectionColor(getForeground().darker());
setSelectedTextColor(getBackground().brighter());
}
@Override
public Component add(Component comp) {
this.initOther(comp);

View File

@@ -4,7 +4,12 @@
package org.openautonomousconnection.oacswing.component;
import org.openautonomousconnection.oacswing.border.RoundedBorder;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.text.Document;
import java.awt.*;
@@ -29,6 +34,29 @@ public class OACTextField extends JTextField implements OACComponent {
super(doc, text, columns);
}
@Override
public void init() {
setOpaque(true);
applyInputBorder();
setCaretColor(getForeground());
setSelectionColor(getForeground().darker());
setSelectedTextColor(getBackground().brighter());
setPreferredSize(new Dimension(Math.max(160, getPreferredSize().width), 34));
}
@Override
public void updateUI() {
super.updateUI();
applyInputBorder();
}
private void applyInputBorder() {
setBorder(new CompoundBorder(
new RoundedBorder(DesignManager.resolveBorderColor(getForeground()), 10, 2),
new EmptyBorder(6, 10, 6, 10)
));
}
@Override
public Component add(Component comp) {
this.initOther(comp);

View File

@@ -5,6 +5,7 @@
package org.openautonomousconnection.oacswing.component;
import lombok.NonNull;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*;
import javax.swing.text.StyledDocument;
@@ -18,6 +19,16 @@ public class OACTextPane extends JTextPane implements OACComponent {
super(doc);
}
@Override
public void init() {
setOpaque(true);
setMargin(new Insets(6, 10, 6, 10));
setBorder(DesignManager.createTextComponentBorder());
setCaretColor(getForeground());
setSelectionColor(getForeground().darker());
setSelectedTextColor(getBackground().brighter());
}
@Override
public Component add(Component comp) {
this.initOther(comp);

View File

@@ -6,6 +6,7 @@ package org.openautonomousconnection.oacswing.component;
import lombok.Getter;
import lombok.Setter;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
@@ -39,6 +40,9 @@ public class OACTitleBar extends OACPanel {
super(new BorderLayout());
this.frame = frame;
setOpaque(true);
setBackground(DesignManager.resolveBackground(OACTitleBar.class, getBackground()));
setForeground(DesignManager.resolveForeground(OACTitleBar.class, getForeground()));
setPreferredSize(new Dimension(1, HEIGHT));
setBorder(new EmptyBorder(6, 10, 6, 10));
@@ -170,4 +174,4 @@ public class OACTitleBar extends OACPanel {
frame.dispatchEvent(new ComponentEvent(this, ComponentEvent.COMPONENT_RESIZED));
}
}
}

View File

@@ -10,11 +10,23 @@ import org.openautonomousconnection.oacswing.border.RoundedBorder;
import org.openautonomousconnection.oacswing.component.*;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.ContainerAdapter;
import java.awt.event.ContainerEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class DesignManager {
private static final String OAC_INIT_PROPERTY = "oac.design.initialized";
private static final String OAC_LISTENER_PROPERTY = "oac.design.listener";
private static final Border INVISIBLE_BORDER = new EmptyBorder(0, 0, 0, 0);
@Getter
@Setter
private static Design globalDesign;
@@ -27,13 +39,13 @@ public class DesignManager {
OACColor.DARK_INPUT_BUTTON_HOVER,
OACColor.DARK_INPUT_BUTTON_HOVER,
OACColor.DARK_INACTIVE_BUTTON,
true
false
));
Design.DARK.getElements().put(OACCheckBox.class, new DesignFlags(OACColor.DARK_INPUT_FIELD));
Design.DARK.getElements().put(OACCheckBoxMenuItem.class, new DesignFlags(OACColor.DARK_ITEM));
Design.DARK.getElements().put(OACTextField.class, new DesignFlags(OACColor.DARK_ITEM, OACColor.DARK_TEXT));
Design.DARK.getElements().put(OACTextArea.class, new DesignFlags(OACColor.DARK_ITEM, 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(OACColorChooser.class, new DesignFlags(OACColor.DARK_SECTION));
Design.DARK.getElements().put(OACComboBox.class, new DesignFlags(OACColor.DARK_INPUT_FIELD));
Design.DARK.getElements().put(OACFrame.class, new DesignFlags(OACColor.DARK_BACKGROUND));
@@ -44,14 +56,16 @@ public class DesignManager {
Design.DARK.getElements().put(OACMenuBar.class, new DesignFlags(OACColor.DARK_SECTION));
Design.DARK.getElements().put(OACMenuItem.class, new DesignFlags(OACColor.DARK_ITEM));
Design.DARK.getElements().put(OACOptionPane.class, new DesignFlags(OACColor.DARK_BACKGROUND));
Design.DARK.getElements().put(OACPanel.class, new DesignFlags(OACColor.DARK_BACKGROUND, true));
Design.DARK.getElements().put(OACPasswordField.class, new DesignFlags(OACColor.DARK_INPUT_FIELD));
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(OACPopupMenu.class, new DesignFlags(OACColor.DARK_BACKGROUND));
Design.DARK.getElements().put(OACProgressBar.class, new DesignFlags(OACColor.DARK_ITEM));
Design.DARK.getElements().put(OACRadioButton.class, new DesignFlags(OACColor.DARK_BUTTON));
Design.DARK.getElements().put(OACTitleBar.class, new DesignFlags(OACColor.DARK_SECTION));
Design.DARK.getElements().put(OACDialog.class, new DesignFlags(OACColor.DARK_BACKGROUND));
Design.DARK.getElements().put(OACTitleBar.class, new DesignFlags(OACColor.DARK_HEADER));
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;
DesignManager.getInstance().registerDesign(Design.DARK);
}
@@ -73,7 +87,7 @@ public class DesignManager {
if (globalDesign == null)
return;
DesignFlags designFlags = globalDesign.getElements().get(component.getClass());
DesignFlags designFlags = resolveFlags(component.getClass());
if (designFlags == null)
return;
@@ -96,22 +110,101 @@ public class DesignManager {
if (disabled != null) pressable.setDisabledColor(disabled.getColor());
}
if (component instanceof JComponent jComponent) {
jComponent.setBackground(backgroundColour.getColor());
if (component instanceof Component awtComponent) {
awtComponent.setBackground(backgroundColour.getColor());
if (foregroundColour != null) {
jComponent.setForeground(foregroundColour.getColor());
awtComponent.setForeground(foregroundColour.getColor());
}
}
if (component instanceof JComponent jComponent) {
if (hasBorder) {
jComponent.setBorder(globalDesign.getBorder());
jComponent.setOpaque(false);
} else if (!(jComponent instanceof OACTitleBar) && !(jComponent instanceof JTextComponent)) {
Border currentBorder = jComponent.getBorder();
if (currentBorder == null || hasZeroInsets(currentBorder, jComponent)) {
jComponent.setBorder(INVISIBLE_BORDER);
}
}
if (jComponent instanceof JTextComponent textComponent) {
textComponent.setOpaque(true);
textComponent.setBorder(createTextComponentBorder());
}
if (!Boolean.TRUE.equals(jComponent.getClientProperty(OAC_INIT_PROPERTY))) {
component.init();
jComponent.putClientProperty(OAC_INIT_PROPERTY, Boolean.TRUE);
}
} else {
component.init();
}
}
private static boolean hasZeroInsets(Border border, JComponent component) {
Insets insets = border.getBorderInsets(component);
return insets.top == 0 && insets.left == 0 && insets.bottom == 0 && insets.right == 0;
}
public static DesignFlags getFlagsFor(Class<?> type) {
if (globalDesign == null) {
return null;
}
return resolveFlags(type);
}
public static Color resolveBackground(Class<?> type, Color fallback) {
DesignFlags flags = getFlagsFor(type);
if (flags != null && flags.background() != null) {
return flags.background().getColor();
}
return fallback;
}
public static Color resolveForeground(Class<?> type, Color fallback) {
DesignFlags flags = getFlagsFor(type);
if (flags != null && flags.foreground() != null) {
return flags.foreground().getColor();
}
if (globalDesign != null && globalDesign.getForegroundColour() != null) {
return globalDesign.getForegroundColour().getColor();
}
return fallback;
}
public static Color resolveHovered(Class<?> type, Color fallback) {
DesignFlags flags = getFlagsFor(type);
if (flags != null && flags.hovered() != null) {
return flags.hovered().getColor();
}
return fallback;
}
public static Color resolvePressed(Class<?> type, Color fallback) {
DesignFlags flags = getFlagsFor(type);
if (flags != null && flags.pressed() != null) {
return flags.pressed().getColor();
}
return fallback;
}
public static Border createTextComponentBorder() {
Color borderColor = resolveBorderColor(new Color(120, 120, 120));
return new CompoundBorder(
new LineBorder(borderColor, 2, true),
new EmptyBorder(4, 8, 4, 8)
);
}
public static Color resolveBorderColor(Color fallback) {
if (globalDesign != null && globalDesign.getBorder() instanceof RoundedBorder roundedBorder) {
return roundedBorder.getColor();
}
return fallback;
}
public static void apply(OACFrame frame) {
DesignFlags designFlags;
@@ -122,8 +215,50 @@ public class DesignManager {
frame.getContentPane().setBackground(designFlags.background().getColor());
}
applyTree(frame.getRootPane());
applyTree(frame.getLayeredPane());
installAutoApply(frame.getRootPane());
installAutoApply(frame.getLayeredPane());
}
private static DesignFlags resolveFlags(Class<?> type) {
Class<?> current = type;
while (current != null) {
DesignFlags flags = globalDesign.getElements().get(current);
if (flags != null) {
return flags;
}
current = current.getSuperclass();
}
return null;
}
private static void applyTree(Component component) {
if (component instanceof OACComponent oacComponent) {
oacComponent.initDesign();
}
if (component instanceof Container container) {
installAutoApply(container);
for (Component child : container.getComponents()) {
applyTree(child);
}
}
}
private static void installAutoApply(Container container) {
if (!(container instanceof JComponent jComponent)) {
return;
}
if (Boolean.TRUE.equals(jComponent.getClientProperty(OAC_LISTENER_PROPERTY))) {
return;
}
container.addContainerListener(new ContainerAdapter() {
@Override
public void componentAdded(ContainerEvent e) {
applyTree(e.getChild());
}
});
jComponent.putClientProperty(OAC_LISTENER_PROPERTY, Boolean.TRUE);
}
public void registerDesign(Design design) {