diff --git a/core/src/main/java/com/github/weisj/darklaf/components/RotatableIconAnimator.java b/core/src/main/java/com/github/weisj/darklaf/components/RotatableIconAnimator.java
new file mode 100644
index 00000000..feeac9a1
--- /dev/null
+++ b/core/src/main/java/com/github/weisj/darklaf/components/RotatableIconAnimator.java
@@ -0,0 +1,69 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Jannis Weis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package com.github.weisj.darklaf.components;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.*;
+
+import com.github.weisj.darklaf.icons.RotatableIcon;
+import com.github.weisj.darklaf.util.Alignment;
+
+public class RotatableIconAnimator extends Timer {
+
+ private final RotatableIcon icon;
+ private final JComponent parent;
+ private final int frameCount;
+ private int frame;
+
+ public RotatableIconAnimator(final RotatableIcon icon, final JComponent parent) {
+ this(Alignment.values().length, icon, parent);
+ }
+
+ public RotatableIconAnimator(final int frames, final RotatableIcon icon, final JComponent parent) {
+ super(100, null);
+ if (icon == null) throw new IllegalArgumentException("Icon is null");
+ if (parent == null) throw new IllegalArgumentException("Component is null");
+ addActionListener(this::onAction);
+ setRepeats(true);
+ this.icon = icon;
+ this.frameCount = frames;
+ this.parent = parent;
+ }
+
+ public void resume() {
+ start();
+ }
+
+ public void onAction(final ActionEvent e) {
+ icon.setRotation(Math.PI * 2 * (((double) frame) / frameCount));
+ parent.repaint();
+ frame = (frame + 1) % frameCount;
+ }
+
+ public void suspend() {
+ stop();
+ }
+}
diff --git a/core/src/main/java/com/github/weisj/darklaf/components/loading/LoadingIndicator.java b/core/src/main/java/com/github/weisj/darklaf/components/loading/LoadingIndicator.java
new file mode 100644
index 00000000..73e83779
--- /dev/null
+++ b/core/src/main/java/com/github/weisj/darklaf/components/loading/LoadingIndicator.java
@@ -0,0 +1,127 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Jannis Weis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package com.github.weisj.darklaf.components.loading;
+
+import javax.swing.*;
+
+import com.github.weisj.darklaf.components.RotatableIconAnimator;
+import com.github.weisj.darklaf.icons.EmptyIcon;
+import com.github.weisj.darklaf.icons.RotatableIcon;
+import com.github.weisj.darklaf.icons.TwoIcon;
+import com.github.weisj.darklaf.util.DarkUIUtil;
+
+/**
+ * Label that functions as an loading indicator.
+ */
+public class LoadingIndicator extends JLabel {
+
+ private final RotatableIcon loadIcon = new RotatableIcon(DarkUIUtil.ICON_LOADER.getIcon("progress/step_working.svg"));
+ private final Icon pausedIcon = DarkUIUtil.ICON_LOADER.getIcon("progress/step_passive.svg");
+ private final Icon emptyIcon = EmptyIcon.create(loadIcon.getIconWidth(), loadIcon.getIconHeight());
+ private final TwoIcon displayIcon = new TwoIcon(loadIcon, null);
+ private final RotatableIconAnimator animator = new RotatableIconAnimator(8, loadIcon, this);
+ private boolean running;
+
+ public LoadingIndicator(final String text, final Icon icon, final int horizontalAlignment) {
+ super(text, icon, horizontalAlignment);
+ displayIcon.setIconGap(getIconTextGap());
+ }
+
+ public LoadingIndicator(final String text, final int horizontalAlignment) {
+ this(text, null, horizontalAlignment);
+ }
+
+ public LoadingIndicator(final String text) {
+ this(text, null, LEADING);
+ }
+
+ public LoadingIndicator(final Icon image, final int horizontalAlignment) {
+ this(null, image, horizontalAlignment);
+ }
+
+ public LoadingIndicator(final Icon image) {
+ this(null, image, CENTER);
+ }
+
+ public LoadingIndicator() {
+ this("", null, LEADING);
+ }
+
+ /**
+ * Sets whether the icon should be animated.
+ *
+ * @param running true if animated.
+ */
+ public void setRunning(final boolean running) {
+ this.running = running;
+ setAnimatorState(running);
+ }
+
+ private void setAnimatorState(final boolean running) {
+ if (running == animator.isRunning()) return;
+ if (running) {
+ animator.resume();
+ } else {
+ animator.suspend();
+ }
+ repaint();
+ }
+
+ @Override
+ public void setEnabled(final boolean enabled) {
+ super.setEnabled(enabled);
+ setAnimatorState(isRunning());
+ }
+
+ /**
+ * Returns whether the loading icon is animated and visible.
+ *
+ * @see #setRunning(boolean)
+ * @return true if animated and visible.
+ */
+ public boolean isRunning() {
+ return running && isEnabled();
+ }
+
+ @Override
+ public void setIconTextGap(final int iconTextGap) {
+ super.setIconTextGap(iconTextGap);
+ displayIcon.setIconGap(getIconTextGap());
+ }
+
+ @Override
+ public Icon getIcon() {
+ displayIcon.setLeftIcon(running ? loadIcon : pausedIcon);
+ displayIcon.setRightIcon(super.getIcon());
+ return displayIcon;
+ }
+
+ @Override
+ public Icon getDisabledIcon() {
+ displayIcon.setLeftIcon(emptyIcon);
+ displayIcon.setRightIcon(super.getDisabledIcon());
+ return displayIcon;
+ }
+}
diff --git a/core/src/main/java/com/github/weisj/darklaf/graphics/Animator.java b/core/src/main/java/com/github/weisj/darklaf/graphics/Animator.java
index e8310c32..a42a8d96 100644
--- a/core/src/main/java/com/github/weisj/darklaf/graphics/Animator.java
+++ b/core/src/main/java/com/github/weisj/darklaf/graphics/Animator.java
@@ -66,18 +66,22 @@ public abstract class Animator {
this.repeatable = repeatable;
this.forward = forward;
currentFrame = forward ? 0 : totalFrames;
-
+ resetTime();
reset();
}
- public void reset() {
- currentFrame = 0;
+ private void resetTime() {
startTime = -1;
}
+ public void reset() {
+ currentFrame %= totalFrames;
+ if (!forward) currentFrame = totalFrames - currentFrame;
+ }
+
private static ScheduledExecutorService createScheduler() {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, r -> {
- final Thread thread = new Thread(r, "Darcula Animations");
+ final Thread thread = new Thread(r, "Animations Thread");
thread.setDaemon(true);
thread.setPriority(Thread.MAX_PRIORITY);
return thread;
@@ -89,7 +93,8 @@ public abstract class Animator {
}
public void suspend() {
- startTime = -1;
+ resetTime();
+ reset();
stopTicker();
}
@@ -119,10 +124,11 @@ public abstract class Animator {
@Override
public void run() {
- if (isScheduled.compareAndSet(false, true) && !isDisposed()) {
+ if (!isScheduled.get() && !isDisposed()) {
+ isScheduled.set(true);
SwingUtilities.invokeLater(() -> {
- isScheduled.set(false);
onTick();
+ isScheduled.set(false);
});
}
}
@@ -136,7 +142,6 @@ public abstract class Animator {
private void animationDone() {
stopTicker();
-
SwingUtilities.invokeLater(this::paintCycleEnd);
}
@@ -145,11 +150,11 @@ public abstract class Animator {
}
private void onTick() {
- if (isDisposed()) return;
+ if (isDisposed() || ticker == null) return;
if (startTime == -1) {
startTime = System.currentTimeMillis();
- stopTime = startTime + cycleDuration * (totalFrames - currentFrame) / totalFrames;
+ stopTime = startTime + (cycleDuration * (totalFrames - currentFrame)) / totalFrames;
}
final double passedTime = System.currentTimeMillis() - startTime;
diff --git a/core/src/main/resources/com/github/weisj/darklaf/icons/progress/pause.svg b/core/src/main/resources/com/github/weisj/darklaf/icons/progress/pause.svg
new file mode 100644
index 00000000..f7db314c
--- /dev/null
+++ b/core/src/main/resources/com/github/weisj/darklaf/icons/progress/pause.svg
@@ -0,0 +1,12 @@
+
diff --git a/core/src/main/resources/com/github/weisj/darklaf/icons/progress/resume.svg b/core/src/main/resources/com/github/weisj/darklaf/icons/progress/resume.svg
new file mode 100644
index 00000000..6713f658
--- /dev/null
+++ b/core/src/main/resources/com/github/weisj/darklaf/icons/progress/resume.svg
@@ -0,0 +1,9 @@
+
diff --git a/core/src/main/resources/com/github/weisj/darklaf/icons/progress/step_passive.svg b/core/src/main/resources/com/github/weisj/darklaf/icons/progress/step_passive.svg
new file mode 100644
index 00000000..a866e34a
--- /dev/null
+++ b/core/src/main/resources/com/github/weisj/darklaf/icons/progress/step_passive.svg
@@ -0,0 +1,18 @@
+
diff --git a/core/src/main/resources/com/github/weisj/darklaf/icons/progress/step_working.svg b/core/src/main/resources/com/github/weisj/darklaf/icons/progress/step_working.svg
new file mode 100644
index 00000000..4bbc2194
--- /dev/null
+++ b/core/src/main/resources/com/github/weisj/darklaf/icons/progress/step_working.svg
@@ -0,0 +1,18 @@
+
diff --git a/core/src/test/java/ui/label/LabelDemo.java b/core/src/test/java/ui/label/LabelDemo.java
index 405c3a27..3862209d 100644
--- a/core/src/test/java/ui/label/LabelDemo.java
+++ b/core/src/test/java/ui/label/LabelDemo.java
@@ -29,36 +29,16 @@ import java.awt.*;
import javax.swing.*;
import ui.ComponentDemo;
-import ui.DemoPanel;
import ui.DemoResources;
-public class LabelDemo implements ComponentDemo {
+public class LabelDemo extends LabelDemoBase {
public static void main(final String[] args) {
ComponentDemo.showDemo(new LabelDemo());
}
- @Override
- public JComponent createComponent() {
- Icon icon = DemoResources.FOLDER_ICON;
- JLabel label = new JLabel("Test Label", icon, JLabel.LEFT);
- DemoPanel panel = new DemoPanel(label);
-
- JPanel controlPanel = panel.addControls();
- controlPanel.add(new JCheckBox("enabled") {
- {
- setSelected(label.isEnabled());
- addActionListener(e -> label.setEnabled(isSelected()));
- }
- });
- controlPanel.add(new JCheckBox("LeftToRight") {
- {
- setSelected(label.getComponentOrientation().isLeftToRight());
- addActionListener(e -> label.setComponentOrientation(isSelected() ? ComponentOrientation.LEFT_TO_RIGHT
- : ComponentOrientation.RIGHT_TO_LEFT));
- }
- });
- return panel;
+ protected JLabel createLabel() {
+ return new JLabel("Test Label", DemoResources.FOLDER_ICON, JLabel.LEFT);
}
@Override
diff --git a/core/src/test/java/ui/label/LabelDemoBase.java b/core/src/test/java/ui/label/LabelDemoBase.java
new file mode 100644
index 00000000..b6cb4544
--- /dev/null
+++ b/core/src/test/java/ui/label/LabelDemoBase.java
@@ -0,0 +1,64 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Jannis Weis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package ui.label;
+
+import java.awt.*;
+
+import javax.swing.*;
+
+import ui.ComponentDemo;
+import ui.DemoPanel;
+
+public abstract class LabelDemoBase implements ComponentDemo {
+
+ @Override
+ public JComponent createComponent() {
+ T label = createLabel();
+ DemoPanel panel = new DemoPanel(label);
+
+ JPanel controlPanel = createControlPanel(panel, label);
+ return panel;
+ }
+
+ protected JPanel createControlPanel(final DemoPanel panel, final T label) {
+ JPanel controlPanel = panel.addControls();
+ controlPanel.add(new JCheckBox("enabled") {
+ {
+ setSelected(label.isEnabled());
+ addActionListener(e -> label.setEnabled(isSelected()));
+ }
+ });
+ controlPanel.add(new JCheckBox("LeftToRight") {
+ {
+ setSelected(label.getComponentOrientation().isLeftToRight());
+ addActionListener(e -> label.setComponentOrientation(isSelected() ? ComponentOrientation.LEFT_TO_RIGHT
+ : ComponentOrientation.RIGHT_TO_LEFT));
+ }
+ });
+ return controlPanel;
+ }
+
+ protected abstract T createLabel();
+}
diff --git a/core/src/test/java/ui/label/LoadIndicator.java b/core/src/test/java/ui/label/LoadIndicator.java
new file mode 100644
index 00000000..3b666a27
--- /dev/null
+++ b/core/src/test/java/ui/label/LoadIndicator.java
@@ -0,0 +1,61 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Jannis Weis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package ui.label;
+
+import javax.swing.*;
+
+import ui.ComponentDemo;
+import ui.DemoPanel;
+import ui.DemoResources;
+
+import com.github.weisj.darklaf.components.loading.LoadingIndicator;
+
+public class LoadIndicator extends LabelDemoBase {
+
+ public static void main(final String[] args) {
+ ComponentDemo.showDemo(new LoadIndicator());
+ }
+
+ @Override
+ protected JPanel createControlPanel(final DemoPanel panel, final LoadingIndicator label) {
+ JPanel controls = super.createControlPanel(panel, label);
+ controls.add(new JCheckBox("running") {
+ {
+ setSelected(label.isRunning());
+ addActionListener(e -> label.setRunning(isSelected()));
+ }
+ });
+ return controls;
+ }
+
+ protected LoadingIndicator createLabel() {
+ return new LoadingIndicator("Load Indicator", DemoResources.FOLDER_ICON, JLabel.LEFT);
+ }
+
+ @Override
+ public String getTitle() {
+ return "Load Indicator Demo";
+ }
+}
diff --git a/property-loader/src/main/java/com/github/weisj/darklaf/icons/DarkSVGIcon.java b/property-loader/src/main/java/com/github/weisj/darklaf/icons/DarkSVGIcon.java
index f8114679..38dec75e 100644
--- a/property-loader/src/main/java/com/github/weisj/darklaf/icons/DarkSVGIcon.java
+++ b/property-loader/src/main/java/com/github/weisj/darklaf/icons/DarkSVGIcon.java
@@ -41,7 +41,7 @@ import com.kitfox.svg.app.beans.SVGIcon;
* @author Jannis Weis
* @since 2019
*/
-public class DarkSVGIcon implements DerivableIcon, Serializable {
+public class DarkSVGIcon implements DerivableIcon, RotateIcon, Serializable {
private static final Logger LOGGER = LogUtil.getLogger(DarkSVGIcon.class);
private final Dimension size;
@@ -99,15 +99,7 @@ public class DarkSVGIcon implements DerivableIcon, Serializable {
return new SVGIcon();
}
- /**
- * Paint the icon with rotation.
- *
- * @param c the parent component.
- * @param g the graphics object.
- * @param x the x coordinate
- * @param y the y coordinate
- * @param rotation the rotation in radians.
- */
+ @Override
public void paintIcon(final Component c, final Graphics g, final int x, final int y,
final double rotation) {
ensureLoaded();
diff --git a/property-loader/src/main/java/com/github/weisj/darklaf/icons/RotatableIcon.java b/property-loader/src/main/java/com/github/weisj/darklaf/icons/RotatableIcon.java
index d4d68fe9..4fdeb24b 100644
--- a/property-loader/src/main/java/com/github/weisj/darklaf/icons/RotatableIcon.java
+++ b/property-loader/src/main/java/com/github/weisj/darklaf/icons/RotatableIcon.java
@@ -51,8 +51,8 @@ public class RotatableIcon implements Icon {
@Override
public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
- if (icon instanceof DarkSVGIcon) {
- ((DarkSVGIcon) icon).paintIcon(c, g, x, y, getAngle());
+ if (icon instanceof RotateIcon) {
+ ((RotateIcon) icon).paintIcon(c, g, x, y, getAngle());
} else if (icon != null) {
Graphics2D g2 = (Graphics2D) g.create();
AffineTransform transform = new AffineTransform();
diff --git a/property-loader/src/main/java/com/github/weisj/darklaf/icons/RotateIcon.java b/property-loader/src/main/java/com/github/weisj/darklaf/icons/RotateIcon.java
new file mode 100644
index 00000000..0352cb65
--- /dev/null
+++ b/property-loader/src/main/java/com/github/weisj/darklaf/icons/RotateIcon.java
@@ -0,0 +1,41 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Jannis Weis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package com.github.weisj.darklaf.icons;
+
+import java.awt.*;
+
+public interface RotateIcon {
+
+ /**
+ * Paint the icon with rotation.
+ *
+ * @param c the parent component.
+ * @param g the graphics object.
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param rotation the rotation in radians.
+ */
+ void paintIcon(Component c, Graphics g, int x, int y, double rotation);
+}
diff --git a/property-loader/src/main/java/com/github/weisj/darklaf/icons/TwoIcon.java b/property-loader/src/main/java/com/github/weisj/darklaf/icons/TwoIcon.java
new file mode 100644
index 00000000..397f1008
--- /dev/null
+++ b/property-loader/src/main/java/com/github/weisj/darklaf/icons/TwoIcon.java
@@ -0,0 +1,92 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Jannis Weis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+package com.github.weisj.darklaf.icons;
+
+import java.awt.*;
+
+import javax.swing.*;
+
+public class TwoIcon implements Icon {
+
+ private int iconGap = 2;
+ private Icon leftIcon;
+ private Icon rightIcon;
+
+ public TwoIcon(final Icon leftIcon, final Icon rightIcon) {
+ this(leftIcon, rightIcon, 2);
+ }
+
+ public TwoIcon(final Icon leftIcon, final Icon rightIcon, final int iconGap) {
+ setLeftIcon(leftIcon);
+ setRightIcon(rightIcon);
+ this.iconGap = iconGap;
+ }
+
+ public void setIconGap(final int iconGap) {
+ this.iconGap = iconGap;
+ }
+
+ public void setLeftIcon(final Icon leftIcon) {
+ this.leftIcon = leftIcon != null ? leftIcon : EmptyIcon.create(0);
+ }
+
+ public void setRightIcon(final Icon rightIcon) {
+ this.rightIcon = rightIcon != null ? rightIcon : EmptyIcon.create(0);
+ }
+
+ public Icon getLeftIcon() {
+ return leftIcon;
+ }
+
+ public Icon getRightIcon() {
+ return rightIcon;
+ }
+
+ @Override
+ public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
+ int mid = getIconHeight() / 2;
+ boolean ltr = c.getComponentOrientation().isLeftToRight();
+ Icon left = ltr ? leftIcon : rightIcon;
+ Icon right = ltr ? rightIcon : leftIcon;
+ int y1 = y + mid - left.getIconHeight() / 2;
+ int y2 = y + mid - right.getIconHeight() / 2;
+ leftIcon.paintIcon(c, g, x, y1);
+ rightIcon.paintIcon(c, g, x + left.getIconWidth() + iconGap, y2);
+ }
+
+ @Override
+ public int getIconWidth() {
+ int l = leftIcon.getIconWidth();
+ int r = rightIcon.getIconWidth();
+ int gap = 0;
+ if (l != 0 && r != 0) gap = iconGap;
+ return l + r + gap;
+ }
+
+ @Override
+ public int getIconHeight() {
+ return Math.max(leftIcon.getIconHeight(), rightIcon.getIconHeight());
+ }
+}
diff --git a/utils/src/main/java/com/github/weisj/darklaf/util/Alignment.java b/utils/src/main/java/com/github/weisj/darklaf/util/Alignment.java
index 88fd492e..399a0f6e 100644
--- a/utils/src/main/java/com/github/weisj/darklaf/util/Alignment.java
+++ b/utils/src/main/java/com/github/weisj/darklaf/util/Alignment.java
@@ -304,4 +304,36 @@ public enum Alignment {
public boolean isWest(final boolean includePure) {
return (this == Alignment.WEST && includePure) || this == Alignment.NORTH_WEST || this == Alignment.SOUTH_WEST;
}
+
+ public double getAngle() {
+ double angle = 0.0;
+ switch (this) {
+ case NORTH :
+ case CENTER :
+ angle = 0.0;
+ break;
+ case SOUTH :
+ angle = 180.0;
+ break;
+ case EAST :
+ angle = 90.0;
+ break;
+ case WEST :
+ angle = 270.0;
+ break;
+ case NORTH_EAST :
+ angle = 45.0;
+ break;
+ case NORTH_WEST :
+ angle = 315.0;
+ break;
+ case SOUTH_EAST :
+ angle = 135.0;
+ break;
+ case SOUTH_WEST :
+ angle = 225.0;
+ break;
+ }
+ return Math.toRadians(angle);
+ }
}