org.pushingpixels.radiance.component.internal.ui.ribbon.BasicRibbonBandUI Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swingset3-demos Show documentation
Show all versions of swingset3-demos Show documentation
Demonstrating the abilities of the Swing UI Toolkit swingset2 and swingx aka swingset3
The newest version!
/*
* Copyright (c) 2005-2021 Radiance Kirill Grouchnikov. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of the copyright holder nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.pushingpixels.radiance.component.internal.ui.ribbon;
import org.pushingpixels.radiance.component.api.common.CommandAction;
import org.pushingpixels.radiance.component.api.common.CommandButtonPresentationState;
import org.pushingpixels.radiance.component.api.common.JCommandButton;
import org.pushingpixels.radiance.component.api.common.RichTooltip;
import org.pushingpixels.radiance.component.api.common.model.Command;
import org.pushingpixels.radiance.component.api.common.model.CommandButtonPresentationModel;
import org.pushingpixels.radiance.component.api.common.popup.JPopupPanel;
import org.pushingpixels.radiance.component.api.common.popup.PopupPanelManager;
import org.pushingpixels.radiance.component.api.ribbon.AbstractRibbonBand;
import org.pushingpixels.radiance.component.api.ribbon.JRibbon;
import org.pushingpixels.radiance.component.api.ribbon.JRibbonBand;
import org.pushingpixels.radiance.component.api.ribbon.resize.CoreRibbonResizePolicies;
import org.pushingpixels.radiance.component.api.ribbon.resize.RibbonBandResizePolicy;
import org.pushingpixels.radiance.theming.api.RadianceThemingCortex;
import org.pushingpixels.radiance.theming.api.RadianceThemingSlices;
import org.pushingpixels.radiance.theming.internal.painter.BackgroundPaintingUtils;
import org.pushingpixels.radiance.theming.internal.utils.RadianceCoreUtilities;
import org.pushingpixels.radiance.theming.internal.utils.RadiancePopupContainer;
import org.pushingpixels.radiance.theming.internal.utils.RadianceSizeUtils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeListener;
import java.util.List;
/**
* Basic UI for ribbon band {@link JRibbonBand}.
*
* @author Kirill Grouchnikov
* @author Matt Nathan
*/
public abstract class BasicRibbonBandUI extends RibbonBandUI {
/**
* The associated ribbon band.
*/
protected AbstractRibbonBand ribbonBand;
/**
* The button for collapsed state.
*/
private JCommandButton collapsedButton;
/**
* The band expand button. Is visible when the {@link JRibbonBand#getExpandCommandListener()} of
* the associated ribbon band is not null
.
*/
protected JCommandButton expandButton;
protected Command expandCommand;
/**
* Mouse listener on the associated ribbon band.
*/
private MouseListener mouseListener;
/**
* Listens to property changes on the associated ribbon band.
*/
private PropertyChangeListener propertyChangeListener;
/**
* Popup panel that shows the contents of the ribbon band when it is in a collapsed state.
*
* @author Kirill Grouchnikov
*/
@RadiancePopupContainer
protected static class CollapsedButtonPopupPanel extends JPopupPanel {
/**
* The main component of this
popup panel. Can be null
.
*/
protected Component component;
/**
* Creates popup gallery with the specified component.
*
* @param component The main component of the popup gallery.
* @param originalSize The original dimension of the main component.
*/
private CollapsedButtonPopupPanel(Component component, Dimension originalSize) {
this.component = component;
this.setLayout(new BorderLayout());
this.add(component, BorderLayout.CENTER);
// System.out.println("Popup dim is " + originalSize);
this.setPreferredSize(originalSize);
this.setSize(originalSize);
RadianceThemingCortex.ComponentOrParentChainScope.setDecorationType(this,
RadianceThemingSlices.DecorationAreaType.CONTROL_PANE);
}
/**
* Removes the main component of this
popup gallery.
*
* @return The removed main component.
*/
public Component removeComponent() {
this.remove(this.component);
return this.component;
}
}
@Override
public void installUI(JComponent c) {
this.ribbonBand = (AbstractRibbonBand) c;
installDefaults();
installComponents();
installListeners();
c.setLayout(createLayoutManager());
AWTRibbonEventListener.install();
}
@Override
public void uninstallUI(JComponent c) {
c.setLayout(null);
uninstallListeners();
uninstallDefaults();
uninstallComponents();
if (!AWTRibbonEventListener.uninstall()) {
// should remove other methods of tracking
}
}
/**
* Installs default parameters on the associated ribbon band.
*/
protected void installDefaults() {
this.ribbonBand.setBorder(null);
}
/**
* Installs subcomponents on the associated ribbon band.
*/
protected void installComponents() {
Command collapseCommand = Command.builder()
.setText(this.ribbonBand.getTitle())
.setIconFactory(this.ribbonBand.getIconFactory())
.build();
this.collapsedButton = (JCommandButton) collapseCommand.project(
CommandButtonPresentationModel.builder()
.setPresentationState(CommandButtonPresentationState.BIG)
.setPopupKeyTip(this.ribbonBand.getCollapsedStateKeyTip())
.build())
.buildComponent();
this.ribbonBand.add(this.collapsedButton);
if (this.ribbonBand.getExpandCommandListener() != null) {
this.expandCommand = this.createExpandCommand();
this.expandButton = this.createExpandButton();
this.ribbonBand.add(this.expandButton);
}
}
/**
* Creates the expand button for the associated ribbon band.
*
* @return Expand button for the associated ribbon band.
*/
protected abstract JCommandButton createExpandButton();
protected abstract Command createExpandCommand();
protected abstract void syncExpandButtonIcon();
/**
* Installs listeners on the associated ribbon band.
*/
protected void installListeners() {
// without this empty adapter, the global listener never
// gets mouse entered events on the ribbon band
this.mouseListener = new MouseAdapter() {
};
this.ribbonBand.addMouseListener(this.mouseListener);
this.propertyChangeListener = propertyChangeEvent -> {
if ("title".equals(propertyChangeEvent.getPropertyName()))
ribbonBand.repaint();
if ("expandButtonKeyTip".equals(propertyChangeEvent.getPropertyName())) {
if (expandButton != null) {
expandButton.setActionKeyTip((String) propertyChangeEvent.getNewValue());
}
}
if ("expandButtonRichTooltip".equals(propertyChangeEvent.getPropertyName())) {
if (expandCommand != null) {
expandCommand.setActionRichTooltip((RichTooltip) propertyChangeEvent.getNewValue());
}
}
if ("collapsedStateKeyTip".equals(propertyChangeEvent.getPropertyName())) {
if (collapsedButton != null) {
collapsedButton.setPopupKeyTip((String) propertyChangeEvent.getNewValue());
}
}
if ("expandCommandListener".equals(propertyChangeEvent.getPropertyName())) {
CommandAction oldListener = (CommandAction) propertyChangeEvent.getOldValue();
CommandAction newListener = (CommandAction) propertyChangeEvent.getNewValue();
if ((oldListener != null) && (newListener == null)) {
// need to remove
ribbonBand.remove(expandButton);
expandButton = null;
ribbonBand.revalidate();
}
if ((oldListener == null) && (newListener != null)) {
// need to add
expandCommand = createExpandCommand();
expandButton = createExpandButton();
ribbonBand.add(expandButton);
ribbonBand.revalidate();
}
if ((oldListener != null) && (newListener != null)) {
// need to reconfigure
expandCommand.setAction(newListener);
}
}
if ("componentOrientation".equals(propertyChangeEvent.getPropertyName())) {
if (expandButton != null) {
syncExpandButtonIcon();
}
}
};
this.ribbonBand.addPropertyChangeListener(this.propertyChangeListener);
}
/**
* Uninstalls default parameters from the associated ribbon band.
*/
protected void uninstallDefaults() {
LookAndFeel.uninstallBorder(this.ribbonBand);
}
/**
* Uninstalls components from the associated ribbon band.
*/
protected void uninstallComponents() {
if (this.collapsedButton.isVisible()) {
// restore the control panel to the ribbon band.
CollapsedButtonPopupPanel popupPanel = (collapsedButton.getPopupCallback() == null)
? null
: (CollapsedButtonPopupPanel) collapsedButton.getPopupCallback()
.getPopupPanel(collapsedButton);
if (popupPanel != null) {
AbstractRibbonBand bandFromPopup = (AbstractRibbonBand) popupPanel
.removeComponent();
ribbonBand.setControlPanel(bandFromPopup.getControlPanel());
ribbonBand.setPopupRibbonBand(null);
collapsedButton.setPopupCallback(null);
}
}
this.ribbonBand.remove(this.collapsedButton);
this.collapsedButton = null;
if (this.expandButton != null)
this.ribbonBand.remove(this.expandButton);
this.expandButton = null;
this.ribbonBand = null;
}
/**
* Uninstalls listeners from the associated ribbon band.
*/
protected void uninstallListeners() {
this.ribbonBand.removePropertyChangeListener(this.propertyChangeListener);
this.propertyChangeListener = null;
this.ribbonBand.removeMouseListener(this.mouseListener);
this.mouseListener = null;
}
/**
* Invoked by installUI
to create a layout manager object to manage the
* ribbon band.
*
* @return a layout manager object
*/
protected LayoutManager createLayoutManager() {
return new RibbonBandLayout();
}
/**
* Layout for the ribbon band.
*
* @author Kirill Grouchnikov
*/
private class RibbonBandLayout implements LayoutManager {
@Override
public void addLayoutComponent(String name, Component c) {
}
@Override
public void removeLayoutComponent(Component c) {
}
@Override
public Dimension preferredLayoutSize(Container c) {
Insets ins = c.getInsets();
AbstractBandControlPanel controlPanel = ribbonBand.getControlPanel();
boolean useCollapsedButton = (controlPanel == null) || !controlPanel.isVisible();
int width = useCollapsedButton ? collapsedButton.getPreferredSize().width
: controlPanel.getPreferredSize().width;
int height = (useCollapsedButton ? collapsedButton.getPreferredSize().height
:
controlPanel.getPreferredSize().height) + getBandTitleHeight();
// System.out.println(ribbonBand.getTitle() + ":" + height);
// Preferred height of the ribbon band is:
// 1. Insets on top and bottom
// 2. Preferred height of the control panel
// 3. Preferred height of the band title panel
// System.out.println("Ribbon band pref = "
// + (height + ins.top + ins.bottom));
int extraTop = RadianceSizeUtils.getDefaultBorderInsets(
RadianceSizeUtils.getComponentFontSize(controlPanel)).top;
return new Dimension(width + ins.left + ins.right,
height + extraTop + ins.top + ins.bottom);
}
@Override
public Dimension minimumLayoutSize(Container c) {
Insets ins = c.getInsets();
AbstractBandControlPanel controlPanel = ribbonBand.getControlPanel();
boolean useCollapsedButton = (controlPanel == null) || (!controlPanel.isVisible());
int width = useCollapsedButton ? collapsedButton.getMinimumSize().width
: controlPanel.getMinimumSize().width;
int height = useCollapsedButton
? collapsedButton.getMinimumSize().height + getBandTitleHeight()
: controlPanel.getMinimumSize().height + getBandTitleHeight();
// System.out.println(useCollapsedButton + ":" + height);
// Preferred height of the ribbon band is:
// 1. Insets on top and bottom
// 2. Preferred height of the control panel
// 3. Preferred height of the band title panel
int extraTop = RadianceSizeUtils.getDefaultBorderInsets(
RadianceSizeUtils.getComponentFontSize(controlPanel)).top;
return new Dimension(width + ins.left + ins.right,
height + extraTop + ins.top + ins.bottom);
}
@Override
public void layoutContainer(Container c) {
// System.out.println("Ribbon band real = " + c.getHeight());
if (!c.isVisible())
return;
Insets ins = c.getInsets();
int extraTop = RadianceSizeUtils.getDefaultBorderInsets(
RadianceSizeUtils.getComponentFontSize(ribbonBand.getControlPanel())).top;
int availableHeight = c.getHeight() - extraTop - ins.top - ins.bottom;
RibbonBandResizePolicy resizePolicy = ((AbstractRibbonBand) c).getCurrentResizePolicy();
if (resizePolicy instanceof CoreRibbonResizePolicies.IconRibbonBandResizePolicy) {
collapsedButton.setVisible(true);
int collapsedButtonWidth = c.getWidth() - ins.left - ins.right - 2;
collapsedButton.setBounds((c.getWidth() - collapsedButtonWidth) / 2,
extraTop + ins.top,
collapsedButtonWidth, c.getHeight() - extraTop - ins.top - ins.bottom);
if (collapsedButton.getPopupCallback() == null) {
final AbstractRibbonBand popupBand = ribbonBand.cloneBand();
popupBand.setControlPanel(ribbonBand.getControlPanel());
List resizePolicies = ribbonBand.getResizePolicies();
popupBand.setResizePolicies(resizePolicies);
RibbonBandResizePolicy largest = resizePolicies.get(0);
popupBand.setCurrentResizePolicy(largest);
int gap = popupBand.getControlPanel().getUI().getLayoutGap();
final Dimension size = new Dimension(
ins.left + ins.right + gap
+ largest.getPreferredWidth(availableHeight, gap),
ins.top + ins.bottom
+ Math.max(c.getHeight(),
ribbonBand.getControlPanel().getPreferredSize().height
+ getBandTitleHeight()));
collapsedButton.setPopupCallback(commandButton -> new CollapsedButtonPopupPanel(popupBand, size));
ribbonBand.setControlPanel(null);
ribbonBand.setPopupRibbonBand(popupBand);
}
if (expandButton != null) {
expandButton.setBounds(0, 0, 0, 0);
}
return;
}
if (collapsedButton.isVisible()) {
// was icon and now is normal band - have to restore the control panel
CollapsedButtonPopupPanel popupPanel =
(collapsedButton.getPopupCallback() != null) ?
(CollapsedButtonPopupPanel) collapsedButton.getPopupCallback()
.getPopupPanel(collapsedButton)
: null;
if (popupPanel != null) {
AbstractRibbonBand bandFromPopup = (AbstractRibbonBand) popupPanel
.removeComponent();
ribbonBand.setControlPanel(bandFromPopup.getControlPanel());
ribbonBand.setPopupRibbonBand(null);
collapsedButton.setPopupCallback(null);
}
}
collapsedButton.setVisible(false);
AbstractBandControlPanel controlPanel = ribbonBand.getControlPanel();
controlPanel.setVisible(true);
controlPanel.setBounds(ins.left, extraTop + ins.top,
c.getWidth() - ins.left - ins.right,
c.getHeight() - getBandTitleHeight() - extraTop - ins.top - ins.bottom);
controlPanel.doLayout();
if (expandButton != null) {
int ebpw = expandButton.getPreferredSize().width;
int ebph = expandButton.getPreferredSize().height;
int maxHeight = getBandTitleHeight() - 4;
if (ebph > maxHeight)
ebph = maxHeight;
int expandButtonBottomY = c.getHeight() - (getBandTitleHeight() - ebph) / 2;
boolean ltr = ribbonBand.getComponentOrientation().isLeftToRight();
if (ltr) {
expandButton.setBounds(c.getWidth() - ins.right - ebpw - 1,
expandButtonBottomY - ebph, ebpw, ebph);
} else {
expandButton.setBounds(ins.left + 1, expandButtonBottomY - ebph, ebpw, ebph);
}
}
}
}
/**
* Event listener to handle global ribbon events. Currently handles:
*
* - Mouse wheel events anywhere in the ribbon to rotate the selected task.
*
*/
private static class AWTRibbonEventListener implements AWTEventListener {
private static AWTRibbonEventListener instance;
private int installCount = 0;
public static void install() {
if (instance == null) {
instance = new AWTRibbonEventListener();
RadianceCoreUtilities.registerAWTEventListener(instance);
}
instance.installCount++;
}
public static boolean uninstall() {
if (instance != null) {
instance.installCount--;
if (instance.installCount == 0) {
RadianceCoreUtilities.unregisterAWTEventListener(instance);
instance = null;
}
return true;
}
return false;
}
public void eventDispatched(AWTEvent event) {
if (!(event instanceof MouseEvent)) {
return;
}
MouseEvent mouseEvent = (MouseEvent) event;
if (mouseEvent.getID() == MouseEvent.MOUSE_WHEEL) {
if (PopupPanelManager.defaultManager().getShownPath().size() > 0) {
return;
}
Object object = event.getSource();
if (!(object instanceof Component)) {
return;
}
Component component = (Component) object;
// get the deepest subcomponent at the event point
MouseWheelEvent mouseWheelEvent = (MouseWheelEvent) mouseEvent;
Component deepest = SwingUtilities.getDeepestComponentAt(component,
mouseWheelEvent.getX(), mouseWheelEvent.getY());
JRibbon ribbon = (JRibbon) SwingUtilities.getAncestorOfClass(JRibbon.class,
deepest);
if (ribbon != null) {
// if the mouse wheel scroll has happened inside a ribbon,
// ask the UI delegate to handle it
ribbon.getUI().handleMouseWheelEvent((MouseWheelEvent) mouseEvent);
mouseEvent.consume();
}
}
}
}
@Override
public void paint(Graphics g, JComponent c) {
Graphics2D graphics = (Graphics2D) g.create();
BackgroundPaintingUtils.update(graphics, c, false);
Insets ins = ribbonBand.getInsets();
this.paintBandTrailingSeparator(graphics,
new Rectangle(0, 0, c.getWidth(), c.getHeight()));
if (!(ribbonBand.getCurrentResizePolicy() instanceof CoreRibbonResizePolicies.IconRibbonBandResizePolicy)) {
String title = ribbonBand.getTitle();
int titleHeight = getBandTitleHeight();
int bandTitleTopY = c.getHeight() - titleHeight;
boolean ltr = ribbonBand.getComponentOrientation().isLeftToRight();
int titleWidth = c.getWidth() - 2 * ins.left - 2 * ins.right;
int titleX = 2 * ins.left;
if (expandButton != null) {
if (ltr) {
titleWidth = expandButton.getX() - 2 * ins.right - 2 * ins.left;
} else {
titleWidth = ribbonBand.getWidth() - expandButton.getX()
- expandButton.getWidth() - 2 * ins.right - 2 * ins.left;
titleX = expandButton.getX() + expandButton.getWidth() + 2 * ins.left;
}
}
this.paintBandTitle(graphics,
new Rectangle(titleX, bandTitleTopY, titleWidth, titleHeight), title);
}
graphics.dispose();
}
/**
* Paints band title pane.
*
* @param g Graphics context.
* @param titleRectangle Rectangle for the title pane.
* @param title Title string.
*/
protected abstract void paintBandTitle(Graphics g, Rectangle titleRectangle, String title);
/**
* Paints band background.
*
* @param graphics Graphics context.
* @param toFill Rectangle for the background.
*/
protected abstract void paintBandTrailingSeparator(Graphics graphics, Rectangle toFill);
@Override
public int getPreferredCollapsedWidth() {
// Don't let long ribbon band titles create collapsed buttons that are too wide
Dimension collapsedPreferredSize = this.collapsedButton.getPreferredSize();
return Math.min((int) (collapsedPreferredSize.height * 1.25),
collapsedPreferredSize.width + 2);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy