-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LafInstallation: Add theme transition animation when installing the l…
…af using the LafManager. This can be disabled by setting the "darklaf.animatedLafChange" system property to false.
- Loading branch information
Showing
9 changed files
with
660 additions
and
49 deletions.
There are no files selected for viewing
88 changes: 88 additions & 0 deletions
88
core/src/main/java/com/github/weisj/darklaf/LafInstaller.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
* MIT License | ||
* | ||
* Copyright (c) 2021 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; | ||
|
||
import java.awt.Window; | ||
import java.util.logging.Logger; | ||
|
||
import javax.swing.SwingUtilities; | ||
import javax.swing.UIManager; | ||
import javax.swing.UnsupportedLookAndFeelException; | ||
|
||
import com.github.weisj.darklaf.theme.Theme; | ||
import com.github.weisj.darklaf.theme.event.ThemeChangeEvent; | ||
import com.github.weisj.darklaf.theme.event.ThemeChangeListener; | ||
import com.github.weisj.darklaf.theme.event.ThemeEventSupport; | ||
import com.github.weisj.darklaf.util.LogUtil; | ||
|
||
final class LafInstaller { | ||
|
||
private static final Logger LOGGER = LogUtil.getLogger(LafManager.class); | ||
private static final ThemeEventSupport<ThemeChangeEvent, ThemeChangeListener> eventSupport = | ||
new ThemeEventSupport<>(); | ||
|
||
void install(final Theme theme) { | ||
try { | ||
LOGGER.fine(() -> "Installing theme " + theme); | ||
LafTransition transition = LafTransition.showSnapshot(); | ||
UIManager.setLookAndFeel(new DarkLaf(false)); | ||
updateLaf(); | ||
SwingUtilities.invokeLater(transition::runTransition); | ||
notifyThemeInstalled(theme); | ||
} catch (final UnsupportedLookAndFeelException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
void updateLaf() { | ||
for (final Window w : Window.getWindows()) { | ||
updateLafRecursively(w); | ||
} | ||
} | ||
|
||
private void updateLafRecursively(final Window window) { | ||
for (final Window childWindow : window.getOwnedWindows()) { | ||
updateLafRecursively(childWindow); | ||
} | ||
SwingUtilities.updateComponentTreeUI(window); | ||
} | ||
|
||
void notifyThemeInstalled(final Theme newTheme) { | ||
eventSupport.dispatchEvent(new ThemeChangeEvent(null, newTheme), ThemeChangeListener::themeInstalled); | ||
} | ||
|
||
void addThemeChangeListener(final ThemeChangeListener listener) { | ||
eventSupport.addListener(listener); | ||
} | ||
|
||
void removeThemeChangeListener(final ThemeChangeListener listener) { | ||
eventSupport.removeListener(listener); | ||
} | ||
|
||
|
||
void notifyThemeChanged(final Theme oldTheme, final Theme newTheme) { | ||
if (oldTheme != newTheme) { | ||
eventSupport.dispatchEvent(new ThemeChangeEvent(oldTheme, newTheme), ThemeChangeListener::themeChanged); | ||
LOGGER.fine(() -> "Setting theme to " + newTheme); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
166 changes: 166 additions & 0 deletions
166
core/src/main/java/com/github/weisj/darklaf/LafTransition.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
/* | ||
* MIT License | ||
* | ||
* Copyright (c) 2021 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; | ||
|
||
|
||
|
||
import java.awt.AlphaComposite; | ||
import java.awt.Component; | ||
import java.awt.Graphics; | ||
import java.awt.Graphics2D; | ||
import java.awt.Image; | ||
import java.awt.Rectangle; | ||
import java.awt.Window; | ||
import java.util.LinkedHashMap; | ||
import java.util.Map; | ||
|
||
import javax.swing.JComponent; | ||
import javax.swing.JLayeredPane; | ||
import javax.swing.RootPaneContainer; | ||
|
||
import com.github.weisj.darklaf.graphics.Animator; | ||
import com.github.weisj.darklaf.graphics.DefaultInterpolator; | ||
import com.github.weisj.darklaf.util.ImageUtil; | ||
import com.github.weisj.darklaf.util.PropertyUtil; | ||
import com.github.weisj.darklaf.util.graphics.ImagePainter; | ||
import com.github.weisj.darklaf.util.value.SharedNonNull; | ||
|
||
class LafTransition { | ||
|
||
private static final String ANIMATED_LAF_CHANGE = DarkLaf.SYSTEM_PROPERTY_PREFIX + "animatedLafChange"; | ||
|
||
private LafTransition() {} | ||
|
||
static LafTransition showSnapshot() { | ||
return PropertyUtil.getSystemFlag(ANIMATED_LAF_CHANGE) | ||
? new AnimatedLafTransition() | ||
: new LafTransition(); | ||
} | ||
|
||
void runTransition() { | ||
// Do nothing. | ||
} | ||
|
||
static final class AnimatedLafTransition extends LafTransition { | ||
|
||
private final Animator animator; | ||
private final Map<JLayeredPane, Component> uiSnapshots; | ||
private final SharedNonNull<Float> sharedAlpha; | ||
|
||
private AnimatedLafTransition() { | ||
sharedAlpha = new SharedNonNull<>(1f); | ||
animator = new TransitionAnimator(); | ||
uiSnapshots = new LinkedHashMap<>(); | ||
Window[] windows = Window.getWindows(); | ||
for (Window window : windows) { | ||
if (window instanceof RootPaneContainer && window.isShowing()) { | ||
RootPaneContainer rootPaneContainer = (RootPaneContainer) window; | ||
Image img = ImageUtil.scaledImageFromComponent(rootPaneContainer.getRootPane()); | ||
JLayeredPane layeredPane = rootPaneContainer.getLayeredPane(); | ||
JComponent imageLayer = new ImageLayer(layeredPane, img, sharedAlpha); | ||
imageLayer.setSize(layeredPane.getSize()); | ||
layeredPane.add(imageLayer, JLayeredPane.DRAG_LAYER); | ||
uiSnapshots.put(layeredPane, imageLayer); | ||
} | ||
} | ||
doPaint(); | ||
} | ||
|
||
void runTransition() { | ||
animator.resume(); | ||
} | ||
|
||
private void disposeSnapshots() { | ||
for (Map.Entry<JLayeredPane, Component> entry : uiSnapshots.entrySet()) { | ||
entry.getKey().remove(entry.getValue()); | ||
entry.getKey().revalidate(); | ||
entry.getKey().repaint(); | ||
} | ||
uiSnapshots.clear(); | ||
} | ||
|
||
private void doPaint() { | ||
for (Map.Entry<JLayeredPane, Component> entry : uiSnapshots.entrySet()) { | ||
if (entry.getKey().isShowing()) { | ||
entry.getValue().revalidate(); | ||
entry.getValue().repaint(); | ||
} | ||
} | ||
} | ||
|
||
private class TransitionAnimator extends Animator { | ||
|
||
private static final int DURATION = 160; | ||
private static final int RESOLUTION = 10; | ||
|
||
public TransitionAnimator() { | ||
super(DURATION / RESOLUTION, DURATION, false, DefaultInterpolator.EASE_IN_SINE); | ||
} | ||
|
||
@Override | ||
public void resume() { | ||
doPaint(); | ||
super.resume(); | ||
} | ||
|
||
@Override | ||
public void paintNow(final float fraction) { | ||
sharedAlpha.set(1f - fraction); | ||
doPaint(); | ||
} | ||
|
||
@Override | ||
protected void paintCycleEnd() { | ||
disposeSnapshots(); | ||
} | ||
} | ||
} | ||
|
||
private static class ImageLayer extends JComponent { | ||
|
||
private final JLayeredPane layeredPane; | ||
private final SharedNonNull<Float> sharedAlpha; | ||
private final Image image; | ||
|
||
private ImageLayer(final JLayeredPane layeredPane, final Image image, final SharedNonNull<Float> sharedAlpha) { | ||
this.layeredPane = layeredPane; | ||
this.image = image; | ||
this.sharedAlpha = sharedAlpha; | ||
} | ||
|
||
@Override | ||
public void updateUI() {} | ||
|
||
@Override | ||
public void paint(final Graphics g) { | ||
Graphics gg = g.create(); | ||
((Graphics2D) gg).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, sharedAlpha.get())); | ||
ImagePainter.drawImage(gg, image, 0, 0, this); | ||
} | ||
|
||
@Override | ||
public Rectangle getBounds() { | ||
return layeredPane.getBounds(); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.