如何在SWT中绘制Composite的子元素?

我理解在绘制一个Composite时,你可以添加一个绘制侦听器,但这会导致在子绘图下绘制。 如果我想画出孩子的顶部怎么办?

下面绘制一条线,但是在它上面画了一个子线。

Composite c = new Composite(shell, 0); c.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_BLUE)); c.setBounds(100, 100, 800, 600); c.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { e.gc.drawLine(0, 0, 500, 1000); } }); final Composite subc = new Composite(c, 0); subc.setLayout(null); subc.setBounds(10, 10, 600, 400); 

不幸的是,这不是一个已实现的function,您可以在Bug#114749中看到。

您必须编写自己的自定义解决方案(例如,计算每个受影响的复合材料的绘图区域,然后在每个复合图上绘制)。

它是一个相当古老的错误,但如果你认为这是一个“必须拥有”,那么提升是一个好主意。

要解决这个问题并不困难:-)

您需要将SWT.Paint侦听器不仅添加到Composite ,还要添加到所有包含的子SWT.Paint (递归)。 然后诀窍是为每个控件适当地映射坐标……

为了说明,我附上了一些我在许多项目中使用的代码。 是的,我确实知道缺少课程,但你可以从中得到这个想法。

 /******************************************************************************* * Copyright (c) 2007, 2011 The RCP Company and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * The RCP Company - initial API and implementation *******************************************************************************/ package com.rcpcompany.uibindings.utils; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import com.rcpcompany.uibindings.IDisposable; import com.rcpcompany.uibindings.internal.utils.PaintDecorationManager; /** * Support for arbitrary decorations for {@link Control controls}. * 

*

* Differs from {@link ControlDecoration} in a number of ways: *

    *
  • Support for cells in tables
  • *
  • Vastly more efficient when there are many decorations
  • *
* * @author Tonny Madsen, The RCP Company */ public interface IPaintDecoration { /** * Factory for {@link IPaintDecoration}. */ final class Factory { private Factory() { } /** * Adds a new decoration. *

* The decoration is only added if the control of the decoration is non-null. *

* If the decoration supports the {@link IDisposable} interface, it will be notified when * the decoration is no longer in use - eg when the decoration is removed with * {@link #removeDecoration(IPaintDecoration)} or if the control is disposed. * * @param decoration the new decoration */ public static void addDecoration(IPaintDecoration decoration) { PaintDecorationManager.addDecoration(decoration); } public static IPaintDecoration paintRectangle(final Control c, Rectangle rect, final Color color) { final Rectangle r; if (rect == null) { final Point s = c.getSize(); r = new Rectangle(0, 0, sx, sy); } else { r = rect; } final IPaintDecoration pd = new IPaintDecoration() { @Override public void paint(Event event, Rectangle area) { // LogUtils.debug(this, event.widget + ": clip=" + event.gc.getClipping() + // " area=" + area); final Color oldForeground = event.gc.getForeground(); event.gc.setForeground(color); event.gc.drawRectangle(area.x, area.y, area.width - 1, area.height - 1); event.gc.setForeground(oldForeground); } @Override public Control getControl() { return c; } @Override public Rectangle getArea() { return r; } }; addDecoration(pd); return pd; } /** * Removes an existing decoration. * * @param decoration the decoration to remove */ public static void removeDecoration(IPaintDecoration decoration) { PaintDecorationManager.removeDecoration(decoration); } }; /** * The control of this decoration. *

* The control of a specific decoration may not change during the lifetime of the decoration. * * @return the control */ Control getControl(); /** * Returns the area of this decoration in relation to the control. * * @return the relative location */ Rectangle getArea(); /** * Paints the decoration. * * @param area TODO */ void paint(Event event, Rectangle area); }

 /******************************************************************************* * Copyright (c) 2007, 2011 The RCP Company and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * The RCP Company - initial API and implementation *******************************************************************************/ package com.rcpcompany.uibindings.internal.utils; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.jface.util.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import com.rcpcompany.uibindings.IDisposable; import com.rcpcompany.uibindings.internal.Activator; import com.rcpcompany.uibindings.utils.IPaintDecoration; import com.rcpcompany.utils.logging.LogUtils; /** * Simple manager that can draw arbitrary "stuff". * 

* A manager exists for each {@link Shell} of the application and is automatically disposed with the * shell. *

* Each decoration of the manager is handled internally via an {@link DecorationData} object. * * @author Tonny Madsen, The RCP Company */ public final class PaintDecorationManager implements IDisposable, Listener { /** * The shell of this manager. */ private final Shell myShell; /** * Cached platform flag for dealing with platform-specific issue: * https://bugs.eclipse.org/bugs/show_bug.cgi?id=219326 : Shell with custom region and * SWT.NO_TRIM still has border */ private static boolean MAC = Util.isMac(); /** * Constructs and returns a new manager. * * @param shell the shell of the manager */ private PaintDecorationManager(Shell shell) { myShell = shell; theManagers.put(getShell(), this); hookControl(getShell()); } @Override public void dispose() { /* * Unhook all controls. This is automatically remove all decorations. */ for (final Control c : myHookedControls.toArray(new Control[myHookedControls.size()])) { unhookControl(c); } theManagers.remove(getShell()); } public static void addDecoration(IPaintDecoration decoration) { final PaintDecorationManager mng = getManager(decoration); if (mng != null) { mng.addADecoration(decoration); } } public static void removeDecoration(IPaintDecoration decoration) { final PaintDecorationManager mng = getManager(decoration); if (mng != null) { mng.removeADecoration(decoration); } } /** * Mapping of all decorations of this manager to internal data for the same decoration. */ private final Map myDecorations = new HashMap(); public void addADecoration(IPaintDecoration decoration) { DecorationData dd = myDecorations.get(decoration); if (dd == null) { dd = new DecorationData(decoration); } dd.update(); } public void removeADecoration(IPaintDecoration decoration) { if (Activator.getDefault().TRACE_CONTROL_DECORATIONS) { LogUtils.debug(this, "control: " + decoration.getControl() + "@" + decoration.getControl().hashCode() + "/" + decoration.getArea()); } final DecorationData dd = myDecorations.get(decoration); if (dd == null) return; dd.dispose(); } /** * Map with all defined managers indexed by the shell. */ private static Map theManagers = new HashMap(); /** * Returns the shell of the manager. * * @return the shell */ private Shell getShell() { return myShell; } /** * Returns the manager for the specified decoration. *

* Creates a new manager if none exists * * @param decoration the decoration * @return the manager for the shell of the decoration */ private static PaintDecorationManager getManager(IPaintDecoration decoration) { final Control c = decoration.getControl(); if (c == null) return null; final Shell shell = c.getShell(); PaintDecorationManager mng = theManagers.get(shell); if (mng == null) { mng = new PaintDecorationManager(shell); } return mng; } /** * The hooked controls of this manager. *

* A control is hooked when first referred in a decoration or a parent... *

* It is not unhooked until the control or this manager is disposed. */ private final Set myHookedControls = new HashSet(); /** * Hooks the specified control into this manager. *

* Also hooks all parent controls. * * @param control the control */ public void hookControl(Control control) { if (myHookedControls.contains(control)) return; myHookedControls.add(control); control.addListener(SWT.Dispose, this); control.addListener(SWT.Paint, this); if (control != getShell()) { hookControl(control.getParent()); } } /** * Unhooks a specific control from the manager. * * @param control the control */ public void unhookControl(Control control) { if (!myHookedControls.contains(control)) return; myHookedControls.remove(control); if (!control.isDisposed()) { control.removeListener(SWT.Dispose, this); control.removeListener(SWT.Paint, this); } for (final DecorationData dd : myDecorations.values() .toArray(new DecorationData[myDecorations.values().size()])) { if (dd.getControl() == control) { dd.dispose(); } } } @Override public void handleEvent(Event event) { // LogUtils.debug(this, ToStringUtils.toString(event)); switch (event.type) { case SWT.Dispose: handleDispose(event); break; case SWT.Paint: handlePaint(event); break; default: break; } } /** * Handles the dispose event. * * @param event the event */ private void handleDispose(Event event) { if (event.widget == getShell()) { dispose(); return; } unhookControl((Control) event.widget); } /** * Handles the paint event. * * @param event the event */ private void handlePaint(Event event) { final Control c = (Control) event.widget; final Display display = c.getDisplay(); final Rectangle area = display.map(c, null, event.x, event.y, event.width, event.height); for (final DecorationData dd : myDecorations.values()) { if (dd.intersects(area)) { dd.paint(event); } } } /** * Manager internal decoration data for one decoration. */ protected class DecorationData implements IDisposable { private final IPaintDecoration myDecoration; /** * The previous area painted by this decoration relative to the display. */ private Rectangle myPreviousArea = null; /** * Set to true when the decoration is disposed */ private boolean isDisposed = false; /** * Constructs and returns a new decoration data object * * @param decoration he base decoration */ protected DecorationData(IPaintDecoration decoration) { myDecoration = decoration; myDecorations.put(getDecoration(), this); if (Activator.getDefault().TRACE_CONTROL_DECORATIONS) { LogUtils.debug(this, "control: " + this); } getManager().hookControl(getDecoration().getControl()); } /** * Returns the control of the decoration * * @return the control */ public Control getControl() { return getDecoration().getControl(); } @Override public void dispose() { isDisposed = true; update(); myDecorations.remove(getDecoration()); if (Activator.getDefault().TRACE_CONTROL_DECORATIONS) { LogUtils.debug(this, "control: " + this); } } /** * Returns the manager of this decoration * * @return the manager */ public PaintDecorationManager getManager() { return PaintDecorationManager.this; } /** * Returns the decoration itself * * @return the decoration */ public IPaintDecoration getDecoration() { return myDecoration; } /** * Updates this decoration */ public void update() { if (Activator.getDefault().TRACE_CONTROL_DECORATIONS) { LogUtils.debug(this, "control: " + this); } /* * Calculate new area */ final Rectangle newArea = getDecorationRectangle(getShell()); /* * Compare with last area and image */ if ((newArea == null ? myPreviousArea == null : newArea.equals(myPreviousArea))) { if (Activator.getDefault().TRACE_CONTROL_DECORATIONS_VERBOSE) { LogUtils.debug(this, "-- return"); } return; } Rectangle r = null; if (myPreviousArea != null) { r = myPreviousArea; if (newArea != null) { r.add(newArea); } } else { r = newArea; } myPreviousArea = newArea; if (r != null) { // LogUtils.debug(this, "redraw: " + r); getShell().redraw(rx, ry, r.width, r.height, true); if (Activator.getDefault().TRACE_CONTROL_DECORATIONS_VERBOSE) { LogUtils.debug(this, "redraw " + r); } } } /** * Calculates the area taken by the image translated to a specified target control * * @param c the target control or null for the Display * * @return the {@link Rectangle} for the image or null if no image is specified */ private Rectangle getDecorationRectangle(Control c) { final Control control = getDecoration().getControl(); final Rectangle b = getDecoration().getArea(); final Rectangle bounds = new Rectangle(bx, by, b.width + 1, b.height + 1); return getShell().getDisplay().map(control, c, bounds); } /** * Paints this decoration. * * @param event the {@link SWT#Paint} event */ public void paint(Event event) { if (Activator.getDefault().TRACE_CONTROL_DECORATIONS_VERBOSE) { LogUtils.debug(this, "paint: " + event.widget); } // if (!shouldShowDecoration()) { // return; // } final Rectangle rect = getDecorationRectangle((Control) event.widget); if (Activator.getDefault().TRACE_CONTROL_DECORATIONS_VERBOSE) { LogUtils.debug(this, "paint: " + event.widget + "/" + event.widget.hashCode() + ": rect=" + rect); } getDecoration().paint(event, rect); } /** * Returns whether this decoration intersects with specified rectangle. * * @param eventArea the area to check * @return true if the decoration is visible and the area intersects */ public boolean intersects(Rectangle eventArea) { if (isDisposed) return false; if (!getControl().isVisible()) return false; final Rectangle area = getDecorationRectangle(null); if (area == null) return false; if (!area.intersects(eventArea)) return false; return true; } @Override public String toString() { return getControl() + "@" + getControl().hashCode() + " " + getDecoration().getArea() + " area " + getDecorationRectangle(null); } } }