天天看點

[Eclipse]GEF入門系列(四、其他功能)

最近由于實驗室任務繁重,一直沒有繼續研究GEF,本來已經掌握的一些東西好象又丢掉了不少,真是無奈啊,看來還是要經常碰碰。剛剛接觸GEF的朋友大都會有這樣的印象:GEF裡概念太多,比較繞,一些能直接實作的功能非要拐幾個彎到另一個類裡做,而且很多類的名字十分相似,加上不知道他們的作用,感覺就好象一團亂麻。我覺得這種情況是由圖形使用者界面(GUI)的複雜性所決定的,GUI看似簡單,實際上包含了相當多的邏輯,特别是GEF處理的這種圖形編輯方式,可以說是最複雜的一種。GEF裡每一個類,應該說都有它存在的理由,我們要盡可能了解作者的意圖,這就需要多看文檔和好的例子。

在Eclipse裡檢視文檔和代碼相當便利,比如我們對某個類的用法不清楚,一般首先找它的注釋(選中類或方法按F2),其次可以檢視它在其他地方用法(選中類或方法按Ctrl+Shift+G),還可以找它的源代碼(Ctrl+滑鼠左鍵或F3)來看,另外Ctrl+Shift+T可以按名稱查找一個類等等。學GEF是少不了看代碼的,當然還需要時間和耐心。

好,閑話少說,下面進入正題。這篇文章将繼續上一篇内容,主要讨論如何實作DirectEdit、屬性頁和大綱視圖,這些都是一個完整GEF應用程式需要提供的基本功能。

實作DirectEdit

圖1 Direct Edit

在GEF裡,這個彈出的編輯器由DirectEditManager類負責管理,在我們的NodePart類裡,通過覆寫performRequest()方法響應使用者的DirectEdit請求,在這個方法裡一般要構造一個DirectEditManager類的執行個體(例子中的NodeDirectEditManager),并傳入必要的參數,包括接受請求的EditPart(就是自己,this)、編輯器類型(使用TextCellEditor)以及用來定位編輯器的CellEditorLocator(NodeCellEditorLocator),然後用show()方法使編輯器顯示出來,而編輯器中顯示的内容已經在構造方法裡得到。簡單看一下NodeCellEditorLocator類,它的關鍵方法在relocate()裡,當編輯器裡的内容改變時,這個方法被調用進而讓編輯器始終處于正确的坐标位置。DirectEditManager有一個重要的initCellEditor()方法,它的主要作用是設定編輯器的初始值。在我們的例子裡,初始值設定為被編輯NodePart對應模型 (Node)的name屬性值;這裡還另外完成了設定編輯器字型和選中全部文字(selectAll)的功能,因為這樣更符合一般使用習慣。

在NodePart裡還要增加一個角色為DIRECT_EDIT_ROLE的EditPolicy,它應該繼承自DirectEditPolicy,有兩個方法需要實作:getDirectEditCommand()和showCurrentEditValue(),雖然還未遇到過,但前者的作用你不應該感到陌生--在編輯結束時生成一個Command對象将修改結果作用到模型;後者的目的是更新Figure中的顯示,雖然我們的編輯器覆寫了Figure中的文本,似乎并不需要管Figure的顯示,但在編輯中時刻保持這兩個文本的一緻才不會出現"蓋不住"的情況,例如當編輯器裡的文本較短時。

實作屬性頁

在GEF裡實作屬性頁和普通應用程式基本一樣,例如我們希望可以通過屬性視圖(PropertyView)顯示和編輯每個節點的屬性,則可以讓Node類實作IPropertySource接口,并通過一個IPropertyDescriptor[]類型的成員變量描述要在屬性視圖裡顯示的那些屬性。有朋友問,要在屬性頁裡增加一個屬性都該改哪些地方,主要是三個地方:首先要在你的IPropertyDescriptor[]變量裡增加對應的描述,包括屬性名和屬性編輯方式(比如文本或是下拉框,如果是後者還要指定選項清單),其次是getPropertyValue()和setPropertyValue()裡增加讀取屬性值和将結果寫入的代碼,這兩個方法裡一般都是像下面的結構(以前者為例):

[Eclipse]GEF入門系列(四、其他功能)

public Object getPropertyValue(Object id) {

[Eclipse]GEF入門系列(四、其他功能)

    if (PROP_NAME.equals(id))

[Eclipse]GEF入門系列(四、其他功能)

        return getName();

[Eclipse]GEF入門系列(四、其他功能)

    if (PROP_VISIBLE.equals(id))

[Eclipse]GEF入門系列(四、其他功能)

        return isVisible() ? new Integer(0) : new Integer(1);

[Eclipse]GEF入門系列(四、其他功能)

    return null;

[Eclipse]GEF入門系列(四、其他功能)

}

也就是根據要處理的屬性名做不同操作。要注意的是,下拉框類型的編輯器是以Integer類型資料代表選中項序号的,而不是int或String,例如上面的代碼根據visible屬性傳回第零項或第一項,否則會出現ClassCastException。

圖2 屬性頁

實作大綱視圖

在Eclipse裡,當編輯器(Editor)被激活時,大綱視圖自動通過這個編輯器的getAdapter()方法尋找它提供的大綱(大綱實作IcontentOutlinePage接口)。GEF提供了ContentOutlinePage類用來實作大綱視圖,我們要做的就是實作一個它的子類,并重點實作createControl()方法。ContentOutlinePage是org.eclipse.ui.part.Page的一個子類,大綱視圖則是PageBookView的子類,在大綱視圖中有一個PageBook,包含了很多Page并可以在它們之間切換,切換的依據就是目前活動的Editor。是以,我們在createControl()方法裡要做的就是構造這個Page,簡化後的代碼如下所示:

[Eclipse]GEF入門系列(四、其他功能)

private Control outline;

[Eclipse]GEF入門系列(四、其他功能)

public OutlinePage() {

[Eclipse]GEF入門系列(四、其他功能)

    super(new TreeViewer());

[Eclipse]GEF入門系列(四、其他功能)
[Eclipse]GEF入門系列(四、其他功能)

public void createControl(Composite parent) {

[Eclipse]GEF入門系列(四、其他功能)

    outline = getViewer().createControl(parent);

[Eclipse]GEF入門系列(四、其他功能)

    getSelectionSynchronizer().addViewer(getViewer());

[Eclipse]GEF入門系列(四、其他功能)

    getViewer().setEditDomain(getEditDomain());

[Eclipse]GEF入門系列(四、其他功能)

    getViewer().setEditPartFactory(new TreePartFactory());

[Eclipse]GEF入門系列(四、其他功能)

    getViewer().setContents(getDiagram());

[Eclipse]GEF入門系列(四、其他功能)

由于我們在構造方法裡指定了使用樹結構顯示大綱,是以createControl()裡的第一句就會使outline變量得到一個Tree(見org.eclipse.gef.ui.parts.TreeViewer的代碼),第二句把TreeViewer加到選擇同步器中,進而讓使用者不論在大綱或編輯區域裡選擇EditPart時,另一方都能自動做出同樣的選擇;最後三行的作用在以前的文章裡都有介紹,總體目的是把大綱視圖的模型與編輯區域的模型聯系在一起,這樣,對于同一個模型我們就有了兩個視圖,體會到MVC的好處了吧。

實作大綱視圖最重要的工作基本就是這些,但還沒有完,我們要在init()方法裡綁定UNDO/REDO/DELETE等指令到Eclipse主視窗,否則當大綱視圖處于活動狀态時,主工具條上的這些指令就會變為不可用狀态;在 getControl()方法裡要傳回我們的outline成員變量,也就是指定讓這個控件出現在大綱視圖中;在dispose()方法裡應該把這個TreeViewer從選擇同步器中移除;最後,必須在PracticeEditor裡覆寫getAdapter()方法,前面說過,這個方法是在Editor激活時被大綱視圖調用的,是以在這裡必須把我們實作好的OutlinePage傳回給大綱視圖使用,代碼如下:

[Eclipse]GEF入門系列(四、其他功能)

public Object getAdapter(Class type) {

[Eclipse]GEF入門系列(四、其他功能)

    if (type == IContentOutlinePage.class)

[Eclipse]GEF入門系列(四、其他功能)

        return new OutlinePage();

[Eclipse]GEF入門系列(四、其他功能)

    return super.getAdapter(type);

[Eclipse]GEF入門系列(四、其他功能)

這樣,樹型大綱視圖就完成了,見下圖。很多GEF應用程式同時具有樹型和縮略圖兩種大綱,實作的基本思路是一樣的,但代碼會稍微複雜一些,因為這兩種大綱一般要通過一個PageBook進行切換,縮略圖一般由org.eclipse.draw2d.parts.ScrollableThumbnail負責實作,這裡暫時不講了(也許以後會詳細說),你也可以通過看logic例子的LogicEditor這個類的代碼來了解。

圖3 大綱視圖

繼續閱讀