com.alee.laf.rootpane.RootPanePainter Maven / Gradle / Ivy
package com.alee.laf.rootpane;
import com.alee.api.jdk.Objects;
import com.alee.extended.behavior.VisibilityBehavior;
import com.alee.laf.WebLookAndFeel;
import com.alee.managers.style.StyleId;
import com.alee.painter.decoration.AbstractContainerPainter;
import com.alee.painter.decoration.DecorationState;
import com.alee.painter.decoration.DecorationUtils;
import com.alee.painter.decoration.IDecoration;
import com.alee.utils.CoreSwingUtils;
import com.alee.utils.ProprietaryUtils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.WindowStateListener;
import java.util.List;
/**
* Basic painter for {@link JRootPane} component.
* It is used as {@link WebRootPaneUI} default painter.
*
* @param component type
* @param component UI type
* @param decoration type
* @author Alexandr Zernov
* @author Mikle Garin
*/
public class RootPanePainter>
extends AbstractContainerPainter implements IRootPanePainter
{
/**
* Listeners.
*/
protected transient WindowFocusListener windowFocusListener;
protected transient WindowStateListener frameStateListener;
protected transient VisibilityBehavior windowVisibilityBehavior;
/**
* Runtime variables.
*/
protected transient boolean maximized = false;
@Override
protected void afterInstall ()
{
/**
* Performing basic actions after installation ends.
*/
super.afterInstall ();
/**
* It is very important to install decoration after all other updates.
* Otherwise we might be missing critically-important styles here.
*/
installWindowDecoration ();
}
@Override
protected void beforeUninstall ()
{
/**
* It is important to uninstall decorations before anything else.
* Otherwise we are risking to cause issues within decoration elements.
*/
uninstallWindowDecoration ();
/**
* Performing basic actions before uninstallation starts.
*/
super.beforeUninstall ();
}
@Override
protected void installPropertiesAndListeners ()
{
super.installPropertiesAndListeners ();
installWindowStateListener ();
installVisibilityListener ();
}
@Override
protected void uninstallPropertiesAndListeners ()
{
uninstallVisibilityListener ();
uninstallWindowStateListener ();
super.uninstallPropertiesAndListeners ();
}
@Override
protected boolean usesFocusedView ()
{
final boolean usesFocusedView;
final Window window = getWindow ();
if ( window != null )
{
// JRootPane is not focusable by default so we need to ask window instead
usesFocusedView = window.isFocusableWindow () && usesState ( DecorationState.focused );
}
else
{
// In case there is no window we check default conditions
usesFocusedView = super.usesFocusedView ();
}
return usesFocusedView;
}
@Override
protected void installFocusListener ()
{
final Window window = getWindow ();
if ( window != null && usesFocusedView () )
{
// Optimized focused state listeners
focused = window.isFocused ();
windowFocusListener = new WindowFocusListener ()
{
@Override
public void windowGainedFocus ( final WindowEvent e )
{
RootPanePainter.this.focusChanged ( true );
}
@Override
public void windowLostFocus ( final WindowEvent e )
{
RootPanePainter.this.focusChanged ( false );
}
};
window.addWindowFocusListener ( windowFocusListener );
}
else
{
// Default focused state listeners
super.installFocusListener ();
}
}
@Override
protected void uninstallFocusListener ()
{
final Window window = getWindow ();
if ( window != null && windowFocusListener != null )
{
// Optimized focused state listeners
window.removeWindowFocusListener ( windowFocusListener );
windowFocusListener = null;
focused = false;
}
else
{
// Default focused state listeners
super.uninstallFocusListener ();
}
}
@Override
protected Boolean isOpaqueUndecorated ()
{
// Make undecorated root panes opaque by default
// This is important to keep any non-opaque components properly painted
// Note that if decoration is provided root pane will not be opaque and will have painting issues on natively-decorated windows
// todo Maybe native/non-native decoration should be taken in account when opacity is provided instead?
return true;
}
@Override
public boolean isDecorated ()
{
final D decoration = getDecoration ();
return decoration != null && decoration.isVisible ();
}
@Override
protected void propertyChanged ( final String property, final Object oldValue, final Object newValue )
{
// Perform basic actions on property changes
super.propertyChanged ( property, oldValue, newValue );
// Updating decoration according to current state
if ( Objects.equals ( property, WebLookAndFeel.WINDOW_DECORATION_STYLE_PROPERTY ) )
{
// Updating decoration state first
// This is necessary to avoid issues with state-dependant decorations
updateDecorationState ();
// Removing window decoration
// This is required to disable custom window decoration upon this property change
// We automatically rollback style here if the property was set from a custom one to JRootPane.NONE
if ( Objects.notEquals ( oldValue, JRootPane.NONE ) && Objects.equals ( newValue, JRootPane.NONE ) &&
Objects.notEquals ( StyleId.get ( component ), StyleId.rootpane, StyleId.dialog, StyleId.frame ) )
{
// Restoring original undecorated style
final Window window = getWindow ();
if ( window != null )
{
if ( window instanceof Frame )
{
StyleId.frame.set ( component );
}
else if ( window instanceof Dialog )
{
StyleId.dialog.set ( component );
}
else
{
StyleId.rootpane.set ( component );
}
}
else
{
StyleId.rootpane.set ( component );
}
}
// Installing default window decoration
// This is an important workaround to allow Frame and Dialog decoration to be enabled according to settings
// We only apply that workaround to non WebFrame/WebDialog based frames and dialogs which have decoration style specified
if ( Objects.equals ( oldValue, JRootPane.NONE ) && Objects.notEquals ( newValue, JRootPane.NONE ) &&
Objects.equals ( StyleId.get ( component ), StyleId.rootpane, StyleId.dialog, StyleId.frame ) )
{
final Window window = getWindow ();
if ( window != null )
{
if ( window instanceof Frame )
{
StyleId.frameDecorated.set ( component );
}
else if ( window instanceof Dialog )
{
switch ( component.getWindowDecorationStyle () )
{
case JRootPane.COLOR_CHOOSER_DIALOG:
{
StyleId.colorchooserDialog.set ( component );
break;
}
case JRootPane.FILE_CHOOSER_DIALOG:
{
StyleId.filechooserDialog.set ( component );
break;
}
case JRootPane.INFORMATION_DIALOG:
{
StyleId.optionpaneInformationDialog.set ( component );
break;
}
case JRootPane.ERROR_DIALOG:
{
StyleId.optionpaneErrorDialog.set ( component );
break;
}
case JRootPane.QUESTION_DIALOG:
{
StyleId.optionpaneQuestionDialog.set ( component );
break;
}
case JRootPane.WARNING_DIALOG:
{
StyleId.optionpaneWarningDialog.set ( component );
break;
}
default:
{
StyleId.dialogDecorated.set ( component );
break;
}
}
}
}
}
}
}
@Override
public List getDecorationStates ()
{
final List states = super.getDecorationStates ();
// Window decoration type state
states.add ( getWindowDecorationState () );
// Window state
if ( ui.isIconified () )
{
states.add ( DecorationState.iconified );
}
if ( ui.isMaximized () )
{
states.add ( DecorationState.maximized );
}
if ( CoreSwingUtils.isFullScreen ( component ) )
{
states.add ( DecorationState.fullscreen );
}
// Additional root pane window states
states.addAll ( DecorationUtils.getExtraStates ( getWindow () ) );
return states;
}
/**
* Returns window decoration state.
*
* @return window decoration state
*/
protected String getWindowDecorationState ()
{
switch ( component.getWindowDecorationStyle () )
{
case JRootPane.NONE:
{
return DecorationState.nativeWindow;
}
case JRootPane.FRAME:
{
return DecorationState.frame;
}
case JRootPane.PLAIN_DIALOG:
{
return DecorationState.dialog;
}
case JRootPane.COLOR_CHOOSER_DIALOG:
{
return DecorationState.colorchooserDialog;
}
case JRootPane.FILE_CHOOSER_DIALOG:
{
return DecorationState.filechooserDialog;
}
case JRootPane.INFORMATION_DIALOG:
{
return DecorationState.informationDialog;
}
case JRootPane.ERROR_DIALOG:
{
return DecorationState.errorDialog;
}
case JRootPane.QUESTION_DIALOG:
{
return DecorationState.questionDialog;
}
case JRootPane.WARNING_DIALOG:
{
return DecorationState.warningDialog;
}
}
throw new RuntimeException ( "Unknown window decoration style: " + component.getWindowDecorationStyle () );
}
/**
* Installs decorations for {@link Window} that uses {@link JRootPane} represented by this painter.
*/
protected void installWindowDecoration ()
{
final Window window = getWindow ();
if ( window != null && isDecorated () )
{
// Enabling frame decoration
if ( window instanceof Frame )
{
if ( !window.isDisplayable () )
{
component.setOpaque ( false );
ProprietaryUtils.setWindowShape ( window, null );
( ( Frame ) window ).setUndecorated ( true );
ProprietaryUtils.setWindowOpaque ( window, false );
}
if ( component.getWindowDecorationStyle () == JRootPane.NONE )
{
component.setWindowDecorationStyle ( JRootPane.FRAME );
}
}
else if ( window instanceof Dialog )
{
if ( !window.isDisplayable () )
{
component.setOpaque ( false );
ProprietaryUtils.setWindowShape ( window, null );
( ( Dialog ) window ).setUndecorated ( true );
ProprietaryUtils.setWindowOpaque ( window, false );
}
if ( component.getWindowDecorationStyle () == JRootPane.NONE )
{
component.setWindowDecorationStyle ( JRootPane.PLAIN_DIALOG );
}
}
// Installing UI decorations
ui.installWindowDecorations ();
}
}
/**
* Uninstalls decoration for {@link Window} that uses {@link JRootPane} represented by this painter.
*/
protected void uninstallWindowDecoration ()
{
final Window window = getWindow ();
if ( window != null && isDecorated () )
{
// Uninstalling UI decorations
ui.uninstallWindowDecorations ();
// Disabling frame decoration
if ( window instanceof Frame )
{
// Updating frame settings if it is not displayed
if ( !window.isDisplayable () )
{
ProprietaryUtils.setWindowOpaque ( window, true );
( ( Frame ) window ).setUndecorated ( false );
component.setOpaque ( true );
}
// Cannot do that here as it would cause double painter uninstall call
// This is also probably not wise to do performance-wise as it might be reused
// if ( c.getWindowDecorationStyle () != JRootPane.NONE )
// {
// c.setWindowDecorationStyle ( JRootPane.NONE );
// }
}
else if ( window instanceof Dialog )
{
// Updating dialog settings if it is not displayed
if ( !window.isDisplayable () )
{
ProprietaryUtils.setWindowOpaque ( window, true );
( ( Dialog ) window ).setUndecorated ( false );
component.setOpaque ( true );
}
// Cannot do that here as it would cause double painter uninstall call
// This is also probably not wise to do performance-wise as it might be reused
// if ( c.getWindowDecorationStyle () != JRootPane.NONE )
// {
// c.setWindowDecorationStyle ( JRootPane.NONE );
// }
}
}
}
/**
* Installs {@link WindowStateListener} into {@link Window} that uses {@link JRootPane} represented by this painter.
* It is only installed if {@link Window} is an instance of {@link Frame}, otherwise this listener is not required.
*/
protected void installWindowStateListener ()
{
final Window window = getWindow ();
if ( window instanceof Frame )
{
frameStateListener = new WindowStateListener ()
{
@Override
public void windowStateChanged ( final WindowEvent e )
{
updateDecorationState ();
}
};
window.addWindowStateListener ( frameStateListener );
}
}
/**
* Uninstalls {@link WindowStateListener} from {@link Window} that uses {@link JRootPane} represented by this painter.
* It is only uninstalled if {@link Window} is an instance of {@link Frame}, otherwise this listener is not even there.
*/
protected void uninstallWindowStateListener ()
{
final Window window = getWindow ();
if ( window != null && frameStateListener != null )
{
window.removeWindowStateListener ( frameStateListener );
frameStateListener = null;
}
}
/**
* Installs {@link VisibilityBehavior} that informs {@link WebLookAndFeel} about {@link Window} visibility changes.
*/
protected void installVisibilityListener ()
{
windowVisibilityBehavior = new VisibilityBehavior ( component )
{
@Override
public void displayed ()
{
final Window window = getWindow ();
if ( window != null )
{
WebLookAndFeel.fireWindowDisplayed ( window );
}
}
@Override
public void hidden ()
{
final Window window = getWindow ();
if ( window != null )
{
WebLookAndFeel.fireWindowHidden ( window );
}
}
};
windowVisibilityBehavior.install ();
}
/**
* Uninstalls {@link VisibilityBehavior} that informs {@link WebLookAndFeel} about {@link Window} visibility changes.
*/
protected void uninstallVisibilityListener ()
{
windowVisibilityBehavior.uninstall ();
windowVisibilityBehavior = null;
}
/**
* Returns {@link Window} that uses {@link JRootPane} represented by this painter.
*
* @return {@link Window} that uses {@link JRootPane} represented by this painter
*/
protected Window getWindow ()
{
final Container parent = component.getParent ();
return parent instanceof Window ? ( Window ) parent : null;
}
}