For the complete documentation index, see llms.txt. This page is also available as Markdown.

Component

Component 基本上是 GXT widget 的共同祖先。

Component 有提供 enable()、disable() 也有 setter 形式的 setEnable()。 也就是說,有別於 GWT 只有某些 widget 才有 setEnable(), 所有的 GXT widget 都可以有 enable / disable 的狀態。 各式 container 的 setEnable() 會連小孩也一起呼叫 setEnable()。

Container 系

(謎之聲:為什麼 TabPanel 不是... ==?)

Container

所有的 widget 都可以設定 HasMargins 的 layout data, 無論是加在哪一個 container 上都會有 margin 的效果。 這是因為 Container.onInser() 會特別檢查 child.getLayoutData() 是否為 HasMargins 並作處理。

SimpleContainer

會自動讓小孩的大小跟自己的大小一樣

BorderLayoutContainer

假設 northLD 的 size 設定為 100、split 設定為 true(北邊區域可以動態調整大小), 調整北邊區域的大小會改變 northLD.getSize() 值。

ContentPanel

在 ui.xml 當中作 contentPanel.addButton() 的方法:

    <core:ContentPanel>
        <core:button>
            <foo:Foo />
        <core:button>
        <core:button>
            <foo:Foo />
        <core:button>        
    </core:ContentPanel>

原來一個 <core:button> 只能塞一個 widget,我之前都誤會它了 [炸]。

Dialog

要知道使用者對 Dialog 做了什麼事情,是掛 addDialogHideHandler() 這個 handler。 從 DialogHideEvent.getHideButton() 可以知道是按下哪個 button。 不知道該說巧妙還是該說 WTF...... Orz

PredefinedButton 的 I18N ###

從 API 上來看,要改變 PredefinedButton(對應的 TextButton)的顯示字串, 就是透過 setDialogMessages() 設定 DialogMessages。 實際上,這個 API 根本有問題...... [怒],

以 ConfirmMessageBox 為例,流程大概是

setDialogMessages() 純粹是 setter,沒有重設顯示字串, 所以 new 完 instance 再 setDialogMessages() 也沒意義。

最直白的 workaround 是

但是被嫌棄這樣違背 API 設計 / OO 原則(阿就原本 API 設計爛阿幹 T__T), 所以找出第二種 workaround

簡單地說,就是再次呼叫 setPredefinedButtons()、重新製造 button, 此時就會用 myDialogMessages 來設定顯示字串啦。

雖然這某種程度還是破壞了 ConfirmMessageBox 的封裝, 不過從程式碼當中可以清楚知道到底有哪些 button, 然後學理上 myDialogMessages 整個 project 只會有一份,好像也不錯啦。

AccordionLayoutContainer

小孩只能是 ContentPanel,而且(應該說「所以」?) ContentPanel 還能塞 AccordionLayoutAppearance 來改變 ContentPanel 的外觀。

HBox / VBox

做了 setPack(),會發現小孩的 layoutData 不管怎麼設定 flex 值, 都不會有預其中的大小變化,而是以小孩原本的大小呈現。 目前這是實驗結果,還沒追 source code [死]。

HorizontalLayoutContainer / VerticalLayoutContainer

HorizontalLayoutData 的 width 數值邏輯為 (VerticalLayoutData 的 height 比照辦理):

  • x > 1:小孩的寬度就是 x

  • x = -1:小孩自己決定自己的寬度(容易錯,使用注意)

  • x < -1:小孩的寬度是「爸爸的寬度加上 x」

    換句話說,就是只留 -x 的寬度給兄弟姊妹

  • 0 < x <= 1:小孩的寬度是「爸爸剩下的寬度乘上 x」。

舉實際的例子,假設一個 HorizontalLayoutContainer 的寬度是 1000, 底下有四個小孩、layoutData 的 width 設定值與實際計算完的寬度分別為:

  • 100→100

  • 0.5→(1000 - 100 - 200 - 600) * 0.5 = 50

  • -1(實際上是 200)→200

  • -400→600

備註 ######

GXT API 文件上針對 0 < x <= 1 這段沒有說明仔細,導致跟實際結果有出入。 以上是追 source code 的結果,詳情可以參閱 HorizontalLayoutContainer.doLayout(), 因為 0 < x <=1 的需求,所以要兩次迴圈才有辦法決定(如果有小孩設定 -1 可能不只)。 第一次迴圈決定 pw,然後第二次迴圈才依照 pw 實際指定小孩的寬度。

輸入系

ComboBox

label 必須是 unique 的, 這是因為 ComboBox.getValue() 會用當下的 getText() 去反找實際的 item instance, 所以如果有兩個 item 是 label 是一樣的, 會導致選第二個 item 後的 getValue() 還是得到第一個 item instance。 以 UI 面來說,這是沒有問題的──使用者本來就無法分辨他到底是選到哪一個...... Orz

setTriggerAction(TriggerAction.ALL) 可以在使用者再次要求下拉選單時顯示所有值。 像 TimeField 就非常需要... Orz

要注意 ComboBox 的顯示值(或著說 LabelProvider.getLabel() 的回傳值)不能是 \n 結尾。 顯示上是不會有問題、也會觸發 SelectionEvent, 但是在 onblur 的時候不會觸發 ValueChangeEvent、ComboBox 的值會消失。

如果 item 的值是 null,顯示時不會觸發 LabelProvider.getLabel(), 在 expand 時選單會多一個高度很詭異的空白行。

setEditable(false) 好像直接包含 setSelectOnFocus() 與 setForceSelection() 的功能, 此乃實驗結果,有待 trace code 證明 [死]。 另外,如果搭配 FieldLabel 使用,居然點 FieldLabel 的 label 也會觸發 focus 效果, 這什麼黑魔法阿阿阿阿... Orz

如果 extend ComboBox,然後想要在裡頭用自己定義的 enum PKG_NAME.Direction, 在 Eclipse 當中會預設使用 com.google.gwt.i18n.client.HasDirection.Direction (還不用 import... WTF ==")。 目前唯一合理的解釋是 ComboBox 的祖先 ValueBaseField 有實作 HasDirectionEstimator 跟 AutoDirectionHandler.Target, 裡頭有 import com.google.gwt.i18n.client.HasDirection.Direction, 可是這還是不科學阿阿阿...... Orz

详细说明 setTriggerAction()、setForceSelection()、setEditable()、 setSelectOnFocus() 的作用和相互影响。

  • setTriggerAction()是否显示全部下拉框中全部内容。 如果是 TriggerAction.QUERY,只能显示根据 ComboBox 内容筛选过后的内容。 如果是 TriggerAction.ALL,在输入的时候会筛选对应内容, 但是在按下下拉框按钮的时候会显示全部内容。 默认值:TriggerAction.QUERY

  • setForceSelect()是否允许 ComboBox 中的值不在下拉框中。 如果是 true,ComboBox 输入的值不在下拉框中,lose focus 后会清空。 (如果使用setValue()设定一个不在下拉框的输入值不会清空) 默认值:false

  • setEditable()是否允许 ComboBox 中直接输入值。 如果是 false,无法在 ComboBox 中直接输入值。 默认值:true

  • setSelectOnFocus()是否在 focus 的时候出现反白的效果。 如果是 true,会在 focus 的时候选中 ComboBox 中的值,出现反白的效果 (只在鼠标点击的时候出现效果,松开后效果消失),使用 Tab 键进入 ComboBox 不会出现这个效果。 默认值:false

  • 打V表示符合描述。打X表示不符合描述。 (包括使用 setValue() 的情况)

setTriggerAction()

setForceSelect()

setEditable()

setSelectOnFocus()

下拉框只显示 ComboBox 筛选过内容

允许 ComboBox 中的值不在下拉框中

允许 ComboBox 中输入值

在 focus 的时候不出现反白的效果

TriggerAction.QUERY

true

true

true

V

X

V

X

TriggerAction.QUERY

true

true

false

V

X

V

V

TriggerAction.QUERY

X

false

X

V

V

X

V

TriggerAction.QUERY

false

true

true

V

V

V

X

TriggerAction.QUERY

false

true

false

V

V

V

V

TriggerAction.ALL

true

true

true

X

X

V

X

TriggerAction.ALL

true

true

false

X

X

V

V

TriggerAction.ALL

X

false

X

X

V

X

V

TriggerAction.ALL

false

true

true

X

V

V

X

TriggerAction.ALL

false

true

false

X

V

V

V

DateField

觸發 setValue() 的來源 ###

DatePicker ####

  • 当 new 一个 DateField 时,就 new 了一个 DateCell 和一个 DateTimePropertyEditor;

  • 当使用者点击画面上的 DateCell 时,会通过 DateCell.onTriggerClick() 呼叫 DateCell.expand(),在这里 new 了一个 DatePicker;

  • 因为在 DatePicker 的 Constructor 中实现了 sinkEvents(),而且在 onBrowserEvent() 中实现了 ONCLICK|ONMOUSEOVER 等的具体操作,所以当画面上发生 ONCLICK Event 则会呼叫 DatePicker.onClick(Event);

按下 today #####

  • 使用者在出现的 date picker 上点击【today】时,作 addSelectHandler() 让 select event 发生时去呼叫 selectToday(),结果呼叫 DatePicker.setValue(Date, boolean),设置 value 的值;

點擊日曆上的日期 #####

  • 当使用者触发 ONCLICK Event 时,呼叫 DatePicker.onClick(Event),若触发为当月的某一天则呼叫 DatePicker.onDayClick(XElement);然后通过 DatePicker.handleDateClick(XElement, String) 呼叫 DatePicker.setValue(Date, boolean),设置 value 的值;

直接輸入字串 ####

  • 当 new 一个 DateField 时,就 new 了一个 DateTimePropertyEditor 和一个 DateCell;

  • 当使用者在 text 中输入数据时,呼叫 ValueBaseField.setValue(T),实际上是呼叫了 CellComponent.setValue(C, boolean, boolean),在这里设置 value 的值;

  • (确实没有找到回到 DatePicker.setValue() 的地方,但确实这里应该会做 resetTime(),我也没有找到在什么时候做 resetTime() 的,/(ㄒoㄒ)/~~)

setValue() 的處理 ####

  • 在 DatePicker.setValue(date, true) 時會作 this.value = new DateWrapper(date).resetTime(),

    resetTime() 的 source code 與結果有點對不起來。

    就結果而言,會將指定日期的時間部份設定為零點零分零秒。

其他

Grid

GridView ###

GridView.setForceFit(),如果設定 true 則 Grid 不會出現橫向 scroll bar; 沒有作 setFixed(true) 的 column 會依設定 width 為比例分配剩餘的寬度。

GridView 可以設定 GridViewConfig,藉此設定 row / column 的 style── 正確的說是加掛 style name。 實務上很吃 CSS 技能所以不好用,還有導致其他 style 亂掉的風險。 要處理 style 還是用 ColumnConfig.setCell(), 在自訂的 cell 裡頭設計比較容易且影響範圍可控。

可以透過 GridView.getScroller() 控制 Grid 的 scroll 狀態。 當 refresh() 結束之後不想強制 scroll 回左上角,就必須得靠這招。

GroupingView ####

如果希望資料初次載入後呈現 collapse 的樣子, 在操作完 store 之後對 view 作 collapseAllGroups() 是不正確的作法, 因為之後只要再對 view 作 refresh(), 原本沒有 expand 的 group 也會 expand。 正確作法是在 grid 初始時作 GroupingView.setStartCollapsed(true)。

AbstractGridEditing ###

setEditableGrid() 如果傳入 null 值是表示移除 edit 的效果, 因為一開始會作 groupRegistration.removeHandler(),然後在傳入值不為 null 才會掛相關 handler。 這在初始化設定(尤其搭配 ui.xml)時要特別注意 [淚目]

使用者的編輯行為會透過 GridEditing 影響 Grid 的 Store, 實際作為是增加 Change 跟 Record。 目前看起來,Store 只有提供兩個對應的 method:commitChanges() 跟 rejectChanges() 來清空 modifiedRecords→才會讓畫面上的 dirty 樣子消失(應該是 StoreUpdateEvent 的結果)。 commitChanges() 跟 rejectChanges() 都會觸發 StoreUpdateEvent, 實驗結果:rejectChanges() 如果影響 n 個 item,就會炸 n 個 StoreUpdateEvent。

從 Store.getModifiedRecords() 可以得到有改變(dirty)的 item:

要讓 fooInstance 的內容變成現在的值,有幾種作法:

  • r.commit(boolean):單筆

  • store.commitChanges():整個 store

  • 根據 Record / Change 來修改 fooInstance [暈]

至於要發 RPC、萬一 RPC 失敗要能 rollback... 目前還沒有想法 [死]

LabelToolItem

LabelToolItem 轉換成 DOM 就只是一個 div 包一個字串,當然有掛一些 css(沒有特別的內容), 除了 package name 很奇怪之外,基本上應該可以視為 GXT 版的 label。 (謎之聲:之前測試不知道是測試到哪裡去了... (艸 )

TabPanel

在 ui.xml 當中只能這樣寫

有點討厭,不過就算自己設計好像也沒辦法改變什麼 Orz

Toggle

如果只是把 HasValue 加到 Toggle 當中、畫面也還沒進行任何操作, 畫面顯示是會正常的,但是 toggle.getValue() 會是 null。 所以 toggle.add() 完還是 toggle.setValue() 一下比較保險。

DrawComponent

獨立出去紀錄在 DrawComponent。

DnD

new DragSource(dSource) 會讓 dSource 變成可以 drag 的 widget, 實際上要看實作,像 TreeDragSource 就是讓 tree 的 item 變成可以 DnD。 new DropTarget(dTarget) 會讓 dTarget 變成可 drop 的對象。 在沒有特別的設定下(也就是 default group),任何的 DropTarget 都會接收任何 DragSource 的 DnD 行為。 反過來說,如果 dSource.setGroup("5566"),那麼 dTarget 必須也要是 5566 這個 group, 才有辦法接收源自於 dSource 的 DnD 行為。

如果 parent、child 這兩個 widget 都是 drop target(不考慮 group), parent 跟 child 同時在畫面上、且 parent 包含 child, 則如果在 child 上作 drop 的動作,只會觸發 child 的 DndDropHandler、 而不會觸發 parent 的 DndDropHandler。

Last updated

Was this helpful?