blob: 229afa73571754264c3c9af6d0c17e84c55b1573 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ecommons.ui.components;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MenuListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
import org.eclipse.statet.ecommons.ui.util.MenuUtils;
import org.eclipse.statet.internal.ecommons.ui.AccessibleArrowImage;
import org.eclipse.statet.internal.ecommons.ui.UIMiscellanyPlugin;
public class DropDownButton extends Composite {
private Button mainButton;
private Button downButton;
private Image image;
private Image disabledImage;
private Point imageButtonDefaultSize;
private String arrowText;
private Menu menu;
private final CopyOnWriteIdentityListSet<MenuListener> menuListener= new CopyOnWriteIdentityListSet<>();
public DropDownButton(final Composite parent) {
this(parent, SWT.NONE);
}
/**
* Creates a new drop down button.
*
* SWT#SINGLE for a single button (shows always the menu, no default action).
*
* @param parent
* @param style SWT#SINGLE, other styles for buttons
*/
public DropDownButton(final Composite parent, final int style) {
super(parent, SWT.NONE);
create(style);
}
private void create(final int style) {
final boolean single= ((style & SWT.SINGLE) != 0);
final int buttonStyle= (style & ~SWT.SINGLE);
this.downButton= new Button(this, SWT.PUSH | buttonStyle);
final AccessibleArrowImage imageDescriptor= new AccessibleArrowImage(SWT.DOWN, SWT.DEFAULT,
this.downButton.getForeground().getRGB(), this.downButton.getBackground().getRGB() );
this.image= UIMiscellanyPlugin.getInstance().getImageDescriptorRegistry().get(imageDescriptor);
updateSizes();
this.downButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final Menu menu= getDropDownMenu();
MenuUtils.setPullDownPosition(menu, DropDownButton.this);
menu.setVisible(true);
}
});
if (single) {
setTabList(new Control[] { this.downButton });
}
else {
this.mainButton= new Button(this, SWT.PUSH | buttonStyle);
setTabList(new Control[] { this.mainButton, this.downButton });
}
final Listener listener= new Listener() {
@Override
public void handleEvent(final Event event) {
switch (event.type) {
case SWT.Dispose:
doDispose();
return;
case SWT.Resize:
updateBounds(event);
return;
case SWT.Paint:
paintButton(event);
return;
}
}
};
addListener(SWT.Dispose, listener);
addListener(SWT.Resize, listener);
this.downButton.addListener(SWT.Paint, listener);
}
protected void doDispose() {
if (this.disabledImage != null) {
this.disabledImage.dispose();
this.disabledImage= null;
}
if (this.menu != null) {
this.menu.dispose();
this.menu= null;
}
}
private void updateSizes() {
{ final Button button= new Button(this, SWT.PUSH);
button.setImage(this.image);
button.setFont(getFont());
this.imageButtonDefaultSize= button.computeSize(SWT.DEFAULT, SWT.DEFAULT);
button.dispose();
}
if (this.mainButton == null) {
final int imageWidth= this.image.getBounds().width;
final int requiredWidth= imageWidth
+ (this.imageButtonDefaultSize.x - imageWidth + 1) / 2;
final GC gc= new GC(this);
String text= " ";
for (int i= 1; i < 10; i++) {
if (gc.stringExtent(text).x >= requiredWidth) {
break;
}
text+= " "; //$NON-NLS-1$
}
this.arrowText= text;
}
}
private void updateBounds(final Event event) {
final Rectangle clientArea= getClientArea();
if (this.mainButton != null) {
final int downButtonWidth= this.imageButtonDefaultSize.x;
this.mainButton.setBounds(
clientArea.x,
clientArea.y,
clientArea.width - downButtonWidth + 1,
clientArea.height);
this.downButton.setBounds(
clientArea.x + clientArea.width - downButtonWidth,
clientArea.y,
downButtonWidth,
clientArea.height );
}
else {
this.downButton.setBounds(clientArea);
}
}
@Override
public Point computeSize(final int wHint, final int hHint, final boolean changed) {
int width;
int height;
if (this.mainButton != null) {
final Point downSize= this.downButton.computeSize(this.imageButtonDefaultSize.x, hHint);
final Point mainSize= (wHint == SWT.DEFAULT) ?
this.mainButton.computeSize(SWT.DEFAULT, hHint) :
this.mainButton.computeSize(Math.max(0, wHint - downSize.x), hHint);
width= mainSize.x + downSize.x - 1;
height= Math.max(mainSize.y, downSize.y);
}
else {
final Point downSize= this.downButton.computeSize(wHint, hHint);
width= downSize.x;
height= downSize.y;
}
final Rectangle trim= super.computeTrim(0, 0, width, height);
return new Point(trim.width, trim.height);
}
private Image getDisabledImage() {
if (this.disabledImage == null) {
this.disabledImage= new Image(this.image.getDevice(), this.image, SWT.IMAGE_DISABLE);
}
return this.disabledImage;
}
private void paintButton(final Event event) {
final Point buttonSize= this.downButton.getSize();
final Image image= (this.downButton.isEnabled()) ? this.image : getDisabledImage();
event.gc.drawImage(image,
(this.mainButton != null) ?
(buttonSize.x - image.getBounds().width) / 2 :
buttonSize.x - this.imageButtonDefaultSize.x
+ (this.imageButtonDefaultSize.x - image.getBounds().width) / 2,
(buttonSize.y - image.getBounds().height) / 2 );
}
@Override
public void setFont(final Font font) {
super.setFont(font);
updateSizes();
}
@Override
public void setEnabled(final boolean enabled) {
if (this.mainButton != null) {
this.mainButton.setEnabled(enabled);
}
this.downButton.setEnabled(enabled);
}
public void setText(final String string) {
if (this.mainButton != null) {
this.mainButton.setText(string);
}
else {
this.downButton.setText(string + this.arrowText);
}
}
@Override
public void setToolTipText(final String string) {
if (this.mainButton != null) {
this.mainButton.setToolTipText(string);
}
else {
this.downButton.setToolTipText(string);
}
}
public void setOptionToolTipText(final String string) {
this.downButton.setToolTipText(string);
}
public void addSelectionListener(final SelectionListener listener) {
this.mainButton.addSelectionListener(listener);
}
public void removeSelectionListener(final SelectionListener listener) {
this.mainButton.removeSelectionListener(listener);
}
public void addMenuListener(final MenuListener listener) {
this.menuListener.add(listener);
if (this.menu != null) {
this.menuListener.add(listener);
}
}
public void removeMenuListener(final MenuListener listener) {
this.menuListener.remove(listener);
if (this.menu != null) {
this.menuListener.remove(listener);
}
}
public Menu getDropDownMenu() {
Menu menu= this.menu;
if (menu == null) {
menu= createDropDownMenu();
final Listener listener= new Listener() {
@Override
public void handleEvent(final Event event) {
switch (event.type) {
case SWT.Dispose:
if (DropDownButton.this.menu == event.widget) {
DropDownButton.this.menu= null;
}
return;
default:
return;
}
}
};
menu.addListener(SWT.Dispose, listener);
for (final MenuListener menuListener : this.menuListener) {
menu.addMenuListener(menuListener);
}
this.menu= menu;
}
return menu;
}
protected Menu createDropDownMenu() {
return new Menu(this);
}
}