GEF 中 Request 承載 Tool 將滑鼠事件轉換為 EditPart 可識別的請求。內建請求類型 (CreateRequest、SelectionRequest 等) 滿足常規需求,但特定業務 (如「油漆桶」工具點擊節點變色) 需自定義 Request。
需求:調色板新增三個工具,分別將選中節點背景色設為紅/綠/藍。

public class Node {
final public static String PROP_COLOR = "COLOR";
protected RGB color = new RGB(255, 255, 255); // 默認白色
public RGB getColor() { return color; }
public void setColor(RGB color) {
if (this.color.equals(color)) return;
this.color = color;
firePropertyChange(PROP_COLOR, null, color); // 發送屬性變更通知
}
}
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Node.PROP_COLOR))
refreshVisuals();
}
protected void refreshVisuals() {
((NodeFigure) getFigure()).setBackgroundColor(new Color(null, node.getColor()));
// 實際項目中 Color 需緩存,避免系統資源耗盡
}
public class ChangeColorRequest extends Request {
final static public String REQ_CHANGE_COLOR = "REQ_CHANGE_COLOR";
private Node node;
private RGB color;
public ChangeColorRequest(Node node, RGB color) {
super();
this.node = node;
this.color = color;
setType(REQ_CHANGE_COLOR); // 關鍵:EditPolicy 憑 type 判斷是否處理
}
// getter/setter 略
}
Request 本質是數據傳輸對象 (DTO),攜帶「目標節點」與「目標顏色」。
public class ChangeColorTool extends SelectionTool {
private RGB color;
public ChangeColorTool(RGB color) { this.color = color; }
@Override
protected boolean handleButtonDown(int button) {
EditPart editPart = getTargetEditPart();
if (editPart instanceof NodePart) {
NodePart nodePart = (NodePart) editPart;
Node node = (Node) nodePart.getModel();
ChangeColorRequest request = new ChangeColorRequest(node, color);
Command command = editPart.getCommand(request); // 從 EditPart 獲取命令
getDomain().getCommandStack().execute(command); // 執行命令
return true;
}
return false;
}
}
繼承 SelectionTool 復用選中邏輯,覆寫 handleButtonDown 構建 Request → 獲取 Command → 執行。
public class ChangeColorToolEntry extends ToolEntry {
private RGB color;
public ChangeColorToolEntry(RGB color, String label, String desc,
ImageDescriptor iconSmall, ImageDescriptor iconLarge) {
super(label, desc, iconSmall, iconLarge);
this.color = color;
}
@Override
public Tool createTool() {
ChangeColorTool tool = new ChangeColorTool(color);
tool.setUnloadWhenFinished(false); // 執行後是否切回選擇工具
tool.setDefaultCursor(SharedCursors.CROSS);
return tool;
}
}
在 PaletteFactory.createColorDrawer() 中創建三個實例 (紅/綠/藍) 加入調色板。
public class ChangeColorCommand extends Command {
private Node node;
private RGB color;
private RGB oldColor;
public ChangeColorCommand(Node node, RGB color) { ... }
@Override
public void execute() {
oldColor = node.getColor();
node.setColor(color);
}
@Override
public void undo() {
node.setColor(oldColor);
}
}
支持 Undo/Redo,保存舊顏色。
public class ChangeColorEditPolicy extends AbstractEditPolicy {
final static public String CHANGE_COLOR_ROLE = "CHANGE_COLOR_ROLE";
@Override
public Command getCommand(Request request) {
if (request.getType().equals(ChangeColorRequest.REQ_CHANGE_COLOR)) {
ChangeColorRequest req = (ChangeColorRequest) request;
return new ChangeColorCommand(req.getNode(), req.getColor());
}
return null;
}
}
根據 Request Type 分發,創建對應 Command。定義獨特 Role 字符串避免覆蓋其他 Policy。
protected void createEditPolicies() {
installEditPolicy(ChangeColorEditPolicy.CHANGE_COLOR_ROLE,
new ChangeColorEditPolicy());
}
| 類型 | 名稱 |
|---|---|
| 新建 | ChangeColorRequest, ChangeColorTool, ChangeColorToolEntry, ChangeColorCommand, ChangeColorEditPolicy |
| 修改 | Node, NodePart, PaletteFactory |
建議按功能包組織:com.example.request、com.example.tool、com.example.command、com.example.policy。
用戶點擊畫布
↓
ChangeColorTool.handleButtonDown()
↓
構建 ChangeColorRequest(節點, 顏色)
↓
EditPart.getCommand(request)
↓
ChangeColorEditPolicy.getCommand() → 返回 ChangeColorCommand
↓
CommandStack.execute(command)
↓
Command.execute() → Node.setColor() → 觸發 PropertyChange
↓
NodePart.propertyChange() → refreshVisuals() → Figure 重繪