Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make SWT Tree column clip beginning of text

I have an SWT Tree (via a JFace TreeViewer), that displays items in columns, some of which have long strings. The end of the string is the meaningful bit for the user, not the start, so when the text is clipped I want the clipping to occur at the start of the string and not the end. Example:

The default behaviour for a cell containing something like: "This is a very long string that completely exceeds the bounds of the tree column" is:

|This is a very l...|

Where as I want:

|... the tree column|

EDIT:

I solved this with a custom PaintItem listener as described here: http://www.eclipse.org/articles/article.php?file=Article-CustomDrawingTableAndTreeItems/index.html

Came up with the following code (not quite perfect, some duplication & magic numbers in there):

    tree.addListener(SWT.EraseItem, new Listener()
    {
        public void handleEvent(Event event)
        {
            String text = ((TreeItem)event.item).getText(event.index);
            Point size = event.gc.textExtent(text);
            TreeColumn column = ((Tree)event.widget).getColumn(event.index);                
            int columnWidth = column.getWidth() - 10; /* magic number alert - the cells have some padding - must be a way of determining this... */ 
            if(size.x > columnWidth)
            {
                event.detail &= ~SWT.FOREGROUND;
            }
        }
    });

    tree.addListener(SWT.PaintItem, new Listener()
    {               
        @Override
        public void handleEvent(Event event)
        {   
            String text = ((TreeItem)event.item).getText(event.index);
            Point size = event.gc.textExtent(text);
            TreeColumn column = ((Tree)event.widget).getColumn(event.index);                
            int columnWidth = column.getWidth() - 10; /* magic number alert - the cells have some padding - must be a way of determining this... */ 
            if(size.x > columnWidth)
            {
                drawTextTail(event, text, columnWidth);
            }                               
        }

        private void drawTextTail(Event event, String text, int columnWidth)
        {
            String clippedText = "";
            int offset = text.length() - 1; 
            String nextClippedText = text.charAt(offset) + clippedText;
            while(fits(nextClippedText, columnWidth, event.gc))
            {
                clippedText = nextClippedText;
                offset--;
                nextClippedText = text.charAt(offset) + clippedText;
            }
            event.gc.drawText("..." + clippedText, 
                    event.x + 5, /* magic number alert - the cells have some padding - must be a way of determining this... */ 
                    event.y, false);
        }

        private boolean fits(String clippedText, int columnWidth, GC gc)
        {
            Point size = gc.textExtent("..." + clippedText);                        
            return size.x < columnWidth;
        }
    });
like image 358
Malcolm Smith Avatar asked Jan 27 '26 19:01

Malcolm Smith


1 Answers

Try this. The second column automatically crops the String to fit into the column.

public static void main(String[] args) {
    Display display = new Display();
    final Shell shell = new Shell(display);
    shell.setLayout(new FillLayout());

    final Tree tree = new Tree(shell, SWT.NONE);
    tree.setLayout(new FillLayout());
    tree.setHeaderVisible(true);

    TreeColumn column = new TreeColumn(tree, SWT.NONE);
    column.pack();
    column.setWidth(100);

    TreeColumn column2 = new TreeColumn(tree, SWT.NONE);
    column2.pack();
    column2.setWidth(100);

    TreeItem item = new TreeItem(tree, SWT.NONE);
    item.setText(0, "Baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz");
    item.setText(1, "VERY VERY VERY VERY VERY VERY VERY VERY VERY VERY VERY long text");

    final TextLayout textLayout = new TextLayout(display);

    tree.addListener(SWT.PaintItem, new Listener() {
        public void handleEvent(Event event) {
            TreeItem item = (TreeItem) event.item;

            Point pt = new Point(event.x + 2, event.y + 2);

            for (int i = 0; i < tree.getColumnCount(); i++) {
                Rectangle rect = item.getBounds(i);

                String text = item.getText(i);

                if (rect.contains(pt)) {
                    String clippedText = "";
                    int offset = text.length() - 1; 
                    int columnWidth = tree.getColumn(i).getWidth();
                    String nextClippedText = text.charAt(offset) + clippedText;
                    while(fits(nextClippedText, columnWidth, event.gc) && offset >= 0)
                    {
                        clippedText = nextClippedText;
                        offset--;
                        if(offset >= 0)
                            nextClippedText = text.charAt(offset) + clippedText;
                    }

                    textLayout.setText(clippedText);

                    textLayout.draw(event.gc, event.x + 3, event.y + 3);
                }
            }
        }
    });
    tree.addListener(SWT.EraseItem, new Listener() {
        public void handleEvent(Event event) {
            /*
             * indicate that we'll be drawing the foreground in the
             * PaintItem listener
             */
            event.detail &= ~SWT.FOREGROUND;
        }
    });

    shell.pack();
    shell.open();
    while (!shell.isDisposed()) {
        if (!display.readAndDispatch())
            display.sleep();
    }
}

private static boolean fits(String clippedText, int columnWidth, GC gc)
{
    Point size = gc.textExtent("..." + clippedText);                        
    return size.x < columnWidth;
}

It might need some fixing to really show the ... in front of the cropped String, but it's a start.

like image 155
Baz Avatar answered Jan 30 '26 09:01

Baz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!