How to align image to center of table cell (SWT Ta

2019-01-19 18:05发布

问题:

I develop Eclipse RCP application and got a problem with a Table. We have some data in database in boolean format and users wants to see that field using checkbox.

I tried to implement it using Button(SWT.CHECK) as Table-Editor, but it worked too slow :(

I tried to use 2 images - checked and unchecked check-boxes, it works, but I can't align them to center, they are aligned to left automatically.

I even found how to catch SWT.MeasureItem and SWT.PaintItem events and process them manually by change event.x field, but I got a problem - I can't get what column measuring or painting at the moment, because Event doesn't provides me that information.

Is it the only way to align images to center by modify event data on redrawing, or may be there is other ways to represent boolean data via check-boxes? I don't need to edit them now, so read-only mode should be enough.

回答1:

You may add PaintListener to your table and when it will paint selected column (column 5 in my case), check the size of row and align the image by yourself..

testTable.addListener(SWT.PaintItem, new Listener() {

    @Override
    public void handleEvent(Event event) {
        // Am I on collumn I need..?
        if(event.index == 5) {
            Image tmpImage = IMAGE_TEST_PASS;
            int tmpWidth = 0;
            int tmpHeight = 0;
            int tmpX = 0;
            int tmpY = 0;

            tmpWidth = testTable.getColumn(event.index).getWidth();
            tmpHeight = ((TableItem)event.item).getBounds().height;

            tmpX = tmpImage.getBounds().width;
            tmpX = (tmpWidth / 2 - tmpX / 2);
            tmpY = tmpImage.getBounds().height;
            tmpY = (tmpHeight / 2 - tmpY / 2);
            if(tmpX <= 0) tmpX = event.x;
            else tmpX += event.x;
            if(tmpY <= 0) tmpY = event.y;
            else tmpY += event.y;
            event.gc.drawImage(tmpImage, tmpX, tmpY);
        }
    }
});


回答2:

Here is the example using OwnerDrawLabelProvider: http://bingjava.appspot.com/snippet.jsp?id=3221

I combined it with Tonny's link to TableViewers and Nativelooking Checkboxes and created handy abstract CenteredCheckboxLabelProvider class

import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.OwnerDrawLabelProvider;
import org.eclipse.swt.SWT;
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.Event;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TableItem;

public abstract class CenteredCheckboxLabelProvider extends OwnerDrawLabelProvider {
    private static final String CHECKED_KEY = "CHECKED";
    private static final String UNCHECK_KEY = "UNCHECKED";

    public CenteredCheckboxLabelProvider(ColumnViewer viewer) {
        if (JFaceResources.getImageRegistry().getDescriptor(CHECKED_KEY) == null) {
            JFaceResources.getImageRegistry().put(UNCHECK_KEY, makeShot(viewer.getControl().getShell(), false));
            JFaceResources.getImageRegistry().put(CHECKED_KEY, makeShot(viewer.getControl().getShell(), true));
        }
    }

    private Image makeShot(Shell shell, boolean type) {
        Shell s = new Shell(shell, SWT.NO_TRIM);
        Button b = new Button(s, SWT.CHECK);
        b.setSelection(type);
        Point bsize = b.computeSize(SWT.DEFAULT, SWT.DEFAULT);
        b.setSize(bsize);
        b.setLocation(0, 0);
        s.setSize(bsize);
        s.open();

        GC gc = new GC(b);
        Image image = new Image(shell.getDisplay(), bsize.x, bsize.y);
        gc.copyArea(image, 0, 0);
        gc.dispose();

        s.close();

        return image;
    }

    public Image getImage(Object element) {
        if (isChecked(element)) {
            return JFaceResources.getImageRegistry().get(CHECKED_KEY);
        } else {
            return JFaceResources.getImageRegistry().get(UNCHECK_KEY);
        }
    }

    @Override
    protected void measure(Event event, Object element) {
    }

    @Override
    protected void paint(Event event, Object element) {
        Image img = getImage(element);

        if (img != null) {
            Rectangle bounds = ((TableItem) event.item).getBounds(event.index);
            Rectangle imgBounds = img.getBounds();
            bounds.width /= 2;
            bounds.width -= imgBounds.width / 2;
            bounds.height /= 2;
            bounds.height -= imgBounds.height / 2;

            int x = bounds.width > 0 ? bounds.x + bounds.width : bounds.x;
            int y = bounds.height > 0 ? bounds.y + bounds.height : bounds.y;

            event.gc.drawImage(img, x, y);
        }
    }

    protected abstract boolean isChecked(Object element);
}


回答3:

You might also want to have a look at this blog entry: TableViewers and Nativelooking Checkboxes.



回答4:

Sorceror's solution came in handy. I've adapted it to a custom IStyledLabelProvider to be used within a DelegatingStyledCellLabelProvider:

public class MutedLabelProvider extends LabelProvider implements IStyledLabelProvider {

  public MutedLabelProvider(Tree containerTree, int columnIndex) {
    registerIconListener(containerTree, columnIndex);
  }

  @Override
  public StyledString getStyledText(Object element) {
    return new StyledString();
  }

  private void registerIconListener(Tree containerTree, int columnIndex) {
    containerTree.addListener(SWT.PaintItem, new Listener() {
      @Override
      public void handleEvent(Event event) {
        if (event.index == columnIndex) {
          TreeItem treeItem = (TreeItem) event.item;
          Object data = treeItem.getData();
          Image tmpImage = getImageFromData(data);
          if (tmpImage != null) {
            int tmpWidth = 0;
            int tmpHeight = 0;
            int tmpX = 0;
            int tmpY = 0;
            tmpWidth = containerTree.getColumn(event.index).getWidth();
            tmpHeight = treeItem.getBounds().height;
            tmpX = tmpImage.getBounds().width;
            tmpX = (tmpWidth / 2 - tmpX / 2);
            tmpY = tmpImage.getBounds().height;
            tmpY = (tmpHeight / 2 - tmpY / 2);
            if (tmpX <= 0) tmpX = event.x;
            else tmpX += event.x;
            if (tmpY <= 0) tmpY = event.y;
            else tmpY += event.y;
            event.gc.drawImage(tmpImage, tmpX, tmpY);
          }
        }
      }
    });
  }

  private Image getImageFromData(Object element) {
    if (element != null && element instanceof IMarker) {
      IMarker marker = (IMarker) element;
      boolean isItTrue = marker.getAttribute("MyBooleanAttr", false);
      if (isItTrue) {
        // TODO return here the image you want for true state
        return JFaceResources.getImageRegistry().get("MyImageForTrue");
      } else {
        // TODO return here the image you want for false state
        // Alternatively, you can return null for no image
        return null;
      }
    } else {
      return null;
    }
  }

}

Note that I'm using IMarker as data for elements, and a TreeViewer (so I'm receiving TreeItems), but your data might differ, and your viewer might be a TableViewer; you just have to make your own adjustments.