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); + } }