Compare commits
5 Commits
c307620dde
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a01110e8cc | ||
|
|
56b79f9129 | ||
|
|
379aa08ed6 | ||
|
|
2d3a3fa1f1 | ||
|
|
9d391c893e |
9
pom.xml
9
pom.xml
@@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>org.openautonomousconnection</groupId>
|
<groupId>org.openautonomousconnection</groupId>
|
||||||
<artifactId>OACSwing</artifactId>
|
<artifactId>OACSwing</artifactId>
|
||||||
<version>1.0.0-BETA.1.0</version>
|
<version>0.0.0-STABLE.1.3</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>
|
||||||
@@ -57,8 +57,7 @@
|
|||||||
<name>GNU General Public License v3.0</name>
|
<name>GNU General Public License v3.0</name>
|
||||||
<url>https://www.gnu.org/licenses/gpl-3.0.html</url>
|
<url>https://www.gnu.org/licenses/gpl-3.0.html</url>
|
||||||
<comments>
|
<comments>
|
||||||
Default license: Applies to all users and projects unless an explicit alternative license has been
|
Default license: Applies to all users and projects unless an explicit alternative license has been granted.
|
||||||
granted.
|
|
||||||
</comments>
|
</comments>
|
||||||
</license>
|
</license>
|
||||||
<license>
|
<license>
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ package org.openautonomousconnection.oacswing.animated;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
// TODO: Implement pause functionality
|
||||||
|
// (preferably not interfering with play() always reliably playing the animation from the start; aka define it as "setPaused(bool)" or "pause()" and "unpause()")
|
||||||
public interface AnimatedComponent {
|
public interface AnimatedComponent {
|
||||||
void setCurrentRun(Timer timer);
|
void setCurrentRun(Timer timer);
|
||||||
Timer getCurrentRun();
|
Timer getCurrentRun();
|
||||||
@@ -16,26 +18,48 @@ public interface AnimatedComponent {
|
|||||||
|
|
||||||
void setBounds(int x, int y, int width, int height);
|
void setBounds(int x, int y, int width, int height);
|
||||||
|
|
||||||
default void play(double speed, boolean loop) {
|
//TODO: implement momentum
|
||||||
|
/**
|
||||||
|
* Plays this object's animation path
|
||||||
|
* @param speed how fast the animation should play percentage (0 to 100; can be more)
|
||||||
|
* @param loop if should the animation should loop
|
||||||
|
* @param momentum basically how far an object "shoots off" from a specified keyframe when stopping at proportional speed
|
||||||
|
* (linear keyframes are excluded from this). Not implemented yet
|
||||||
|
*/
|
||||||
|
default void play(double speed, boolean loop, double momentum) {
|
||||||
|
|
||||||
|
// Speed has to be calculated like this since it heavily impacts animations with low inbetweens
|
||||||
|
// TODO: definetly shouhld fix this, so that speed means the same for every animation and does not depend on framerate
|
||||||
|
speed /= 100;
|
||||||
|
|
||||||
AtomicInteger ticksPassed = new AtomicInteger();
|
AtomicInteger ticksPassed = new AtomicInteger();
|
||||||
|
|
||||||
this.setCurrentRun(new Timer(0, e -> {
|
// Cloning the animation path to not mess with the original,
|
||||||
|
// If an extra frame is to be added because loop is set to true
|
||||||
|
|
||||||
if(ticksPassed.get() * speed / (100) < 1) {
|
AnimationPath playedPath = this.getAnimationPath().clone();
|
||||||
ticksPassed.addAndGet(this.getAnimationPath().getInbetweens());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyFrame next = this.getAnimationPath().getNext();
|
|
||||||
|
|
||||||
if(next == null) {
|
|
||||||
if(loop)
|
if(loop)
|
||||||
this.getAnimationPath().reset();
|
playedPath.add(playedPath.getNext());
|
||||||
|
|
||||||
|
// Finalize for timer
|
||||||
|
double finalSpeed = speed;
|
||||||
|
this.setCurrentRun(new Timer(0, e -> {
|
||||||
|
if (!playedPath.anyMore()) {
|
||||||
|
if (loop)
|
||||||
|
playedPath.reset();
|
||||||
else
|
else
|
||||||
((Timer) e.getSource()).stop();
|
((Timer) e.getSource()).stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(ticksPassed.get() * finalSpeed / (100) < 1) {
|
||||||
|
ticksPassed.addAndGet(playedPath.getInbetweens());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyFrame next = playedPath.getNext();
|
||||||
|
|
||||||
this.setBounds(next.position().x, next.position().y, next.width(), next.height());
|
this.setBounds(next.position().x, next.position().y, next.width(), next.height());
|
||||||
|
|
||||||
ticksPassed.set(0);
|
ticksPassed.set(0);
|
||||||
@@ -44,14 +68,33 @@ public interface AnimatedComponent {
|
|||||||
this.getCurrentRun().start();
|
this.getCurrentRun().start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays this object's animation path
|
||||||
|
* @param speed how fast the animation should play percentage (0 to 100; can be more)
|
||||||
|
* @param loop if should the animation should loop
|
||||||
|
*/
|
||||||
|
default void play(double speed, boolean loop) {
|
||||||
|
this.play(speed, loop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays this object's animation path
|
||||||
|
* @param speed how fast the animation should play percentage (0 to 100; can be more)
|
||||||
|
*/
|
||||||
default void play(double speed) {
|
default void play(double speed) {
|
||||||
this.play(speed, false);
|
this.play(speed, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays this object's animation path
|
||||||
|
*/
|
||||||
default void play() {
|
default void play() {
|
||||||
this.play(1, false);
|
this.play(1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops and resets replay of this object's animation path
|
||||||
|
*/
|
||||||
default void stop() {
|
default void stop() {
|
||||||
if(this.getCurrentRun() != null)
|
if(this.getCurrentRun() != null)
|
||||||
if(this.getCurrentRun().isRunning())
|
if(this.getCurrentRun().isRunning())
|
||||||
|
|||||||
@@ -80,10 +80,6 @@ public class AnimationPath extends ArrayList<KeyFrame> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private KeyFrame getKeyFrame(KeyFrame current, KeyFrame next, int subIterator) {
|
private KeyFrame getKeyFrame(KeyFrame current, KeyFrame next, int subIterator) {
|
||||||
// How far the transition should be finished
|
|
||||||
|
|
||||||
double transition = this.linear(subIterator);
|
|
||||||
|
|
||||||
CombinedPathMethod method;
|
CombinedPathMethod method;
|
||||||
|
|
||||||
KeyFrame.PathMethod
|
KeyFrame.PathMethod
|
||||||
@@ -95,26 +91,16 @@ public class AnimationPath extends ArrayList<KeyFrame> {
|
|||||||
if(currentMethod.equals(KeyFrame.PathMethod.EASE_OUT) || currentMethod.equals(KeyFrame.PathMethod.EASE_IN_AND_OUT))
|
if(currentMethod.equals(KeyFrame.PathMethod.EASE_OUT) || currentMethod.equals(KeyFrame.PathMethod.EASE_IN_AND_OUT))
|
||||||
currentMethod = KeyFrame.PathMethod.EASE_OUT;
|
currentMethod = KeyFrame.PathMethod.EASE_OUT;
|
||||||
|
|
||||||
if(nextMethod.equals(KeyFrame.PathMethod.EASE_OUT) || nextMethod.equals(KeyFrame.PathMethod.EASE_IN_AND_OUT))
|
if(nextMethod.equals(KeyFrame.PathMethod.EASE_IN) || nextMethod.equals(KeyFrame.PathMethod.EASE_IN_AND_OUT))
|
||||||
nextMethod = KeyFrame.PathMethod.EASE_OUT;
|
nextMethod = KeyFrame.PathMethod.EASE_IN;
|
||||||
|
|
||||||
method = switch (currentMethod) {
|
method = switch (currentMethod) {
|
||||||
case LINEAR -> switch (nextMethod) {
|
case LINEAR -> switch (nextMethod) {
|
||||||
case LINEAR -> CombinedPathMethod.LINEAR;
|
case LINEAR -> CombinedPathMethod.LINEAR;
|
||||||
case EASE_IN -> CombinedPathMethod.LINEAR_EASE_IN;
|
case EASE_IN -> CombinedPathMethod.LINEAR_EASE_IN;
|
||||||
// Removed this case, as ease-out doesn't make sense right before a keyframe
|
|
||||||
// case EASE_OUT -> CombinedPathMethod.LINEAR_EASE_OUT;
|
|
||||||
default -> throw new IllegalStateException("Unexpected value: " + nextMethod);
|
default -> throw new IllegalStateException("Unexpected value: " + nextMethod);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Removed this case, as ease-in doesn't make sense right after a keyframe
|
|
||||||
// case EASE_IN -> switch (nextMethod) {
|
|
||||||
// case LINEAR -> CombinedPathMethod.EASE_IN_LINEAR;
|
|
||||||
// case EASE_IN -> CombinedPathMethod.EASE_IN;
|
|
||||||
// case EASE_OUT -> CombinedPathMethod.EASE_OUT_LINEAR;
|
|
||||||
// default -> throw new IllegalStateException("Unexpected value: " + nextMethod);
|
|
||||||
// };
|
|
||||||
|
|
||||||
case EASE_OUT -> switch (nextMethod) {
|
case EASE_OUT -> switch (nextMethod) {
|
||||||
case LINEAR -> CombinedPathMethod.EASE_OUT_LINEAR;
|
case LINEAR -> CombinedPathMethod.EASE_OUT_LINEAR;
|
||||||
case EASE_IN -> CombinedPathMethod.EASE_OUT_AND_IN;
|
case EASE_IN -> CombinedPathMethod.EASE_OUT_AND_IN;
|
||||||
@@ -124,36 +110,7 @@ public class AnimationPath extends ArrayList<KeyFrame> {
|
|||||||
default -> throw new IllegalStateException("Unexpected value: " + currentMethod);
|
default -> throw new IllegalStateException("Unexpected value: " + currentMethod);
|
||||||
};
|
};
|
||||||
|
|
||||||
double threshold = Math.min(current.transition(), next.transition());
|
return this.inBetween(current, next, method, subIterator);
|
||||||
|
|
||||||
boolean thresholdReached;
|
|
||||||
|
|
||||||
if(current.transition() < next.transition())
|
|
||||||
thresholdReached = transition > threshold;
|
|
||||||
else
|
|
||||||
thresholdReached = transition <= next.transition();
|
|
||||||
|
|
||||||
if(thresholdReached)
|
|
||||||
transition = 2 * transition - 1;
|
|
||||||
|
|
||||||
transition = switch (method) {
|
|
||||||
case LINEAR -> transition;
|
|
||||||
|
|
||||||
case LINEAR_EASE_IN -> thresholdReached ? easeIn(subIterator) : transition;
|
|
||||||
|
|
||||||
// case EASE_IN -> easeIn(subIterator);
|
|
||||||
|
|
||||||
case EASE_OUT -> easeOut(subIterator);
|
|
||||||
|
|
||||||
case EASE_OUT_LINEAR -> thresholdReached ? transition : easeOut(subIterator);
|
|
||||||
|
|
||||||
case EASE_OUT_AND_IN -> thresholdReached ? easeOut(subIterator) : easeIn(subIterator);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
System.out.println(method + " " + transition + " - linear: " + linear(subIterator));
|
|
||||||
|
|
||||||
return inBetween(current, next, transition, thresholdReached);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,6 +190,7 @@ public class AnimationPath extends ArrayList<KeyFrame> {
|
|||||||
return new Point(point.x - subtrahend.x, point.y - subtrahend.y);
|
return new Point(point.x - subtrahend.x, point.y - subtrahend.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unused right now
|
||||||
/**
|
/**
|
||||||
* Calculate point between both points
|
* Calculate point between both points
|
||||||
* @param p1 first point
|
* @param p1 first point
|
||||||
@@ -248,28 +206,72 @@ public class AnimationPath extends ArrayList<KeyFrame> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Point onLine(Point p1, Point p2, double scalar) {
|
||||||
|
return add(
|
||||||
|
p1,
|
||||||
|
multiply(
|
||||||
|
subtract(p2, p1),
|
||||||
|
scalar
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find in-between with given scalar
|
* Find in-between with given scalar
|
||||||
* @param kf1 first frame
|
* @param kf1 first frame
|
||||||
* @param kf2 next frame
|
* @param kf2 next frame
|
||||||
* @param scalar factor (ideally between 0 and 1, representing 0% transition to 100% transition)
|
* @param subIterator how far the animation path has proceeded
|
||||||
* @param overHalf if this inbetween is over halfway through the transition process
|
|
||||||
* @return in-between frame
|
* @return in-between frame
|
||||||
*/
|
*/
|
||||||
private static KeyFrame inBetween(KeyFrame kf1, KeyFrame kf2, double scalar, boolean overHalf) {
|
private KeyFrame inBetween(KeyFrame kf1, KeyFrame kf2, CombinedPathMethod method, int subIterator) {
|
||||||
// create halfway marked point
|
double scalar = this.linear(subIterator);
|
||||||
if(overHalf)
|
|
||||||
kf1.position().setLocation(
|
double remainder = kf1.transition() - kf2.transition();
|
||||||
middle(kf1.position(), kf2.position())
|
|
||||||
|
double transition = 0.01;
|
||||||
|
|
||||||
|
if(remainder > 0) {
|
||||||
|
if(kf1.transition() > kf2.transition())
|
||||||
|
transition = kf1.transition() + remainder / 2;
|
||||||
|
else
|
||||||
|
transition = 1 - kf2.transition() - remainder / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
double threshold = Math.min(kf1.transition(), 1 - kf2.transition());
|
||||||
|
|
||||||
|
boolean thresholdReached = scalar > threshold;
|
||||||
|
|
||||||
|
// Create point on the line between both keyframes
|
||||||
|
if(thresholdReached) {
|
||||||
|
|
||||||
|
kf1 = new KeyFrame(
|
||||||
|
onLine(kf1.position(), kf2.position(), threshold),
|
||||||
|
|
||||||
|
(int) (kf1.width() + (kf2.width() - kf1.width()) * threshold),
|
||||||
|
(int) (kf1.height() + (kf2.height() - kf1.height()) * threshold)
|
||||||
);
|
);
|
||||||
|
|
||||||
// position
|
}
|
||||||
|
|
||||||
|
scalar = switch (method) {
|
||||||
|
case LINEAR -> scalar;
|
||||||
|
|
||||||
|
case LINEAR_EASE_IN -> thresholdReached ? this.easeIn(subIterator) * (1 - transition) : scalar;
|
||||||
|
|
||||||
|
case EASE_OUT -> easeOut(subIterator);
|
||||||
|
|
||||||
|
case EASE_OUT_LINEAR -> thresholdReached ? 2 * (scalar - 0.5) : this.easeOut(subIterator);
|
||||||
|
|
||||||
|
case EASE_OUT_AND_IN -> thresholdReached ? this.easeOut(subIterator) : this.easeIn(subIterator) * (1 - transition);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Position
|
||||||
Point difference = subtract(kf2.position(), kf1.position());
|
Point difference = subtract(kf2.position(), kf1.position());
|
||||||
|
|
||||||
Point pos = add(kf1.position(), multiply(difference, scalar));
|
Point pos = add(kf1.position(), multiply(difference, scalar));
|
||||||
|
|
||||||
// scale
|
// Scale
|
||||||
|
|
||||||
int width, height;
|
int width, height;
|
||||||
|
|
||||||
@@ -288,25 +290,27 @@ public class AnimationPath extends ArrayList<KeyFrame> {
|
|||||||
return new AnimationPath(this.inbetweens, new ArrayList<>(this));
|
return new AnimationPath(this.inbetweens, new ArrayList<>(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private double linear(int subIterator) {
|
private double linear(double subIterator) {
|
||||||
return (double) subIterator/2 / this.inbetweens;
|
return subIterator / this.inbetweens;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double easeIn(int subIterator) {
|
private double easeIn(double subIterator) {
|
||||||
return this.linear(subIterator*2) * this.linear(subIterator*2);
|
return this.linear(subIterator - 1) * this.linear(subIterator - 1) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double easeOut(int subIterator) {
|
private double easeOut(double subIterator) {
|
||||||
return Math.sqrt(this.linear(subIterator*2));
|
return 2 * this.linear(subIterator) * this.linear(subIterator);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumerator purely to describe two combined PathMethods, like a 2-dimensional PathMethod enum
|
* Enumerator purely to describe two combined PathMethods, like a 2-dimensional PathMethod enum
|
||||||
*/
|
*/
|
||||||
private enum CombinedPathMethod {
|
private enum CombinedPathMethod {
|
||||||
LINEAR,
|
LINEAR,
|
||||||
LINEAR_EASE_IN,
|
LINEAR_EASE_IN,
|
||||||
// EASE_IN,
|
|
||||||
EASE_OUT,
|
EASE_OUT,
|
||||||
EASE_OUT_LINEAR,
|
EASE_OUT_LINEAR,
|
||||||
EASE_OUT_AND_IN;
|
EASE_OUT_AND_IN;
|
||||||
|
|||||||
@@ -73,11 +73,15 @@ public record KeyFrame(Point position, int width, int height, PathMethod pathMet
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public enum PathMethod {
|
public enum PathMethod {
|
||||||
LINEAR,
|
LINEAR,
|
||||||
EASE_IN,
|
EASE_IN,
|
||||||
EASE_OUT,
|
EASE_OUT,
|
||||||
EASE_IN_AND_OUT
|
EASE_IN_AND_OUT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyFrame clone() {
|
||||||
|
return new KeyFrame(this.position, this.width, this.height, this.pathMethod, this.transition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ public class OACButton extends JButton implements OACPressable {
|
|||||||
setContentAreaFilled(false);
|
setContentAreaFilled(false);
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
setFocusPainted(false);
|
setFocusPainted(false);
|
||||||
|
setBorderPainted(false);
|
||||||
|
|
||||||
if (!isEnabled() && getDisabledColor() != null) {
|
if (!isEnabled() && getDisabledColor() != null) {
|
||||||
super.setBackground(getDisabledColor());
|
super.setBackground(getDisabledColor());
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -0,0 +1,251 @@
|
|||||||
|
package org.openautonomousconnection.oacswing.component;
|
||||||
|
|
||||||
|
import lombok.NonNull;
|
||||||
|
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.border.CompoundBorder;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
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 {
|
||||||
|
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() {
|
||||||
|
super();
|
||||||
|
initDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OACDialog(Frame owner) {
|
||||||
|
super(owner);
|
||||||
|
initDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OACDialog(Frame owner, boolean modal) {
|
||||||
|
super(owner, modal);
|
||||||
|
initDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OACDialog(Frame owner, String title) {
|
||||||
|
super(owner, title);
|
||||||
|
initDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OACDialog(Frame owner, String title, boolean modal) {
|
||||||
|
super(owner, title, modal);
|
||||||
|
initDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OACDialog(Dialog owner) {
|
||||||
|
super(owner);
|
||||||
|
initDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OACDialog(Dialog owner, boolean modal) {
|
||||||
|
super(owner, modal);
|
||||||
|
initDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OACDialog(Dialog owner, String title) {
|
||||||
|
super(owner, title);
|
||||||
|
initDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OACDialog(Dialog owner, String title, boolean modal) {
|
||||||
|
super(owner, title, modal);
|
||||||
|
initDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDialog() {
|
||||||
|
setUndecorated(true);
|
||||||
|
DesignManager.apply(this);
|
||||||
|
Color dialogBackground = DesignManager.resolveBackground(OACDialog.class, getBackground());
|
||||||
|
Color dialogForeground = DesignManager.resolveForeground(OACDialog.class, getForeground());
|
||||||
|
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
|
||||||
|
public Component add(Component comp) {
|
||||||
|
this.initOther(comp);
|
||||||
|
return super.add(comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component add(Component comp, int index) {
|
||||||
|
this.initOther(comp);
|
||||||
|
return super.add(comp, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(@NonNull Component comp, Object constraints) {
|
||||||
|
this.initOther(comp);
|
||||||
|
super.add(comp, constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component add(String name, Component comp) {
|
||||||
|
this.initOther(comp);
|
||||||
|
return super.add(name, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(Component comp, Object constraints, int index) {
|
||||||
|
this.initOther(comp);
|
||||||
|
super.add(comp, constraints, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package org.openautonomousconnection.oacswing.component;
|
package org.openautonomousconnection.oacswing.component;
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NonNull;
|
|
||||||
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import javax.swing.border.LineBorder;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ComponentAdapter;
|
import java.awt.event.ComponentAdapter;
|
||||||
import java.awt.event.ComponentEvent;
|
import java.awt.event.ComponentEvent;
|
||||||
@@ -13,51 +13,42 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OAC Swing frame with a custom title bar.
|
* Custom undecorated frame with rounded visuals without using Window#setShape.
|
||||||
|
*
|
||||||
|
* <p>Windows note:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>Fully transparent pixels (alpha=0) are click-through on per-pixel transparent windows.</li>
|
||||||
|
* <li>Therefore this implementation ensures alpha is NEVER 0 anywhere in the window.</li>
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class OACFrame extends JFrame {
|
public class OACFrame extends JFrame {
|
||||||
private static final int RESIZE_MARGIN = 8;
|
|
||||||
|
private static final int RESIZE_MARGIN = 2;
|
||||||
private static final int TITLE_BAR_HEIGHT = 42;
|
private static final int TITLE_BAR_HEIGHT = 42;
|
||||||
|
private static final int CORNER_ARC = 30;
|
||||||
|
|
||||||
private Point dragStart;
|
private Point dragStart;
|
||||||
private Rectangle startBounds;
|
private Rectangle startBounds;
|
||||||
private int resizeCursor = Cursor.DEFAULT_CURSOR;
|
private int resizeCursor = Cursor.DEFAULT_CURSOR;
|
||||||
@Getter
|
|
||||||
private OACTitleBar titleBar;
|
private OACTitleBar titleBar;
|
||||||
|
|
||||||
/**
|
private final RoundedRootPanel roundedRoot = new RoundedRootPanel(CORNER_ARC);
|
||||||
* Creates a new frame.
|
|
||||||
*/
|
|
||||||
public OACFrame() {
|
public OACFrame() {
|
||||||
super();
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new frame with the given graphics configuration.
|
|
||||||
*
|
|
||||||
* @param gc graphics configuration
|
|
||||||
*/
|
|
||||||
public OACFrame(GraphicsConfiguration gc) {
|
public OACFrame(GraphicsConfiguration gc) {
|
||||||
super(gc);
|
super(gc);
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new frame with the given title.
|
|
||||||
*
|
|
||||||
* @param title frame title
|
|
||||||
*/
|
|
||||||
public OACFrame(String title) {
|
public OACFrame(String title) {
|
||||||
super(title);
|
super(title);
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new frame with the given title and graphics configuration.
|
|
||||||
*
|
|
||||||
* @param title frame title
|
|
||||||
* @param gc graphics configuration
|
|
||||||
*/
|
|
||||||
public OACFrame(String title, GraphicsConfiguration gc) {
|
public OACFrame(String title, GraphicsConfiguration gc) {
|
||||||
super(title, gc);
|
super(title, gc);
|
||||||
init();
|
init();
|
||||||
@@ -71,12 +62,16 @@ public class OACFrame extends JFrame {
|
|||||||
setMinimumSize(new Dimension(900, 600));
|
setMinimumSize(new Dimension(900, 600));
|
||||||
setLocationByPlatform(true);
|
setLocationByPlatform(true);
|
||||||
|
|
||||||
OACPanel content = new OACPanel(new BorderLayout());
|
// CRITICAL: Do NOT use alpha=0. Alpha=0 pixels are click-through on Windows.
|
||||||
setContentPane(content);
|
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);
|
titleBar = new OACTitleBar(this);
|
||||||
|
|
||||||
OACPanel titleRoot = new OACPanel(new BorderLayout());
|
final OACPanel titleRoot = new OACPanel(new BorderLayout());
|
||||||
titleRoot.setOpaque(false);
|
titleRoot.setOpaque(false);
|
||||||
titleRoot.add(titleBar, BorderLayout.CENTER);
|
titleRoot.add(titleBar, BorderLayout.CENTER);
|
||||||
|
|
||||||
@@ -87,42 +82,97 @@ public class OACFrame extends JFrame {
|
|||||||
@Override
|
@Override
|
||||||
public void componentResized(ComponentEvent e) {
|
public void componentResized(ComponentEvent e) {
|
||||||
titleRoot.setBounds(0, 0, getWidth(), TITLE_BAR_HEIGHT);
|
titleRoot.setBounds(0, 0, getWidth(), TITLE_BAR_HEIGHT);
|
||||||
|
updateWindowChrome();
|
||||||
setShape(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), 30, 30));
|
roundedRoot.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void componentShown(ComponentEvent e) {
|
public void componentShown(ComponentEvent e) {
|
||||||
titleRoot.setBounds(0, 0, getWidth(), TITLE_BAR_HEIGHT);
|
titleRoot.setBounds(0, 0, getWidth(), TITLE_BAR_HEIGHT);
|
||||||
|
updateWindowChrome();
|
||||||
setShape(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), 30, 30));
|
roundedRoot.repaint();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addWindowStateListener(e -> updateWindowChrome());
|
||||||
|
|
||||||
setSize(900, 600);
|
setSize(900, 600);
|
||||||
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
titleRoot.setBounds(0, 0, getWidth(), TITLE_BAR_HEIGHT);
|
titleRoot.setBounds(0, 0, getWidth(), TITLE_BAR_HEIGHT);
|
||||||
setShape(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), 30, 30));
|
roundedRoot.repaint();
|
||||||
});
|
});
|
||||||
|
|
||||||
MouseAdapter adapter = new MouseAdapter() {
|
installResizeHandling();
|
||||||
|
|
||||||
|
DesignManager.apply(this);
|
||||||
|
updateWindowChrome();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseMoved(MouseEvent e) {
|
protected OACRootPane createRootPane() {
|
||||||
resizeCursor = getResizeCursor(e);
|
OACRootPane rp = new OACRootPane();
|
||||||
|
rp.setOpaque(false);
|
||||||
|
return rp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OACRootPane getRootPane() {
|
||||||
|
return (OACRootPane) rootPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OACLayeredPane getLayeredPane() {
|
||||||
|
return (OACLayeredPane) super.getLayeredPane();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLayeredPane(JLayeredPane layeredPane) {
|
||||||
|
if (layeredPane instanceof OACLayeredPane) {
|
||||||
|
super.setLayeredPane(layeredPane);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLayeredPane(OACLayeredPane layeredPane) {
|
||||||
|
setLayeredPane((JLayeredPane) layeredPane);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the title bar.
|
||||||
|
*
|
||||||
|
* @return title bar
|
||||||
|
*/
|
||||||
|
public OACTitleBar getTitleBar() {
|
||||||
|
return titleBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installResizeHandling() {
|
||||||
|
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));
|
setCursor(Cursor.getPredefinedCursor(resizeCursor));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed(MouseEvent e) {
|
public void mousePressed(MouseEvent e) {
|
||||||
|
if (isInFullscreenState()) {
|
||||||
|
dragStart = null;
|
||||||
|
startBounds = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
dragStart = e.getLocationOnScreen();
|
dragStart = e.getLocationOnScreen();
|
||||||
startBounds = getBounds();
|
startBounds = getBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseDragged(MouseEvent e) {
|
public void mouseDragged(MouseEvent e) {
|
||||||
|
if (isInFullscreenState()) return;
|
||||||
if (resizeCursor == Cursor.DEFAULT_CURSOR) return;
|
if (resizeCursor == Cursor.DEFAULT_CURSOR) return;
|
||||||
|
|
||||||
Point dragNow = e.getLocationOnScreen();
|
Point dragNow = e.getLocationOnScreen();
|
||||||
@@ -132,20 +182,15 @@ public class OACFrame extends JFrame {
|
|||||||
Rectangle newBounds = new Rectangle(startBounds);
|
Rectangle newBounds = new Rectangle(startBounds);
|
||||||
|
|
||||||
switch (resizeCursor) {
|
switch (resizeCursor) {
|
||||||
case Cursor.E_RESIZE_CURSOR -> newBounds.width += dx;
|
case Cursor.SW_RESIZE_CURSOR -> {
|
||||||
case Cursor.S_RESIZE_CURSOR -> newBounds.height += dy;
|
newBounds.x += dx;
|
||||||
|
newBounds.width -= dx;
|
||||||
|
newBounds.height += dy;
|
||||||
|
}
|
||||||
case Cursor.SE_RESIZE_CURSOR -> {
|
case Cursor.SE_RESIZE_CURSOR -> {
|
||||||
newBounds.width += dx;
|
newBounds.width += dx;
|
||||||
newBounds.height += dy;
|
newBounds.height += dy;
|
||||||
}
|
}
|
||||||
case Cursor.W_RESIZE_CURSOR -> {
|
|
||||||
newBounds.x += dx;
|
|
||||||
newBounds.width -= dx;
|
|
||||||
}
|
|
||||||
case Cursor.N_RESIZE_CURSOR -> {
|
|
||||||
newBounds.y += dy;
|
|
||||||
newBounds.height -= dy;
|
|
||||||
}
|
|
||||||
case Cursor.NW_RESIZE_CURSOR -> {
|
case Cursor.NW_RESIZE_CURSOR -> {
|
||||||
newBounds.x += dx;
|
newBounds.x += dx;
|
||||||
newBounds.y += dy;
|
newBounds.y += dy;
|
||||||
@@ -157,101 +202,34 @@ public class OACFrame extends JFrame {
|
|||||||
newBounds.width += dx;
|
newBounds.width += dx;
|
||||||
newBounds.height -= dy;
|
newBounds.height -= dy;
|
||||||
}
|
}
|
||||||
case Cursor.SW_RESIZE_CURSOR -> {
|
case Cursor.N_RESIZE_CURSOR -> {
|
||||||
|
newBounds.y += dy;
|
||||||
|
newBounds.height -= dy;
|
||||||
|
}
|
||||||
|
case Cursor.S_RESIZE_CURSOR -> newBounds.height += dy;
|
||||||
|
case Cursor.W_RESIZE_CURSOR -> {
|
||||||
newBounds.x += dx;
|
newBounds.x += dx;
|
||||||
newBounds.width -= dx;
|
newBounds.width -= dx;
|
||||||
newBounds.height += dy;
|
|
||||||
}
|
}
|
||||||
|
case Cursor.E_RESIZE_CURSOR -> newBounds.width += dx;
|
||||||
}
|
}
|
||||||
|
|
||||||
setBounds(newBounds);
|
setBounds(newBounds);
|
||||||
|
e.getComponent().dispatchEvent(
|
||||||
e.getComponent().dispatchEvent(new ComponentEvent(e.getComponent(), ComponentEvent.COMPONENT_RESIZED));
|
new ComponentEvent(e.getComponent(), ComponentEvent.COMPONENT_RESIZED)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
addMouseListener(adapter);
|
addMouseListener(adapter);
|
||||||
addMouseMotionListener(adapter);
|
addMouseMotionListener(adapter);
|
||||||
|
|
||||||
DesignManager.apply(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a component into the content area (center) by default.
|
|
||||||
*
|
|
||||||
* @param comp component
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Component add(Component comp) {
|
|
||||||
initIfOACComponent(comp);
|
|
||||||
return super.add(comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component add(Component comp, int index) {
|
|
||||||
initIfOACComponent(comp);
|
|
||||||
return super.add(comp, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void add(@NonNull Component comp, Object constraints) {
|
|
||||||
initIfOACComponent(comp);
|
|
||||||
super.add(comp, constraints);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component add(String name, Component comp) {
|
|
||||||
initIfOACComponent(comp);
|
|
||||||
return super.add(name, comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void add(Component comp, Object constraints, int index) {
|
|
||||||
initIfOACComponent(comp);
|
|
||||||
super.add(comp, constraints, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initIfOACComponent(Component comp) {
|
|
||||||
if (comp instanceof OACComponent component) {
|
|
||||||
component.initDesign();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected OACRootPane createRootPane() {
|
|
||||||
OACRootPane rp = new OACRootPane();
|
|
||||||
rp.setOpaque(true);
|
|
||||||
return rp;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OACRootPane getRootPane() {
|
|
||||||
return (OACRootPane) this.rootPane;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OACLayeredPane getLayeredPane() {
|
|
||||||
return (OACLayeredPane) super.getLayeredPane();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLayeredPane(JLayeredPane layeredPane) {
|
|
||||||
if (layeredPane instanceof OACLayeredPane)
|
|
||||||
super.setLayeredPane(layeredPane);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLayeredPane(OACLayeredPane layeredPane) {
|
|
||||||
setLayeredPane((JLayeredPane) layeredPane);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the resize cursor for the given edge / corner of this frame.
|
|
||||||
* Required, since undecorated Frames cannot be resized by default
|
|
||||||
*
|
|
||||||
* @param e event passed by mouse adapter
|
|
||||||
* @return id of detected resize cursor
|
|
||||||
*/
|
|
||||||
private int getResizeCursor(MouseEvent e) {
|
private int getResizeCursor(MouseEvent e) {
|
||||||
|
if (isInFullscreenState()) {
|
||||||
|
return Cursor.DEFAULT_CURSOR;
|
||||||
|
}
|
||||||
|
|
||||||
int x = e.getX();
|
int x = e.getX();
|
||||||
int y = e.getY();
|
int y = e.getY();
|
||||||
int w = e.getComponent().getWidth();
|
int w = e.getComponent().getWidth();
|
||||||
@@ -273,4 +251,88 @@ public class OACFrame extends JFrame {
|
|||||||
|
|
||||||
return Cursor.DEFAULT_CURSOR;
|
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 int arc;
|
||||||
|
|
||||||
|
private RoundedRootPanel(int arc) {
|
||||||
|
super(new BorderLayout());
|
||||||
|
this.arc = Math.max(0, arc);
|
||||||
|
setOpaque(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setArc(int arc) {
|
||||||
|
this.arc = Math.max(0, arc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
try {
|
||||||
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
|
int w = getWidth();
|
||||||
|
int h = getHeight();
|
||||||
|
if (w <= 0 || h <= 0) return;
|
||||||
|
|
||||||
|
// 1) Hit-test filler: alpha MUST be > 0 everywhere, otherwise clicks go through.
|
||||||
|
g2.setComposite(AlphaComposite.Src);
|
||||||
|
g2.setColor(new Color(0, 0, 0, 1));
|
||||||
|
g2.fillRect(0, 0, w, h);
|
||||||
|
|
||||||
|
// 2) Rounded background.
|
||||||
|
Shape rr = new RoundRectangle2D.Double(0, 0, w, h, arc, arc);
|
||||||
|
|
||||||
|
Color bg = getBackground();
|
||||||
|
if (bg == null) bg = new Color(0, 0, 0);
|
||||||
|
// Force opaque fill for the visible area.
|
||||||
|
Color opaqueBg = new Color(bg.getRed(), bg.getGreen(), bg.getBlue(), 255);
|
||||||
|
|
||||||
|
g2.setColor(opaqueBg);
|
||||||
|
g2.fill(rr);
|
||||||
|
} finally {
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintChildren(Graphics g) {
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
try {
|
||||||
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
|
int w = getWidth();
|
||||||
|
int h = getHeight();
|
||||||
|
if (w <= 0 || h <= 0) return;
|
||||||
|
|
||||||
|
Shape clip = new RoundRectangle2D.Double(0, 0, w, h, arc, arc);
|
||||||
|
g2.clip(clip);
|
||||||
|
|
||||||
|
super.paintChildren(g2);
|
||||||
|
} finally {
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -5,9 +5,14 @@
|
|||||||
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.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class OACOptionPane extends JOptionPane implements OACComponent {
|
public class OACOptionPane extends JOptionPane implements OACComponent {
|
||||||
public OACOptionPane() {
|
public OACOptionPane() {
|
||||||
@@ -38,6 +43,219 @@ public class OACOptionPane extends JOptionPane implements OACComponent {
|
|||||||
super(message, messageType, optionType, icon, options, initialValue);
|
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), null, 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
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 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(0, 12, 8, 12));
|
||||||
|
header.setBackground(background);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message instanceof Component messageComponent) {
|
||||||
|
if (messageComponent instanceof OACComponent oacComponent) {
|
||||||
|
oacComponent.initDesign();
|
||||||
|
} else {
|
||||||
|
messageComponent.setBackground(background);
|
||||||
|
messageComponent.setForeground(foreground);
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|||||||
@@ -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 javax.swing.text.Document;
|
import javax.swing.text.Document;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
@@ -31,6 +33,39 @@ public class OACPasswordField extends JPasswordField implements OACComponent {
|
|||||||
super(doc, txt, columns);
|
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
|
@Override
|
||||||
public Component add(Component comp) {
|
public Component add(Component comp) {
|
||||||
this.initOther(comp);
|
this.initOther(comp);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 javax.swing.text.Document;
|
import javax.swing.text.Document;
|
||||||
@@ -35,6 +36,29 @@ public class OACTextArea extends JTextArea implements OACComponent {
|
|||||||
super(doc, text, rows, columns);
|
super(doc, text, rows, columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
applyDesignColors();
|
||||||
|
setOpaque(true);
|
||||||
|
setMargin(new Insets(6, 10, 6, 10));
|
||||||
|
setBorder(DesignManager.createTextComponentBorder());
|
||||||
|
setCaretColor(getForeground());
|
||||||
|
setSelectionColor(getForeground().darker());
|
||||||
|
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);
|
||||||
|
|||||||
@@ -4,7 +4,12 @@
|
|||||||
|
|
||||||
package org.openautonomousconnection.oacswing.component;
|
package org.openautonomousconnection.oacswing.component;
|
||||||
|
|
||||||
|
import org.openautonomousconnection.oacswing.border.RoundedBorder;
|
||||||
|
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.CompoundBorder;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
import javax.swing.text.Document;
|
import javax.swing.text.Document;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
@@ -29,6 +34,36 @@ public class OACTextField extends JTextField implements OACComponent {
|
|||||||
super(doc, text, columns);
|
super(doc, text, columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
applyDesignColors();
|
||||||
|
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();
|
||||||
|
applyDesignColors();
|
||||||
|
applyInputBorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyDesignColors() {
|
||||||
|
setBackground(DesignManager.resolveBackground(OACTextField.class, getBackground()));
|
||||||
|
setForeground(DesignManager.resolveForeground(OACTextField.class, getForeground()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyInputBorder() {
|
||||||
|
setBorder(new CompoundBorder(
|
||||||
|
new RoundedBorder(DesignManager.resolveBorderColor(getForeground()), 10, 2),
|
||||||
|
new EmptyBorder(6, 10, 6, 10)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component add(Component comp) {
|
public Component add(Component comp) {
|
||||||
this.initOther(comp);
|
this.initOther(comp);
|
||||||
|
|||||||
@@ -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 javax.swing.text.StyledDocument;
|
import javax.swing.text.StyledDocument;
|
||||||
@@ -18,6 +19,16 @@ public class OACTextPane extends JTextPane implements OACComponent {
|
|||||||
super(doc);
|
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
|
@Override
|
||||||
public Component add(Component comp) {
|
public Component add(Component comp) {
|
||||||
this.initOther(comp);
|
this.initOther(comp);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package org.openautonomousconnection.oacswing.component;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
@@ -39,6 +40,9 @@ public class OACTitleBar extends OACPanel {
|
|||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
this.frame = frame;
|
this.frame = frame;
|
||||||
|
|
||||||
|
setOpaque(true);
|
||||||
|
setBackground(DesignManager.resolveBackground(OACTitleBar.class, getBackground()));
|
||||||
|
setForeground(DesignManager.resolveForeground(OACTitleBar.class, getForeground()));
|
||||||
setPreferredSize(new Dimension(1, HEIGHT));
|
setPreferredSize(new Dimension(1, HEIGHT));
|
||||||
setBorder(new EmptyBorder(6, 10, 6, 10));
|
setBorder(new EmptyBorder(6, 10, 6, 10));
|
||||||
|
|
||||||
@@ -109,6 +113,7 @@ public class OACTitleBar extends OACPanel {
|
|||||||
b.setBorderPainted(false);
|
b.setBorderPainted(false);
|
||||||
b.setContentAreaFilled(true);
|
b.setContentAreaFilled(true);
|
||||||
b.setOpaque(true);
|
b.setOpaque(true);
|
||||||
|
b.setBorder(null);
|
||||||
b.setPreferredSize(new Dimension(42, 28));
|
b.setPreferredSize(new Dimension(42, 28));
|
||||||
|
|
||||||
return b;
|
return b;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -10,38 +10,66 @@ import org.openautonomousconnection.oacswing.border.RoundedBorder;
|
|||||||
import org.openautonomousconnection.oacswing.component.*;
|
import org.openautonomousconnection.oacswing.component.*;
|
||||||
|
|
||||||
import javax.swing.*;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class DesignManager {
|
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
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private static Design globalDesign;
|
private static Design globalDesign;
|
||||||
private static DesignManager instance;
|
private static DesignManager instance;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Design.DARK.getElements().put(OACButton.class, new DesignFlags(OACColor.DARK_BUTTON, OACColor.DARK_TEXT, OACColor.DARK_BUTTON_HOVER, OACColor.DARK_BUTTON_HOVER, OACColor.DARK_INACTIVE_BUTTON, true));
|
Design.DARK.getElements().put(OACButton.class, new DesignFlags(
|
||||||
Design.DARK.getElements().put(OACCheckBox.class, new DesignFlags(OACColor.DARK_INPUT_FIELD));
|
OACColor.DARK_INPUT_BUTTON, // background
|
||||||
Design.DARK.getElements().put(OACCheckBoxMenuItem.class, new DesignFlags(OACColor.DARK_ITEM));
|
OACColor.DARK_TEXT, // foreground
|
||||||
Design.DARK.getElements().put(OACColorChooser.class, new DesignFlags(OACColor.DARK_SECTION));
|
OACColor.DARK_INPUT_BUTTON_HOVER,
|
||||||
Design.DARK.getElements().put(OACComboBox.class, new DesignFlags(OACColor.DARK_INPUT_FIELD));
|
OACColor.DARK_INPUT_BUTTON_HOVER,
|
||||||
Design.DARK.getElements().put(OACFrame.class, new DesignFlags(OACColor.DARK_BACKGROUND));
|
OACColor.DARK_INACTIVE_BUTTON,
|
||||||
Design.DARK.getElements().put(OACLabel.class, new DesignFlags(OACColor.DARK_TEXT));
|
false
|
||||||
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(OACMenu.class, new DesignFlags(OACColor.DARK_INPUT_BUTTON));
|
|
||||||
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(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.border = new RoundedBorder(OACColor.DARK_BORDERS.getColor(), 8, 1);
|
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_INPUT_BUTTON, 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, 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(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(OACList.class, new DesignFlags(OACColor.DARK_SECTION, OACColor.DARK_TEXT));
|
||||||
|
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, OACColor.DARK_TEXT));
|
||||||
|
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(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, OACColor.DARK_TEXT));
|
||||||
|
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, OACColor.DARK_TEXT));
|
||||||
|
Design.DARK.getElements().put(OACScrollPane.class, new DesignFlags(OACColor.DARK_SECTION, OACColor.DARK_TEXT));
|
||||||
|
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);
|
||||||
|
globalDesign = Design.DARK;
|
||||||
|
|
||||||
DesignManager.getInstance().registerDesign(Design.DARK);
|
DesignManager.getInstance().registerDesign(Design.DARK);
|
||||||
}
|
}
|
||||||
@@ -63,7 +91,7 @@ public class DesignManager {
|
|||||||
if (globalDesign == null)
|
if (globalDesign == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DesignFlags designFlags = globalDesign.getElements().get(component.getClass());
|
DesignFlags designFlags = resolveFlags(component.getClass());
|
||||||
|
|
||||||
if (designFlags == null)
|
if (designFlags == null)
|
||||||
return;
|
return;
|
||||||
@@ -86,22 +114,106 @@ public class DesignManager {
|
|||||||
if (disabled != null) pressable.setDisabledColor(disabled.getColor());
|
if (disabled != null) pressable.setDisabledColor(disabled.getColor());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component instanceof JComponent jComponent) {
|
if (component instanceof Component awtComponent) {
|
||||||
jComponent.setBackground(backgroundColour.getColor());
|
awtComponent.setBackground(backgroundColour.getColor());
|
||||||
|
|
||||||
if (foregroundColour != null) {
|
if (foregroundColour != null) {
|
||||||
jComponent.setForeground(foregroundColour.getColor());
|
awtComponent.setForeground(foregroundColour.getColor());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (component instanceof JComponent jComponent) {
|
||||||
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)
|
||||||
|
&& !(jComponent instanceof JViewport)
|
||||||
|
&& !(jComponent instanceof JScrollPane)
|
||||||
|
&& !(jComponent instanceof JScrollBar)
|
||||||
|
&& !(jComponent instanceof JTabbedPane)) {
|
||||||
|
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();
|
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) {
|
public static void apply(OACFrame frame) {
|
||||||
DesignFlags designFlags;
|
DesignFlags designFlags;
|
||||||
|
|
||||||
@@ -112,8 +224,50 @@ public class DesignManager {
|
|||||||
frame.getContentPane().setBackground(designFlags.background().getColor());
|
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) {
|
public void registerDesign(Design design) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class AnimationTests {
|
|||||||
|
|
||||||
OACFrame frame = TestUtils.mockOacFrame();
|
OACFrame frame = TestUtils.mockOacFrame();
|
||||||
|
|
||||||
AnimationPath animationPath = new AnimationPath(50);
|
AnimationPath animationPath = new AnimationPath(100);
|
||||||
|
|
||||||
// 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,8 +30,8 @@ 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, 600), 400, 400, KeyFrame.PathMethod.EASE_OUT));
|
||||||
animationPath.add(new KeyFrame(new Point(400, 300), 400, 400));
|
animationPath.add(new KeyFrame(new Point(400, 100), 400, 400, KeyFrame.PathMethod.LINEAR));
|
||||||
|
|
||||||
JAnimatedPanel animatedPanel = new JAnimatedPanel(animationPath);
|
JAnimatedPanel animatedPanel = new JAnimatedPanel(animationPath);
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ public class AnimationTests {
|
|||||||
|
|
||||||
frame.add(animatedPanel);
|
frame.add(animatedPanel);
|
||||||
|
|
||||||
animatedPanel.play(5, true);
|
animatedPanel.play(10, true);
|
||||||
|
|
||||||
frame.setVisible(true);
|
frame.setVisible(true);
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
package org.openautonomousconnection.oacswing.test;
|
package org.openautonomousconnection.oacswing.test;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.openautonomousconnection.oacswing.component.OACButton;
|
import org.openautonomousconnection.oacswing.component.*;
|
||||||
import org.openautonomousconnection.oacswing.component.OACFrame;
|
|
||||||
import org.openautonomousconnection.oacswing.component.design.Design;
|
import org.openautonomousconnection.oacswing.component.design.Design;
|
||||||
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
public class CustomizedTests {
|
public class CustomizedTests {
|
||||||
@@ -18,15 +18,28 @@ public class CustomizedTests {
|
|||||||
DesignManager designManager = DesignManager.getInstance();
|
DesignManager designManager = DesignManager.getInstance();
|
||||||
|
|
||||||
DesignManager.setGlobalDesign(Design.DARK);
|
DesignManager.setGlobalDesign(Design.DARK);
|
||||||
|
OACPanel navBar = new OACPanel(new BorderLayout(8, 0));
|
||||||
OACFrame frame = TestUtils.mockOacFrame();
|
OACFrame frame = TestUtils.mockOacFrame();
|
||||||
|
OACTextField textField = new OACTextField();
|
||||||
|
|
||||||
frame.setLayout(new FlowLayout());
|
textField.setText("Hello");
|
||||||
|
textField.setToolTipText("test");
|
||||||
frame.add(new OACButton());
|
|
||||||
|
|
||||||
|
navBar.add(textField, BorderLayout.CENTER);
|
||||||
|
frame.getContentPane().add(navBar, BorderLayout.NORTH);
|
||||||
|
frame.add(navBar);
|
||||||
frame.setVisible(true);
|
frame.setVisible(true);
|
||||||
|
Object[] options = {"Continue", "Cancel"};
|
||||||
wait(15000);
|
// OACOptionPane.showOptionDialog(
|
||||||
|
// frame,
|
||||||
|
// "You never connected to this INS before!\n" +
|
||||||
|
// "Fingerprint: " + "caFingerprint" + "\nDo you want to connect?",
|
||||||
|
// "INS Connection",
|
||||||
|
// OACOptionPane.YES_NO_OPTION,
|
||||||
|
// OACOptionPane.INFORMATION_MESSAGE,
|
||||||
|
// null,
|
||||||
|
// options,
|
||||||
|
// options[0]
|
||||||
|
// ); wait(15000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user