正確來說,ZK-1309 應該稱之為 onFloatUp
的故事...... [淚目]
故事發生在 modal window 當中如果用了 Clients.showNotification()
,
要再對 modal window 作 drag 的時候,覆蓋畫面的 mask 就會消失,
在 drop 的時候才又出現;令人傻眼的是,如果繼續 DnD,就又恢復正常行為。
因為搭配 Notification
才會出問題,所以看了一下 Notification
是不是有做什麼手腳......
根本就是浪費時間 [死]
既然時間都浪費掉了,就順便記一下。
Clients.showNotification()
最後負責呈現的 JS
是 Notification.js
,在 show()
當中會去新增一個 zul.wgt.Notification()
,原則上行為都是繼承自 Popup
。
問題應該還是回歸到 Window
自己身上,不過先找到 mask 是誰處理再說。
用 css name 硬幹的結果找到 effect.js
中的 zk.eff.FullMask
,
在 sync()
的時候會用 jqzk.isVisible()
(在 dom.js
裡頭)去檢查 Window
是不是看得到
(要 css 的 display != "none"
而且 visibility != "hidden"
),
如果看不到就把自己 hide 起來,故事的舞台就在這兒了。
於是在 debug 的過程中,難免會去印 sync()
的參數 el
的值,遇到一個靈異現象:
console.log(zk(el).jq[0]); //DOM 的 visibility 沒有值
console.log(zk(el).jq[0].style.visibility); //印出 hidden
//也有剛好顛倒過來的情形
真是有夠莫名的,完全不懂為什麼?一開始只用第一個寫法觀察值,就發生了結果跟過程對不起來的悲劇 [核爆]
從這往回推,是 Window.zsync()
呼叫這個 method,再往回推...
就被推倒了推不下去了,因為在重現問題的時候 FullMask.sync()
在一次動作中會被呼叫好幾次;
而 Window.zsync()
在 Window
裡頭遍地開花。
只好用 console.trace()
大法來慢慢看,最後發現在這個 case 中一致的起點是 Window.onFloatUp()
,
整理如下:
- 單純 drag window
- 開始時:
Drag._startDrag()
→Window._startmove()
→zWatch.fire('onFloatUp, dg.control)
- 結束時:
Drag._finishDrag()
→Window._aftermove()
→Window.zsync()
- 開始時:
- 叫出
Notification
然後點 window:Widget.mimicMouseDown_()
- 叫出
Notification
然後 drag window:- 下列選一(原因不明,好像還有測出另一個只是忘了記......)
_domEvtProxy0()
doMouseover()
- 回歸單純 drag window 流程
- 下列選一(原因不明,好像還有測出另一個只是忘了記......)
在發了 onFloatUp
的 event 之後,Window
方面流程大致上是:
Window.onFloatUp()
Widget.setTopmost()
Widget.setFloatZIndex_() :有給 opt = {fire:true},所以後面 onZIndex() 一定會 fire
Window.setZIndex()
Widget.setZIndex() : 上面有 super("setZIndex"),如果值沒變就不會往下作
Window.onZIndex() : 上面有 fire("onZIndex")
Window.zsync()
Window.zsync()
只要有進入 setTopmost()
, 那麼 onZIndex()
跟 setZIndex()
會各 call 一次 zsync()
,
也就導致 mask 也會 sync()
兩次,雖然很無奈,但反正不是要修 performance issue,所以不管他 XD。
那麼,問題是出在哪裡呢?
簡單地說,就是 Notification
與 modal window 爭奪「最高」的機制(可以說是 setTopmost()
)所延伸出來的問題。
一開始 modal window 出來時,他得是「全 DOM 最高」,所以 setTopmost()
計算出來的結果 zIndex 是 1800。
後來殺出的 Notification
必須高過 modal window,所以 setTopmost()
決定給他 zIndex 為 1801。
當 Notification 消失(無論是哪一種方式)的那個瞬間,modal window 觸發 onFloatUp()
,
於是要再次奪回「全 DOM 最高」的地位,偏偏這時候 Notification 還沒有真正死掉,所以 setTopmost()
就給了 1802。
真正開始處理 modal window 的 DnD 時(也會觸發 onFloatUp()
),又會再確保一次自己是「全 DOM 最高」,
但是這時候 setTopmost()
發現只要給 1800 就夠了,就真的給 1800。
結果往下處理到 Widget.setZIndex()
的時候發現前後兩次的 zIndex 值不一樣,
按照規定去作(兩次)Window.zsync()
,在 FullMask.sync()
那裡檢查 modal window 是不是還在......
登登! [炸]
根據 DnD 的設計原則(細節略),在開始 drag 的時候,end-user 看到、拖曳的其實是假的鬼魂,
真正的本尊還留在原本的位置只是隱藏不見,所以 FullMask
覺得 modal window 看不見,自己也就乖乖地跟著不見。
在解 bug 的途中(?)嘗試撰寫一個 Widget.isTopmost()
來判斷是不是已經是「全 DOM 最高」,
沒有經過嚴格驗證,不過在「叫出 Notification
然後點 modal window」的情況下測試會過,
就保留一份在這灌水字數以供緬懷:
isTopmost: function () {
if (!this.desktop) return false;
for (var wgt = this; wgt; wgt = wgt.parent) {
if (wgt._floating) {
for (var j = _floatings.length; j--;) {
var w = _floatings[j].widget,
h = _floatings[j].node;
if (wgt == w) continue;
if (this.getFloatZIndex_(this.$n()) < w.getFloatZIndex_(h) && !zUtl.isAncestor(this, w)) {
return false;
}
}
}
}
return true;
},
最後的解法還是在 Window
的 _startmove()
時塞一個叫 isDragging
的 field,
在 _aftermove()
的時候拿掉。所以只要在 onFloatUp()
的時候檢查這個 field,
如果正在 drag 就不繼續往下作,bug 就圓滿地圓寂(?)了。
當然,不管是解 bug 的當下、或是在回顧這個故事時,
都不覺得這樣的作法有很好,總覺得應該去解決 setTopmost()
或是 onFloatUp()
機制著手來斬草除根......
這大概就是所謂 maintain 的情懷(?)吧......
No comments:
Post a Comment