本文实现效果及其工具下载地址: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:Brook.Tran.C@gmail.com">Brook.Tran.C@gmail.com</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));
}
}
实现的效果: