com.alee.extended.panel.WebComponentPanel Maven / Gradle / Ivy
/*
* This file is part of WebLookAndFeel library.
*
* WebLookAndFeel library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* WebLookAndFeel library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WebLookAndFeel library. If not, see .
*/
package com.alee.extended.panel;
import com.alee.extended.layout.ComponentPanelLayout;
import com.alee.extended.painter.AbstractPainter;
import com.alee.global.StyleConstants;
import com.alee.laf.panel.WebPanel;
import com.alee.managers.focus.DefaultFocusTracker;
import com.alee.managers.focus.FocusManager;
import com.alee.managers.hotkey.Hotkey;
import com.alee.managers.hotkey.HotkeyManager;
import com.alee.managers.hotkey.HotkeyRunnable;
import com.alee.utils.CollectionUtils;
import com.alee.utils.SwingUtils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* User: mgarin Date: 29.02.12 Time: 16:53
*/
public class WebComponentPanel extends WebPanel
{
public static final int GRIPPER_SIZE = 7;
public static final int SINGLE_GRIPPER_STEP = 4;
private final List listeners = new ArrayList ( 1 );
private WebPanel container;
private final Map components = new LinkedHashMap ();
private Insets elementMargin = new Insets ( 2, 2, 2, 2 );
private boolean reorderingAllowed = false;
private boolean showReorderGrippers = true;
private boolean upDownHotkeysAllowed = true;
private boolean leftRightHotkeysAllowed = false;
public WebComponentPanel ()
{
super ();
initialize ();
}
public WebComponentPanel ( final boolean decorated )
{
super ( decorated );
initialize ();
}
private void initialize ()
{
// Default styling
setWebColoredBackground ( false );
// Elements layout
container = new WebPanel ();
container.setLayout ( new ComponentPanelLayout () );
add ( container, BorderLayout.CENTER );
// Previous action hotkeys
final HotkeyRunnable prevAction = new HotkeyRunnable ()
{
@Override
public void run ( final KeyEvent e )
{
if ( upDownHotkeysAllowed && Hotkey.UP.isTriggered ( e ) || leftRightHotkeysAllowed && Hotkey.LEFT.isTriggered ( e ) )
{
final int index = getFocusedElementIndex ();
if ( index == -1 )
{
focusElement ( getElementCount () - 1 );
}
else
{
focusElement ( index > 0 ? index - 1 : getElementCount () - 1 );
}
}
}
};
HotkeyManager.registerHotkey ( this, this, Hotkey.UP, prevAction );
HotkeyManager.registerHotkey ( this, this, Hotkey.LEFT, prevAction );
// Next action hotkeys
final HotkeyRunnable nextAction = new HotkeyRunnable ()
{
@Override
public void run ( final KeyEvent e )
{
if ( upDownHotkeysAllowed && Hotkey.DOWN.isTriggered ( e ) || leftRightHotkeysAllowed && Hotkey.RIGHT.isTriggered ( e ) )
{
final int index = getFocusedElementIndex ();
if ( index == -1 )
{
focusElement ( 0 );
}
else
{
focusElement ( index < getElementCount () - 1 ? index + 1 : 0 );
}
}
}
};
HotkeyManager.registerHotkey ( this, this, Hotkey.DOWN, nextAction );
HotkeyManager.registerHotkey ( this, this, Hotkey.RIGHT, nextAction );
}
@Override
public void applyComponentOrientation ( final ComponentOrientation o )
{
super.applyComponentOrientation ( o );
updateAllBorders ();
}
public ComponentPanelLayout getContainerLayout ()
{
return ( ComponentPanelLayout ) container.getLayout ();
}
public boolean isUpDownHotkeysAllowed ()
{
return upDownHotkeysAllowed;
}
public void setUpDownHotkeysAllowed ( final boolean upDownHotkeysAllowed )
{
this.upDownHotkeysAllowed = upDownHotkeysAllowed;
}
public boolean isLeftRightHotkeysAllowed ()
{
return leftRightHotkeysAllowed;
}
public void setLeftRightHotkeysAllowed ( final boolean leftRightHotkeysAllowed )
{
this.leftRightHotkeysAllowed = leftRightHotkeysAllowed;
}
public WebSelectablePanel addElement ( final Component component )
{
// Ignore existing component insert
if ( components.containsKey ( component ) )
{
return components.get ( component );
}
// Creating view for component
final WebSelectablePanel element = new WebSelectablePanel ();
element.add ( component, BorderLayout.CENTER );
// todo Fix this workaround and check other layouts for that problem
// String is needed to invoke proper layout method
container.add ( element, "" );
// Saving view for component
components.put ( component, element );
// Updating existing panels
updateAllBorders ();
return element;
}
public void removeElement ( final int index )
{
removeElement ( getElement ( index ) );
}
public void removeElement ( final WebSelectablePanel element )
{
for ( final Component component : components.keySet () )
{
if ( components.get ( component ) == element )
{
removeElement ( component );
break;
}
}
}
public void removeElement ( final Component component )
{
// Removing actual element
final WebSelectablePanel element = components.get ( component );
container.remove ( element );
// Removing data
components.remove ( component );
// Updating existing panels
updateAllBorders ();
}
public int getElementCount ()
{
return components.size ();
}
public WebSelectablePanel getElement ( final int index )
{
return ( WebSelectablePanel ) getContainerLayout ().getComponent ( index );
}
public WebSelectablePanel getFocusedElement ()
{
for ( final Component component : getContainerLayout ().getComponents () )
{
final WebSelectablePanel selectablePanel = ( WebSelectablePanel ) component;
if ( selectablePanel.isFocused () )
{
return selectablePanel;
}
}
return null;
}
public int getFocusedElementIndex ()
{
return getContainerLayout ().indexOf ( getFocusedElement () );
}
public void focusElement ( final int index )
{
getElement ( index ).transferFocus ();
}
public Insets getElementMargin ()
{
return elementMargin;
}
public void setElementMargin ( final int margin )
{
setElementMargin ( margin, margin, margin, margin );
}
public void setElementMargin ( final int top, final int left, final int bottom, final int right )
{
setElementMargin ( new Insets ( top, left, bottom, right ) );
}
public void setElementMargin ( final Insets margin )
{
this.elementMargin = margin;
// Updating existing panels
updateAllBorders ();
}
public boolean isReorderingAllowed ()
{
return reorderingAllowed;
}
public void setReorderingAllowed ( final boolean reorderingAllowed )
{
this.reorderingAllowed = reorderingAllowed;
// Updating existing panels
updateAllBorders ();
}
public boolean isShowReorderGrippers ()
{
return showReorderGrippers;
}
public void setShowReorderGrippers ( final boolean showReorderGrippers )
{
this.showReorderGrippers = showReorderGrippers;
// Updating existing panels
updateAllBorders ();
}
private void updateAllBorders ()
{
for ( final WebSelectablePanel panel : components.values () )
{
panel.updateBorder ();
}
}
public class WebSelectablePanel extends WebPanel
{
private final DefaultFocusTracker focusTracker;
private boolean dragged = false;
private boolean focused = false;
public WebSelectablePanel ()
{
super ();
updateBorder ();
// Selection painter
setPainter ( new WebSelectablePanelPainter () );
// On-press focus transfer & reorder
final MouseAdapter mouseAdapter = new MouseAdapter ()
{
private int startY;
@Override
public void mousePressed ( final MouseEvent e )
{
if ( !SwingUtils.hasFocusOwner ( WebSelectablePanel.this ) )
{
WebSelectablePanel.this.transferFocus ();
}
if ( WebComponentPanel.this.isEnabled () && SwingUtilities.isLeftMouseButton ( e ) )
{
dragged = true;
startY = getY ();
container.setComponentZOrder ( WebSelectablePanel.this, 0 );
}
}
@Override
public void mouseDragged ( final MouseEvent e )
{
if ( dragged )
{
getContainerLayout ().setComponentShift ( WebSelectablePanel.this, getY () - startY );
WebSelectablePanel.this.revalidate ();
}
}
@Override
public void mouseReleased ( final MouseEvent e )
{
final WebSelectablePanel wsp = WebSelectablePanel.this;
final ComponentPanelLayout cpl = getContainerLayout ();
if ( SwingUtilities.isLeftMouseButton ( e ) && dragged )
{
// Stop drag
dragged = false;
// Update if needed
if ( getY () - startY == 0 || cpl.getComponents ().size () <= 1 )
{
// Ignore drag if there is only 1 component or change is zero
cpl.setComponentShift ( wsp, null );
}
else
{
// Dragged panel index and middle
final int oldIndex = cpl.indexOf ( wsp );
final int middle = getMiddleY ( wsp );
// Searching for insert index
int insertIndex = 0;
for ( final Component component : cpl.getComponents () )
{
if ( component != wsp && middle > getMiddleY ( component ) )
{
insertIndex = cpl.indexOf ( component ) + 1;
}
}
// Fix index
if ( insertIndex > oldIndex )
{
insertIndex--;
}
// Resetting shift
cpl.setComponentShift ( wsp, null );
// Changing panel location if it has actually changed
if ( oldIndex != insertIndex )
{
// Updating panel indices
cpl.removeLayoutComponent ( wsp );
cpl.insertLayoutComponent ( insertIndex, wsp );
updateAllBorders ();
// Informing all listeners
fireComponentOrderChanged ( wsp.getComponent (), oldIndex, insertIndex );
}
}
// Update panel positions
wsp.revalidate ();
}
}
private int getMiddleY ( final Component component )
{
final Rectangle rectangle = component.getBounds ();
return rectangle.y + rectangle.height / 2;
}
private int getY ()
{
return MouseInfo.getPointerInfo ().getLocation ().y;
}
};
addMouseListener ( mouseAdapter );
addMouseMotionListener ( mouseAdapter );
// Panel focus tracking
focusTracker = new DefaultFocusTracker ( true )
{
@Override
public void focusChanged ( final boolean focused )
{
final WebSelectablePanel wsp = WebSelectablePanel.this;
wsp.focused = focused;
// Cancel panel drag on focus loss
if ( !focused && dragged )
{
dragged = false;
getContainerLayout ().setComponentShift ( wsp, null );
wsp.revalidate ();
}
repaint ();
}
};
FocusManager.addFocusTracker ( WebSelectablePanel.this, focusTracker );
}
public void updateBorder ()
{
final int index = getIndex ();
final boolean ltr = getComponentOrientation ().isLeftToRight ();
final int top = index == 0 ? elementMargin.top : elementMargin.top + 1;
final int left = elementMargin.left + ( reorderingAllowed && showReorderGrippers && ltr ? GRIPPER_SIZE : 0 );
final int bottom = index == components.size () - 1 ? elementMargin.bottom : elementMargin.bottom + 1;
final int right = elementMargin.right + ( reorderingAllowed && showReorderGrippers && !ltr ? GRIPPER_SIZE : 0 );
setMargin ( top, left, bottom, right );
}
public boolean isFocused ()
{
return focused;
}
public boolean isDragged ()
{
return dragged;
}
public Component getComponent ()
{
return getComponent ( 0 );
}
public int getIndex ()
{
return getContainerLayout ().indexOf ( WebSelectablePanel.this );
}
}
/**
* Custom painter for selectable panels.
*/
public class WebSelectablePanelPainter extends AbstractPainter
{
/**
* Style settings.
*/
protected float[] fractions = { 0f, 0.25f, 0.75f, 1f };
protected Color[] lightColors = { StyleConstants.transparent, Color.WHITE, Color.WHITE, StyleConstants.transparent };
protected Color[] darkColors = { StyleConstants.transparent, Color.GRAY, Color.GRAY, StyleConstants.transparent };
/**
* {@inheritDoc}
*/
@Override
public Boolean isOpaque ( final WebSelectablePanel c )
{
return true;
}
/**
* {@inheritDoc}
*/
@Override
public void paint ( final Graphics2D g2d, final Rectangle bounds, final WebSelectablePanel panel )
{
final boolean notFirst = panel.getIndex () > 0;
final boolean notLast = panel.getIndex () < components.size () - 1;
if ( panel.isFocused () )
{
// Background
g2d.setPaint ( new GradientPaint ( bounds.x, bounds.y, StyleConstants.topBgColor, bounds.x, bounds.y + bounds.height,
StyleConstants.bottomBgColor ) );
g2d.fill ( bounds );
// Borders
final Integer shift = getContainerLayout ().getComponentShift ( panel );
final boolean moved = panel.isDragged () && shift != null && shift != 0;
g2d.setPaint ( StyleConstants.darkBorderColor );
if ( notFirst || moved )
{
g2d.drawLine ( bounds.x, bounds.y, bounds.x + bounds.width - 1, bounds.y );
}
if ( notLast || moved )
{
g2d.drawLine ( bounds.x, bounds.y + bounds.height - 1, bounds.x + bounds.width - 1, bounds.y + bounds.height - 1 );
}
}
// Gripper
if ( reorderingAllowed && showReorderGrippers )
{
// Determining coordinates
final boolean ltr = panel.getComponentOrientation ().isLeftToRight ();
final int minY = bounds.y + 2 + ( notFirst ? 1 : 0 );
final int maxY = bounds.x + bounds.height - 2 - ( notLast ? 1 : 0 );
final int x = ltr ? bounds.x + 3 : bounds.x + bounds.width - GRIPPER_SIZE + 2;
int y = minY + ( ( maxY - minY ) % SINGLE_GRIPPER_STEP ) / 2;
// Painters
final Paint light = new LinearGradientPaint ( x, minY, x, maxY, fractions, lightColors );
final Paint dark = new LinearGradientPaint ( x, minY, x, maxY, fractions, darkColors );
// Paint cycle
while ( y + 3 < maxY )
{
g2d.setPaint ( light );
g2d.fillRect ( x + 1, y + 1, 2, 2 );
g2d.setPaint ( dark );
g2d.fillRect ( x, y, 2, 2 );
y += SINGLE_GRIPPER_STEP;
}
}
}
}
public void addComponentReorderListener ( final ComponentReorderListener listener )
{
listeners.add ( listener );
}
public void removeComponentReorderListener ( final ComponentReorderListener listener )
{
listeners.remove ( listener );
}
private void fireComponentOrderChanged ( final Component component, final int oldIndex, final int newIndex )
{
for ( final ComponentReorderListener listener : CollectionUtils.copy ( listeners ) )
{
listener.componentOrderChanged ( component, oldIndex, newIndex );
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy