From 2ffd5437a90d6c42af099e3de6c9e41d4959cde3 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Wed, 22 Jul 2020 12:56:42 +0200 Subject: [PATCH] animated Laf changing added to flatlaf-extras, used in Demo --- CHANGELOG.md | 1 + .../com/formdev/flatlaf/util/Animator.java | 1 + .../com/formdev/flatlaf/demo/ControlBar.java | 4 + .../com/formdev/flatlaf/demo/DemoFrame.java | 16 ++ .../com/formdev/flatlaf/demo/DemoFrame.jfd | 11 +- .../demo/intellijthemes/IJThemesPanel.java | 8 + flatlaf-extras/README.md | 2 + .../flatlaf/extras/FlatAnimatedLafChange.java | 163 ++++++++++++++++++ .../flatlaf/testing/FlatTestFrame.java | 3 + 9 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatAnimatedLafChange.java diff --git a/CHANGELOG.md b/CHANGELOG.md index f46ac370..43a97ad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ FlatLaf Change Log ## Unreleased +- Animated theme change (see [FlatLaf Extras](flatlaf-extras)). - Custom window decorations: Fixed maximized window bounds when programmatically maximizing window. E.g. restoring window state at startup. (issue #129) - InternalFrame: Title pane height was too small when iconify, maximize and close diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/Animator.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/Animator.java index 67dfa94e..6eac10d0 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/Animator.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/Animator.java @@ -92,6 +92,7 @@ public class Animator /** * Sets the resolution of the animation in milliseconds. * + * @param resolution the resolution of the animation in milliseconds * @throws IllegalStateException if animation is running * @throws IllegalArgumentException if resolution is <= zero */ diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/ControlBar.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/ControlBar.java index cd6c07c1..b34c0a30 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/ControlBar.java +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/ControlBar.java @@ -27,6 +27,7 @@ import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.plaf.metal.MetalLookAndFeel; import javax.swing.plaf.nimbus.NimbusLookAndFeel; import com.formdev.flatlaf.*; +import com.formdev.flatlaf.extras.FlatAnimatedLafChange; import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.UIScale; import net.miginfocom.swing.*; @@ -193,6 +194,8 @@ class ControlBar EventQueue.invokeLater( () -> { try { + FlatAnimatedLafChange.showSnapshot(); + // change look and feel UIManager.setLookAndFeel( lafClassName ); @@ -202,6 +205,7 @@ class ControlBar // update all components FlatLaf.updateUI(); + FlatAnimatedLafChange.hideSnapshotWithAnimation(); // increase size of frame if necessary int width = frame.getWidth(); diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java index c7357383..8ddd8618 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java @@ -27,6 +27,7 @@ import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.demo.extras.*; import com.formdev.flatlaf.demo.intellijthemes.*; +import com.formdev.flatlaf.extras.FlatAnimatedLafChange; import com.formdev.flatlaf.extras.FlatSVGIcon; import com.formdev.flatlaf.extras.SVGUtils; import com.formdev.flatlaf.ui.JBRCustomDecorations; @@ -105,14 +106,21 @@ class DemoFrame repaint(); } + private void animatedLafChangeChanged() { + System.setProperty( "flatlaf.animatedLafChange", String.valueOf( animatedLafChangeMenuItem.isSelected() ) ); + } + private void fontFamilyChanged( ActionEvent e ) { String fontFamily = e.getActionCommand(); + FlatAnimatedLafChange.showSnapshot(); + Font font = UIManager.getFont( "defaultFont" ); Font newFont = StyleContext.getDefaultStyleContext().getFont( fontFamily, font.getStyle(), font.getSize() ); UIManager.put( "defaultFont", newFont ); FlatLaf.updateUI(); + FlatAnimatedLafChange.hideSnapshotWithAnimation(); } private void fontSizeChanged( ActionEvent e ) { @@ -248,6 +256,7 @@ class DemoFrame menuBarEmbeddedCheckBoxMenuItem = new JCheckBoxMenuItem(); underlineMenuSelectionMenuItem = new JCheckBoxMenuItem(); alwaysShowMnemonicsMenuItem = new JCheckBoxMenuItem(); + animatedLafChangeMenuItem = new JCheckBoxMenuItem(); JMenu helpMenu = new JMenu(); JMenuItem aboutMenuItem = new JMenuItem(); JToolBar toolBar1 = new JToolBar(); @@ -501,6 +510,12 @@ class DemoFrame alwaysShowMnemonicsMenuItem.setText("Always show mnemonics"); alwaysShowMnemonicsMenuItem.addActionListener(e -> alwaysShowMnemonics()); optionsMenu.add(alwaysShowMnemonicsMenuItem); + + //---- animatedLafChangeMenuItem ---- + animatedLafChangeMenuItem.setText("Animated Laf Change"); + animatedLafChangeMenuItem.setSelected(true); + animatedLafChangeMenuItem.addActionListener(e -> animatedLafChangeChanged()); + optionsMenu.add(animatedLafChangeMenuItem); } menuBar1.add(optionsMenu); @@ -620,6 +635,7 @@ class DemoFrame private JCheckBoxMenuItem menuBarEmbeddedCheckBoxMenuItem; private JCheckBoxMenuItem underlineMenuSelectionMenuItem; private JCheckBoxMenuItem alwaysShowMnemonicsMenuItem; + private JCheckBoxMenuItem animatedLafChangeMenuItem; private JTabbedPane tabbedPane; private ControlBar controlBar; // JFormDesigner - End of variables declaration //GEN-END:variables diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.jfd b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.jfd index 0146ff6b..12f8061c 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.jfd +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.jfd @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "7.0.1.0.272" Java: "13.0.2" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.2.0.298" Java: "14.0.2" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -356,6 +356,15 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "alwaysShowMnemonics", false ) ) } ) + add( new FormComponent( "javax.swing.JCheckBoxMenuItem" ) { + name: "animatedLafChangeMenuItem" + "text": "Animated Laf Change" + "selected": true + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "animatedLafChangeChanged", false ) ) + } ) } ) add( new FormContainer( "javax.swing.JMenu", new FormLayoutManager( class javax.swing.JMenu ) ) { name: "helpMenu" diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/intellijthemes/IJThemesPanel.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/intellijthemes/IJThemesPanel.java index f247b3d0..2fa7d4a5 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/intellijthemes/IJThemesPanel.java +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/intellijthemes/IJThemesPanel.java @@ -49,6 +49,7 @@ import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.FlatPropertiesLaf; import com.formdev.flatlaf.IntelliJTheme; import com.formdev.flatlaf.demo.DemoPrefs; +import com.formdev.flatlaf.extras.FlatAnimatedLafChange; import com.formdev.flatlaf.extras.FlatSVGIcon; import com.formdev.flatlaf.util.StringUtils; import net.miginfocom.swing.*; @@ -219,6 +220,8 @@ public class IJThemesPanel if( themeInfo.lafClassName.equals( UIManager.getLookAndFeel().getClass().getName() ) ) return; + FlatAnimatedLafChange.showSnapshot(); + try { UIManager.setLookAndFeel( themeInfo.lafClassName ); } catch( Exception ex ) { @@ -226,6 +229,8 @@ public class IJThemesPanel showInformationDialog( "Failed to create '" + themeInfo.lafClassName + "'.", ex ); } } else if( themeInfo.themeFile != null ) { + FlatAnimatedLafChange.showSnapshot(); + try { if( themeInfo.themeFile.getName().endsWith( ".properties" ) ) { FlatLaf.install( new FlatPropertiesLaf( themeInfo.name, themeInfo.themeFile ) ); @@ -238,12 +243,15 @@ public class IJThemesPanel showInformationDialog( "Failed to load '" + themeInfo.themeFile + "'.", ex ); } } else { + FlatAnimatedLafChange.showSnapshot(); + IntelliJTheme.install( getClass().getResourceAsStream( THEMES_PACKAGE + themeInfo.resourceName ) ); DemoPrefs.getState().put( DemoPrefs.KEY_LAF_THEME, DemoPrefs.RESOURCE_PREFIX + themeInfo.resourceName ); } // update all components FlatLaf.updateUI(); + FlatAnimatedLafChange.hideSnapshotWithAnimation(); } private void saveTheme() { diff --git a/flatlaf-extras/README.md b/flatlaf-extras/README.md index 6e6e5bcd..42b0499e 100644 --- a/flatlaf-extras/README.md +++ b/flatlaf-extras/README.md @@ -3,6 +3,8 @@ FlatLaf Extras This sub-project provides some additional components and classes: +- [FlatAnimatedLafChange](src/main/java/com/formdev/flatlaf/extras/FlatAnimatedLafChange.java): + Animated Laf changing. - [FlatInspector](src/main/java/com/formdev/flatlaf/extras/FlatInspector.java): A simple UI inspector that shows information about UI component at mouse location in a tooltip. diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatAnimatedLafChange.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatAnimatedLafChange.java new file mode 100644 index 00000000..e7689bb1 --- /dev/null +++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatAnimatedLafChange.java @@ -0,0 +1,163 @@ +/* + * Copyright 2020 FormDev Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.formdev.flatlaf.extras; + +import java.awt.AlphaComposite; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Window; +import java.util.HashMap; +import java.util.Map; +import javax.swing.JComponent; +import javax.swing.JLayeredPane; +import javax.swing.RootPaneContainer; +import com.formdev.flatlaf.FlatSystemProperties; +import com.formdev.flatlaf.util.Animator; + +/** + * Animated look and feel changing. + *

+ * Invoke {@link #showSnapshot()} before setting look and feel and + * {@link #hideSnapshotWithAnimation()} after updating UI. E.g. + *

+ * FlatAnimatedLafChange.showSnapshot();
+ * UIManager.setLookAndFeel( lafClassName );
+ * FlatLaf.updateUI();
+ * FlatAnimatedLafChange.hideSnapshotWithAnimation();
+ * 
+ * + * @author Karl Tauber + */ +public class FlatAnimatedLafChange +{ + /** + * The duration of the animation in milliseconds. Default is 160 ms. + */ + public static int duration = 160; + + /** + * The resolution of the animation in milliseconds. Default is 40 ms. + */ + public static int resolution = 40; + + private static Animator animator; + private static final Map map = new HashMap<>(); + private static float alpha; + + /** + * Create a snapshot of the old UI and shows it on top of the UI. + * Invoke before setting new look and feel. + */ + public static void showSnapshot() { + if( !FlatSystemProperties.getBoolean( "flatlaf.animatedLafChange", true ) ) + return; + + // stop already running animation + if( animator != null ) + animator.stop(); + + alpha = 1; + + // create snapshots for all shown windows + Window[] windows = Window.getWindows(); + for( Window window : windows ) { + if( !(window instanceof RootPaneContainer) || !window.isShowing() ) + continue; + + JLayeredPane layeredPane = ((RootPaneContainer)window).getLayeredPane(); + + // create snapshot image of layered pane + Image snapshot = window.createImage( window.getWidth(), window.getHeight() ); + layeredPane.paint( snapshot.getGraphics() ); + + // create snapshot layer, which is added to layered pane and paints + // snapshot with animated alpha + JComponent snapshotLayer = new JComponent() { + @Override + public void paint( Graphics g ) { + ((Graphics2D)g).setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER, alpha ) ); + g.drawImage( snapshot, 0, 0, null ); + } + }; + snapshotLayer.setSize( layeredPane.getSize() ); + + // add image layer to layered pane + layeredPane.add( snapshotLayer, JLayeredPane.DRAG_LAYER ); + map.put( layeredPane, snapshotLayer ); + } + } + + /** + * Starts an animation that shows the snapshot (created by {@link #showSnapshot()} + * with an decreasing alpha. At the end, the snapshot is removed and the new UI is shown. + * Invoke after updating UI. + */ + public static void hideSnapshotWithAnimation() { + if( !FlatSystemProperties.getBoolean( "flatlaf.animatedLafChange", true ) ) + return; + + if( map.isEmpty() ) + return; + + // create animator + animator = new Animator( duration, new Animator.TimingTarget() { + @Override + public void timingEvent( float fraction ) { + if( fraction < 0.1 || fraction > 0.9 ) + return; // ignore initial and last events + + alpha = 1f - fraction; + + // repaint snapshots + for( Map.Entry e : map.entrySet() ) { + if( e.getKey().isShowing() ) + e.getValue().repaint(); + } + } + + @Override + public void end() { + hideSnapshot(); + animator = null; + } + } ); + + animator.setResolution( resolution ); + animator.start(); + } + + private static void hideSnapshot() { + // remove snapshots + for( Map.Entry e : map.entrySet() ) { + e.getKey().remove( e.getValue() ); + e.getKey().repaint(); + } + + map.clear(); + } + + /** + * Stops a running animation (if any) and hides the snapshot. + */ + public static void stop() { + if( animator != null ) + animator.stop(); + else + hideSnapshot(); + } +} diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTestFrame.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTestFrame.java index eae07aea..591b4275 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTestFrame.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTestFrame.java @@ -73,6 +73,9 @@ public class FlatTestFrame System.setProperty( FlatSystemProperties.UI_SCALE, scaleFactor ); } + // disable animated Laf change + System.setProperty( "flatlaf.animatedLafChange", "false" ); + // set look and feel DemoPrefs.initLaf( args );