我正在寻找一种在我的应用程序中为某些复合材料添加叠加层的方法。叠加层将包含带有文字的标签"没有可用的数据"。需要显示底层复合,但用户无法执行任何操作。我的应用程序在一个屏幕中包含不同的复合部件,所以我需要一种方法只将叠加层放在其中一个复合材料上。有没有办法在SWT中实现这个?
答案 0 :(得分:5)
一种可能的解决方案是在您要覆盖的Shell
上放置一个半透明的Composite
,而不会有任何修剪。
棘手的部分是更新叠加层Shell
以不断匹配Composite
及其父级的大小,位置和可见性(因为它们也可能影响子边界和可见性)。
所以我决定尝试上课Overlay
来做那件事;它可用于覆盖任何Control
,它使用控件和绘制侦听器来跟踪和匹配基础Control
。这些侦听器也附加到Control
的父级的整个层次结构中。
您可以使用相应的方法在Overlay
上设置颜色,透明度和文字。
我做了一些简单的测试,它似乎工作正常,但我无法保证任何事情。你可能想尝试一下。
使用它的一个简单示例:
public class OverlayTest {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout(SWT.VERTICAL));
shell.setSize(250, 250);
// create the composite
Composite composite = new Composite(shell, SWT.NONE);
composite.setLayout(new FillLayout(SWT.VERTICAL));
// add stuff to the composite
for (int i = 0; i < 5; i++) {
new Text(composite, SWT.BORDER).setText("Text " + i);
}
// create the overlay over the composite
Overlay overlay = new Overlay(composite);
overlay.setText("No data available");
// create the button to show/hide the overlay
Button button = new Button(shell, SWT.PUSH);
button.setText("Show/hide overlay");
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent arg0) {
// if the overlay is showing we hide it, otherwise we show it
if (overlay.isShowing()) {
overlay.remove();
}
else {
overlay.show();
}
}
});
shell.open();
while (shell != null && !shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
Overlay
类:
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Scrollable;
import org.eclipse.swt.widgets.Shell;
/**
* A customizable overlay over a control.
*
* @author Loris Securo
*/
public class Overlay {
private List<Composite> parents;
private Control objectToOverlay;
private Shell overlay;
private Label label;
private ControlListener controlListener;
private DisposeListener disposeListener;
private PaintListener paintListener;
private boolean showing;
private boolean hasClientArea;
private Scrollable scrollableToOverlay;
public Overlay(Control objectToOverlay) {
Objects.requireNonNull(objectToOverlay);
this.objectToOverlay = objectToOverlay;
// if the object to overlay is an instance of Scrollable (e.g. Shell) then it has
// the getClientArea method, which is preferable over Control.getSize
if (objectToOverlay instanceof Scrollable) {
hasClientArea = true;
scrollableToOverlay = (Scrollable) objectToOverlay;
}
else {
hasClientArea = false;
scrollableToOverlay = null;
}
// save the parents of the object, so we can add/remove listeners to them
parents = new ArrayList<Composite>();
Composite parent = objectToOverlay.getParent();
while (parent != null) {
parents.add(parent);
parent = parent.getParent();
}
// listener to track position and size changes in order to modify the overlay bounds as well
controlListener = new ControlListener() {
@Override
public void controlMoved(ControlEvent e) {
reposition();
}
@Override
public void controlResized(ControlEvent e) {
reposition();
}
};
// listener to track paint changes, like when the object or its parents become not visible (for example changing tab in a TabFolder)
paintListener = new PaintListener() {
@Override
public void paintControl(PaintEvent arg0) {
reposition();
}
};
// listener to remove the overlay if the object to overlay is disposed
disposeListener = new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
remove();
}
};
// create the overlay shell
overlay = new Shell(objectToOverlay.getShell(), SWT.NO_TRIM);
// default values of the overlay
overlay.setBackground(objectToOverlay.getDisplay().getSystemColor(SWT.COLOR_GRAY));
overlay.setAlpha(200);
// so the label can inherit the background of the overlay
overlay.setBackgroundMode(SWT.INHERIT_DEFAULT);
// label to display a text
// style WRAP so if it is too long the text get wrapped
label = new Label(overlay, SWT.WRAP);
// to center the label
overlay.setLayout(new GridLayout());
label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
showing = false;
overlay.open();
overlay.setVisible(showing);
}
public void show() {
// if it's already visible we just exit
if (showing) {
return;
}
// set the overlay position over the object
reposition();
// show the overlay
overlay.setVisible(true);
// add listeners to the object to overlay
objectToOverlay.addControlListener(controlListener);
objectToOverlay.addDisposeListener(disposeListener);
objectToOverlay.addPaintListener(paintListener);
// add listeners also to the parents because if they change then also the visibility of our object could change
for (Composite parent : parents) {
parent.addControlListener(controlListener);
parent.addPaintListener(paintListener);
}
showing = true;
}
public void remove() {
// if it's already not visible we just exit
if (!showing) {
return;
}
// remove the listeners
if (!objectToOverlay.isDisposed()) {
objectToOverlay.removeControlListener(controlListener);
objectToOverlay.removeDisposeListener(disposeListener);
objectToOverlay.removePaintListener(paintListener);
}
// remove the parents listeners
for (Composite parent : parents) {
if (!parent.isDisposed()) {
parent.removeControlListener(controlListener);
parent.removePaintListener(paintListener);
}
}
// remove the overlay shell
if (!overlay.isDisposed()) {
overlay.setVisible(false);
}
showing = false;
}
public void setBackground(Color background) {
overlay.setBackground(background);
}
public Color getBackground() {
return overlay.getBackground();
}
public void setAlpha(int alpha) {
overlay.setAlpha(alpha);
}
public int getAlpha() {
return overlay.getAlpha();
}
public boolean isShowing() {
return showing;
}
public void setText(String text) {
label.setText(text);
// to adjust the label size accordingly
overlay.layout();
}
public String getText() {
return label.getText();
}
private void reposition() {
// if the object is not visible, we hide the overlay and exit
if (!objectToOverlay.isVisible()) {
overlay.setBounds(new Rectangle(0, 0, 0, 0));
return;
}
// if the object is visible we need to find the visible region in order to correctly place the overlay
// get the display bounds of the object to overlay
Point objectToOverlayDisplayLocation = objectToOverlay.toDisplay(0, 0);
Point objectToOverlaySize;
// if it has a client area, we prefer that instead of the size
if (hasClientArea) {
Rectangle clientArea = scrollableToOverlay.getClientArea();
objectToOverlaySize = new Point(clientArea.width, clientArea.height);
}
else {
objectToOverlaySize = objectToOverlay.getSize();
}
Rectangle objectToOverlayBounds = new Rectangle(objectToOverlayDisplayLocation.x, objectToOverlayDisplayLocation.y, objectToOverlaySize.x,
objectToOverlaySize.y);
Rectangle intersection = objectToOverlayBounds;
// intersect the bounds of the object with its parents bounds so we get only the visible bounds
for (Composite parent : parents) {
Rectangle parentClientArea = parent.getClientArea();
Point parentLocation = parent.toDisplay(parentClientArea.x, parentClientArea.y);
Rectangle parentBounds = new Rectangle(parentLocation.x, parentLocation.y, parentClientArea.width, parentClientArea.height);
intersection = intersection.intersection(parentBounds);
// if intersection has no size then it would be a waste of time to continue
if (intersection.width == 0 || intersection.height == 0) {
break;
}
}
overlay.setBounds(intersection);
}
}