From 8cfcc418b70f25de801a0985f7e0b1cc845e60f2 Mon Sep 17 00:00:00 2001 From: John Trujillo Date: Tue, 14 Apr 2026 11:46:38 -0500 Subject: [PATCH] refactor: optimize bottom sheet height and clean up attribute dialogs Apply fit-to-contents behavior and idiomatic Kotlin refactoring to DesignEditor --- .../layouteditor/editor/DesignEditor.kt | 167 ++++++++---------- 1 file changed, 78 insertions(+), 89 deletions(-) diff --git a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/DesignEditor.kt b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/DesignEditor.kt index 2ee5f818cb..26dc132342 100644 --- a/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/DesignEditor.kt +++ b/layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/editor/DesignEditor.kt @@ -4,6 +4,8 @@ import android.animation.LayoutTransition import android.content.Context import android.graphics.Canvas import android.graphics.Paint +import android.text.Editable +import android.text.TextWatcher import android.util.AttributeSet import android.view.DragEvent import android.view.View @@ -20,6 +22,7 @@ import androidx.core.view.isEmpty import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.blankj.utilcode.util.VibrateUtils +import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -31,6 +34,8 @@ import com.itsaky.androidide.utils.displayTooltipOnLongPress import com.itsaky.androidide.utils.handleLongClicksAndDrag import org.appdevforall.codeonthego.layouteditor.R import org.appdevforall.codeonthego.layouteditor.adapters.AppliedAttributesAdapter +import org.appdevforall.codeonthego.layouteditor.adapters.AvailableAttributesAdapter +import org.appdevforall.codeonthego.layouteditor.databinding.DialogAvailableAttributesBinding import org.appdevforall.codeonthego.layouteditor.databinding.ShowAttributesDialogBinding import org.appdevforall.codeonthego.layouteditor.editor.dialogs.AttributeDialog import org.appdevforall.codeonthego.layouteditor.editor.dialogs.BooleanDialog @@ -673,143 +678,127 @@ class DesignEditor : LinearLayout { val allKeysAndValues = viewAttributeMap[target] ?: return val allAttrs = initializer.getAllAttributesForView(target) - val displayKeys: MutableList = ArrayList() - val displayValues: MutableList = ArrayList() - val displayAttrs: MutableList> = ArrayList() - - val originalKeys = allKeysAndValues.keySet() - val originalValues = allKeysAndValues.values() - - for (i in originalKeys.indices) { - val key = originalKeys[i] + val displayKeys = mutableListOf() + val displayValues = mutableListOf() + val displayAttrs = mutableListOf>() + allKeysAndValues.keySet().forEachIndexed { i, key -> val foundAttrDef = allAttrs.find { it[Constants.KEY_ATTRIBUTE_NAME].toString() == key } if (foundAttrDef != null) { displayKeys.add(key) - displayValues.add(originalValues[i]) + displayValues.add(allKeysAndValues.values()[i]) displayAttrs.add(foundAttrDef) } } - val dialog = BottomSheetDialog(context) + val dialog = BottomSheetDialog(context).apply { + behavior.isFitToContents = true + behavior.state = BottomSheetBehavior.STATE_EXPANDED + behavior.skipCollapsed = true + } + val binding = ShowAttributesDialogBinding.inflate(dialog.layoutInflater) dialog.setContentView(binding.root) + TooltipCompat.setTooltipText(binding.btnAdd, "Add attribute") TooltipCompat.setTooltipText(binding.btnDelete, "Delete") - // Now, we use our new, guaranteed-to-be-in-sync lists. - val appliedAttributesAdapter = AppliedAttributesAdapter(displayAttrs, displayValues) + val appliedAttributesAdapter = AppliedAttributesAdapter(displayAttrs, displayValues).apply { + onClick = { position -> + showAttributeEdit(target, displayKeys[position]) + dialog.dismiss() + } - appliedAttributesAdapter.onClick = { position -> - showAttributeEdit(target, displayKeys[position]) - dialog.dismiss() + onRemoveButtonClick = { position -> + dialog.dismiss() + val updatedView = removeAttribute(target, displayKeys[position]) + showDefinedAttributes(updatedView) + } } - appliedAttributesAdapter.onRemoveButtonClick = { position -> - dialog.dismiss() - val view = removeAttribute(target, displayKeys[position]) - showDefinedAttributes(view) + binding.attributesList.apply { + adapter = appliedAttributesAdapter + layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) } - binding.attributesList.adapter = appliedAttributesAdapter - binding.attributesList.layoutManager = - LinearLayoutManager(context, RecyclerView.VERTICAL, false) - binding.viewLayout.apply { - displayTooltipOnLongPress( - context = this.context, - anchorView = this, - tooltipCategory = TooltipCategory.CATEGORY_XML, - tooltipTag = target.javaClass.superclass.simpleName, - ) - } + binding.viewLayout.displayTooltipOnLongPress( + context = context, + anchorView = binding.viewLayout, + tooltipCategory = TooltipCategory.CATEGORY_XML, + tooltipTag = target.javaClass.superclass.simpleName, + ) + binding.viewName.text = target.javaClass.superclass.simpleName binding.viewFullName.text = target.javaClass.superclass.name + binding.btnAdd.setOnClickListener { showAvailableAttributes(target) dialog.dismiss() } binding.btnDelete.setOnClickListener { - MaterialAlertDialogBuilder(context) - .setTitle(R.string.delete_view) - .setMessage(R.string.msg_delete_view) - .setNegativeButton( - R.string.no, - ) { d, _ -> - d.dismiss() - }.setPositiveButton( - R.string.yes, - ) { _, _ -> - removeId(target, target is ViewGroup) - removeViewAttributes(target) - removeWidget(target) - updateStructure() - updateUndoRedoHistory() - dialog.dismiss() - }.show() + confirmViewDeletion(target, dialog) } dialog.show() } private fun showAvailableAttributes(target: View) { - val availableAttrs = - initializer.getAvailableAttributesForView(target) - val names: MutableList = ArrayList() - - for (attr: HashMap in availableAttrs) { - names.add(attr["name"].toString()) + val availableAttrs = initializer.getAvailableAttributesForView(target) + val names = availableAttrs.map { it["name"].toString() }.toMutableList() + val dialog = BottomSheetDialog(context).apply { + behavior.isFitToContents = true + behavior.state = BottomSheetBehavior.STATE_EXPANDED + behavior.skipCollapsed = true } - val dialog = BottomSheetDialog(context) - val binding = - org.appdevforall.codeonthego.layouteditor.databinding.DialogAvailableAttributesBinding - .inflate(dialog.layoutInflater) + val binding = DialogAvailableAttributesBinding.inflate(dialog.layoutInflater) dialog.setContentView(binding.root) - val adapter = - org.appdevforall.codeonthego.layouteditor.adapters.AvailableAttributesAdapter(names) { attributeName -> - // Find the attribute by name - for (attr in availableAttrs) { - if (attr["name"].toString() == attributeName) { - showAttributeEdit(target, attr[Constants.KEY_ATTRIBUTE_NAME].toString()) - break - } - } - dialog.dismiss() - } + val adapter = AvailableAttributesAdapter(names) { attributeName -> + availableAttrs.find { it["name"].toString() == attributeName }?.let { attr -> + showAttributeEdit(target, attr[Constants.KEY_ATTRIBUTE_NAME].toString()) + } + dialog.dismiss() + } - binding.attributesList.adapter = adapter - binding.attributesList.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) + binding.attributesList.apply { + this.adapter = adapter + layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) + } - // Set up search functionality binding.searchEditText.addTextChangedListener( - object : android.text.TextWatcher { - override fun beforeTextChanged( - s: CharSequence?, - start: Int, - count: Int, - after: Int, - ) {} - - override fun onTextChanged( - s: CharSequence?, - start: Int, - before: Int, - count: Int, - ) {} - - override fun afterTextChanged(s: android.text.Editable?) { + object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} + override fun afterTextChanged(s: Editable?) { adapter.filter(s?.toString() ?: "") } - }, + } ) dialog.show() } + private fun confirmViewDeletion(target: View, dialog: BottomSheetDialog) { + MaterialAlertDialogBuilder(context) + .setTitle(R.string.delete_view) + .setMessage(R.string.msg_delete_view) + .setNegativeButton(R.string.no) { d, _ -> + d.dismiss() + } + .setPositiveButton(R.string.yes) { _, _ -> + removeId(target, target is ViewGroup) + removeViewAttributes(target) + removeWidget(target) + updateStructure() + updateUndoRedoHistory() + dialog.dismiss() + }.show() + } + private fun showAttributeEdit( target: View, attributeKey: String,