| /******************************************************************************* |
| * Copyright (c) 2018 Red Hat and others. All rights reserved. |
| * The contents of this file are made available under the terms |
| * of the GNU Lesser General Public License (LGPL) Version 2.1 that |
| * accompanies this distribution (lgpl-v21.txt). The LGPL is also |
| * available at http://www.gnu.org/licenses/lgpl.html. If the version |
| * of the LGPL at http://www.gnu.org is different to the version of |
| * the LGPL accompanying this distribution and there is any conflict |
| * between the two license versions, the terms of the LGPL accompanying |
| * this distribution shall govern. |
| * |
| * Contributors: |
| * Red Hat - initial API and implementation |
| */ |
| package org.eclipse.swt.tests.manualJUnit; |
| |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.function.BiConsumer; |
| import java.util.function.Consumer; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.ControlAdapter; |
| import org.eclipse.swt.events.ControlEvent; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.layout.FillLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeColumn; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.junit.FixMethodOrder; |
| import org.junit.Test; |
| import org.junit.runners.MethodSorters; |
| |
| /** Convienience class for easy copy & paste */ |
| @FixMethodOrder(MethodSorters.JVM) |
| public class MJ_Tree extends MJ_root { |
| |
| |
| // Shared elements: |
| final Listener ownerDrawnListener = event -> { |
| final TreeItem item = (TreeItem)event.item; |
| Tree tree = item.getParent(); |
| AtomicBoolean packpending = (AtomicBoolean) tree.getData(); |
| |
| if (event.type == SWT.EraseItem) { |
| } |
| |
| if (event.type == SWT.PaintItem) { |
| final String text1 = (String)item.getData() + " (gc)"; |
| if (event.index == 0) { |
| event.gc.drawText(text1, event.x, event.y, true); |
| } |
| } |
| if (event.type == SWT.MeasureItem) { |
| event.height = 50; |
| event.width = 100; |
| } |
| if (event.type == SWT.SetData) { |
| final int index = tree.indexOf(item); |
| final String data = "Virtual item:" + index; |
| item.setData(data); |
| item.setText("VirtItem " + index); |
| if (tree.getColumnCount() > 1) { |
| for (int i = 1; i <= tree.getColumnCount(); i++) { |
| item.setText(i, " Col: " + i + " " + data); |
| } |
| } |
| |
| if (packpending.get()) { |
| packpending.set(false); |
| display.asyncExec(() -> { |
| tree.setRedraw(false); |
| for (TreeColumn column : tree.getColumns()) { |
| column.pack(); |
| } |
| tree.setRedraw(true); |
| }); |
| } |
| } |
| }; |
| |
| /** |
| * <a href="https://bugs.eclipse.org/bugs/attachment.cgi?id=272647">Screenshot </a> |
| */ |
| @Test |
| public void ownerDrawn_cheese_single_col () { |
| knownToBeBrokenGtk3("Cheese on gtk3"); // z for warning |
| Shell shell = mkShell("Expected: There should be no cheese in the items. Move over shouldn't cheese out. See javadoc for screenshot"); |
| shell.setLayout(new FillLayout(SWT.VERTICAL)); |
| |
| final Tree tree = new Tree(shell, SWT.VIRTUAL | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL); |
| |
| AtomicBoolean packpending = new AtomicBoolean(true); |
| tree.setData(packpending); |
| |
| tree.addListener(SWT.EraseItem, ownerDrawnListener); // Not relevant. |
| tree.addListener(SWT.SetData, ownerDrawnListener); |
| tree.addListener(SWT.MeasureItem, ownerDrawnListener); |
| tree.addListener(SWT.PaintItem, ownerDrawnListener); |
| |
| final TreeColumn treeColumn = new TreeColumn(tree, SWT.LEFT); |
| treeColumn.setText("First Left Column"); |
| treeColumn.setMoveable(true); |
| |
| tree.setItemCount(100); |
| |
| shell.setSize(800, 600); // Shell size seems to have a little bit of an initial impact. (may show proper with some shell sizes, incorrectly for others). |
| shell.open(); |
| mainLoop(shell); |
| } |
| |
| @Test |
| public void ownerDrawn_cheese_multiple_col() { |
| final Shell shell = mkShell("Expected: No cheese in multiple column, also mouse move over no cheese."); |
| shell.setLayout(new FillLayout(SWT.VERTICAL)); |
| final Tree tree = new Tree(shell, SWT.VIRTUAL | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL); |
| |
| AtomicBoolean packpending = new AtomicBoolean(true); |
| tree.setData(packpending); |
| |
| tree.addListener(SWT.EraseItem, ownerDrawnListener); |
| tree.addListener(SWT.SetData, ownerDrawnListener); |
| tree.addListener(SWT.MeasureItem, ownerDrawnListener); |
| tree.addListener(SWT.PaintItem, ownerDrawnListener); |
| |
| BiConsumer<Integer, String> createColCons = (colStyle, colName) -> { |
| final TreeColumn treeColumn = new TreeColumn(tree, colStyle); |
| treeColumn.setText(colName); |
| treeColumn.setMoveable(true); |
| }; |
| |
| createColCons.accept(SWT.LEFT, "LEFT"); |
| createColCons.accept(SWT.CENTER, "CENTER"); |
| createColCons.accept(SWT.RIGHT, "RIGHT"); |
| |
| int ItemCount = 1000; |
| tree.setItemCount(ItemCount); |
| tree.setHeaderVisible(true); |
| |
| shell.setSize(SWIDTH, SHEIGHT); |
| shell.open(); |
| mainLoop(shell); |
| } |
| |
| /** |
| * <a href="http://www.eclipse.org/articles/Article-CustomDrawingTreeAndTreeItems/customDraw.htm#_example4"> Screenshot </a> |
| */ |
| @Test |
| public void ownerDrawn_eraseItem_Snippet273() { |
| knownToBeBrokenGtk3("Test currently broken on Gtk3. See Comment#1 of Bug 531551"); |
| |
| final String[] MONTHS = { |
| "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
| }; |
| final int[] HIGHS = {-7, -4, 1, 11, 18, 24, 26, 25, 20, 13, 5, -4}; |
| final int[] LOWS = {-15, -13, -7, 1, 7, 13, 15, 14, 10, 4, -2, -11}; |
| final int SCALE_MIN = -30; final int SCALE_MAX = 30; |
| final int SCALE_RANGE = Math.abs(SCALE_MIN - SCALE_MAX); |
| |
| Shell shell = mkShell(" Gtk3:broken, no erasing (1st March 2018) Gtk2: Background is used as bar-chart of sort. See screenshot."); |
| final Color blue = display.getSystemColor(SWT.COLOR_BLUE); |
| final Color white = display.getSystemColor(SWT.COLOR_WHITE); |
| final Color red = display.getSystemColor(SWT.COLOR_RED); |
| // final Image parliamentImage = new Image(display, "./parliament.jpg"); |
| final Tree tree = new Tree(shell, SWT.NONE); |
| tree.setBounds(10,10,350,300); |
| // tree.setBackgroundImage(parliamentImage); |
| for (int i = 0; i < 12; i++) { |
| TreeItem item = new TreeItem(tree, SWT.NONE); |
| item.setText(MONTHS[i] + " (" + LOWS[i] + "C..." + HIGHS[i] + "C)"); |
| } |
| final int clientWidth = tree.getClientArea().width; |
| |
| /* |
| * NOTE: MeasureItem and EraseItem are called repeatedly. Therefore it is |
| * critical for performance that these methods be as efficient as possible. |
| */ |
| tree.addListener(SWT.MeasureItem, event -> { |
| int itemIndex = tree.indexOf((TreeItem)event.item); |
| int rightX = (HIGHS[itemIndex] - SCALE_MIN) * clientWidth / SCALE_RANGE; |
| event.width = rightX; |
| }); |
| tree.addListener(SWT.EraseItem, event -> { |
| int itemIndex = tree.indexOf((TreeItem)event.item); |
| int leftX = (LOWS[itemIndex] - SCALE_MIN) * clientWidth / SCALE_RANGE; |
| int rightX = (HIGHS[itemIndex] - SCALE_MIN) * clientWidth / SCALE_RANGE; |
| GC gc = event.gc; |
| Rectangle clipping = gc.getClipping(); |
| clipping.x = leftX; |
| clipping.width = rightX - leftX; |
| gc.setClipping(clipping); |
| Color oldForeground = gc.getForeground(); |
| Color oldBackground = gc.getBackground(); |
| gc.setForeground(blue); |
| gc.setBackground(white); |
| gc.fillGradientRectangle(event.x, event.y, event.width / 2, event.height, false); |
| gc.setForeground(white); |
| gc.setBackground(red); |
| gc.fillGradientRectangle( |
| event.x + event.width / 2, event.y, event.width / 2, event.height, false); |
| gc.setForeground(oldForeground); |
| gc.setBackground(oldBackground); |
| event.detail &= ~SWT.BACKGROUND; |
| event.detail &= ~SWT.HOT; |
| }); |
| shell.setSize(SWIDTH, SHEIGHT); |
| shell.open(); |
| mainLoop(shell); |
| } |
| |
| /** |
| * On Windows/Mac, if columnWidth is not set via setWidth or pack, then items are not visible. |
| */ |
| @Test |
| public void column_noWidth_bug399522 () { |
| Shell shell = mkShell("Expected : You shouldn't see the column/item as column width not set yet."); |
| |
| final Tree tree = new Tree(shell, SWT.BORDER); |
| tree.setHeaderVisible(true); |
| |
| new TreeItem(tree, SWT.NONE).setText("Item1"); |
| TreeColumn column1 = new TreeColumn(tree, SWT.NONE); |
| // column1.setWidth(10); // Setting column width (or packing) makes items visible. |
| |
| tree.setSize(200, 200); |
| System.out.println(column1.handle); |
| |
| shell.setSize(SWIDTH, SHEIGHT); |
| shell.open(); |
| mainLoop(shell); |
| } |
| |
| @Test |
| public void ownerDrawn_multiColumn_gc_snippet239 () { |
| Shell shell = mkShell("Verify that text is correctly drawn across 2 columns. 4 columns in total."); |
| shell.setText("Text spans two columns in a TreeItem"); |
| shell.setLayout (new FillLayout()); |
| final Tree tree = new Tree(shell, SWT.MULTI | SWT.FULL_SELECTION); |
| tree.setHeaderVisible(true); |
| int columnCount = 4; |
| for (int i=0; i<columnCount; i++) { |
| TreeColumn column = new TreeColumn(tree, SWT.NONE); |
| System.out.println("Column " + i); |
| column.setText("Column " + i); |
| } |
| int itemCount = 8; |
| for (int i = 0; i < itemCount; i++) { |
| TreeItem item = new TreeItem(tree, SWT.NONE); |
| item.setText(0, "item "+i+" a"); |
| item.setText(3, "item "+i+" d"); |
| } |
| /* |
| * NOTE: MeasureItem, PaintItem and EraseItem are called repeatedly. |
| * Therefore, it is critical for performance that these methods be |
| * as efficient as possible. |
| */ |
| final String string = "text that spans two columns"; |
| GC gc = new GC(tree); |
| final Point extent = gc.stringExtent(string); |
| gc.dispose(); |
| final Color red = display.getSystemColor(SWT.COLOR_RED); |
| Listener paintListener = event -> { |
| switch(event.type) { |
| case SWT.MeasureItem: { |
| if (event.index == 1 || event.index == 2) { |
| event.width = extent.x/2; |
| event.height = Math.max(event.height, extent.y + 2); |
| } |
| break; |
| } |
| case SWT.PaintItem: { |
| if (event.index == 1 || event.index == 2) { |
| int offset = 0; |
| if (event.index == 2) { |
| TreeColumn column1 = tree.getColumn(1); |
| offset = column1.getWidth(); |
| } |
| event.gc.setForeground(red); |
| int y = event.y + (event.height - extent.y)/2; |
| event.gc.drawString(string, event.x - offset, y, true); |
| } |
| break; |
| } |
| } |
| }; |
| tree.addListener(SWT.MeasureItem, paintListener); |
| tree.addListener(SWT.PaintItem, paintListener); |
| for (int i = 0; i < columnCount; i++) { |
| tree.getColumn(i).pack(); |
| } |
| shell.setSize(SWIDTH, SHEIGHT); |
| shell.open(); |
| mainLoop(shell); |
| } |
| |
| @Test |
| public void bug73812_treeColumn_getWidth_0 () { |
| Shell shell = mkShell("Verify that all columns are of same width. (100)."); |
| shell.setSize(SWIDTH, SHEIGHT); |
| shell.setLayout(new FillLayout()); |
| |
| final Tree tt = new Tree(shell, SWT.FULL_SELECTION | SWT.MULTI | SWT.VIRTUAL); |
| tt.setLinesVisible(true); |
| tt.setHeaderVisible(true); |
| |
| for (int i = 0; i < 10; i++) { |
| TreeColumn tc = new TreeColumn(tt, SWT.NONE); |
| tc.setWidth(100); |
| System.out.println(tc.getWidth()); |
| tc.setWidth(tc.getWidth()); |
| tc.setText("Column " + i); |
| } |
| |
| for (int i = 0; i < 100; i++) { |
| new TreeItem(tt, SWT.NONE); |
| } |
| |
| shell.open(); |
| mainLoop(shell); |
| } |
| |
| /** |
| * Last column should be big enough so that text inside it can be read. |
| * Notion that getWidth() when called after setWidth() returns a different size. |
| */ |
| @Test |
| public void bug51079_setWidth_getWidth() { |
| Shell shell = mkShell("column SetGet Width : Make shell smaller and bigger. If you don't see COL_SIZE_ERROR in console, all is well."); |
| shell.setSize(SWIDTH, SHEIGHT); |
| shell.setLayout(new FillLayout()); |
| StringBuilder sbBuffer = new StringBuilder(); |
| |
| final Composite comp = new Composite(shell, SWT.NONE); |
| final Tree tree = new Tree(comp, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION); |
| tree.setHeaderVisible(true); |
| tree.setLinesVisible(true); |
| final TreeColumn column1 = new TreeColumn(tree, SWT.NONE); |
| column1.setText("Column 1"); |
| column1.setResizable(false); |
| final TreeColumn column2 = new TreeColumn(tree, SWT.NONE); |
| column2.setText("Column 2"); |
| column2.setResizable(false); |
| for (int i = 0; i < 60; i++) { |
| TreeItem item = new TreeItem(tree, SWT.NONE); |
| sbBuffer.append("M"); |
| item.setText(new String[] { "item 0 " + sbBuffer.toString() + " " + i, "item 1 " + i }); |
| } |
| |
| Consumer<Integer> setColumnWidths = (width) -> { |
| int c1w = (int)(width * 0.9); |
| column1.setWidth(c1w); |
| int c1wPost = column1.getWidth(); |
| if (c1w != c1wPost) |
| System.err.println("COL_SIZE_ERROR 1 Expected:" + c1w + " actual:" + c1wPost); |
| |
| int c2w = width - column1.getWidth(); |
| column2.setWidth(c2w); |
| int c2wPost = column2.getWidth(); |
| if (c2w != c2wPost) |
| System.err.println("COL_SIZE_ERROR 2 Expected:" + c2w + " actual:" + column2.getWidth()); |
| }; |
| |
| comp.addControlListener(new ControlAdapter() |
| { |
| @Override |
| public void controlResized(ControlEvent e) |
| { |
| Rectangle area = tree.getParent().getClientArea(); |
| Point preferredSize = tree.computeSize(SWT.DEFAULT, SWT.DEFAULT); |
| int width = area.width - 2 * tree.getBorderWidth(); |
| if (preferredSize.y > area.height) |
| { |
| // Subtract the scrollbar width from the total column width |
| // if a vertical scrollbar will be required |
| Point vBarSize = tree.getVerticalBar().getSize(); |
| width -= vBarSize.x; |
| } |
| Point oldSize = tree.getSize(); |
| if (oldSize.x > area.width) |
| { |
| // tree is getting smaller so make the columns |
| // smaller first and then resize the tree to |
| // match the client area width |
| setColumnWidths.accept(width); |
| tree.setSize(area.width, area.height); |
| } |
| else |
| { |
| // tree is getting bigger so make the tree |
| // bigger first and then make the columns wider |
| // to match the client area width |
| tree.setSize(area.width, area.height); |
| setColumnWidths.accept(width); |
| } |
| } |
| }); |
| shell.open(); |
| mainLoop(shell); |
| } |
| |
| @Test |
| public void basicTree_Snippet35() { |
| // original was table, modified for tree. |
| Shell shell = mkShell("TREE: Basic Tree with a few items, no column, no headers"); |
| shell.setLayout(new FillLayout()); |
| Tree tree = new Tree (shell, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); |
| for (int i=0; i<12; i++) { |
| TreeItem item = new TreeItem (tree, 0); |
| item.setText ("Item " + i); |
| } |
| shell.setSize (SWIDTH, SHEIGHT); |
| shell.open (); |
| mainLoop(shell); |
| } |
| } |