可能是为了保持平台独立性,SWT没有开放许多控件的自定义接口。例如,Win32中的Button、Label、List和ComboBox都是可以自绘(Owner Draw)的,但是SWT并没有把这些绘制方法开放出来。在最新的3.2版本中添加的一个新特性是Table和Tree现在支持Custom Draw了(但是并未整合到Viewer体系中),不过对于上述控件的支持仍付阙如。
上一次,我实现了一个自绘的按钮。现在,看到有人询问是否可以在Combo的列表中加入图像。其实这相当容易,只要重载Combo Widget并把自绘接口暴露出来即可。以下是简单的代码示例:
package org.eclipse.swt.widgets;
import java.io.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.win32.*;
public class CustomCombo extends Combo
{
public CustomCombo( Composite parent, int style )
{
super( parent, style );
try
{
InputStream is = getClass().getResourceAsStream( "bullet.gif" );
image = new Image( getDisplay(), is );
is.close();
}
catch ( IOException e )
{
e.printStackTrace();
}
final int CB_SETITEMHEIGHT = 0x0153;
OS.SendMessage( handle, CB_SETITEMHEIGHT, 0, 24 );
OS.SendMessage( handle, CB_SETITEMHEIGHT, -1, 24 );
}
@Override
int widgetStyle()
{
final int CBS_OWNERDRAWFIXED = 0x0010;
final int CBS_HASSTRINGS = 0x0200;
// final int CBS_OWNERDRAWVARIABLE = 0x0020;
return super.widgetStyle() | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS;
}
@Override
protected void checkSubclass()
{
}
@Override
public void dispose()
{
image.dispose();
super.dispose();
}
/* @Override
LRESULT wmMeasureChild( int wParam, int lParam )
{
MEASUREITEMSTRUCT mis = new MEASUREITEMSTRUCT();
OS.MoveMemory( mis, lParam, MEASUREITEMSTRUCT.sizeof );
mis.itemHeight = 40;
OS.MoveMemory( lParam, mis, MEASUREITEMSTRUCT.sizeof );
return null; // super.wmMeasureChild( wParam, lParam );
} */
@Override
LRESULT wmDrawChild( int wParam, int lParam )
{
DRAWITEMSTRUCT dis = new DRAWITEMSTRUCT();
OS.MoveMemory( dis, lParam, DRAWITEMSTRUCT.sizeof );
GC gc = new GC( new DCWrapper( dis.hDC ) );
Rectangle rc = new Rectangle( dis.left, dis.top, dis.right - dis.left,
dis.bottom - dis.top );
Display display = getDisplay();
if ( (dis.itemState & OS.ODS_SELECTED) != 0 )
{
gc
.setBackground( display
.getSystemColor( SWT.COLOR_LIST_SELECTION ) );
gc.setForeground( display
.getSystemColor( SWT.COLOR_LIST_SELECTION_TEXT ) );
gc.fillRectangle( rc );
}
else
{
gc.setBackground( display
.getSystemColor( SWT.COLOR_LIST_BACKGROUND ) );
gc.setForeground( display
.getSystemColor( SWT.COLOR_LIST_FOREGROUND ) );
gc.fillRectangle( rc );
}
String text = getItem( dis.itemID );
gc.drawImage( image, dis.left + 1, dis.top + 1 );
gc.drawText( text, dis.left + 20, dis.top );
gc.dispose();
return null;
}
private static class DCWrapper implements Drawable
{
private int hdc;
DCWrapper( int hdc )
{
this.hdc = hdc;
}
public int internal_new_GC( GCData data )
{
return hdc;
}
public void internal_dispose_GC( int handle, GCData data )
{
}
}
private Image image;
}
值得说明的是,如果设置Combo为OwnerDraw Variable风格,则必须重载wmMeasureChild方法来指定每一项的高度。如果使用OwnerDraw Fixed风格,则只需要在构造的时候发送一条CB_SETITEMHEIGHT消息就行了。
另外一种值得考虑的选择是将Win32的ComboBoxEx控件包装成SWT Widget。不过,这需要转换若干结构并提供接口,Win32的ImageList管理机制和SWT的Image包装方法差别比较大,使得这种方法实现起来麻烦的多。