透支 Eclipse RCP 应用程序的主要 shell

Overdraw Eclipse RCP application's main shell

我一直在尝试 "overdraw" 我的 Eclipse RCP 应用程序的主要 window 以便在应用程序启用屏幕录制功能时显示红色 "recording" 边框。

private boolean isActive;

private final ProgressMonitorDialog monitor;

private PaintListener paintListener;
private final int recordingFrameThickness = 5;  

public boolean toggle() {
  isActive = !isActive;

  try {
    // New state
    if (isActive) {
      monitor.run(true, false, new BackupExecutionBeginRecording(Display.getCurrent()));
      addRecordingFrame(Display.getCurrent().getActiveShell());
    }
    else {
      monitor.run(true, false, new BackupExecutionAfterRecording());
      removeRecoringFrame(Display.getCurrent().getActiveShell());
    }
  }
  catch (InvocationTargetException e) {
    System.err.println("Couldn't start backup task. Error: " + e.getMessage());
  }
  catch (InterruptedException e) {
    System.err.println("Backup thread was interrupted. Error: " + e.getMessage());
  }

  return isActive;
}

private void addRecordingFrame(Shell shell) {
  paintListener = new PaintListener() {

    @Override
    public void paintControl(PaintEvent e) {
      Rectangle clientArea = shell.getClientArea();
      e.gc.setLineWidth(recordingFrameThickness);
      e.gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
      e.gc.drawRectangle(clientArea);
    }
  };

  shell.addPaintListener(paintListener);
}

private void removeRecoringFrame(Shell shell) {
  shell.removePaintListener(paintListener);
}

如您所见,我采用的方法是等到应用程序 main window 绘制完成后添加一个矩形。理论上,每次 window 大小或位置发生变化后,覆盖的红框应该重新渲染。但似乎应用程序的主要 shell.

没有调用 PaintEvent

是否有其他方法可以在应用程序的现有布局上绘制内容,而不会阻止与下方控件的交互?

Eclipse 平台通过在 overlay shell 上创建和绘制来做到这一点,该 shell 小心地放置在基础 shell 之上。覆盖层是用 SWT.NO_TRIM | SWT.ON_TOP 创建的,其位置跟踪基础 shell。有关示例,请参见 e4 Overlay

使用下面的解决方案,我能够在 shell 的主要 shell 周围绘制一个红色边框,"follows" shell 的任何重新定位和调整大小 activity。一个很大的缺点仍然存在:红色边框似乎有一个外边框(由区域创建?),它与用于调整 shell 大小的手柄重叠。因此,只有在不显示红色边框时才能调整 shell 的大小(等于 shell 没有焦点)。

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Region;
import org.eclipse.swt.widgets.Shell;

public class RecordingFrame {

    private Shell baseShell;
    private Shell overlayShell;

    private ControlListener controlListener;
    private ShellListener shellListener;
    private PaintListener paintListener;

    private Rectangle outerRect;
    private Rectangle innerRect;
    private Region region;

    private int lineWidth = 10;
    private Color color;

    public RecordingFrame(Shell baseShell) {
        this.baseShell = baseShell;

        // Set up new shell
        overlayShell = new Shell(baseShell, SWT.BORDER | SWT.NO_TRIM | SWT.ON_TOP);
        overlayShell.setVisible(true);

        // Initialize objects
        outerRect = new Rectangle(0, 0, 0, 0);
        innerRect = new Rectangle(0, 0, 0, 0);
        region = new Region();

        color = new Color(baseShell.getDisplay(), 255, 0, 0);

        // First drawing of frame
        redraw();

        // Configure listeners
        initListeners();

        // Add listeners to shells
        baseShell.addControlListener(controlListener);
        baseShell.addShellListener(shellListener);
        overlayShell.addPaintListener(paintListener);
    }

    public void dispose() {
      // Remove all listeners
      overlayShell.removePaintListener(paintListener);
        baseShell.removeControlListener(controlListener);
        baseShell.removeShellListener(shellListener);

        if (!overlayShell.isDisposed())
            overlayShell.dispose();
    }

    private void redraw() {

      // Get bounds of base shell
        overlayShell.setBounds(baseShell.getBounds());

        // Calculate outer rectangle
        outerRect.height = overlayShell.getBounds().height;
        outerRect.width = overlayShell.getBounds().width;
        outerRect.x = 0;
        outerRect.y = 0;

        // Calculate inner rectangle
        innerRect.height = outerRect.height - 2 * lineWidth;
        innerRect.width = outerRect.width - 2 * lineWidth;
        innerRect.x = 0 + lineWidth;
        innerRect.y = 0 + lineWidth;

        // Create a new region which is the outer shell minus the inner shell
        region = new Region();
        region.add(outerRect);
        region.subtract(innerRect);

        overlayShell.setRegion(region);
        region.dispose();

        // Draw rectangle with new GC
        GC gc = new GC(overlayShell);
        gc.setLineWidth(lineWidth);
        gc.setForeground(color);
        gc.drawRectangle(innerRect);
        gc.dispose();
    }

    private void initListeners() {
       controlListener = new ControlListener() {

          @Override
          public void controlResized(ControlEvent e) {
            redraw();
          }

          @Override
          public void controlMoved(ControlEvent e) {
            redraw();
          }
        };

        shellListener = new ShellListener() {

          @Override
          public void shellIconified(ShellEvent e) {
            overlayShell.setVisible(false);        
          }

          @Override
          public void shellDeiconified(ShellEvent e) {
            overlayShell.setVisible(true);                
          }

          @Override
          public void shellDeactivated(ShellEvent e) {
            overlayShell.setVisible(false);         
          }

          @Override
          public void shellClosed(ShellEvent e) {
            dispose();        
          }

          @Override
          public void shellActivated(ShellEvent e) {
            overlayShell.setVisible(true);          
          }
        };

        paintListener = new PaintListener() {

          @Override
          public void paintControl(PaintEvent e) {
            redraw();
          }
        };
    }
}