@@ -187,13 +187,21 @@ class ActionParamsLayoutRender(private var linearLayout: LinearLayout, activity:
187187 }
188188 }
189189
190- // CHỈ set View.visibility tức thời để tránh nhấp nháy lúc mở dialog.
191- // KHÔNG ghi vào visibilityState ở đây: evaluateDependencies() được gọi ngay sau
192- // initializeDependencyStates() sẽ tính trạng thái thật (có cascade) và mới là nơi
193- // ghi visibilityState + quyết định gọi depend-onchange. Nếu ghi ở đây, lần đánh giá
194- // đầu tiên sẽ hiểu nhầm đây là "thay đổi trạng thái" và gọi nhầm callback ngay khi
195- // dialog vừa mở.
196- rowViews[name]?.visibility = if (initialVisibility) View .VISIBLE else View .GONE
190+ // CHỈ set View.visibility (hoặc mờ/khóa với depend-readonly) tức thời để tránh
191+ // nhấp nháy lúc mở dialog. KHÔNG ghi vào visibilityState ở đây: evaluateDependencies()
192+ // được gọi ngay sau initializeDependencyStates() sẽ tính trạng thái thật (có cascade)
193+ // và mới là nơi ghi visibilityState + quyết định gọi depend-onchange. Nếu ghi ở đây,
194+ // lần đánh giá đầu tiên sẽ hiểu nhầm đây là "thay đổi trạng thái" và gọi nhầm
195+ // callback ngay khi dialog vừa mở.
196+ val row = rowViews[name] ? : continue
197+ if (info.dependReadonly) {
198+ // Luôn hiện, chỉ mờ/khóa tương tác theo initialVisibility - không set GONE,
199+ // tránh hàng bị "biến mất" rồi mới hiện lại (nhấp nháy) khi dialog vừa mở.
200+ row.visibility = View .VISIBLE
201+ setRowInteractive(row, initialVisibility)
202+ } else {
203+ row.visibility = if (initialVisibility) View .VISIBLE else View .GONE
204+ }
197205 }
198206 }
199207
@@ -439,20 +447,48 @@ class ActionParamsLayoutRender(private var linearLayout: LinearLayout, activity:
439447 visibilityState[name] = shouldShow
440448
441449 val view = rowViews[name]
442- view?.visibility = if (shouldShow) View .VISIBLE else View .GONE
450+ val info = currentParamInfos.find { it.name == name }
451+
452+ if (info?.dependReadonly == true ) {
453+ // ========== TÍNH NĂNG MỚI: depend-readonly ==========
454+ // Không ẩn (GONE) mà giữ VISIBLE, chỉ làm mờ + khóa tương tác (isEnabled = false)
455+ // khi điều kiện phụ thuộc không thỏa (shouldShow = false).
456+ view?.visibility = View .VISIBLE
457+ view?.let { setRowInteractive(it, shouldShow) }
458+ } else {
459+ view?.visibility = if (shouldShow) View .VISIBLE else View .GONE
460+ }
443461
444462 // Chỉ gọi callback khi ĐÃ TỪNG có trạng thái trước đó (oldState != null) VÀ trạng thái
445463 // thực sự đổi - tránh gọi callback ngay khi dialog vừa mở (lần đầu tiên oldState luôn
446464 // null vì initializeDependencyStates() không ghi vào visibilityState).
447465 if (oldState != null && oldState != shouldShow) {
448- val info = currentParamInfos.find { it.name == name }
449466 val script = info?.dependOnChangeCallback
450467 if (! script.isNullOrEmpty()) {
451468 executeDependOnChangeCallback(name, shouldShow, script)
452469 }
453470 }
454471 }
455472
473+ // ========== TÍNH NĂNG MỚI: BẬT/TẮT TƯƠNG TÁC + LÀM MỜ CHO depend-readonly ==========
474+ // enabled = true -> hiện bình thường, hết mờ, có thể bấm/nhập/chọn
475+ // enabled = false -> làm mờ cả hàng (alpha) và vô hiệu hóa toàn bộ control con
476+ // (EditText/CheckBox/Switch/SeekBar/Spinner/nút bấm...) để
477+ // người dùng không thể chỉnh sửa giá trị, nhưng vẫn nhìn thấy nó.
478+ private fun setRowInteractive (row : View , enabled : Boolean ) {
479+ row.alpha = if (enabled) 1f else 0.4f
480+ setEnabledRecursively(row, enabled)
481+ }
482+
483+ private fun setEnabledRecursively (view : View , enabled : Boolean ) {
484+ view.isEnabled = enabled
485+ if (view is ViewGroup ) {
486+ for (i in 0 until view.childCount) {
487+ setEnabledRecursively(view.getChildAt(i), enabled)
488+ }
489+ }
490+ }
491+
456492 // ========== TÍNH NĂNG MỚI: THỰC THI depend-onchange ==========
457493 // Chạy script/callback trên luồng nền (KHÔNG được chạy trực tiếp trên UI thread vì lệnh
458494 // shell root có thể mất nhiều thời gian và sẽ làm treo giao diện). Truyền tên param vừa
@@ -584,7 +620,11 @@ class ActionParamsLayoutRender(private var linearLayout: LinearLayout, activity:
584620 }
585621 }
586622
587- val isHiddenByDepend = rowViews[actionParamInfo.name]?.visibility == View .GONE
623+ // Dùng visibilityState (kết quả logic của evaluateDependencies) thay vì đọc trực
624+ // tiếp View.visibility, vì với depend-readonly=true, view vẫn ở trạng thái
625+ // VISIBLE (chỉ bị mờ + khóa tương tác) dù về mặt logic vẫn được xem là "không
626+ // thỏa điều kiện" (shouldShow = false).
627+ val isHiddenByDepend = visibilityState[actionParamInfo.name] == false
588628
589629 // ========== TÍNH NĂNG MỚI: BỎ QUA PARAM ẨN NẾU KHÔNG CÓ dependIncludeHidden ==========
590630 if (isHiddenByDepend && ! actionParamInfo.dependIncludeHidden) {
0 commit comments