WIP but usable

This commit is contained in:
UnlegitDqrk
2026-02-11 23:28:02 +01:00
parent a268926d0b
commit 197e8510a2
29 changed files with 251 additions and 1424 deletions

View File

@@ -8,12 +8,14 @@ import javax.swing.*;
import java.util.concurrent.atomic.AtomicInteger;
public interface AnimatedComponent {
void setCurrentRun(Timer timer);
Timer getCurrentRun();
void setAnimationPath(AnimationPath animationPath);
void setCurrentRun(Timer timer);
AnimationPath getAnimationPath();
void setAnimationPath(AnimationPath animationPath);
void setBounds(int x, int y, int width, int height);
default void play(double speed, boolean loop) {
@@ -21,15 +23,15 @@ public interface AnimatedComponent {
this.setCurrentRun(new Timer(0, e -> {
if(ticksPassed.get() * speed / (100) < 1) {
if (ticksPassed.get() * speed / (100) < 1) {
ticksPassed.addAndGet(this.getAnimationPath().getInbetweens());
return;
}
KeyFrame next = this.getAnimationPath().getNext();
if(next == null) {
if(loop)
if (next == null) {
if (loop)
this.getAnimationPath().reset();
else
((Timer) e.getSource()).stop();
@@ -53,8 +55,8 @@ public interface AnimatedComponent {
}
default void stop() {
if(this.getCurrentRun() != null)
if(this.getCurrentRun().isRunning())
if (this.getCurrentRun() != null)
if (this.getCurrentRun().isRunning())
this.getCurrentRun().stop();
this.getAnimationPath().reset();

View File

@@ -13,7 +13,8 @@ import java.util.Arrays;
import java.util.Collection;
public class AnimationPath extends ArrayList<KeyFrame> {
@Getter @Setter
@Getter
@Setter
private int inbetweens;
private int animationIterator = 0;
@@ -34,57 +35,10 @@ public class AnimationPath extends ArrayList<KeyFrame> {
this.inbetweens = inbetweens;
}
/**
* Get next keyframe according to current path iterator, depending on the current frame
* @return next keyframe in order, depending on the current frame
*/
public KeyFrame getNext() {
if(this.subFrameIterator >= this.inbetweens) {
this.subFrameIterator = 0;
this.animationIterator++;
}
if(this.animationIterator >= this.size())
return null;
this.subFrameIterator++;
KeyFrame current = this.get(this.animationIterator);
KeyFrame next = this.getNextInOrder();
// How far the transition should be finished
double transition = (double) this.subFrameIterator / this.inbetweens;
return inBetween(current, next, transition);
}
/**
* Get next keyframe according to current path iterator, not depending on the current frame
* @return next keyframe in order, not depending on the current frame
*/
public KeyFrame getNextInOrder() {
int i = this.animationIterator + 1;
if(i >= this.size())
i--;
return this.get(i);
}
/**
* Reset animation path iterator to start
*/
public void reset() {
this.subFrameIterator = 0;
this.animationIterator = 0;
}
/**
* Utility method needed to get in-betweens
* @param point point to multiply
*
* @param point point to multiply
* @param scalar scalar to multiply with
* @return scalar product point
*/
@@ -95,10 +49,10 @@ public class AnimationPath extends ArrayList<KeyFrame> {
return new Point(x, y);
}
/**
* Add two points together; also needed for in-betweens
* @param point augend
*
* @param point augend
* @param addend addend
* @return sum of both points
*/
@@ -108,7 +62,8 @@ public class AnimationPath extends ArrayList<KeyFrame> {
/**
* Subtracts one point from another; also needed for in-betweens
* @param point minuend
*
* @param point minuend
* @param subtrahend subtrahend
* @return sum of both points
*/
@@ -118,8 +73,9 @@ public class AnimationPath extends ArrayList<KeyFrame> {
/**
* Find in-between with given scalar
* @param kf1 first frame
* @param kf2 next frame
*
* @param kf1 first frame
* @param kf2 next frame
* @param scalar factor (ideally between 0 and 1, representing 0% transition to 100% transition)
* @return in-between frame
*/
@@ -143,4 +99,53 @@ public class AnimationPath extends ArrayList<KeyFrame> {
return new KeyFrame(pos, width, height);
}
/**
* Get next keyframe according to current path iterator, depending on the current frame
*
* @return next keyframe in order, depending on the current frame
*/
public KeyFrame getNext() {
if (this.subFrameIterator >= this.inbetweens) {
this.subFrameIterator = 0;
this.animationIterator++;
}
if (this.animationIterator >= this.size())
return null;
this.subFrameIterator++;
KeyFrame current = this.get(this.animationIterator);
KeyFrame next = this.getNextInOrder();
// How far the transition should be finished
double transition = (double) this.subFrameIterator / this.inbetweens;
return inBetween(current, next, transition);
}
/**
* Get next keyframe according to current path iterator, not depending on the current frame
*
* @return next keyframe in order, not depending on the current frame
*/
public KeyFrame getNextInOrder() {
int i = this.animationIterator + 1;
if (i >= this.size())
i--;
return this.get(i);
}
/**
* Reset animation path iterator to start
*/
public void reset() {
this.subFrameIterator = 0;
this.animationIterator = 0;
}
}

View File

@@ -10,10 +10,12 @@ import lombok.Setter;
import javax.swing.*;
public class JAnimatedPanel extends JPanel implements AnimatedComponent {
@Getter @Setter
@Getter
@Setter
private AnimationPath animationPath;
@Getter @Setter
@Getter
@Setter
private Timer currentRun = null;
public JAnimatedPanel(AnimationPath animationPath) {

View File

@@ -16,7 +16,8 @@ public class OACAnimatedPanel extends OACPanel implements AnimatedComponent {
@Setter
private AnimationPath animationPath;
@Getter @Setter
@Getter
@Setter
private Timer currentRun = null;
public OACAnimatedPanel(AnimationPath animationPath) {

View File

@@ -4,15 +4,10 @@ import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import org.openautonomousconnection.oacswing.border.RoundedBorder;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import org.openautonomousconnection.oacswing.component.design.OACColor;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.plaf.basic.BasicButtonUI;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
@@ -23,27 +18,23 @@ import java.beans.ConstructorProperties;
*/
public class OACButton extends JButton implements OACPressable {
@Getter @Setter
@Getter
@Setter
private Color pressedColor;
@Getter @Setter
@Getter
@Setter
private Color hoveredColor;
@Getter @Setter
@Getter
@Setter
private Color disabledColor;
@Setter
private int cornerRadius = 10;
private Shape shape;
private int getCornerRadius() {
Border b = getBorder();
if (b instanceof RoundedBorder rb) {
return rb.getRadius();
}
return cornerRadius; // Fallback
}
private Color original;
public OACButton() {
@@ -67,7 +58,13 @@ public class OACButton extends JButton implements OACPressable {
super(text, icon);
}
private Color original;
private int getCornerRadius() {
Border b = getBorder();
if (b instanceof RoundedBorder rb) {
return rb.getRadius();
}
return cornerRadius; // Fallback
}
@Override
public void setBackground(Color bg) {
@@ -119,6 +116,7 @@ public class OACButton extends JButton implements OACPressable {
}
});
}
@Override
public boolean contains(int x, int y) {
int r = getCornerRadius();

View File

@@ -1,7 +1,6 @@
package org.openautonomousconnection.oacswing.component;
import jdk.jshell.spi.ExecutionControl;
import org.openautonomousconnection.oacswing.component.design.Design;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*;
@@ -18,32 +17,32 @@ public interface OACComponent {
// this.setBackground(design.getElements().get(this.getClass()).getColor());
}
default void init() {}
default void init() {
}
default void initOther(Component comp) {
if(comp instanceof OACComponent component)
if (comp instanceof OACComponent component)
component.initDesign();
}
default Component add(Optional<String> name, Component comp, OptionalInt index, Optional<?> constrains) throws ExecutionControl.NotImplementedException {
JComponent superclass = this.getSuperclass();
if(comp instanceof OACComponent component)
if (comp instanceof OACComponent component)
component.initDesign();
if(name.isPresent())
if (name.isPresent())
return superclass.add(name.get(), comp);
else if(constrains.isPresent())
if(index.isPresent()) {
else if (constrains.isPresent())
if (index.isPresent()) {
superclass.add(comp, constrains.get(), index.getAsInt());
return null;
}
else {
} else {
superclass.add(comp, constrains.get());
return null;
}
else if(index.isPresent())
else if (index.isPresent())
return superclass.add(comp, index.getAsInt());
else
@@ -92,11 +91,12 @@ public interface OACComponent {
/**
* Needed to run JComponent / Component methods
*
* @return JComponent type superclass
* @throws ExecutionControl.NotImplementedException superclass is not of type JComponent
*/
default JComponent getSuperclass() throws ExecutionControl.NotImplementedException {
if(this instanceof JComponent superClass)
if (this instanceof JComponent superClass)
return superClass;
else
throw new ExecutionControl.NotImplementedException("Trying to implement OACComponent interface for non-JComponent class");

View File

@@ -2,7 +2,6 @@ package org.openautonomousconnection.oacswing.component;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*;
@@ -18,7 +17,7 @@ import java.awt.geom.RoundRectangle2D;
*/
public class OACFrame extends JFrame {
private static final int RESIZE_MARGIN = 8;
private static final int TITLE_BAR_HEIGHT = 42;
private Point dragStart;
private Rectangle startBounds;
private int resizeCursor = Cursor.DEFAULT_CURSOR;
@@ -57,13 +56,13 @@ public class OACFrame extends JFrame {
* Creates a new frame with the given title and graphics configuration.
*
* @param title frame title
* @param gc graphics configuration
* @param gc graphics configuration
*/
public OACFrame(String title, GraphicsConfiguration gc) {
super(title, gc);
init();
}
private static final int TITLE_BAR_HEIGHT = 42;
private void init() {
super.setLayeredPane(new OACLayeredPane());
@@ -230,9 +229,14 @@ public class OACFrame extends JFrame {
return (OACRootPane) this.rootPane;
}
@Override
public OACLayeredPane getLayeredPane() {
return (OACLayeredPane) super.getLayeredPane();
}
@Override
public void setLayeredPane(JLayeredPane layeredPane) {
if(layeredPane instanceof OACLayeredPane)
if (layeredPane instanceof OACLayeredPane)
super.setLayeredPane(layeredPane);
}
@@ -240,14 +244,10 @@ public class OACFrame extends JFrame {
setLayeredPane((JLayeredPane) layeredPane);
}
@Override
public OACLayeredPane getLayeredPane() {
return (OACLayeredPane) super.getLayeredPane();
}
/**
* 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
*/
@@ -257,9 +257,9 @@ public class OACFrame extends JFrame {
int w = e.getComponent().getWidth();
int h = e.getComponent().getHeight();
boolean left = x < RESIZE_MARGIN;
boolean right = x > w - RESIZE_MARGIN;
boolean top = y < RESIZE_MARGIN;
boolean left = x < RESIZE_MARGIN;
boolean right = x > w - RESIZE_MARGIN;
boolean top = y < RESIZE_MARGIN;
boolean bottom = y > h - RESIZE_MARGIN;
if (left && top) return Cursor.NW_RESIZE_CURSOR;

View File

@@ -1,17 +1,18 @@
package org.openautonomousconnection.oacswing.component;
import org.openautonomousconnection.oacswing.component.design.OACColor;
import java.awt.*;
public interface OACPressable extends OACComponent {
Color getPressedColor();
void setPressedColor(Color color);
Color getHoveredColor();
void setHoveredColor(Color color);
Color getDisabledColor();
void setDisabledColor(Color color);
}

View File

@@ -17,14 +17,14 @@ public class OACRootPane extends JRootPane implements OACComponent {
@Override
protected OACLayeredPane createLayeredPane() {
OACLayeredPane p = new OACLayeredPane();
p.setName(this.getName()+".layeredPane");
p.setName(this.getName() + ".layeredPane");
return p;
}
@Override
protected Container createContentPane() {
JComponent c = new OACPanel();
c.setName(this.getName()+".contentPane");
c.setName(this.getName() + ".contentPane");
c.setLayout(new BorderLayout() {
/* This BorderLayout subclass maps a null constraint to CENTER.
* Although the reference BorderLayout also does this, some VMs
@@ -47,7 +47,7 @@ public class OACRootPane extends JRootPane implements OACComponent {
@Deprecated
@Override
public JMenuBar getJMenuBar() {
if(this.menuBar instanceof OACMenuBar oacMenuBar)
if (this.menuBar instanceof OACMenuBar oacMenuBar)
return oacMenuBar;
else
return this.menuBar;
@@ -57,21 +57,15 @@ public class OACRootPane extends JRootPane implements OACComponent {
return (OACMenuBar) this.menuBar;
}
@Deprecated
@Override
public void setLayeredPane(JLayeredPane layered) {
if(layered instanceof OACLayeredPane)
super.setLayeredPane(layered);
}
public void setLayeredPane(OACLayeredPane layeredPane) {
super.setLayeredPane(layeredPane);
public OACButton getDefaultButton() {
return (OACButton) this.defaultButton;
}
@Deprecated
@Override
public void setDefaultButton(JButton defaultButton) {
if(defaultButton instanceof OACButton)
if (defaultButton instanceof OACButton)
super.setDefaultButton(defaultButton);
}
@@ -80,18 +74,25 @@ public class OACRootPane extends JRootPane implements OACComponent {
}
@Override
public OACButton getDefaultButton() {
return (OACButton) this.defaultButton;
public OACLayeredPane getLayeredPane() {
return (OACLayeredPane) this.layeredPane;
}
@Deprecated
@Override
public OACLayeredPane getLayeredPane() { return (OACLayeredPane) this.layeredPane; }
public void setLayeredPane(JLayeredPane layered) {
if (layered instanceof OACLayeredPane)
super.setLayeredPane(layered);
}
public void setLayeredPane(OACLayeredPane layeredPane) {
super.setLayeredPane(layeredPane);
}
@Override
protected Component createGlassPane() {
OACPanel c = new OACPanel();
c.setName(this.getName()+".glassPane");
c.setName(this.getName() + ".glassPane");
c.setVisible(false);
c.setOpaque(false);
return c;

View File

@@ -6,7 +6,6 @@ package org.openautonomousconnection.oacswing.component;
import lombok.Getter;
import lombok.Setter;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
@@ -21,7 +20,8 @@ import java.awt.event.WindowEvent;
*/
public class OACTitleBar extends OACPanel {
@Getter @Setter
@Getter
@Setter
private static int HEIGHT = 42;
private final OACFrame frame;

View File

@@ -11,10 +11,12 @@ import javax.swing.*;
import java.awt.*;
public class OACTitledComponent<C extends JComponent & OACComponent> extends OACPanel {
@Getter @Setter
@Getter
@Setter
private OACLabel title;
@Getter @Setter
@Getter
@Setter
private C component;
public OACTitledComponent(String title, C component, int alignment) {

View File

@@ -42,4 +42,5 @@ public class OACToolTip extends JToolTip implements OACComponent {
public void add(Component comp, Object constraints, int index) {
this.initOther(comp);
super.add(comp, constraints, index);
}}
}
}

View File

@@ -42,4 +42,5 @@ public class OACViewport extends JViewport implements OACComponent {
public void add(Component comp, Object constraints, int index) {
this.initOther(comp);
super.add(comp, constraints, index);
}}
}
}

View File

@@ -8,7 +8,6 @@ import lombok.Getter;
import lombok.NonNull;
import javax.swing.border.Border;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

View File

@@ -1,6 +1,7 @@
package org.openautonomousconnection.oacswing.component.design;
public record DesignFlags(OACColor background, OACColor foreground, OACColor pressed, OACColor hovered, OACColor disabled, boolean hasBorder) {
public record DesignFlags(OACColor background, OACColor foreground, OACColor pressed, OACColor hovered,
OACColor disabled, boolean hasBorder) {
public DesignFlags(OACColor background) {
this(background, null, null, null, null, false);
}

View File

@@ -10,108 +10,15 @@ import org.openautonomousconnection.oacswing.border.RoundedBorder;
import org.openautonomousconnection.oacswing.component.*;
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class DesignManager {
@Getter @Setter
private static Design globalDesign;
public static DesignManager getInstance() {
return Objects.requireNonNullElseGet(instance, DesignManager::new);
}
private static DesignManager instance;
private DesignManager() {
instance = this;
this.designs = new ArrayList<>();
}
@Getter
private final List<Design> designs;
public static void apply(OACComponent component) {
if(globalDesign == null)
return;
DesignFlags designFlags = globalDesign.getElements().get(component.getClass());
if(designFlags == null)
return;
OACColor backgroundColour = designFlags.background();
OACColor foregroundColour = designFlags.foreground();
OACColor hovered = designFlags.hovered();
OACColor pressed = designFlags.pressed();
OACColor disabled = designFlags.disabled();
boolean hasBorder = designFlags.hasBorder();
if(backgroundColour == null)
throw new NullPointerException("Background color cannot be null; please exclude the component from your design instead");
if (component instanceof OACPressable pressable) {
if (hovered != null) pressable.setHoveredColor(hovered.getColor());
if (pressed != null) pressable.setPressedColor(pressed.getColor());
if (disabled != null) pressable.setDisabledColor(disabled.getColor());
}
if (component instanceof JComponent jComponent) {
jComponent.setBackground(backgroundColour.getColor());
if (foregroundColour != null) {
jComponent.setForeground(foregroundColour.getColor());
}
if (hasBorder) {
jComponent.setBorder(globalDesign.getBorder());
jComponent.setOpaque(false);
}
component.init();
}
}
public static void apply(OACFrame frame) {
DesignFlags designFlags;
if(globalDesign != null && ((designFlags = globalDesign.getElements().get(frame.getClass())) != null)) {
if(designFlags.hasBorder())
frame.getRootPane().setBorder(globalDesign.border);
frame.getContentPane().setBackground(designFlags.background().getColor());
}
}
public void registerDesign(Design design) {
this.designs.add(design);
}
public void registerComponent(Class<?> componentClass, DesignFlags flags) {
Style.Design eDesign = Style.Design.DARK;
if(componentClass.isAnnotationPresent(Style.class))
eDesign = componentClass.getAnnotation(Style.class).design();
Design design = switch (eDesign) {
case LIGHT -> Design.LIGHT;
case DARK -> Design.DARK;
case CONTRAST -> Design.CONTRAST;
};
design.getElements().putIfAbsent(componentClass, null);
design.getElements().replace(componentClass, flags);
}
@Setter
private static Design globalDesign;
private static DesignManager instance;
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));
@@ -140,4 +47,95 @@ public class DesignManager {
DesignManager.getInstance().registerDesign(Design.DARK);
}
@Getter
private final List<Design> designs;
private DesignManager() {
instance = this;
this.designs = new ArrayList<>();
}
public static DesignManager getInstance() {
return Objects.requireNonNullElseGet(instance, DesignManager::new);
}
public static void apply(OACComponent component) {
if (globalDesign == null)
return;
DesignFlags designFlags = globalDesign.getElements().get(component.getClass());
if (designFlags == null)
return;
OACColor backgroundColour = designFlags.background();
OACColor foregroundColour = designFlags.foreground();
OACColor hovered = designFlags.hovered();
OACColor pressed = designFlags.pressed();
OACColor disabled = designFlags.disabled();
boolean hasBorder = designFlags.hasBorder();
if (backgroundColour == null)
throw new NullPointerException("Background color cannot be null; please exclude the component from your design instead");
if (component instanceof OACPressable pressable) {
if (hovered != null) pressable.setHoveredColor(hovered.getColor());
if (pressed != null) pressable.setPressedColor(pressed.getColor());
if (disabled != null) pressable.setDisabledColor(disabled.getColor());
}
if (component instanceof JComponent jComponent) {
jComponent.setBackground(backgroundColour.getColor());
if (foregroundColour != null) {
jComponent.setForeground(foregroundColour.getColor());
}
if (hasBorder) {
jComponent.setBorder(globalDesign.getBorder());
jComponent.setOpaque(false);
}
component.init();
}
}
public static void apply(OACFrame frame) {
DesignFlags designFlags;
if (globalDesign != null && ((designFlags = globalDesign.getElements().get(frame.getClass())) != null)) {
if (designFlags.hasBorder())
frame.getRootPane().setBorder(globalDesign.border);
frame.getContentPane().setBackground(designFlags.background().getColor());
}
}
public void registerDesign(Design design) {
this.designs.add(design);
}
public void registerComponent(Class<?> componentClass, DesignFlags flags) {
Style.Design eDesign = Style.Design.DARK;
if (componentClass.isAnnotationPresent(Style.class))
eDesign = componentClass.getAnnotation(Style.class).design();
Design design = switch (eDesign) {
case LIGHT -> Design.LIGHT;
case DARK -> Design.DARK;
case CONTRAST -> Design.CONTRAST;
};
design.getElements().putIfAbsent(componentClass, null);
design.getElements().replace(componentClass, flags);
}
}

View File

@@ -12,11 +12,11 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Style {
Design design();
enum Design {
LIGHT,
DARK,
CONTRAST
}
Design design();
}

View File

@@ -24,6 +24,7 @@ public enum IconSize {
@Getter
private final int scale;
IconSize(int scale) {
this.scale = scale;
}

View File

@@ -6,8 +6,6 @@ package org.openautonomousconnection.oacswing.icons.temp;
import org.openautonomousconnection.oacswing.ModifiableImageIcon;
import javax.swing.*;
/**
* Class containing testing / placeholder icons
*/