本文实现效果及其工具下载地址:http://sourceforge.net/projects/filetools/
windows7/8的资源管理器个人觉得比较便利的还属地址栏,在定位资源位置时非常方面。见下图:
当窗口大小改变,或地址栏的长度过长时,地址栏会自动隐藏头部。这是一个优点,但同时也是一个缺点。大多数时候,我们需要点击多次才能回到地址头部。而显示出来的那一部分,恰恰是不经常使用的。所以,我想对其进行改进,使地址栏可以达到:
- 能够显示全部地址栏信息,在长度大导致空间不足时显示尽量多的位置信息,可以隐藏文本信息,但应该显示相应标志指示父文件夹和根目录;
初步实现的效果如图:
实现这个效果并不困难。第一种方法是是通过layout来实现。在swt中,常见的rowlayout实现的就是类似的效果,我们可以通过修改rowlayout来达到目的,并达到支持水平和垂直布局。第二种方法是调用jdt里面的BreadcrumbView,做适当修改就可以。
第一种方法的实现(详见附件,里面有源码和测试代码):
/* AutoAdjustRowLayout.java * Copyright (c) 2013 by Brook Tran * All rights reserved. * * The copyright of this software is own by the authors. * You may not use, copy or modify this software, except * in accordance with the license agreement you entered into * with the copyright holders. For details see accompanying license * terms. */ package org.jeelee.filemanager.ui.breadcrumb; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.RowData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Layout; /** * <B>AutoAdjustRowLayout</B> * extends from {@link org.eclipse.swt.layout.RowLayout} * @author Brook Tran. Email: <a href="mailto:[email protected]">[email protected]</a> * @since org.jeelee.filemanager 2013-1-9 created */ public class BreadcrumbLayout extends Layout{ /** * type specifies whether the layout places controls in rows or * columns. * * The default value is HORIZONTAL. * * Possible values are: <ul> * <li>HORIZONTAL: Position the controls horizontally from left to right</li> * <li>VERTICAL: Position the controls vertically from top to bottom</li> * </ul> * * @since 2.0 */ public int type = SWT.HORIZONTAL | SWT.RIGHT_TO_LEFT; /** * marginWidth specifies the number of pixels of horizontal margin * that will be placed along the left and right edges of the layout. * * The default value is 0. * * @since 3.0 */ public int marginWidth = 0; /** * marginHeight specifies the number of pixels of vertical margin * that will be placed along the top and bottom edges of the layout. * * The default value is 0. * * @since 3.0 */ public int marginHeight = 0; /** * spacing specifies the number of pixels between the edge of one cell * and the edge of its neighbouring cell. * * The default value is 3. */ public int spacing = 3; /** * pack specifies whether all controls in the layout take * their preferred size. If pack is false, all controls will * have the same size which is the size required to accommodate the * largest preferred height and the largest preferred width of all * the controls in the layout. * * The default value is true. */ public boolean pack = true; /** * fill specifies whether the controls in a row should be * all the same height for horizontal layouts, or the same * width for vertical layouts. * * The default value is false. * * @since 3.0 */ public boolean fill = false; /** * marginLeft specifies the number of pixels of horizontal margin * that will be placed along the left edge of the layout. * * The default value is 3. */ public int marginLeft = 3; /** * marginTop specifies the number of pixels of vertical margin * that will be placed along the top edge of the layout. * * The default value is 3. */ public int marginTop = 3; /** * marginRight specifies the number of pixels of horizontal margin * that will be placed along the right edge of the layout. * * The default value is 3. */ public int marginRight = 3; /** * marginBottom specifies the number of pixels of vertical margin * that will be placed along the bottom edge of the layout. * * The default value is 3. */ public int marginBottom = 3; public BreadcrumbLayout() { } public BreadcrumbLayout (int type) { this.type = type; } @Override protected void layout (Composite composite, boolean flushCache) { Rectangle clientArea = composite.getClientArea (); if ((type & SWT.HORIZONTAL) == SWT.HORIZONTAL) { layoutHorizontal (composite, true, clientArea.width, flushCache); } else { layoutVertical (composite, true, clientArea.height, flushCache); } } @Override protected Point computeSize (Composite composite, int wHint, int hHint, boolean flushCache) { Point extent; if ((type & SWT.HORIZONTAL) == SWT.HORIZONTAL) { extent = layoutHorizontal (composite, false, wHint, flushCache); } else { extent = layoutVertical (composite, false, hHint, flushCache); } if (wHint != SWT.DEFAULT) { extent.x = wHint; } if (hHint != SWT.DEFAULT) { extent.y = hHint; } return extent; } Point layoutHorizontal (Composite composite, boolean move, int width, boolean flushCache) { Control [] children = composite.getChildren (); int count = 0; for (int i=0; i<children.length; i++) { Control control = children [i]; RowData data = (RowData) control.getLayoutData (); if (data == null || !data.exclude) { children [count++] = children [i]; } } if (count == 0) { return new Point (marginLeft + marginWidth * 2 + marginRight, marginTop + marginHeight * 2 + marginBottom); } int startIndex = 0; if( ( type & SWT.RIGHT_TO_LEFT) == SWT.RIGHT_TO_LEFT){ startIndex = count; int maxWidth = marginLeft + marginWidth + marginRight + marginWidth; // marginRight + marginLeft +marginWidth ; while(startIndex>0 && maxWidth<width){ Control child = children [startIndex-1]; Point size = computeSize (child, flushCache); maxWidth += size.x +spacing + marginWidth; startIndex--; } startIndex = adjust(move, children, count, startIndex); } int childWidth = 0, childHeight = 0, maxHeight = 0; // get max width & height if (!pack) { for (int i=startIndex; i<count; i++) { Control child = children [i]; Point size = computeSize (child, flushCache); childWidth = Math.max (childWidth, size.x); childHeight = Math.max (childHeight, size.y); } maxHeight = childHeight; } int clientX = 0, clientY = 0; if (move) { Rectangle rect = composite.getClientArea (); clientX = rect.x; clientY = rect.y; } Rectangle [] bounds = null; if (move && (fill )) { bounds = new Rectangle [count]; } int maxX = 0, x = marginLeft + marginWidth, y = marginTop + marginHeight; for (int i=startIndex; i<count; i++) { Control child = children [i]; if (pack) { Point size = computeSize (child, flushCache); childWidth = size.x; childHeight = size.y; } if (pack || fill ) { maxHeight = Math.max (maxHeight, childHeight); } if (move) { int childX = x + clientX, childY = y + clientY; if ( fill ) { bounds [i] = new Rectangle (childX, childY, childWidth, childHeight); } else { child.setBounds (childX, childY, childWidth, childHeight); } } x += spacing + childWidth; maxX = Math.max (maxX, x); } maxX = Math.max (clientX + marginLeft + marginWidth, maxX - spacing); maxX += marginRight + marginWidth; if (move && (fill)) { for (int i=startIndex; i<count; i++) { if (fill) { bounds [i].height = maxHeight; } children [i].setBounds (bounds [i]); } } return new Point (maxX, y + maxHeight + marginBottom + marginHeight); } Point layoutVertical (Composite composite, boolean move,int height, boolean flushCache) { Control [] children = composite.getChildren (); int count = 0; for (int i=0; i<children.length; i++) { Control control = children [i]; RowData data = (RowData) control.getLayoutData (); if (data == null || !data.exclude) { children [count++] = children [i]; } } if (count == 0) { return new Point (marginLeft + marginWidth * 2 + marginRight, marginTop + marginHeight * 2 + marginBottom); } int startIndex = 0; if( ( type & SWT.RIGHT_TO_LEFT) == SWT.RIGHT_TO_LEFT){ startIndex = count; int maxHeight = marginTop + marginHeight+ marginBottom +marginHeight ; while(startIndex>0 && maxHeight<height){ Control child = children [startIndex-1]; Point size = computeSize (child, flushCache); maxHeight += size.y +spacing + marginTop +marginHeight; startIndex--; } startIndex = adjust(move, children, count, startIndex); } int childWidth = 0, childHeight = 0, maxWidth = 0; if (!pack) { for (int i=startIndex; i<count; i++) { Control child = children [i]; Point size = computeSize (child, flushCache); childWidth = Math.max (childWidth, size.x); childHeight = Math.max (childHeight, size.y); } maxWidth = childWidth; } int clientX = 0, clientY = 0; if (move) { Rectangle rect = composite.getClientArea (); clientX = rect.x; clientY = rect.y; } Rectangle [] bounds = null; if (move && (fill)) { bounds = new Rectangle [count]; } int maxY = 0, x = marginLeft + marginWidth, y = marginTop + marginHeight; for (int i=startIndex; i<count; i++) { Control child = children [i]; if (pack) { Point size = computeSize (child, flushCache); childWidth = size.x; childHeight = size.y; } if (pack || fill ) { maxWidth = Math.max (maxWidth, childWidth); } if (move) { int childX = x + clientX, childY = y + clientY; if ( fill ) { bounds [i] = new Rectangle (childX, childY, childWidth, childHeight); } else { child.setBounds (childX, childY, childWidth, childHeight); } } y += spacing + childHeight; maxY = Math.max (maxY, y); } maxY = Math.max (clientY + marginTop + marginHeight, maxY - spacing); if (move && (fill )) { for (int i=startIndex; i<count; i++) { children [i].setBounds (bounds [i]); } } return new Point (x + maxWidth + marginRight + marginWidth, maxY); } private int adjust(boolean move, Control[] children, int count, int startIndex) { if (startIndex > 0) { startIndex++; } if(startIndex > count){ startIndex--; } if(move){ Rectangle hide = new Rectangle(0, 0, 0, 0); for(int i=0;i<startIndex;i++){ // children[i].setVisible(false); children[i].setBounds(hide); } } return startIndex; } Point computeSize (Control control, boolean flushCache) { int wHint = SWT.DEFAULT, hHint = SWT.DEFAULT; RowData data = (RowData) control.getLayoutData (); if (data != null) { wHint = data.width; hHint = data.height; } return control.computeSize (wHint, hHint, flushCache); } @Override protected boolean flushCache (Control control) { return true; } String getName () { String string = getClass ().getName (); int index = string.lastIndexOf ('.'); if (index == -1) { return string; } return string.substring (index + 1, string.length ()); } @Override public String toString () { String string = getName ()+" {"; string += "type="+((type != SWT.HORIZONTAL) ? "SWT.VERTICAL" : "SWT.HORIZONTAL")+" "; if (marginWidth != 0) { string += "marginWidth="+marginWidth+" "; } if (marginHeight != 0) { string += "marginHeight="+marginHeight+" "; } if (marginLeft != 0) { string += "marginLeft="+marginLeft+" "; } if (marginTop != 0) { string += "marginTop="+marginTop+" "; } if (marginRight != 0) { string += "marginRight="+marginRight+" "; } if (marginBottom != 0) { string += "marginBottom="+marginBottom+" "; } if (spacing != 0) { string += "spacing="+spacing+" "; } string += "pack="+pack+" "; string += "fill="+fill+" "; string = string.trim(); string += "}"; return string; } }
实现效果见下图。其中,第一栏是地址栏的模拟实现,这个会隐藏根目录和父级目录,只显示最多子节点。
第二种方法相对比较简单,像使用普通view一样调用BreadcrumbView,设置LabelProvider和ContentProvider就可以了。
class FilePathBreadcrumViewer extends BreadcrumbViewer{ private FileFilterDelegate fFilter; public FilePathBreadcrumViewer(FileFilterDelegate fileFilter, Composite parent, int style) { super(parent, style); fFilter=fileFilter; } @Override protected void configureDropDownViewer(TreeViewer viewer, Object input) { viewer.setLabelProvider(new FileDelegateLableProvider()); viewer.setContentProvider(new FileDelegateTreeContentProvider(fFilter)); } }
实现的效果: